wifi.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. #include "common.h"
  2. #include "wifi.h"
  3. #include "config.h"
  4. #include "httpd.h"
  5. #include "led.h"
  6. #include <WiFi.h>
  7. #include <mdns.h>
  8. #include <lwip/dns.h>
  9. #include <lwip/inet.h>
  10. #include <lwip/apps/sntp.h>
  11. #include <esp_sntp.h>
  12. #include <esp_wifi.h>
  13. #include "IP4.h"
  14. WiFiUDP UDP;
  15. static const char *ssid, *password, *hostname;
  16. static IP4 dnsserver;
  17. static TimerHandle_t sta_failure_timer;
  18. enum connected {
  19. CON_STA = 1,
  20. CON_ETH = 2,
  21. CON_AP = 4
  22. };
  23. static volatile bool sta_timeout_enabled;
  24. static volatile unsigned int sta_timeout_count;
  25. static unsigned int connected;
  26. static inline bool setvar_ip4(enum sysvar_enum var, const IP4 &ip)
  27. {
  28. return setvar_ip(var, static_cast<uint32_t>(ip));
  29. }
  30. static inline IP4 getvar_ip4(enum sysvar_enum var)
  31. {
  32. return IP4(getvar_ip(var));
  33. }
  34. static void sta_bounce(void)
  35. {
  36. if (!ssid)
  37. return;
  38. if (WiFi.status() != WL_CONNECTED) {
  39. WiFi.disconnect();
  40. WiFi.begin();
  41. }
  42. }
  43. static void sta_timeout(void)
  44. {
  45. unsigned int count = ++sta_timeout_count;
  46. if (connected & CON_STA)
  47. return;
  48. // Try to ping the STA even if there are AP clients every
  49. // 15 seconds (if an SSID is configured)
  50. sta_bounce();
  51. if (!(connected & (CON_AP|CON_STA)) && (count >= 2)) {
  52. // Enable the AP if the STA doesn't connect after 30s
  53. WiFi.enableAP(true);
  54. }
  55. }
  56. static void sta_timeout_enable(void)
  57. {
  58. if (!sta_failure_timer || sta_timeout_enabled)
  59. return;
  60. sta_timeout_enabled = xTimerStart(sta_failure_timer, 1);
  61. sta_timeout_count = 0;
  62. }
  63. static void sta_timeout_disable(void)
  64. {
  65. if (!sta_failure_timer || !sta_timeout_enabled)
  66. return;
  67. sta_timeout_enabled = !xTimerStop(sta_failure_timer, 1);
  68. }
  69. static void sntp_sync_cb(struct timeval *tv)
  70. {
  71. uint8_t sync_status = sntp_get_sync_status();
  72. switch (sync_status) {
  73. case SNTP_SYNC_STATUS_RESET:
  74. sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync
  75. time_net_sync(NULL);
  76. break;
  77. case SNTP_SYNC_STATUS_COMPLETED:
  78. sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); // After successful sync
  79. time_net_sync(tv);
  80. break;
  81. default:
  82. break;
  83. }
  84. }
  85. static void my_sntp_start(void)
  86. {
  87. setvar_bool(status_net_sntp_sync, false);
  88. if (getvar_bool(config_sntp_enabled)) {
  89. sntp_set_time_sync_notification_cb(sntp_sync_cb);
  90. sntp_setoperatingmode(SNTP_OPMODE_POLL);
  91. sntp_servermode_dhcp(!getvar_bool(config_ip4_dhcp_nosntp));
  92. sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync
  93. sntp_init();
  94. } else {
  95. sntp_stop();
  96. }
  97. }
  98. static bool services_started;
  99. static void stop_services(void)
  100. {
  101. if (services_started) {
  102. esp_unregister_shutdown_handler(stop_services);
  103. printf("[WIFI] Stopping network services\n");
  104. my_httpd_stop();
  105. services_started = false;
  106. }
  107. }
  108. static void sntp_server_show(void)
  109. {
  110. IP4 sntp_ip = *sntp_getserver(0);
  111. if (!sntp_ip) {
  112. printf("[SNTP] Time server: %s\n", sntp_ip.cstr());
  113. setvar_ip4(status_net_sntp_server, sntp_ip);
  114. } else {
  115. setvar_ip4(status_net_sntp_server, null_ip);
  116. }
  117. }
  118. static void sntp_server_found(const char *name, const ip_addr_t *addr,
  119. void *arg)
  120. {
  121. (void)name;
  122. (void)arg;
  123. if (!IP4(*addr))
  124. return;
  125. sntp_setserver(0, addr);
  126. sntp_server_show();
  127. }
  128. static void sntp_set_server(const char *name)
  129. {
  130. if (!name || !*name)
  131. return;
  132. ip_addr_t addr;
  133. err_t err = dns_gethostbyname(name, &addr, sntp_server_found, NULL);
  134. if (err == ERR_OK)
  135. sntp_server_found(name, &addr, NULL);
  136. }
  137. static void dns_setup(void)
  138. {
  139. IP4 dns_ip = *dns_getserver(0);
  140. if (!dns_ip || getvar_bool(config_ip4_dhcp_nodns)) {
  141. /* Static DNS server configuration */
  142. if (dns_ip != dnsserver) {
  143. ip_addr_t addr = dnsserver;
  144. dns_setserver(0, &addr);
  145. }
  146. }
  147. dns_ip = *dns_getserver(0);
  148. printf("[DNS] DNS server: %s\n", dns_ip.cstr());
  149. setvar_ip4(status_net_dns_server, dns_ip);
  150. }
  151. static void mdns_setup(void)
  152. {
  153. static bool mdns_started;
  154. static const struct mdns_service {
  155. const char *type, *proto;
  156. uint16_t port;
  157. } mdns_services[] = {
  158. { "_http", "_tcp", 80 },
  159. { NULL, NULL, 0 }
  160. };
  161. char unique_name[32];
  162. esp_err_t unique_mdns;
  163. if (mdns_started)
  164. mdns_free();
  165. if (!getvar_bool(config_mdns_enabled))
  166. return;
  167. mdns_started = mdns_init() == ESP_OK;
  168. if (!mdns_started)
  169. return;
  170. mdns_hostname_set(hostname);
  171. mdns_instance_name_set(hostname);
  172. printf("[MDNS] mDNS hostname: %s\n", hostname);
  173. unique_mdns = ESP_ERR_INVALID_STATE;
  174. snprintf(unique_name, sizeof unique_name, "MAX80-%s", serial_number);
  175. if (connected & CON_STA) {
  176. mdns_ip_addr_t iplist;
  177. iplist.addr = IP4(WiFi.localIP());
  178. iplist.next = NULL;
  179. unique_mdns = mdns_delegate_hostname_add(unique_name, &iplist);
  180. printf("[MDNS] mDNS unique hostname: %s\n", unique_name);
  181. }
  182. for (const struct mdns_service *svc = mdns_services; svc->type; svc++) {
  183. mdns_service_add(NULL, svc->type, svc->proto, svc->port, NULL, 0);
  184. if (unique_mdns == ESP_OK) {
  185. mdns_service_add_for_host(NULL, svc->type, svc->proto,
  186. unique_name, svc->port, NULL, 0);
  187. }
  188. }
  189. }
  190. static void start_services(void)
  191. {
  192. /* Always run after (re)connect */
  193. dns_setup();
  194. mdns_setup();
  195. // If Arduino supported both of these at the same that would be
  196. // awesome, but it requires ESP-IDF reconfiguration...
  197. if (getvar_bool(config_sntp_enabled)) {
  198. IP4 sntp_ip = *sntp_getserver(0);
  199. if (sntp_ip) {
  200. sntp_server_show();
  201. } else {
  202. sntp_set_server(getvar_str(config_sntp_server));
  203. }
  204. }
  205. /* Only run on first start */
  206. if (!services_started) {
  207. services_started = true;
  208. printf("[WIFI] Starting network services\n");
  209. my_httpd_start();
  210. esp_register_shutdown_handler(stop_services);
  211. }
  212. }
  213. static bool force_conn_update;
  214. static void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
  215. {
  216. bool retry_sta = false;
  217. bool is_connect = false;
  218. unsigned int prev_connected = connected;
  219. static int ap_clients;
  220. int prev_ap_clients = ap_clients;
  221. IP4 wifi_local_ip = WiFi.localIP();
  222. const char *local_ip = wifi_local_ip.cstr();
  223. switch (event) {
  224. case ARDUINO_EVENT_WIFI_READY:
  225. printf("[WIFI] Interface ready\n");
  226. break;
  227. case ARDUINO_EVENT_WIFI_SCAN_DONE:
  228. printf("[WIFI] Completed scan for access points\n");
  229. break;
  230. case ARDUINO_EVENT_WIFI_STA_START:
  231. printf("[WIFI] Client mode started\n");
  232. break;
  233. case ARDUINO_EVENT_WIFI_STA_STOP:
  234. printf("[WIFI] Client mode stopped\n");
  235. connected &= ~CON_STA;
  236. break;
  237. case ARDUINO_EVENT_WIFI_STA_CONNECTED:
  238. printf("[WIFI] Connected to access point\n");
  239. break;
  240. case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
  241. printf("[WIFI] Disconnected from WiFi access point\n");
  242. connected &= ~CON_STA;
  243. retry_sta = true;
  244. break;
  245. case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
  246. printf("[WIFI] Authentication mode of access point has changed\n");
  247. break;
  248. case ARDUINO_EVENT_WIFI_STA_GOT_IP:
  249. {
  250. printf("[WIFI] Obtained IP address: %s\n", local_ip);
  251. connected |= CON_STA;
  252. is_connect = true;
  253. break;
  254. }
  255. case ARDUINO_EVENT_WIFI_STA_LOST_IP:
  256. {
  257. printf("[WIFI] Lost IP address\n");
  258. connected &= ~CON_STA;
  259. retry_sta = true;
  260. break;
  261. }
  262. case ARDUINO_EVENT_WPS_ER_SUCCESS:
  263. printf("[WIFI] WiFi Protected Setup (WPS): succeeded in enrollee mode\n");
  264. break;
  265. case ARDUINO_EVENT_WPS_ER_FAILED:
  266. printf("[WIFI] WiFi Protected Setup (WPS): failed in enrollee mode\n");
  267. break;
  268. case ARDUINO_EVENT_WPS_ER_TIMEOUT:
  269. printf("[WIFI] WiFi Protected Setup (WPS): timeout in enrollee mode\n");
  270. break;
  271. case ARDUINO_EVENT_WPS_ER_PIN:
  272. printf("[WIFI] WiFi Protected Setup (WPS): pin code in enrollee mode\n");
  273. break;
  274. case ARDUINO_EVENT_WIFI_AP_START:
  275. printf("[WIFI] Access point started\n");
  276. ap_clients = 0;
  277. connected |= CON_AP;
  278. is_connect = true;
  279. break;
  280. case ARDUINO_EVENT_WIFI_AP_STOP:
  281. printf("[WIFI] Access point stopped\n");
  282. connected &= ~CON_AP;
  283. ap_clients = 0;
  284. break;
  285. case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
  286. printf("[WIFI] Client connected\n");
  287. ap_clients = WiFi.softAPgetStationNum();
  288. break;
  289. case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
  290. printf("[WIFI] Client disconnected\n");
  291. ap_clients = WiFi.softAPgetStationNum();
  292. break;
  293. case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
  294. printf("[WIFI] Assigned IP address %s to client\n",
  295. inet_ntoa(info.wifi_ap_staipassigned.ip));
  296. ap_clients = WiFi.softAPgetStationNum();
  297. is_connect = true;
  298. break;
  299. case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
  300. printf("[WIFI] Received probe request\n");
  301. ap_clients = WiFi.softAPgetStationNum();
  302. break;
  303. case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
  304. printf("[WIFI] AP IPv6 is preferred\n");
  305. ap_clients = WiFi.softAPgetStationNum();
  306. is_connect = true;
  307. break;
  308. case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
  309. printf("[WIFI] STA IPv6 is preferred\n");
  310. is_connect = true;
  311. break;
  312. case ARDUINO_EVENT_ETH_GOT_IP6:
  313. printf("[ETH] Ethernet IPv6 is preferred\n");
  314. is_connect = true;
  315. break;
  316. case ARDUINO_EVENT_ETH_START:
  317. printf("[ETH] Ethernet started\n");
  318. break;
  319. case ARDUINO_EVENT_ETH_STOP:
  320. printf("[ETH] Ethernet stopped\n");
  321. connected &= ~CON_ETH;
  322. break;
  323. case ARDUINO_EVENT_ETH_CONNECTED:
  324. printf("[ETH] Ethernet connected\n");
  325. break;
  326. case ARDUINO_EVENT_ETH_DISCONNECTED:
  327. printf("[ETH] Ethernet disconnected\n");
  328. connected &= ~CON_ETH;
  329. retry_sta = true;
  330. break;
  331. case ARDUINO_EVENT_ETH_GOT_IP:
  332. printf("[ETH] Obtained IP address: %s\n", local_ip);
  333. connected |= CON_ETH;
  334. is_connect = true;
  335. break;
  336. default:
  337. break;
  338. }
  339. if (connected & ~CON_AP) {
  340. sta_timeout_disable();
  341. if (!ap_clients) {
  342. WiFi.enableAP(false);
  343. }
  344. } else if (ssid) {
  345. sta_timeout_enable();
  346. }
  347. unsigned int conn_change = force_conn_update ?
  348. -1U : (connected ^ prev_connected);
  349. if (conn_change) {
  350. force_conn_update = false;
  351. if (conn_change & CON_STA) {
  352. setvar_bool(status_net_sta_conn, connected & CON_STA);
  353. setvar_ip4(status_net_sta_ip4,
  354. connected & CON_STA ? wifi_local_ip : null_ip);
  355. setvar_ip4(status_net_sta_ip4_mask, WiFi.subnetMask());
  356. setvar_ip4(status_net_sta_ip4_gw, WiFi.gatewayIP());
  357. }
  358. if (conn_change & CON_AP)
  359. setvar_bool(status_net_ap_conn, connected & CON_AP);
  360. if (conn_change & CON_ETH) {
  361. setvar_bool(status_net_eth_conn, connected & CON_ETH);
  362. setvar_ip4(status_net_eth_ip4,
  363. connected & CON_STA ? wifi_local_ip : null_ip);
  364. setvar_ip4(status_net_eth_ip4_mask, WiFi.subnetMask());
  365. setvar_ip4(status_net_eth_ip4_gw, WiFi.gatewayIP());
  366. }
  367. if (!ssid) {
  368. // No network configured
  369. led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_SLOW : LED_OFF);
  370. } else {
  371. led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_FAST : LED_ON);
  372. }
  373. }
  374. if (is_connect) {
  375. start_services();
  376. }
  377. if (ap_clients != prev_ap_clients)
  378. setvar_uint(status_net_ap_clients, ap_clients);
  379. /*
  380. * Don't keep retrying if there are AP clients - makes the AP
  381. * too unreliable to use.
  382. */
  383. if (retry_sta && !ap_clients) {
  384. sta_bounce();
  385. }
  386. }
  387. static void wifi_config_ap(void)
  388. {
  389. /* No network configuration set */
  390. IP4 AP_IP = IP4(192,168,0,1);
  391. IP4 AP_Netmask = IP4(255,255,255,0);
  392. IP4 AP_Gateway = IP4(0,0,0,0); // No gateway
  393. unsigned int channel = (time(NULL) % 11) + 1; // Pseudo-random
  394. uint8_t mac[6];
  395. static char ap_ssid[64];
  396. // The default SoftAP MAC is totally useless, so try to
  397. // set it to something more sensible...
  398. WiFi.macAddress(mac);
  399. uint8_t add = !!(mac[0] & 2);
  400. mac[0] |= 2; // Set local bit
  401. mac[5] += add; // Increment last byte if already local
  402. esp_wifi_set_mac(WIFI_IF_AP, mac);
  403. // Read it back to check what we got...
  404. WiFi.softAPmacAddress(mac);
  405. setvar_mac(status_net_ap_mac, mac);
  406. /* The last two bytes of the efuse MAC */
  407. snprintf(ap_ssid, sizeof ap_ssid, "MAX80_%02X%02X",
  408. efuse_default_mac[4], efuse_default_mac[5]);
  409. printf("[WIFI] AP SSID %s IP %s ", ap_ssid, AP_IP.cstr());
  410. printf("netmask %s channel %u\n", AP_Netmask.cstr(), channel);
  411. setvar_str(status_net_ap_ssid, ap_ssid);
  412. setvar_ip4(status_net_ap_ip4, AP_IP);
  413. setvar_ip4(status_net_ap_ip4_mask, AP_Netmask);
  414. setvar_uint(status_net_ap_clients, 0);
  415. printf("WiFi.softAP\n");
  416. WiFi.softAP(ap_ssid, NULL, channel, 0, 4, true);
  417. printf("WiFi.softAPConfig\n");
  418. WiFi.softAPConfig(AP_IP, AP_Gateway, AP_Netmask);
  419. printf("WiFi.softAPsetHostname\n");
  420. WiFi.softAPsetHostname(hostname);
  421. // Conservative setting: 20 MHz (single channel) only; this is for
  422. // reliability, not performance.
  423. printf("esp_wifi_set_bandwidth\n");
  424. esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_AP, WIFI_BW_HT20);
  425. // Enable AP immediately if no SSID configured
  426. WiFi.enableAP(!ssid);
  427. }
  428. static void wifi_config_sta(void)
  429. {
  430. uint8_t mac[6];
  431. printf("WiFi.macAddress\n");
  432. WiFi.macAddress(mac);
  433. printf("setenv_mac\n");
  434. setvar_mac(status_net_sta_mac, mac);
  435. printf("setenv ssid\n");
  436. setvar_str(status_net_sta_ssid, ssid);
  437. if (!ssid) {
  438. WiFi.enableSTA(false);
  439. return;
  440. }
  441. printf("xTimerCreate\n");
  442. sta_failure_timer = xTimerCreate("wifi_sta", configTICK_RATE_HZ*15,
  443. pdTRUE, NULL,
  444. (TimerCallbackFunction_t)sta_timeout);
  445. printf("sta_timeout_enable\n");
  446. sta_timeout_enable();
  447. printf("WiFi.setAutoReconnect\n");
  448. WiFi.setAutoReconnect(false); // We are doing this "ourselves"
  449. printf("WiFi.enableSTA\n");
  450. WiFi.enableSTA(true);
  451. printf("WiFi.begin(%s)\n", ssid);
  452. WiFi.begin(ssid, password);
  453. }
  454. static void wifi_config(void)
  455. {
  456. ssid = dupstr(getvar_str(config_wifi_ssid));
  457. password = dupstr(getvar_str(config_wifi_psk));
  458. hostname = dupstr(getvar_str(config_hostname));
  459. dnsserver = getvar_ip4(config_ip4_dns);
  460. force_conn_update = true;
  461. printf("WiFi.persistent\n");
  462. WiFi.persistent(false);
  463. printf("WiFi.setSleep\n");
  464. WiFi.setSleep(false);
  465. WiFi.setTxPower(WIFI_POWER_19_5dBm);
  466. setvar_str(status_hostname, hostname);
  467. WiFi.hostname(hostname);
  468. printf("wifi_config_ap\n");
  469. wifi_config_ap();
  470. printf("wifi_config_sta\n");
  471. wifi_config_sta();
  472. printf("wifi_config done\n");
  473. }
  474. void SetupWiFi() {
  475. services_started = false;
  476. WiFi.onEvent(WiFiEvent);
  477. my_sntp_start();
  478. printf("[INFO] Setting up WiFi\n");
  479. wifi_config();
  480. }