From 135f835cbca9227039c23670f654eff4e0067bf5 Mon Sep 17 00:00:00 2001 From: Hieromon Ikasamo Date: Tue, 3 Sep 2019 17:49:12 +0900 Subject: [PATCH] Add a new example --- .travis.yml | 1 + examples/ConfigIP/ConfigIP.ino | 318 +++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 examples/ConfigIP/ConfigIP.ino diff --git a/.travis.yml b/.travis.yml index e83148d..783c059 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ install: - mkdir -p ~/Arduino/libraries - ln -s $PWD ~/Arduino/libraries/. script: + - buildExampleSketch ConfigIP - buildExampleSketch Credential - buildExampleSketch Elements - buildExampleSketch FileUpload diff --git a/examples/ConfigIP/ConfigIP.ino b/examples/ConfigIP/ConfigIP.ino new file mode 100644 index 0000000..aaba593 --- /dev/null +++ b/examples/ConfigIP/ConfigIP.ino @@ -0,0 +1,318 @@ +/* + ConfigIP.ino, Example for the AutoConnect library. + Copyright (c) 2019, Hieromon Ikasamo + https://github.com/Hieromon/AutoConnect + This software is released under the MIT License. + https://opensource.org/licenses/MIT + + This sketch implements an example of enabling a custom web page + according to the state of an externally equipped switch. The custom + web page configures and provides a static IP to the ESP module. + This example needs to equip an external switch circuit that pulls up + with a resistor (1K to 10K ohms) and then drops it to GND via a push + switch to connect to any GPIO of the ESP module. + + An external switch circuit: + 3.3V + --+-- + | + +-+ + | | 1K ~ 10K + +-+ + | + +--> D2 + | + | O + --+ + | O + | + --+-- + GND +*/ + +#if defined(ARDUINO_ARCH_ESP8266) +#include +#include +#elif defined(ARDUINO_ARCH_ESP32) +#include +#include +#endif +#include +#include + +static const char AUX_CONFIGIP[] PROGMEM = R"( +{ + "title": "Config IP", + "uri": "/configip", + "menu": true, + "element": [ + { + "name": "caption", + "type": "ACText", + "value": "Module IP configuration", + "style": "color:steelblue" + }, + { + "name": "mac", + "type": "ACText", + "format": "MAC: %s" + }, + { + "name": "staip", + "type": "ACInput", + "label": "IP", + "global": true, + "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" + }, + { + "name": "gateway", + "type": "ACInput", + "label": "Gateway", + "global": true, + "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" + }, + { + "name": "netmask", + "type": "ACInput", + "label": "Netmask", + "global": true, + "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" + }, + { + "name": "dns1", + "type": "ACInput", + "label": "DNS", + "global": true, + "pattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" + }, + { + "name": "ok", + "type": "ACSubmit", + "value": "OK", + "uri": "/restart" + }, + { + "name": "cancel", + "type": "ACSubmit", + "value": "Cancel", + "uri": "/_ac" + } + ] +} +)"; + +static const char AUX_RESTART[] PROGMEM = R"( +{ + "title": "Config IP", + "uri": "/restart", + "menu": false, + "element": [ + { + "name": "caption", + "type": "ACText", + "value": "Settings", + "style": "font-family:Arial;font-weight:bold;text-align:center;margin-bottom:10px;color:steelblue" + }, + { + "name": "staip", + "type": "ACText", + "global": true, + "format": "IP: %s" + }, + { + "name": "gateway", + "type": "ACText", + "global": true, + "format": "Gateway: %s" + }, + { + "name": "netmask", + "type": "ACText", + "global": true, + "format": "Netmask: %s" + }, + { + "name": "dns1", + "type": "ACText", + "global": true, + "format": "DNS1: %s" + }, + { + "name": "result", + "type": "ACText", + "posterior": "par" + } + ] +} +)"; + +AutoConnect portal; +AutoConnectConfig config; +AutoConnectAux auxIPConfig; +AutoConnectAux auxRestart; + +// Pin assignment for an external configuration switch +uint8_t ConfigPin = D2; +uint8_t ActiveLevel = LOW; + +// EEPROM saving structure +typedef union { + struct { + uint32_t ip; + uint32_t gateway; + uint32_t netmask; + uint32_t dns1; + } ipconfig; + uint8_t ipraw[sizeof(uint32_t) * 4]; +} IPCONFIG; + +// Load IP configuration from EEPROM +void loadConfig(IPCONFIG* ipconfig) { + EEPROM.begin(sizeof(IPCONFIG)); + int dp = 0; + for (uint8_t i = 0; i < 4; i++) { + for (uint8_t c = 0; c < sizeof(uint32_t); c++) + ipconfig->ipraw[c + i * sizeof(uint32_t)] = EEPROM.read(dp++); + } + EEPROM.end(); + + // Unset value screening + if (ipconfig->ipconfig.ip == 0xffffffffL) + ipconfig->ipconfig.ip = 0U; + if (ipconfig->ipconfig.gateway == 0xffffffffL) + ipconfig->ipconfig.gateway = 0U; + if (ipconfig->ipconfig.netmask == 0xffffffffL) + ipconfig->ipconfig.netmask = 0U; + if (ipconfig->ipconfig.dns1 == 0xffffffffL) + ipconfig->ipconfig.dns1 = 0U; + + Serial.println("IP configuration loaded"); + Serial.printf("Sta IP :0x%08lx\n", ipconfig->ipconfig.ip); + Serial.printf("Gateway:0x%08lx\n", ipconfig->ipconfig.gateway); + Serial.printf("Netmask:0x%08lx\n", ipconfig->ipconfig.netmask); + Serial.printf("DNS1 :0x%08lx\n", ipconfig->ipconfig.dns1); +} + +// Save current IP configuration to EEPROM +void saveConfig(const IPCONFIG* ipconfig) { + EEPROM.begin(128); + int dp = 0; + for (uint8_t i = 0; i < 4; i++) { + for (uint8_t d = 0; d < sizeof(uint32_t); d++) + EEPROM.write(dp++, ipconfig->ipraw[d + i * sizeof(uint32_t)]); + } + EEPROM.end(); + delay(100); +} + +// Custom web page handler to set current configuration to the page +String getConfig(AutoConnectAux& aux, PageArgument& args) { + IPCONFIG ipconfig; + loadConfig(&ipconfig); + + // Fetch MAC address + String macAddress; + uint8_t mac[6]; + WiFi.macAddress(mac); + for (uint8_t i = 0; i < 6; i++) { + char buf[3]; + sprintf(buf, "%02x", mac[i]); + macAddress += buf; + if (i < 5) + macAddress += ':'; + } + aux["mac"].value = macAddress; + + // Fetch each IP address configuration from EEPROM + IPAddress staip = IPAddress(ipconfig.ipconfig.ip); + IPAddress gateway = IPAddress(ipconfig.ipconfig.gateway); + IPAddress netmask = IPAddress(ipconfig.ipconfig.netmask); + IPAddress dns1 = IPAddress(ipconfig.ipconfig.dns1); + + // Echo back the IP settings + aux["staip"].value = staip.toString(); + aux["gateway"].value = gateway.toString(); + aux["netmask"].value = netmask.toString(); + aux["dns1"].value = dns1.toString(); + + return String(); +} + +// Convert IP address from AutoConnectInput string value +void getIPAddress(AutoConnectInput& input, uint32_t* ip) { + IPAddress ipAddress; + + if (input.length()) + ipAddress.fromString(input); + *ip = (uint32_t)ipAddress; +} + +// Custom web page handler to save the configuration to AutoConnectConfig +String setConfig(AutoConnectAux& aux, PageArgument& args) { + IPCONFIG ipconfig; + + // Retrieve each IP address from AutoConnectInput field + getIPAddress(aux["staip"].value, &ipconfig.ipconfig.ip); + getIPAddress(aux["gateway"].value, &ipconfig.ipconfig.gateway); + getIPAddress(aux["netmask"].value, &ipconfig.ipconfig.netmask); + getIPAddress(aux["dns1"].value, &ipconfig.ipconfig.dns1); + + // Make a result message + if (auxIPConfig.isValid()) { + saveConfig(&ipconfig); + aux["result"].value = "Reset by AutoConnect menu will restart with the above."; + } + else + aux["result"].value = "Invalid IP address specified."; + return String(); +} + +// Sense the external switch to enter the configuraton mode +bool senseSW(const uint8_t pin, const uint8_t activeLevel) { + bool sw = digitalRead(pin) == activeLevel; + if (sw) { + // Cut-off the chattering noise + while (digitalRead(pin) == activeLevel) + delay(10); + } + return sw; +} + +void setup() { + delay(1000); + Serial.begin(115200); + Serial.println(); + + // Shift the credentials storage to reserve saving IPCONFIG + config.boundaryOffset = sizeof(IPCONFIG); + + // Load current IP configuration + IPCONFIG ipconfig; + loadConfig(&ipconfig); + + // Configure pre-loaded static IPs + config.staip = IPAddress(ipconfig.ipconfig.ip); + config.staGateway = IPAddress(ipconfig.ipconfig.gateway); + config.staNetmask = IPAddress(ipconfig.ipconfig.netmask); + config.dns1 = IPAddress(ipconfig.ipconfig.dns1); + portal.config(config); + + // Sense the configuration button (external switch) + pinMode(ConfigPin, INPUT); + if (senseSW(ConfigPin, ActiveLevel)) { + Serial.println("IP configuration enable"); + auxIPConfig.load(AUX_CONFIGIP); + auxIPConfig.on(getConfig); + auxRestart.load(AUX_RESTART); + auxRestart.on(setConfig); + portal.join({ auxIPConfig, auxRestart }); + } + + portal.begin(); +} + +void loop() { + // User sketch process is here. + + portal.handleClient(); +}