ConfigIP.ino 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /*
  2. ConfigIP.ino, Example for the AutoConnect library.
  3. Copyright (c) 2019, Hieromon Ikasamo
  4. https://github.com/Hieromon/AutoConnect
  5. This software is released under the MIT License.
  6. https://opensource.org/licenses/MIT
  7. This sketch implements an example of enabling a custom web page
  8. according to the state of an externally equipped switch. The custom
  9. web page configures and provides a static IP to the ESP module.
  10. This example needs to equip an external switch circuit that pulls up
  11. with a resistor (1K to 10K ohms) and then drops it to GND via a push
  12. switch to connect to any GPIO of the ESP module.
  13. An external switch circuit:
  14. 3.3V
  15. --+--
  16. |
  17. +-+
  18. | | 1K ~ 10K
  19. +-+
  20. |
  21. +--> D2 (for ESP8266, ex: GPIO16 in case of ESP32)
  22. |
  23. | O
  24. --+
  25. | O
  26. |
  27. --+--
  28. GND
  29. */
  30. #if defined(ARDUINO_ARCH_ESP8266)
  31. #include <ESP8266WiFi.h>
  32. #include <ESP8266WebServer.h>
  33. #define EXTERNAL_SWITCH_PIN 4
  34. #elif defined(ARDUINO_ARCH_ESP32)
  35. #include <WiFi.h>
  36. #include <WebServer.h>
  37. #define EXTERNAL_SWITCH_PIN 16
  38. #endif
  39. #include <AutoConnect.h>
  40. #include <EEPROM.h>
  41. static const char AUX_CONFIGIP[] PROGMEM = R"(
  42. {
  43. "title": "Config IP",
  44. "uri": "/configip",
  45. "menu": true,
  46. "element": [
  47. {
  48. "name": "caption",
  49. "type": "ACText",
  50. "value": "<b>Module IP configuration</b>",
  51. "style": "color:steelblue"
  52. },
  53. {
  54. "name": "mac",
  55. "type": "ACText",
  56. "format": "MAC: %s"
  57. },
  58. {
  59. "name": "staip",
  60. "type": "ACInput",
  61. "label": "IP",
  62. "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
  63. "global": true
  64. },
  65. {
  66. "name": "gateway",
  67. "type": "ACInput",
  68. "label": "Gateway",
  69. "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
  70. "global": true
  71. },
  72. {
  73. "name": "netmask",
  74. "type": "ACInput",
  75. "label": "Netmask",
  76. "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
  77. "global": true
  78. },
  79. {
  80. "name": "dns1",
  81. "type": "ACInput",
  82. "label": "DNS",
  83. "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
  84. "global": true
  85. },
  86. {
  87. "name": "ok",
  88. "type": "ACSubmit",
  89. "value": "OK",
  90. "uri": "/restart"
  91. },
  92. {
  93. "name": "cancel",
  94. "type": "ACSubmit",
  95. "value": "Cancel",
  96. "uri": "/_ac"
  97. }
  98. ]
  99. }
  100. )";
  101. static const char AUX_RESTART[] PROGMEM = R"(
  102. {
  103. "title": "Config IP",
  104. "uri": "/restart",
  105. "menu": false,
  106. "element": [
  107. {
  108. "name": "caption",
  109. "type": "ACText",
  110. "value": "Settings",
  111. "style": "font-family:Arial;font-weight:bold;text-align:center;margin-bottom:10px;color:steelblue"
  112. },
  113. {
  114. "name": "staip",
  115. "type": "ACText",
  116. "format": "IP: %s",
  117. "global": true
  118. },
  119. {
  120. "name": "gateway",
  121. "type": "ACText",
  122. "format": "Gateway: %s",
  123. "global": true
  124. },
  125. {
  126. "name": "netmask",
  127. "type": "ACText",
  128. "format": "Netmask: %s",
  129. "global": true
  130. },
  131. {
  132. "name": "dns1",
  133. "type": "ACText",
  134. "format": "DNS1: %s",
  135. "global": true
  136. },
  137. {
  138. "name": "result",
  139. "type": "ACText",
  140. "posterior": "par"
  141. }
  142. ]
  143. }
  144. )";
  145. AutoConnect portal;
  146. AutoConnectConfig config;
  147. AutoConnectAux auxIPConfig;
  148. AutoConnectAux auxRestart;
  149. // Pin assignment for an external configuration switch
  150. uint8_t ConfigPin = EXTERNAL_SWITCH_PIN;
  151. uint8_t ActiveLevel = LOW;
  152. // EEPROM saving structure
  153. typedef union {
  154. struct {
  155. uint32_t ip;
  156. uint32_t gateway;
  157. uint32_t netmask;
  158. uint32_t dns1;
  159. } ipconfig;
  160. uint8_t ipraw[sizeof(uint32_t) * 4];
  161. } IPCONFIG;
  162. // Load IP configuration from EEPROM
  163. void loadConfig(IPCONFIG* ipconfig) {
  164. EEPROM.begin(sizeof(IPCONFIG));
  165. int dp = 0;
  166. for (uint8_t i = 0; i < 4; i++) {
  167. for (uint8_t c = 0; c < sizeof(uint32_t); c++)
  168. ipconfig->ipraw[c + i * sizeof(uint32_t)] = EEPROM.read(dp++);
  169. }
  170. EEPROM.end();
  171. // Unset value screening
  172. if (ipconfig->ipconfig.ip == 0xffffffffL)
  173. ipconfig->ipconfig.ip = 0U;
  174. if (ipconfig->ipconfig.gateway == 0xffffffffL)
  175. ipconfig->ipconfig.gateway = 0U;
  176. if (ipconfig->ipconfig.netmask == 0xffffffffL)
  177. ipconfig->ipconfig.netmask = 0U;
  178. if (ipconfig->ipconfig.dns1 == 0xffffffffL)
  179. ipconfig->ipconfig.dns1 = 0U;
  180. Serial.println("IP configuration loaded");
  181. Serial.printf("Sta IP :0x%08lx\n", ipconfig->ipconfig.ip);
  182. Serial.printf("Gateway:0x%08lx\n", ipconfig->ipconfig.gateway);
  183. Serial.printf("Netmask:0x%08lx\n", ipconfig->ipconfig.netmask);
  184. Serial.printf("DNS1 :0x%08lx\n", ipconfig->ipconfig.dns1);
  185. }
  186. // Save current IP configuration to EEPROM
  187. void saveConfig(const IPCONFIG* ipconfig) {
  188. // EEPROM.begin will truncate the area to the size given by the argument.
  189. // The part overflowing from the specified size is filled with 0xff,
  190. // so if the argument value is too small, the credentials may be lost.
  191. EEPROM.begin(128);
  192. int dp = 0;
  193. for (uint8_t i = 0; i < 4; i++) {
  194. for (uint8_t d = 0; d < sizeof(uint32_t); d++)
  195. EEPROM.write(dp++, ipconfig->ipraw[d + i * sizeof(uint32_t)]);
  196. }
  197. EEPROM.end();
  198. delay(100);
  199. }
  200. // Custom web page handler to set current configuration to the page
  201. String getConfig(AutoConnectAux& aux, PageArgument& args) {
  202. IPCONFIG ipconfig;
  203. loadConfig(&ipconfig);
  204. // Fetch MAC address
  205. String macAddress;
  206. uint8_t mac[6];
  207. WiFi.macAddress(mac);
  208. for (uint8_t i = 0; i < 6; i++) {
  209. char buf[3];
  210. sprintf(buf, "%02X", mac[i]);
  211. macAddress += buf;
  212. if (i < 5)
  213. macAddress += ':';
  214. }
  215. aux["mac"].value = macAddress;
  216. // Fetch each IP address configuration from EEPROM
  217. IPAddress staip = IPAddress(ipconfig.ipconfig.ip);
  218. IPAddress gateway = IPAddress(ipconfig.ipconfig.gateway);
  219. IPAddress netmask = IPAddress(ipconfig.ipconfig.netmask);
  220. IPAddress dns1 = IPAddress(ipconfig.ipconfig.dns1);
  221. // Echo back the IP settings
  222. aux["staip"].value = staip.toString();
  223. aux["gateway"].value = gateway.toString();
  224. aux["netmask"].value = netmask.toString();
  225. aux["dns1"].value = dns1.toString();
  226. return String();
  227. }
  228. // Convert IP address from AutoConnectInput string value
  229. void getIPAddress(String ipString, uint32_t* ip) {
  230. IPAddress ipAddress;
  231. if (ipString.length())
  232. ipAddress.fromString(ipString);
  233. *ip = (uint32_t)ipAddress;
  234. }
  235. // Custom web page handler to save the configuration to AutoConnectConfig
  236. String setConfig(AutoConnectAux& aux, PageArgument& args) {
  237. IPCONFIG ipconfig;
  238. // Retrieve each IP address from AutoConnectInput field
  239. getIPAddress(aux["staip"].value, &ipconfig.ipconfig.ip);
  240. getIPAddress(aux["gateway"].value, &ipconfig.ipconfig.gateway);
  241. getIPAddress(aux["netmask"].value, &ipconfig.ipconfig.netmask);
  242. getIPAddress(aux["dns1"].value, &ipconfig.ipconfig.dns1);
  243. // Make a result message
  244. if (auxIPConfig.isValid()) {
  245. saveConfig(&ipconfig);
  246. aux["result"].value = "Reset by AutoConnect menu will restart with the above.";
  247. }
  248. else
  249. aux["result"].value = "Invalid IP address specified.";
  250. return String();
  251. }
  252. // Sense the external switch to enter the configuraton mode
  253. bool senseSW(const uint8_t pin, const uint8_t activeLevel) {
  254. bool sw = digitalRead(pin) == activeLevel;
  255. if (sw) {
  256. // Cut-off the chattering noise
  257. unsigned long tm = millis();
  258. while (digitalRead(pin) == activeLevel) {
  259. if (millis() - tm > 1000)
  260. break;
  261. delay(1);
  262. }
  263. }
  264. return sw;
  265. }
  266. void setup() {
  267. delay(1000);
  268. Serial.begin(115200);
  269. Serial.println();
  270. // Shift the credentials storage to reserve saving IPCONFIG
  271. config.boundaryOffset = sizeof(IPCONFIG);
  272. // Load current IP configuration
  273. IPCONFIG ipconfig;
  274. loadConfig(&ipconfig);
  275. // Configure pre-loaded static IPs
  276. config.staip = IPAddress(ipconfig.ipconfig.ip);
  277. config.staGateway = IPAddress(ipconfig.ipconfig.gateway);
  278. config.staNetmask = IPAddress(ipconfig.ipconfig.netmask);
  279. config.dns1 = IPAddress(ipconfig.ipconfig.dns1);
  280. portal.config(config);
  281. // Sense the configuration button (external switch)
  282. pinMode(ConfigPin, INPUT);
  283. if (senseSW(ConfigPin, ActiveLevel)) {
  284. Serial.println("IP configuration enable");
  285. auxIPConfig.load(AUX_CONFIGIP);
  286. auxIPConfig.on(getConfig);
  287. auxRestart.load(AUX_RESTART);
  288. auxRestart.on(setConfig);
  289. portal.join({ auxIPConfig, auxRestart });
  290. }
  291. portal.begin();
  292. }
  293. void loop() {
  294. // User sketch process is here.
  295. portal.handleClient();
  296. }