FSPage.ino 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. FSPage.ino - Example PageBuilder with HTML page stored SPIFFS
  3. Copyright (c) 2017 Hieromon Ikasamo. All rights reserved.
  4. This software is released under the MIT License.
  5. http://opensource.org/licenses/mit-license.php
  6. This is a sample sketch that connects to an existing WiFi router already
  7. connected Internet and makes ESP8266 join the network.
  8. After writing this sketch, probably you can access 192.168.4.1 from
  9. your smartphone and it will be listed the WiFi AP that you can connect.
  10. You can select and connect the listed SSID.
  11. It is using Web page transition in the dialogue procedure for connecting
  12. to SSID, and its HTML source is stored in SPIFFS or LittleFS file.
  13. Before executing this sketch, you need to upload the data folder to SPIFFS
  14. or LittleFS of ESP8266 by the tool as "ESP8266 Sketch Data Upload" in Tools
  15. menu in Arduino IDE.
  16. */
  17. #if defined(ARDUINO_ARCH_ESP8266)
  18. #include <ESP8266WiFi.h>
  19. #include <ESP8266WebServer.h>
  20. #define WIFI_EVENT_STA_CONNECTED WIFI_EVENT_STAMODE_CONNECTED
  21. #define WIFI_EVENT_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED
  22. #define WIFI_EVENT_AP_STACONNECTED WIFI_EVENT_SOFTAPMODE_STACONNECTED
  23. #define WIFI_EVENT_AP_STADISCONNECTED WIFI_EVENT_SOFTAPMODE_STADISCONNECTED
  24. #define WIFI_EVENT_ALL WIFI_EVENT_ANY
  25. #define WIFI_AUTH_OPEN ENC_TYPE_NONE
  26. #elif defined(ARDUINO_ARCH_ESP32)
  27. #include <WiFi.h>
  28. #include <WebServer.h>
  29. #define WIFI_EVENT_STA_CONNECTED SYSTEM_EVENT_STA_CONNECTED
  30. #define WIFI_EVENT_STA_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED
  31. #define WIFI_EVENT_AP_STACONNECTED SYSTEM_EVENT_AP_STACONNECTED
  32. #define WIFI_EVENT_AP_STADISCONNECTED SYSTEM_EVENT_AP_STADISCONNECTED
  33. #define WIFI_EVENT_ALL SYSTEM_EVENT_MAX
  34. #endif
  35. #include "PageBuilder.h"
  36. #define AP_NAME "esp-ap"
  37. #define AP_PASS "12345678"
  38. #if defined(ARDUINO_ARCH_ESP8266)
  39. #ifdef PB_USE_SPIFFS
  40. #include <FS.h>
  41. FS& FlashFile = SPIFFS;
  42. #else
  43. #include <LittleFS.h>
  44. FS& FlashFile = LittleFS;
  45. #endif
  46. ESP8266WebServer server;
  47. #elif defined(ARDUINO_ARCH_ESP32)
  48. #include <FS.h>
  49. #include <SPIFFS.h>
  50. fs::SPIFFSFS& FlashFile = SPIFFS;
  51. WebServer server;
  52. #endif
  53. bool CONNECT_REQ;
  54. String CONN_SSID;
  55. String CONN_PSK;
  56. String REDIRECT_URI;
  57. String CURRENT_HOST;
  58. // Page URIs
  59. #define URI_ROOT "/"
  60. #define URI_JOIN "/join"
  61. #define URI_REQ "/request"
  62. #define URI_RESULT "/result"
  63. #define URI_WELCOME "/welcome"
  64. #define URI_FAILED "/failed"
  65. // Connection request redirector
  66. String reqConnect(PageArgument&);
  67. PageElement REQ_ELM("{{REQ}}", { {"REQ", reqConnect} });
  68. PageBuilder REQ_PAGE(URI_REQ, {REQ_ELM});
  69. // Connection result redirector
  70. String resConnect(PageArgument&);
  71. PageElement CONNRES_ELM("{{CONN}}", { {"CONN", resConnect} });
  72. PageBuilder CONNRES_PAGE(URI_RESULT, {CONNRES_ELM});
  73. // Convert RSSI dBm to signal strength
  74. unsigned int toWiFiQuality(int32_t rssi) {
  75. unsigned int qu;
  76. if (rssi <= -100)
  77. qu = 0;
  78. else if (rssi >= -50)
  79. qu = 100;
  80. else
  81. qu = 2 * (rssi + 100);
  82. return qu;
  83. }
  84. // This callback function would be invoked from the root page and scans nearby
  85. // WiFi-AP to make a connectable list.
  86. String listSSID(PageArgument& args) {
  87. String s_ssid_list = "";
  88. int8_t nn = WiFi.scanNetworks(false, true);
  89. for (uint8_t i = 0; i < nn; i++) {
  90. String ssid = WiFi.SSID(i);
  91. if (ssid.length() == 0)
  92. ssid = "?";
  93. s_ssid_list += "<div class=\"ssid_list\"><a href=\"" + String(URI_JOIN) + String("?ssid=");
  94. s_ssid_list += ssid == "?" ? "%3F" : ssid;
  95. s_ssid_list += "&psk_type=" + String(WiFi.encryptionType(i)) + "\">" + ssid + "</a>";
  96. s_ssid_list += String(toWiFiQuality(WiFi.RSSI(i))) + "%";
  97. if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN)
  98. s_ssid_list += "<span class=\"img_lock\" />";
  99. s_ssid_list += "</div>";
  100. }
  101. CONNECT_REQ = false;
  102. return s_ssid_list;
  103. }
  104. // Accepting connection request.
  105. // Flag the occurrence of the connection request and response a redirect to
  106. // the connection result page.
  107. String reqConnect(PageArgument& args) {
  108. if (CONN_SSID == "?")
  109. CONN_SSID = args.arg("ssid");
  110. CONN_PSK = args.arg("psk");
  111. // Leave from the AP currently.
  112. if (WiFi.status() == WL_CONNECTED) {
  113. WiFi.disconnect();
  114. while (WiFi.status() == WL_CONNECTED);
  115. }
  116. // Available upon connection request.
  117. CONNECT_REQ = true;
  118. REDIRECT_URI = "";
  119. CURRENT_HOST = "";
  120. // Redirect http request to connection result page.
  121. server.sendHeader("Location", String("http://") + WiFi.softAPIP().toString() + String(URI_RESULT), true);
  122. server.send(302, "text/plain", "");
  123. REQ_PAGE.cancel();
  124. return "";
  125. }
  126. // Performing AP connection procedure.
  127. void connWiFi() {
  128. // Starts connecting the ap as WIFI_STA
  129. Serial.print("Connect to " + CONN_SSID + "/" + CONN_PSK + " ");
  130. WiFi.begin(CONN_SSID.c_str(), CONN_PSK.c_str());
  131. unsigned long tm = millis();
  132. do {
  133. delay(500);
  134. Serial.print('.');
  135. if (millis() - tm > 30000)
  136. break;
  137. } while (WiFi.status() != WL_CONNECTED);
  138. Serial.print(String(WiFi.status()) + ":");
  139. // Dynamically switch the result page URL according to the connection result.
  140. // If it connected to the AP, redirect localIP () as the host address.
  141. // Connection failed, redirect to a failed page addressed on softAPIP().
  142. if (WiFi.status() == WL_CONNECTED) {
  143. // Connection established, makes a response page into a new IP address.
  144. CURRENT_HOST = WiFi.localIP().toString();
  145. REDIRECT_URI = URI_WELCOME;
  146. Serial.println("OK");
  147. }
  148. else {
  149. // Connection refused, a fails page keeps on softAPIP.
  150. CURRENT_HOST = WiFi.softAPIP().toString();
  151. REDIRECT_URI = URI_FAILED;
  152. Serial.println("Failed");
  153. }
  154. }
  155. // Responds redirect destination according to the connection result of success
  156. // or failure to the AP.
  157. String resConnect(PageArgument& args) {
  158. String redirect = String("http://") + CURRENT_HOST + REDIRECT_URI;
  159. Serial.println("Redirect: " + redirect);
  160. server.sendHeader("Location", redirect, true);
  161. server.sendHeader("Connection", "keep-alive");
  162. server.send(302, "text/plain", "");
  163. server.client().stop();
  164. CONNRES_PAGE.cancel();
  165. Serial.println();
  166. WiFi.printDiag(Serial);
  167. return "";
  168. }
  169. // WiFi event determination and echo back.
  170. void broadcastEvent(WiFiEvent_t event) {
  171. static const char eventText_APSTA_CONN[] PROGMEM = "SoftAP station connected.";
  172. static const char eventText_APSTA_DISC[] PROGMEM = "SoftAP station disconnected.";
  173. static const char eventText_STA_CONN[] PROGMEM = "WiFi AP connected.";
  174. static const char eventText_STA_DISC[] PROGMEM = "WiFi AP disconnected.";
  175. const char* eventText;
  176. switch (event) {
  177. case WIFI_EVENT_AP_STACONNECTED:
  178. eventText = eventText_APSTA_CONN;
  179. break;
  180. case WIFI_EVENT_AP_STADISCONNECTED:
  181. eventText = eventText_APSTA_DISC;
  182. break;
  183. case WIFI_EVENT_STA_CONNECTED:
  184. eventText = eventText_STA_CONN;
  185. break;
  186. case WIFI_EVENT_STA_DISCONNECTED:
  187. eventText = eventText_STA_DISC;
  188. break;
  189. default:
  190. eventText = nullptr;
  191. }
  192. if (eventText)
  193. Serial.println(String("[event] ") + String(FPSTR(eventText)));
  194. }
  195. // Get an architecture of compiled
  196. String getArch(PageArgument& args) {
  197. #if defined(ARDUINO_ARCH_ESP8266)
  198. return "ESP8266";
  199. #elif defined(ARDUINO_ARCH_ESP32)
  200. return "ESP32";
  201. #endif
  202. }
  203. // HTML page declarations.
  204. // root page
  205. PageElement SSID_ELM("file:/root.htm", {
  206. { "SSID_LIST", listSSID },
  207. { "URI_ROOT", [](PageArgument& args) { return URI_ROOT; }}
  208. });
  209. PageBuilder SSID_PAGE(URI_ROOT, { SSID_ELM });
  210. // SSID & Password entry page
  211. PageElement ENTRY_ELM("file:/entry.htm", {
  212. { "ESP_ARCH", getArch },
  213. { "ENTRY", [](PageArgument& args) { CONN_SSID = args.arg("ssid"); return "AP"; } },
  214. { "URI_REQ", [](PageArgument& args) { return URI_REQ; } },
  215. { "SSID", [](PageArgument& args) { return CONN_SSID == "?" ? "placeholder=\"SSID\"" : String("value=\"" + CONN_SSID + "\" readonly"); } },
  216. { "PSK", [](PageArgument& args) { return args.arg("psk_type") != "7" ? "<input type=\"text\" name=\"psk\" placeholder=\"Password\" />" : ""; } }
  217. });
  218. PageBuilder ENTRY_PAGE(URI_JOIN, {ENTRY_ELM});
  219. // Connection successful page
  220. PageElement WELCOME_ELM("file:/connect.htm", {
  221. { "ESP-AP", [](PageArgument& args) { return AP_NAME; } },
  222. { "SSID", [](PageArgument& args) { return WiFi.SSID(); } },
  223. { "IP", [](PageArgument& args) { return WiFi.localIP().toString(); } },
  224. { "GATEWAY", [](PageArgument& args) { return WiFi.gatewayIP().toString(); } },
  225. { "SUBNET", [](PageArgument& args) { return WiFi.subnetMask().toString(); } }
  226. });
  227. PageBuilder WELCOME_PAGE(URI_WELCOME, {WELCOME_ELM});
  228. // Connection failed page
  229. PageElement FAILED_ELM("file:/failed.htm", {
  230. { "SSID", [](PageArgument& args) { return CONN_SSID; } },
  231. { "RESULT", [](PageArgument& args) { return String(WiFi.status()); } }
  232. });
  233. PageBuilder FAILED_PAGE(URI_FAILED, {FAILED_ELM});
  234. void setup() {
  235. delay(1000);
  236. Serial.begin(115200);
  237. Serial.println();
  238. // Prepare HTML page
  239. SSID_PAGE.insert(server);
  240. ENTRY_PAGE.insert(server);
  241. REQ_PAGE.insert(server);
  242. CONNRES_PAGE.insert(server);
  243. WELCOME_PAGE.insert(server);
  244. FAILED_PAGE.insert(server);
  245. // Start WiFi soft AP.
  246. WiFi.softAPdisconnect();
  247. WiFi.disconnect();
  248. delay(100);
  249. WiFi.mode(WIFI_AP_STA);
  250. WiFi.softAP(AP_NAME, AP_PASS);
  251. while (WiFi.softAPIP() == IPAddress(0, 0, 0, 0)) {
  252. yield();
  253. delay(100);
  254. }
  255. Serial.print(AP_NAME " started. IP:");
  256. Serial.println(WiFi.softAPIP());
  257. // Start http server.
  258. FlashFile.begin();
  259. server.begin();
  260. // Turn on WiFi event handling.
  261. // It is deprecated in ESP8266, but it remains because the alternative
  262. // API's onSoftAPModeStationConnected event does not fire properly in
  263. // SoftAP + STA mode.
  264. WiFi.onEvent(broadcastEvent, WIFI_EVENT_ALL);
  265. }
  266. void loop() {
  267. server.handleClient();
  268. if (CONNECT_REQ) {
  269. connWiFi();
  270. CONNECT_REQ = false;
  271. }
  272. }