From 484f4b3aadbcf9e97c21bbdaf35265946b7fda9c Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:34:28 -0500 Subject: [PATCH] [cc1101] Add PLL lock verification and retry support (#13006) --- esphome/components/cc1101/cc1101.cpp | 67 +++++++++++++++++++------- esphome/components/cc1101/cc1101.h | 5 +- esphome/components/cc1101/cc1101defs.h | 3 ++ 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/esphome/components/cc1101/cc1101.cpp b/esphome/components/cc1101/cc1101.cpp index 5727d485f6..c4507a54e5 100644 --- a/esphome/components/cc1101/cc1101.cpp +++ b/esphome/components/cc1101/cc1101.cpp @@ -140,7 +140,10 @@ void CC1101Component::setup() { this->write_(static_cast(i)); } this->set_output_power(this->output_power_requested_); - this->strobe_(Command::RX); + if (!this->enter_rx_()) { + this->mark_failed(); + return; + } // 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 @@ -163,8 +166,7 @@ void CC1101Component::loop() { ESP_LOGW(TAG, "RX FIFO overflow, flushing"); this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return; } @@ -181,8 +183,7 @@ void CC1101Component::loop() { ESP_LOGW(TAG, "Invalid packet: rx_bytes %u, payload_length %u", rx_bytes, payload_length); this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return; } this->packet_.resize(payload_length); @@ -203,8 +204,7 @@ void CC1101Component::loop() { // Return to rx this->enter_idle_(); this->strobe_(Command::FRX); - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); } void CC1101Component::dump_config() { @@ -235,9 +235,8 @@ void CC1101Component::begin_tx() { if (this->gdo0_pin_ != nullptr) { this->gdo0_pin_->pin_mode(gpio::FLAG_OUTPUT); } - this->strobe_(Command::TX); - if (!this->wait_for_state_(State::TX, 50)) { - ESP_LOGW(TAG, "Timed out waiting for TX state!"); + if (!this->enter_tx_()) { + ESP_LOGW(TAG, "Failed to enter TX state!"); } } @@ -246,7 +245,9 @@ void CC1101Component::begin_rx() { if (this->gdo0_pin_ != nullptr) { this->gdo0_pin_->pin_mode(gpio::FLAG_INPUT); } - this->strobe_(Command::RX); + if (!this->enter_rx_()) { + ESP_LOGW(TAG, "Failed to enter RX state!"); + } } void CC1101Component::reset() { @@ -272,11 +273,33 @@ bool CC1101Component::wait_for_state_(State target_state, uint32_t timeout_ms) { return false; } +bool CC1101Component::enter_calibrated_(State target_state, Command cmd) { + // The PLL must be recalibrated until PLL lock is achieved + for (uint8_t retries = PLL_LOCK_RETRIES; retries > 0; retries--) { + this->strobe_(cmd); + if (!this->wait_for_state_(target_state)) { + return false; + } + this->read_(Register::FSCAL1); + if (this->state_.FSCAL1 != FSCAL1_PLL_NOT_LOCKED) { + return true; + } + ESP_LOGW(TAG, "PLL lock failed, retrying calibration"); + this->enter_idle_(); + } + ESP_LOGE(TAG, "PLL lock failed after retries"); + return false; +} + void CC1101Component::enter_idle_() { this->strobe_(Command::IDLE); this->wait_for_state_(State::IDLE); } +bool CC1101Component::enter_rx_() { return this->enter_calibrated_(State::RX, Command::RX); } + +bool CC1101Component::enter_tx_() { return this->enter_calibrated_(State::TX, Command::TX); } + uint8_t CC1101Component::strobe_(Command cmd) { uint8_t index = static_cast(cmd); if (cmd < Command::RES || cmd > Command::NOP) { @@ -338,18 +361,26 @@ CC1101Error CC1101Component::transmit_packet(const std::vector &packet) this->write_(Register::FIFO, static_cast(packet.size())); } this->write_(Register::FIFO, packet.data(), packet.size()); + + // Calibrate PLL + if (!this->enter_calibrated_(State::FSTXON, Command::FSTXON)) { + ESP_LOGW(TAG, "PLL lock failed during TX"); + this->enter_idle_(); + this->enter_rx_(); + return CC1101Error::PLL_LOCK; + } + + // Transmit packet 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); + this->enter_rx_(); return CC1101Error::TIMEOUT; } // Return to rx - this->strobe_(Command::RX); - this->wait_for_state_(State::RX); + this->enter_rx_(); return CC1101Error::NONE; } @@ -406,7 +437,7 @@ void CC1101Component::set_frequency(float value) { this->write_(Register::FREQ2); this->write_(Register::FREQ1); this->write_(Register::FREQ0); - this->strobe_(Command::RX); + this->enter_rx_(); } } @@ -433,7 +464,7 @@ void CC1101Component::set_channel(uint8_t value) { if (this->initialized_) { this->enter_idle_(); this->write_(Register::CHANNR); - this->strobe_(Command::RX); + this->enter_rx_(); } } @@ -502,7 +533,7 @@ void CC1101Component::set_modulation_type(Modulation value) { this->set_output_power(this->output_power_requested_); this->write_(Register::MDMCFG2); this->write_(Register::FREND0); - this->strobe_(Command::RX); + this->enter_rx_(); } } diff --git a/esphome/components/cc1101/cc1101.h b/esphome/components/cc1101/cc1101.h index 9b8d4e56a8..43ae5b3612 100644 --- a/esphome/components/cc1101/cc1101.h +++ b/esphome/components/cc1101/cc1101.h @@ -9,7 +9,7 @@ namespace esphome::cc1101 { -enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW }; +enum class CC1101Error { NONE = 0, TIMEOUT, PARAMS, CRC_ERROR, FIFO_OVERFLOW, PLL_LOCK }; class CC1101Component : public Component, public spi::SPIDevice