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