wifi.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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, sntpserver;
  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. if (prev_sync_status != sync_status) {
  42. printf("[SNTP] time synchronization lost\n");
  43. }
  44. break;
  45. case SNTP_SYNC_STATUS_COMPLETED:
  46. sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); // After successful sync
  47. if (prev_sync_status != sync_status) {
  48. char timebuf[64];
  49. const struct tm *tm = localtime(&tv->tv_sec);
  50. strftime(timebuf, sizeof timebuf, "%a %Y-%m-%d %H:%M:%S %z", tm);
  51. printf("[SNTP] Time synchronized: %s\n", timebuf);
  52. }
  53. break;
  54. default:
  55. break;
  56. }
  57. prev_sync_status = sync_status;
  58. }
  59. static void my_sntp_start()
  60. {
  61. if (getenv_bool("sntp.enabled")) {
  62. sntp_set_time_sync_notification_cb(sntp_sync_cb);
  63. sntp_setoperatingmode(SNTP_OPMODE_POLL);
  64. sntp_servermode_dhcp(!getenv_bool("ip4.dhcp.nosntp"));
  65. sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); // Until first sync
  66. sntp_init();
  67. } else {
  68. sntp_stop();
  69. }
  70. }
  71. static bool services_started;
  72. static void stop_services(void)
  73. {
  74. if (services_started) {
  75. esp_unregister_shutdown_handler(stop_services);
  76. my_httpd_stop();
  77. services_started = false;
  78. }
  79. }
  80. static inline bool invalid_ip(const ip_addr_t *ip)
  81. {
  82. return !memcmp(ip, &ip_addr_any, sizeof *ip);
  83. }
  84. static void start_services(void)
  85. {
  86. /* Always run after (re)connect */
  87. const ip_addr_t *dns_ip = dns_getserver(0);
  88. if (invalid_ip(dns_ip) || getenv_bool("ip4.dhcp.nodns")) {
  89. /* Static DNS server configuration */
  90. ip_addr_t addr;
  91. if (dnsserver != "" && inet_aton(dnsserver.c_str(), &addr)) {
  92. if (memcmp(dns_ip, &addr, sizeof addr))
  93. dns_setserver(0, &addr);
  94. }
  95. }
  96. dns_ip = dns_getserver(0);
  97. printf("[DNS] DNS server: %s\n", inet_ntoa(*dns_ip));
  98. // If Arduino supported both of these at the same that would be
  99. // awesome, but it requires ESP-IDF reconfiguration...
  100. if (sntp_enabled()) {
  101. const ip_addr_t *sntp_ip = sntp_getserver(0);
  102. if (invalid_ip(sntp_ip)) {
  103. if (sntpserver != "") {
  104. sntp_setservername(0, sntpserver.c_str());
  105. sntp_ip = sntp_getserver(0);
  106. }
  107. }
  108. if (!invalid_ip(sntp_ip))
  109. printf("[SNTP] Time server: %s\n", inet_ntoa(*sntp_ip));
  110. }
  111. /* Only run on first start */
  112. if (!services_started) {
  113. services_started = true;
  114. my_httpd_start();
  115. esp_register_shutdown_handler(stop_services);
  116. }
  117. }
  118. static String wifi_local_ip(void)
  119. {
  120. return WiFi.localIP().toString();
  121. }
  122. static void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
  123. {
  124. bool retry_sta = false;
  125. bool is_connect = false;
  126. enum connected {
  127. CON_STA = 1,
  128. CON_ETH = 2,
  129. CON_AP = 4
  130. };
  131. static int connected;
  132. static int ap_clients = 0;
  133. switch (event) {
  134. case ARDUINO_EVENT_WIFI_READY:
  135. printf("[WIFI] Interface ready\n");
  136. break;
  137. case ARDUINO_EVENT_WIFI_SCAN_DONE:
  138. printf("[WIFI] Completed scan for access points\n");
  139. break;
  140. case ARDUINO_EVENT_WIFI_STA_START:
  141. printf("[WIFI] Client started\n");
  142. break;
  143. case ARDUINO_EVENT_WIFI_STA_STOP:
  144. printf("[WIFI] Clients stopped\n");
  145. connected &= ~CON_STA;
  146. break;
  147. case ARDUINO_EVENT_WIFI_STA_CONNECTED:
  148. printf("[WIFI] Connected to access point\n");
  149. break;
  150. case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
  151. printf("[WIFI] Disconnected from WiFi access point\n");
  152. connected &= ~CON_STA;
  153. retry_sta = true;
  154. break;
  155. case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
  156. printf("[WIFI] Authentication mode of access point has changed\n");
  157. break;
  158. case ARDUINO_EVENT_WIFI_STA_GOT_IP:
  159. printf("[WIFI] Obtained IP address: %s\n", wifi_local_ip().c_str());
  160. connected |= CON_STA;
  161. is_connect = true;
  162. break;
  163. case ARDUINO_EVENT_WIFI_STA_LOST_IP:
  164. printf("[WIFI] Lost IP address and IP address is reset to 0\n");
  165. connected &= ~CON_STA;
  166. retry_sta = true;
  167. break;
  168. case ARDUINO_EVENT_WPS_ER_SUCCESS:
  169. printf("[WIFI] WiFi Protected Setup (WPS): succeeded in enrollee mode\n");
  170. break;
  171. case ARDUINO_EVENT_WPS_ER_FAILED:
  172. printf("[WIFI] WiFi Protected Setup (WPS): failed in enrollee mode\n");
  173. break;
  174. case ARDUINO_EVENT_WPS_ER_TIMEOUT:
  175. printf("[WIFI] WiFi Protected Setup (WPS): timeout in enrollee mode\n");
  176. break;
  177. case ARDUINO_EVENT_WPS_ER_PIN:
  178. printf("[WIFI] WiFi Protected Setup (WPS): pin code in enrollee mode\n");
  179. break;
  180. case ARDUINO_EVENT_WIFI_AP_START:
  181. printf("[WIFI] Access point started\n");
  182. ap_clients = 0;
  183. connected |= CON_AP;
  184. is_connect = true;
  185. break;
  186. case ARDUINO_EVENT_WIFI_AP_STOP:
  187. printf("[WIFI] Access point stopped\n");
  188. connected &= ~CON_AP;
  189. ap_clients = 0;
  190. break;
  191. case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
  192. printf("[WIFI] Client connected\n");
  193. ap_clients++;
  194. break;
  195. case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
  196. printf("[WIFI] Client disconnected\n");
  197. ap_clients--;
  198. break;
  199. case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
  200. printf("[WIFI] Assigned IP address %s to client\n",
  201. inet_ntoa(info.wifi_ap_staipassigned.ip));
  202. break;
  203. case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
  204. printf("[WIFI] Received probe request\n");
  205. break;
  206. case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
  207. printf("[WIFI] AP IPv6 is preferred\n");
  208. break;
  209. case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
  210. printf("[WIFI] STA IPv6 is preferred\n");
  211. is_connect = true;
  212. break;
  213. case ARDUINO_EVENT_ETH_GOT_IP6:
  214. printf("[ETH] Ethernet IPv6 is preferred\n");
  215. is_connect = true;
  216. break;
  217. case ARDUINO_EVENT_ETH_START:
  218. printf("[ETH] Ethernet started\n");
  219. break;
  220. case ARDUINO_EVENT_ETH_STOP:
  221. printf("[ETH] Ethernet stopped\n");
  222. connected &= ~CON_ETH;
  223. break;
  224. case ARDUINO_EVENT_ETH_CONNECTED:
  225. printf("[ETH] Ethernet connected\n");
  226. break;
  227. case ARDUINO_EVENT_ETH_DISCONNECTED:
  228. printf("[ETH] Ethernet disconnected\n");
  229. connected &= ~CON_ETH;
  230. retry_sta = true;
  231. break;
  232. case ARDUINO_EVENT_ETH_GOT_IP:
  233. printf("[ETH] Obtained IP address: %s\n", wifi_local_ip().c_str());
  234. connected |= CON_ETH;
  235. is_connect = true;
  236. default:
  237. break;
  238. }
  239. if (connected & ~CON_AP) {
  240. sta_timeout_disable();
  241. if (!ap_clients)
  242. WiFi.enableAP(false);
  243. } else if (ssid != "") {
  244. sta_timeout_enable();
  245. }
  246. if (ssid == "") {
  247. // No network configured
  248. led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_SLOW : LED_OFF);
  249. } else {
  250. led_set(LED_GREEN, connected & CON_AP ? LED_FLASH_FAST : LED_ON);
  251. }
  252. /* Maybe need to do these in a different thread? */
  253. if (is_connect) {
  254. start_services();
  255. }
  256. if (retry_sta) {
  257. WiFi.disconnect();
  258. WiFi.begin();
  259. }
  260. }
  261. static void wifi_config_ap(void)
  262. {
  263. /* No network configuration set */
  264. IPAddress AP_IP = IPAddress(192,168,0,1);
  265. IPAddress AP_Netmask = IPAddress(255,255,255,0);
  266. IPAddress AP_Gateway = IPAddress(0,0,0,0); // No gateway
  267. unsigned int channel = time(NULL) % 11; // Pseudo-random
  268. uint8_t mac[6];
  269. char ap_ssid[64];
  270. WiFi.macAddress(mac);
  271. /* The last two bytes of the MAC */
  272. snprintf(ap_ssid, sizeof ap_ssid, "MAX80_%02X%02X", mac[4], mac[5]);
  273. printf("[WIFI] AP SSID %s IP %s netmask %s channel %u\n",
  274. ap_ssid, AP_IP.toString(), AP_Netmask.toString(), channel+1);
  275. printf("WiFi.softAP\n");
  276. WiFi.softAP(ap_ssid, NULL, channel+1, 0, 4, true);
  277. printf("WiFi.softAPConfig\n");
  278. WiFi.softAPConfig(AP_IP, AP_Gateway, AP_Netmask);
  279. printf("WiFi.softAPsetHostname\n");
  280. WiFi.softAPsetHostname(ap_ssid);
  281. // Conservative setting: 20 MHz (single channel) only; this is for
  282. // reliability, not performance.
  283. printf("esp_wifi_set_bandwidth\n");
  284. esp_wifi_set_bandwidth((wifi_interface_t)ESP_IF_WIFI_AP, WIFI_BW_HT20);
  285. printf("WiFi.enableAP\n");
  286. WiFi.enableAP(ssid == "");
  287. printf("wifi_config_ap done\n");
  288. }
  289. static void wifi_config_sta(void)
  290. {
  291. if (ssid == "") {
  292. WiFi.enableSTA(false);
  293. return;
  294. }
  295. sta_failure_timer = xTimerCreate("wifi_sta", configTICK_RATE_HZ*30,
  296. pdFALSE, NULL,
  297. (TimerCallbackFunction_t)sta_timeout);
  298. sta_timeout_enable();
  299. WiFi.begin(ssid.c_str(), password.c_str());
  300. WiFi.setAutoConnect(true);
  301. WiFi.setAutoReconnect(true);
  302. WiFi.enableSTA(true);
  303. }
  304. static void wifi_config(void)
  305. {
  306. ssid = getenv("wifi.ssid");
  307. password = getenv("wifi.psk");
  308. hostname = getenv("hostname");
  309. dnsserver = getenv("ip4.dns");
  310. WiFi.persistent(false);
  311. WiFi.setSleep(false);
  312. if (hostname != "")
  313. WiFi.hostname(hostname);
  314. wifi_config_sta();
  315. wifi_config_ap();
  316. }
  317. void SetupWiFi() {
  318. services_started = false;
  319. WiFi.onEvent(WiFiEvent);
  320. my_sntp_start();
  321. printf("[INFO] Setting up WiFi\n");
  322. wifi_config();
  323. }