ESPHome  2024.4.0
wifi_component_esp32_arduino.cpp
Go to the documentation of this file.
1 #include "wifi_component.h"
2 
3 #ifdef USE_ESP32_FRAMEWORK_ARDUINO
4 
5 #include <esp_wifi.h>
6 
7 #include <algorithm>
8 #include <utility>
9 #ifdef USE_WIFI_WPA2_EAP
10 #include <esp_wpa2.h>
11 #endif
12 #include "lwip/apps/sntp.h"
13 #include "lwip/dns.h"
14 #include "lwip/err.h"
15 
17 #include "esphome/core/hal.h"
18 #include "esphome/core/helpers.h"
19 #include "esphome/core/log.h"
20 #include "esphome/core/util.h"
21 
22 namespace esphome {
23 namespace wifi {
24 
25 static const char *const TAG = "wifi_esp32";
26 
27 static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
28 
30  uint8_t current_mode = WiFiClass::getMode();
31  bool current_sta = current_mode & 0b01;
32  bool current_ap = current_mode & 0b10;
33  bool enable_sta = sta.value_or(current_sta);
34  bool enable_ap = ap.value_or(current_ap);
35  if (current_sta == enable_sta && current_ap == enable_ap)
36  return true;
37 
38  if (enable_sta && !current_sta) {
39  ESP_LOGV(TAG, "Enabling STA.");
40  } else if (!enable_sta && current_sta) {
41  ESP_LOGV(TAG, "Disabling STA.");
42  }
43  if (enable_ap && !current_ap) {
44  ESP_LOGV(TAG, "Enabling AP.");
45  } else if (!enable_ap && current_ap) {
46  ESP_LOGV(TAG, "Disabling AP.");
47  }
48 
49  uint8_t mode = 0;
50  if (enable_sta)
51  mode |= 0b01;
52  if (enable_ap)
53  mode |= 0b10;
54  bool ret = WiFiClass::mode(static_cast<wifi_mode_t>(mode));
55 
56  if (!ret) {
57  ESP_LOGW(TAG, "Setting WiFi mode failed!");
58  }
59 
60  return ret;
61 }
62 bool WiFiComponent::wifi_apply_output_power_(float output_power) {
63  int8_t val = static_cast<int8_t>(output_power * 4);
64  return esp_wifi_set_max_tx_power(val) == ESP_OK;
65 }
67  if (!this->wifi_mode_(true, {}))
68  return false;
69 
70  WiFi.setAutoReconnect(false);
71  delay(10);
72  return true;
73 }
75  wifi_ps_type_t power_save;
76  switch (this->power_save_) {
78  power_save = WIFI_PS_MIN_MODEM;
79  break;
81  power_save = WIFI_PS_MAX_MODEM;
82  break;
84  default:
85  power_save = WIFI_PS_NONE;
86  break;
87  }
88  return esp_wifi_set_ps(power_save) == ESP_OK;
89 }
91  // enable STA
92  if (!this->wifi_mode_(true, {}))
93  return false;
94 
95  tcpip_adapter_dhcp_status_t dhcp_status;
96  tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dhcp_status);
97  if (!manual_ip.has_value()) {
98  // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly,
99  // the built-in SNTP client has a memory leak in certain situations. Disable this feature.
100  // https://github.com/esphome/issues/issues/2299
101  sntp_servermode_dhcp(false);
102 
103  // Use DHCP client
104  if (dhcp_status != TCPIP_ADAPTER_DHCP_STARTED) {
105  esp_err_t err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA);
106  if (err != ESP_OK) {
107  ESP_LOGV(TAG, "Starting DHCP client failed! %d", err);
108  }
109  return err == ESP_OK;
110  }
111  return true;
112  }
113 
114  tcpip_adapter_ip_info_t info;
115  memset(&info, 0, sizeof(info));
116  info.ip = manual_ip->static_ip;
117  info.gw = manual_ip->gateway;
118  info.netmask = manual_ip->subnet;
119 
120  esp_err_t dhcp_stop_ret = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
121  if (dhcp_stop_ret != ESP_OK && dhcp_stop_ret != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) {
122  ESP_LOGV(TAG, "Stopping DHCP client failed! %s", esp_err_to_name(dhcp_stop_ret));
123  }
124 
125  esp_err_t wifi_set_info_ret = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info);
126  if (wifi_set_info_ret != ESP_OK) {
127  ESP_LOGV(TAG, "Setting manual IP info failed! %s", esp_err_to_name(wifi_set_info_ret));
128  }
129 
130  ip_addr_t dns;
131 // TODO: is this needed?
132 #if LWIP_IPV6
133  dns.type = IPADDR_TYPE_V4;
134 #endif /* LWIP_IPV6 */
135  if (manual_ip->dns1.is_set()) {
136  dns = manual_ip->dns1;
137  dns_setserver(0, &dns);
138  }
139  if (manual_ip->dns2.is_set()) {
140  dns = manual_ip->dns2;
141  dns_setserver(1, &dns);
142  }
143 
144  return true;
145 }
146 
148  if (!this->has_sta())
149  return {};
150  network::IPAddresses addresses;
151  tcpip_adapter_ip_info_t ip;
152  esp_err_t err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip);
153  if (err != ESP_OK) {
154  ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
155  // TODO: do something smarter
156  // return false;
157  } else {
158  addresses[0] = network::IPAddress(&ip.ip);
159  }
160 #if USE_NETWORK_IPV6
161  ip6_addr_t ipv6;
162  err = tcpip_adapter_get_ip6_global(TCPIP_ADAPTER_IF_STA, &ipv6);
163  if (err != ESP_OK) {
164  ESP_LOGV(TAG, "esp_netif_get_ip6_gobal failed: %s", esp_err_to_name(err));
165  } else {
166  addresses[1] = network::IPAddress(&ipv6);
167  }
168  err = tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &ipv6);
169  if (err != ESP_OK) {
170  ESP_LOGV(TAG, "esp_netif_get_ip6_linklocal failed: %s", esp_err_to_name(err));
171  } else {
172  addresses[2] = network::IPAddress(&ipv6);
173  }
174 #endif /* USE_NETWORK_IPV6 */
175 
176  return addresses;
177 }
178 
180  // setting is done in SYSTEM_EVENT_STA_START callback
181  return true;
182 }
184  // enable STA
185  if (!this->wifi_mode_(true, {}))
186  return false;
187 
188  // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t
189  wifi_config_t conf;
190  memset(&conf, 0, sizeof(conf));
191  strncpy(reinterpret_cast<char *>(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid));
192  strncpy(reinterpret_cast<char *>(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password));
193 
194  // The weakest authmode to accept in the fast scan mode
195  if (ap.get_password().empty()) {
196  conf.sta.threshold.authmode = WIFI_AUTH_OPEN;
197  } else {
198  conf.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK;
199  }
200 
201 #ifdef USE_WIFI_WPA2_EAP
202  if (ap.get_eap().has_value()) {
203  conf.sta.threshold.authmode = WIFI_AUTH_WPA2_ENTERPRISE;
204  }
205 #endif
206 
207  if (ap.get_bssid().has_value()) {
208  conf.sta.bssid_set = true;
209  memcpy(conf.sta.bssid, ap.get_bssid()->data(), 6);
210  } else {
211  conf.sta.bssid_set = false;
212  }
213  if (ap.get_channel().has_value()) {
214  conf.sta.channel = *ap.get_channel();
215  conf.sta.scan_method = WIFI_FAST_SCAN;
216  } else {
217  conf.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
218  }
219  // Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set.
220  // Units: AP beacon intervals. Defaults to 3 if set to 0.
221  conf.sta.listen_interval = 0;
222 
223  // Protected Management Frame
224  // Device will prefer to connect in PMF mode if other device also advertises PMF capability.
225  conf.sta.pmf_cfg.capable = true;
226  conf.sta.pmf_cfg.required = false;
227 
228  // note, we do our own filtering
229  // The minimum rssi to accept in the fast scan mode
230  conf.sta.threshold.rssi = -127;
231 
232  conf.sta.threshold.authmode = WIFI_AUTH_OPEN;
233 
234  wifi_config_t current_conf;
235  esp_err_t err;
236  esp_wifi_get_config(WIFI_IF_STA, &current_conf);
237 
238  if (memcmp(&current_conf, &conf, sizeof(wifi_config_t)) != 0) { // NOLINT
239  err = esp_wifi_disconnect();
240  if (err != ESP_OK) {
241  ESP_LOGV(TAG, "esp_wifi_disconnect failed! %d", err);
242  return false;
243  }
244  }
245 
246  err = esp_wifi_set_config(WIFI_IF_STA, &conf);
247  if (err != ESP_OK) {
248  ESP_LOGV(TAG, "esp_wifi_set_config failed! %d", err);
249  }
250 
251  if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) {
252  return false;
253  }
254 
255  // setup enterprise authentication if required
256 #ifdef USE_WIFI_WPA2_EAP
257  if (ap.get_eap().has_value()) {
258  // note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0.
259  EAPAuth eap = ap.get_eap().value();
260  err = esp_wifi_sta_wpa2_ent_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length());
261  if (err != ESP_OK) {
262  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", err);
263  }
264  int ca_cert_len = strlen(eap.ca_cert);
265  int client_cert_len = strlen(eap.client_cert);
266  int client_key_len = strlen(eap.client_key);
267  if (ca_cert_len) {
268  err = esp_wifi_sta_wpa2_ent_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1);
269  if (err != ESP_OK) {
270  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", err);
271  }
272  }
273  // workout what type of EAP this is
274  // validation is not required as the config tool has already validated it
275  if (client_cert_len && client_key_len) {
276  // if we have certs, this must be EAP-TLS
277  err = esp_wifi_sta_wpa2_ent_set_cert_key((uint8_t *) eap.client_cert, client_cert_len + 1,
278  (uint8_t *) eap.client_key, client_key_len + 1,
279  (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str()));
280  if (err != ESP_OK) {
281  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", err);
282  }
283  } else {
284  // in the absence of certs, assume this is username/password based
285  err = esp_wifi_sta_wpa2_ent_set_username((uint8_t *) eap.username.c_str(), eap.username.length());
286  if (err != ESP_OK) {
287  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", err);
288  }
289  err = esp_wifi_sta_wpa2_ent_set_password((uint8_t *) eap.password.c_str(), eap.password.length());
290  if (err != ESP_OK) {
291  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err);
292  }
293  }
294  err = esp_wifi_sta_wpa2_ent_enable();
295  if (err != ESP_OK) {
296  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err);
297  }
298  }
299 #endif // USE_WIFI_WPA2_EAP
300 
301  this->wifi_apply_hostname_();
302 
303  s_sta_connecting = true;
304 
305  err = esp_wifi_connect();
306  if (err != ESP_OK) {
307  ESP_LOGW(TAG, "esp_wifi_connect failed! %d", err);
308  return false;
309  }
310 
311  return true;
312 }
313 const char *get_auth_mode_str(uint8_t mode) {
314  switch (mode) {
315  case WIFI_AUTH_OPEN:
316  return "OPEN";
317  case WIFI_AUTH_WEP:
318  return "WEP";
319  case WIFI_AUTH_WPA_PSK:
320  return "WPA PSK";
321  case WIFI_AUTH_WPA2_PSK:
322  return "WPA2 PSK";
323  case WIFI_AUTH_WPA_WPA2_PSK:
324  return "WPA/WPA2 PSK";
325  case WIFI_AUTH_WPA2_ENTERPRISE:
326  return "WPA2 Enterprise";
327  default:
328  return "UNKNOWN";
329  }
330 }
331 
332 using esphome_ip4_addr_t = esp_ip4_addr_t;
333 
334 std::string format_ip4_addr(const esphome_ip4_addr_t &ip) {
335  char buf[20];
336  sprintf(buf, "%u.%u.%u.%u", uint8_t(ip.addr >> 0), uint8_t(ip.addr >> 8), uint8_t(ip.addr >> 16),
337  uint8_t(ip.addr >> 24));
338  return buf;
339 }
340 const char *get_op_mode_str(uint8_t mode) {
341  switch (mode) {
342  case WIFI_OFF:
343  return "OFF";
344  case WIFI_STA:
345  return "STA";
346  case WIFI_AP:
347  return "AP";
348  case WIFI_AP_STA:
349  return "AP+STA";
350  default:
351  return "UNKNOWN";
352  }
353 }
354 const char *get_disconnect_reason_str(uint8_t reason) {
355  switch (reason) {
356  case WIFI_REASON_AUTH_EXPIRE:
357  return "Auth Expired";
358  case WIFI_REASON_AUTH_LEAVE:
359  return "Auth Leave";
360  case WIFI_REASON_ASSOC_EXPIRE:
361  return "Association Expired";
362  case WIFI_REASON_ASSOC_TOOMANY:
363  return "Too Many Associations";
364  case WIFI_REASON_NOT_AUTHED:
365  return "Not Authenticated";
366  case WIFI_REASON_NOT_ASSOCED:
367  return "Not Associated";
368  case WIFI_REASON_ASSOC_LEAVE:
369  return "Association Leave";
370  case WIFI_REASON_ASSOC_NOT_AUTHED:
371  return "Association not Authenticated";
372  case WIFI_REASON_DISASSOC_PWRCAP_BAD:
373  return "Disassociate Power Cap Bad";
374  case WIFI_REASON_DISASSOC_SUPCHAN_BAD:
375  return "Disassociate Supported Channel Bad";
376  case WIFI_REASON_IE_INVALID:
377  return "IE Invalid";
378  case WIFI_REASON_MIC_FAILURE:
379  return "Mic Failure";
380  case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT:
381  return "4-Way Handshake Timeout";
382  case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT:
383  return "Group Key Update Timeout";
384  case WIFI_REASON_IE_IN_4WAY_DIFFERS:
385  return "IE In 4-Way Handshake Differs";
386  case WIFI_REASON_GROUP_CIPHER_INVALID:
387  return "Group Cipher Invalid";
388  case WIFI_REASON_PAIRWISE_CIPHER_INVALID:
389  return "Pairwise Cipher Invalid";
390  case WIFI_REASON_AKMP_INVALID:
391  return "AKMP Invalid";
392  case WIFI_REASON_UNSUPP_RSN_IE_VERSION:
393  return "Unsupported RSN IE version";
394  case WIFI_REASON_INVALID_RSN_IE_CAP:
395  return "Invalid RSN IE Cap";
396  case WIFI_REASON_802_1X_AUTH_FAILED:
397  return "802.1x Authentication Failed";
398  case WIFI_REASON_CIPHER_SUITE_REJECTED:
399  return "Cipher Suite Rejected";
400  case WIFI_REASON_BEACON_TIMEOUT:
401  return "Beacon Timeout";
402  case WIFI_REASON_NO_AP_FOUND:
403  return "AP Not Found";
404  case WIFI_REASON_AUTH_FAIL:
405  return "Authentication Failed";
406  case WIFI_REASON_ASSOC_FAIL:
407  return "Association Failed";
408  case WIFI_REASON_HANDSHAKE_TIMEOUT:
409  return "Handshake Failed";
410  case WIFI_REASON_CONNECTION_FAIL:
411  return "Connection Failed";
412  case WIFI_REASON_UNSPECIFIED:
413  default:
414  return "Unspecified";
415  }
416 }
417 
418 #define ESPHOME_EVENT_ID_WIFI_READY ARDUINO_EVENT_WIFI_READY
419 #define ESPHOME_EVENT_ID_WIFI_SCAN_DONE ARDUINO_EVENT_WIFI_SCAN_DONE
420 #define ESPHOME_EVENT_ID_WIFI_STA_START ARDUINO_EVENT_WIFI_STA_START
421 #define ESPHOME_EVENT_ID_WIFI_STA_STOP ARDUINO_EVENT_WIFI_STA_STOP
422 #define ESPHOME_EVENT_ID_WIFI_STA_CONNECTED ARDUINO_EVENT_WIFI_STA_CONNECTED
423 #define ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED ARDUINO_EVENT_WIFI_STA_DISCONNECTED
424 #define ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE
425 #define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP ARDUINO_EVENT_WIFI_STA_GOT_IP
426 #define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6 ARDUINO_EVENT_WIFI_STA_GOT_IP6
427 #define ESPHOME_EVENT_ID_WIFI_STA_LOST_IP ARDUINO_EVENT_WIFI_STA_LOST_IP
428 #define ESPHOME_EVENT_ID_WIFI_AP_START ARDUINO_EVENT_WIFI_AP_START
429 #define ESPHOME_EVENT_ID_WIFI_AP_STOP ARDUINO_EVENT_WIFI_AP_STOP
430 #define ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED ARDUINO_EVENT_WIFI_AP_STACONNECTED
431 #define ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED ARDUINO_EVENT_WIFI_AP_STADISCONNECTED
432 #define ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED
433 #define ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED
434 #define ESPHOME_EVENT_ID_WIFI_AP_GOT_IP6 ARDUINO_EVENT_WIFI_AP_GOT_IP6
435 using esphome_wifi_event_id_t = arduino_event_id_t;
436 using esphome_wifi_event_info_t = arduino_event_info_t;
437 
439  switch (event) {
440  case ESPHOME_EVENT_ID_WIFI_READY: {
441  ESP_LOGV(TAG, "Event: WiFi ready");
442  break;
443  }
444  case ESPHOME_EVENT_ID_WIFI_SCAN_DONE: {
445  auto it = info.wifi_scan_done;
446  ESP_LOGV(TAG, "Event: WiFi Scan Done status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id);
447 
448  this->wifi_scan_done_callback_();
449  break;
450  }
451  case ESPHOME_EVENT_ID_WIFI_STA_START: {
452  ESP_LOGV(TAG, "Event: WiFi STA start");
453  tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, App.get_name().c_str());
454  break;
455  }
456  case ESPHOME_EVENT_ID_WIFI_STA_STOP: {
457  ESP_LOGV(TAG, "Event: WiFi STA stop");
458  break;
459  }
460  case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: {
461  auto it = info.wifi_sta_connected;
462  char buf[33];
463  memcpy(buf, it.ssid, it.ssid_len);
464  buf[it.ssid_len] = '\0';
465  ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf,
466  format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode));
467 #if USE_NETWORK_IPV6
468  this->set_timeout(100, [] { WiFi.enableIpV6(); });
469 #endif /* USE_NETWORK_IPV6 */
470 
471  break;
472  }
473  case ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED: {
474  auto it = info.wifi_sta_disconnected;
475  char buf[33];
476  memcpy(buf, it.ssid, it.ssid_len);
477  buf[it.ssid_len] = '\0';
478  if (it.reason == WIFI_REASON_NO_AP_FOUND) {
479  ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf);
480  } else {
481  ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf,
482  format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason));
483  }
484 
485  uint8_t reason = it.reason;
486  if (reason == WIFI_REASON_AUTH_EXPIRE || reason == WIFI_REASON_BEACON_TIMEOUT ||
487  reason == WIFI_REASON_NO_AP_FOUND || reason == WIFI_REASON_ASSOC_FAIL ||
488  reason == WIFI_REASON_HANDSHAKE_TIMEOUT) {
489  err_t err = esp_wifi_disconnect();
490  if (err != ESP_OK) {
491  ESP_LOGV(TAG, "Disconnect failed: %s", esp_err_to_name(err));
492  }
493  this->error_from_callback_ = true;
494  }
495 
496  s_sta_connecting = false;
497  break;
498  }
499  case ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE: {
500  auto it = info.wifi_sta_authmode_change;
501  ESP_LOGV(TAG, "Event: Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode),
502  get_auth_mode_str(it.new_mode));
503  // Mitigate CVE-2020-12638
504  // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors
505  if (it.old_mode != WIFI_AUTH_OPEN && it.new_mode == WIFI_AUTH_OPEN) {
506  ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting...");
507  // we can't call retry_connect() from this context, so disconnect immediately
508  // and notify main thread with error_from_callback_
509  err_t err = esp_wifi_disconnect();
510  if (err != ESP_OK) {
511  ESP_LOGW(TAG, "Disconnect failed: %s", esp_err_to_name(err));
512  }
513  this->error_from_callback_ = true;
514  }
515  break;
516  }
517  case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP: {
518  auto it = info.got_ip.ip_info;
519  ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(),
520  format_ip4_addr(it.gw).c_str());
521  this->got_ipv4_address_ = true;
522 #if USE_NETWORK_IPV6
523  s_sta_connecting = this->num_ipv6_addresses_ < USE_NETWORK_MIN_IPV6_ADDR_COUNT;
524 #else
525  s_sta_connecting = false;
526 #endif /* USE_NETWORK_IPV6 */
527  break;
528  }
529 #if USE_NETWORK_IPV6
530  case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: {
531  auto it = info.got_ip6.ip6_info;
532  ESP_LOGV(TAG, "Got IPv6 address=" IPV6STR, IPV62STR(it.ip));
533  this->num_ipv6_addresses_++;
534  s_sta_connecting = !(this->got_ipv4_address_ & (this->num_ipv6_addresses_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT));
535  break;
536  }
537 #endif /* USE_NETWORK_IPV6 */
538  case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: {
539  ESP_LOGV(TAG, "Event: Lost IP");
540  this->got_ipv4_address_ = false;
541  break;
542  }
543  case ESPHOME_EVENT_ID_WIFI_AP_START: {
544  ESP_LOGV(TAG, "Event: WiFi AP start");
545  break;
546  }
547  case ESPHOME_EVENT_ID_WIFI_AP_STOP: {
548  ESP_LOGV(TAG, "Event: WiFi AP stop");
549  break;
550  }
551  case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: {
552  auto it = info.wifi_sta_connected;
553  auto &mac = it.bssid;
554  ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(mac).c_str());
555  break;
556  }
557  case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: {
558  auto it = info.wifi_sta_disconnected;
559  auto &mac = it.bssid;
560  ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(mac).c_str());
561  break;
562  }
563  case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: {
564  ESP_LOGV(TAG, "Event: AP client assigned IP");
565  break;
566  }
567  case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: {
568  auto it = info.wifi_ap_probereqrecved;
569  ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi);
570  break;
571  }
572  default:
573  break;
574  }
575 }
577  auto f = std::bind(&WiFiComponent::wifi_event_callback_, this, std::placeholders::_1, std::placeholders::_2);
578  WiFi.onEvent(f);
579  WiFi.persistent(false);
580  // Make sure WiFi is in clean state before anything starts
581  this->wifi_mode_(false, false);
582 }
584  auto status = WiFiClass::status();
585  if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST) {
587  } else if (status == WL_NO_SSID_AVAIL) {
589  } else if (s_sta_connecting) {
591  } else if (status == WL_CONNECTED) {
593  }
595 }
597  // enable STA
598  if (!this->wifi_mode_(true, {}))
599  return false;
600 
601  // need to use WiFi because of WiFiScanClass allocations :(
602  int16_t err = WiFi.scanNetworks(true, true, passive, 200);
603  if (err != WIFI_SCAN_RUNNING) {
604  ESP_LOGV(TAG, "WiFi.scanNetworks failed! %d", err);
605  return false;
606  }
607 
608  return true;
609 }
611  this->scan_result_.clear();
612 
613  int16_t num = WiFi.scanComplete();
614  if (num < 0)
615  return;
616 
617  this->scan_result_.reserve(static_cast<unsigned int>(num));
618  for (int i = 0; i < num; i++) {
619  String ssid = WiFi.SSID(i);
620  wifi_auth_mode_t authmode = WiFi.encryptionType(i);
621  int32_t rssi = WiFi.RSSI(i);
622  uint8_t *bssid = WiFi.BSSID(i);
623  int32_t channel = WiFi.channel(i);
624 
625  WiFiScanResult scan({bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]}, std::string(ssid.c_str()),
626  channel, rssi, authmode != WIFI_AUTH_OPEN, ssid.length() == 0);
627  this->scan_result_.push_back(scan);
628  }
629  WiFi.scanDelete();
630  this->scan_done_ = true;
631 }
632 
633 #ifdef USE_WIFI_AP
635  esp_err_t err;
636 
637  // enable AP
638  if (!this->wifi_mode_({}, true))
639  return false;
640 
641  tcpip_adapter_ip_info_t info;
642  memset(&info, 0, sizeof(info));
643  if (manual_ip.has_value()) {
644  info.ip = manual_ip->static_ip;
645  info.gw = manual_ip->gateway;
646  info.netmask = manual_ip->subnet;
647  } else {
648  info.ip = network::IPAddress(192, 168, 4, 1);
649  info.gw = network::IPAddress(192, 168, 4, 1);
650  info.netmask = network::IPAddress(255, 255, 255, 0);
651  }
652  tcpip_adapter_dhcp_status_t dhcp_status;
653  tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status);
654  err = tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP);
655  if (err != ESP_OK) {
656  ESP_LOGV(TAG, "tcpip_adapter_dhcps_stop failed! %d", err);
657  return false;
658  }
659 
660  err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_AP, &info);
661  if (err != ESP_OK) {
662  ESP_LOGV(TAG, "tcpip_adapter_set_ip_info failed! %d", err);
663  return false;
664  }
665 
666  dhcps_lease_t lease;
667  lease.enable = true;
668  network::IPAddress start_address = network::IPAddress(&info.ip);
669  start_address += 99;
670  lease.start_ip = start_address;
671  ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str());
672  start_address += 100;
673  lease.end_ip = start_address;
674  ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str());
675  err = tcpip_adapter_dhcps_option(TCPIP_ADAPTER_OP_SET, TCPIP_ADAPTER_REQUESTED_IP_ADDRESS, &lease, sizeof(lease));
676 
677  if (err != ESP_OK) {
678  ESP_LOGV(TAG, "tcpip_adapter_dhcps_option failed! %d", err);
679  return false;
680  }
681 
682  err = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP);
683 
684  if (err != ESP_OK) {
685  ESP_LOGV(TAG, "tcpip_adapter_dhcps_start failed! %d", err);
686  return false;
687  }
688 
689  return true;
690 }
691 
693  // enable AP
694  if (!this->wifi_mode_({}, true))
695  return false;
696 
697  wifi_config_t conf;
698  memset(&conf, 0, sizeof(conf));
699  strncpy(reinterpret_cast<char *>(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid));
700  conf.ap.channel = ap.get_channel().value_or(1);
701  conf.ap.ssid_hidden = ap.get_ssid().size();
702  conf.ap.max_connection = 5;
703  conf.ap.beacon_interval = 100;
704 
705  if (ap.get_password().empty()) {
706  conf.ap.authmode = WIFI_AUTH_OPEN;
707  *conf.ap.password = 0;
708  } else {
709  conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
710  strncpy(reinterpret_cast<char *>(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password));
711  }
712 
713  conf.ap.pairwise_cipher = WIFI_CIPHER_TYPE_CCMP;
714 
715  esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &conf);
716  if (err != ESP_OK) {
717  ESP_LOGV(TAG, "esp_wifi_set_config failed! %d", err);
718  return false;
719  }
720 
721  yield();
722 
723  if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
724  ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!");
725  return false;
726  }
727 
728  return true;
729 }
730 
732  tcpip_adapter_ip_info_t ip;
733  tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip);
734  return network::IPAddress(&ip.ip);
735 }
736 #endif // USE_WIFI_AP
737 
738 bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); }
739 
741  bssid_t bssid{};
742  uint8_t *raw_bssid = WiFi.BSSID();
743  if (raw_bssid != nullptr) {
744  for (size_t i = 0; i < bssid.size(); i++)
745  bssid[i] = raw_bssid[i];
746  }
747  return bssid;
748 }
749 std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); }
750 int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); }
751 int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); }
756 
757 } // namespace wifi
758 } // namespace esphome
759 
760 #endif // USE_ESP32_FRAMEWORK_ARDUINO
std::array< uint8_t, 6 > bssid_t
const optional< EAPAuth > & get_eap() const
static std::string format_mac_addr(const uint8_t mac[6])
const std::string & get_password() const
WiFiPowerSaveMode power_save_
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
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
bool wifi_apply_output_power_(float output_power)
bool wifi_sta_ip_config_(optional< ManualIP > manual_ip)
mopeka_std_values val[4]
bool has_value() const
Definition: optional.h:87
const char * get_op_mode_str(uint8_t mode)
std::vector< WiFiScanResult > scan_result_
const char *const TAG
Definition: spi.cpp:8
const optional< ManualIP > & get_manual_ip() const
const optional< uint8_t > & get_channel() const
arduino_event_id_t esphome_wifi_event_id_t
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:151
Application App
Global storage of Application pointer - only one Application can exist.
bool wifi_ap_ip_config_(optional< ManualIP > manual_ip)
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:174
std::array< IPAddress, 5 > IPAddresses
Definition: ip_address.h:139
void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info)
arduino_event_info_t esphome_wifi_event_info_t
uint8_t status
Definition: bl0942.h:23
const char * get_auth_mode_str(uint8_t mode)
void IRAM_ATTR HOT yield()
Definition: core.cpp:24
in_addr ip_addr_t
Definition: ip_address.h:20
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
std::string format_ip4_addr(const esphome_ip4_addr_t &ip)
const std::string & get_ssid() const
const char * get_disconnect_reason_str(uint8_t reason)
value_type value_or(U const &v) const
Definition: optional.h:93
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26