You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AutoConnect/examples/ConfigIP/ConfigIP.ino

329 lines
8.5 KiB

/*
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 (for ESP8266, ex: GPIO16 in case of ESP32)
|
| O
--+
| O
|
--+--
GND
*/
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#define EXTERNAL_SWITCH_PIN 4
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#include <WebServer.h>
#define EXTERNAL_SWITCH_PIN 16
#endif
#include <AutoConnect.h>
#include <EEPROM.h>
static const char AUX_CONFIGIP[] PROGMEM = R"(
{
"title": "Config IP",
"uri": "/configip",
"menu": true,
"element": [
{
"name": "caption",
"type": "ACText",
"value": "<b>Module IP configuration</b>",
"style": "color:steelblue"
},
{
"name": "mac",
"type": "ACText",
"format": "MAC: %s"
},
{
"name": "staip",
"type": "ACInput",
"label": "IP",
"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])$",
"global": true
},
{
"name": "gateway",
"type": "ACInput",
"label": "Gateway",
"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])$",
"global": true
},
{
"name": "netmask",
"type": "ACInput",
"label": "Netmask",
"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])$",
"global": true
},
{
"name": "dns1",
"type": "ACInput",
"label": "DNS",
"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])$",
"global": true
},
{
"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",
"format": "IP: %s",
"global": true
},
{
"name": "gateway",
"type": "ACText",
"format": "Gateway: %s",
"global": true
},
{
"name": "netmask",
"type": "ACText",
"format": "Netmask: %s",
"global": true
},
{
"name": "dns1",
"type": "ACText",
"format": "DNS1: %s",
"global": true
},
{
"name": "result",
"type": "ACText",
"posterior": "par"
}
]
}
)";
AutoConnect portal;
AutoConnectConfig config;
AutoConnectAux auxIPConfig;
AutoConnectAux auxRestart;
// Pin assignment for an external configuration switch
uint8_t ConfigPin = EXTERNAL_SWITCH_PIN;
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 will truncate the area to the size given by the argument.
// The part overflowing from the specified size is filled with 0xff,
// so if the argument value is too small, the credentials may be lost.
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(String ipString, uint32_t* ip) {
IPAddress ipAddress;
if (ipString.length())
ipAddress.fromString(ipString);
*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
unsigned long tm = millis();
while (digitalRead(pin) == activeLevel) {
if (millis() - tm > 1000)
break;
delay(1);
}
}
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();
}