wifi.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. #include "common.h"
  2. #include "WiFi.h"
  3. #include "wifi.h"
  4. #include "config.h"
  5. #include "httpd.h"
  6. #include "led.h"
  7. #include <lwip/dns.h>
  8. #include <lwip/inet.h>
  9. #include <lwip/apps/sntp.h>
  10. #include <esp_sntp.h>
  11. #include <esp_wifi.h>
  12. static String ssid, password, hostname, dnsserver;
  13. static TimerHandle_t sta_failure_timer;
  14. static bool sta_timeout_enabled;
  15. static void sta_timeout(void)
  16. {
  17. // Enable the AP if the STA doesn't connect after a timeout
  18. WiFi.enableAP(true);
  19. }
  20. static void sta_timeout_enable(void)
  21. {
  22. if (!sta_failure_timer || sta_timeout_enabled)
  23. return;
  24. sta_timeout_enabled =
  25. xTimerStart(sta_failure_timer, 1);
  26. }
  27. static void sta_timeout_disable(void)
  28. {
  29. if (!sta_failure_timer || !sta_timeout_enabled)
  30. return;
  31. sta_timeout_enabled =
  32. !xTimerStop(sta_failure_timer, 1);
  33. }
  34. static void sntp_sync_cb(struct timeval *tv)
  35. {
  36. static uint8_t prev_sync_status = SNTP_SYNC_STATUS_RESET;
  37. uint8_t sync_status = sntp_get_sync_status();
  38. switch (sync_status) {
  39. case SNTP_SYNC_STATUS_RESET:
  40. sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync
  41. time_net_sync(NULL);
  42. break;
  43. case SNTP_SYNC_STATUS_COMPLETED:
  44. sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); // After successful sync
  45. time_net_sync(tv);
  46. break;
  47. default:
  48. break;
  49. }
  50. prev_sync_status = sync_status;
  51. }
  52. static void my_sntp_start(void)
  53. {
  54. setenv_bool("status.net.sntp.sync", false);
  55. if (getenv_bool("sntp.enabled")) {
  56. sntp_set_time_sync_notification_cb(sntp_sync_cb);
  57. sntp_setoperatingmode(SNTP_OPMODE_POLL);
  58. sntp_servermode_dhcp(!getenv_bool("ip4.dhcp.nosntp"));
  59. sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync
  60. sntp_init();
  61. } else {
  62. sntp_stop();
  63. }
  64. }
  65. static bool services_started;
  66. static void stop_services(void)
  67. {
  68. if (services_started) {
  69. esp_unregister_shutdown_handler(stop_services);
  70. my_httpd_stop();
  71. services_started = false;
  72. }
  73. }
  74. static inline bool invalid_ip(const ip_addr_t *ip)
  75. {
  76. return !memcmp(ip, &ip_addr_any, sizeof *ip);
  77. }
  78. static void sntp_server_show(void)
  79. {
  80. const ip_addr_t *sntp_ip = sntp_getserver(0);
  81. if (!invalid_ip(sntp_ip)) {
  82. const char *sntp_server = inet_ntoa(*sntp_ip);
  83. printf("[SNTP] Time server: %s\n", sntp_server);
  84. setenv_cond("status.net.sntp.server", sntp_server);
  85. } else {
  86. unsetenv("status.net.sntp.server");
  87. }
  88. }
  89. static void sntp_server_found(const char *name, const ip_addr_t *addr,
  90. void *arg)
  91. {
  92. (void)name;
  93. (void)arg;
  94. if (invalid_ip(addr))
  95. return;
  96. sntp_setserver(0, addr);
  97. sntp_server_show();
  98. }
  99. static void sntp_set_server(const char *name)
  100. {
  101. if (!name || !*name)
  102. return;
  103. ip_addr_t addr;
  104. err_t err = dns_gethostbyname(name, &addr, sntp_server_found, NULL);
  105. if (err == ERR_OK)
  106. sntp_server_found(name, &addr, NULL);
  107. }
  108. static void start_services(void)
  109. {
  110. /* Always run after (re)connect */
  111. const ip_addr_t *dns_ip = dns_getserver(0);
  112. if (invalid_ip(dns_ip) || getenv_bool("ip4.dhcp.nodns")) {
  113. /* Static DNS server configuration */
  114. ip_addr_t addr;
  115. if (dnsserver != "" && inet_aton(dnsserver.c_str(), &addr)) {
  116. if (memcmp(dns_ip, &addr, sizeof addr))
  117. dns_setserver(0, &addr);
  118. }
  119. }
  120. {
  121. dns_ip = dns_getserver(0);
  122. const char *dns_server_str = inet_ntoa(*dns_ip);
  123. printf("[DNS] DNS server: %s\n", dns_server_str);
  124. setenv_cond("status.net.dns.server", dns_server_str);
  125. }
  126. // If Arduino supported both of these at the same that would be
  127. // awesome, but it requires ESP-IDF reconfiguration...
  128. if (getenv_bool("sntp.enabled")) {
  129. if (!invalid_ip(sntp_getserver(0))) {
  130. sntp_server_show();
  131. } else {
  132. sntp_set_server(getenv("sntp.server"));
  133. }
  134. }
  135. /* Only run on first start */
  136. if (!services_started) {
  137. services_started = true;
  138. my_httpd_start();
  139. esp_register_shutdown_handler(stop_services);
  140. }
  141. }
  142. static const char *ip_str(const IPAddress &ip)
  143. {
  144. static char ip_buf[4*4];
  145. const IPAddress ip_none(0,0,0,0);
  146. return strcpy(ip_buf, ip == ip_none ? "" : ip.toString().c_str());
  147. }
  148. static void setenv_ip(const char *var, const IPAddress &ip)
  149. {
  150. setenv_config(var, ip_str(ip));
  151. }
  152. static bool force_conn_update;
  153. static void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
  154. {
  155. bool retry_sta = false;
  156. bool is_connect = false;
  157. enum connected {
  158. CON_STA = 1,
  159. CON_ETH = 2,
  160. CON_AP = 4
  161. };
  162. static unsigned int connected;
  163. unsigned int prev_connected = connected;
  164. static int ap_clients = 0;
  165. int prev_ap_clients = ap_clients;
  166. IPAddress wifi_local_ip = WiFi.localIP();
  167. const char *local_ip = ip_str(wifi_local_ip);
  168. switch (event) {
  169. case ARDUINO_EVENT_WIFI_READY:
  170. printf("[WIFI] Interface ready\n");
  171. break;
  172. case ARDUINO_EVENT_WIFI_SCAN_DONE:
  173. printf("[WIFI] Completed scan for access points\n");
  174. break;
  175. case ARDUINO_EVENT_WIFI_STA_START:
  176. printf("[WIFI] Client started\n");
  177. break;
  178. case ARDUINO_EVENT_WIFI_STA_STOP:
  179. printf("[WIFI] Clients stopped\n");
  180. connected &= ~CON_STA;
  181. break;
  182. case ARDUINO_EVENT_WIFI_STA_CONNECTED:
  183. printf("[WIFI] Connected to access point\n");
  184. break;
  185. case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
  186. printf("[WIFI] Disconnected from WiFi access point\n");
  187. connected &= ~CON_STA;
  188. retry_sta = true;
  189. break;
  190. case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
  191. printf("[WIFI] Authentication mode of access point has changed\n");
  192. break;
  193. case ARDUINO_EVENT_WIFI_STA_GOT_IP:
  194. {
  195. printf("[WIFI] Obtained IP address: %s\n", local_ip);
  196. connected |= CON_STA;
  197. is_connect = true;
  198. break;
  199. }
  200. case ARDUINO_EVENT_WIFI_STA_LOST_IP:
  201. {
  202. printf("[WIFI] Lost IP address\n");
  203. connected &= ~CON_STA;
  204. retry_sta = true;
  205. break;
  206. }
  207. case ARDUINO_EVENT_WPS_ER_SUCCESS:
  208. printf("[WIFI] WiFi Protected Setup (WPS): succeeded in enrollee mode\n");
  209. break;
  210. case ARDUINO_EVENT_WPS_ER_FAILED:
  211. printf("[WIFI] WiFi Protected Setup (WPS): failed in enrollee mode\n");
  212. break;
  213. case ARDUINO_EVENT_WPS_ER_TIMEOUT:
  214. printf("[WIFI] WiFi Protected Setup (WPS): timeout in enrollee mode\n");
  215. break;
  216. case ARDUINO_EVENT_WPS_ER_PIN:
  217. printf("[WIFI] WiFi Protected Setup (WPS): pin code in enrollee mode\n");
  218. break;
  219. case ARDUINO_EVENT_WIFI_AP_START:
  220. printf("[WIFI] Access point started\n");
  221. ap_clients = 0;
  222. connected |= CON_AP;
  223. is_connect = true;
  224. break;
  225. case ARDUINO_EVENT_WIFI_AP_STOP:
  226. printf("[WIFI] Access point stopped\n");
  227. connected &= ~CON_AP;
  228. ap_clients = 0;
  229. break;
  230. case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
  231. printf("[WIFI] Client connected\n");
  232. ap_clients++;
  233. break;
  234. case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
  235. printf("[WIFI] Client disconnected\n");
  236. ap_clients--;
  237. break;
  238. case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
  239. printf("[WIFI] Assigned IP address %s to client\n",
  240. inet_ntoa(info.wifi_ap_staipassigned.ip));
  241. break;
  242. case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
  243. printf("[WIFI] Received probe request\n");
  244. break;
  245. case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
  246. printf("[WIFI] AP IPv6 is preferred\n");
  247. break;
  248. case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
  249. printf("[WIFI] STA IPv6 is preferred\n");
  250. is_connect = true;
  251. break;
  252. case ARDUINO_EVENT_ETH_GOT_IP6:
  253. printf("[ETH] Ethernet IPv6 is preferred\n");
  254. is_connect = true;
  255. break;
  256. case ARDUINO_EVENT_ETH_START:
  257. printf("[ETH] Ethernet started\n");
  258. break;
  259. case ARDUINO_EVENT_ETH_STOP:
  260. printf("[ETH] Ethernet stopped\n");
  261. connected &= ~CON_ETH;
  262. break;
  263. case ARDUINO_EVENT_ETH_CONNECTED:
  264. printf("[ETH] Ethernet connected\n");
  265. break;
  266. case ARDUINO_EVENT_ETH_DISCONNECTED:
  267. printf("[ETH] Ethernet disconnected\n");
  268. connected &= ~CON_ETH;
  269. retry_sta = true;
  270. break;
  271. case ARDUINO_EVENT_ETH_GOT_IP:
  272. printf("[ETH] Obtained IP address: %s\n", local_ip);
  273. connected |= CON_ETH;
  274. is_connect = true;
  275. break;
  276. default:
  277. break;
  278. }
  279. if (connected & ~CON_AP) {
  280. sta_timeout_disable();
  281. if (!ap_clients)
  282. WiFi.enableAP(false);
  283. } else if (ssid != "") {
  284. sta_timeout_enable();
  285. }
  286. unsigned int conn_change = force_conn_update ?
  287. -1U : (connected ^ prev_connected);
  288. if (conn_change) {
  289. force_conn_update = false;
  290. if (conn_change & CON_STA) {
  291. setenv_bool("status.net.sta.conn", connected & CON_STA);
  292. setenv_config("status.net.sta.ip4",
  293. connected & CON_STA ? local_ip : "");
  294. setenv_ip("status.net.sta.ip4.mask", WiFi.subnetMask());
  295. setenv_ip("status.net.sta.ip4.gw", WiFi.gatewayIP());
  296. }
  297. if (conn_change & CON_AP)
  298. setenv_bool("status.net.ap.conn", connected & CON_AP);
  299. if (conn_change & CON_ETH) {
  300. setenv_bool("status.net.eth.conn", connected & CON_ETH);
  301. setenv_config("status.net.eth.ip4",
  302. connected & CON_ETH ? local_ip : "");
  303. setenv_ip("status.net.eth.ip4.mask", WiFi.subnetMask());
  304. setenv_ip("status.net.eth.ip4.gw", WiFi.gatewayIP());
  305. }
  306. if (ssid == "") {
  307. // No network configured
  308. led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_SLOW : LED_OFF);
  309. } else {
  310. led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_FAST : LED_ON);
  311. }
  312. }
  313. if (is_connect) {
  314. start_services();
  315. }
  316. if (ap_clients != prev_ap_clients)
  317. setenv_ul("status.net.ap.clients", ap_clients);
  318. if (retry_sta) {
  319. WiFi.disconnect();
  320. WiFi.begin();
  321. }
  322. }
  323. static void setenv_mac(const char *var, const uint8_t mac[6])
  324. {
  325. char mac_str[3*6];
  326. snprintf(mac_str, sizeof mac_str, "%02x:%02x:%02x:%02x:%02x:%02x",
  327. mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  328. setenv(var, mac_str, 1);
  329. }
  330. static void wifi_config_ap(void)
  331. {
  332. /* No network configuration set */
  333. IPAddress AP_IP = IPAddress(192,168,0,1);
  334. IPAddress AP_Netmask = IPAddress(255,255,255,0);
  335. IPAddress AP_Gateway = IPAddress(0,0,0,0); // No gateway
  336. unsigned int channel = time(NULL) % 11; // Pseudo-random
  337. uint8_t mac[6];
  338. char mac_str[6*3];
  339. char ap_ssid[64];
  340. WiFi.softAPmacAddress(mac);
  341. setenv_mac("status.net.ap.mac", mac);
  342. /* The last two bytes of the MAC */
  343. snprintf(ap_ssid, sizeof ap_ssid, "MAX80_%02X%02X", mac[4], mac[5]);
  344. printf("[WIFI] AP SSID %s IP %s netmask %s channel %u\n",
  345. ap_ssid, AP_IP.toString(), AP_Netmask.toString(), channel+1);
  346. setenv_cond("status.net.ap.ssid", ap_ssid);
  347. setenv_ip("status.net.ap.ip4", AP_IP);
  348. setenv_ip("status.net.ap.ip4.mask", AP_Netmask);
  349. setenv_ul("status.net.ap.clients", 0);
  350. WiFi.softAP(ap_ssid, NULL, channel+1, 0, 4, true);
  351. WiFi.softAPConfig(AP_IP, AP_Gateway, AP_Netmask);
  352. WiFi.softAPsetHostname(ap_ssid);
  353. // Conservative setting: 20 MHz (single channel) only; this is for
  354. // reliability, not performance.
  355. esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_AP, WIFI_BW_HT20);
  356. // Enable unconditionally if no SSID
  357. WiFi.enableAP(ssid == "");
  358. }
  359. static void wifi_config_sta(void)
  360. {
  361. uint8_t mac[6];
  362. WiFi.macAddress(mac);
  363. setenv_mac("status.net.sta.mac", mac);
  364. setenv_cond("status.net.sta.ssid", ssid.c_str());
  365. if (ssid == "") {
  366. WiFi.enableSTA(false);
  367. return;
  368. }
  369. sta_failure_timer = xTimerCreate("wifi_sta", configTICK_RATE_HZ*30,
  370. pdFALSE, NULL,
  371. (TimerCallbackFunction_t)sta_timeout);
  372. sta_timeout_enable();
  373. WiFi.begin(ssid.c_str(), password.c_str());
  374. WiFi.setAutoConnect(true);
  375. WiFi.setAutoReconnect(true);
  376. WiFi.enableSTA(true);
  377. }
  378. static void wifi_config(void)
  379. {
  380. ssid = getenv("wifi.ssid");
  381. password = getenv("wifi.psk");
  382. hostname = getenv("hostname");
  383. dnsserver = getenv("ip4.dns");
  384. force_conn_update = true;
  385. WiFi.persistent(false);
  386. WiFi.setSleep(false);
  387. if (hostname != "")
  388. WiFi.hostname(hostname);
  389. wifi_config_sta();
  390. wifi_config_ap();
  391. }
  392. void SetupWiFi() {
  393. services_started = false;
  394. WiFi.onEvent(WiFiEvent);
  395. my_sntp_start();
  396. printf("[INFO] Setting up WiFi\n");
  397. wifi_config();
  398. }