diff --git a/esphome/components/cc1101/__init__.py b/esphome/components/cc1101/__init__.py index 8f3a0897e2..a10df9841f 100644 --- a/esphome/components/cc1101/__init__.py +++ b/esphome/components/cc1101/__init__.py @@ -5,23 +5,28 @@ from esphome.automation import maybe_simple_id from esphome.components import sensor from esphome.components import spi from esphome.components import remote_base +from esphome.components import voltage_sampler from esphome.const import ( CONF_ID, CONF_FREQUENCY, CONF_PROTOCOL, CONF_CODE, + CONF_TEMPERATURE, UNIT_EMPTY, UNIT_DECIBEL_MILLIWATT, + UNIT_CELSIUS, DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT, ) DEPENDENCIES = ["spi"] -AUTO_LOAD = ["sensor", "remote_base"] +AUTO_LOAD = ["sensor", "remote_base", "voltage_sampler"] CODEOWNERS = ["@gabest11"] -CONF_GDO0 = "gdo0" +CONF_GDO0_PIN = "gdo0_pin" +CONF_GDO0_ADC_ID = "gdo0_adc_id" CONF_BANDWIDTH = "bandwidth" # CONF_FREQUENCY = "frequency" CONF_RSSI = "rssi" @@ -36,7 +41,8 @@ CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(CC1101), - cv.Optional(CONF_GDO0): pins.gpio_output_pin_schema, + cv.Optional(CONF_GDO0_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_GDO0_ADC_ID): cv.use_id(voltage_sampler.VoltageSampler), cv.Optional(CONF_BANDWIDTH, default=200): cv.uint32_t, cv.Optional(CONF_FREQUENCY, default=433920): cv.uint32_t, cv.Optional(CONF_RSSI): sensor.sensor_schema( @@ -50,6 +56,12 @@ CONFIG_SCHEMA = ( accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), } ) .extend(cv.polling_component_schema("60s")) @@ -62,9 +74,12 @@ async def to_code(config): await cg.register_component(var, config) await spi.register_spi_device(var, config) - if CONF_GDO0 in config: - gdo0 = await cg.gpio_pin_expression(config[CONF_GDO0]) - cg.add(var.set_config_gdo0(gdo0)) + if CONF_GDO0_PIN in config: + gdo0_pin = await cg.gpio_pin_expression(config[CONF_GDO0_PIN]) + cg.add(var.set_config_gdo0_pin(gdo0_pin)) + if CONF_GDO0_ADC_ID in config: + gdo0_adc_id = await cg.get_variable(config[CONF_GDO0_ADC_ID]) + cg.add(var.set_config_gdo0_adc_pin(gdo0_adc_id)) cg.add(var.set_config_bandwidth(config[CONF_BANDWIDTH])) cg.add(var.set_config_frequency(config[CONF_FREQUENCY])) if CONF_RSSI in config: @@ -73,6 +88,9 @@ async def to_code(config): if CONF_LQI in config: lqi = await sensor.new_sensor(config[CONF_LQI]) cg.add(var.set_config_lqi_sensor(lqi)) + if CONF_TEMPERATURE in config: + temperature = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_config_temperature_sensor(temperature)) BeginTxAction = ns.class_("BeginTxAction", automation.Action) diff --git a/esphome/components/cc1101/cc1101.cpp b/esphome/components/cc1101/cc1101.cpp index 46f8475eb2..4c94602c8d 100644 --- a/esphome/components/cc1101/cc1101.cpp +++ b/esphome/components/cc1101/cc1101.cpp @@ -38,6 +38,9 @@ int32_t map(int32_t x, int32_t in_min, int32_t in_max, int32_t out_min, int32_t return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } #endif +int32_t mapfloat(float x, float in_min, float in_max, float out_min, float out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} namespace esphome { namespace cc1101 { @@ -54,15 +57,18 @@ static const uint8_t PA_TABLE_915[10]{0x03, 0x0E, 0x1E, 0x27, 0x38, 0x8E, 0x84, CC1101::CC1101() { this->gdo0_ = nullptr; + this->gdo0_adc_ = nullptr; this->bandwidth_ = 200; this->frequency_ = 433920; this->rssi_sensor_ = nullptr; this->lqi_sensor_ = nullptr; + this->temperature_sensor_ = nullptr; this->partnum_ = 0; this->version_ = 0; this->last_rssi_ = INT_MIN; this->last_lqi_ = INT_MIN; + this->last_temperature_ = NAN; this->mode_ = false; this->modulation_ = 2; @@ -85,7 +91,9 @@ CC1101::CC1101() { this->pa_table_[1] = 0xc0; } -void CC1101::set_config_gdo0(InternalGPIOPin *pin) { gdo0_ = pin; } +void CC1101::set_config_gdo0_pin(InternalGPIOPin *pin) { gdo0_ = pin; } + +void CC1101::set_config_gdo0_adc_pin(voltage_sampler::VoltageSampler *pin) { gdo0_adc_ = pin; } void CC1101::set_config_bandwidth(uint32_t bandwidth) { bandwidth_ = bandwidth; } @@ -95,10 +103,19 @@ void CC1101::set_config_rssi_sensor(sensor::Sensor *rssi_sensor) { rssi_sensor_ void CC1101::set_config_lqi_sensor(sensor::Sensor *lqi_sensor) { lqi_sensor_ = lqi_sensor; } +void CC1101::set_config_temperature_sensor(sensor::Sensor *temperature_sensor) { + temperature_sensor_ = temperature_sensor; +} + void CC1101::setup() { if (this->gdo0_ != nullptr) { +#ifdef USE_ESP8266 + // ESP8266 GDO0 generally input, switched to output for TX + // ESP32 GDO0 output, GDO2 input + // if there is an ADC, GDO0 is input only while reading temperature this->gdo0_->setup(); this->gdo0_->pin_mode(gpio::FLAG_INPUT); +#endif } this->spi_setup(); @@ -149,7 +166,7 @@ void CC1101::setup() { // - this->set_rx_(); + this->set_state_(CC1101_SRX); // @@ -159,23 +176,30 @@ void CC1101::setup() { void CC1101::update() { if (this->rssi_sensor_ != nullptr) { int32_t rssi = this->get_rssi_(); - + ESP_LOGV(TAG, "rssi = %d", rssi); if (rssi != this->last_rssi_) { this->rssi_sensor_->publish_state(rssi); - this->last_rssi_ = rssi; } } if (this->lqi_sensor_ != nullptr) { int32_t lqi = this->get_lqi_() & 0x7f; // msb = CRC ok or not set - + ESP_LOGV(TAG, "lqi = %d", lqi); if (lqi != this->last_lqi_) { this->lqi_sensor_->publish_state(lqi); - this->last_lqi_ = lqi; } } + + if (this->temperature_sensor_ != nullptr && this->gdo0_ != nullptr && this->gdo0_adc_ != nullptr) { + float temperature = this->get_temperature_(); + ESP_LOGV(TAG, "temperature = %.2f", temperature); + if (temperature != NAN && temperature != this->last_temperature_) { + this->temperature_sensor_->publish_state(temperature); + this->last_temperature_ = temperature; + } + } } void CC1101::dump_config() { @@ -186,25 +210,14 @@ void CC1101::dump_config() { ESP_LOGCONFIG(TAG, " CC1101 Frequency: %d KHz", this->frequency_); LOG_SENSOR(" ", "RSSI", this->rssi_sensor_); LOG_SENSOR(" ", "LQI", this->lqi_sensor_); + LOG_SENSOR(" ", "Temperature sensor", this->temperature_sensor_); } bool CC1101::reset_() { - // Chip reset sequence. CS wiggle (CC1101 manual page 45) - - // this->disable(); // esp-idf calls end_transaction and asserts, because no begin_transaction was called - this->cs_->digital_write(false); - delayMicroseconds(5); - // this->enable(); - this->cs_->digital_write(true); - delayMicroseconds(10); - // this->disable(); - this->cs_->digital_write(false); - delayMicroseconds(41); - - this->send_cmd_(CC1101_SRES); - ESP_LOGD(TAG, "Issued CC1101 reset sequence."); + this->set_state_(CC1101_SRES); + // Read part number and version this->partnum_ = this->read_status_register_(CC1101_PARTNUM); @@ -215,15 +228,15 @@ bool CC1101::reset_() { return this->version_ > 0; } -void CC1101::send_cmd_(uint8_t cmd) { +void CC1101::strobe_(uint8_t cmd) { this->enable(); - this->transfer_byte(cmd); + this->write_byte(cmd); this->disable(); } uint8_t CC1101::read_register_(uint8_t reg) { this->enable(); - this->transfer_byte(reg); + this->write_byte(reg); uint8_t value = this->transfer_byte(0); this->disable(); return value; @@ -241,7 +254,7 @@ void CC1101::read_register_burst_(uint8_t reg, uint8_t *buffer, size_t length) { } void CC1101::write_register_(uint8_t reg, uint8_t *value, size_t length) { this->enable(); - this->transfer_byte(reg); + this->write_byte(reg); this->transfer_array(value, length); this->disable(); } @@ -267,18 +280,9 @@ bool CC1101::send_data_(const uint8_t* data, size_t length) this->write_register_burst_(CC1101_TXFIFO, buffer, length); - this->send_cmd_(CC1101_STX); + this->strobe_(CC1101_STX); - uint8_t state = this->read_status_register_(CC1101_MARCSTATE) & 0x1f; - - if(state != CC1101_MARCSTATE_TX && state != CC1101_MARCSTATE_TX_END && state != CC1101_MARCSTATE_RXTX_SWITCH) - { - ESP_LOGE(TAG, "CC1101 in invalid state after sending, returning to idle. State: 0x%02x", state); - this->send_cmd_(CC1101_SIDLE); - return false; - } - - return true; + return this->wait_state(CC1101_STX); } */ @@ -294,6 +298,80 @@ int32_t CC1101::get_rssi_() { uint8_t CC1101::get_lqi_() { return this->read_status_register_(CC1101_LQI); } +float CC1101::get_temperature_() { + if (this->gdo0_ == nullptr || this->gdo0_adc_ == nullptr) { + ESP_LOGE(TAG, "cannot read temperature if GDO0_ADC is not set"); + return NAN; + } + + uint8_t trxstate = this->trxstate_; + +#ifndef USE_ESP8266 + this->gdo0_->pin_mode(gpio::FLAG_INPUT); +#endif + + // datasheet 11.2, 26 + + this->set_state_(CC1101_SIDLE); + + this->write_register_(CC1101_IOCFG0, 0x80); + this->write_register_(CC1101_PTEST, 0xBF); + + delay(50); // TODO + + float voltage = 0.0f; + int successful_samples = 0; + + for (uint8_t i = 0, num_samples = 3; i < num_samples; i++) { + float voltage_reading = this->gdo0_adc_->sample(); + ESP_LOGV(TAG, "ADC voltage_reading = %f", voltage_reading); + if (std::isfinite(voltage_reading) && voltage_reading > 0.6 && voltage_reading < 1.0) { + voltage += voltage_reading; + successful_samples++; + } + } + + this->write_register_(CC1101_PTEST, 0x7F); + + if (this->mode_) { + this->write_register_(CC1101_IOCFG0, 0x06); + } else { + this->write_register_(CC1101_IOCFG0, 0x0D); + } + +#ifndef USE_ESP8266 + this->gdo0_->pin_mode(gpio::FLAG_OUTPUT); +#endif + + switch (trxstate) { + case CC1101_STX: + this->set_state_(CC1101_STX); + break; + case CC1101_SRX: + this->set_state_(CC1101_SRX); + break; + default: + this->set_state_(CC1101_SIDLE); + break; + } + + if (successful_samples == 0) { + return NAN; + } + + float v = voltage * 1000 / static_cast(successful_samples); + + if (v >= 651 && v < 747) { + return mapfloat(v, 651, 747, -40, 0); + } else if (v >= 747 && v < 847) { + return mapfloat(v, 747, 847, 0, 40); + } else if (v >= 847 && v < 945) { + return mapfloat(v, 847, 945, 40, 80); + } + + return NAN; +} + void CC1101::set_mode_(bool s) { this->mode_ = s; @@ -594,34 +672,62 @@ void CC1101::set_rxbw_(uint32_t bw) { this->write_register_(CC1101_MDMCFG4, this->m4rxbw_ + this->m4dara_); } -void CC1101::set_tx_() { - ESP_LOGD(TAG, "CC1101 set_tx"); - this->send_cmd_(CC1101_SIDLE); - this->send_cmd_(CC1101_STX); - this->trxstate_ = 1; +void CC1101::set_state_(uint8_t state) { + if (state == CC1101_STX || state == CC1101_SRX || state == CC1101_SPWD) { + this->set_state_(CC1101_SIDLE); + } + + ESP_LOGV(TAG, "set_state_(0x%02x)", state); + + this->trxstate_ = state; + + if (state == CC1101_SRES) { + // datasheet 19.1.2 + // this->disable(); // esp-idf calls end_transaction and asserts, because no begin_transaction was called + this->cs_->digital_write(false); + delayMicroseconds(5); + // this->enable(); + this->cs_->digital_write(true); + delayMicroseconds(10); + // this->disable(); + this->cs_->digital_write(false); + delayMicroseconds(41); + } + + this->strobe_(state); + this->wait_state_(state); } -void CC1101::set_rx_() { - ESP_LOGD(TAG, "CC1101 set_rx"); - this->send_cmd_(CC1101_SIDLE); - this->send_cmd_(CC1101_SRX); - this->trxstate_ = 2; -} +bool CC1101::wait_state_(uint8_t state) { + static constexpr uint16_t timeout_limit = 5000; + uint16_t timeout = timeout_limit; -void CC1101::set_sres_() { - this->send_cmd_(CC1101_SRES); - this->trxstate_ = 0; -} + while (timeout > 0) { + uint8_t s = this->read_status_register_(CC1101_MARCSTATE) & 0x1f; + if (state == CC1101_SIDLE) { + if (s == CC1101_MARCSTATE_IDLE) + break; + } else if (state == CC1101_SRX) { + if (s == CC1101_MARCSTATE_RX || s == CC1101_MARCSTATE_RX_END || s == CC1101_MARCSTATE_RXTX_SWITCH) + break; + } else if (state == CC1101_STX) { + if (s == CC1101_MARCSTATE_TX || s == CC1101_MARCSTATE_TX_END || s == CC1101_MARCSTATE_TXRX_SWITCH) + break; + } else { + break; // else if TODO + } -void CC1101::set_sidle_() { - this->send_cmd_(CC1101_SIDLE); - this->trxstate_ = 0; -} + timeout--; -void CC1101::set_sleep_() { - this->send_cmd_(CC1101_SIDLE); // Exit RX / TX, turn off frequency synthesizer and exit - this->send_cmd_(CC1101_SPWD); // Enter power down mode when CSn goes high. - this->trxstate_ = 0; + delayMicroseconds(1); + } + + if (timeout < timeout_limit) { + ESP_LOGW(TAG, "wait_state_(0x%02x) timeout = %d/%d", state, timeout, timeout_limit); + delayMicroseconds(100); + } + + return timeout > 0; } void CC1101::split_mdmcfg2_() { @@ -641,7 +747,7 @@ void CC1101::split_mdmcfg4_() { } void CC1101::begin_tx() { - this->set_tx_(); + this->set_state_(CC1101_STX); if (this->gdo0_ != nullptr) { #ifdef USE_ESP8266 @@ -649,9 +755,9 @@ void CC1101::begin_tx() { noInterrupts(); // NOLINT #else // USE_ESP_IDF portDISABLE_INTERRUPTS() -#endif #endif this->gdo0_->pin_mode(gpio::FLAG_OUTPUT); +#endif } } @@ -662,13 +768,12 @@ void CC1101::end_tx() { interrupts(); // NOLINT #else // USE_ESP_IDF portENABLE_INTERRUPTS() -#endif #endif this->gdo0_->pin_mode(gpio::FLAG_INPUT); +#endif } - this->set_rx_(); - this->set_rx_(); // yes, twice (really?) + this->set_state_(CC1101_SRX); } } // namespace cc1101 diff --git a/esphome/components/cc1101/cc1101.h b/esphome/components/cc1101/cc1101.h index ca7c302415..b5fff996ac 100644 --- a/esphome/components/cc1101/cc1101.h +++ b/esphome/components/cc1101/cc1101.h @@ -4,6 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/spi/spi.h" #include "esphome/components/remote_base/rc_switch_protocol.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" namespace esphome { namespace cc1101 { @@ -13,18 +14,21 @@ class CC1101 : public PollingComponent, spi::DATA_RATE_1KHZ> { protected: InternalGPIOPin *gdo0_; + voltage_sampler::VoltageSampler *gdo0_adc_; uint32_t bandwidth_; uint32_t frequency_; sensor::Sensor *rssi_sensor_; sensor::Sensor *lqi_sensor_; + sensor::Sensor *temperature_sensor_; uint8_t partnum_; uint8_t version_; int32_t last_rssi_; int32_t last_lqi_; + float last_temperature_; bool reset_(); - void send_cmd_(uint8_t cmd); + void strobe_(uint8_t cmd); uint8_t read_register_(uint8_t reg); uint8_t read_config_register_(uint8_t reg); uint8_t read_status_register_(uint8_t reg); @@ -57,6 +61,7 @@ class CC1101 : public PollingComponent, int32_t get_rssi_(); uint8_t get_lqi_(); + float get_temperature_(); void set_mode_(bool s); void set_frequency_(uint32_t f); @@ -64,11 +69,8 @@ class CC1101 : public PollingComponent, void set_pa_(int8_t pa); void set_clb_(uint8_t b, uint8_t s, uint8_t e); void set_rxbw_(uint32_t bw); - void set_tx_(); - void set_rx_(); - void set_sres_(); - void set_sidle_(); - void set_sleep_(); + void set_state_(uint8_t state); + bool wait_state_(uint8_t state); void split_mdmcfg2_(); void split_mdmcfg4_(); @@ -76,11 +78,13 @@ class CC1101 : public PollingComponent, public: CC1101(); - void set_config_gdo0(InternalGPIOPin *pin); + void set_config_gdo0_pin(InternalGPIOPin *pin); + void set_config_gdo0_adc_pin(voltage_sampler::VoltageSampler *pin); void set_config_bandwidth(uint32_t bandwidth); void set_config_frequency(uint32_t frequency); void set_config_rssi_sensor(sensor::Sensor *rssi_sensor); void set_config_lqi_sensor(sensor::Sensor *lqi_sensor); + void set_config_temperature_sensor(sensor::Sensor *temperature_sensor); void setup() override; void update() override; diff --git a/esphome/components/cc1101/cc1101defs.h b/esphome/components/cc1101/cc1101defs.h index ad068e7ec9..77b9754a3f 100644 --- a/esphome/components/cc1101/cc1101defs.h +++ b/esphome/components/cc1101/cc1101defs.h @@ -102,6 +102,12 @@ static constexpr uint32_t CC1101_READ_SINGLE = 0x80; // read single static constexpr uint32_t CC1101_READ_BURST = 0xC0; // read burst static constexpr uint32_t CC1101_BYTES_IN_RXFIFO = 0x7F; // byte number in RXfifo +static constexpr uint32_t CC1101_MARCSTATE_IDLE = 0x01; + +static constexpr uint32_t CC1101_MARCSTATE_RX = 0x0D; +static constexpr uint32_t CC1101_MARCSTATE_RX_END = 0x0E; +static constexpr uint32_t CC1101_MARCSTATE_TXRX_SWITCH = 0x0F; + static constexpr uint32_t CC1101_MARCSTATE_TX = 0x13; static constexpr uint32_t CC1101_MARCSTATE_TX_END = 0x14; static constexpr uint32_t CC1101_MARCSTATE_RXTX_SWITCH = 0x15;