wifi.cpp 14 KB

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