49#include <esp_system.h>
56#include <dhcpserver/dhcpserver.h>
58#include <esp_private/wifi.h>
60#if defined(CONFIG_IDF_TARGET_ESP32S2)
61#include <esp32s2/rom/crc.h>
62#elif defined(CONFIG_IDF_TARGET_ESP32C3)
63#include <esp32c3/rom/crc.h>
64#elif defined(CONFIG_IDF_TARGET_ESP32S3)
65#include <esp32s3/rom/crc.h>
67#include <esp32/rom/crc.h>
73using openlcb::TcpAutoAddress;
74using openlcb::TcpClientConfig;
77using openlcb::TcpManualAddress;
81#ifndef ESP32_WIFIMGR_SOCKETPARAMS_LOG_LEVEL
84#define ESP32_WIFIMGR_SOCKETPARAMS_LOG_LEVEL INFO
87#ifndef ESP32_WIFIMGR_MDNS_LOOKUP_LOG_LEVEL
90#define ESP32_WIFIMGR_MDNS_LOOKUP_LOG_LEVEL INFO
100void mdns_publish(
const char *name,
const char *service, uint16_t port);
112void split_mdns_service_name(
string *service_name,
string *protocol_name);
116namespace openmrn_arduino
121#if CONFIG_FREERTOS_UNICORE
124static constexpr UBaseType_t EXECUTOR_TASK_PRIORITY = 1;
128static constexpr UBaseType_t EXECUTOR_TASK_PRIORITY = 3;
132static constexpr uint32_t EXECUTOR_TASK_STACK_SIZE = 5120L;
135static constexpr uint32_t WIFI_CONNECT_CHECK_INTERVAL_SEC = 5;
138static constexpr uint32_t HUB_STARTUP_DELAY_USEC =
MSEC_TO_USEC(50);
141static constexpr uint32_t TASK_SHUTDOWN_DELAY_USEC =
MSEC_TO_USEC(1);
145static constexpr int WIFI_CONNECTED_BIT = BIT0;
149static constexpr int WIFI_GOTIP_BIT = BIT1;
155static constexpr uint8_t MAX_CONNECTION_CHECK_ATTEMPTS = 36;
162 int fd,
const TcpClientConfig<TcpClientDefaultParams> &cfg)
168 mdnsService_ = cfg_.auto_address().service_name().read(configFd_);
169 staticHost_ = cfg_.manual_address().ip_address().read(configFd_);
182 string mdns_host_name()
override
184 return cfg_.auto_address().host_name().read(configFd_);
189 bool enable_last()
override
197 string last_host_name()
override
199 return cfg_.last_address().ip_address().read(configFd_);
203 int last_port()
override
212 void set_last(
const char *hostname,
int port)
override
214 cfg_.last_address().ip_address().write(configFd_, hostname);
215 cfg_.last_address().port().write(configFd_, port);
218 void log_message(LogMessage
id,
const string &arg)
override
223 LOG(
INFO,
"[Uplink] Reconnecting to %s.", arg.c_str());
226 LOG(ESP32_WIFIMGR_SOCKETPARAMS_LOG_LEVEL,
227 "[Uplink] Starting mDNS searching for %s.",
231 LOG(ESP32_WIFIMGR_SOCKETPARAMS_LOG_LEVEL,
232 "[Uplink] mDNS search failed.");
235 LOG(ESP32_WIFIMGR_SOCKETPARAMS_LOG_LEVEL,
236 "[Uplink] mDNS search succeeded.");
239 LOG(
INFO,
"[Uplink] mDNS connecting to %s.", arg.c_str());
242 LOG(
INFO,
"[Uplink] Connecting to %s.", arg.c_str());
244 case CONNECT_FAILED_SELF:
245 LOG(ESP32_WIFIMGR_SOCKETPARAMS_LOG_LEVEL,
246 "[Uplink] Rejecting attempt to connect to localhost.");
248 case CONNECTION_LOST:
249 LOG(
INFO,
"[Uplink] Connection lost.");
259 bool disallow_local()
override
266 const TcpClientConfig<TcpClientDefaultParams> cfg_;
273 ,
const WiFiConfiguration &cfg, wifi_mode_t wifi_mode
274 , uint8_t connection_mode,
const char *hostname_prefix
275 ,
const char *sntp_server,
const char *timezone,
bool sntp_enabled
276 , uint8_t softap_channel, wifi_auth_mode_t softap_auth_mode
277 ,
const char *softap_ssid,
const char *softap_password)
279 , hostname_(hostname_prefix)
280 , ssid_(station_ssid), password_(station_password), cfg_(cfg)
281 , stack_(stack), wifiMode_(wifi_mode), softAPChannel_(softap_channel)
282 , softAPAuthMode_(softap_auth_mode), softAPName_(softap_ssid)
283 , softAPPassword_(softap_password), sntpEnabled_(sntp_enabled)
284 , sntpServer_(sntp_server), timeZone_(timezone)
285 , connectionMode_(connection_mode)
289 hostname_.reserve(MAX_HOSTNAME_LENGTH);
294 NodeID node_id = stack_->node()->node_id();
295 hostname_.append(uint64_to_string_hex(node_id, 0));
300 if (hostname_.length() > MAX_HOSTNAME_LENGTH)
302 LOG(
WARNING,
"ESP32 hostname is too long, original hostname:%s",
304 hostname_.resize(MAX_HOSTNAME_LENGTH);
305 LOG(
WARNING,
"truncated hostname:%s", hostname_.c_str());
309 hostname_.shrink_to_fit();
312Esp32WiFiManager::~Esp32WiFiManager()
316 esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID
317 , &Esp32WiFiManager::process_idf_event);
318 esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID
319 , &Esp32WiFiManager::process_idf_event);
322 executor_.shutdown();
325 vEventGroupDelete(wifiStatusEventGroup_);
328 ssidScanResults_.clear();
329 mdnsDeferredPublish_.clear();
332void Esp32WiFiManager::display_configuration()
334 static const char *
const wifiModes[] =
339 "Station and SoftAP",
343 LOG(
INFO,
"[WiFi] Configuration Settings:");
344 LOG(
INFO,
"[WiFi] Mode:%s (%d)", wifiModes[wifiMode_], wifiMode_);
345 LOG(
INFO,
"[WiFi] Station SSID:'%s'", ssid_.c_str());
346 LOG(
INFO,
"[WiFi] SoftAP SSID:'%s'", softAPName_.c_str());
347 LOG(
INFO,
"[WiFi] Hostname:%s", hostname_.c_str());
348 LOG(
INFO,
"[WiFi] SNTP Enabled:%s, Server:%s, TimeZone:%s",
349 sntpEnabled_ ?
"true" :
"false", sntpServer_.c_str(),
353#ifndef CONFIG_FREERTOS_UNICORE
358static void thread_entry(
void *param)
362 vTaskDelete(
nullptr);
370 LOG(
VERBOSE,
"Esp32WiFiManager::apply_configuration(%d, %d)", fd,
382 unique_ptr<uint8_t[]> crcbuf(
new uint8_t[cfg_.size()]);
386 if (lseek(fd, cfg_.offset(), SEEK_SET) != cfg_.offset())
388 LOG_ERROR(
"lseek failed to reset fd offset, REBOOT_NEEDED");
396 uint32_t configCrc32 = crc32_le(0, crcbuf.get(), cfg_.size());
397 LOG(
VERBOSE,
"existing config CRC32:%" PRIu32
", new CRC32:%" PRIu32,
398 configCrc32_, configCrc32);
404#ifndef CONFIG_FREERTOS_UNICORE
405 xTaskCreatePinnedToCore(&thread_entry,
407 EXECUTOR_TASK_STACK_SIZE,
409 EXECUTOR_TASK_PRIORITY,
415 executor_.start_thread(
416 "Esp32WiFiConn", EXECUTOR_TASK_PRIORITY, EXECUTOR_TASK_STACK_SIZE);
419 else if (configCrc32 != configCrc32_)
423 wifiStackFlow_.notify();
428 configCrc32_ = configCrc32;
436void Esp32WiFiManager::factory_reset(
int fd)
438 LOG(
VERBOSE,
"Esp32WiFiManager::factory_reset(%d)", fd);
444 cfg_.connection_mode().write(fd, connectionMode_);
448 cfg_.hub().service_name().write(
449 fd, TcpDefs::MDNS_SERVICE_NAME_GRIDCONNECT_CAN_TCP);
456 cfg_.uplink().manual_address().ip_address().write(fd,
"");
460 cfg_.uplink().auto_address().service_name().write(
461 fd, TcpDefs::MDNS_SERVICE_NAME_GRIDCONNECT_CAN_TCP);
462 cfg_.uplink().auto_address().host_name().write(fd,
"");
465 cfg_.uplink().last_address().ip_address().write(fd,
"");
472void Esp32WiFiManager::process_idf_event(
void *arg, esp_event_base_t event_base
473 , int32_t event_id,
void *event_data)
475 LOG(
VERBOSE,
"Esp32WiFiManager::process_idf_event(%s, %" PRIi32
", %p)",
476 event_base, event_id, event_data);
477 if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
481 else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED)
485 else if (event_base == WIFI_EVENT &&
486 event_id == WIFI_EVENT_STA_DISCONNECTED)
489 static_cast<wifi_event_sta_disconnected_t *
>(event_data)->reason);
491 else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_START)
495 else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STOP)
499 else if (event_base == WIFI_EVENT &&
500 event_id == WIFI_EVENT_AP_STACONNECTED)
502 auto sta_data =
static_cast<wifi_event_ap_staconnected_t *
>(event_data);
504 sta_data->mac, sta_data->aid);
506 else if (event_base == WIFI_EVENT &&
507 event_id == WIFI_EVENT_AP_STADISCONNECTED)
509 auto sta_data =
static_cast<wifi_event_ap_staconnected_t *
>(event_data);
511 sta_data->mac, sta_data->aid);
513 else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE)
515 auto scan_data =
static_cast<wifi_event_sta_scan_done_t *
>(event_data);
517 scan_data->status, scan_data->number);
519 else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
521 ip_event_got_ip_t *data =
static_cast<ip_event_got_ip_t *
>(event_data);
523 htonl(data->ip_info.ip.addr));
525 else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP)
532void Esp32WiFiManager::register_network_up_callback(
533 esp_network_up_callback_t callback)
536 networkUpCallbacks_.push_back(callback);
540void Esp32WiFiManager::register_network_down_callback(
541 esp_network_down_callback_t callback)
544 networkDownCallbacks_.push_back(callback);
548void Esp32WiFiManager::register_network_init_callback(
549 esp_network_init_callback_t callback)
552 networkInitCallbacks_.push_back(callback);
556void Esp32WiFiManager::register_network_time_callback(
557 esp_network_time_callback_t callback)
560 networkTimeCallbacks_.push_back(callback);
564void Esp32WiFiManager::stop_hub()
567 stack->shutdown_tcp_hub_server();
571void Esp32WiFiManager::start_hub()
578 if (!stack->get_tcp_hub_server())
580 hubServiceName_ = cfg_.hub().service_name().read(configFd_);
582 LOG(
INFO,
"[HUB] Starting TCP/IP listener on port %" PRIu16, hub_port);
583 stack->start_tcp_hub_server(hub_port);
584 auto hub = stack->get_tcp_hub_server();
587 while (!hub->is_started())
589 usleep(HUB_STARTUP_DELAY_USEC);
597void Esp32WiFiManager::stop_uplink()
601 LOG(
INFO,
"[Uplink] Disconnecting from uplink.");
603 uplink_.reset(
nullptr);
606 uplinkNotifiable_ =
nullptr;
615void Esp32WiFiManager::start_uplink()
617 unique_ptr<SocketClientParams> params(
618 new Esp32SocketParams(configFd_, cfg_.uplink()));
623 uplink_->reset_params(std::move(params));
628 uplink_.reset(
new SocketClient(stack_->service(), &executor_,
629 &executor_, std::move(params),
630 std::bind(&Esp32WiFiManager::on_uplink_created,
this,
631 std::placeholders::_1, std::placeholders::_2)));
636void Esp32WiFiManager::on_uplink_created(
int fd,
Notifiable *on_exit)
638 LOG(
INFO,
"[Uplink] Connected to hub, configuring GridConnect HubPort.");
643 uplinkNotifiable_ = on_exit;
645 const bool use_select =
654 uplinkFd_, &uplinkNotifiableProxy_, use_select);
658 stack_->restart_stack();
663void Esp32WiFiManager::enable_esp_wifi_logging()
665 esp_log_level_set(
"wifi", ESP_LOG_VERBOSE);
669#if defined(WIFI_LOG_SUBMODULE_ALL)
670 esp_wifi_internal_set_log_level(WIFI_LOG_VERBOSE);
671 esp_wifi_internal_set_log_mod(
672 WIFI_LOG_MODULE_ALL, WIFI_LOG_SUBMODULE_ALL,
true);
677void Esp32WiFiManager::start_ssid_scan(
Notifiable *n)
679 clear_ssid_scan_results();
680 std::swap(ssidCompleteNotifiable_, n);
688 wifi_scan_config_t cfg;
689 bzero(&cfg,
sizeof(wifi_scan_config_t));
691 ESP_ERROR_CHECK(esp_wifi_scan_start(&cfg,
false));
695size_t Esp32WiFiManager::get_ssid_scan_result_count()
698 return ssidScanResults_.size();
702wifi_ap_record_t Esp32WiFiManager::get_ssid_scan_result(
size_t index)
705 wifi_ap_record_t record = wifi_ap_record_t();
706 if (index < ssidScanResults_.size())
708 record = ssidScanResults_[index];
714void Esp32WiFiManager::clear_ssid_scan_results()
717 ssidScanResults_.clear();
724void Esp32WiFiManager::mdns_publish(
string service,
const uint16_t port)
728 if (!mdnsInitialized_)
732 mdnsDeferredPublish_[service] = port;
741 string service_name = service;
742 string protocol_name;
743 split_mdns_service_name(&service_name, &protocol_name);
744 esp_err_t res = mdns_service_add(
745 NULL, service_name.c_str(), protocol_name.c_str(), port, NULL, 0);
746 LOG(
VERBOSE,
"[mDNS] mdns_service_add(%s.%s:%" PRIu16
"):%s.",
747 service_name.c_str(), protocol_name.c_str(), port,
748 esp_err_to_name(res));
759 else if (res == ESP_ERR_INVALID_ARG)
770 else if (res == ESP_OK)
772 LOG(
INFO,
"[mDNS] Advertising %s.%s:%" PRIu16
".",
773 service_name.c_str(), protocol_name.c_str(), port);
777 LOG_ERROR(
"[mDNS] Failed to publish:%s.%s:%" PRIu16,
778 service_name.c_str(), protocol_name.c_str(), port);
786void Esp32WiFiManager::mdns_unpublish(
string service)
790 if (!mdnsInitialized_)
797 string service_name = service;
798 string protocol_name;
799 split_mdns_service_name(&service_name, &protocol_name);
800 LOG(
INFO,
"[mDNS] Removing advertisement of %s.%s."
801 , service_name.c_str(), protocol_name.c_str());
803 mdns_service_remove(service_name.c_str(), protocol_name.c_str());
804 LOG(
VERBOSE,
"[mDNS] mdns_service_remove:%s.", esp_err_to_name(res));
812void Esp32WiFiManager::start_mdns_system()
817 if (mdnsInitialized_)
823 LOG(
INFO,
"[mDNS] Initializing mDNS system");
824 ESP_ERROR_CHECK(mdns_init());
828 LOG(
INFO,
"[mDNS] Setting hostname to \"%s\"", hostname_.c_str());
829 ESP_ERROR_CHECK(mdns_hostname_set(hostname_.c_str()));
832 ESP_ERROR_CHECK(mdns_instance_name_set(hostname_.c_str()));
835 mdnsInitialized_ =
true;
839 for (
auto & entry : mdnsDeferredPublish_)
843 mdnsDeferredPublish_.clear();
846void Esp32WiFiManager::on_station_started()
851 LOG(
INFO,
"[Station] Setting hostname to \"%s\".", hostname_.c_str());
852 esp_netif_set_hostname(espNetIfaces_[STATION_INTERFACE], hostname_.c_str());
854 esp_wifi_get_mac(WIFI_IF_STA, mac);
855 LOG(
INFO,
"[Station] MAC Address:%s", mac_to_string(mac).c_str());
859 LOG(
INFO,
"[Station] Starting DHCP Client.");
860 ESP_ERROR_CHECK(esp_netif_dhcpc_start(espNetIfaces_[STATION_INTERFACE]));
862 LOG(
INFO,
"[Station] Connecting to SSID:%s.", ssid_.c_str());
869 for (esp_network_init_callback_t cb : networkInitCallbacks_)
873 cb(STATION_INTERFACE);
879void Esp32WiFiManager::on_station_connected()
881 LOG(
INFO,
"[Station] Connected to SSID:%s", ssid_.c_str());
883 xEventGroupSetBits(wifiStatusEventGroup_, WIFI_CONNECTED_BIT);
886void Esp32WiFiManager::on_station_disconnected(uint8_t reason)
889 bool was_previously_connected =
false;
893 EventBits_t event_bits = xEventGroupGetBits(wifiStatusEventGroup_);
898 if (event_bits & WIFI_CONNECTED_BIT)
904 was_previously_connected = event_bits & WIFI_GOTIP_BIT;
906 LOG(
INFO,
"[Station] Lost connection to SSID:%s (reason:%d)",
907 ssid_.c_str(), reason);
909 xEventGroupClearBits(wifiStatusEventGroup_, WIFI_CONNECTED_BIT);
911 xEventGroupClearBits(wifiStatusEventGroup_, WIFI_GOTIP_BIT);
916 if (was_previously_connected)
918 LOG(
INFO,
"[Station] Reconnecting to SSID:%s.",
920 wifiStackFlow_.notify();
925 "[Station] Connection to SSID:%s (reason:%d) failed, retrying.",
926 ssid_.c_str(), reason);
933 for (esp_network_init_callback_t cb : networkInitCallbacks_)
937 cb(STATION_INTERFACE);
943void Esp32WiFiManager::on_station_ip_assigned(uint32_t ip_address)
945 LOG(
INFO,
"[Station] IP address:%s", ipv4_to_string(ip_address).c_str());
953 xEventGroupSetBits(wifiStatusEventGroup_, WIFI_GOTIP_BIT);
956 wifiStackFlow_.notify();
961 for (esp_network_up_callback_t cb : networkUpCallbacks_)
965 cb(STATION_INTERFACE, ip_address);
971 reconfigure_wifi_tx_power();
974 statusLed_->write(
true);
978void Esp32WiFiManager::on_station_ip_lost()
982 xEventGroupClearBits(wifiStatusEventGroup_, WIFI_GOTIP_BIT);
985 wifiStackFlow_.notify();
990 for (esp_network_down_callback_t cb : networkDownCallbacks_)
994 cb(STATION_INTERFACE);
1000void Esp32WiFiManager::on_softap_start()
1002 uint32_t ip_address = 0;
1004 esp_wifi_get_mac(WIFI_IF_AP, mac);
1005 LOG(
INFO,
"[SoftAP] MAC Address:%s", mac_to_string(mac).c_str());
1010 LOG(
INFO,
"[SoftAP] Setting hostname to \"%s\".", hostname_.c_str());
1011 ESP_ERROR_CHECK(esp_netif_set_hostname(
1012 espNetIfaces_[SOFTAP_INTERFACE], hostname_.c_str()));
1016 esp_netif_ip_info_t ip_info;
1018 esp_netif_get_ip_info(espNetIfaces_[SOFTAP_INTERFACE], &ip_info));
1019 ip_address =
ntohl(ip4_addr_get_u32(&ip_info.ip));
1021 LOG(
INFO,
"[SoftAP] IP address:%s", ipv4_to_string(ip_address).c_str());
1026 if (wifiMode_ == WIFI_MODE_AP)
1028 start_mdns_system();
1029 reconfigure_wifi_tx_power();
1030 if (connectionMode_ & CONN_MODE_HUB_BIT)
1039 for (esp_network_up_callback_t cb : networkUpCallbacks_)
1043 cb(SOFTAP_INTERFACE,
htonl(ip_address));
1049void Esp32WiFiManager::on_softap_stop()
1051 if (wifiMode_ == WIFI_MODE_AP)
1060 for (esp_network_down_callback_t cb : networkDownCallbacks_)
1064 cb(SOFTAP_INTERFACE);
1070void Esp32WiFiManager::on_softap_station_connected(uint8_t mac[6], uint8_t aid)
1072 LOG(
INFO,
"[SoftAP aid:%d] %s connected.", aid,
1073 mac_to_string(mac).c_str());
1076void Esp32WiFiManager::on_softap_station_disconnected(uint8_t mac[6],
1079 LOG(
INFO,
"[SoftAP aid:%d] %s disconnected.", aid,
1080 mac_to_string(mac).c_str());
1083void Esp32WiFiManager::on_wifi_scan_completed(uint32_t status, uint8_t count)
1092 uint16_t num_found = count;
1093 esp_wifi_scan_get_ap_num(&num_found);
1094 LOG(
VERBOSE,
"[WiFi] %" PRIu16
" SSIDs found via scan", num_found);
1095 ssidScanResults_.resize(num_found);
1096 esp_wifi_scan_get_ap_records(&num_found, ssidScanResults_.data());
1097#if LOGLEVEL >= VERBOSE
1098 for (
int i = 0; i < num_found; i++)
1101 , ssidScanResults_[i].ssid
1102 , ssidScanResults_[i].rssi, ssidScanResults_[i].primary);
1106 if (ssidCompleteNotifiable_)
1108 ssidCompleteNotifiable_->notify();
1109 ssidCompleteNotifiable_ =
nullptr;
1114void Esp32WiFiManager::sync_time(time_t now)
1117 for (esp_network_time_callback_t cb : networkTimeCallbacks_)
1126static void sntp_update_received(
struct timeval *tv)
1128 time_t new_time = tv->tv_sec;
1129 LOG(
INFO,
"[SNTP] Received time update, new localtime:%s"
1130 , ctime(&new_time));
1134void Esp32WiFiManager::configure_sntp()
1136 if (sntpEnabled_ && !sntpConfigured_)
1138 sntpConfigured_ =
true;
1139 LOG(
INFO,
"[SNTP] Polling %s for time updates", sntpServer_.c_str());
1140#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,1,0)
1141 esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
1142 esp_sntp_setservername(0, sntpServer_.c_str());
1143 sntp_set_time_sync_notification_cb(sntp_update_received);
1146 sntp_setoperatingmode(SNTP_OPMODE_POLL);
1147 sntp_setservername(0, sntpServer_.c_str());
1148 sntp_set_time_sync_notification_cb(sntp_update_received);
1152 if (!timeZone_.empty())
1154 LOG(
INFO,
"[TimeZone] %s", timeZone_.c_str());
1155 setenv(
"TZ", timeZone_.c_str(), 1);
1161void Esp32WiFiManager::reconfigure_wifi_radio_sleep()
1163 wifi_ps_type_t current_mode = WIFI_PS_NONE;
1164 ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_get_ps(¤t_mode));
1167 if (sleepEnabled && current_mode != WIFI_PS_MIN_MODEM)
1169 LOG(
INFO,
"[WiFi] Enabling radio power saving mode");
1174 ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_MIN_MODEM));
1176 else if (!sleepEnabled && current_mode != WIFI_PS_NONE)
1178 LOG(
INFO,
"[WiFi] Disabling radio power saving mode");
1183 ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
1187void Esp32WiFiManager::reconfigure_wifi_tx_power()
1189 ESP_ERROR_CHECK(esp_wifi_set_max_tx_power(wifiTXPower_));
1192Esp32WiFiManager::WiFiStackFlow::WiFiStackFlow(Esp32WiFiManager * parent)
1194 wifiConnectBitMask_(WIFI_CONNECTED_BIT)
1196 start_flow(
STATE(startup));
1201 if (parent_->wifiMode_ != WIFI_MODE_NULL)
1203 return yield_and_call(
STATE(init_interface));
1205 return yield_and_call(
STATE(noop));
1210 return wait_and_call(
STATE(noop));
1217 ESP_ERROR_CHECK(esp_netif_init());
1220 esp_err_t err = esp_event_loop_create_default();
1225 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE)
1227 LOG(
FATAL,
"[WiFi] Failed to initialize the default event loop:%s",
1228 esp_err_to_name(err));
1232 esp_netif_create_default_wifi_sta();
1234 esp_netif_create_default_wifi_ap();
1237 esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
1238 &Esp32WiFiManager::process_idf_event,
nullptr);
1239 esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID,
1240 &Esp32WiFiManager::process_idf_event,
nullptr);
1242 return yield_and_call(
STATE(init_wifi));
1248 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
1249 LOG(
INFO,
"[WiFi] Initializing WiFi stack");
1252 cfg.nvs_enable =
false;
1265 if (cfg.static_rx_buf_num < 16)
1267 cfg.static_rx_buf_num = 16;
1269 if (cfg.dynamic_rx_buf_num < 32)
1271 cfg.dynamic_rx_buf_num = 32;
1273 if (cfg.rx_ba_win < 16)
1278 ESP_ERROR_CHECK(esp_wifi_init(&cfg));
1280 if (parent_->verboseLogging_)
1282 parent_->enable_esp_wifi_logging();
1288 parent_->wifiStatusEventGroup_ = xEventGroupCreate();
1290 wifi_mode_t requested_wifi_mode = parent_->wifiMode_;
1291 if (parent_->wifiMode_ == WIFI_MODE_AP)
1295 requested_wifi_mode = WIFI_MODE_APSTA;
1298 ESP_ERROR_CHECK(esp_wifi_set_mode(requested_wifi_mode));
1304 esp_wifi_set_storage(WIFI_STORAGE_RAM);
1306 if (parent_->wifiMode_ == WIFI_MODE_APSTA ||
1307 parent_->wifiMode_ == WIFI_MODE_AP)
1309 return yield_and_call(
STATE(configure_softap));
1311 return yield_and_call(
STATE(configure_station));
1319 memset(&conf, 0,
sizeof(wifi_config_t));
1320 strcpy(
reinterpret_cast<char *
>(conf.sta.ssid), parent_->ssid_.c_str());
1321 if (!parent_->password_.empty())
1323 strcpy(
reinterpret_cast<char *
>(conf.sta.password),
1324 parent_->password_.c_str());
1327 LOG(
INFO,
"[WiFi] Configuring Station (SSID:%s)", conf.sta.ssid);
1328 ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &conf));
1330 return yield_and_call(
STATE(start_wifi));
1336 memset(&conf, 0,
sizeof(wifi_config_t));
1337 conf.ap.authmode = parent_->softAPAuthMode_;
1338 conf.ap.beacon_interval = 100;
1339 conf.ap.channel = parent_->softAPChannel_;
1340 conf.ap.max_connection = 4;
1342 if (!parent_->softAPName_.empty())
1346 strcpy(
reinterpret_cast<char *
>(conf.ap.ssid),
1347 parent_->softAPName_.c_str());
1349 else if (parent_->wifiMode_ == WIFI_MODE_AP && !parent_->ssid_.empty())
1351 strcpy(
reinterpret_cast<char *
>(conf.ap.ssid),
1352 parent_->ssid_.c_str());
1358 strcpy(
reinterpret_cast<char *
>(conf.ap.ssid),
1359 parent_->hostname_.c_str());
1362 if (parent_->softAPAuthMode_ != WIFI_AUTH_OPEN)
1364 if (!parent_->softAPPassword_.empty())
1366 strcpy(
reinterpret_cast<char *
>(conf.ap.password),
1367 parent_->softAPPassword_.c_str());
1369 else if (!parent_->password_.empty())
1371 strcpy(
reinterpret_cast<char *
>(conf.ap.password),
1372 parent_->password_.c_str());
1377 "[WiFi] SoftAP password is blank, using OPEN auth mode.");
1378 parent_->softAPAuthMode_ = WIFI_AUTH_OPEN;
1382 LOG(
INFO,
"[WiFi] Configuring SoftAP (SSID:%s)", conf.ap.ssid);
1383 ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &conf));
1387 if (parent_->wifiMode_ == WIFI_MODE_AP)
1389 return yield_and_call(
STATE(start_wifi));
1392 return yield_and_call(
STATE(configure_station));
1399 LOG(
INFO,
"[WiFi] Starting WiFi stack");
1400 ESP_ERROR_CHECK(esp_wifi_start());
1403 ESP_ERROR_CHECK(esp_wifi_set_max_tx_power(84));
1405 if (parent_->wifiMode_ != WIFI_MODE_AP && parent_->waitForStationConnect_)
1407 return sleep_and_call(&timer_,
1409 STATE(wait_for_connect));
1411 return wait_and_call(
STATE(reload));
1417 xEventGroupWaitBits(parent_->wifiStatusEventGroup_,
1418 wifiConnectBitMask_, pdFALSE, pdTRUE, 0);
1424 if (++wifiConnectAttempts_ <= MAX_CONNECTION_CHECK_ATTEMPTS)
1428 if (bits & WIFI_CONNECTED_BIT)
1430 LOG(
INFO,
"[IPv4] [%d/%d] Waiting for IP address assignment.",
1431 wifiConnectAttempts_, MAX_CONNECTION_CHECK_ATTEMPTS);
1436 LOG(
INFO,
"[WiFi] [%d/%d] Waiting for SSID connection.",
1437 wifiConnectAttempts_, MAX_CONNECTION_CHECK_ATTEMPTS);
1440 if (bits & WIFI_CONNECTED_BIT)
1444 wifiConnectBitMask_ |= WIFI_GOTIP_BIT;
1447 if (bits & WIFI_GOTIP_BIT)
1449 return yield_and_call(
STATE(reload));
1451 return sleep_and_call(&timer_,
1453 STATE(wait_for_connect));
1456 if ((bits & WIFI_CONNECTED_BIT) != WIFI_CONNECTED_BIT)
1458 LOG(
FATAL,
"[WiFi] Failed to connect to SSID:%s.",
1459 parent_->ssid_.c_str());
1463 if ((bits & WIFI_GOTIP_BIT) != WIFI_GOTIP_BIT)
1465 LOG(
FATAL,
"[IPv4] Timeout waiting for an IP.");
1468 return yield_and_call(
STATE(reload));
1473 if (parent_->wifiMode_ == WIFI_MODE_STA ||
1474 parent_->wifiMode_ == WIFI_MODE_APSTA)
1476 EventBits_t bits = xEventGroupGetBits(parent_->wifiStatusEventGroup_);
1477 if (!(bits & WIFI_GOTIP_BIT))
1482 parent_->stop_hub();
1483 parent_->stop_uplink();
1484 return wait_and_call(
STATE(reload));
1488 parent_->reconfigure_wifi_radio_sleep();
1489 if (parent_->connectionMode_ & CONN_MODE_HUB_BIT)
1491 parent_->start_hub();
1495 parent_->stop_hub();
1498 if (parent_->connectionMode_ & CONN_MODE_UPLINK_BIT)
1500 parent_->start_uplink();
1504 parent_->stop_uplink();
1506 return wait_and_call(
STATE(reload));
1512static constexpr uint32_t MDNS_QUERY_TIMEOUT = 2000;
1515static constexpr size_t MDNS_MAX_RESULTS = 10;
1518void mdns_publish(
const char *name,
const char *service, uint16_t port)
1531void split_mdns_service_name(
string *service_name,
string *protocol_name)
1533 HASSERT(service_name !=
nullptr);
1534 HASSERT(protocol_name !=
nullptr);
1537 if (service_name->length() && service_name->find(
'.', 0) != string::npos)
1539 string::size_type split_loc = service_name->find(
'.', 0);
1540 protocol_name->assign(service_name->substr(split_loc + 1));
1541 service_name->resize(split_loc);
1548#define EAI_AGAIN TRY_AGAIN
1559 unique_ptr<struct addrinfo> ai(
new struct addrinfo);
1560 if (ai.get() ==
nullptr)
1562 LOG_ERROR(
"[mDNS] Allocation failed for addrinfo.");
1565 bzero(ai.get(),
sizeof(
struct addrinfo));
1567 unique_ptr<struct sockaddr> sa(
new struct sockaddr);
1568 if (sa.get() ==
nullptr)
1570 LOG_ERROR(
"[mDNS] Allocation failed for sockaddr.");
1573 bzero(sa.get(),
sizeof(
struct sockaddr));
1584 string service_name = service;
1585 string protocol_name;
1586 split_mdns_service_name(&service_name, &protocol_name);
1588 mdns_result_t *results = NULL;
1589 if (ESP_ERROR_CHECK_WITHOUT_ABORT(
1590 mdns_query_ptr(service_name.c_str(),
1591 protocol_name.c_str(),
1603 LOG(ESP32_WIFIMGR_MDNS_LOOKUP_LOG_LEVEL,
1604 "[mDNS] No matches found for service:%s.",
1610 mdns_result_t *res = results;
1613 bool match_found =
false;
1614 while (res && !match_found)
1616 mdns_ip_addr_t *ipaddr = res->addr;
1617 while (ipaddr && !match_found)
1620 if (ipaddr->addr.type == IPADDR_TYPE_V4)
1622 LOG(ESP32_WIFIMGR_MDNS_LOOKUP_LOG_LEVEL,
1623 "[mDNS] Found %s providing service:%s on port %" PRIu16,
1624 res->hostname, service, res->port);
1625 inet_addr_from_ip4addr(
1626 &sa_in->
sin_addr, &ipaddr->addr.u_addr.ip4);
1630 ipaddr = ipaddr->next;
1636 mdns_query_results_free(results);
1640 LOG(ESP32_WIFIMGR_MDNS_LOOKUP_LOG_LEVEL,
1641 "[mDNS] No matches found for service:%s.", service);
1646 *addr = ai.release();
1647 (*addr)->
ai_addr = sa.release();
1663 esp_netif_ip_info_t ip_info;
1669 esp_netif_t *iface = esp_netif_get_handle_from_ifkey(
"WIFI_STA_DEF");
1670 if (iface ==
nullptr)
1678 if (esp_netif_get_ip_info(iface, &ip_info) != ESP_OK)
1681 errno = EADDRNOTAVAIL;
1686 std::unique_ptr<struct ifaddrs> ia(
new struct ifaddrs);
1687 if (ia.get() ==
nullptr)
1692 bzero(ia.get(),
sizeof(
struct ifaddrs));
1693 std::unique_ptr<char[]> ifa_name(
new char[6]);
1694 if (ifa_name.get() ==
nullptr)
1699 strcpy(ifa_name.get(),
"wlan0");
1700 std::unique_ptr<struct sockaddr> ifa_addr(
new struct sockaddr);
1701 if (ifa_addr ==
nullptr)
1706 bzero(ifa_addr.get(),
sizeof(
struct sockaddr));
1712 ia.get()->ifa_next =
nullptr;
1713 ia.get()->ifa_name = ifa_name.release();
1714 ia.get()->ifa_flags = 0;
1715 ia.get()->ifa_addr = ifa_addr.release();
1716 ia.get()->ifa_netmask =
nullptr;
1717 ia.get()->ifa_ifu.ifu_broadaddr =
nullptr;
1718 ia.get()->ifa_data =
nullptr;
1721 *ifap = ia.release();
1752 return "gai_strerror unknown";
1754 return "temporary failure";
1756 return "non-recoverable failure";
1758 return "memory allocation failure";
#define CDI_READ_TRIMMED(PATH, fd)
Requests a readout of a numeric variable with trimming.
#define CDI_FACTORY_RESET(PATH)
Performs factory reset on a CDI variable.
@ STATION_INTERFACE
This is used for the Station WiFi interface.
@ SOFTAP_INTERFACE
This is used for the SoftAP WiFi interface.
void create_gc_port_for_can_hub(CanHubFlow *can_hub, int fd, Notifiable *on_exit, bool use_select)
Creates a new port on a CAN hub in gridconnect format for a select-compatible file descriptor.
int mdns_lookup(const char *service, struct addrinfo *hints, struct addrinfo **addr)
Lookup an mDNS name.
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
This class sends a notification in its destructor.
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
A notifiable class that calls a particular function object once when it is invoked,...
UpdateAction
Specifies what additional steps are needed to apply the new configuration.
@ UPDATED
No additional step is necessary.
@ REBOOT_NEEDED
Need to reboot the hardware.
Implementation of ConfigUpdateListener that registers itself in the constructor and unregisters itsel...
Default implementation that supplies parametrized values for static and mdns connection methods.
Implementation the ExecutorBase with a specific number of priority bands.
An object that can schedule itself on an executor to run.
virtual void notify()=0
Generic callback.
Class to allow convenient locking and unlocking of mutexes in a C context.
Collection of related state machines that pend on incoming messages.
SearchMode
Parameter that determines what order we will attempt to connect.
Return type for a state flow callback.
Base class for state machines.
SimpleStack with a CAN-bus based interface and IO functions for CAN-bus.
This structure shows what parameters are configurable in the TcpClientConfig CDI group definition.
Static class for constants and utilities related to the TCP transport protocol.
Esp32WiFiManager()
Default constructor.
#define CONSTANT_TRUE
We cannot compare constants to zero, so we use 1 and 2 as constant values for booleans.
#define htons(x)
Converts a host endian short value to network endian.
#define ntohl(x)
Converts a network endian long value to host endian.
#define htonl(x)
Converts a host endian long value to network endian.
#define LOG(level, message...)
Conditionally write a message to the logging output.
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
static const int INFO
Loglevel that is printed by default, reporting some status information.
static const int FATAL
Loglevel that kills the current process.
#define LOG_ERROR(message...)
Shorthand for LOG(LEVEL_ERROR, message...). See LOG.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
uint64_t NodeID
48-bit NMRAnet Node ID type
const char * gai_strerror(int __ecode)
see 'man gai_strerror'
int getifaddrs(struct ifaddrs **ifap)
Create a linked list of structures describing the network interfaces of the local system.
void freeifaddrs(struct ifaddrs *ifa)
Free a previously generated linked list of structures describing the network interfaces of the local ...
void mdns_publish(const char *name, const char *service, uint16_t port)
Publish an mDNS name.
void mdns_unpublish(const char *name, const char *service)
Unpublish an mDNS name.
#define EAI_MEMORY
Memory allocation failure.
#define EAI_AGAIN
Temporary failure in name resolution.
#define EAI_FAIL
, Non-recoverable failure in name res
#define MSEC_TO_USEC(_msec)
Convert a millisecond value to a microsecond value.
#define SEC_TO_NSEC(_sec)
Convert a second value to a nanosecond value.
#define AF_INET
IPv4 Socket (UDP, TCP, etc...)
static void repeated_read(int fd, void *buf, size_t size)
Performs a reliable read from the given FD.
Structure to contain information about address of a service provider.
struct sockaddr * ai_addr
Socket address for socket.
int ai_socktype
Socket type.
int ai_protocol
Protocol for socket.
int ai_family
Protocol family for socket.
network interface address list member
char * ifa_name
name of interface.
struct sockaddr * ifa_netmask
netmask of interface
void * ifa_data
address-specific data
struct ifaddrs * ifa_next
next item in list
struct sockaddr * ifu_broadaddr
broadcast address of interface
struct sockaddr * ifa_addr
address of interface
Structure describing an Internet socket address.
uint16_t sin_family
protocol family (AF_INET)
uint16_t sin_port
port number
struct in_addr sin_addr
internet address