wifi.cpp 14 KB

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