mirror of
https://github.com/esphome/esphome.git
synced 2024-11-29 12:55:46 +01:00
Add Adalight support (#956)
A component to support [Adalight](https://learn.adafruit.com/adalight-diy-ambient-tv-lighting). This allows to control addressable LEDs over UART, by pushing data right into LEDs. The most useful to use [Prismatik](https://github.com/psieg/Lightpack) to create an immersive effect on PC. Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
This commit is contained in:
parent
27204aa53c
commit
d1b051a6bd
24
esphome/components/adalight/__init__.py
Normal file
24
esphome/components/adalight/__init__.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import uart
|
||||||
|
from esphome.components.light.types import AddressableLightEffect
|
||||||
|
from esphome.components.light.effects import register_addressable_effect
|
||||||
|
from esphome.const import CONF_NAME, CONF_UART_ID
|
||||||
|
|
||||||
|
DEPENDENCIES = ['uart']
|
||||||
|
|
||||||
|
adalight_ns = cg.esphome_ns.namespace('adalight')
|
||||||
|
AdalightLightEffect = adalight_ns.class_(
|
||||||
|
'AdalightLightEffect', uart.UARTDevice, AddressableLightEffect)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({})
|
||||||
|
|
||||||
|
|
||||||
|
@register_addressable_effect('adalight', AdalightLightEffect, "Adalight", {
|
||||||
|
cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)
|
||||||
|
})
|
||||||
|
def adalight_light_effect_to_code(config, effect_id):
|
||||||
|
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
|
||||||
|
yield uart.register_uart_device(effect, config)
|
||||||
|
|
||||||
|
yield effect
|
140
esphome/components/adalight/adalight_light_effect.cpp
Normal file
140
esphome/components/adalight/adalight_light_effect.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include "adalight_light_effect.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adalight {
|
||||||
|
|
||||||
|
static const char *TAG = "adalight_light_effect";
|
||||||
|
|
||||||
|
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
|
||||||
|
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
|
||||||
|
|
||||||
|
AdalightLightEffect::AdalightLightEffect(const std::string &name) : AddressableLightEffect(name) {}
|
||||||
|
|
||||||
|
void AdalightLightEffect::start() {
|
||||||
|
AddressableLightEffect::start();
|
||||||
|
|
||||||
|
last_ack_ = 0;
|
||||||
|
last_byte_ = 0;
|
||||||
|
last_reset_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::stop() {
|
||||||
|
frame_.resize(0);
|
||||||
|
|
||||||
|
AddressableLightEffect::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AdalightLightEffect::get_frame_size_(int led_count) const {
|
||||||
|
// 3 bytes: Ada
|
||||||
|
// 2 bytes: LED count
|
||||||
|
// 1 byte: checksum
|
||||||
|
// 3 bytes per LED
|
||||||
|
return 3 + 2 + 1 + led_count * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
|
||||||
|
int buffer_capacity = get_frame_size_(it.size());
|
||||||
|
|
||||||
|
frame_.clear();
|
||||||
|
frame_.reserve(buffer_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
|
||||||
|
for (int led = it.size(); led-- > 0;) {
|
||||||
|
it[led].set(light::ESPColor::BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤t_color) {
|
||||||
|
const uint32_t now = millis();
|
||||||
|
|
||||||
|
if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) {
|
||||||
|
ESP_LOGV(TAG, "Sending ACK");
|
||||||
|
this->write_str("Ada\n");
|
||||||
|
this->last_ack_ = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->last_reset_) {
|
||||||
|
ESP_LOGW(TAG, "Frame: Reset.");
|
||||||
|
reset_frame_(it);
|
||||||
|
blank_all_leds_(it);
|
||||||
|
this->last_reset_ = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->frame_.empty() && now - this->last_byte_ >= ADALIGHT_RECEIVE_TIMEOUT) {
|
||||||
|
ESP_LOGW(TAG, "Frame: Receive timeout (size=%zu).", this->frame_.size());
|
||||||
|
reset_frame_(it);
|
||||||
|
blank_all_leds_(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->available() > 0) {
|
||||||
|
ESP_LOGV(TAG, "Frame: Available (size=%d).", this->available());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this->available() != 0) {
|
||||||
|
uint8_t data;
|
||||||
|
if (!this->read_byte(&data))
|
||||||
|
break;
|
||||||
|
this->frame_.push_back(data);
|
||||||
|
this->last_byte_ = now;
|
||||||
|
|
||||||
|
switch (this->parse_frame_(it)) {
|
||||||
|
case INVALID:
|
||||||
|
ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
|
||||||
|
reset_frame_(it);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARTIAL:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSUMED:
|
||||||
|
ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
|
||||||
|
reset_frame_(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableLight &it) {
|
||||||
|
if (frame_.empty())
|
||||||
|
return INVALID;
|
||||||
|
|
||||||
|
// Check header: `Ada`
|
||||||
|
if (frame_[0] != 'A')
|
||||||
|
return INVALID;
|
||||||
|
if (frame_.size() > 1 && frame_[1] != 'd')
|
||||||
|
return INVALID;
|
||||||
|
if (frame_.size() > 2 && frame_[2] != 'a')
|
||||||
|
return INVALID;
|
||||||
|
|
||||||
|
// 3 bytes: Count Hi, Count Lo, Checksum
|
||||||
|
if (frame_.size() < 6)
|
||||||
|
return PARTIAL;
|
||||||
|
|
||||||
|
// Check checksum
|
||||||
|
uint16_t checksum = frame_[3] ^ frame_[4] ^ 0x55;
|
||||||
|
if (checksum != frame_[5])
|
||||||
|
return INVALID;
|
||||||
|
|
||||||
|
// Check if we received the full frame
|
||||||
|
uint16_t led_count = (frame_[3] << 8) + frame_[4] + 1;
|
||||||
|
auto buffer_size = get_frame_size_(led_count);
|
||||||
|
if (frame_.size() < buffer_size)
|
||||||
|
return PARTIAL;
|
||||||
|
|
||||||
|
// Apply lights
|
||||||
|
auto accepted_led_count = std::min<int>(led_count, it.size());
|
||||||
|
uint8_t *led_data = &frame_[6];
|
||||||
|
|
||||||
|
for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
|
||||||
|
auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
|
||||||
|
|
||||||
|
it[led].set(light::ESPColor(led_data[0], led_data[1], led_data[2], white));
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace adalight
|
||||||
|
} // namespace esphome
|
41
esphome/components/adalight/adalight_light_effect.h
Normal file
41
esphome/components/adalight/adalight_light_effect.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/light/addressable_light_effect.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adalight {
|
||||||
|
|
||||||
|
class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
|
||||||
|
public:
|
||||||
|
AdalightLightEffect(const std::string &name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
void stop() override;
|
||||||
|
void apply(light::AddressableLight &it, const light::ESPColor ¤t_color) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum Frame {
|
||||||
|
INVALID,
|
||||||
|
PARTIAL,
|
||||||
|
CONSUMED,
|
||||||
|
};
|
||||||
|
|
||||||
|
int get_frame_size_(int led_count) const;
|
||||||
|
void reset_frame_(light::AddressableLight &it);
|
||||||
|
void blank_all_leds_(light::AddressableLight &it);
|
||||||
|
Frame parse_frame_(light::AddressableLight &it);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t last_ack_{0};
|
||||||
|
uint32_t last_byte_{0};
|
||||||
|
uint32_t last_reset_{0};
|
||||||
|
std::vector<uint8_t> frame_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace adalight
|
||||||
|
} // namespace esphome
|
@ -128,7 +128,7 @@ spi:
|
|||||||
miso_pin: GPIO23
|
miso_pin: GPIO23
|
||||||
|
|
||||||
uart:
|
uart:
|
||||||
tx_pin: GPIO22
|
- tx_pin: GPIO22
|
||||||
rx_pin: GPIO23
|
rx_pin: GPIO23
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
id: uart0
|
id: uart0
|
||||||
@ -137,6 +137,12 @@ uart:
|
|||||||
stop_bits: 1
|
stop_bits: 1
|
||||||
rx_buffer_size: 512
|
rx_buffer_size: 512
|
||||||
|
|
||||||
|
- id: adalight_uart
|
||||||
|
tx_pin: GPIO25
|
||||||
|
rx_pin: GPIO26
|
||||||
|
baud_rate: 115200
|
||||||
|
rx_buffer_size: 1024
|
||||||
|
|
||||||
ota:
|
ota:
|
||||||
safe_mode: True
|
safe_mode: True
|
||||||
password: 'superlongpasswordthatnoonewillknow'
|
password: 'superlongpasswordthatnoonewillknow'
|
||||||
@ -179,6 +185,8 @@ as3935_spi:
|
|||||||
cs_pin: GPIO12
|
cs_pin: GPIO12
|
||||||
irq_pin: GPIO13
|
irq_pin: GPIO13
|
||||||
|
|
||||||
|
adalight:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: adc
|
- platform: adc
|
||||||
pin: A0
|
pin: A0
|
||||||
@ -1177,6 +1185,8 @@ light:
|
|||||||
if (initial_run) {
|
if (initial_run) {
|
||||||
it[0] = current_color;
|
it[0] = current_color;
|
||||||
}
|
}
|
||||||
|
- adalight:
|
||||||
|
uart_id: adalight_uart
|
||||||
- automation:
|
- automation:
|
||||||
name: Custom Effect
|
name: Custom Effect
|
||||||
sequence:
|
sequence:
|
||||||
|
@ -183,7 +183,11 @@ spi:
|
|||||||
miso_pin: GPIO14
|
miso_pin: GPIO14
|
||||||
|
|
||||||
uart:
|
uart:
|
||||||
tx_pin: GPIO1
|
- tx_pin: GPIO1
|
||||||
|
rx_pin: GPIO3
|
||||||
|
baud_rate: 115200
|
||||||
|
|
||||||
|
- id: adalight_uart
|
||||||
rx_pin: GPIO3
|
rx_pin: GPIO3
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
|
||||||
@ -203,6 +207,8 @@ deep_sleep:
|
|||||||
run_duration: 20s
|
run_duration: 20s
|
||||||
sleep_duration: 50s
|
sleep_duration: 50s
|
||||||
|
|
||||||
|
adalight:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: apds9960
|
- platform: apds9960
|
||||||
type: proximity
|
type: proximity
|
||||||
@ -708,6 +714,8 @@ light:
|
|||||||
method: ESP8266_UART0
|
method: ESP8266_UART0
|
||||||
num_leds: 100
|
num_leds: 100
|
||||||
effects:
|
effects:
|
||||||
|
- adalight:
|
||||||
|
uart_id: adalight_uart
|
||||||
- e131:
|
- e131:
|
||||||
universe: 1
|
universe: 1
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user