2#ifdef USE_IMPROV_SERIAL
21static constexpr uint32_t WIFI_CONNECT_TIMEOUT_ID = 0;
25#ifdef USE_IMPROV_SERIAL_UART
27#elif defined(USE_ESP32)
30#elif defined(USE_ARDUINO)
38 this->
state_ = improv::STATE_PROVISIONED;
52 ESP_LOGV(TAG,
"Timeout");
57 if (!
byte.has_value())
73 char ssid_buf[wifi::SSID_BUFFER_SIZE];
76 this->connecting_sta_.get_password());
93 const bool there_is_data = data !=
nullptr &&
size > 0;
95 const uint8_t header_checksum_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE - 2;
97 const uint8_t header_tx_len = there_is_data ? TX_BUFFER_SIZE - 3 : TX_BUFFER_SIZE;
104#ifdef USE_IMPROV_SERIAL_UART
110#elif defined(USE_ESP32)
114#if defined(USE_ESP32_VARIANT_ESP32)
123#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC)
125 esp_usb_console_write_buf((
const char *) this->
tx_header_, header_tx_len);
127 esp_usb_console_write_buf((
const char *) data,
size);
128 esp_usb_console_write_buf((
const char *) &this->tx_header_[TX_CHECKSUM_IDX],
133#ifdef USE_LOGGER_USB_SERIAL_JTAG
135 usb_serial_jtag_write_bytes((
const char *) this->tx_header_, header_tx_len, 20 / portTICK_PERIOD_MS);
137 usb_serial_jtag_write_bytes((
const char *) data,
size, 20 / portTICK_PERIOD_MS);
138 usb_serial_jtag_write_bytes((
const char *) &this->tx_header_[TX_CHECKSUM_IDX], 2,
139 20 / portTICK_PERIOD_MS);
146#elif defined(USE_ARDUINO)
163 for (
const auto &ip : addresses) {
164 if (!ip.is_ip4() || !ip.is_set())
166 char ip_buf[network::IP_ADDRESS_BUFFER_SIZE];
169 char webserver_url[7 + network::IP_ADDRESS_BUFFER_SIZE + 1 + 5 + 1];
172 buf_append_printf(webserver_url,
sizeof(webserver_url), 0,
"http://%s:%u", ip_buf, USE_WEBSERVER_PORT);
173 if (!builder.add_string(webserver_url,
len)) {
174 ESP_LOGW(TAG,
"Response full; URL dropped");
181 const auto append_wifi_urls = [&append_urls]() {
204 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
205 improv::RpcResponseBuilder builder(buf, command);
206#ifdef USE_IMPROV_NEXT_URL
219#ifdef ESPHOME_PROJECT_NAME
220 static constexpr size_t INFO_ENTRIES_LEN =
221 sizeof(ESPHOME_PROJECT_NAME) +
sizeof(ESPHOME_PROJECT_VERSION) +
sizeof(ESPHOME_VARIANT);
223 static constexpr size_t INFO_ENTRIES_LEN =
sizeof(
"ESPHome") +
sizeof(ESPHOME_VERSION) +
sizeof(ESPHOME_VARIANT);
225 static_assert(INFO_ENTRIES_LEN < MAX_SERIAL_PAYLOAD,
226 "esphome project name and version too long for the improv_serial device info frame");
227 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
228 improv::RpcResponseBuilder builder(buf, improv::GET_DEVICE_INFO);
232#define IMPROV_ADD_INFO(lit) \
234 static const char progmem_str[] PROGMEM = lit; \
235 char tmp[sizeof(lit)]; \
236 progmem_memcpy(tmp, progmem_str, sizeof(lit)); \
237 builder.add_string(tmp, sizeof(lit) - 1); \
241#define IMPROV_ADD_INFO(lit) builder.add_string(lit, sizeof(lit) - 1)
243#ifdef ESPHOME_PROJECT_NAME
244 IMPROV_ADD_INFO(ESPHOME_PROJECT_NAME);
245 IMPROV_ADD_INFO(ESPHOME_PROJECT_VERSION);
247 IMPROV_ADD_INFO(
"ESPHome");
248 IMPROV_ADD_INFO(ESPHOME_VERSION);
250 IMPROV_ADD_INFO(ESPHOME_VARIANT);
251#undef IMPROV_ADD_INFO
254 if (INFO_ENTRIES_LEN + 1 + name.size() <= MAX_SERIAL_PAYLOAD) {
255 builder.add_string(name.c_str(), name.size());
257 ESP_LOGW(TAG,
"Response full; device name dropped");
265 ESP_LOGV(TAG,
"Byte: 0x%02X",
byte);
268 return improv::parse_improv_serial_byte(
270 [
this](improv::Error error) ->
void {
271 ESP_LOGW(TAG,
"Error decoding payload");
277 switch (command.command) {
278 case improv::WIFI_SETTINGS: {
283 ESP_LOGW(TAG,
"Wi-Fi is disabled; cannot provision");
284 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
289 sta.set_password(command.password.c_str());
297 ESP_LOGD(TAG,
"Received settings: SSID=%s, password=" LOG_SECRET(
"%s"), command.ssid.c_str(),
298 command.password.c_str());
300 this->
set_timeout(WIFI_CONNECT_TIMEOUT_ID, switching ? WIFI_SWITCH_TIMEOUT_MS : WIFI_CONNECT_TIMEOUT_MS,
304 ESP_LOGW(TAG,
"Wi-Fi not supported; cannot provision");
305 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
309 case improv::GET_CURRENT_STATE: {
322 if (this->
state_ == improv::STATE_PROVISIONED) {
330 case improv::GET_DEVICE_INFO: {
334 case improv::GET_WIFI_NETWORKS: {
336 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
339 for (
const auto &scan : results) {
340 bool with_auth =
false;
345 char *rssi_end =
int8_to_str(rssi_buf, scan.get_rssi());
347 improv::RpcResponseBuilder builder(buf, improv::GET_WIFI_NETWORKS);
349 const auto &ssid = scan.get_ssid();
350 builder.add_string(ssid.c_str(), ssid.size());
351 builder.add_string(rssi_buf, rssi_end - rssi_buf);
352 builder.add_string(YESNO(with_auth));
357 improv::RpcResponseBuilder builder(buf, improv::GET_WIFI_NETWORKS);
361 case improv::GET_NETWORK_STATE: {
367 flags |= improv::NETWORK_IS_ONLINE;
369 flags |= improv::NETWORK_SUPPORTS_WIFI;
372 flags |= improv::NETWORK_SUPPORTS_ETHERNET;
375 flags |= improv::NETWORK_SUPPORTS_THREAD;
378 flags |= improv::NETWORK_SUPPORTS_MODEM;
380 std::array<uint8_t, improv::RPC_RESPONSE_MAX_SIZE> buf;
381 improv::RpcResponseBuilder builder(buf, improv::GET_NETWORK_STATE);
383 static_assert(improv::NETWORK_SUPPORTS_MODEM <= 0x7F,
"network flags no longer fit int8_to_str");
386 builder.add_string(flags_buf, flags_end - flags_buf);
390 if (
flags & improv::NETWORK_IS_ONLINE) {
391#if defined(USE_NETWORK_PRIMARY_INTERFACE_WIFI) && defined(USE_WIFI)
402 ESP_LOGW(TAG,
"Unknown payload");
428 if (response.size() > MAX_SERIAL_RESPONSE) {
429 ESP_LOGE(TAG,
"Response too long");
435 this->
write_data_(response.data(), response.size());
440 this->
set_error_(improv::ERROR_UNABLE_TO_CONNECT);
442 ESP_LOGW(TAG,
"Timed out while connecting to Wi-Fi network");
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
bool cancel_timeout(const char *name)
Cancel a timeout function.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
constexpr const char * c_str() const
void add_next_url_(improv::RpcResponseBuilder &builder, size_t max_len)
Append the formatted next_url to the RPC response, warning if it does not fit.
std::vector< uint8_t > rx_buffer_
void send_version_info_()
void set_state_(improv::State state)
wifi::WiFiAP connecting_sta_
void on_wifi_connect_timeout_()
void send_settings_response_(improv::Command command)
void write_data_(const uint8_t *data=nullptr, size_t size=0)
void send_response_(std::span< const uint8_t > response)
void dump_config() override
ESPHOME_ALWAYS_INLINE optional< uint8_t > read_byte_()
bool parse_improv_serial_byte_(uint8_t byte)
logger::UARTSelection uart_selection_
void send_current_state_(improv::State state)
void add_webserver_urls_(improv::RpcResponseBuilder &builder, bool wifi_first)
Append one web server URL per interface that has a usable IPv4.
uint8_t tx_header_[TX_BUFFER_SIZE]
bool parse_improv_payload_(improv::ImprovCommand &command)
void set_error_(improv::Error error)
uart::UARTComponent * uart_
Stream * get_hw_serial() const
UARTSelection get_uart() const
Get the UART used by the logger.
uart_port_t get_uart_num() const
void write_array(const std::vector< uint8_t > &data)
StringRef get_ssid() const
void set_ssid(const std::string &ssid)
void set_sta(const WiFiAP &ap)
void save_wifi_sta(const std::string &ssid, const std::string &password)
void start_connecting(const WiFiAP &ap)
bool is_connected() const
const wifi_scan_vector_t< WiFiScanResult > & get_scan_result() const
Main-loop callers may read this directly.
EthernetComponent * global_eth_component
ESPHOME_LOG_TAG(TAG, "improv_serial")
ImprovSerialComponent * global_improv_serial_component
@ UART_SELECTION_USB_SERIAL_JTAG
ModemComponent * global_modem_component
std::array< IPAddress, 5 > IPAddresses
ESPHOME_ALWAYS_INLINE bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
bool should_show_scan_entry(const Results &results, const Entry &scan, bool &with_auth)
WiFiComponent * global_wifi_component
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
char * int8_to_str(char *buf, int8_t val)
Write int8 value to buffer without modulo operations.
Application App
Global storage of Application pointer - only one Application can exist.