ESPHome  2024.2.1
wifi_component_esp8266.cpp
Go to the documentation of this file.
1 #include "wifi_component.h"
2 #include "esphome/core/defines.h"
3 
4 #ifdef USE_ESP8266
5 
6 #include <user_interface.h>
7 
8 #include <utility>
9 #include <algorithm>
10 #ifdef USE_WIFI_WPA2_EAP
11 #include <wpa2_enterprise.h>
12 #endif
13 
14 extern "C" {
15 #include "lwip/err.h"
16 #include "lwip/dns.h"
17 #include "lwip/dhcp.h"
18 #include "lwip/init.h" // LWIP_VERSION_
19 #include "lwip/apps/sntp.h"
20 #if LWIP_IPV6
21 #include "lwip/netif.h" // struct netif
22 #include <AddrList.h>
23 #endif
24 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0)
25 #include "LwipDhcpServer.h"
26 #define wifi_softap_set_dhcps_lease(lease) dhcpSoftAP.set_dhcps_lease(lease)
27 #define wifi_softap_set_dhcps_lease_time(time) dhcpSoftAP.set_dhcps_lease_time(time)
28 #define wifi_softap_set_dhcps_offer_option(offer, mode) dhcpSoftAP.set_dhcps_offer_option(offer, mode)
29 #endif
30 }
31 
32 #include "esphome/core/helpers.h"
33 #include "esphome/core/log.h"
34 #include "esphome/core/hal.h"
35 #include "esphome/core/util.h"
37 
38 namespace esphome {
39 namespace wifi {
40 
41 static const char *const TAG = "wifi_esp8266";
42 
43 static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
44 static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
45 static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
46 static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
47 static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
48 
49 bool WiFiComponent::wifi_mode_(optional<bool> sta, optional<bool> ap) {
50  uint8_t current_mode = wifi_get_opmode();
51  bool current_sta = current_mode & 0b01;
52  bool current_ap = current_mode & 0b10;
53  bool target_sta = sta.value_or(current_sta);
54  bool target_ap = ap.value_or(current_ap);
55  if (current_sta == target_sta && current_ap == target_ap)
56  return true;
57 
58  if (target_sta && !current_sta) {
59  ESP_LOGV(TAG, "Enabling STA.");
60  } else if (!target_sta && current_sta) {
61  ESP_LOGV(TAG, "Disabling STA.");
62  // Stop DHCP client when disabling STA
63  // See https://github.com/esp8266/Arduino/pull/5703
64  wifi_station_dhcpc_stop();
65  }
66  if (target_ap && !current_ap) {
67  ESP_LOGV(TAG, "Enabling AP.");
68  } else if (!target_ap && current_ap) {
69  ESP_LOGV(TAG, "Disabling AP.");
70  }
71 
72  ETS_UART_INTR_DISABLE();
73  uint8_t mode = 0;
74  if (target_sta)
75  mode |= 0b01;
76  if (target_ap)
77  mode |= 0b10;
78  bool ret = wifi_set_opmode_current(mode);
79  ETS_UART_INTR_ENABLE();
80 
81  if (!ret) {
82  ESP_LOGW(TAG, "Setting WiFi mode failed!");
83  }
84 
85  return ret;
86 }
88  sleep_type_t power_save;
89  switch (this->power_save_) {
91  power_save = LIGHT_SLEEP_T;
92  break;
94  power_save = MODEM_SLEEP_T;
95  break;
97  default:
98  power_save = NONE_SLEEP_T;
99  break;
100  }
101  wifi_fpm_auto_sleep_set_in_null_mode(1);
102  return wifi_set_sleep_type(power_save);
103 }
104 
105 #if LWIP_VERSION_MAJOR != 1
106 /*
107  lwip v2 needs to be notified of IP changes, see also
108  https://github.com/d-a-v/Arduino/blob/0e7d21e17144cfc5f53c016191daca8723e89ee8/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp#L251
109  */
110 #undef netif_set_addr // need to call lwIP-v1.4 netif_set_addr()
111 extern "C" {
112 struct netif *eagle_lwip_getif(int netif_index);
113 void netif_set_addr(struct netif *netif, const ip4_addr_t *ip, const ip4_addr_t *netmask, const ip4_addr_t *gw);
114 };
115 #endif
116 
117 bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
118  // enable STA
119  if (!this->wifi_mode_(true, {}))
120  return false;
121 
122  enum dhcp_status dhcp_status = wifi_station_dhcpc_status();
123  if (!manual_ip.has_value()) {
124  // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly,
125  // the built-in SNTP client has a memory leak in certain situations. Disable this feature.
126  // https://github.com/esphome/issues/issues/2299
127  sntp_servermode_dhcp(false);
128 
129  // Use DHCP client
130  if (dhcp_status != DHCP_STARTED) {
131  bool ret = wifi_station_dhcpc_start();
132  if (!ret) {
133  ESP_LOGV(TAG, "Starting DHCP client failed!");
134  }
135  return ret;
136  }
137  return true;
138  }
139 
140  bool ret = true;
141 
142 #if LWIP_VERSION_MAJOR != 1
143  // get current->previous IP address
144  // (check below)
145  ip_info previp{};
146  wifi_get_ip_info(STATION_IF, &previp);
147 #endif
148 
149  struct ip_info info {};
150  info.ip = manual_ip->static_ip;
151  info.gw = manual_ip->gateway;
152  info.netmask = manual_ip->subnet;
153 
154  if (dhcp_status == DHCP_STARTED) {
155  bool dhcp_stop_ret = wifi_station_dhcpc_stop();
156  if (!dhcp_stop_ret) {
157  ESP_LOGV(TAG, "Stopping DHCP client failed!");
158  ret = false;
159  }
160  }
161  bool wifi_set_info_ret = wifi_set_ip_info(STATION_IF, &info);
162  if (!wifi_set_info_ret) {
163  ESP_LOGV(TAG, "Setting manual IP info failed!");
164  ret = false;
165  }
166 
167  ip_addr_t dns;
168  if (manual_ip->dns1.is_set()) {
169  dns = manual_ip->dns1;
170  dns_setserver(0, &dns);
171  }
172  if (manual_ip->dns2.is_set()) {
173  dns = manual_ip->dns2;
174  dns_setserver(1, &dns);
175  }
176 
177 #if LWIP_VERSION_MAJOR != 1
178  // trigger address change by calling lwIP-v1.4 api
179  // only when ip is already set by other mean (generally dhcp)
180  if (previp.ip.addr != 0 && previp.ip.addr != info.ip.addr) {
181  netif_set_addr(eagle_lwip_getif(STATION_IF), reinterpret_cast<const ip4_addr_t *>(&info.ip),
182  reinterpret_cast<const ip4_addr_t *>(&info.netmask), reinterpret_cast<const ip4_addr_t *>(&info.gw));
183  }
184 #endif
185  return ret;
186 }
187 
188 network::IPAddress WiFiComponent::wifi_sta_ip() {
189  if (!this->has_sta())
190  return {};
191  struct ip_info ip {};
192  wifi_get_ip_info(STATION_IF, &ip);
193  return network::IPAddress(&ip.ip);
194 }
196  const std::string &hostname = App.get_name();
197  bool ret = wifi_station_set_hostname(const_cast<char *>(hostname.c_str()));
198  if (!ret) {
199  ESP_LOGV(TAG, "Setting WiFi Hostname failed!");
200  }
201 
202  // inform dhcp server of hostname change using dhcp_renew()
203  for (netif *intf = netif_list; intf; intf = intf->next) {
204  // unconditionally update all known interfaces
205 #if LWIP_VERSION_MAJOR == 1
206  intf->hostname = (char *) wifi_station_get_hostname();
207 #else
208  intf->hostname = wifi_station_get_hostname();
209 #endif
210  if (netif_dhcp_data(intf) != nullptr) {
211  // renew already started DHCP leases
212  err_t lwipret = dhcp_renew(intf);
213  if (lwipret != ERR_OK) {
214  ESP_LOGW(TAG, "wifi_apply_hostname_(%s): lwIP error %d on interface %c%c (index %d)", intf->hostname,
215  (int) lwipret, intf->name[0], intf->name[1], intf->num);
216  ret = false;
217  }
218  }
219  }
220 
221  return ret;
222 }
223 
224 bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
225  // enable STA
226  if (!this->wifi_mode_(true, {}))
227  return false;
228 
229  this->wifi_disconnect_();
230 
231  struct station_config conf {};
232  memset(&conf, 0, sizeof(conf));
233  strncpy(reinterpret_cast<char *>(conf.ssid), ap.get_ssid().c_str(), sizeof(conf.ssid));
234  strncpy(reinterpret_cast<char *>(conf.password), ap.get_password().c_str(), sizeof(conf.password));
235 
236  if (ap.get_bssid().has_value()) {
237  conf.bssid_set = 1;
238  memcpy(conf.bssid, ap.get_bssid()->data(), 6);
239  } else {
240  conf.bssid_set = 0;
241  }
242 
243 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0)
244  if (ap.get_password().empty()) {
245  conf.threshold.authmode = AUTH_OPEN;
246  } else {
247  // Only allow auth modes with at least WPA
248  conf.threshold.authmode = AUTH_WPA_PSK;
249  }
250  conf.threshold.rssi = -127;
251 #endif
252 
253  ETS_UART_INTR_DISABLE();
254  bool ret = wifi_station_set_config_current(&conf);
255  ETS_UART_INTR_ENABLE();
256 
257  if (!ret) {
258  ESP_LOGV(TAG, "Setting WiFi Station config failed!");
259  return false;
260  }
261 
262  if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) {
263  return false;
264  }
265 
266  // setup enterprise authentication if required
267 #ifdef USE_WIFI_WPA2_EAP
268  if (ap.get_eap().has_value()) {
269  // note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0.
270  EAPAuth eap = ap.get_eap().value();
271  ret = wifi_station_set_enterprise_identity((uint8_t *) eap.identity.c_str(), eap.identity.length());
272  if (ret) {
273  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", ret);
274  }
275  int ca_cert_len = strlen(eap.ca_cert);
276  int client_cert_len = strlen(eap.client_cert);
277  int client_key_len = strlen(eap.client_key);
278  if (ca_cert_len) {
279  ret = wifi_station_set_enterprise_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1);
280  if (ret) {
281  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", ret);
282  }
283  }
284  // workout what type of EAP this is
285  // validation is not required as the config tool has already validated it
286  if (client_cert_len && client_key_len) {
287  // if we have certs, this must be EAP-TLS
288  ret = wifi_station_set_enterprise_cert_key((uint8_t *) eap.client_cert, client_cert_len + 1,
289  (uint8_t *) eap.client_key, client_key_len + 1,
290  (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str()));
291  if (ret) {
292  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", ret);
293  }
294  } else {
295  // in the absence of certs, assume this is username/password based
296  ret = wifi_station_set_enterprise_username((uint8_t *) eap.username.c_str(), eap.username.length());
297  if (ret) {
298  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", ret);
299  }
300  ret = wifi_station_set_enterprise_password((uint8_t *) eap.password.c_str(), eap.password.length());
301  if (ret) {
302  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", ret);
303  }
304  }
305  ret = wifi_station_set_wpa2_enterprise_auth(true);
306  if (ret) {
307  ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", ret);
308  }
309  }
310 #endif // USE_WIFI_WPA2_EAP
311 
312  this->wifi_apply_hostname_();
313 
314  // Reset flags, do this _before_ wifi_station_connect as the callback method
315  // may be called from wifi_station_connect
316  s_sta_connecting = true;
317  s_sta_connected = false;
318  s_sta_got_ip = false;
319  s_sta_connect_error = false;
320  s_sta_connect_not_found = false;
321 
322  ETS_UART_INTR_DISABLE();
323  ret = wifi_station_connect();
324  ETS_UART_INTR_ENABLE();
325  if (!ret) {
326  ESP_LOGV(TAG, "wifi_station_connect failed!");
327  return false;
328  }
329 
330 #if ENABLE_IPV6
331  for (bool configured = false; !configured;) {
332  for (auto addr : addrList) {
333  ESP_LOGV(TAG, "Address %s", addr.toString().c_str());
334  if ((configured = !addr.isLocal() && addr.isV6())) {
335  break;
336  }
337  }
338  delay(500); // NOLINT
339  }
340 #endif
341 
342  if (ap.get_channel().has_value()) {
343  ret = wifi_set_channel(*ap.get_channel());
344  if (!ret) {
345  ESP_LOGV(TAG, "wifi_set_channel failed!");
346  return false;
347  }
348  }
349 
350  return true;
351 }
352 
353 class WiFiMockClass : public ESP8266WiFiGenericClass {
354  public:
355  static void _event_callback(void *event) { ESP8266WiFiGenericClass::_eventCallback(event); } // NOLINT
356 };
357 
358 const LogString *get_auth_mode_str(uint8_t mode) {
359  switch (mode) {
360  case AUTH_OPEN:
361  return LOG_STR("OPEN");
362  case AUTH_WEP:
363  return LOG_STR("WEP");
364  case AUTH_WPA_PSK:
365  return LOG_STR("WPA PSK");
366  case AUTH_WPA2_PSK:
367  return LOG_STR("WPA2 PSK");
368  case AUTH_WPA_WPA2_PSK:
369  return LOG_STR("WPA/WPA2 PSK");
370  default:
371  return LOG_STR("UNKNOWN");
372  }
373 }
374 #ifdef ipv4_addr
375 std::string format_ip_addr(struct ipv4_addr ip) {
376  char buf[20];
377  sprintf(buf, "%u.%u.%u.%u", uint8_t(ip.addr >> 0), uint8_t(ip.addr >> 8), uint8_t(ip.addr >> 16),
378  uint8_t(ip.addr >> 24));
379  return buf;
380 }
381 #else
382 std::string format_ip_addr(struct ip_addr ip) {
383  char buf[20];
384  sprintf(buf, "%u.%u.%u.%u", uint8_t(ip.addr >> 0), uint8_t(ip.addr >> 8), uint8_t(ip.addr >> 16),
385  uint8_t(ip.addr >> 24));
386  return buf;
387 }
388 #endif
389 const LogString *get_op_mode_str(uint8_t mode) {
390  switch (mode) {
391  case WIFI_OFF:
392  return LOG_STR("OFF");
393  case WIFI_STA:
394  return LOG_STR("STA");
395  case WIFI_AP:
396  return LOG_STR("AP");
397  case WIFI_AP_STA:
398  return LOG_STR("AP+STA");
399  default:
400  return LOG_STR("UNKNOWN");
401  }
402 }
403 
404 const LogString *get_disconnect_reason_str(uint8_t reason) {
405  /* If this were one big switch statement, GCC would generate a lookup table for it. However, the values of the
406  * REASON_* constants aren't continuous, and GCC will fill in the gap with the default value -- wasting 4 bytes of RAM
407  * per entry. As there's ~175 default entries, this wastes 700 bytes of RAM.
408  */
409  if (reason <= REASON_CIPHER_SUITE_REJECTED) { // This must be the last constant with a value <200
410  switch (reason) {
411  case REASON_AUTH_EXPIRE:
412  return LOG_STR("Auth Expired");
413  case REASON_AUTH_LEAVE:
414  return LOG_STR("Auth Leave");
415  case REASON_ASSOC_EXPIRE:
416  return LOG_STR("Association Expired");
417  case REASON_ASSOC_TOOMANY:
418  return LOG_STR("Too Many Associations");
419  case REASON_NOT_AUTHED:
420  return LOG_STR("Not Authenticated");
421  case REASON_NOT_ASSOCED:
422  return LOG_STR("Not Associated");
423  case REASON_ASSOC_LEAVE:
424  return LOG_STR("Association Leave");
425  case REASON_ASSOC_NOT_AUTHED:
426  return LOG_STR("Association not Authenticated");
427  case REASON_DISASSOC_PWRCAP_BAD:
428  return LOG_STR("Disassociate Power Cap Bad");
429  case REASON_DISASSOC_SUPCHAN_BAD:
430  return LOG_STR("Disassociate Supported Channel Bad");
431  case REASON_IE_INVALID:
432  return LOG_STR("IE Invalid");
433  case REASON_MIC_FAILURE:
434  return LOG_STR("Mic Failure");
435  case REASON_4WAY_HANDSHAKE_TIMEOUT:
436  return LOG_STR("4-Way Handshake Timeout");
437  case REASON_GROUP_KEY_UPDATE_TIMEOUT:
438  return LOG_STR("Group Key Update Timeout");
439  case REASON_IE_IN_4WAY_DIFFERS:
440  return LOG_STR("IE In 4-Way Handshake Differs");
441  case REASON_GROUP_CIPHER_INVALID:
442  return LOG_STR("Group Cipher Invalid");
443  case REASON_PAIRWISE_CIPHER_INVALID:
444  return LOG_STR("Pairwise Cipher Invalid");
445  case REASON_AKMP_INVALID:
446  return LOG_STR("AKMP Invalid");
447  case REASON_UNSUPP_RSN_IE_VERSION:
448  return LOG_STR("Unsupported RSN IE version");
449  case REASON_INVALID_RSN_IE_CAP:
450  return LOG_STR("Invalid RSN IE Cap");
451  case REASON_802_1X_AUTH_FAILED:
452  return LOG_STR("802.1x Authentication Failed");
453  case REASON_CIPHER_SUITE_REJECTED:
454  return LOG_STR("Cipher Suite Rejected");
455  }
456  }
457 
458  switch (reason) {
459  case REASON_BEACON_TIMEOUT:
460  return LOG_STR("Beacon Timeout");
461  case REASON_NO_AP_FOUND:
462  return LOG_STR("AP Not Found");
463  case REASON_AUTH_FAIL:
464  return LOG_STR("Authentication Failed");
465  case REASON_ASSOC_FAIL:
466  return LOG_STR("Association Failed");
467  case REASON_HANDSHAKE_TIMEOUT:
468  return LOG_STR("Handshake Failed");
469  case REASON_UNSPECIFIED:
470  default:
471  return LOG_STR("Unspecified");
472  }
473 }
474 
475 void WiFiComponent::wifi_event_callback(System_Event_t *event) {
476  switch (event->event) {
477  case EVENT_STAMODE_CONNECTED: {
478  auto it = event->event_info.connected;
479  char buf[33];
480  memcpy(buf, it.ssid, it.ssid_len);
481  buf[it.ssid_len] = '\0';
482  ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=%s channel=%u", buf, format_mac_addr(it.bssid).c_str(),
483  it.channel);
484  s_sta_connected = true;
485  break;
486  }
487  case EVENT_STAMODE_DISCONNECTED: {
488  auto it = event->event_info.disconnected;
489  char buf[33];
490  memcpy(buf, it.ssid, it.ssid_len);
491  buf[it.ssid_len] = '\0';
492  if (it.reason == REASON_NO_AP_FOUND) {
493  ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf);
494  s_sta_connect_not_found = true;
495  } else {
496  ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf,
497  format_mac_addr(it.bssid).c_str(), LOG_STR_ARG(get_disconnect_reason_str(it.reason)));
498  s_sta_connect_error = true;
499  }
500  s_sta_connected = false;
501  s_sta_connecting = false;
502  break;
503  }
504  case EVENT_STAMODE_AUTHMODE_CHANGE: {
505  auto it = event->event_info.auth_change;
506  ESP_LOGV(TAG, "Event: Changed AuthMode old=%s new=%s", LOG_STR_ARG(get_auth_mode_str(it.old_mode)),
507  LOG_STR_ARG(get_auth_mode_str(it.new_mode)));
508  // Mitigate CVE-2020-12638
509  // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors
510  if (it.old_mode != AUTH_OPEN && it.new_mode == AUTH_OPEN) {
511  ESP_LOGW(TAG, "Potential Authmode downgrade detected, disconnecting...");
512  // we can't call retry_connect() from this context, so disconnect immediately
513  // and notify main thread with error_from_callback_
514  wifi_station_disconnect();
516  }
517  break;
518  }
519  case EVENT_STAMODE_GOT_IP: {
520  auto it = event->event_info.got_ip;
521  ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s netmask=%s", format_ip_addr(it.ip).c_str(),
522  format_ip_addr(it.gw).c_str(), format_ip_addr(it.mask).c_str());
523  s_sta_got_ip = true;
524  break;
525  }
526  case EVENT_STAMODE_DHCP_TIMEOUT: {
527  ESP_LOGW(TAG, "Event: Getting IP address timeout");
528  break;
529  }
530  case EVENT_SOFTAPMODE_STACONNECTED: {
531  auto it = event->event_info.sta_connected;
532  ESP_LOGV(TAG, "Event: AP client connected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid);
533  break;
534  }
535  case EVENT_SOFTAPMODE_STADISCONNECTED: {
536  auto it = event->event_info.sta_disconnected;
537  ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s aid=%u", format_mac_addr(it.mac).c_str(), it.aid);
538  break;
539  }
540  case EVENT_SOFTAPMODE_PROBEREQRECVED: {
541  auto it = event->event_info.ap_probereqrecved;
542  ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi);
543  break;
544  }
545 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0)
546  case EVENT_OPMODE_CHANGED: {
547  auto it = event->event_info.opmode_changed;
548  ESP_LOGV(TAG, "Event: Changed Mode old=%s new=%s", LOG_STR_ARG(get_op_mode_str(it.old_opmode)),
549  LOG_STR_ARG(get_op_mode_str(it.new_opmode)));
550  break;
551  }
552  case EVENT_SOFTAPMODE_DISTRIBUTE_STA_IP: {
553  auto it = event->event_info.distribute_sta_ip;
554  ESP_LOGV(TAG, "Event: AP Distribute Station IP MAC=%s IP=%s aid=%u", format_mac_addr(it.mac).c_str(),
555  format_ip_addr(it.ip).c_str(), it.aid);
556  break;
557  }
558 #endif
559  default:
560  break;
561  }
562 
563  if (event->event == EVENT_STAMODE_DISCONNECTED) {
565  }
566 
567  WiFiMockClass::_event_callback(event);
568 }
569 
570 bool WiFiComponent::wifi_apply_output_power_(float output_power) {
571  uint8_t val = static_cast<uint8_t>(output_power * 4);
572  system_phy_set_max_tpw(val);
573  return true;
574 }
576  if (!this->wifi_mode_(true, {}))
577  return false;
578 
579  bool ret1, ret2;
580  ETS_UART_INTR_DISABLE();
581  ret1 = wifi_station_set_auto_connect(0);
582  ret2 = wifi_station_set_reconnect_policy(false);
583  ETS_UART_INTR_ENABLE();
584 
585  if (!ret1 || !ret2) {
586  ESP_LOGV(TAG, "Disabling Auto-Connect failed!");
587  }
588 
589  delay(10);
590  return true;
591 }
592 
594  wifi_set_event_handler_cb(&WiFiComponent::wifi_event_callback);
595 
596  // Make sure WiFi is in clean state before anything starts
597  this->wifi_mode_(false, false);
598 }
599 
601  station_status_t status = wifi_station_get_connect_status();
602  switch (status) {
603  case STATION_GOT_IP:
605  case STATION_NO_AP_FOUND:
607  ;
608  case STATION_CONNECT_FAIL:
609  case STATION_WRONG_PASSWORD:
611  case STATION_CONNECTING:
613  case STATION_IDLE:
614  default:
616  }
617 }
618 bool WiFiComponent::wifi_scan_start_(bool passive) {
619  static bool first_scan = false;
620 
621  // enable STA
622  if (!this->wifi_mode_(true, {}))
623  return false;
624 
625  struct scan_config config {};
626  memset(&config, 0, sizeof(config));
627  config.ssid = nullptr;
628  config.bssid = nullptr;
629  config.channel = 0;
630  config.show_hidden = 1;
631 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0)
632  config.scan_type = passive ? WIFI_SCAN_TYPE_PASSIVE : WIFI_SCAN_TYPE_ACTIVE;
633  if (first_scan) {
634  if (passive) {
635  config.scan_time.passive = 200;
636  } else {
637  config.scan_time.active.min = 100;
638  config.scan_time.active.max = 200;
639  }
640  } else {
641  if (passive) {
642  config.scan_time.passive = 500;
643  } else {
644  config.scan_time.active.min = 400;
645  config.scan_time.active.max = 500;
646  }
647  }
648 #endif
649  first_scan = false;
650  bool ret = wifi_station_scan(&config, &WiFiComponent::s_wifi_scan_done_callback);
651  if (!ret) {
652  ESP_LOGV(TAG, "wifi_station_scan failed!");
653  return false;
654  }
655 
656  return ret;
657 }
659  bool ret = true;
660  // Only call disconnect if interface is up
661  if (wifi_get_opmode() & WIFI_STA)
662  ret = wifi_station_disconnect();
663  station_config conf{};
664  memset(&conf, 0, sizeof(conf));
665  ETS_UART_INTR_DISABLE();
666  wifi_station_set_config_current(&conf);
667  ETS_UART_INTR_ENABLE();
668  return ret;
669 }
672 }
673 
675  this->scan_result_.clear();
676 
677  if (status != OK) {
678  ESP_LOGV(TAG, "Scan failed! %d", status);
679  this->retry_connect();
680  return;
681  }
682  auto *head = reinterpret_cast<bss_info *>(arg);
683  for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) {
684  WiFiScanResult res({it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]},
685  std::string(reinterpret_cast<char *>(it->ssid), it->ssid_len), it->channel, it->rssi,
686  it->authmode != AUTH_OPEN, it->is_hidden != 0);
687  this->scan_result_.push_back(res);
688  }
689  this->scan_done_ = true;
690 }
691 
692 #ifdef USE_WIFI_AP
694  // enable AP
695  if (!this->wifi_mode_({}, true))
696  return false;
697 
698  struct ip_info info {};
699  if (manual_ip.has_value()) {
700  info.ip = manual_ip->static_ip;
701  info.gw = manual_ip->gateway;
702  info.netmask = manual_ip->subnet;
703  } else {
704  info.ip = network::IPAddress(192, 168, 4, 1);
705  info.gw = network::IPAddress(192, 168, 4, 1);
706  info.netmask = network::IPAddress(255, 255, 255, 0);
707  }
708 
709  if (wifi_softap_dhcps_status() == DHCP_STARTED) {
710  if (!wifi_softap_dhcps_stop()) {
711  ESP_LOGV(TAG, "Stopping DHCP server failed!");
712  }
713  }
714 
715  if (!wifi_set_ip_info(SOFTAP_IF, &info)) {
716  ESP_LOGV(TAG, "Setting SoftAP info failed!");
717  return false;
718  }
719 
720 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(3, 0, 0)
721  dhcpSoftAP.begin(&info);
722 #endif
723 
724  struct dhcps_lease lease {};
725  lease.enable = true;
726  network::IPAddress start_address = network::IPAddress(&info.ip);
727  start_address += 99;
728  lease.start_ip = start_address;
729  ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str());
730  start_address += 100;
731  lease.end_ip = start_address;
732  ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str());
733  if (!wifi_softap_set_dhcps_lease(&lease)) {
734  ESP_LOGV(TAG, "Setting SoftAP DHCP lease failed!");
735  return false;
736  }
737 
738  // lease time 1440 minutes (=24 hours)
739  if (!wifi_softap_set_dhcps_lease_time(1440)) {
740  ESP_LOGV(TAG, "Setting SoftAP DHCP lease time failed!");
741  return false;
742  }
743 
744  uint8_t mode = 1;
745  // bit0, 1 enables router information from ESP8266 SoftAP DHCP server.
746  if (!wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &mode)) {
747  ESP_LOGV(TAG, "wifi_softap_set_dhcps_offer_option failed!");
748  return false;
749  }
750 
751  if (!wifi_softap_dhcps_start()) {
752  ESP_LOGV(TAG, "Starting SoftAP DHCPS failed!");
753  return false;
754  }
755 
756  return true;
757 }
758 
759 bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
760  // enable AP
761  if (!this->wifi_mode_({}, true))
762  return false;
763 
764  struct softap_config conf {};
765  strncpy(reinterpret_cast<char *>(conf.ssid), ap.get_ssid().c_str(), sizeof(conf.ssid));
766  conf.ssid_len = static_cast<uint8>(ap.get_ssid().size());
767  conf.channel = ap.get_channel().value_or(1);
768  conf.ssid_hidden = ap.get_hidden();
769  conf.max_connection = 5;
770  conf.beacon_interval = 100;
771 
772  if (ap.get_password().empty()) {
773  conf.authmode = AUTH_OPEN;
774  *conf.password = 0;
775  } else {
776  conf.authmode = AUTH_WPA2_PSK;
777  strncpy(reinterpret_cast<char *>(conf.password), ap.get_password().c_str(), sizeof(conf.password));
778  }
779 
780  ETS_UART_INTR_DISABLE();
781  bool ret = wifi_softap_set_config_current(&conf);
782  ETS_UART_INTR_ENABLE();
783 
784  if (!ret) {
785  ESP_LOGV(TAG, "wifi_softap_set_config_current failed!");
786  return false;
787  }
788 
789  if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) {
790  ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!");
791  return false;
792  }
793 
794  return true;
795 }
796 
798  struct ip_info ip {};
799  wifi_get_ip_info(SOFTAP_IF, &ip);
800  return network::IPAddress(&ip.ip);
801 }
802 #endif // USE_WIFI_AP
803 
805  bssid_t bssid{};
806  uint8_t *raw_bssid = WiFi.BSSID();
807  if (raw_bssid != nullptr) {
808  for (size_t i = 0; i < bssid.size(); i++)
809  bssid[i] = raw_bssid[i];
810  }
811  return bssid;
812 }
813 std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); }
814 int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); }
815 int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); }
816 network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; }
817 network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; }
818 network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {(const ip_addr_t *) WiFi.dnsIP(num)}; }
820 
821 } // namespace wifi
822 } // namespace esphome
823 
824 #endif
std::string format_ip_addr(struct ipv4_addr ip)
std::array< uint8_t, 6 > bssid_t
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)
std::string str() const
Definition: ip_address.h:112
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_
void netif_set_addr(struct netif *netif, const ip4_addr_t *ip, const ip4_addr_t *netmask, const ip4_addr_t *gw)
const char *const TAG
Definition: spi.cpp:8
struct netif * eagle_lwip_getif(int netif_index)
const optional< ManualIP > & get_manual_ip() const
in_addr ip4_addr_t
Definition: ip_address.h:20
const optional< uint8_t > & get_channel() const
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)
WiFiComponent * global_wifi_component
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:160
static void s_wifi_scan_done_callback(void *arg, STATUS status)
uint8_t status
Definition: bl0942.h:23
void wifi_scan_done_callback_(void *arg, STATUS status)
const char * get_auth_mode_str(uint8_t mode)
in_addr ip_addr_t
Definition: ip_address.h:19
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
static void wifi_event_callback(System_Event_t *event)
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