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