ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
modem_component.h
Go to the documentation of this file.
1#pragma once
2#ifdef USE_ESP32
3
5#include "esphome/core/gpio.h"
6#ifdef USE_MODEM_URC
8#endif
12#ifdef USE_MODEM_URC
14#endif
15
16#include <cxx_include/esp_modem_api.hpp>
17#include <esp_modem_config.h>
18
19#include <atomic>
20#include <memory>
21#include <string>
22#include <utility>
23
24namespace esphome::modem {
25
35
36// CONNECTING runs its long commands once; the later passes only poll the attachment
42
43enum class SimResult : uint8_t {
45 SIM_RESULT_RETRY, // status not readable yet, or a PIN step is in progress
46 SIM_RESULT_LOCKED, // nothing more to try: PUK, another lock, no pin_code or the PIN is not sent again
47 SIM_RESULT_HALT, // stop this attempt; a reboot may still open the SIM, so the reboot timer stays
48};
49
50// The step that failed most recently; CONNECTED clears it
65
66#ifdef USE_MODEM_PIN_CODE
67enum class PinStep : uint8_t {
69 PIN_STEP_SAVING, // the send is counted and waits for the record to reach flash
71};
72
73// Written by loop() once it tried to save pin_record_; the task leaves the record alone while PENDING
74enum class PinSave : uint8_t {
78};
79#endif
80
81// Every esp_modem factory shares this signature, so codegen picks the one for the model
82using DceFactory = std::unique_ptr<esp_modem::DCE> (*)(const esp_modem::dce_config *, std::shared_ptr<esp_modem::DTE>,
83 esp_netif_t *);
84
85class ModemComponent : public Component {
86 public:
87 ModemComponent(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, const char *model, DceFactory factory,
88 const char *apn);
89 void setup() override;
90 void loop() override;
91 void dump_config() override;
92 float get_setup_priority() const override { return setup_priority::WIFI + 1; } // Just before Wi-Fi
93
94 void set_reboot_timeout(uint32_t timeout) { this->timeout_ = timeout; }
95 void set_use_address(const char *use_address) { this->use_address_ = use_address; }
96 void set_rts_pin(InternalGPIOPin *rts_pin) { this->rts_pin_ = rts_pin; }
97 void set_cts_pin(InternalGPIOPin *cts_pin) { this->cts_pin_ = cts_pin; }
98 void set_baud_rate(int baud_rate) { this->baud_rate_ = baud_rate; }
99#ifdef USE_MODEM_PIN_CODE
100 void set_pin_code(const char *pin_code) { this->pin_code_ = pin_code; }
101#endif
102 void set_tx_buffer_size(uint16_t tx_buffer_size) { this->tx_buffer_size_ = tx_buffer_size; }
103 void set_rx_buffer_size(uint16_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; }
104 void set_dte_buffer_size(uint16_t dte_buffer_size) { this->dte_buffer_size_ = dte_buffer_size; }
105 void set_init_at_commands(const char *const *cmds, uint8_t count) {
106 this->init_at_commands_ = cmds;
107 this->init_at_count_ = count;
108 }
109#ifdef USE_MODEM_URC
110 void set_urc_text_sensor(text_sensor::TextSensor *urc_text_sensor) { this->urc_text_sensor_ = urc_text_sensor; }
111#endif
112 template<typename F> void add_on_state_callback(F &&callback) {
113 this->on_state_callback_.add(std::forward<F>(callback));
114 }
115
116 bool is_connected() const { return this->component_state_.load() == ModemState::MODEM_STATE_CONNECTED; }
117 bool is_disabled() const {
118 return this->component_state_.load() == ModemState::MODEM_STATE_DISABLED &&
120 }
121 bool is_enabled() const { return !this->is_disabled(); }
122 void enable();
123 void disable();
124#ifdef USE_MODEM_SEND_AT
127 bool send_at(const char *cmd, uint32_t timeout = COMMAND_TIMEOUT_MS);
128 bool send_at(const std::string &cmd, uint32_t timeout = COMMAND_TIMEOUT_MS);
129#endif
130
132 const char *get_use_address() const { return this->use_address_; }
133
134 protected:
135 static constexpr uint32_t COMMAND_TIMEOUT_MS = 1000;
136 static constexpr uint32_t RETRY_DELAY_MS = 1000;
137 static constexpr size_t URC_LINE_SIZE = 128;
138 static constexpr size_t AT_COMMAND_SIZE = 128;
139
140 // esp_modem's calls block until the modem answers or their own timeouts run out, up to a minute for a
141 // mode change, so the state machine runs on its own task and leaves the main loop alone
142 static void task_entry(void *arg);
143 void run_task_();
144 void wake_task_();
154 void transition_to_(ModemState next_state);
155 // What the task left for loop() to do
159#ifdef USE_MODEM_PIN_CODE
161#endif
162 PENDING_CONNECT_TIMEOUT = 1 << 3, // arm or cancel the reboot timer for the current state
163 PENDING_ON_SYNC = 1 << 4, // loop() wakes the task once on_sync ran
166 };
167
168 // Main loop only, so arming and cancelling cannot interleave
171 void fail_setup_(const LogString *reason);
172 void set_fault_(ModemFault fault);
173 void set_teardown_fault_();
175 void hand_over_(uint32_t pending);
177 esp_modem::command_result at_(const char *cmd, uint32_t timeout = COMMAND_TIMEOUT_MS);
178 void publish_status_();
179 void not_responding_(int baud);
182#ifdef USE_MODEM_PIN_CODE
184 void save_pin_record_();
185#endif
186
191 const char *model_;
193 const char *apn_;
194 const char *const *init_at_commands_{nullptr}; // static array from code generation
195#ifdef USE_MODEM_URC
197 const char *urc_line_{nullptr}; // the line urc_pending_ hands to loop(), inside urc_reader_
198#endif
199 std::unique_ptr<esp_modem::DCE> dce_;
200 std::shared_ptr<esp_modem::DTE> dte_;
204 int baud_rate_{0}; // 0 = use the esp_modem default
210 uint32_t state_started_ms_{0}; // when the IP wait or the PIN unlock wait began
211 std::atomic<uint32_t> pending_{0};
212#ifdef USE_MODEM_PIN_CODE
213 const char *pin_code_{nullptr};
215#endif
216 // Kept in flash so reboots cannot keep spending the SIM's attempts on a PIN it never confirmed
217 struct PinRecord {
218 uint32_t hash; // pin_code the record is about
219 uint8_t sends; // AT+CPIN= sent without the SIM reporting READY since
220 };
221#ifdef USE_MODEM_PIN_CODE
222 PinRecord pin_record_{}; // the task writes it, loop() saves it while pin_save_state_ is PENDING
223#endif
224#ifdef USE_MODEM_SEND_AT
226#endif
227 uint16_t tx_buffer_size_{1024};
228 uint16_t rx_buffer_size_{1024};
229 uint16_t dte_buffer_size_{1024};
230 // The task owns component_state_; the main loop sets target_state_ and reads both
233 // Written by the task only, read by loop()
234 std::atomic<ModemFault> fault_{ModemFault::MODEM_FAULT_NONE};
235 // A failed baud_rate or init_at step, shown once fault_ is clear until the step passes again
237 // std::atomic<uint8_t> flags: GCC on Xtensa calls out of line for std::atomic<bool>
238 // Written by the IP event task, read by the modem task
239 std::atomic<uint8_t> got_ip_{0};
240 std::atomic<uint8_t> ppp_error_{0}; // last NETIF_PPP_STATUS error code, 0 = none
241 // Doublings of the silent-modem probe delay; enable() and an answer reset it
242 std::atomic<uint8_t> probe_backoff_{0};
243 // Set by loop() once on_sync ran; the task waits for it, not for any notification
244 std::atomic<uint8_t> sync_acked_{0};
245#ifdef USE_MODEM_SEND_AT
246 // Set by send_at() on the main loop once at_command_ is written, cleared by the task after sending it
247 std::atomic<uint8_t> at_pending_{0};
248#endif
249#ifdef USE_MODEM_URC
250 // Set by the UART task with urc_line_; until loop() has published it, new lines are dropped
251 std::atomic<uint8_t> urc_pending_{0};
252#endif
253#ifdef USE_MODEM_PIN_CODE
256 bool pin_record_unsaved_{false}; // loop() only; the last PIN record save failed
257#endif
259 uint8_t init_at_count_{0};
260 uint8_t uart_port_{UART_NUM_MAX};
261 uint8_t attach_polls_{0};
263 bool modem_synced_{false}; // a modem that never answered has nothing to tear down
264 bool resync_{false}; // the attachment read failed, so CONNECTING syncs again first
265#ifdef USE_MODEM_SEND_AT
267#endif
268#ifdef USE_MODEM_URC
269 // Fed on the UART task
271#endif
272
273 private:
274 // Points at a string literal from code generation, never at dynamic storage
275 const char *use_address_{""};
276};
277
278extern ModemComponent *global_modem_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
279
280} // namespace esphome::modem
281
282#endif
Collects bytes into NUL terminated lines of up to N - 1 characters; IGNORE_CHAR bytes are skipped and...
Definition line_reader.h:15
Helper for FreeRTOS static task management.
Definition static_task.h:18
void set_cts_pin(InternalGPIOPin *cts_pin)
std::shared_ptr< esp_modem::DTE > dte_
std::atomic< ModemState > component_state_
std::atomic< uint8_t > ppp_error_
bool send_at(const char *cmd, uint32_t timeout=COMMAND_TIMEOUT_MS)
Queue an AT command for the modem task, which sends it at its next pass and logs the result and respo...
char at_command_[AT_COMMAND_SIZE]
network::IPAddresses get_ip_addresses()
void set_dte_buffer_size(uint16_t dte_buffer_size)
void hand_over_(uint32_t pending)
std::atomic< ModemFault > fault_
void set_use_address(const char *use_address)
std::atomic< ModemFault > config_fault_
void set_rts_pin(InternalGPIOPin *rts_pin)
void set_baud_rate(int baud_rate)
ModemComponent(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, const char *model, DceFactory factory, const char *apn)
LineReader< URC_LINE_SIZE > urc_reader_
const char *const * init_at_commands_
uint32_t next_probe_delay_()
Pause the task before the next pass, then go to state.
static constexpr size_t AT_COMMAND_SIZE
std::atomic< uint8_t > at_pending_
float get_setup_priority() const override
std::unique_ptr< esp_modem::DCE > dce_
void set_rx_buffer_size(uint16_t rx_buffer_size)
void add_on_state_callback(F &&callback)
void set_init_at_commands(const char *const *cmds, uint8_t count)
std::atomic< ModemState > target_state_
static constexpr uint32_t RETRY_DELAY_MS
std::atomic< uint8_t > urc_pending_
LazyCallbackManager< void(ModemState)> on_state_callback_
void transition_to_(ModemState next_state)
void clear_config_fault_(ModemFault fault)
static void task_entry(void *arg)
void set_tx_buffer_size(uint16_t tx_buffer_size)
std::atomic< uint8_t > got_ip_
void set_fault_(ModemFault fault)
void set_reboot_timeout(uint32_t timeout)
void set_urc_text_sensor(text_sensor::TextSensor *urc_text_sensor)
std::atomic< uint8_t > sync_acked_
const char * get_use_address() const
std::atomic< PinSave > pin_save_state_
void fail_setup_(const LogString *reason)
std::atomic< uint32_t > pending_
void set_pin_code(const char *pin_code)
esp_modem::command_result at_(const char *cmd, uint32_t timeout=COMMAND_TIMEOUT_MS)
Send an AT command from the task and log the result; blocks for up to the timeout.
ModemState retry_(ModemState state, uint32_t delay_ms=RETRY_DELAY_MS)
text_sensor::TextSensor * urc_text_sensor_
std::atomic< uint8_t > probe_backoff_
static constexpr size_t URC_LINE_SIZE
static constexpr uint32_t COMMAND_TIMEOUT_MS
bool state
Definition fan.h:2
unsigned count
Definition gif.cpp:51
ModemComponent * global_modem_component
std::unique_ptr< esp_modem::DCE >(*)(const esp_modem::dce_config *, std::shared_ptr< esp_modem::DTE >, esp_netif_t *) DceFactory
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:369
constexpr float WIFI
Definition component.h:50
struct esp_netif_obj esp_netif_t
static void uint32_t