ESPHome  2024.7.2
wifi_component_esp_idf.cpp
Go to the documentation of this file.
1 #include "wifi_component.h"
2 
3 #ifdef USE_ESP_IDF
4 
5 #include <esp_event.h>
6 #include <esp_netif.h>
7 #include <esp_system.h>
8 #include <esp_wifi.h>
9 #include <esp_wifi_types.h>
10 #include <freertos/FreeRTOS.h>
11 #include <freertos/event_groups.h>
12 #include <freertos/task.h>
13 
14 #include <algorithm>
15 #include <cinttypes>
16 #include <utility>
17 #ifdef USE_WIFI_WPA2_EAP
18 #include <esp_wpa2.h>
19 #endif
20 
21 #ifdef USE_WIFI_AP
22 #include "dhcpserver/dhcpserver.h"
23 #endif // USE_WIFI_AP
24 
25 #include "lwip/apps/sntp.h"
26 #include "lwip/dns.h"
27 #include "lwip/err.h"
28 
30 #include "esphome/core/hal.h"
31 #include "esphome/core/helpers.h"
32 #include "esphome/core/log.h"
33 #include "esphome/core/util.h"
34 
35 namespace esphome {
36 namespace wifi {
37 
38 static const char *const TAG = "wifi_esp32";
39 
40 static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
41 static QueueHandle_t s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
42 static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
43 #ifdef USE_WIFI_AP
44 static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
45 #endif // USE_WIFI_AP
46 static bool s_sta_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
47 static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
48 static bool s_ap_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
49 static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
50 static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
51 static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
52 static bool s_wifi_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
53 
54 struct IDFWiFiEvent {
55  esp_event_base_t event_base;
56  int32_t event_id;
57  union {
58  wifi_event_sta_scan_done_t sta_scan_done;
59  wifi_event_sta_connected_t sta_connected;
60  wifi_event_sta_disconnected_t sta_disconnected;
61  wifi_event_sta_authmode_change_t sta_authmode_change;
62  wifi_event_ap_staconnected_t ap_staconnected;
63  wifi_event_ap_stadisconnected_t ap_stadisconnected;
64  wifi_event_ap_probe_req_rx_t ap_probe_req_rx;
65  wifi_event_bss_rssi_low_t bss_rssi_low;
66  ip_event_got_ip_t ip_got_ip;
67 #if USE_NETWORK_IPV6
68  ip_event_got_ip6_t ip_got_ip6;
69 #endif /* USE_NETWORK_IPV6 */
70  ip_event_ap_staipassigned_t ip_ap_staipassigned;
71  } data;
72 };
73 
74 // general design: event handler translates events and pushes them to a queue,
75 // events get processed in the main loop
76 void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
77  IDFWiFiEvent event;
78  memset(&event, 0, sizeof(IDFWiFiEvent));
79  event.event_base = event_base;
80  event.event_id = event_id;
81  if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { // NOLINT(bugprone-branch-clone)
82  // no data
83  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) { // NOLINT(bugprone-branch-clone)
84  // no data
85  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) {
86  memcpy(&event.data.sta_authmode_change, event_data, sizeof(wifi_event_sta_authmode_change_t));
87  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
88  memcpy(&event.data.sta_connected, event_data, sizeof(wifi_event_sta_connected_t));
89  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
90  memcpy(&event.data.sta_disconnected, event_data, sizeof(wifi_event_sta_disconnected_t));
91  } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
92  memcpy(&event.data.ip_got_ip, event_data, sizeof(ip_event_got_ip_t));
93 #if USE_NETWORK_IPV6
94  } else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) {
95  memcpy(&event.data.ip_got_ip6, event_data, sizeof(ip_event_got_ip6_t));
96 #endif
97  } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP) { // NOLINT(bugprone-branch-clone)
98  // no data
99  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) {
100  memcpy(&event.data.sta_scan_done, event_data, sizeof(wifi_event_sta_scan_done_t));
101  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_START) { // NOLINT(bugprone-branch-clone)
102  // no data
103  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STOP) { // NOLINT(bugprone-branch-clone)
104  // no data
105  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_PROBEREQRECVED) {
106  memcpy(&event.data.ap_probe_req_rx, event_data, sizeof(wifi_event_ap_probe_req_rx_t));
107  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
108  memcpy(&event.data.ap_staconnected, event_data, sizeof(wifi_event_ap_staconnected_t));
109  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
110  memcpy(&event.data.ap_stadisconnected, event_data, sizeof(wifi_event_ap_stadisconnected_t));
111  } else if (event_base == IP_EVENT && event_id == IP_EVENT_AP_STAIPASSIGNED) {
112  memcpy(&event.data.ip_ap_staipassigned, event_data, sizeof(ip_event_ap_staipassigned_t));
113  } else {
114  // did not match any event, don't send anything
115  return;
116  }
117 
118  // copy to heap to keep queue object small
119  auto *to_send = new IDFWiFiEvent; // NOLINT(cppcoreguidelines-owning-memory)
120  memcpy(to_send, &event, sizeof(IDFWiFiEvent));
121  // don't block, we may miss events but the core can handle that
122  if (xQueueSend(s_event_queue, &to_send, 0L) != pdPASS) {
123  delete to_send; // NOLINT(cppcoreguidelines-owning-memory)
124  }
125 }
126 
128 #ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
129  uint8_t mac[6];
130  get_mac_address_raw(mac);
131  set_mac_address(mac);
132  ESP_LOGV(TAG, "Use EFuse MAC without checking CRC: %s", get_mac_address_pretty().c_str());
133 #endif
134  esp_err_t err = esp_netif_init();
135  if (err != ERR_OK) {
136  ESP_LOGE(TAG, "esp_netif_init failed: %s", esp_err_to_name(err));
137  return;
138  }
139  s_wifi_event_group = xEventGroupCreate();
140  if (s_wifi_event_group == nullptr) {
141  ESP_LOGE(TAG, "xEventGroupCreate failed");
142  return;
143  }
144  // NOLINTNEXTLINE(bugprone-sizeof-expression)
145  s_event_queue = xQueueCreate(64, sizeof(IDFWiFiEvent *));
146  if (s_event_queue == nullptr) {
147  ESP_LOGE(TAG, "xQueueCreate failed");
148  return;
149  }
150  err = esp_event_loop_create_default();
151  if (err != ERR_OK) {
152  ESP_LOGE(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(err));
153  return;
154  }
155  esp_event_handler_instance_t instance_wifi_id, instance_ip_id;
156  err = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, nullptr, &instance_wifi_id);
157  if (err != ERR_OK) {
158  ESP_LOGE(TAG, "esp_event_handler_instance_register failed: %s", esp_err_to_name(err));
159  return;
160  }
161  err = esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, nullptr, &instance_ip_id);
162  if (err != ERR_OK) {
163  ESP_LOGE(TAG, "esp_event_handler_instance_register failed: %s", esp_err_to_name(err));
164  return;
165  }
166 
167  s_sta_netif = esp_netif_create_default_wifi_sta();
168 
169 #ifdef USE_WIFI_AP
170  s_ap_netif = esp_netif_create_default_wifi_ap();
171 #endif // USE_WIFI_AP
172 
173  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
174  // cfg.nvs_enable = false;
175  err = esp_wifi_init(&cfg);
176  if (err != ERR_OK) {
177  ESP_LOGE(TAG, "esp_wifi_init failed: %s", esp_err_to_name(err));
178  return;
179  }
180  err = esp_wifi_set_storage(WIFI_STORAGE_RAM);
181  if (err != ERR_OK) {
182  ESP_LOGE(TAG, "esp_wifi_set_storage failed: %s", esp_err_to_name(err));
183  return;
184  }
185 }
186 
188  esp_err_t err;
189  wifi_mode_t current_mode = WIFI_MODE_NULL;
190  if (s_wifi_started) {
191  err = esp_wifi_get_mode(&current_mode);
192  if (err != ERR_OK) {
193  ESP_LOGW(TAG, "esp_wifi_get_mode failed: %s", esp_err_to_name(err));
194  return false;
195  }
196  }
197  bool current_sta = current_mode == WIFI_MODE_STA || current_mode == WIFI_MODE_APSTA;
198  bool current_ap = current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA;
199 
200  bool set_sta = sta.value_or(current_sta);
201  bool set_ap = ap.value_or(current_ap);
202 
203  wifi_mode_t set_mode;
204  if (set_sta && set_ap) {
205  set_mode = WIFI_MODE_APSTA;
206  } else if (set_sta && !set_ap) {
207  set_mode = WIFI_MODE_STA;
208  } else if (!set_sta && set_ap) {
209  set_mode = WIFI_MODE_AP;
210  } else {
211  set_mode = WIFI_MODE_NULL;
212  }
213 
214  if (current_mode == set_mode)
215  return true;
216 
217  if (set_sta && !current_sta) {
218  ESP_LOGV(TAG, "Enabling STA.");
219  } else if (!set_sta && current_sta) {
220  ESP_LOGV(TAG, "Disabling STA.");
221  }
222  if (set_ap && !current_ap) {
223  ESP_LOGV(TAG, "Enabling AP.");
224  } else if (!set_ap && current_ap) {
225  ESP_LOGV(TAG, "Disabling AP.");
226  }
227 
228  if (set_mode == WIFI_MODE_NULL && s_wifi_started) {
229  err = esp_wifi_stop();
230  if (err != ESP_OK) {
231  ESP_LOGV(TAG, "esp_wifi_stop failed: %s", esp_err_to_name(err));
232  return false;
233  }
234  s_wifi_started = false;
235  return true;
236  }
237 
238  err = esp_wifi_set_mode(set_mode);
239  if (err != ERR_OK) {
240  ESP_LOGW(TAG, "esp_wifi_set_mode failed: %s", esp_err_to_name(err));
241  return false;
242  }
243 
244  if (set_mode != WIFI_MODE_NULL && !s_wifi_started) {
245  err = esp_wifi_start();
246  if (err != ESP_OK) {
247  ESP_LOGV(TAG, "esp_wifi_start failed: %s", esp_err_to_name(err));
248  return false;
249  }
250  s_wifi_started = true;
251  }
252 
253  return true;
254 }
255 
256 bool WiFiComponent::wifi_sta_pre_setup_() { return this->wifi_mode_(true, {}); }
257 
258 bool WiFiComponent::wifi_apply_output_power_(float output_power) {
259  int8_t val = static_cast<int8_t>(output_power * 4);
260  return esp_wifi_set_max_tx_power(val) == ESP_OK;
261 }
262 
264  wifi_ps_type_t power_save;
265  switch (this->power_save_) {
267  power_save = WIFI_PS_MIN_MODEM;
268  break;
270  power_save = WIFI_PS_MAX_MODEM;
271  break;
273  default:
274  power_save = WIFI_PS_NONE;
275  break;
276  }
277  return esp_wifi_set_ps(power_save) == ESP_OK;
278 }
279 
281  // enable STA
282  if (!this->wifi_mode_(true, {}))
283  return false;
284 
285  // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t
286  wifi_config_t conf;
287  memset(&conf, 0, sizeof(conf));
288  strncpy(reinterpret_cast<char *>(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid));
289  strncpy(reinterpret_cast<char *>(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password));
290 
291  // The weakest authmode to accept in the fast scan mode
292  if (ap.get_password().empty()) {
293  conf.sta.threshold.authmode = WIFI_AUTH_OPEN;
294  } else {
295  conf.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK;
296  }
297 
298 #ifdef USE_WIFI_WPA2_EAP
299  if (ap.get_eap().has_value()) {
300  conf.sta.threshold.authmode = WIFI_AUTH_WPA2_ENTERPRISE;
301  }
302 #endif
303 
304 #ifdef USE_WIFI_11KV_SUPPORT
305  conf.sta.btm_enabled = this->btm_;
306  conf.sta.rm_enabled = this->rrm_;
307 #endif
308 
309  if (ap.get_bssid().has_value()) {
310  conf.sta.bssid_set = true;
311  memcpy(conf.sta.bssid, ap.get_bssid()->data(), 6);
312  } else {
313  conf.sta.bssid_set = false;
314  }
315  if (ap.get_channel().has_value()) {
316  conf.sta.channel = *ap.get_channel();
317  conf.sta.scan_method = WIFI_FAST_SCAN;
318  } else {
319  conf.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
320  }
321  // Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set.
322  // Units: AP beacon intervals. Defaults to 3 if set to 0.
323  conf.sta.listen_interval = 0;
324 
325  // Protected Management Frame
326  // Device will prefer to connect in PMF mode if other device also advertises PMF capability.
327  conf.sta.pmf_cfg.capable = true;
328  conf.sta.pmf_cfg.required = false;
329 
330  // note, we do our own filtering
331  // The minimum rssi to accept in the fast scan mode
332  conf.sta.threshold.rssi = -127;
333 
334  conf.sta.threshold.authmode = WIFI_AUTH_OPEN;
335 
336  wifi_config_t current_conf;
337  esp_err_t err;
338  err = esp_wifi_get_config(WIFI_IF_STA, &current_conf);
339  if (err != ERR_OK) {
340  ESP_LOGW(TAG, "esp_wifi_get_config failed: %s", esp_err_to_name(err));
341  // can continue
342  }
343 
344  if (memcmp(&current_conf, &conf, sizeof(wifi_config_t)) != 0) { // NOLINT
345  err = esp_wifi_disconnect();
346  if (err != ESP_OK) {
347  ESP_LOGV(TAG, "esp_wifi_disconnect failed: %s", esp_err_to_name(err));
348  return false;
349  }
350  }
351 
352  err = esp_wifi_set_config(WIFI_IF_STA, &conf);
353  if (err != ESP_OK) {
354  ESP_LOGV(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(err));
355  return false;
356  }
357 
358  if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) {
359  return false;
360  }
361 
362  // setup enterprise authentication if required
363 #ifdef USE_WIFI_WPA2_EAP
364  if (ap.get_eap().has_value()) {
365  // note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0.
366  EAPAuth eap = ap.get_eap().value();
367  err = esp_wifi_sta_wpa2_ent_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length());
368  if (err != ESP_OK) {
369  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", err);
370  }
371  int ca_cert_len = strlen(eap.ca_cert);
372  int client_cert_len = strlen(eap.client_cert);
373  int client_key_len = strlen(eap.client_key);
374  if (ca_cert_len) {
375  err = esp_wifi_sta_wpa2_ent_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1);
376  if (err != ESP_OK) {
377  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", err);
378  }
379  }
380  // workout what type of EAP this is
381  // validation is not required as the config tool has already validated it
382  if (client_cert_len && client_key_len) {
383  // if we have certs, this must be EAP-TLS
384  err = esp_wifi_sta_wpa2_ent_set_cert_key((uint8_t *) eap.client_cert, client_cert_len + 1,
385  (uint8_t *) eap.client_key, client_key_len + 1,
386  (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str()));
387  if (err != ESP_OK) {
388  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", err);
389  }
390  } else {
391  // in the absence of certs, assume this is username/password based
392  err = esp_wifi_sta_wpa2_ent_set_username((uint8_t *) eap.username.c_str(), eap.username.length());
393  if (err != ESP_OK) {
394  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", err);
395  }
396  err = esp_wifi_sta_wpa2_ent_set_password((uint8_t *) eap.password.c_str(), eap.password.length());
397  if (err != ESP_OK) {
398  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err);
399  }
400  // set TTLS Phase 2, defaults to MSCHAPV2
401  err = esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(eap.ttls_phase_2);
402  if (err != ESP_OK) {
403  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ttls_phase2_method failed! %d", err);
404  }
405  }
406  err = esp_wifi_sta_wpa2_ent_enable();
407  if (err != ESP_OK) {
408  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err);
409  }
410  }
411 #endif // USE_WIFI_WPA2_EAP
412 
413  // Reset flags, do this _before_ wifi_station_connect as the callback method
414  // may be called from wifi_station_connect
415  s_sta_connecting = true;
416  s_sta_connected = false;
417  s_sta_connect_error = false;
418  s_sta_connect_not_found = false;
419 
420  err = esp_wifi_connect();
421  if (err != ESP_OK) {
422  ESP_LOGW(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(err));
423  return false;
424  }
425 
426  return true;
427 }
428 
430  // enable STA
431  if (!this->wifi_mode_(true, {}))
432  return false;
433 
434  esp_netif_dhcp_status_t dhcp_status;
435  esp_err_t err = esp_netif_dhcpc_get_status(s_sta_netif, &dhcp_status);
436  if (err != ESP_OK) {
437  ESP_LOGV(TAG, "esp_netif_dhcpc_get_status failed: %s", esp_err_to_name(err));
438  return false;
439  }
440 
441  if (!manual_ip.has_value()) {
442  // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly,
443  // the built-in SNTP client has a memory leak in certain situations. Disable this feature.
444  // https://github.com/esphome/issues/issues/2299
445  sntp_servermode_dhcp(false);
446 
447  // No manual IP is set; use DHCP client
448  if (dhcp_status != ESP_NETIF_DHCP_STARTED) {
449  err = esp_netif_dhcpc_start(s_sta_netif);
450  if (err != ESP_OK) {
451  ESP_LOGV(TAG, "Starting DHCP client failed! %d", err);
452  }
453  return err == ESP_OK;
454  }
455  return true;
456  }
457 
458  esp_netif_ip_info_t info; // struct of ip4_addr_t with ip, netmask, gw
459  info.ip = manual_ip->static_ip;
460  info.gw = manual_ip->gateway;
461  info.netmask = manual_ip->subnet;
462  err = esp_netif_dhcpc_stop(s_sta_netif);
463  if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
464  ESP_LOGV(TAG, "Stopping DHCP client failed! %s", esp_err_to_name(err));
465  }
466 
467  err = esp_netif_set_ip_info(s_sta_netif, &info);
468  if (err != ESP_OK) {
469  ESP_LOGV(TAG, "Setting manual IP info failed! %s", esp_err_to_name(err));
470  }
471 
472  esp_netif_dns_info_t dns;
473  if (manual_ip->dns1.is_set()) {
474  dns.ip = manual_ip->dns1;
475  esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_MAIN, &dns);
476  }
477  if (manual_ip->dns2.is_set()) {
478  dns.ip = manual_ip->dns2;
479  esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_BACKUP, &dns);
480  }
481 
482  return true;
483 }
484 
486  if (!this->has_sta())
487  return {};
488  network::IPAddresses addresses;
489  esp_netif_ip_info_t ip;
490  esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip);
491  if (err != ESP_OK) {
492  ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
493  // TODO: do something smarter
494  // return false;
495  } else {
496  addresses[0] = network::IPAddress(&ip.ip);
497  }
498 #if USE_NETWORK_IPV6
499  struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
500  uint8_t count = 0;
501  count = esp_netif_get_all_ip6(s_sta_netif, if_ip6s);
502  assert(count <= CONFIG_LWIP_IPV6_NUM_ADDRESSES);
503  for (int i = 0; i < count; i++) {
504  addresses[i + 1] = network::IPAddress(&if_ip6s[i]);
505  }
506 #endif /* USE_NETWORK_IPV6 */
507  return addresses;
508 }
509 
511  // setting is done in SYSTEM_EVENT_STA_START callback
512  return true;
513 }
514 const char *get_auth_mode_str(uint8_t mode) {
515  switch (mode) {
516  case WIFI_AUTH_OPEN:
517  return "OPEN";
518  case WIFI_AUTH_WEP:
519  return "WEP";
520  case WIFI_AUTH_WPA_PSK:
521  return "WPA PSK";
522  case WIFI_AUTH_WPA2_PSK:
523  return "WPA2 PSK";
524  case WIFI_AUTH_WPA_WPA2_PSK:
525  return "WPA/WPA2 PSK";
526  case WIFI_AUTH_WPA2_ENTERPRISE:
527  return "WPA2 Enterprise";
528  case WIFI_AUTH_WPA3_PSK:
529  return "WPA3 PSK";
530  case WIFI_AUTH_WPA2_WPA3_PSK:
531  return "WPA2/WPA3 PSK";
532  case WIFI_AUTH_WAPI_PSK:
533  return "WAPI PSK";
534  default:
535  return "UNKNOWN";
536  }
537 }
538 
539 std::string format_ip4_addr(const esp_ip4_addr_t &ip) { return str_snprintf(IPSTR, 15, IP2STR(&ip)); }
540 #if LWIP_IPV6
541 std::string format_ip6_addr(const esp_ip6_addr_t &ip) { return str_snprintf(IPV6STR, 39, IPV62STR(ip)); }
542 #endif /* LWIP_IPV6 */
543 const char *get_disconnect_reason_str(uint8_t reason) {
544  switch (reason) {
545  case WIFI_REASON_AUTH_EXPIRE:
546  return "Auth Expired";
547  case WIFI_REASON_AUTH_LEAVE:
548  return "Auth Leave";
549  case WIFI_REASON_ASSOC_EXPIRE:
550  return "Association Expired";
551  case WIFI_REASON_ASSOC_TOOMANY:
552  return "Too Many Associations";
553  case WIFI_REASON_NOT_AUTHED:
554  return "Not Authenticated";
555  case WIFI_REASON_NOT_ASSOCED:
556  return "Not Associated";
557  case WIFI_REASON_ASSOC_LEAVE:
558  return "Association Leave";
559  case WIFI_REASON_ASSOC_NOT_AUTHED:
560  return "Association not Authenticated";
561  case WIFI_REASON_DISASSOC_PWRCAP_BAD:
562  return "Disassociate Power Cap Bad";
563  case WIFI_REASON_DISASSOC_SUPCHAN_BAD:
564  return "Disassociate Supported Channel Bad";
565  case WIFI_REASON_IE_INVALID:
566  return "IE Invalid";
567  case WIFI_REASON_MIC_FAILURE:
568  return "Mic Failure";
569  case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
570  return "4-Way Handshake Timeout";
571  case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT:
572  return "Group Key Update Timeout";
573  case WIFI_REASON_IE_IN_4WAY_DIFFERS:
574  return "IE In 4-Way Handshake Differs";
575  case WIFI_REASON_GROUP_CIPHER_INVALID:
576  return "Group Cipher Invalid";
577  case WIFI_REASON_PAIRWISE_CIPHER_INVALID:
578  return "Pairwise Cipher Invalid";
579  case WIFI_REASON_AKMP_INVALID:
580  return "AKMP Invalid";
581  case WIFI_REASON_UNSUPP_RSN_IE_VERSION:
582  return "Unsupported RSN IE version";
583  case WIFI_REASON_INVALID_RSN_IE_CAP:
584  return "Invalid RSN IE Cap";
585  case WIFI_REASON_802_1X_AUTH_FAILED:
586  return "802.1x Authentication Failed";
587  case WIFI_REASON_CIPHER_SUITE_REJECTED:
588  return "Cipher Suite Rejected";
589  case WIFI_REASON_BEACON_TIMEOUT:
590  return "Beacon Timeout";
591  case WIFI_REASON_NO_AP_FOUND:
592  return "AP Not Found";
593  case WIFI_REASON_AUTH_FAIL:
594  return "Authentication Failed";
595  case WIFI_REASON_ASSOC_FAIL:
596  return "Association Failed";
597  case WIFI_REASON_HANDSHAKE_TIMEOUT:
598  return "Handshake Failed";
599  case WIFI_REASON_CONNECTION_FAIL:
600  return "Connection Failed";
601  case WIFI_REASON_ROAMING:
602  return "Station Roaming";
603  case WIFI_REASON_UNSPECIFIED:
604  default:
605  return "Unspecified";
606  }
607 }
608 
610  while (true) {
611  IDFWiFiEvent *data;
612  if (xQueueReceive(s_event_queue, &data, 0L) != pdTRUE) {
613  // no event ready
614  break;
615  }
616 
617  // process event
618  wifi_process_event_(data);
619 
620  delete data; // NOLINT(cppcoreguidelines-owning-memory)
621  }
622 }
623 void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
624  esp_err_t err;
625  if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_START) {
626  ESP_LOGV(TAG, "Event: WiFi STA start");
627  // apply hostname
628  err = esp_netif_set_hostname(s_sta_netif, App.get_name().c_str());
629  if (err != ERR_OK) {
630  ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err));
631  }
632 
633  s_sta_started = true;
634  // re-apply power save mode
635  wifi_apply_power_save_();
636 
637  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) {
638  ESP_LOGV(TAG, "Event: WiFi STA stop");
639  s_sta_started = false;
640 
641  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) {
642  const auto &it = data->data.sta_authmode_change;
643  ESP_LOGV(TAG, "Event: Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode),
644  get_auth_mode_str(it.new_mode));
645 
646  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_CONNECTED) {
647  const auto &it = data->data.sta_connected;
648  char buf[33];
649  assert(it.ssid_len <= 32);
650  memcpy(buf, it.ssid, it.ssid_len);
651  buf[it.ssid_len] = '\0';
652  ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf,
653  format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode));
654  s_sta_connected = true;
655 
656  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_DISCONNECTED) {
657  const auto &it = data->data.sta_disconnected;
658  char buf[33];
659  assert(it.ssid_len <= 32);
660  memcpy(buf, it.ssid, it.ssid_len);
661  buf[it.ssid_len] = '\0';
662  if (it.reason == WIFI_REASON_NO_AP_FOUND) {
663  ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf);
664  s_sta_connect_not_found = true;
665  } else if (it.reason == WIFI_REASON_ROAMING) {
666  ESP_LOGI(TAG, "Event: Disconnected ssid='%s' reason='Station Roaming'", buf);
667  return;
668  } else {
669  ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf,
670  format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason));
671  s_sta_connect_error = true;
672  }
673  s_sta_connected = false;
674  s_sta_connecting = false;
675  error_from_callback_ = true;
676 
677  } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) {
678  const auto &it = data->data.ip_got_ip;
679 #if USE_NETWORK_IPV6
680  esp_netif_create_ip6_linklocal(s_sta_netif);
681 #endif /* USE_NETWORK_IPV6 */
682  ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(),
683  format_ip4_addr(it.ip_info.gw).c_str());
684  this->got_ipv4_address_ = true;
685 
686 #if USE_NETWORK_IPV6
687  } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_GOT_IP6) {
688  const auto &it = data->data.ip_got_ip6;
689  ESP_LOGV(TAG, "Event: Got IPv6 address=%s", format_ip6_addr(it.ip6_info.ip).c_str());
690  this->num_ipv6_addresses_++;
691 #endif /* USE_NETWORK_IPV6 */
692 
693  } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) {
694  ESP_LOGV(TAG, "Event: Lost IP");
695  this->got_ipv4_address_ = false;
696 
697  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_SCAN_DONE) {
698  const auto &it = data->data.sta_scan_done;
699  ESP_LOGV(TAG, "Event: WiFi Scan Done status=%" PRIu32 " number=%u scan_id=%u", it.status, it.number, it.scan_id);
700 
701  scan_result_.clear();
702  this->scan_done_ = true;
703  if (it.status != 0) {
704  // scan error
705  return;
706  }
707 
708  if (it.number == 0) {
709  // no results
710  return;
711  }
712 
713  uint16_t number = it.number;
714  std::vector<wifi_ap_record_t> records(number);
715  err = esp_wifi_scan_get_ap_records(&number, records.data());
716  if (err != ESP_OK) {
717  ESP_LOGW(TAG, "esp_wifi_scan_get_ap_records failed: %s", esp_err_to_name(err));
718  return;
719  }
720  records.resize(number);
721 
722  scan_result_.reserve(number);
723  for (int i = 0; i < number; i++) {
724  auto &record = records[i];
725  bssid_t bssid;
726  std::copy(record.bssid, record.bssid + 6, bssid.begin());
727  std::string ssid(reinterpret_cast<const char *>(record.ssid));
728  WiFiScanResult result(bssid, ssid, record.primary, record.rssi, record.authmode != WIFI_AUTH_OPEN, ssid.empty());
729  scan_result_.push_back(result);
730  }
731 
732  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_START) {
733  ESP_LOGV(TAG, "Event: WiFi AP start");
734  s_ap_started = true;
735 
736  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STOP) {
737  ESP_LOGV(TAG, "Event: WiFi AP stop");
738  s_ap_started = false;
739 
740  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) {
741  const auto &it = data->data.ap_probe_req_rx;
742  ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi);
743 
744  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STACONNECTED) {
745  const auto &it = data->data.ap_staconnected;
746  ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(it.mac).c_str());
747 
748  } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STADISCONNECTED) {
749  const auto &it = data->data.ap_stadisconnected;
750  ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(it.mac).c_str());
751 
752  } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_AP_STAIPASSIGNED) {
753  const auto &it = data->data.ip_ap_staipassigned;
754  ESP_LOGV(TAG, "Event: AP client assigned IP %s", format_ip4_addr(it.ip).c_str());
755  }
756 }
757 
759  if (s_sta_connected && this->got_ipv4_address_) {
760 #if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
761  if (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT) {
763  }
764 #else
766 #endif /* USE_NETWORK_IPV6 */
767  }
768  if (s_sta_connect_error) {
770  }
771  if (s_sta_connect_not_found) {
773  }
774  if (s_sta_connecting) {
776  }
778 }
779 bool WiFiComponent::wifi_scan_start_(bool passive) {
780  // enable STA
781  if (!this->wifi_mode_(true, {}))
782  return false;
783 
784  wifi_scan_config_t config{};
785  config.ssid = nullptr;
786  config.bssid = nullptr;
787  config.channel = 0;
788  config.show_hidden = true;
789  config.scan_type = passive ? WIFI_SCAN_TYPE_PASSIVE : WIFI_SCAN_TYPE_ACTIVE;
790  if (passive) {
791  config.scan_time.passive = 300;
792  } else {
793  config.scan_time.active.min = 100;
794  config.scan_time.active.max = 300;
795  }
796 
797  esp_err_t err = esp_wifi_scan_start(&config, false);
798  if (err != ESP_OK) {
799  ESP_LOGV(TAG, "esp_wifi_scan_start failed: %s", esp_err_to_name(err));
800  return false;
801  }
802 
803  this->scan_done_ = false;
804  return true;
805 }
806 
807 #ifdef USE_WIFI_AP
809  esp_err_t err;
810 
811  // enable AP
812  if (!this->wifi_mode_({}, true))
813  return false;
814 
815  esp_netif_ip_info_t info;
816  if (manual_ip.has_value()) {
817  info.ip = manual_ip->static_ip;
818  info.gw = manual_ip->gateway;
819  info.netmask = manual_ip->subnet;
820  } else {
821  info.ip = network::IPAddress(192, 168, 4, 1);
822  info.gw = network::IPAddress(192, 168, 4, 1);
823  info.netmask = network::IPAddress(255, 255, 255, 0);
824  }
825 
826  err = esp_netif_dhcps_stop(s_ap_netif);
827  if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
828  ESP_LOGE(TAG, "esp_netif_dhcps_stop failed: %s", esp_err_to_name(err));
829  return false;
830  }
831 
832  err = esp_netif_set_ip_info(s_ap_netif, &info);
833  if (err != ESP_OK) {
834  ESP_LOGE(TAG, "esp_netif_set_ip_info failed! %d", err);
835  return false;
836  }
837 
838  dhcps_lease_t lease;
839  lease.enable = true;
840  network::IPAddress start_address = network::IPAddress(&info.ip);
841  start_address += 99;
842  lease.start_ip = start_address;
843  ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str());
844  start_address += 10;
845  lease.end_ip = start_address;
846  ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str());
847  err = esp_netif_dhcps_option(s_ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease, sizeof(lease));
848 
849  if (err != ESP_OK) {
850  ESP_LOGE(TAG, "esp_netif_dhcps_option failed! %d", err);
851  return false;
852  }
853 
854  err = esp_netif_dhcps_start(s_ap_netif);
855 
856  if (err != ESP_OK) {
857  ESP_LOGE(TAG, "esp_netif_dhcps_start failed! %d", err);
858  return false;
859  }
860 
861  return true;
862 }
863 
864 bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
865  // enable AP
866  if (!this->wifi_mode_({}, true))
867  return false;
868 
869  wifi_config_t conf;
870  memset(&conf, 0, sizeof(conf));
871  strncpy(reinterpret_cast<char *>(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid));
872  conf.ap.channel = ap.get_channel().value_or(1);
873  conf.ap.ssid_hidden = ap.get_ssid().size();
874  conf.ap.max_connection = 5;
875  conf.ap.beacon_interval = 100;
876 
877  if (ap.get_password().empty()) {
878  conf.ap.authmode = WIFI_AUTH_OPEN;
879  *conf.ap.password = 0;
880  } else {
881  conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
882  strncpy(reinterpret_cast<char *>(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password));
883  }
884 
885  // pairwise cipher of SoftAP, group cipher will be derived using this.
886  conf.ap.pairwise_cipher = WIFI_CIPHER_TYPE_CCMP;
887 
888  esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &conf);
889  if (err != ESP_OK) {
890  ESP_LOGE(TAG, "esp_wifi_set_config failed! %d", err);
891  return false;
892  }
893 
894  if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
895  ESP_LOGE(TAG, "wifi_ap_ip_config_ failed!");
896  return false;
897  }
898 
899  return true;
900 }
901 
903  esp_netif_ip_info_t ip;
904  esp_netif_get_ip_info(s_ap_netif, &ip);
905  return network::IPAddress(&ip.ip);
906 }
907 #endif // USE_WIFI_AP
908 
909 bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); }
910 
912  bssid_t bssid{};
913  wifi_ap_record_t info;
914  esp_err_t err = esp_wifi_sta_get_ap_info(&info);
915  if (err != ESP_OK) {
916  ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err));
917  return bssid;
918  }
919  std::copy(info.bssid, info.bssid + 6, bssid.begin());
920  return bssid;
921 }
922 std::string WiFiComponent::wifi_ssid() {
923  wifi_ap_record_t info{};
924  esp_err_t err = esp_wifi_sta_get_ap_info(&info);
925  if (err != ESP_OK) {
926  ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err));
927  return "";
928  }
929  auto *ssid_s = reinterpret_cast<const char *>(info.ssid);
930  size_t len = strnlen(ssid_s, sizeof(info.ssid));
931  return {ssid_s, len};
932 }
933 int8_t WiFiComponent::wifi_rssi() {
934  wifi_ap_record_t info;
935  esp_err_t err = esp_wifi_sta_get_ap_info(&info);
936  if (err != ESP_OK) {
937  ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err));
938  return 0;
939  }
940  return info.rssi;
941 }
943  uint8_t primary;
944  wifi_second_chan_t second;
945  esp_err_t err = esp_wifi_get_channel(&primary, &second);
946  if (err != ESP_OK) {
947  ESP_LOGW(TAG, "esp_wifi_get_channel failed: %s", esp_err_to_name(err));
948  return 0;
949  }
950  return primary;
951 }
953  esp_netif_ip_info_t ip;
954  esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip);
955  if (err != ESP_OK) {
956  ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
957  return {};
958  }
959  return network::IPAddress(&ip.netmask);
960 }
962  esp_netif_ip_info_t ip;
963  esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip);
964  if (err != ESP_OK) {
965  ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
966  return {};
967  }
968  return network::IPAddress(&ip.gw);
969 }
971  const ip_addr_t *dns_ip = dns_getserver(num);
972  return network::IPAddress(dns_ip);
973 }
974 
975 } // namespace wifi
976 } // namespace esphome
977 
978 #endif // USE_ESP_IDF
std::array< uint8_t, 6 > bssid_t
const optional< EAPAuth > & get_eap() const
const std::string & get_password() const
network::IPAddress wifi_dns_ip_(int num)
bool wifi_mode_(optional< bool > sta, optional< bool > ap)
const optional< bssid_t > & get_bssid() const
std::string str() const
Definition: ip_address.h:120
bool wifi_apply_output_power_(float output_power)
bool wifi_sta_ip_config_(optional< ManualIP > manual_ip)
void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
mopeka_std_values val[4]
bool has_value() const
Definition: optional.h:87
std::string format_ip6_addr(const esp_ip6_addr_t &ip)
const char *const TAG
Definition: spi.cpp:8
void wifi_process_event_(IDFWiFiEvent *data)
const optional< ManualIP > & get_manual_ip() const
const optional< uint8_t > & get_channel() const
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:181
esp_eap_ttls_phase2_types ttls_phase_2
uint8_t second
Application App
Global storage of Application pointer - only one Application can exist.
bool wifi_ap_ip_config_(optional< ManualIP > manual_ip)
void set_mac_address(uint8_t *mac)
Set the MAC address to use from the provided byte array (6 bytes).
Definition: helpers.cpp:699
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:202
std::array< IPAddress, 5 > IPAddresses
Definition: ip_address.h:139
const char * get_auth_mode_str(uint8_t mode)
std::string size_t len
Definition: helpers.h:292
in_addr ip_addr_t
Definition: ip_address.h:20
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
std::string format_ip4_addr(const esphome_ip4_addr_t &ip)
const std::string & get_ssid() const
uint8_t event_id
Definition: tt21100.cpp:15
const char * get_disconnect_reason_str(uint8_t reason)
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition: helpers.cpp:693
std::string str_snprintf(const char *fmt, size_t len,...)
Definition: helpers.cpp:298
value_type value_or(U const &v) const
Definition: optional.h:93
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
Definition: helpers.cpp:662