| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 | #include "common.h"#include "wifi.h"#include "config.h"#include "httpd.h"#include "led.h"#include <WiFi.h>#include <mdns.h>#include <lwip/dns.h>#include <lwip/inet.h>#include <lwip/apps/sntp.h>#include <esp_sntp.h>#include <esp_wifi.h>#include "IP4.h"WiFiUDP UDP;static const char *ssid, *password, *hostname;static IP4 dnsserver;static TimerHandle_t sta_failure_timer;enum connected {    CON_STA = 1,    CON_ETH = 2,    CON_AP  = 4};static volatile bool sta_timeout_enabled;static volatile unsigned int sta_timeout_count;static unsigned int connected;static inline bool setvar_ip4(enum sysvar_enum var, const IP4 &ip){    return setvar_ip(var, static_cast<uint32_t>(ip));}static inline IP4 getvar_ip4(enum sysvar_enum var){    return IP4(getvar_ip(var));}static void sta_bounce(void){    if (!ssid)	return;    if (WiFi.status() != WL_CONNECTED) {	WiFi.disconnect();	WiFi.begin();    }}static void sta_timeout(void){    unsigned int count = ++sta_timeout_count;    if (connected & CON_STA)	return;    // Try to ping the STA even if there are AP clients every    // 15 seconds (if an SSID is configured)    sta_bounce();    if (!(connected & (CON_AP|CON_STA)) && (count >= 2)) {	// Enable the AP if the STA doesn't connect after 30s	WiFi.enableAP(true);    }}static void sta_timeout_enable(void){    if (!sta_failure_timer || sta_timeout_enabled)	return;    sta_timeout_enabled = xTimerStart(sta_failure_timer, 1);    sta_timeout_count = 0;}static void sta_timeout_disable(void){    if (!sta_failure_timer || !sta_timeout_enabled)	return;    sta_timeout_enabled = !xTimerStop(sta_failure_timer, 1);}static void sntp_sync_cb(struct timeval *tv){    uint8_t sync_status = sntp_get_sync_status();    switch (sync_status) {    case SNTP_SYNC_STATUS_RESET:	sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync	time_net_sync(NULL);	break;    case SNTP_SYNC_STATUS_COMPLETED:	sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); // After successful sync	time_net_sync(tv);	break;    default:	break;    }}static void my_sntp_start(void){    setvar_bool(status_net_sntp_sync, false);    if (getvar_bool(config_sntp_enabled)) {	sntp_set_time_sync_notification_cb(sntp_sync_cb);	sntp_setoperatingmode(SNTP_OPMODE_POLL);	sntp_servermode_dhcp(!getvar_bool(config_ip4_dhcp_nosntp));	sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync	sntp_init();    } else {	sntp_stop();    }}static bool services_started;static void stop_services(void){    if (services_started) {	esp_unregister_shutdown_handler(stop_services);	printf("[WIFI] Stopping network services\n");	my_httpd_stop();	services_started = false;    }}static void sntp_server_show(void){    IP4 sntp_ip = *sntp_getserver(0);    if (!sntp_ip) {	printf("[SNTP] Time server: %s\n", sntp_ip.cstr());	setvar_ip4(status_net_sntp_server, sntp_ip);    } else {	setvar_ip4(status_net_sntp_server, null_ip);    }}static void sntp_server_found(const char *name, const ip_addr_t *addr,			      void *arg){    (void)name;    (void)arg;    if (!IP4(*addr))	return;    sntp_setserver(0, addr);    sntp_server_show();}static void sntp_set_server(const char *name){    if (!name || !*name)	return;    ip_addr_t addr;    err_t err = dns_gethostbyname(name, &addr, sntp_server_found, NULL);    if (err == ERR_OK)	sntp_server_found(name, &addr, NULL);}static void dns_setup(void){    IP4 dns_ip = *dns_getserver(0);    if (!dns_ip || getvar_bool(config_ip4_dhcp_nodns)) {	/* Static DNS server configuration */	if (dns_ip != dnsserver) {	    ip_addr_t addr = dnsserver;	    dns_setserver(0, &addr);	}    }    dns_ip = *dns_getserver(0);    printf("[DNS]  DNS server: %s\n", dns_ip.cstr());    setvar_ip4(status_net_dns_server, dns_ip);}static void mdns_setup(void){    static bool mdns_started;    static const struct mdns_service {	const char *type, *proto;	uint16_t port;    } mdns_services[] = {	{ "_http", "_tcp", 80 },	{ NULL, NULL, 0 }    };    char unique_name[32];    esp_err_t unique_mdns;    if (mdns_started)	mdns_free();    if (!getvar_bool(config_mdns_enabled))	return;    mdns_started = mdns_init() == ESP_OK;    if (!mdns_started)	return;    mdns_hostname_set(hostname);    mdns_instance_name_set(hostname);    printf("[MDNS] mDNS hostname: %s\n", hostname);    unique_mdns = ESP_ERR_INVALID_STATE;    snprintf(unique_name, sizeof unique_name, "MAX80-%s", serial_number);    if (connected & CON_STA) {	mdns_ip_addr_t iplist;	iplist.addr = IP4(WiFi.localIP());	iplist.next = NULL;	unique_mdns = mdns_delegate_hostname_add(unique_name, &iplist);	printf("[MDNS] mDNS unique hostname: %s\n", unique_name);    }    for (const struct mdns_service *svc = mdns_services; svc->type; svc++) {	mdns_service_add(NULL, svc->type, svc->proto, svc->port, NULL, 0);	if (unique_mdns == ESP_OK) {	    mdns_service_add_for_host(NULL, svc->type, svc->proto,				      unique_name, svc->port, NULL, 0);	}    }}static void start_services(void){    /* Always run after (re)connect */    dns_setup();    mdns_setup();    // If Arduino supported both of these at the same that would be    // awesome, but it requires ESP-IDF reconfiguration...    if (getvar_bool(config_sntp_enabled)) {	IP4 sntp_ip = *sntp_getserver(0);	if (sntp_ip) {	    sntp_server_show();	} else {	    sntp_set_server(getvar_str(config_sntp_server));	}    }    /* Only run on first start */    if (!services_started) {	services_started = true;	printf("[WIFI] Starting network services\n");	my_httpd_start();	esp_register_shutdown_handler(stop_services);    }}static bool force_conn_update;static void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info){    bool retry_sta  = false;    bool is_connect = false;    unsigned int prev_connected = connected;    static int ap_clients;    int prev_ap_clients = ap_clients;    IP4 wifi_local_ip = WiFi.localIP();    const char *local_ip = wifi_local_ip.cstr();    switch (event) {    case ARDUINO_EVENT_WIFI_READY:	printf("[WIFI] Interface ready\n");	break;    case ARDUINO_EVENT_WIFI_SCAN_DONE:	printf("[WIFI] Completed scan for access points\n");	break;    case ARDUINO_EVENT_WIFI_STA_START:	printf("[WIFI] Client mode started\n");	break;    case ARDUINO_EVENT_WIFI_STA_STOP:	printf("[WIFI] Client mode stopped\n");	connected &= ~CON_STA;	break;    case ARDUINO_EVENT_WIFI_STA_CONNECTED:	printf("[WIFI] Connected to access point\n");	break;    case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:	printf("[WIFI] Disconnected from WiFi access point\n");	connected &= ~CON_STA;	retry_sta = true;	break;    case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:	printf("[WIFI] Authentication mode of access point has changed\n");	break;    case ARDUINO_EVENT_WIFI_STA_GOT_IP:    {	printf("[WIFI] Obtained IP address: %s\n", local_ip);	connected |= CON_STA;	is_connect = true;	break;    }    case ARDUINO_EVENT_WIFI_STA_LOST_IP:    {	printf("[WIFI] Lost IP address\n");	connected &= ~CON_STA;	retry_sta = true;	break;    }    case ARDUINO_EVENT_WPS_ER_SUCCESS:	printf("[WIFI] WiFi Protected Setup (WPS): succeeded in enrollee mode\n");	break;    case ARDUINO_EVENT_WPS_ER_FAILED:	printf("[WIFI] WiFi Protected Setup (WPS): failed in enrollee mode\n");	break;    case ARDUINO_EVENT_WPS_ER_TIMEOUT:	printf("[WIFI] WiFi Protected Setup (WPS): timeout in enrollee mode\n");	break;    case ARDUINO_EVENT_WPS_ER_PIN:	printf("[WIFI] WiFi Protected Setup (WPS): pin code in enrollee mode\n");	break;    case ARDUINO_EVENT_WIFI_AP_START:	printf("[WIFI] Access point started\n");	ap_clients  = 0;	connected  |= CON_AP;	is_connect  = true;	break;    case ARDUINO_EVENT_WIFI_AP_STOP:	printf("[WIFI] Access point stopped\n");	connected  &= ~CON_AP;	ap_clients  = 0;	break;    case ARDUINO_EVENT_WIFI_AP_STACONNECTED:	printf("[WIFI] Client connected\n");	ap_clients = WiFi.softAPgetStationNum();	break;    case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:	printf("[WIFI] Client disconnected\n");	ap_clients = WiFi.softAPgetStationNum();	break;    case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:	printf("[WIFI] Assigned IP address %s to client\n",	       inet_ntoa(info.wifi_ap_staipassigned.ip));	ap_clients = WiFi.softAPgetStationNum();	is_connect = true;	break;    case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:	printf("[WIFI] Received probe request\n");	ap_clients = WiFi.softAPgetStationNum();	break;    case ARDUINO_EVENT_WIFI_AP_GOT_IP6:	printf("[WIFI] AP IPv6 is preferred\n");	ap_clients = WiFi.softAPgetStationNum();	is_connect = true;	break;    case ARDUINO_EVENT_WIFI_STA_GOT_IP6:	printf("[WIFI] STA IPv6 is preferred\n");	is_connect = true;	break;    case ARDUINO_EVENT_ETH_GOT_IP6:	printf("[ETH]  Ethernet IPv6 is preferred\n");	is_connect = true;	break;    case ARDUINO_EVENT_ETH_START:	printf("[ETH]  Ethernet started\n");	break;    case ARDUINO_EVENT_ETH_STOP:	printf("[ETH]  Ethernet stopped\n");	connected &= ~CON_ETH;	break;    case ARDUINO_EVENT_ETH_CONNECTED:	printf("[ETH]  Ethernet connected\n");	break;    case ARDUINO_EVENT_ETH_DISCONNECTED:	printf("[ETH]  Ethernet disconnected\n");	connected &= ~CON_ETH;	retry_sta = true;	break;    case ARDUINO_EVENT_ETH_GOT_IP:	printf("[ETH]  Obtained IP address: %s\n", local_ip);	connected |= CON_ETH;	is_connect = true;	break;    default:	break;    }    if (connected & ~CON_AP) {	sta_timeout_disable();	if (!ap_clients) {	    WiFi.enableAP(false);	}    } else if (ssid) {	sta_timeout_enable();    }    unsigned int conn_change = force_conn_update ?	-1U : (connected ^ prev_connected);    if (conn_change) {	force_conn_update = false;	if (conn_change & CON_STA) {	    setvar_bool(status_net_sta_conn, connected & CON_STA);	    setvar_ip4(status_net_sta_ip4,		      connected & CON_STA ? wifi_local_ip : null_ip);	    setvar_ip4(status_net_sta_ip4_mask, WiFi.subnetMask());	    setvar_ip4(status_net_sta_ip4_gw, WiFi.gatewayIP());	}	if (conn_change & CON_AP)	    setvar_bool(status_net_ap_conn, connected & CON_AP);	if (conn_change & CON_ETH) {	    setvar_bool(status_net_eth_conn, connected & CON_ETH);	    setvar_ip4(status_net_eth_ip4,		      connected & CON_STA ? wifi_local_ip : null_ip);	    setvar_ip4(status_net_eth_ip4_mask, WiFi.subnetMask());	    setvar_ip4(status_net_eth_ip4_gw, WiFi.gatewayIP());	}	if (!ssid) {	    // No network configured	    led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_SLOW : LED_OFF);	} else {	    led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_FAST : LED_ON);	}    }    if (is_connect) {	start_services();    }    if (ap_clients != prev_ap_clients)	setvar_uint(status_net_ap_clients, ap_clients);    /*     * Don't keep retrying if there are AP clients - makes the AP     * too unreliable to use.     */    if (retry_sta && !ap_clients) {	sta_bounce();    }}static void wifi_config_ap(void){    /* No network configuration set */    IP4 AP_IP      = IP4(192,168,0,1);    IP4 AP_Netmask = IP4(255,255,255,0);    IP4 AP_Gateway = IP4(0,0,0,0); // No gateway    unsigned int channel = (time(NULL) % 11) + 1;	   // Pseudo-random    uint8_t mac[6];    static char ap_ssid[64];    // The default SoftAP MAC is totally useless, so try to    // set it to something more sensible...    WiFi.macAddress(mac);    uint8_t add = !!(mac[0] & 2);    mac[0] |= 2;		// Set local bit    mac[5] += add;		// Increment last byte if already local    esp_wifi_set_mac(WIFI_IF_AP, mac);    // Read it back to check what we got...    WiFi.softAPmacAddress(mac);    setvar_mac(status_net_ap_mac, mac);    /* The last two bytes of the efuse MAC */    snprintf(ap_ssid, sizeof ap_ssid, "MAX80_%02X%02X",	     efuse_default_mac[4], efuse_default_mac[5]);    printf("[WIFI] AP SSID %s IP %s ", ap_ssid, AP_IP.cstr());    printf("netmask %s channel %u\n", AP_Netmask.cstr(), channel);    setvar_str(status_net_ap_ssid, ap_ssid);    setvar_ip4(status_net_ap_ip4, AP_IP);    setvar_ip4(status_net_ap_ip4_mask, AP_Netmask);    setvar_uint(status_net_ap_clients, 0);    printf("WiFi.softAP\n");    WiFi.softAP(ap_ssid, NULL, channel, 0, 4, true);    printf("WiFi.softAPConfig\n");    WiFi.softAPConfig(AP_IP, AP_Gateway, AP_Netmask);    printf("WiFi.softAPsetHostname\n");    WiFi.softAPsetHostname(hostname);    // Conservative setting: 20 MHz (single channel) only; this is for    // reliability, not performance.    printf("esp_wifi_set_bandwidth\n");    esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_AP, WIFI_BW_HT20);    // Enable AP immediately if no SSID configured    WiFi.enableAP(!ssid);}static void wifi_config_sta(void){    uint8_t mac[6];    printf("WiFi.macAddress\n");    WiFi.macAddress(mac);    printf("setenv_mac\n");    setvar_mac(status_net_sta_mac, mac);    printf("setenv ssid\n");    setvar_str(status_net_sta_ssid, ssid);    if (!ssid) {	WiFi.enableSTA(false);	return;    }    printf("xTimerCreate\n");    sta_failure_timer = xTimerCreate("wifi_sta", configTICK_RATE_HZ*15,				     pdTRUE, NULL,				     (TimerCallbackFunction_t)sta_timeout);    printf("sta_timeout_enable\n");    sta_timeout_enable();    printf("WiFi.setAutoReconnect\n");    WiFi.setAutoReconnect(false); // We are doing this "ourselves"    printf("WiFi.enableSTA\n");    WiFi.enableSTA(true);    printf("WiFi.begin(%s)\n", ssid);    WiFi.begin(ssid, password);}static void wifi_config(void){    ssid         = dupstr(getvar_str(config_wifi_ssid));    password     = dupstr(getvar_str(config_wifi_psk));    hostname     = dupstr(getvar_str(config_hostname));    dnsserver    = getvar_ip4(config_ip4_dns);    force_conn_update = true;    printf("WiFi.persistent\n");    WiFi.persistent(false);    printf("WiFi.setSleep\n");    WiFi.setSleep(false);    WiFi.setTxPower(WIFI_POWER_19_5dBm);    setvar_str(status_hostname, hostname);    WiFi.hostname(hostname);    printf("wifi_config_ap\n");    wifi_config_ap();    printf("wifi_config_sta\n");    wifi_config_sta();    printf("wifi_config done\n");}void SetupWiFi() {    services_started = false;    WiFi.onEvent(WiFiEvent);    my_sntp_start();    printf("[INFO] Setting up WiFi\n");    wifi_config();}
 |