This commit is contained in:
Penny Wood 2024-05-02 17:21:58 +12:00 committed by GitHub
commit a7a15a7219
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 233 additions and 0 deletions

View File

@ -421,6 +421,7 @@ esphome/components/wk2204_spi/* @DrCoolZic
esphome/components/wk2212_i2c/* @DrCoolZic
esphome/components/wk2212_spi/* @DrCoolZic
esphome/components/wl_134/* @hobbypunk90
esphome/components/ws18x0_uart/* @Swamp-Ig
esphome/components/x9c/* @EtienneMD
esphome/components/xgzp68xx/* @gcormier
esphome/components/xiaomi_hhccjcy10/* @fariouche

View File

@ -0,0 +1,44 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import uart
from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID
CODEOWNERS = ["@Swamp-Ig"]
DEPENDENCIES = ["uart"]
AUTO_LOAD = ["binary_sensor"]
MULTI_CONF = True
ws18x0_uart_ns = cg.esphome_ns.namespace("ws18x0_uart")
WS18x0UARTComponent = ws18x0_uart_ns.class_(
"WS18x0UARTComponent", cg.Component, uart.UARTDevice
)
WS18x0UARTTrigger = ws18x0_uart_ns.class_(
"WS18x0UARTTrigger", automation.Trigger.template(cg.uint32)
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(WS18x0UARTComponent),
cv.Optional(CONF_ON_TAG): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WS18x0UARTTrigger),
}
),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(uart.UART_DEVICE_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
for conf in config.get(CONF_ON_TAG, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
cg.add(var.register_trigger(trigger))
await automation.build_automation(trigger, [(cg.uint32, "x")], conf)

View File

@ -0,0 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor, ws18x0_uart
from esphome.const import CONF_UID
from . import ws18x0_uart_ns
DEPENDENCIES = ["ws18x0_uart"]
CONF_WS18x0_UART_ID = "ws18x0_uart_id"
WS18x0UARTBinarySensor = ws18x0_uart_ns.class_(
"WS18x0UARTBinarySensor", binary_sensor.BinarySensor
)
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(WS18x0UARTBinarySensor).extend(
{
cv.GenerateID(CONF_WS18x0_UART_ID): cv.use_id(ws18x0_uart.WS18x0UARTComponent),
cv.Required(CONF_UID): cv.uint32_t,
}
)
async def to_code(config):
var = await binary_sensor.new_binary_sensor(config)
hub = await cg.get_variable(config[CONF_WS18x0_UART_ID])
cg.add(hub.register_card(var))
cg.add(var.set_id(config[CONF_UID]))

View File

@ -0,0 +1,100 @@
#include "ws18x0_uart.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ws18x0_uart {
static const char *const TAG = "ws18x0_uart";
static const uint8_t WS18X0_UART_START_BYTE = 0x02;
static const uint8_t WS18X0_UART_END_BYTE = 0x03;
static const int8_t WS18X0_UART_STATE_WAITING_FOR_START = -1;
static const int8_t WS18X0_UART_STATE_WAITING_FOR_LENGTH = -2;
static const int8_t WS18X0_UART_STATE_WAITING_FOR_CHECKSUM = 2;
static const int8_t WS18X0_UART_STATE_WAITING_FOR_END = 1;
void ws18x0_uart::WS18x0UARTComponent::loop() {
while (this->available() > 0) {
uint8_t data;
if (!this->read_byte(&data)) {
ESP_LOGW(TAG, "Reading data from WS18x0UART failed!");
this->status_set_warning();
return;
}
switch (this->read_state_) {
case WS18X0_UART_STATE_WAITING_FOR_START:
if (data == WS18X0_UART_START_BYTE) {
this->read_state_ = WS18X0_UART_STATE_WAITING_FOR_LENGTH;
} else {
// Not start byte, probably not synced up correctly, skip forward.
}
break;
case WS18X0_UART_STATE_WAITING_FOR_LENGTH:
if (data >= 9 && data <= 10) {
this->raw_ = data;
this->read_state_ = data - 2;
} else {
// Invalid length. Warn and resync
ESP_LOGW(TAG, "Reading data from WS18x0UART failed with length out of expected range: %d", data);
this->read_state_ = WS18X0_UART_STATE_WAITING_FOR_START;
this->status_set_warning();
}
break;
case WS18X0_UART_STATE_WAITING_FOR_CHECKSUM: {
uint8_t checksum = 0;
for (int i = 0; i < 8; ++i)
checksum ^= (this->raw_ >> i * 8) & 0xFF;
if (checksum == data) {
this->read_state_ = WS18X0_UART_STATE_WAITING_FOR_END;
} else {
// Invalid checksum. Warn and resync
ESP_LOGW(TAG,
"Reading data from WS18x0UART failed with invalid checksum read 0x%2X != calculated 0x%2X from raw "
"data: 0x%llX",
data, checksum, this->raw_);
this->read_state_ = WS18X0_UART_STATE_WAITING_FOR_START;
this->status_set_warning();
}
break;
}
case WS18X0_UART_STATE_WAITING_FOR_END:
if (data != WS18X0_UART_END_BYTE) {
// Invalid end byte. Warn and resync
ESP_LOGW(TAG, "Reading data from WS18x0UART failed with invalid end byte: 0x%2X", data);
this->read_state_ = WS18X0_UART_STATE_WAITING_FOR_START;
this->status_set_warning();
} else {
// Valid data
this->status_clear_warning();
bool report = this->raw_ != this->last_raw_;
uint32_t result = (uint32_t) this->raw_;
for (auto *card : this->cards_) {
if (card->process(result)) {
report = false;
}
}
for (auto *trig : this->triggers_)
trig->process(result);
if (report) {
ESP_LOGD(TAG, "Found new tag with ID %" PRIu32, result);
}
this->last_raw_ = this->raw_;
this->read_state_ = WS18X0_UART_STATE_WAITING_FOR_START;
}
break;
default:
// Read next byte
this->raw_ = this->raw_ << 8 | data;
this->read_state_--;
break;
}
}
}
} // namespace ws18x0_uart
} // namespace esphome

View File

@ -0,0 +1,61 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/uart/uart.h"
#include <cinttypes>
#include <vector>
namespace esphome {
namespace ws18x0_uart {
class WS18x0UARTBinarySensor;
class WS18x0UARTTrigger;
class WS18x0UARTComponent : public Component, public uart::UARTDevice {
public:
void loop() override;
void register_card(WS18x0UARTBinarySensor *obj) { this->cards_.push_back(obj); }
void register_trigger(WS18x0UARTTrigger *trig) { this->triggers_.push_back(trig); }
float get_setup_priority() const override { return setup_priority::DATA; }
uint32_t id() { return (uint32_t) raw_; }
uint64_t raw() { return raw_; }
protected:
int8_t read_state_{-1};
uint64_t raw_{0};
uint64_t last_raw_{0};
std::vector<WS18x0UARTBinarySensor *> cards_;
std::vector<WS18x0UARTTrigger *> triggers_;
};
class WS18x0UARTBinarySensor : public binary_sensor::BinarySensorInitiallyOff {
public:
void set_id(uint32_t id) { id_ = id; }
bool process(uint32_t id) {
if (this->id_ == id) {
this->publish_state(true);
yield();
this->publish_state(false);
return true;
}
return false;
}
protected:
uint32_t id_;
};
class WS18x0UARTTrigger : public Trigger<uint32_t> {
public:
void process(uint32_t uid) { this->trigger(uid); }
};
} // namespace ws18x0_uart
} // namespace esphome