ESPAsyncWiFiManager.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348
  1. /**************************************************************
  2. AsyncWiFiManager is a library for the ESP8266/Arduino platform
  3. (https://github.com/esp8266/Arduino) to enable easy
  4. configuration and reconfiguration of WiFi credentials using a Captive Portal
  5. inspired by:
  6. http://www.esp8266.com/viewtopic.php?f=29&t=2520
  7. https://github.com/chriscook8/esp-arduino-apboot
  8. https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced
  9. Built by AlexT https://github.com/tzapu
  10. Ported to Async Web Server by https://github.com/alanswx
  11. Licensed under MIT license
  12. **************************************************************/
  13. #include "ESPAsyncWiFiManager.h"
  14. AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *custom)
  15. {
  16. _id = NULL;
  17. _placeholder = NULL;
  18. _length = 0;
  19. _value = NULL;
  20. _customHTML = custom;
  21. }
  22. AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id,
  23. const char *placeholder,
  24. const char *defaultValue,
  25. unsigned int length)
  26. {
  27. init(id, placeholder, defaultValue, length, "");
  28. }
  29. AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id,
  30. const char *placeholder,
  31. const char *defaultValue,
  32. unsigned int length,
  33. const char *custom)
  34. {
  35. init(id, placeholder, defaultValue, length, custom);
  36. }
  37. void AsyncWiFiManagerParameter::init(const char *id,
  38. const char *placeholder,
  39. const char *defaultValue,
  40. unsigned int length,
  41. const char *custom)
  42. {
  43. _id = id;
  44. _placeholder = placeholder;
  45. _length = length;
  46. _value = new char[length + 1];
  47. for (unsigned int i = 0; i < length; i++)
  48. {
  49. _value[i] = 0;
  50. }
  51. if (defaultValue != NULL)
  52. {
  53. strncpy(_value, defaultValue, length);
  54. }
  55. _customHTML = custom;
  56. }
  57. const char *AsyncWiFiManagerParameter::getValue()
  58. {
  59. return _value;
  60. }
  61. const char *AsyncWiFiManagerParameter::getID()
  62. {
  63. return _id;
  64. }
  65. const char *AsyncWiFiManagerParameter::getPlaceholder()
  66. {
  67. return _placeholder;
  68. }
  69. unsigned int AsyncWiFiManagerParameter::getValueLength()
  70. {
  71. return _length;
  72. }
  73. const char *AsyncWiFiManagerParameter::getCustomHTML()
  74. {
  75. return _customHTML;
  76. }
  77. #ifdef USE_EADNS
  78. AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server,
  79. AsyncDNSServer *dns) : server(server), dnsServer(dns)
  80. {
  81. #else
  82. AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server,
  83. DNSServer *dns) : server(server), dnsServer(dns)
  84. {
  85. #endif
  86. wifiSSIDs = NULL;
  87. wifiSSIDscan = true;
  88. _modeless = false;
  89. shouldscan = true;
  90. }
  91. void AsyncWiFiManager::addParameter(AsyncWiFiManagerParameter *p)
  92. {
  93. _params[_paramsCount] = p;
  94. _paramsCount++;
  95. DEBUG_WM(F("Adding parameter"));
  96. DEBUG_WM(p->getID());
  97. }
  98. void AsyncWiFiManager::setupConfigPortal()
  99. {
  100. // dnsServer.reset(new DNSServer());
  101. // server.reset(new ESP8266WebServer(80));
  102. server->reset();
  103. DEBUG_WM(F(""));
  104. _configPortalStart = millis();
  105. DEBUG_WM(F("Configuring access point... "));
  106. DEBUG_WM(_apName);
  107. if (_apPassword != NULL)
  108. {
  109. if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63)
  110. {
  111. // fail passphrase to short or long!
  112. DEBUG_WM(F("Invalid AccessPoint password. Ignoring"));
  113. _apPassword = NULL;
  114. }
  115. DEBUG_WM(_apPassword);
  116. }
  117. // optional soft ip config
  118. if (_ap_static_ip)
  119. {
  120. DEBUG_WM(F("Custom AP IP/GW/Subnet"));
  121. WiFi.softAPConfig(_ap_static_ip, _ap_static_gw, _ap_static_sn);
  122. }
  123. if (_apPassword != NULL)
  124. {
  125. WiFi.softAP(_apName, _apPassword); // password option
  126. }
  127. else
  128. {
  129. WiFi.softAP(_apName);
  130. }
  131. delay(500); // without delay I've seen the IP address blank
  132. DEBUG_WM(F("AP IP address: "));
  133. DEBUG_WM(WiFi.softAPIP());
  134. // setup the DNS server redirecting all the domains to the apIP
  135. #ifdef USE_EADNS
  136. dnsServer->setErrorReplyCode(AsyncDNSReplyCode::NoError);
  137. #else
  138. dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
  139. #endif
  140. if (!dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()))
  141. {
  142. DEBUG_WM(F("Could not start Captive DNS Server!"));
  143. }
  144. setInfo();
  145. // setup web pages: root, wifi config pages, SO captive portal detectors and not found
  146. server->on("/",
  147. std::bind(&AsyncWiFiManager::handleRoot, this, std::placeholders::_1))
  148. .setFilter(ON_AP_FILTER);
  149. server->on("/wifi",
  150. std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, true))
  151. .setFilter(ON_AP_FILTER);
  152. server->on("/0wifi",
  153. std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, false))
  154. .setFilter(ON_AP_FILTER);
  155. server->on("/wifisave",
  156. std::bind(&AsyncWiFiManager::handleWifiSave, this, std::placeholders::_1))
  157. .setFilter(ON_AP_FILTER);
  158. server->on("/i",
  159. std::bind(&AsyncWiFiManager::handleInfo, this, std::placeholders::_1))
  160. .setFilter(ON_AP_FILTER);
  161. server->on("/r",
  162. std::bind(&AsyncWiFiManager::handleReset, this, std::placeholders::_1))
  163. .setFilter(ON_AP_FILTER);
  164. server->on("/fwlink",
  165. std::bind(&AsyncWiFiManager::handleRoot, this, std::placeholders::_1))
  166. .setFilter(ON_AP_FILTER); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
  167. server->onNotFound(std::bind(&AsyncWiFiManager::handleNotFound, this, std::placeholders::_1));
  168. server->begin(); // web server start
  169. DEBUG_WM(F("HTTP server started"));
  170. }
  171. static const char HEX_CHAR_ARRAY[17] = "0123456789ABCDEF";
  172. #if !defined(ESP8266)
  173. /**
  174. * convert char array (hex values) to readable string by seperator
  175. * buf: buffer to convert
  176. * length: data length
  177. * strSeperator seperator between each hex value
  178. * return: formated value as String
  179. */
  180. static String byteToHexString(uint8_t *buf, uint8_t length, String strSeperator = "-")
  181. {
  182. String dataString = "";
  183. for (uint8_t i = 0; i < length; i++)
  184. {
  185. byte v = buf[i] / 16;
  186. byte w = buf[i] % 16;
  187. if (i > 0)
  188. {
  189. dataString += strSeperator;
  190. }
  191. dataString += String(HEX_CHAR_ARRAY[v]);
  192. dataString += String(HEX_CHAR_ARRAY[w]);
  193. }
  194. dataString.toUpperCase();
  195. return dataString;
  196. } // byteToHexString
  197. String getESP32ChipID()
  198. {
  199. uint64_t chipid;
  200. chipid = ESP.getEfuseMac(); // the chip ID is essentially its MAC address (length: 6 bytes)
  201. uint8_t chipid_size = 6;
  202. uint8_t chipid_arr[chipid_size];
  203. for (uint8_t i = 0; i < chipid_size; i++)
  204. {
  205. chipid_arr[i] = (chipid >> (8 * i)) & 0xff;
  206. }
  207. return byteToHexString(chipid_arr, chipid_size, "");
  208. }
  209. #endif
  210. boolean AsyncWiFiManager::autoConnect(unsigned long maxConnectRetries,
  211. unsigned long retryDelayMs)
  212. {
  213. String ssid = "ESP";
  214. #if defined(ESP8266)
  215. ssid += String(ESP.getChipId());
  216. #else
  217. ssid += getESP32ChipID();
  218. #endif
  219. return autoConnect(ssid.c_str(), NULL);
  220. }
  221. boolean AsyncWiFiManager::autoConnect(char const *apName,
  222. char const *apPassword,
  223. unsigned long maxConnectRetries,
  224. unsigned long retryDelayMs)
  225. {
  226. DEBUG_WM(F(""));
  227. // attempt to connect; should it fail, fall back to AP
  228. WiFi.mode(WIFI_STA);
  229. for (unsigned long tryNumber = 0; tryNumber < maxConnectRetries; tryNumber++)
  230. {
  231. DEBUG_WM(F("AutoConnect Try No.:"));
  232. DEBUG_WM(tryNumber);
  233. if (connectWifi("", "") == WL_CONNECTED)
  234. {
  235. DEBUG_WM(F("IP Address:"));
  236. DEBUG_WM(WiFi.localIP());
  237. // connected
  238. return true;
  239. }
  240. if (tryNumber + 1 < maxConnectRetries)
  241. {
  242. // we might connect during the delay
  243. unsigned long restDelayMs = retryDelayMs;
  244. while (restDelayMs != 0)
  245. {
  246. if (WiFi.status() == WL_CONNECTED)
  247. {
  248. DEBUG_WM(F("IP Address (connected during delay):"));
  249. DEBUG_WM(WiFi.localIP());
  250. return true;
  251. }
  252. unsigned long thisDelay = std::min(restDelayMs, 100ul);
  253. delay(thisDelay);
  254. restDelayMs -= thisDelay;
  255. }
  256. }
  257. }
  258. return startConfigPortal(apName, apPassword);
  259. }
  260. String AsyncWiFiManager::networkListAsString()
  261. {
  262. String pager;
  263. // display networks in page
  264. for (int i = 0; i < wifiSSIDCount; i++)
  265. {
  266. if (wifiSSIDs[i].duplicate == true)
  267. {
  268. continue; // skip dups
  269. }
  270. unsigned int quality = getRSSIasQuality(wifiSSIDs[i].RSSI);
  271. if (_minimumQuality == 0 || _minimumQuality < quality)
  272. {
  273. String item = FPSTR(HTTP_ITEM);
  274. String rssiQ;
  275. rssiQ += quality;
  276. item.replace("{v}", wifiSSIDs[i].SSID);
  277. item.replace("{r}", rssiQ);
  278. #if defined(ESP8266)
  279. if (wifiSSIDs[i].encryptionType != ENC_TYPE_NONE)
  280. {
  281. #else
  282. if (wifiSSIDs[i].encryptionType != WIFI_AUTH_OPEN)
  283. {
  284. #endif
  285. item.replace("{i}", "l");
  286. }
  287. else
  288. {
  289. item.replace("{i}", "");
  290. }
  291. pager += item;
  292. }
  293. else
  294. {
  295. DEBUG_WM(F("Skipping due to quality"));
  296. }
  297. }
  298. return pager;
  299. }
  300. String AsyncWiFiManager::scanModal()
  301. {
  302. shouldscan = true;
  303. scan();
  304. String pager = networkListAsString();
  305. return pager;
  306. }
  307. void AsyncWiFiManager::scan(boolean async)
  308. {
  309. if (!shouldscan)
  310. {
  311. return;
  312. }
  313. DEBUG_WM(F("About to scan()"));
  314. if (wifiSSIDscan)
  315. {
  316. wifi_ssid_count_t n = WiFi.scanNetworks(async);
  317. copySSIDInfo(n);
  318. }
  319. }
  320. void AsyncWiFiManager::copySSIDInfo(wifi_ssid_count_t n)
  321. {
  322. if (n == WIFI_SCAN_FAILED)
  323. {
  324. DEBUG_WM(F("scanNetworks returned: WIFI_SCAN_FAILED!"));
  325. }
  326. else if (n == WIFI_SCAN_RUNNING)
  327. {
  328. DEBUG_WM(F("scanNetworks returned: WIFI_SCAN_RUNNING!"));
  329. }
  330. else if (n < 0)
  331. {
  332. DEBUG_WM(F("scanNetworks failed with unknown error code!"));
  333. }
  334. else if (n == 0)
  335. {
  336. DEBUG_WM(F("No networks found"));
  337. // page += F("No networks found. Refresh to scan again.");
  338. }
  339. else
  340. {
  341. DEBUG_WM(F("Scan done"));
  342. }
  343. if (n > 0)
  344. {
  345. // WE SHOULD MOVE THIS IN PLACE ATOMICALLY
  346. if (wifiSSIDs)
  347. {
  348. delete[] wifiSSIDs;
  349. }
  350. wifiSSIDs = new WiFiResult[n];
  351. wifiSSIDCount = n;
  352. if (n > 0)
  353. {
  354. shouldscan = false;
  355. }
  356. for (wifi_ssid_count_t i = 0; i < n; i++)
  357. {
  358. wifiSSIDs[i].duplicate = false;
  359. #if defined(ESP8266)
  360. WiFi.getNetworkInfo(i,
  361. wifiSSIDs[i].SSID,
  362. wifiSSIDs[i].encryptionType,
  363. wifiSSIDs[i].RSSI,
  364. wifiSSIDs[i].BSSID,
  365. wifiSSIDs[i].channel,
  366. wifiSSIDs[i].isHidden);
  367. #else
  368. WiFi.getNetworkInfo(i,
  369. wifiSSIDs[i].SSID,
  370. wifiSSIDs[i].encryptionType,
  371. wifiSSIDs[i].RSSI,
  372. wifiSSIDs[i].BSSID,
  373. wifiSSIDs[i].channel);
  374. #endif
  375. }
  376. // RSSI SORT
  377. // old sort
  378. for (int i = 0; i < n; i++)
  379. {
  380. for (int j = i + 1; j < n; j++)
  381. {
  382. if (wifiSSIDs[j].RSSI > wifiSSIDs[i].RSSI)
  383. {
  384. std::swap(wifiSSIDs[i], wifiSSIDs[j]);
  385. }
  386. }
  387. }
  388. // remove duplicates ( must be RSSI sorted )
  389. if (_removeDuplicateAPs)
  390. {
  391. String cssid;
  392. for (int i = 0; i < n; i++)
  393. {
  394. if (wifiSSIDs[i].duplicate == true)
  395. {
  396. continue;
  397. }
  398. cssid = wifiSSIDs[i].SSID;
  399. for (int j = i + 1; j < n; j++)
  400. {
  401. if (cssid == wifiSSIDs[j].SSID)
  402. {
  403. DEBUG_WM("DUP AP: " + wifiSSIDs[j].SSID);
  404. wifiSSIDs[j].duplicate = true; // set dup aps to NULL
  405. }
  406. }
  407. }
  408. }
  409. }
  410. }
  411. void AsyncWiFiManager::startConfigPortalModeless(char const *apName, char const *apPassword)
  412. {
  413. _modeless = true;
  414. _apName = apName;
  415. _apPassword = apPassword;
  416. /*
  417. AJS - do we want this?
  418. */
  419. // setup AP
  420. WiFi.mode(WIFI_AP_STA);
  421. DEBUG_WM(F("SET AP STA"));
  422. // try to connect
  423. if (connectWifi("", "") == WL_CONNECTED)
  424. {
  425. DEBUG_WM(F("IP Address:"));
  426. DEBUG_WM(WiFi.localIP());
  427. // connected
  428. // call the callback!
  429. if (_savecallback != NULL)
  430. {
  431. // TODO: check if any custom parameters actually exist, and check if they really changed maybe
  432. _savecallback();
  433. }
  434. }
  435. // notify we entered AP mode
  436. if (_apcallback != NULL)
  437. {
  438. _apcallback(this);
  439. }
  440. connect = false;
  441. setupConfigPortal();
  442. scannow = 0;
  443. }
  444. void AsyncWiFiManager::loop()
  445. {
  446. safeLoop();
  447. criticalLoop();
  448. }
  449. void AsyncWiFiManager::setInfo()
  450. {
  451. if (needInfo)
  452. {
  453. pager = infoAsString();
  454. wifiStatus = WiFi.status();
  455. needInfo = false;
  456. }
  457. }
  458. // anything that accesses WiFi, ESP or EEPROM goes here
  459. void AsyncWiFiManager::criticalLoop()
  460. {
  461. if (_modeless)
  462. {
  463. if (scannow == 0 || millis() - scannow >= 60000)
  464. {
  465. scannow = millis();
  466. scan(true);
  467. }
  468. wifi_ssid_count_t n = WiFi.scanComplete();
  469. if (n >= 0)
  470. {
  471. copySSIDInfo(n);
  472. WiFi.scanDelete();
  473. }
  474. if (connect)
  475. {
  476. connect = false;
  477. //delay(2000);
  478. DEBUG_WM(F("Connecting to new AP"));
  479. // using user-provided _ssid, _pass in place of system-stored ssid and pass
  480. if (connectWifi(_ssid, _pass) != WL_CONNECTED)
  481. {
  482. DEBUG_WM(F("Failed to connect."));
  483. }
  484. else
  485. {
  486. // connected
  487. // alanswx - should we have a config to decide if we should shut down AP?
  488. // WiFi.mode(WIFI_STA);
  489. // notify that configuration has changed and any optional parameters should be saved
  490. if (_savecallback != NULL)
  491. {
  492. // TODO: check if any custom parameters actually exist, and check if they really changed maybe
  493. _savecallback();
  494. }
  495. return;
  496. }
  497. if (_shouldBreakAfterConfig)
  498. {
  499. // flag set to exit after config after trying to connect
  500. // notify that configuration has changed and any optional parameters should be saved
  501. if (_savecallback != NULL)
  502. {
  503. // TODO: check if any custom parameters actually exist, and check if they really changed maybe
  504. _savecallback();
  505. }
  506. }
  507. }
  508. }
  509. }
  510. // anything that doesn't access WiFi, ESP or EEPROM can go here
  511. void AsyncWiFiManager::safeLoop()
  512. {
  513. #ifndef USE_EADNS
  514. dnsServer->processNextRequest();
  515. #endif
  516. }
  517. boolean AsyncWiFiManager::startConfigPortal(char const *apName, char const *apPassword)
  518. {
  519. // setup AP
  520. WiFi.mode(WIFI_AP_STA);
  521. DEBUG_WM(F("SET AP STA"));
  522. _apName = apName;
  523. _apPassword = apPassword;
  524. // notify we entered AP mode
  525. if (_apcallback != NULL)
  526. {
  527. _apcallback(this);
  528. }
  529. connect = false;
  530. setupConfigPortal();
  531. scannow = 0;
  532. while (_configPortalTimeout == 0 || millis() - _configPortalStart < _configPortalTimeout)
  533. {
  534. // DNS
  535. #ifndef USE_EADNS
  536. dnsServer->processNextRequest();
  537. #endif
  538. //
  539. // we should do a scan every so often here and
  540. // try to reconnect to AP while we are at it
  541. //
  542. if (scannow == 0 || millis() - scannow >= 10000)
  543. {
  544. DEBUG_WM(F("About to scan()"));
  545. shouldscan = true; // since we are modal, we can scan every time
  546. #if defined(ESP8266)
  547. // we might still be connecting, so that has to stop for scanning
  548. ETS_UART_INTR_DISABLE();
  549. wifi_station_disconnect();
  550. ETS_UART_INTR_ENABLE();
  551. #else
  552. WiFi.disconnect(false);
  553. #endif
  554. scanModal();
  555. if (_tryConnectDuringConfigPortal)
  556. {
  557. WiFi.begin(); // try to reconnect to AP
  558. }
  559. scannow = millis();
  560. }
  561. // attempts to reconnect were successful
  562. if (WiFi.status() == WL_CONNECTED)
  563. {
  564. // connected
  565. WiFi.mode(WIFI_STA);
  566. // notify that configuration has changed and any optional parameters should be saved
  567. if (_savecallback != NULL)
  568. {
  569. // TODO: check if any custom parameters actually exist, and check if they really changed maybe
  570. _savecallback();
  571. }
  572. break;
  573. }
  574. if (connect)
  575. {
  576. connect = false;
  577. delay(2000);
  578. DEBUG_WM(F("Connecting to new AP"));
  579. // using user-provided _ssid, _pass in place of system-stored ssid and pass
  580. if (connectWifi(_ssid, _pass) == WL_CONNECTED)
  581. {
  582. // connected
  583. WiFi.mode(WIFI_STA);
  584. // notify that configuration has changed and any optional parameters should be saved
  585. if (_savecallback != NULL)
  586. {
  587. // TODO: check if any custom parameters actually exist, and check if they really changed maybe
  588. _savecallback();
  589. }
  590. break;
  591. }
  592. else
  593. {
  594. DEBUG_WM(F("Failed to connect."));
  595. }
  596. if (_shouldBreakAfterConfig)
  597. {
  598. // flag set to exit after config after trying to connect
  599. // notify that configuration has changed and any optional parameters should be saved
  600. if (_savecallback != NULL)
  601. {
  602. // TODO: check if any custom parameters actually exist, and check if they really changed maybe
  603. _savecallback();
  604. }
  605. break;
  606. }
  607. }
  608. yield();
  609. }
  610. server->reset();
  611. dnsServer->stop();
  612. return WiFi.status() == WL_CONNECTED;
  613. }
  614. uint8_t AsyncWiFiManager::connectWifi(String ssid, String pass)
  615. {
  616. DEBUG_WM(F("Connecting as wifi client..."));
  617. // check if we've got static_ip settings, if we do, use those
  618. if (_sta_static_ip)
  619. {
  620. DEBUG_WM(F("Custom STA IP/GW/Subnet/DNS"));
  621. WiFi.config(_sta_static_ip, _sta_static_gw, _sta_static_sn, _sta_static_dns1, _sta_static_dns2);
  622. DEBUG_WM(WiFi.localIP());
  623. }
  624. // fix for auto connect racing issue
  625. // if (WiFi.status() == WL_CONNECTED) {
  626. // DEBUG_WM("Already connected. Bailing out.");
  627. // return WL_CONNECTED;
  628. // }
  629. // check if we have ssid and pass and force those, if not, try with last saved values
  630. if (ssid != "")
  631. {
  632. #if defined(ESP8266)
  633. // trying to fix connection in progress hanging
  634. ETS_UART_INTR_DISABLE();
  635. wifi_station_disconnect();
  636. ETS_UART_INTR_ENABLE();
  637. #else
  638. WiFi.disconnect(false);
  639. #endif
  640. WiFi.begin(ssid.c_str(), pass.c_str());
  641. }
  642. else
  643. {
  644. if (WiFi.SSID().length() > 0)
  645. {
  646. DEBUG_WM(F("Using last saved values, should be faster"));
  647. #if defined(ESP8266)
  648. // trying to fix connection in progress hanging
  649. ETS_UART_INTR_DISABLE();
  650. wifi_station_disconnect();
  651. ETS_UART_INTR_ENABLE();
  652. #else
  653. WiFi.disconnect(false);
  654. #endif
  655. WiFi.begin();
  656. }
  657. else
  658. {
  659. DEBUG_WM(F("Try to connect with saved credentials"));
  660. WiFi.begin();
  661. }
  662. }
  663. uint8_t connRes = waitForConnectResult();
  664. DEBUG_WM(F("Connection result: "));
  665. DEBUG_WM(connRes);
  666. // not connected, WPS enabled, no pass - first attempt
  667. #ifdef NO_EXTRA_4K_HEAP
  668. if (_tryWPS && connRes != WL_CONNECTED && pass == "")
  669. {
  670. startWPS();
  671. // should be connected at the end of WPS
  672. connRes = waitForConnectResult();
  673. }
  674. #endif
  675. needInfo = true;
  676. setInfo();
  677. return connRes;
  678. }
  679. uint8_t AsyncWiFiManager::waitForConnectResult()
  680. {
  681. if (_connectTimeout == 0)
  682. {
  683. return WiFi.waitForConnectResult();
  684. }
  685. else
  686. {
  687. DEBUG_WM(F("Waiting for connection result with time out"));
  688. unsigned long start = millis();
  689. boolean keepConnecting = true;
  690. uint8_t status;
  691. while (keepConnecting)
  692. {
  693. status = WiFi.status();
  694. if (millis() > start + _connectTimeout)
  695. {
  696. keepConnecting = false;
  697. DEBUG_WM(F("Connection timed out"));
  698. }
  699. if (status == WL_CONNECTED || status == WL_CONNECT_FAILED)
  700. {
  701. keepConnecting = false;
  702. }
  703. delay(100);
  704. }
  705. return status;
  706. }
  707. }
  708. #ifdef NO_EXTRA_4K_HEAP
  709. void AsyncWiFiManager::startWPS()
  710. {
  711. DEBUG_WM(F("START WPS"));
  712. #if defined(ESP8266)
  713. WiFi.beginWPSConfig();
  714. #else
  715. //esp_wps_config_t config = WPS_CONFIG_INIT_DEFAULT(ESP_WPS_MODE);
  716. esp_wps_config_t config = {};
  717. config.wps_type = ESP_WPS_MODE;
  718. config.crypto_funcs = &g_wifi_default_wps_crypto_funcs;
  719. strcpy(config.factory_info.manufacturer, "ESPRESSIF");
  720. strcpy(config.factory_info.model_number, "ESP32");
  721. strcpy(config.factory_info.model_name, "ESPRESSIF IOT");
  722. strcpy(config.factory_info.device_name, "ESP STATION");
  723. esp_wifi_wps_enable(&config);
  724. esp_wifi_wps_start(0);
  725. #endif
  726. DEBUG_WM(F("END WPS"));
  727. }
  728. #endif
  729. String AsyncWiFiManager::getConfigPortalSSID()
  730. {
  731. return _apName;
  732. }
  733. void AsyncWiFiManager::resetSettings()
  734. {
  735. DEBUG_WM(F("settings invalidated"));
  736. DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA."));
  737. WiFi.mode(WIFI_AP_STA); // cannot erase if not in STA mode !
  738. WiFi.persistent(true);
  739. #if defined(ESP8266)
  740. WiFi.disconnect(true);
  741. #else
  742. WiFi.disconnect(true, true);
  743. #endif
  744. WiFi.persistent(false);
  745. //delay(200);
  746. }
  747. void AsyncWiFiManager::setTimeout(unsigned long seconds)
  748. {
  749. setConfigPortalTimeout(seconds);
  750. }
  751. void AsyncWiFiManager::setConfigPortalTimeout(unsigned long seconds)
  752. {
  753. _configPortalTimeout = seconds * 1000;
  754. }
  755. void AsyncWiFiManager::setConnectTimeout(unsigned long seconds)
  756. {
  757. _connectTimeout = seconds * 1000;
  758. }
  759. void AsyncWiFiManager::setTryConnectDuringConfigPortal(boolean v)
  760. {
  761. _tryConnectDuringConfigPortal = v;
  762. }
  763. void AsyncWiFiManager::setDebugOutput(boolean debug)
  764. {
  765. _debug = debug;
  766. }
  767. void AsyncWiFiManager::setAPStaticIPConfig(IPAddress ip,
  768. IPAddress gw,
  769. IPAddress sn)
  770. {
  771. _ap_static_ip = ip;
  772. _ap_static_gw = gw;
  773. _ap_static_sn = sn;
  774. }
  775. void AsyncWiFiManager::setSTAStaticIPConfig(IPAddress ip,
  776. IPAddress gw,
  777. IPAddress sn,
  778. IPAddress dns1,
  779. IPAddress dns2)
  780. {
  781. _sta_static_ip = ip;
  782. _sta_static_gw = gw;
  783. _sta_static_sn = sn;
  784. _sta_static_dns1 = dns1;
  785. _sta_static_dns2 = dns2;
  786. }
  787. void AsyncWiFiManager::setMinimumSignalQuality(unsigned int quality)
  788. {
  789. _minimumQuality = quality;
  790. }
  791. void AsyncWiFiManager::setBreakAfterConfig(boolean shouldBreak)
  792. {
  793. _shouldBreakAfterConfig = shouldBreak;
  794. }
  795. // handle root or redirect to captive portal
  796. void AsyncWiFiManager::handleRoot(AsyncWebServerRequest *request)
  797. {
  798. // AJS - maybe we should set a scan when we get to the root???
  799. // and only scan on demand? timer + on demand? plus a link to make it happen?
  800. shouldscan = true;
  801. scannow = 0;
  802. DEBUG_WM(F("Handle root"));
  803. if (captivePortal(request))
  804. {
  805. // if captive portal redirect instead of displaying the page
  806. return;
  807. }
  808. DEBUG_WM(F("Sending Captive Portal"));
  809. String page = FPSTR(WFM_HTTP_HEAD);
  810. page.replace("{v}", "Options");
  811. page += FPSTR(HTTP_SCRIPT);
  812. page += FPSTR(HTTP_STYLE);
  813. page += _customHeadElement;
  814. page += FPSTR(HTTP_HEAD_END);
  815. page += "<h1>";
  816. page += _apName;
  817. page += "</h1>";
  818. page += F("<h3>AsyncWiFiManager</h3>");
  819. page += FPSTR(HTTP_PORTAL_OPTIONS);
  820. page += _customOptionsElement;
  821. page += FPSTR(HTTP_END);
  822. request->send(200, "text/html", page);
  823. DEBUG_WM(F("Sent..."));
  824. }
  825. // wifi config page handler
  826. void AsyncWiFiManager::handleWifi(AsyncWebServerRequest *request, boolean scan)
  827. {
  828. shouldscan = true;
  829. scannow = 0;
  830. DEBUG_WM(F("Handle wifi"));
  831. String page = FPSTR(WFM_HTTP_HEAD);
  832. page.replace("{v}", "Config ESP");
  833. page += FPSTR(HTTP_SCRIPT);
  834. page += FPSTR(HTTP_STYLE);
  835. page += _customHeadElement;
  836. page += FPSTR(HTTP_HEAD_END);
  837. if (scan)
  838. {
  839. wifiSSIDscan = false;
  840. DEBUG_WM(F("Scan done"));
  841. if (wifiSSIDCount == 0)
  842. {
  843. DEBUG_WM(F("No networks found"));
  844. page += F("No networks found. Refresh to scan again.");
  845. }
  846. else
  847. {
  848. // display networks in page
  849. String pager = networkListAsString();
  850. page += pager;
  851. page += "<br/>";
  852. }
  853. }
  854. wifiSSIDscan = true;
  855. page += FPSTR(HTTP_FORM_START);
  856. char parLength[2];
  857. // add the extra parameters to the form
  858. for (unsigned int i = 0; i < _paramsCount; i++)
  859. {
  860. if (_params[i] == NULL)
  861. {
  862. break;
  863. }
  864. String pitem = FPSTR(HTTP_FORM_PARAM);
  865. if (_params[i]->getID() != NULL)
  866. {
  867. pitem.replace("{i}", _params[i]->getID());
  868. pitem.replace("{n}", _params[i]->getID());
  869. pitem.replace("{p}", _params[i]->getPlaceholder());
  870. snprintf(parLength, 2, "%d", _params[i]->getValueLength());
  871. pitem.replace("{l}", parLength);
  872. pitem.replace("{v}", _params[i]->getValue());
  873. pitem.replace("{c}", _params[i]->getCustomHTML());
  874. }
  875. else
  876. {
  877. pitem = _params[i]->getCustomHTML();
  878. }
  879. page += pitem;
  880. }
  881. if (_params[0] != NULL)
  882. {
  883. page += "<br/>";
  884. }
  885. if (_sta_static_ip)
  886. {
  887. String item = FPSTR(HTTP_FORM_PARAM);
  888. item.replace("{i}", "ip");
  889. item.replace("{n}", "ip");
  890. item.replace("{p}", "Static IP");
  891. item.replace("{l}", "15");
  892. item.replace("{v}", _sta_static_ip.toString());
  893. page += item;
  894. item = FPSTR(HTTP_FORM_PARAM);
  895. item.replace("{i}", "gw");
  896. item.replace("{n}", "gw");
  897. item.replace("{p}", "Static Gateway");
  898. item.replace("{l}", "15");
  899. item.replace("{v}", _sta_static_gw.toString());
  900. page += item;
  901. item = FPSTR(HTTP_FORM_PARAM);
  902. item.replace("{i}", "sn");
  903. item.replace("{n}", "sn");
  904. item.replace("{p}", "Subnet");
  905. item.replace("{l}", "15");
  906. item.replace("{v}", _sta_static_sn.toString());
  907. page += item;
  908. item = FPSTR(HTTP_FORM_PARAM);
  909. item.replace("{i}", "dns1");
  910. item.replace("{n}", "dns1");
  911. item.replace("{p}", "DNS1");
  912. item.replace("{l}", "15");
  913. item.replace("{v}", _sta_static_dns1.toString());
  914. page += item;
  915. item = FPSTR(HTTP_FORM_PARAM);
  916. item.replace("{i}", "dns2");
  917. item.replace("{n}", "dns2");
  918. item.replace("{p}", "DNS2");
  919. item.replace("{l}", "15");
  920. item.replace("{v}", _sta_static_dns2.toString());
  921. page += item;
  922. page += "<br/>";
  923. }
  924. page += FPSTR(HTTP_FORM_END);
  925. page += FPSTR(HTTP_SCAN_LINK);
  926. page += FPSTR(HTTP_END);
  927. request->send(200, "text/html", page);
  928. DEBUG_WM(F("Sent config page"));
  929. }
  930. // handle the WLAN save form and redirect to WLAN config page again
  931. void AsyncWiFiManager::handleWifiSave(AsyncWebServerRequest *request)
  932. {
  933. DEBUG_WM(F("WiFi save"));
  934. // SAVE/connect here
  935. needInfo = true;
  936. _ssid = request->arg("s").c_str();
  937. _pass = request->arg("p").c_str();
  938. // parameters
  939. for (unsigned int i = 0; i < _paramsCount; i++)
  940. {
  941. if (_params[i] == NULL)
  942. {
  943. break;
  944. }
  945. // read parameter
  946. String value = request->arg(_params[i]->getID()).c_str();
  947. // store it in array
  948. value.toCharArray(_params[i]->_value, _params[i]->_length);
  949. DEBUG_WM(F("Parameter"));
  950. DEBUG_WM(_params[i]->getID());
  951. DEBUG_WM(value);
  952. }
  953. if (request->hasArg("ip"))
  954. {
  955. DEBUG_WM(F("static ip"));
  956. DEBUG_WM(request->arg("ip"));
  957. //_sta_static_ip.fromString(request->arg("ip"));
  958. String ip = request->arg("ip");
  959. optionalIPFromString(&_sta_static_ip, ip.c_str());
  960. }
  961. if (request->hasArg("gw"))
  962. {
  963. DEBUG_WM(F("static gateway"));
  964. DEBUG_WM(request->arg("gw"));
  965. String gw = request->arg("gw");
  966. optionalIPFromString(&_sta_static_gw, gw.c_str());
  967. }
  968. if (request->hasArg("sn"))
  969. {
  970. DEBUG_WM(F("static netmask"));
  971. DEBUG_WM(request->arg("sn"));
  972. String sn = request->arg("sn");
  973. optionalIPFromString(&_sta_static_sn, sn.c_str());
  974. }
  975. if (request->hasArg("dns1"))
  976. {
  977. DEBUG_WM(F("static DNS 1"));
  978. DEBUG_WM(request->arg("dns1"));
  979. String dns1 = request->arg("dns1");
  980. optionalIPFromString(&_sta_static_dns1, dns1.c_str());
  981. }
  982. if (request->hasArg("dns2"))
  983. {
  984. DEBUG_WM(F("static DNS 2"));
  985. DEBUG_WM(request->arg("dns2"));
  986. String dns2 = request->arg("dns2");
  987. optionalIPFromString(&_sta_static_dns2, dns2.c_str());
  988. }
  989. String page = FPSTR(WFM_HTTP_HEAD);
  990. page.replace("{v}", "Credentials Saved");
  991. page += FPSTR(HTTP_SCRIPT);
  992. page += FPSTR(HTTP_STYLE);
  993. page += _customHeadElement;
  994. page += F("<meta http-equiv=\"refresh\" content=\"5; url=/i\">");
  995. page += FPSTR(HTTP_HEAD_END);
  996. page += FPSTR(HTTP_SAVED);
  997. page += FPSTR(HTTP_END);
  998. request->send(200, "text/html", page);
  999. DEBUG_WM(F("Sent wifi save page"));
  1000. connect = true; // signal ready to connect/reset
  1001. }
  1002. // handle the info page
  1003. String AsyncWiFiManager::infoAsString()
  1004. {
  1005. String page;
  1006. page += F("<dt>Chip ID</dt><dd>");
  1007. #if defined(ESP8266)
  1008. page += ESP.getChipId();
  1009. #else
  1010. page += getESP32ChipID();
  1011. #endif
  1012. page += F("</dd>");
  1013. page += F("<dt>Flash Chip ID</dt><dd>");
  1014. #if defined(ESP8266)
  1015. page += ESP.getFlashChipId();
  1016. #else
  1017. page += F("N/A for ESP32");
  1018. #endif
  1019. page += F("</dd>");
  1020. page += F("<dt>IDE Flash Size</dt><dd>");
  1021. page += ESP.getFlashChipSize();
  1022. page += F(" bytes</dd>");
  1023. page += F("<dt>Real Flash Size</dt><dd>");
  1024. #if defined(ESP8266)
  1025. page += ESP.getFlashChipRealSize();
  1026. #else
  1027. page += F("N/A for ESP32");
  1028. #endif
  1029. page += F(" bytes</dd>");
  1030. page += F("<dt>Soft AP IP</dt><dd>");
  1031. page += WiFi.softAPIP().toString();
  1032. page += F("</dd>");
  1033. page += F("<dt>Soft AP MAC</dt><dd>");
  1034. page += WiFi.softAPmacAddress();
  1035. page += F("</dd>");
  1036. page += F("<dt>Station SSID</dt><dd>");
  1037. page += WiFi.SSID();
  1038. page += F("</dd>");
  1039. page += F("<dt>Station IP</dt><dd>");
  1040. page += WiFi.localIP().toString();
  1041. page += F("</dd>");
  1042. page += F("<dt>Station MAC</dt><dd>");
  1043. page += WiFi.macAddress();
  1044. page += F("</dd>");
  1045. page += F("</dl>");
  1046. return page;
  1047. }
  1048. void AsyncWiFiManager::handleInfo(AsyncWebServerRequest *request)
  1049. {
  1050. DEBUG_WM(F("Info"));
  1051. String page = FPSTR(WFM_HTTP_HEAD);
  1052. page.replace("{v}", "Info");
  1053. page += FPSTR(HTTP_SCRIPT);
  1054. page += FPSTR(HTTP_STYLE);
  1055. page += _customHeadElement;
  1056. if (connect == true)
  1057. {
  1058. page += F("<meta http-equiv=\"refresh\" content=\"5; url=/i\">");
  1059. }
  1060. page += FPSTR(HTTP_HEAD_END);
  1061. page += F("<dl>");
  1062. if (connect == true)
  1063. {
  1064. page += F("<dt>Trying to connect</dt><dd>");
  1065. page += wifiStatus;
  1066. page += F("</dd>");
  1067. }
  1068. page += pager;
  1069. page += FPSTR(HTTP_END);
  1070. request->send(200, "text/html", page);
  1071. DEBUG_WM(F("Sent info page"));
  1072. }
  1073. // handle the reset page
  1074. void AsyncWiFiManager::handleReset(AsyncWebServerRequest *request)
  1075. {
  1076. DEBUG_WM(F("Reset"));
  1077. String page = FPSTR(WFM_HTTP_HEAD);
  1078. page.replace("{v}", "Info");
  1079. page += FPSTR(HTTP_SCRIPT);
  1080. page += FPSTR(HTTP_STYLE);
  1081. page += _customHeadElement;
  1082. page += FPSTR(HTTP_HEAD_END);
  1083. page += F("Module will reset in a few seconds.");
  1084. page += FPSTR(HTTP_END);
  1085. request->send(200, "text/html", page);
  1086. DEBUG_WM(F("Sent reset page"));
  1087. delay(5000);
  1088. #if defined(ESP8266)
  1089. ESP.reset();
  1090. #else
  1091. ESP.restart();
  1092. #endif
  1093. delay(2000);
  1094. }
  1095. void AsyncWiFiManager::handleNotFound(AsyncWebServerRequest *request)
  1096. {
  1097. DEBUG_WM(F("Handle not found"));
  1098. if (captivePortal(request))
  1099. {
  1100. // if captive portal redirect instead of displaying the error page
  1101. return;
  1102. }
  1103. String message = "File Not Found\n\n";
  1104. message += "URI: ";
  1105. message += request->url();
  1106. message += "\nMethod: ";
  1107. message += (request->method() == HTTP_GET) ? "GET" : "POST";
  1108. message += "\nArguments: ";
  1109. message += request->args();
  1110. message += "\n";
  1111. for (unsigned int i = 0; i < request->args(); i++)
  1112. {
  1113. message += " " + request->argName(i) + ": " + request->arg(i) + "\n";
  1114. }
  1115. AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", message);
  1116. response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  1117. response->addHeader("Pragma", "no-cache");
  1118. response->addHeader("Expires", "-1");
  1119. request->send(response);
  1120. }
  1121. /** Redirect to captive portal if we got a request for another domain.
  1122. * Return true in that case so the page handler do not try to handle the request again. */
  1123. boolean AsyncWiFiManager::captivePortal(AsyncWebServerRequest *request)
  1124. {
  1125. if (!isIp(request->host()))
  1126. {
  1127. DEBUG_WM(F("Request redirected to captive portal"));
  1128. AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "");
  1129. response->addHeader("Location", String("http://") + toStringIp(request->client()->localIP()));
  1130. request->send(response);
  1131. return true;
  1132. }
  1133. return false;
  1134. }
  1135. // start up config portal callback
  1136. void AsyncWiFiManager::setAPCallback(void (*func)(AsyncWiFiManager *myAsyncWiFiManager))
  1137. {
  1138. _apcallback = func;
  1139. }
  1140. // start up save config callback
  1141. void AsyncWiFiManager::setSaveConfigCallback(void (*func)(void))
  1142. {
  1143. _savecallback = func;
  1144. }
  1145. // sets a custom element to add to head, like a new style tag
  1146. void AsyncWiFiManager::setCustomHeadElement(const char *element)
  1147. {
  1148. _customHeadElement = element;
  1149. }
  1150. // sets a custom element to add to options page
  1151. void AsyncWiFiManager::setCustomOptionsElement(const char *element)
  1152. {
  1153. _customOptionsElement = element;
  1154. }
  1155. // if this is true, remove duplicated Access Points - defaut true
  1156. void AsyncWiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates)
  1157. {
  1158. _removeDuplicateAPs = removeDuplicates;
  1159. }
  1160. template <typename Generic>
  1161. void AsyncWiFiManager::DEBUG_WM(Generic text)
  1162. {
  1163. if (_debug)
  1164. {
  1165. Serial.print(F("*WM: "));
  1166. Serial.println(text);
  1167. }
  1168. }
  1169. unsigned int AsyncWiFiManager::getRSSIasQuality(int RSSI)
  1170. {
  1171. unsigned int quality = 0;
  1172. if (RSSI <= -100)
  1173. {
  1174. quality = 0;
  1175. }
  1176. else if (RSSI >= -50)
  1177. {
  1178. quality = 100;
  1179. }
  1180. else
  1181. {
  1182. quality = 2 * (RSSI + 100);
  1183. }
  1184. return quality;
  1185. }
  1186. // is this an IP?
  1187. boolean AsyncWiFiManager::isIp(String str)
  1188. {
  1189. for (unsigned int i = 0; i < str.length(); i++)
  1190. {
  1191. int c = str.charAt(i);
  1192. if (c != '.' && (c < '0' || c > '9'))
  1193. {
  1194. return false;
  1195. }
  1196. }
  1197. return true;
  1198. }
  1199. // IP to String?
  1200. String AsyncWiFiManager::toStringIp(IPAddress ip)
  1201. {
  1202. String res = "";
  1203. for (int i = 0; i < 3; i++)
  1204. {
  1205. res += String((ip >> (8 * i)) & 0xFF) + ".";
  1206. }
  1207. res += String(((ip >> 8 * 3)) & 0xFF);
  1208. return res;
  1209. }