ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
improv_serial_component.h
Go to the documentation of this file.
1#pragma once
2
9#ifdef USE_IMPROV_SERIAL
10#include <improv.h>
11#include <span>
12#include <vector>
13
14#ifdef USE_WIFI
16#endif
17
18#ifdef USE_IMPROV_SERIAL_UART
20#elif defined(USE_ESP32)
21#include <driver/uart.h>
22#ifdef USE_LOGGER_USB_SERIAL_JTAG
23#include <driver/usb_serial_jtag.h>
24#include <hal/usb_serial_jtag_ll.h>
25#endif
26#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
27#include <esp_private/usb_console.h>
28#endif
29#elif defined(USE_ARDUINO)
30#include <HardwareSerial.h>
31#endif
32
33namespace esphome::improv_serial {
34
35// TX buffer layout constants
36static constexpr uint8_t TX_HEADER_SIZE = 6; // Bytes 0-5 = "IMPROV"
37static constexpr uint8_t TX_VERSION_IDX = 6;
38static constexpr uint8_t TX_TYPE_IDX = 7;
39static constexpr uint8_t TX_LENGTH_IDX = 8;
40static constexpr uint8_t TX_DATA_IDX = 9; // For state/error messages only
41static constexpr uint8_t TX_CHECKSUM_IDX = 10;
42static constexpr uint8_t TX_NEWLINE_IDX = 11;
43static constexpr uint8_t TX_BUFFER_SIZE = 12;
44
51
52static const uint16_t IMPROV_SERIAL_TIMEOUT = 100;
53static const uint8_t IMPROV_SERIAL_VERSION = 1;
54
55#ifdef USE_WIFI
56// Wi-Fi connect failure timers: a fresh provision reports at 30 s (stock behavior), while
57// switching networks on an already-connected device (disconnect + reconnect) can legitimately
58// take longer; 90 s matches esp32_improv's default wifi_timeout.
59static const uint32_t WIFI_CONNECT_TIMEOUT_MS = 30000;
60static const uint32_t WIFI_SWITCH_TIMEOUT_MS = 90000;
61#endif
62
63// The serial frame length field is one byte
64static constexpr size_t MAX_SERIAL_RESPONSE = 255;
65// command + data length + trailing byte
66static constexpr size_t RPC_RESPONSE_OVERHEAD = 3;
67static constexpr size_t MAX_SERIAL_PAYLOAD = MAX_SERIAL_RESPONSE - RPC_RESPONSE_OVERHEAD;
68#ifdef USE_WEBSERVER
69// length byte + "http://" + IPv4 + ":" + port. Reserves the first URL only; a device with
70// several interfaces online adds the rest best-effort and warns if one no longer fits.
71static constexpr size_t WEBSERVER_URL_RESERVE = 1 + 7 + 15 + 1 + 5;
72#else
73static constexpr size_t WEBSERVER_URL_RESERVE = 0;
74#endif
75// Entry budget minus its own length byte
76static constexpr size_t MAX_NEXT_URL_LEN = MAX_SERIAL_PAYLOAD - WEBSERVER_URL_RESERVE - 1;
77
78static_assert(MAX_SERIAL_RESPONSE <= improv::RPC_RESPONSE_MAX_SIZE, "builder buffer too small for the frame");
79
81 public:
82 void setup() override;
83 void loop() override;
84 void dump_config() override;
85
86 float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
87
88#ifdef USE_IMPROV_SERIAL_UART
89 void set_uart(uart::UARTComponent *uart) { this->uart_ = uart; }
90#endif
91
92 protected:
93 bool parse_improv_serial_byte_(uint8_t byte);
94 bool parse_improv_payload_(improv::ImprovCommand &command);
95
96 void set_state_(improv::State state);
97 void send_current_state_(improv::State state);
98 void set_error_(improv::Error error);
99 void send_response_(std::span<const uint8_t> response);
100#ifdef USE_WIFI
102#endif
103
104#ifdef USE_WEBSERVER
107 void add_webserver_urls_(improv::RpcResponseBuilder &builder, [[maybe_unused]] bool wifi_first);
108#endif
109 void send_settings_response_(improv::Command command);
110 void send_version_info_();
111
112 ESPHOME_ALWAYS_INLINE optional<uint8_t> read_byte_() {
113 optional<uint8_t> byte;
114 uint8_t data = 0;
115#ifdef USE_IMPROV_SERIAL_UART
116 if (this->uart_->available() && this->uart_->read_byte(&data)) {
117 byte = data;
118 }
119#elif defined(USE_ESP32)
120 switch (this->uart_selection_) {
123#if defined(USE_ESP32_VARIANT_ESP32)
125#endif
126 if (this->uart_num_ >= 0) {
127 size_t available;
128 uart_get_buffered_data_len(this->uart_num_, &available);
129 if (available) {
130 uart_read_bytes(this->uart_num_, &data, 1, 0);
131 byte = data;
132 }
133 }
134 break;
135#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC)
137 if (esp_usb_console_available_for_read()) {
138 esp_usb_console_read_buf((char *) &data, 1);
139 byte = data;
140 }
141 break;
142#endif
143#ifdef USE_LOGGER_USB_SERIAL_JTAG
145 if (usb_serial_jtag_read_bytes((char *) &data, 1, 0)) {
146 byte = data;
147 }
148 break;
149 }
150#endif
151 default:
152 break;
153 }
154#elif defined(USE_ARDUINO)
155 if (this->hw_serial_->available()) {
156 this->hw_serial_->readBytes(&data, 1);
157 byte = data;
158 }
159#endif
160 return byte;
161 }
162 void write_data_(const uint8_t *data = nullptr, size_t size = 0);
163
164 uint8_t tx_header_[TX_BUFFER_SIZE] = {
165 'I', // 0: Header
166 'M', // 1: Header
167 'P', // 2: Header
168 'R', // 3: Header
169 'O', // 4: Header
170 'V', // 5: Header
171 IMPROV_SERIAL_VERSION, // 6: Version
172 0, // 7: ImprovSerialType
173 0, // 8: Length
174 0, // 9...X: Data (here, one byte reserved for state/error)
175 0, // X + 10: Checksum
176 '\n',
177 };
178
179#ifdef USE_IMPROV_SERIAL_UART
181#elif defined(USE_ESP32)
182 uart_port_t uart_num_;
184#elif defined(USE_ARDUINO)
185 Stream *hw_serial_{nullptr};
186#endif
187
188 std::vector<uint8_t> rx_buffer_;
190#ifdef USE_WIFI
192#endif
193 improv::State state_{improv::STATE_AUTHORIZED};
194};
195
196extern ImprovSerialComponent
197 *global_improv_serial_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
198
199} // namespace esphome::improv_serial
200
201#endif
void write_data_(const uint8_t *data=nullptr, size_t size=0)
void send_response_(std::span< const uint8_t > response)
ESPHOME_ALWAYS_INLINE optional< uint8_t > read_byte_()
void add_webserver_urls_(improv::RpcResponseBuilder &builder, bool wifi_first)
Append one web server URL per interface that has a usable IPv4.
bool parse_improv_payload_(improv::ImprovCommand &command)
virtual size_t available()=0
bool state
Definition fan.h:2
ImprovSerialComponent * global_improv_serial_component
UARTSelection
Enum for logging UART selection.
Definition logger.h:104
@ UART_SELECTION_UART2
Definition logger.h:113
@ UART_SELECTION_USB_SERIAL_JTAG
Definition logger.h:119
@ UART_SELECTION_USB_CDC
Definition logger.h:116
@ UART_SELECTION_UART0
Definition logger.h:107
@ UART_SELECTION_UART1
Definition logger.h:111
constexpr float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition component.h:55
uint16_t size
Definition helpers.cpp:25
static void uint32_t