mirror of
https://github.com/esphome/esphome.git
synced 2026-05-30 07:16:11 +08:00
[cc1101] Add packet mode support (#12474)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,17 @@
|
|||||||
from esphome import automation
|
from esphome import automation, pins
|
||||||
from esphome.automation import maybe_simple_id
|
from esphome.automation import maybe_simple_id
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import spi
|
from esphome.components import spi
|
||||||
|
from esphome.components.const import CONF_CRC_ENABLE, CONF_ON_PACKET
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_CHANNEL, CONF_FREQUENCY, CONF_ID, CONF_WAIT_TIME
|
from esphome.const import (
|
||||||
|
CONF_CHANNEL,
|
||||||
|
CONF_DATA,
|
||||||
|
CONF_FREQUENCY,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_WAIT_TIME,
|
||||||
|
)
|
||||||
|
from esphome.core import ID
|
||||||
|
|
||||||
CODEOWNERS = ["@lygris", "@gabest11"]
|
CODEOWNERS = ["@lygris", "@gabest11"]
|
||||||
DEPENDENCIES = ["spi"]
|
DEPENDENCIES = ["spi"]
|
||||||
@@ -29,7 +37,6 @@ CONF_MANCHESTER = "manchester"
|
|||||||
CONF_NUM_PREAMBLE = "num_preamble"
|
CONF_NUM_PREAMBLE = "num_preamble"
|
||||||
CONF_SYNC1 = "sync1"
|
CONF_SYNC1 = "sync1"
|
||||||
CONF_SYNC0 = "sync0"
|
CONF_SYNC0 = "sync0"
|
||||||
CONF_PKTLEN = "pktlen"
|
|
||||||
CONF_MAGN_TARGET = "magn_target"
|
CONF_MAGN_TARGET = "magn_target"
|
||||||
CONF_MAX_LNA_GAIN = "max_lna_gain"
|
CONF_MAX_LNA_GAIN = "max_lna_gain"
|
||||||
CONF_MAX_DVGA_GAIN = "max_dvga_gain"
|
CONF_MAX_DVGA_GAIN = "max_dvga_gain"
|
||||||
@@ -41,6 +48,12 @@ CONF_FILTER_LENGTH_ASK_OOK = "filter_length_ask_ook"
|
|||||||
CONF_FREEZE = "freeze"
|
CONF_FREEZE = "freeze"
|
||||||
CONF_HYST_LEVEL = "hyst_level"
|
CONF_HYST_LEVEL = "hyst_level"
|
||||||
|
|
||||||
|
# Packet mode config keys
|
||||||
|
CONF_PACKET_MODE = "packet_mode"
|
||||||
|
CONF_PACKET_LENGTH = "packet_length"
|
||||||
|
CONF_WHITENING = "whitening"
|
||||||
|
CONF_GDO0_PIN = "gdo0_pin"
|
||||||
|
|
||||||
# Enums
|
# Enums
|
||||||
SyncMode = ns.enum("SyncMode", True)
|
SyncMode = ns.enum("SyncMode", True)
|
||||||
SYNC_MODE = {
|
SYNC_MODE = {
|
||||||
@@ -167,7 +180,6 @@ CONFIG_MAP = {
|
|||||||
CONF_NUM_PREAMBLE: cv.int_range(min=0, max=7),
|
CONF_NUM_PREAMBLE: cv.int_range(min=0, max=7),
|
||||||
CONF_SYNC1: cv.hex_uint8_t,
|
CONF_SYNC1: cv.hex_uint8_t,
|
||||||
CONF_SYNC0: cv.hex_uint8_t,
|
CONF_SYNC0: cv.hex_uint8_t,
|
||||||
CONF_PKTLEN: cv.uint8_t,
|
|
||||||
CONF_MAGN_TARGET: cv.enum(MAGN_TARGET, upper=False),
|
CONF_MAGN_TARGET: cv.enum(MAGN_TARGET, upper=False),
|
||||||
CONF_MAX_LNA_GAIN: cv.enum(MAX_LNA_GAIN, upper=False),
|
CONF_MAX_LNA_GAIN: cv.enum(MAX_LNA_GAIN, upper=False),
|
||||||
CONF_MAX_DVGA_GAIN: cv.enum(MAX_DVGA_GAIN, upper=False),
|
CONF_MAX_DVGA_GAIN: cv.enum(MAX_DVGA_GAIN, upper=False),
|
||||||
@@ -179,13 +191,36 @@ CONFIG_MAP = {
|
|||||||
CONF_FREEZE: cv.enum(FREEZE, upper=False),
|
CONF_FREEZE: cv.enum(FREEZE, upper=False),
|
||||||
CONF_WAIT_TIME: cv.enum(WAIT_TIME, upper=False),
|
CONF_WAIT_TIME: cv.enum(WAIT_TIME, upper=False),
|
||||||
CONF_HYST_LEVEL: cv.enum(HYST_LEVEL, upper=False),
|
CONF_HYST_LEVEL: cv.enum(HYST_LEVEL, upper=False),
|
||||||
|
CONF_PACKET_MODE: cv.boolean,
|
||||||
|
CONF_PACKET_LENGTH: cv.uint8_t,
|
||||||
|
CONF_CRC_ENABLE: cv.boolean,
|
||||||
|
CONF_WHITENING: cv.boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
|
||||||
cv.Schema({cv.GenerateID(): cv.declare_id(CC1101Component)})
|
def _validate_packet_mode(config):
|
||||||
|
if config.get(CONF_PACKET_MODE, False):
|
||||||
|
if CONF_GDO0_PIN not in config:
|
||||||
|
raise cv.Invalid("gdo0_pin is required when packet_mode is enabled")
|
||||||
|
if CONF_PACKET_LENGTH not in config:
|
||||||
|
raise cv.Invalid("packet_length is required when packet_mode is enabled")
|
||||||
|
if config[CONF_PACKET_LENGTH] > 64:
|
||||||
|
raise cv.Invalid("packet_length must be <= 64 (FIFO size)")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(CC1101Component),
|
||||||
|
cv.Optional(CONF_GDO0_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
|
cv.Optional(CONF_ON_PACKET): automation.validate_automation(single=True),
|
||||||
|
}
|
||||||
|
)
|
||||||
.extend({cv.Optional(key): validator for key, validator in CONFIG_MAP.items()})
|
.extend({cv.Optional(key): validator for key, validator in CONFIG_MAP.items()})
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
.extend(spi.spi_device_schema(cs_pin_required=True))
|
.extend(spi.spi_device_schema(cs_pin_required=True)),
|
||||||
|
_validate_packet_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -198,12 +233,29 @@ async def to_code(config):
|
|||||||
if key in config:
|
if key in config:
|
||||||
cg.add(getattr(var, f"set_{key}")(config[key]))
|
cg.add(getattr(var, f"set_{key}")(config[key]))
|
||||||
|
|
||||||
|
if CONF_GDO0_PIN in config:
|
||||||
|
gdo0_pin = await cg.gpio_pin_expression(config[CONF_GDO0_PIN])
|
||||||
|
cg.add(var.set_gdo0_pin(gdo0_pin))
|
||||||
|
if CONF_ON_PACKET in config:
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_packet_trigger(),
|
||||||
|
[
|
||||||
|
(cg.std_vector.template(cg.uint8), "x"),
|
||||||
|
(cg.float_, "rssi"),
|
||||||
|
(cg.uint8, "lqi"),
|
||||||
|
],
|
||||||
|
config[CONF_ON_PACKET],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Actions
|
# Actions
|
||||||
BeginTxAction = ns.class_("BeginTxAction", automation.Action)
|
BeginTxAction = ns.class_("BeginTxAction", automation.Action)
|
||||||
BeginRxAction = ns.class_("BeginRxAction", automation.Action)
|
BeginRxAction = ns.class_("BeginRxAction", automation.Action)
|
||||||
ResetAction = ns.class_("ResetAction", automation.Action)
|
ResetAction = ns.class_("ResetAction", automation.Action)
|
||||||
SetIdleAction = ns.class_("SetIdleAction", automation.Action)
|
SetIdleAction = ns.class_("SetIdleAction", automation.Action)
|
||||||
|
SendPacketAction = ns.class_(
|
||||||
|
"SendPacketAction", automation.Action, cg.Parented.template(CC1101Component)
|
||||||
|
)
|
||||||
|
|
||||||
CC1101_ACTION_SCHEMA = cv.Schema(
|
CC1101_ACTION_SCHEMA = cv.Schema(
|
||||||
maybe_simple_id({cv.GenerateID(CONF_ID): cv.use_id(CC1101Component)})
|
maybe_simple_id({cv.GenerateID(CONF_ID): cv.use_id(CC1101Component)})
|
||||||
@@ -218,3 +270,42 @@ async def cc1101_action_to_code(config, action_id, template_arg, args):
|
|||||||
var = cg.new_Pvariable(action_id, template_arg)
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
await cg.register_parented(var, config[CONF_ID])
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
def validate_raw_data(value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
return value.encode("utf-8")
|
||||||
|
if isinstance(value, list):
|
||||||
|
return cv.Schema([cv.hex_uint8_t])(value)
|
||||||
|
raise cv.Invalid(
|
||||||
|
"data must either be a string wrapped in quotes or a list of bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
SEND_PACKET_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(CC1101Component),
|
||||||
|
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
|
||||||
|
},
|
||||||
|
key=CONF_DATA,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"cc1101.send_packet", SendPacketAction, SEND_PACKET_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def send_packet_action_to_code(config, action_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
data = config[CONF_DATA]
|
||||||
|
if isinstance(data, bytes):
|
||||||
|
data = list(data)
|
||||||
|
if cg.is_template(data):
|
||||||
|
templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
|
||||||
|
cg.add(var.set_data_template(templ))
|
||||||
|
else:
|
||||||
|
# Generate static array in flash to avoid RAM copy
|
||||||
|
arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8)
|
||||||
|
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data))
|
||||||
|
cg.add(var.set_data_static(arr, len(data)))
|
||||||
|
return var
|
||||||
|
|||||||
@@ -143,6 +143,11 @@ void CC1101Component::setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup GDO0 pin if configured
|
||||||
|
if (this->gdo0_pin_ != nullptr) {
|
||||||
|
this->gdo0_pin_->setup();
|
||||||
|
}
|
||||||
|
|
||||||
this->initialized_ = true;
|
this->initialized_ = true;
|
||||||
|
|
||||||
for (uint8_t i = 0; i <= static_cast<uint8_t>(Register::TEST0); i++) {
|
for (uint8_t i = 0; i <= static_cast<uint8_t>(Register::TEST0); i++) {
|
||||||
@@ -151,8 +156,69 @@ void CC1101Component::setup() {
|
|||||||
}
|
}
|
||||||
this->write_(static_cast<Register>(i));
|
this->write_(static_cast<Register>(i));
|
||||||
}
|
}
|
||||||
this->write_(Register::PATABLE, this->pa_table_, sizeof(this->pa_table_));
|
this->set_output_power(this->output_power_requested_);
|
||||||
this->strobe_(Command::RX);
|
this->strobe_(Command::RX);
|
||||||
|
|
||||||
|
// Defer pin mode setup until after all components have completed setup()
|
||||||
|
// This handles the case where remote_transmitter runs after CC1101 and changes pin mode
|
||||||
|
if (this->gdo0_pin_ != nullptr) {
|
||||||
|
this->defer([this]() { this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CC1101Component::loop() {
|
||||||
|
if (this->state_.PKT_FORMAT != static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO) || this->gdo0_pin_ == nullptr ||
|
||||||
|
!this->gdo0_pin_->digital_read()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read state
|
||||||
|
this->read_(Register::RXBYTES);
|
||||||
|
uint8_t rx_bytes = this->state_.NUM_RXBYTES;
|
||||||
|
bool overflow = this->state_.RXFIFO_OVERFLOW;
|
||||||
|
if (overflow || rx_bytes == 0) {
|
||||||
|
ESP_LOGW(TAG, "RX FIFO overflow, flushing");
|
||||||
|
this->enter_idle_();
|
||||||
|
this->strobe_(Command::FRX);
|
||||||
|
this->strobe_(Command::RX);
|
||||||
|
this->wait_for_state_(State::RX);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read packet
|
||||||
|
uint8_t payload_length;
|
||||||
|
if (this->state_.LENGTH_CONFIG == static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_VARIABLE)) {
|
||||||
|
this->read_(Register::FIFO, &payload_length, 1);
|
||||||
|
} else {
|
||||||
|
payload_length = this->state_.PKTLEN;
|
||||||
|
}
|
||||||
|
if (payload_length == 0 || payload_length > 64) {
|
||||||
|
ESP_LOGW(TAG, "Invalid payload length: %u", payload_length);
|
||||||
|
this->enter_idle_();
|
||||||
|
this->strobe_(Command::FRX);
|
||||||
|
this->strobe_(Command::RX);
|
||||||
|
this->wait_for_state_(State::RX);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->packet_.resize(payload_length);
|
||||||
|
this->read_(Register::FIFO, this->packet_.data(), payload_length);
|
||||||
|
|
||||||
|
// Read status and trigger
|
||||||
|
uint8_t status[2];
|
||||||
|
this->read_(Register::FIFO, status, 2);
|
||||||
|
int8_t rssi_raw = static_cast<int8_t>(status[0]);
|
||||||
|
float rssi = (rssi_raw * RSSI_STEP) - RSSI_OFFSET;
|
||||||
|
bool crc_ok = (status[1] & STATUS_CRC_OK_MASK) != 0;
|
||||||
|
uint8_t lqi = status[1] & STATUS_LQI_MASK;
|
||||||
|
if (this->state_.CRC_EN == 0 || crc_ok) {
|
||||||
|
this->packet_trigger_->trigger(this->packet_, rssi, lqi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return to rx
|
||||||
|
this->enter_idle_();
|
||||||
|
this->strobe_(Command::FRX);
|
||||||
|
this->strobe_(Command::RX);
|
||||||
|
this->wait_for_state_(State::RX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CC1101Component::dump_config() {
|
void CC1101Component::dump_config() {
|
||||||
@@ -177,9 +243,12 @@ void CC1101Component::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CC1101Component::begin_tx() {
|
void CC1101Component::begin_tx() {
|
||||||
// Ensure Packet Format is 3 (Async Serial), use GDO0 as input during TX
|
// Ensure Packet Format is 3 (Async Serial)
|
||||||
this->write_(Register::PKTCTRL0, 0x32);
|
this->write_(Register::PKTCTRL0, 0x32);
|
||||||
ESP_LOGV(TAG, "Beginning TX sequence");
|
ESP_LOGV(TAG, "Beginning TX sequence");
|
||||||
|
if (this->gdo0_pin_ != nullptr) {
|
||||||
|
this->gdo0_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||||
|
}
|
||||||
this->strobe_(Command::TX);
|
this->strobe_(Command::TX);
|
||||||
if (!this->wait_for_state_(State::TX, 50)) {
|
if (!this->wait_for_state_(State::TX, 50)) {
|
||||||
ESP_LOGW(TAG, "Timed out waiting for TX state!");
|
ESP_LOGW(TAG, "Timed out waiting for TX state!");
|
||||||
@@ -188,6 +257,9 @@ void CC1101Component::begin_tx() {
|
|||||||
|
|
||||||
void CC1101Component::begin_rx() {
|
void CC1101Component::begin_rx() {
|
||||||
ESP_LOGV(TAG, "Beginning RX sequence");
|
ESP_LOGV(TAG, "Beginning RX sequence");
|
||||||
|
if (this->gdo0_pin_ != nullptr) {
|
||||||
|
this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||||
|
}
|
||||||
this->strobe_(Command::RX);
|
this->strobe_(Command::RX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,20 +273,6 @@ void CC1101Component::set_idle() {
|
|||||||
this->enter_idle_();
|
this->enter_idle_();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CC1101Component::set_gdo0_config(uint8_t value) {
|
|
||||||
this->state_.GDO0_CFG = value;
|
|
||||||
if (this->initialized_) {
|
|
||||||
this->write_(Register::IOCFG0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CC1101Component::set_gdo2_config(uint8_t value) {
|
|
||||||
this->state_.GDO2_CFG = value;
|
|
||||||
if (this->initialized_) {
|
|
||||||
this->write_(Register::IOCFG2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CC1101Component::wait_for_state_(State target_state, uint32_t timeout_ms) {
|
bool CC1101Component::wait_for_state_(State target_state, uint32_t timeout_ms) {
|
||||||
uint32_t start = millis();
|
uint32_t start = millis();
|
||||||
while (millis() - start < timeout_ms) {
|
while (millis() - start < timeout_ms) {
|
||||||
@@ -282,6 +340,33 @@ void CC1101Component::read_(Register reg, uint8_t *buffer, size_t length) {
|
|||||||
this->disable();
|
this->disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CC1101Error CC1101Component::transmit_packet(const std::vector<uint8_t> &packet) {
|
||||||
|
if (this->state_.PKT_FORMAT != static_cast<uint8_t>(PacketFormat::PACKET_FORMAT_FIFO)) {
|
||||||
|
return CC1101Error::PARAMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write packet
|
||||||
|
this->enter_idle_();
|
||||||
|
this->strobe_(Command::FTX);
|
||||||
|
if (this->state_.LENGTH_CONFIG == static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_VARIABLE)) {
|
||||||
|
this->write_(Register::FIFO, static_cast<uint8_t>(packet.size()));
|
||||||
|
}
|
||||||
|
this->write_(Register::FIFO, packet.data(), packet.size());
|
||||||
|
this->strobe_(Command::TX);
|
||||||
|
if (!this->wait_for_state_(State::IDLE, 1000)) {
|
||||||
|
ESP_LOGW(TAG, "TX timeout");
|
||||||
|
this->enter_idle_();
|
||||||
|
this->strobe_(Command::RX);
|
||||||
|
this->wait_for_state_(State::RX);
|
||||||
|
return CC1101Error::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return to rx
|
||||||
|
this->strobe_(Command::RX);
|
||||||
|
this->wait_for_state_(State::RX);
|
||||||
|
return CC1101Error::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
void CC1101Component::set_output_power(float value) {
|
void CC1101Component::set_output_power(float value) {
|
||||||
this->output_power_requested_ = value;
|
this->output_power_requested_ = value;
|
||||||
@@ -428,6 +513,7 @@ void CC1101Component::set_modulation_type(Modulation value) {
|
|||||||
this->state_.PA_POWER = value == Modulation::MODULATION_ASK_OOK ? 1 : 0;
|
this->state_.PA_POWER = value == Modulation::MODULATION_ASK_OOK ? 1 : 0;
|
||||||
if (this->initialized_) {
|
if (this->initialized_) {
|
||||||
this->enter_idle_();
|
this->enter_idle_();
|
||||||
|
this->set_output_power(this->output_power_requested_);
|
||||||
this->write_(Register::MDMCFG2);
|
this->write_(Register::MDMCFG2);
|
||||||
this->write_(Register::FREND0);
|
this->write_(Register::FREND0);
|
||||||
this->strobe_(Command::RX);
|
this->strobe_(Command::RX);
|
||||||
@@ -462,13 +548,6 @@ void CC1101Component::set_sync0(uint8_t value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CC1101Component::set_pktlen(uint8_t value) {
|
|
||||||
this->state_.PKTLEN = value;
|
|
||||||
if (this->initialized_) {
|
|
||||||
this->write_(Register::PKTLEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CC1101Component::set_magn_target(MagnTarget value) {
|
void CC1101Component::set_magn_target(MagnTarget value) {
|
||||||
this->state_.MAGN_TARGET = static_cast<uint8_t>(value);
|
this->state_.MAGN_TARGET = static_cast<uint8_t>(value);
|
||||||
if (this->initialized_) {
|
if (this->initialized_) {
|
||||||
@@ -546,4 +625,50 @@ void CC1101Component::set_hyst_level(HystLevel value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CC1101Component::set_packet_mode(bool value) {
|
||||||
|
this->state_.PKT_FORMAT =
|
||||||
|
static_cast<uint8_t>(value ? PacketFormat::PACKET_FORMAT_FIFO : PacketFormat::PACKET_FORMAT_ASYNC_SERIAL);
|
||||||
|
if (value) {
|
||||||
|
// Configure GDO0 for FIFO status (asserts on RX FIFO threshold or end of packet)
|
||||||
|
this->state_.GDO0_CFG = 0x01;
|
||||||
|
// Set max RX FIFO threshold to ensure we only trigger on end-of-packet
|
||||||
|
this->state_.FIFO_THR = 15;
|
||||||
|
} else {
|
||||||
|
// Configure GDO0 for serial data (async serial mode)
|
||||||
|
this->state_.GDO0_CFG = 0x0D;
|
||||||
|
}
|
||||||
|
if (this->initialized_) {
|
||||||
|
this->write_(Register::PKTCTRL0);
|
||||||
|
this->write_(Register::IOCFG0);
|
||||||
|
this->write_(Register::FIFOTHR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CC1101Component::set_packet_length(uint8_t value) {
|
||||||
|
if (value == 0) {
|
||||||
|
this->state_.LENGTH_CONFIG = static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_VARIABLE);
|
||||||
|
} else {
|
||||||
|
this->state_.LENGTH_CONFIG = static_cast<uint8_t>(LengthConfig::LENGTH_CONFIG_FIXED);
|
||||||
|
this->state_.PKTLEN = value;
|
||||||
|
}
|
||||||
|
if (this->initialized_) {
|
||||||
|
this->write_(Register::PKTCTRL0);
|
||||||
|
this->write_(Register::PKTLEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CC1101Component::set_crc_enable(bool value) {
|
||||||
|
this->state_.CRC_EN = value ? 1 : 0;
|
||||||
|
if (this->initialized_) {
|
||||||
|
this->write_(Register::PKTCTRL0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CC1101Component::set_whitening(bool value) {
|
||||||
|
this->state_.WHITE_DATA = value ? 1 : 0;
|
||||||
|
if (this->initialized_) {
|
||||||
|
this->write_(Register::PKTCTRL0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace esphome::cc1101
|
} // namespace esphome::cc1101
|
||||||
|
|||||||
@@ -5,9 +5,12 @@
|
|||||||
#include "esphome/components/spi/spi.h"
|
#include "esphome/components/spi/spi.h"
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "cc1101defs.h"
|
#include "cc1101defs.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace esphome::cc1101 {
|
namespace esphome::cc1101 {
|
||||||
|
|
||||||
|
enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW };
|
||||||
|
|
||||||
class CC1101Component : public Component,
|
class CC1101Component : public Component,
|
||||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
||||||
@@ -15,6 +18,7 @@ class CC1101Component : public Component,
|
|||||||
CC1101Component();
|
CC1101Component();
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@@ -24,8 +28,7 @@ class CC1101Component : public Component,
|
|||||||
void set_idle();
|
void set_idle();
|
||||||
|
|
||||||
// GDO Pin Configuration
|
// GDO Pin Configuration
|
||||||
void set_gdo0_config(uint8_t value);
|
void set_gdo0_pin(InternalGPIOPin *pin) { this->gdo0_pin_ = pin; }
|
||||||
void set_gdo2_config(uint8_t value);
|
|
||||||
|
|
||||||
// Configuration Setters
|
// Configuration Setters
|
||||||
void set_output_power(float value);
|
void set_output_power(float value);
|
||||||
@@ -48,7 +51,6 @@ class CC1101Component : public Component,
|
|||||||
void set_num_preamble(uint8_t value);
|
void set_num_preamble(uint8_t value);
|
||||||
void set_sync1(uint8_t value);
|
void set_sync1(uint8_t value);
|
||||||
void set_sync0(uint8_t value);
|
void set_sync0(uint8_t value);
|
||||||
void set_pktlen(uint8_t value);
|
|
||||||
|
|
||||||
// AGC settings
|
// AGC settings
|
||||||
void set_magn_target(MagnTarget value);
|
void set_magn_target(MagnTarget value);
|
||||||
@@ -63,6 +65,16 @@ class CC1101Component : public Component,
|
|||||||
void set_wait_time(WaitTime value);
|
void set_wait_time(WaitTime value);
|
||||||
void set_hyst_level(HystLevel value);
|
void set_hyst_level(HystLevel value);
|
||||||
|
|
||||||
|
// Packet mode settings
|
||||||
|
void set_packet_mode(bool value);
|
||||||
|
void set_packet_length(uint8_t value);
|
||||||
|
void set_crc_enable(bool value);
|
||||||
|
void set_whitening(bool value);
|
||||||
|
|
||||||
|
// Packet mode operations
|
||||||
|
CC1101Error transmit_packet(const std::vector<uint8_t> &packet);
|
||||||
|
Trigger<std::vector<uint8_t>, float, uint8_t> *get_packet_trigger() const { return this->packet_trigger_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t chip_id_{0};
|
uint16_t chip_id_{0};
|
||||||
bool initialized_{false};
|
bool initialized_{false};
|
||||||
@@ -73,6 +85,13 @@ class CC1101Component : public Component,
|
|||||||
|
|
||||||
CC1101State state_;
|
CC1101State state_;
|
||||||
|
|
||||||
|
// GDO pin for packet reception
|
||||||
|
InternalGPIOPin *gdo0_pin_{nullptr};
|
||||||
|
|
||||||
|
// Packet handling
|
||||||
|
Trigger<std::vector<uint8_t>, float, uint8_t> *packet_trigger_{new Trigger<std::vector<uint8_t>, float, uint8_t>()};
|
||||||
|
std::vector<uint8_t> packet_;
|
||||||
|
|
||||||
// Low-level Helpers
|
// Low-level Helpers
|
||||||
uint8_t strobe_(Command cmd);
|
uint8_t strobe_(Command cmd);
|
||||||
void write_(Register reg);
|
void write_(Register reg);
|
||||||
@@ -107,4 +126,28 @@ template<typename... Ts> class SetIdleAction : public Action<Ts...>, public Pare
|
|||||||
void play(const Ts &...x) override { this->parent_->set_idle(); }
|
void play(const Ts &...x) override { this->parent_->set_idle(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class SendPacketAction : public Action<Ts...>, public Parented<CC1101Component> {
|
||||||
|
public:
|
||||||
|
void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) { this->data_func_ = func; }
|
||||||
|
void set_data_static(const uint8_t *data, size_t len) {
|
||||||
|
this->data_static_ = data;
|
||||||
|
this->data_static_len_ = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void play(const Ts &...x) override {
|
||||||
|
if (this->data_func_) {
|
||||||
|
auto data = this->data_func_(x...);
|
||||||
|
this->parent_->transmit_packet(data);
|
||||||
|
} else if (this->data_static_ != nullptr) {
|
||||||
|
std::vector<uint8_t> data(this->data_static_, this->data_static_ + this->data_static_len_);
|
||||||
|
this->parent_->transmit_packet(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
|
||||||
|
const uint8_t *data_static_{nullptr};
|
||||||
|
size_t data_static_len_{0};
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace esphome::cc1101
|
} // namespace esphome::cc1101
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ namespace esphome::cc1101 {
|
|||||||
|
|
||||||
static constexpr float XTAL_FREQUENCY = 26000000;
|
static constexpr float XTAL_FREQUENCY = 26000000;
|
||||||
|
|
||||||
|
static constexpr float RSSI_OFFSET = 74.0f;
|
||||||
|
static constexpr float RSSI_STEP = 0.5f;
|
||||||
|
|
||||||
|
static constexpr uint8_t STATUS_CRC_OK_MASK = 0x80;
|
||||||
|
static constexpr uint8_t STATUS_LQI_MASK = 0x7F;
|
||||||
|
|
||||||
static constexpr uint8_t BUS_BURST = 0x40;
|
static constexpr uint8_t BUS_BURST = 0x40;
|
||||||
static constexpr uint8_t BUS_READ = 0x80;
|
static constexpr uint8_t BUS_READ = 0x80;
|
||||||
static constexpr uint8_t BUS_WRITE = 0x00;
|
static constexpr uint8_t BUS_WRITE = 0x00;
|
||||||
@@ -134,6 +140,10 @@ enum class SyncMode : uint8_t {
|
|||||||
SYNC_MODE_15_16,
|
SYNC_MODE_15_16,
|
||||||
SYNC_MODE_16_16,
|
SYNC_MODE_16_16,
|
||||||
SYNC_MODE_30_32,
|
SYNC_MODE_30_32,
|
||||||
|
SYNC_MODE_NONE_CS,
|
||||||
|
SYNC_MODE_15_16_CS,
|
||||||
|
SYNC_MODE_16_16_CS,
|
||||||
|
SYNC_MODE_30_32_CS,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Modulation : uint8_t {
|
enum class Modulation : uint8_t {
|
||||||
@@ -218,6 +228,19 @@ enum class HystLevel : uint8_t {
|
|||||||
HYST_LEVEL_HIGH,
|
HYST_LEVEL_HIGH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class PacketFormat : uint8_t {
|
||||||
|
PACKET_FORMAT_FIFO,
|
||||||
|
PACKET_FORMAT_SYNC_SERIAL,
|
||||||
|
PACKET_FORMAT_RANDOM_TX,
|
||||||
|
PACKET_FORMAT_ASYNC_SERIAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LengthConfig : uint8_t {
|
||||||
|
LENGTH_CONFIG_FIXED,
|
||||||
|
LENGTH_CONFIG_VARIABLE,
|
||||||
|
LENGTH_CONFIG_INFINITE,
|
||||||
|
};
|
||||||
|
|
||||||
struct __attribute__((packed)) CC1101State {
|
struct __attribute__((packed)) CC1101State {
|
||||||
// Byte array accessors for bulk SPI transfers
|
// Byte array accessors for bulk SPI transfers
|
||||||
uint8_t *regs() { return reinterpret_cast<uint8_t *>(this); }
|
uint8_t *regs() { return reinterpret_cast<uint8_t *>(this); }
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ BYTE_ORDER_LITTLE = "little_endian"
|
|||||||
BYTE_ORDER_BIG = "big_endian"
|
BYTE_ORDER_BIG = "big_endian"
|
||||||
|
|
||||||
CONF_COLOR_DEPTH = "color_depth"
|
CONF_COLOR_DEPTH = "color_depth"
|
||||||
|
CONF_CRC_ENABLE = "crc_enable"
|
||||||
CONF_DRAW_ROUNDING = "draw_rounding"
|
CONF_DRAW_ROUNDING = "draw_rounding"
|
||||||
CONF_ENABLED = "enabled"
|
CONF_ENABLED = "enabled"
|
||||||
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
|
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
|
||||||
|
CONF_ON_PACKET = "on_packet"
|
||||||
CONF_ON_RECEIVE = "on_receive"
|
CONF_ON_RECEIVE = "on_receive"
|
||||||
CONF_ON_STATE_CHANGE = "on_state_change"
|
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||||
CONF_REQUEST_HEADERS = "request_headers"
|
CONF_REQUEST_HEADERS = "request_headers"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from esphome import automation, pins
|
from esphome import automation, pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import spi
|
from esphome.components import spi
|
||||||
|
from esphome.components.const import CONF_CRC_ENABLE, CONF_ON_PACKET
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_BUSY_PIN, CONF_DATA, CONF_FREQUENCY, CONF_ID
|
from esphome.const import CONF_BUSY_PIN, CONF_DATA, CONF_FREQUENCY, CONF_ID
|
||||||
from esphome.core import ID, TimePeriod
|
from esphome.core import ID, TimePeriod
|
||||||
@@ -14,7 +15,6 @@ CONF_SX126X_ID = "sx126x_id"
|
|||||||
CONF_BANDWIDTH = "bandwidth"
|
CONF_BANDWIDTH = "bandwidth"
|
||||||
CONF_BITRATE = "bitrate"
|
CONF_BITRATE = "bitrate"
|
||||||
CONF_CODING_RATE = "coding_rate"
|
CONF_CODING_RATE = "coding_rate"
|
||||||
CONF_CRC_ENABLE = "crc_enable"
|
|
||||||
CONF_CRC_INVERTED = "crc_inverted"
|
CONF_CRC_INVERTED = "crc_inverted"
|
||||||
CONF_CRC_SIZE = "crc_size"
|
CONF_CRC_SIZE = "crc_size"
|
||||||
CONF_CRC_POLYNOMIAL = "crc_polynomial"
|
CONF_CRC_POLYNOMIAL = "crc_polynomial"
|
||||||
@@ -23,7 +23,6 @@ CONF_DEVIATION = "deviation"
|
|||||||
CONF_DIO1_PIN = "dio1_pin"
|
CONF_DIO1_PIN = "dio1_pin"
|
||||||
CONF_HW_VERSION = "hw_version"
|
CONF_HW_VERSION = "hw_version"
|
||||||
CONF_MODULATION = "modulation"
|
CONF_MODULATION = "modulation"
|
||||||
CONF_ON_PACKET = "on_packet"
|
|
||||||
CONF_PA_POWER = "pa_power"
|
CONF_PA_POWER = "pa_power"
|
||||||
CONF_PA_RAMP = "pa_ramp"
|
CONF_PA_RAMP = "pa_ramp"
|
||||||
CONF_PAYLOAD_LENGTH = "payload_length"
|
CONF_PAYLOAD_LENGTH = "payload_length"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from esphome import automation, pins
|
from esphome import automation, pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import spi
|
from esphome.components import spi
|
||||||
|
from esphome.components.const import CONF_CRC_ENABLE, CONF_ON_PACKET
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_DATA, CONF_FREQUENCY, CONF_ID
|
from esphome.const import CONF_DATA, CONF_FREQUENCY, CONF_ID
|
||||||
from esphome.core import ID
|
from esphome.core import ID
|
||||||
@@ -16,11 +17,9 @@ CONF_BANDWIDTH = "bandwidth"
|
|||||||
CONF_BITRATE = "bitrate"
|
CONF_BITRATE = "bitrate"
|
||||||
CONF_BITSYNC = "bitsync"
|
CONF_BITSYNC = "bitsync"
|
||||||
CONF_CODING_RATE = "coding_rate"
|
CONF_CODING_RATE = "coding_rate"
|
||||||
CONF_CRC_ENABLE = "crc_enable"
|
|
||||||
CONF_DEVIATION = "deviation"
|
CONF_DEVIATION = "deviation"
|
||||||
CONF_DIO0_PIN = "dio0_pin"
|
CONF_DIO0_PIN = "dio0_pin"
|
||||||
CONF_MODULATION = "modulation"
|
CONF_MODULATION = "modulation"
|
||||||
CONF_ON_PACKET = "on_packet"
|
|
||||||
CONF_PA_PIN = "pa_pin"
|
CONF_PA_PIN = "pa_pin"
|
||||||
CONF_PA_POWER = "pa_power"
|
CONF_PA_POWER = "pa_power"
|
||||||
CONF_PA_RAMP = "pa_ramp"
|
CONF_PA_RAMP = "pa_ramp"
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
cc1101:
|
cc1101:
|
||||||
id: transceiver
|
id: transceiver
|
||||||
cs_pin: ${cs_pin}
|
cs_pin: ${cs_pin}
|
||||||
|
gdo0_pin: ${gdo0_pin}
|
||||||
frequency: 433.92MHz
|
frequency: 433.92MHz
|
||||||
if_frequency: 153kHz
|
if_frequency: 153kHz
|
||||||
filter_bandwidth: 203kHz
|
filter_bandwidth: 203kHz
|
||||||
channel: 0
|
channel: 0
|
||||||
channel_spacing: 200kHz
|
channel_spacing: 200kHz
|
||||||
symbol_rate: 5000
|
symbol_rate: 4800
|
||||||
modulation_type: ASK/OOK
|
modulation_type: GFSK
|
||||||
|
packet_mode: true
|
||||||
|
packet_length: 8
|
||||||
|
crc_enable: true
|
||||||
|
whitening: false
|
||||||
|
sync_mode: "16/16"
|
||||||
|
sync0: 0x91
|
||||||
|
sync1: 0xD3
|
||||||
|
num_preamble: 2
|
||||||
|
on_packet:
|
||||||
|
then:
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGD("cc1101", "packet %s rssi %.1f dBm lqi %u", format_hex(x).c_str(), rssi, lqi);
|
||||||
|
|
||||||
button:
|
button:
|
||||||
- platform: template
|
- platform: template
|
||||||
@@ -18,3 +31,7 @@ button:
|
|||||||
- cc1101.begin_rx: transceiver
|
- cc1101.begin_rx: transceiver
|
||||||
- cc1101.set_idle: transceiver
|
- cc1101.set_idle: transceiver
|
||||||
- cc1101.reset: transceiver
|
- cc1101.reset: transceiver
|
||||||
|
- cc1101.send_packet:
|
||||||
|
data: [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]
|
||||||
|
- cc1101.send_packet: !lambda |-
|
||||||
|
return {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
substitutions:
|
substitutions:
|
||||||
cs_pin: GPIO5
|
cs_pin: GPIO5
|
||||||
|
gdo0_pin: GPIO4
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
|
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
|
||||||
remote_receiver: !include ../../test_build_components/common/remote_receiver/esp32-idf.yaml
|
|
||||||
|
|
||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
substitutions:
|
substitutions:
|
||||||
cs_pin: GPIO5
|
cs_pin: GPIO5
|
||||||
|
gdo0_pin: GPIO4
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml
|
spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml
|
||||||
remote_receiver: !include ../../test_build_components/common/remote_receiver/esp8266-ard.yaml
|
|
||||||
|
|
||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|||||||
Reference in New Issue
Block a user