1291 lines
35 KiB
C++
1291 lines
35 KiB
C++
/**
|
|
* IotWebConf.cpp -- IotWebConf is an ESP8266/ESP32
|
|
* non blocking WiFi/AP web configuration library for Arduino.
|
|
* https://github.com/prampec/IotWebConf
|
|
*
|
|
* Copyright (C) 2018 Balazs Kelemen <prampec+arduino@gmail.com>
|
|
*
|
|
* This software may be modified and distributed under the terms
|
|
* of the MIT license. See the LICENSE file for details.
|
|
*/
|
|
|
|
#include <EEPROM.h>
|
|
|
|
#include "IotWebConf.h"
|
|
|
|
#ifdef IOTWEBCONF_CONFIG_USE_MDNS
|
|
# ifdef ESP8266
|
|
# include <ESP8266mDNS.h>
|
|
# elif defined(ESP32)
|
|
# include <ESPmDNS.h>
|
|
# endif
|
|
#endif
|
|
|
|
#define IOTWEBCONF_STATUS_ENABLED (this->_statusPin >= 0)
|
|
|
|
IotWebConfParameter::IotWebConfParameter()
|
|
{
|
|
}
|
|
IotWebConfParameter::IotWebConfParameter(
|
|
const char* label, const char* id, char* valueBuffer, int length,
|
|
const char* type, const char* placeholder, const char* defaultValue,
|
|
const char* customHtml, boolean visible)
|
|
{
|
|
this->label = label;
|
|
this->_id = id;
|
|
this->valueBuffer = valueBuffer;
|
|
this->_length = length;
|
|
this->type = type;
|
|
this->placeholder = placeholder;
|
|
this->defaultValue = defaultValue;
|
|
this->customHtml = customHtml;
|
|
this->visible = visible;
|
|
}
|
|
IotWebConfParameter::IotWebConfParameter(
|
|
const char* id, char* valueBuffer, int length, const char* customHtml,
|
|
const char* type)
|
|
{
|
|
this->label = NULL;
|
|
this->_id = id;
|
|
this->valueBuffer = valueBuffer;
|
|
this->_length = length;
|
|
this->type = type;
|
|
this->customHtml = customHtml;
|
|
this->visible = true;
|
|
this->errorMessage = NULL;
|
|
}
|
|
|
|
IotWebConfSeparator::IotWebConfSeparator()
|
|
: IotWebConfParameter(NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, true)
|
|
{
|
|
}
|
|
|
|
IotWebConfSeparator::IotWebConfSeparator(const char* label)
|
|
: IotWebConfParameter(label, NULL, NULL, 0, NULL, NULL, NULL, NULL, true)
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
IotWebConf::IotWebConf(
|
|
const char* defaultThingName, DNSServer* dnsServer, WebServer* server,
|
|
const char* initialApPassword, const char* configVersion)
|
|
#else
|
|
IotWebConf::IotWebConf(
|
|
const char* defaultThingName, DNSServer* dnsServer, AsyncWebServer* server,
|
|
const char* initialApPassword, const char* configVersion)
|
|
#endif
|
|
{
|
|
strncpy(this->_thingName, defaultThingName, IOTWEBCONF_WORD_LEN);
|
|
this->_dnsServer = dnsServer;
|
|
this->_server = server;
|
|
this->_initialApPassword = initialApPassword;
|
|
this->_configVersion = configVersion;
|
|
itoa(this->_apTimeoutMs / 1000, this->_apTimeoutStr, 10);
|
|
|
|
this->_thingNameParameter = IotWebConfParameter("Thing name", "iwcThingName", this->_thingName, IOTWEBCONF_WORD_LEN);
|
|
this->_apPasswordParameter = IotWebConfParameter("AP password", "iwcApPassword", this->_apPassword, IOTWEBCONF_WORD_LEN, "password");
|
|
this->_wifiSsidParameter = IotWebConfParameter("WiFi SSID", "iwcWifiSsid", this->_wifiSsid, IOTWEBCONF_WORD_LEN);
|
|
this->_wifiPasswordParameter = IotWebConfParameter("WiFi password", "iwcWifiPassword", this->_wifiPassword, IOTWEBCONF_WORD_LEN, "password");
|
|
this->_apTimeoutParameter = IotWebConfParameter("Startup delay (seconds)", "iwcApTimeout", this->_apTimeoutStr, IOTWEBCONF_WORD_LEN, "number", NULL, NULL, "min='1' max='600'", false);
|
|
this->addParameter(&this->_thingNameParameter);
|
|
this->addParameter(&this->_apPasswordParameter);
|
|
this->addParameter(&this->_wifiSsidParameter);
|
|
this->addParameter(&this->_wifiPasswordParameter);
|
|
this->addParameter(&this->_apTimeoutParameter);
|
|
}
|
|
|
|
char* IotWebConf::getThingName()
|
|
{
|
|
return this->_thingName;
|
|
}
|
|
|
|
void IotWebConf::setConfigPin(int configPin)
|
|
{
|
|
this->_configPin = configPin;
|
|
}
|
|
|
|
void IotWebConf::setStatusPin(int statusPin)
|
|
{
|
|
this->_statusPin = statusPin;
|
|
}
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
void IotWebConf::setupUpdateServer(
|
|
HTTPUpdateServer* updateServer, const char* updatePath)
|
|
{
|
|
this->_updateServer = updateServer;
|
|
this->_updatePath = updatePath;
|
|
}
|
|
#endif
|
|
|
|
boolean IotWebConf::init()
|
|
{
|
|
// -- Setup pins.
|
|
if (this->_configPin >= 0)
|
|
{
|
|
pinMode(this->_configPin, INPUT_PULLUP);
|
|
this->_forceDefaultPassword = (digitalRead(this->_configPin) == LOW);
|
|
}
|
|
if (IOTWEBCONF_STATUS_ENABLED)
|
|
{
|
|
pinMode(this->_statusPin, OUTPUT);
|
|
digitalWrite(this->_statusPin, IOTWEBCONF_STATUS_ON);
|
|
}
|
|
|
|
// -- Load configuration from EEPROM.
|
|
this->configInit();
|
|
boolean validConfig = this->configLoad();
|
|
if (!validConfig)
|
|
{
|
|
// -- No config
|
|
this->_apPassword[0] = '\0';
|
|
this->_wifiSsid[0] = '\0';
|
|
this->_wifiPassword[0] = '\0';
|
|
this->_apTimeoutMs = IOTWEBCONF_DEFAULT_AP_MODE_TIMEOUT_MS;
|
|
}
|
|
else
|
|
{
|
|
this->_apTimeoutMs = atoi(this->_apTimeoutStr) * 1000;
|
|
}
|
|
|
|
// -- Setup mdns
|
|
#ifdef ESP8266
|
|
WiFi.hostname(this->_thingName);
|
|
#elif defined(ESP32)
|
|
WiFi.setHostname(this->_thingName);
|
|
#endif
|
|
#ifdef IOTWEBCONF_CONFIG_USE_MDNS
|
|
MDNS.begin(this->_thingName);
|
|
MDNS.addService("http", "tcp", 80);
|
|
#endif
|
|
#ifdef USE_ESPASYNCWEBSERVER
|
|
// This is required to init wifi already within setup
|
|
// (Otherwise AsyncWebserver will crash on begin)
|
|
doLoop();
|
|
#endif
|
|
return validConfig;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
bool IotWebConf::addParameter(IotWebConfParameter* parameter)
|
|
{
|
|
/*
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Adding parameter '");
|
|
Serial.print(parameter->getId());
|
|
Serial.println("'");
|
|
#endif
|
|
*/
|
|
if (this->_firstParameter == NULL)
|
|
{
|
|
this->_firstParameter = parameter;
|
|
// IOTWEBCONF_DEBUG_LINE(F("Adding as first"));
|
|
return true;
|
|
}
|
|
IotWebConfParameter* current = this->_firstParameter;
|
|
while (current->_nextParameter != NULL)
|
|
{
|
|
current = current->_nextParameter;
|
|
}
|
|
|
|
current->_nextParameter = parameter;
|
|
return true;
|
|
}
|
|
|
|
void IotWebConf::configInit()
|
|
{
|
|
int size = 0;
|
|
IotWebConfParameter* current = this->_firstParameter;
|
|
while (current != NULL)
|
|
{
|
|
size += current->getLength();
|
|
current = current->_nextParameter;
|
|
}
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Config size: ");
|
|
Serial.println(size);
|
|
#endif
|
|
|
|
EEPROM.begin(
|
|
IOTWEBCONF_CONFIG_START + IOTWEBCONF_CONFIG_VESION_LENGTH + size);
|
|
}
|
|
|
|
/**
|
|
* Load the configuration from the eeprom.
|
|
*/
|
|
boolean IotWebConf::configLoad()
|
|
{
|
|
if (this->configTestVersion())
|
|
{
|
|
IotWebConfParameter* current = this->_firstParameter;
|
|
int start = IOTWEBCONF_CONFIG_START + IOTWEBCONF_CONFIG_VESION_LENGTH;
|
|
while (current != NULL)
|
|
{
|
|
if (current->getId() != NULL)
|
|
{
|
|
this->readEepromValue(start, current->valueBuffer, current->getLength());
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
const char* defaultMarker = "";
|
|
#endif
|
|
if ((strlen(current->valueBuffer) == 0) && (current->defaultValue != NULL))
|
|
{
|
|
strncpy(current->valueBuffer, current->defaultValue, current->getLength());
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
defaultMarker = " (using default)";
|
|
#endif
|
|
}
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Loaded config '");
|
|
Serial.print(current->getId());
|
|
Serial.print("'= ");
|
|
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
|
|
Serial.print("'");
|
|
Serial.print(current->valueBuffer);
|
|
Serial.print("'");
|
|
Serial.println(defaultMarker);
|
|
# else
|
|
if (strcmp("password", current->type) == 0)
|
|
{
|
|
Serial.print(F("<hidden>"));
|
|
Serial.println(defaultMarker);
|
|
}
|
|
else
|
|
{
|
|
Serial.print("'");
|
|
Serial.print(current->valueBuffer);
|
|
Serial.print("'");
|
|
Serial.println(defaultMarker);
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
start += current->getLength();
|
|
}
|
|
current = current->_nextParameter;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
IOTWEBCONF_DEBUG_LINE(F("Wrong config version."));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void IotWebConf::configSave()
|
|
{
|
|
this->configSaveConfigVersion();
|
|
IotWebConfParameter* current = this->_firstParameter;
|
|
int start = IOTWEBCONF_CONFIG_START + IOTWEBCONF_CONFIG_VESION_LENGTH;
|
|
while (current != NULL)
|
|
{
|
|
if (current->getId() != NULL)
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Saving config '");
|
|
Serial.print(current->getId());
|
|
Serial.print("'= ");
|
|
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
|
|
Serial.print("'");
|
|
Serial.print(current->valueBuffer);
|
|
Serial.println("'");
|
|
# else
|
|
if (strcmp("password", current->type) == 0)
|
|
{
|
|
Serial.print(F("<hidden>"));
|
|
}
|
|
else
|
|
{
|
|
Serial.print("'");
|
|
Serial.print(current->valueBuffer);
|
|
Serial.println("'");
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
this->writeEepromValue(start, current->valueBuffer, current->getLength());
|
|
start += current->getLength();
|
|
}
|
|
current = current->_nextParameter;
|
|
}
|
|
EEPROM.commit();
|
|
|
|
this->_apTimeoutMs = atoi(this->_apTimeoutStr) * 1000;
|
|
|
|
if (this->_configSavedCallback != NULL)
|
|
{
|
|
this->_configSavedCallback();
|
|
}
|
|
}
|
|
|
|
void IotWebConf::readEepromValue(int start, char* valueBuffer, int length)
|
|
{
|
|
for (int t = 0; t < length; t++)
|
|
{
|
|
*((char*)valueBuffer + t) = EEPROM.read(start + t);
|
|
}
|
|
}
|
|
void IotWebConf::writeEepromValue(int start, char* valueBuffer, int length)
|
|
{
|
|
for (int t = 0; t < length; t++)
|
|
{
|
|
EEPROM.write(start + t, *((char*)valueBuffer + t));
|
|
}
|
|
}
|
|
|
|
boolean IotWebConf::configTestVersion()
|
|
{
|
|
for (byte t = 0; t < IOTWEBCONF_CONFIG_VESION_LENGTH; t++)
|
|
{
|
|
if (EEPROM.read(IOTWEBCONF_CONFIG_START + t) != this->_configVersion[t])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void IotWebConf::configSaveConfigVersion()
|
|
{
|
|
for (byte t = 0; t < IOTWEBCONF_CONFIG_VESION_LENGTH; t++)
|
|
{
|
|
EEPROM.write(IOTWEBCONF_CONFIG_START + t, this->_configVersion[t]);
|
|
}
|
|
}
|
|
|
|
void IotWebConf::setWifiConnectionCallback(std::function<void()> func)
|
|
{
|
|
this->_wifiConnectionCallback = func;
|
|
}
|
|
|
|
void IotWebConf::setConfigSavedCallback(std::function<void()> func)
|
|
{
|
|
this->_configSavedCallback = func;
|
|
}
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
void IotWebConf::setFormValidator(std::function<boolean()> func)
|
|
#else
|
|
void IotWebConf::setFormValidator(std::function<boolean(AsyncWebServerRequest *request)> func)
|
|
#endif
|
|
{
|
|
this->_formValidator = func;
|
|
}
|
|
|
|
void IotWebConf::setWifiConnectionTimeoutMs(unsigned long millis)
|
|
{
|
|
this->_wifiConnectionTimeoutMs = millis;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
void IotWebConf::handleConfig()
|
|
#else
|
|
void IotWebConf::handleConfig(AsyncWebServerRequest *request)
|
|
#endif
|
|
{
|
|
if (this->_state == IOTWEBCONF_STATE_ONLINE)
|
|
{
|
|
// -- Authenticate
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
if (!this->_server->authenticate(
|
|
#else
|
|
if (!request->authenticate(
|
|
#endif
|
|
IOTWEBCONF_ADMIN_USER_NAME, this->_apPassword))
|
|
{
|
|
IOTWEBCONF_DEBUG_LINE(F("Requesting authentication."));
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->_server->requestAuthentication();
|
|
#else
|
|
request->requestAuthentication();
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
if (!this->_server->hasArg("iotSave") || !this->validateForm())
|
|
#else
|
|
if (!request->hasArg("iotSave") || !this->validateForm(request))
|
|
#endif
|
|
{
|
|
// -- Display config portal
|
|
IOTWEBCONF_DEBUG_LINE(F("Configuration page requested."));
|
|
String page = htmlFormatProvider->getHead();
|
|
page.replace("{v}", "Config ESP");
|
|
page += htmlFormatProvider->getScript();
|
|
page += htmlFormatProvider->getStyle();
|
|
page += htmlFormatProvider->getHeadExtension();
|
|
page += htmlFormatProvider->getHeadEnd();
|
|
|
|
page += htmlFormatProvider->getFormStart();
|
|
char parLength[5];
|
|
// -- Add parameters to the form
|
|
IotWebConfParameter* current = this->_firstParameter;
|
|
while (current != NULL)
|
|
{
|
|
if (current->getId() == NULL)
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.println("Rendering separator");
|
|
#endif
|
|
page += "</fieldset><fieldset>";
|
|
if (current->label != NULL)
|
|
{
|
|
page += "<legend>";
|
|
page += current->label;
|
|
page += "</legend>";
|
|
}
|
|
}
|
|
else if (current->visible)
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Rendering '");
|
|
Serial.print(current->getId());
|
|
Serial.print("' with value: ");
|
|
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
|
|
Serial.println(current->valueBuffer);
|
|
# else
|
|
if (strcmp("password", current->type) == 0)
|
|
{
|
|
Serial.println(F("<hidden>"));
|
|
}
|
|
else
|
|
{
|
|
Serial.println(current->valueBuffer);
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
String pitem;
|
|
if (current->label != NULL)
|
|
{
|
|
pitem = htmlFormatProvider->getFormParam(current->type);
|
|
pitem.replace("{b}", current->label);
|
|
pitem.replace("{t}", current->type);
|
|
pitem.replace("{i}", current->getId());
|
|
pitem.replace("{p}", current->placeholder == NULL ? "" : current->placeholder);
|
|
snprintf(parLength, 5, "%d", current->getLength());
|
|
pitem.replace("{l}", parLength);
|
|
if (strcmp("password", current->type) == 0)
|
|
{
|
|
// -- Value of password is not rendered
|
|
pitem.replace("{v}", "");
|
|
}
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
else if (this->_server->hasArg(current->getId()))
|
|
#else
|
|
else if (request->hasArg(current->getId()))
|
|
#endif
|
|
{
|
|
// -- Value from previous submit
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
pitem.replace("{v}", this->_server->arg(current->getId()));
|
|
#else
|
|
pitem.replace("{v}", request->arg(current->getId()));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// -- Value from config
|
|
pitem.replace("{v}", current->valueBuffer);
|
|
}
|
|
pitem.replace(
|
|
"{c}", current->customHtml == NULL ? "" : current->customHtml);
|
|
pitem.replace(
|
|
"{e}",
|
|
current->errorMessage == NULL ? "" : current->errorMessage);
|
|
pitem.replace(
|
|
"{s}",
|
|
current->errorMessage == NULL ? "" : "de"); // Div style class.
|
|
}
|
|
else
|
|
{
|
|
pitem = current->customHtml;
|
|
}
|
|
|
|
page += pitem;
|
|
}
|
|
current = current->_nextParameter;
|
|
}
|
|
|
|
page += htmlFormatProvider->getFormEnd();
|
|
|
|
if (this->_updatePath != NULL)
|
|
{
|
|
String pitem = htmlFormatProvider->getUpdate();
|
|
pitem.replace("{u}", this->_updatePath);
|
|
page += pitem;
|
|
}
|
|
|
|
// -- Fill config version string;
|
|
{
|
|
String pitem = htmlFormatProvider->getConfigVer();
|
|
pitem.replace("{v}", this->_configVersion);
|
|
page += pitem;
|
|
}
|
|
|
|
page += htmlFormatProvider->getEnd();
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->_server->sendHeader("Content-Length", String(page.length()));
|
|
this->_server->send(200, "text/html; charset=UTF-8", page);
|
|
#else
|
|
AsyncWebServerResponse *response = request->beginResponse(200, "text/html; charset=UTF-8", page);
|
|
response->addHeader("Content-Length", String(page.length()));
|
|
request->send(response);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// -- Save config
|
|
IOTWEBCONF_DEBUG_LINE(F("Updating configuration"));
|
|
char temp[IOTWEBCONF_WORD_LEN];
|
|
|
|
IotWebConfParameter* current = this->_firstParameter;
|
|
while (current != NULL)
|
|
{
|
|
if ((current->getId() != NULL) && (current->visible))
|
|
{
|
|
if ((strcmp("password", current->type) == 0) &&
|
|
(current->getLength() <= IOTWEBCONF_WORD_LEN))
|
|
{
|
|
// TODO: Passwords longer than IOTWEBCONF_WORD_LEN not supported.
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->readParamValue(current->getId(), temp, current->getLength());
|
|
#else
|
|
this->readParamValue(request, current->getId(), temp, current->getLength());
|
|
#endif
|
|
if (temp[0] != '\0')
|
|
{
|
|
// -- Value was set.
|
|
strncpy(current->valueBuffer, temp, current->getLength());
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print(current->getId());
|
|
Serial.println(" was set");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print(current->getId());
|
|
Serial.println(" was not changed");
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->readParamValue(
|
|
current->getId(), current->valueBuffer, current->getLength());
|
|
#else
|
|
this->readParamValue(
|
|
request, current->getId(), current->valueBuffer, current->getLength());
|
|
#endif
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print(current->getId());
|
|
Serial.print("='");
|
|
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
|
|
Serial.print(current->valueBuffer);
|
|
# else
|
|
if (strcmp("password", current->type) == 0)
|
|
{
|
|
Serial.print(F("<hidden>"));
|
|
}
|
|
else
|
|
{
|
|
Serial.print(current->valueBuffer);
|
|
}
|
|
# endif
|
|
Serial.print(current->valueBuffer);
|
|
Serial.println("'");
|
|
#endif
|
|
}
|
|
}
|
|
current = current->_nextParameter;
|
|
}
|
|
|
|
this->configSave();
|
|
|
|
String page = htmlFormatProvider->getHead();
|
|
page.replace("{v}", "Config ESP");
|
|
page += htmlFormatProvider->getScript();
|
|
page += htmlFormatProvider->getStyle();
|
|
// page += _customHeadElement;
|
|
page += htmlFormatProvider->getHeadExtension();
|
|
page += htmlFormatProvider->getHeadEnd();
|
|
page += "Configuration saved. ";
|
|
if (this->_apPassword[0] == '\0')
|
|
{
|
|
page += F("You must change the default AP password to continue. Return "
|
|
"to <a href=''>configuration page</a>.");
|
|
}
|
|
else if (this->_wifiSsid[0] == '\0')
|
|
{
|
|
page += F("You must provide the local wifi settings to continue. Return "
|
|
"to <a href=''>configuration page</a>.");
|
|
}
|
|
else if (this->_state == IOTWEBCONF_STATE_NOT_CONFIGURED)
|
|
{
|
|
page += F("Please disconnect from WiFi AP to continue!");
|
|
}
|
|
else
|
|
{
|
|
page += F("Return to <a href='/'>home page</a>.");
|
|
}
|
|
page += htmlFormatProvider->getHeadEnd();
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->_server->sendHeader("Content-Length", String(page.length()));
|
|
this->_server->send(200, "text/html; charset=UTF-8", page);
|
|
#else
|
|
AsyncWebServerResponse *response = request->beginResponse(200, "text/html; charset=UTF-8", page);
|
|
response->addHeader("Content-Length", String(page.length()));
|
|
request->send(response);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
void IotWebConf::readParamValue(
|
|
const char* paramName, char* target, unsigned int len)
|
|
#else
|
|
void IotWebConf::readParamValue(
|
|
AsyncWebServerRequest* request, const char* paramName, char* target, unsigned int len)
|
|
#endif
|
|
{
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
String value = this->_server->arg(paramName);
|
|
#else
|
|
String value = request->arg(paramName);
|
|
#endif
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Value of arg '");
|
|
Serial.print(paramName);
|
|
Serial.print("' is:");
|
|
Serial.println(value);
|
|
#endif
|
|
value.toCharArray(target, len);
|
|
}
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
boolean IotWebConf::validateForm()
|
|
#else
|
|
boolean IotWebConf::validateForm(AsyncWebServerRequest* request)
|
|
#endif
|
|
{
|
|
// -- Clean previous error messages.
|
|
IotWebConfParameter* current = this->_firstParameter;
|
|
while (current != NULL)
|
|
{
|
|
current->errorMessage = NULL;
|
|
current = current->_nextParameter;
|
|
}
|
|
|
|
// -- Call external validator.
|
|
boolean valid = true;
|
|
if (this->_formValidator != NULL)
|
|
{
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
valid = this->_formValidator();
|
|
#else
|
|
valid = this->_formValidator(request);
|
|
#endif
|
|
}
|
|
|
|
// -- Internal validation.
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
int l = this->_server->arg(this->_thingNameParameter.getId()).length();
|
|
#else
|
|
int l = request->arg(this->_thingNameParameter.getId()).length();
|
|
#endif
|
|
if (3 > l)
|
|
{
|
|
this->_thingNameParameter.errorMessage =
|
|
"Give a name with at least 3 characters.";
|
|
valid = false;
|
|
}
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
l = this->_server->arg(this->_apPasswordParameter.getId()).length();
|
|
#else
|
|
l = request->arg(this->_apPasswordParameter.getId()).length();
|
|
#endif
|
|
if ((0 < l) && (l < 8))
|
|
{
|
|
this->_apPasswordParameter.errorMessage =
|
|
"Password length must be at least 8 characters.";
|
|
valid = false;
|
|
}
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
l = this->_server->arg(this->_wifiPasswordParameter.getId()).length();
|
|
#else
|
|
l = request->arg(this->_wifiPasswordParameter.getId()).length();
|
|
#endif
|
|
if ((0 < l) && (l < 8))
|
|
{
|
|
this->_wifiPasswordParameter.errorMessage =
|
|
"Password length must be at least 8 characters.";
|
|
valid = false;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
void IotWebConf::handleNotFound()
|
|
#else
|
|
void IotWebConf::handleNotFound(AsyncWebServerRequest *request)
|
|
#endif
|
|
{
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
if (this->handleCaptivePortal())
|
|
#else
|
|
if (this->handleCaptivePortal(request))
|
|
#endif
|
|
{
|
|
// If captive portal redirect instead of displaying the error page.
|
|
return;
|
|
}
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Requested non-existing page '");
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
Serial.print(this->_server->uri());
|
|
#else
|
|
Serial.print(request->url());
|
|
#endif
|
|
Serial.print("' arguments(");
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
Serial.print(this->_server->method() == HTTP_GET ? "GET" : "POST");
|
|
#else
|
|
Serial.print(request->method() == HTTP_GET ? "GET" : "POST");
|
|
#endif
|
|
Serial.print("):");
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
Serial.println(this->_server->args());
|
|
#else
|
|
Serial.println(request->args());
|
|
#endif
|
|
#endif
|
|
String message = "File Not Found\n\n";
|
|
message += "URI: ";
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
message += this->_server->uri();
|
|
#else
|
|
message += request->url();
|
|
#endif
|
|
message += "\nMethod: ";
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
message += (this->_server->method() == HTTP_GET) ? "GET" : "POST";
|
|
#else
|
|
message += (request->method() == HTTP_GET) ? "GET" : "POST";
|
|
#endif
|
|
message += "\nArguments: ";
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
message += this->_server->args();
|
|
#else
|
|
message += request->args();
|
|
#endif
|
|
message += "\n";
|
|
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
for (uint8_t i = 0; i < this->_server->args(); i++)
|
|
#else
|
|
for (uint8_t i = 0; i < request->args(); i++)
|
|
#endif
|
|
{
|
|
message +=
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
" " + this->_server->argName(i) + ": " + this->_server->arg(i) + "\n";
|
|
#else
|
|
" " + request->argName(i) + ": " + request->arg(i) + "\n";
|
|
#endif
|
|
}
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->_server->sendHeader(
|
|
"Cache-Control", "no-cache, no-store, must-revalidate");
|
|
this->_server->sendHeader("Pragma", "no-cache");
|
|
this->_server->sendHeader("Expires", "-1");
|
|
this->_server->sendHeader("Content-Length", String(message.length()));
|
|
this->_server->send(404, "text/plain", message);
|
|
#else
|
|
AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", message);
|
|
response->addHeader(
|
|
"Cache-Control", "no-cache, no-store, must-revalidate");
|
|
response->addHeader("Pragma", "no-cache");
|
|
response->addHeader("Expires", "-1");
|
|
response->addHeader("Content-Length", String(message.length()));
|
|
request->send(response);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Redirect to captive portal if we got a request for another domain.
|
|
* Return true in that case so the page handler do not try to handle the request
|
|
* again. (Code from WifiManager project.)
|
|
*/
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
boolean IotWebConf::handleCaptivePortal()
|
|
#else
|
|
boolean IotWebConf::handleCaptivePortal(AsyncWebServerRequest *request)
|
|
#endif
|
|
{
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
String host = this->_server->hostHeader();
|
|
#else
|
|
String host = request->host().c_str();
|
|
#endif
|
|
String thingName = String(this->_thingName);
|
|
thingName.toLowerCase();
|
|
if (!isIp(host) && !host.startsWith(thingName))
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Request for ");
|
|
Serial.print(host);
|
|
Serial.print(" redirected to ");
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
Serial.println(this->_server->client().localIP());
|
|
#else
|
|
if (ON_STA_FILTER(request)) {
|
|
Serial.println(WiFi.localIP());
|
|
} else {
|
|
Serial.println(WiFi.softAPIP());
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->_server->sendHeader(
|
|
"Location", String("http://") + toStringIp(this->_server->client().localIP()), true);
|
|
this->_server->send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
|
this->_server->client().stop(); // Stop is needed because we sent no content length
|
|
return true;
|
|
#else
|
|
String RedirectUrl = "http://";
|
|
if (ON_STA_FILTER(request)) {
|
|
RedirectUrl += WiFi.localIP().toString();
|
|
} else {
|
|
RedirectUrl += WiFi.softAPIP().toString();
|
|
}
|
|
request->redirect(RedirectUrl);
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Is this an IP? */
|
|
boolean IotWebConf::isIp(String str)
|
|
{
|
|
for (size_t i = 0; i < str.length(); i++)
|
|
{
|
|
int c = str.charAt(i);
|
|
if (c != '.' && (c < '0' || c > '9'))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** IP to String? */
|
|
String IotWebConf::toStringIp(IPAddress ip)
|
|
{
|
|
String res = "";
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
res += String((ip >> (8 * i)) & 0xFF) + ".";
|
|
}
|
|
res += String(((ip >> 8 * 3)) & 0xFF);
|
|
return res;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void IotWebConf::delay(unsigned long m)
|
|
{
|
|
unsigned long delayStart = millis();
|
|
while (m > millis() - delayStart)
|
|
{
|
|
this->doLoop();
|
|
delay(1); // -- Note: 1ms might not be enough to perform a full yield. So
|
|
// 'yeild' in 'doLoop' is eventually a good idea.
|
|
}
|
|
}
|
|
|
|
void IotWebConf::doLoop()
|
|
{
|
|
doBlink();
|
|
yield(); // -- Yield should not be necessary, but cannot hurt eather.
|
|
if (this->_state == IOTWEBCONF_STATE_BOOT)
|
|
{
|
|
// -- After boot, fall immediately to AP mode.
|
|
byte startupState = IOTWEBCONF_STATE_AP_MODE;
|
|
if (this->_skipApStartup)
|
|
{
|
|
if (isWifiModePossible())
|
|
{
|
|
IOTWEBCONF_DEBUG_LINE(
|
|
F("SkipApStartup is requested, but either no WiFi was set up, or "
|
|
"configButton was pressed."));
|
|
}
|
|
else
|
|
{
|
|
// -- Startup state can be WiFi, if it is requested and also possible.
|
|
IOTWEBCONF_DEBUG_LINE(F("SkipApStartup mode was applied"));
|
|
startupState = IOTWEBCONF_STATE_CONNECTING;
|
|
}
|
|
}
|
|
this->changeState(startupState);
|
|
}
|
|
else if (
|
|
(this->_state == IOTWEBCONF_STATE_NOT_CONFIGURED) ||
|
|
(this->_state == IOTWEBCONF_STATE_AP_MODE))
|
|
{
|
|
// -- We must only leave the AP mode, when no slaves are connected.
|
|
// -- Other than that AP mode has a timeout. E.g. after boot, or when retry
|
|
// connecting to WiFi
|
|
checkConnection();
|
|
checkApTimeout();
|
|
this->_dnsServer->processNextRequest();
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->_server->handleClient();
|
|
#endif
|
|
}
|
|
else if (this->_state == IOTWEBCONF_STATE_CONNECTING)
|
|
{
|
|
if (checkWifiConnection())
|
|
{
|
|
this->changeState(IOTWEBCONF_STATE_ONLINE);
|
|
return;
|
|
}
|
|
}
|
|
else if (this->_state == IOTWEBCONF_STATE_ONLINE)
|
|
{
|
|
// -- In server mode we provide web interface. And check whether it is time
|
|
// to run the client.
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
this->_server->handleClient();
|
|
#endif
|
|
if (WiFi.status() != WL_CONNECTED)
|
|
{
|
|
IOTWEBCONF_DEBUG_LINE(F("Not connected. Try reconnect..."));
|
|
this->changeState(IOTWEBCONF_STATE_CONNECTING);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* What happens, when a state changed...
|
|
*/
|
|
void IotWebConf::changeState(byte newState)
|
|
{
|
|
switch (newState)
|
|
{
|
|
case IOTWEBCONF_STATE_AP_MODE:
|
|
{
|
|
// -- In AP mode we must override the default AP password. Otherwise we stay
|
|
// in STATE_NOT_CONFIGURED.
|
|
if (isWifiModePossible())
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
if (this->_forceDefaultPassword)
|
|
{
|
|
Serial.println("AP mode forced by reset pin");
|
|
}
|
|
else
|
|
{
|
|
Serial.println("AP password was not set in configuration");
|
|
}
|
|
#endif
|
|
newState = IOTWEBCONF_STATE_NOT_CONFIGURED;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("State changing from: ");
|
|
Serial.print(this->_state);
|
|
Serial.print(" to ");
|
|
Serial.println(newState);
|
|
#endif
|
|
byte oldState = this->_state;
|
|
this->_state = newState;
|
|
this->stateChanged(oldState, newState);
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("State changed from: ");
|
|
Serial.print(oldState);
|
|
Serial.print(" to ");
|
|
Serial.println(newState);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* What happens, when a state changed...
|
|
*/
|
|
void IotWebConf::stateChanged(byte oldState, byte newState)
|
|
{
|
|
// updateOutput();
|
|
switch (newState)
|
|
{
|
|
case IOTWEBCONF_STATE_AP_MODE:
|
|
case IOTWEBCONF_STATE_NOT_CONFIGURED:
|
|
if (newState == IOTWEBCONF_STATE_AP_MODE)
|
|
{
|
|
this->blinkInternal(300, 90);
|
|
}
|
|
else
|
|
{
|
|
this->blinkInternal(300, 50);
|
|
}
|
|
setupAp();
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
if (this->_updateServer != NULL)
|
|
{
|
|
this->_updateServer->setup(this->_server, this->_updatePath);
|
|
}
|
|
#endif
|
|
this->_server->begin();
|
|
this->_apConnectionStatus = IOTWEBCONF_AP_CONNECTION_STATE_NC;
|
|
this->_apStartTimeMs = millis();
|
|
break;
|
|
case IOTWEBCONF_STATE_CONNECTING:
|
|
if ((oldState == IOTWEBCONF_STATE_AP_MODE) ||
|
|
(oldState == IOTWEBCONF_STATE_NOT_CONFIGURED))
|
|
{
|
|
stopAp();
|
|
}
|
|
this->blinkInternal(1000, 50);
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Connecting to [");
|
|
Serial.print(this->_wifiAuthInfo.ssid);
|
|
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
|
|
Serial.print("] with password [");
|
|
Serial.print(this->_wifiAuthInfo.password);
|
|
Serial.println("]");
|
|
# else
|
|
Serial.println(F("] (password is hidden)"));
|
|
# endif
|
|
#endif
|
|
this->_wifiConnectionStart = millis();
|
|
this->_wifiConnectionHandler(
|
|
this->_wifiAuthInfo.ssid, this->_wifiAuthInfo.password);
|
|
break;
|
|
case IOTWEBCONF_STATE_ONLINE:
|
|
this->blinkInternal(8000, 2);
|
|
#ifndef USE_ESPASYNCWEBSERVER
|
|
if (this->_updateServer != NULL)
|
|
{
|
|
this->_updateServer->updateCredentials(
|
|
IOTWEBCONF_ADMIN_USER_NAME, this->_apPassword);
|
|
}
|
|
#endif
|
|
this->_server->begin();
|
|
IOTWEBCONF_DEBUG_LINE(F("Accepting connection"));
|
|
if (this->_wifiConnectionCallback != NULL)
|
|
{
|
|
this->_wifiConnectionCallback();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void IotWebConf::checkApTimeout()
|
|
{
|
|
if ((this->_wifiSsid[0] != '\0') && (this->_apPassword[0] != '\0') &&
|
|
(!this->_forceDefaultPassword))
|
|
{
|
|
// -- Only move on, when we have a valid WifF and AP configured.
|
|
if ((this->_apConnectionStatus == IOTWEBCONF_AP_CONNECTION_STATE_DC) ||
|
|
((this->_apTimeoutMs < millis() - this->_apStartTimeMs) &&
|
|
(this->_apConnectionStatus != IOTWEBCONF_AP_CONNECTION_STATE_C)))
|
|
{
|
|
this->changeState(IOTWEBCONF_STATE_CONNECTING);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether we have anyone joined to our AP.
|
|
* If so, we must not change state. But when our guest leaved, we can
|
|
* immediately move on.
|
|
*/
|
|
void IotWebConf::checkConnection()
|
|
{
|
|
if ((this->_apConnectionStatus == IOTWEBCONF_AP_CONNECTION_STATE_NC) &&
|
|
(WiFi.softAPgetStationNum() > 0))
|
|
{
|
|
this->_apConnectionStatus = IOTWEBCONF_AP_CONNECTION_STATE_C;
|
|
IOTWEBCONF_DEBUG_LINE(F("Connection to AP."));
|
|
}
|
|
else if (
|
|
(this->_apConnectionStatus == IOTWEBCONF_AP_CONNECTION_STATE_C) &&
|
|
(WiFi.softAPgetStationNum() == 0))
|
|
{
|
|
this->_apConnectionStatus = IOTWEBCONF_AP_CONNECTION_STATE_DC;
|
|
IOTWEBCONF_DEBUG_LINE(F("Disconnected from AP."));
|
|
if (this->_forceDefaultPassword)
|
|
{
|
|
IOTWEBCONF_DEBUG_LINE(F("Releasing forced AP mode."));
|
|
this->_forceDefaultPassword = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean IotWebConf::checkWifiConnection()
|
|
{
|
|
if (WiFi.status() != WL_CONNECTED)
|
|
{
|
|
if (this->_wifiConnectionTimeoutMs < millis() - this->_wifiConnectionStart)
|
|
{
|
|
// -- WiFi not available, fall back to AP mode.
|
|
IOTWEBCONF_DEBUG_LINE(F("Giving up."));
|
|
WiFi.disconnect(true);
|
|
IotWebConfWifiAuthInfo* newWifiAuthInfo = _wifiConnectionFailureHandler();
|
|
if (newWifiAuthInfo != NULL)
|
|
{
|
|
// -- Try connecting with another connection info.
|
|
this->_wifiAuthInfo.ssid = newWifiAuthInfo->ssid;
|
|
this->_wifiAuthInfo.password = newWifiAuthInfo->password;
|
|
this->changeState(IOTWEBCONF_STATE_CONNECTING);
|
|
}
|
|
else
|
|
{
|
|
this->changeState(IOTWEBCONF_STATE_AP_MODE);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -- Connected
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.println("WiFi connected");
|
|
Serial.print("IP address: ");
|
|
Serial.println(WiFi.localIP());
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void IotWebConf::setupAp()
|
|
{
|
|
WiFi.mode(WIFI_AP);
|
|
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Setting up AP: ");
|
|
Serial.println(this->_thingName);
|
|
#endif
|
|
if (this->_state == IOTWEBCONF_STATE_NOT_CONFIGURED)
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("With default password: ");
|
|
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
|
|
Serial.println(this->_initialApPassword);
|
|
# else
|
|
Serial.println(F("<hidden>"));
|
|
# endif
|
|
#endif
|
|
this->_apConnectionHandler(this->_thingName, this->_initialApPassword);
|
|
}
|
|
else
|
|
{
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print("Use password: ");
|
|
# ifdef IOTWEBCONF_DEBUG_PWD_TO_SERIAL
|
|
Serial.println(this->_apPassword);
|
|
# else
|
|
Serial.println(F("<hidden>"));
|
|
# endif
|
|
#endif
|
|
this->_apConnectionHandler(this->_thingName, this->_apPassword);
|
|
}
|
|
|
|
#ifdef IOTWEBCONF_DEBUG_TO_SERIAL
|
|
Serial.print(F("AP IP address: "));
|
|
Serial.println(WiFi.softAPIP());
|
|
#endif
|
|
// delay(500); // Without delay I've seen the IP address blank
|
|
// Serial.print(F("AP IP address: "));
|
|
// Serial.println(WiFi.softAPIP());
|
|
|
|
/* Setup the DNS server redirecting all the domains to the apIP */
|
|
this->_dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
|
this->_dnsServer->start(IOTWEBCONF_DNS_PORT, "*", WiFi.softAPIP());
|
|
}
|
|
|
|
void IotWebConf::stopAp()
|
|
{
|
|
WiFi.softAPdisconnect(true);
|
|
WiFi.mode(WIFI_STA);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
void IotWebConf::blink(unsigned long repeatMs, byte dutyCyclePercent)
|
|
{
|
|
if (repeatMs == 0)
|
|
{
|
|
this->stopCustomBlink();
|
|
}
|
|
else
|
|
{
|
|
this->_blinkOnMs = repeatMs * dutyCyclePercent / 100;
|
|
this->_blinkOffMs = repeatMs * (100 - dutyCyclePercent) / 100;
|
|
}
|
|
}
|
|
|
|
void IotWebConf::fineBlink(unsigned long onMs, unsigned long offMs)
|
|
{
|
|
this->_blinkOnMs = onMs;
|
|
this->_blinkOffMs = offMs;
|
|
}
|
|
|
|
void IotWebConf::stopCustomBlink()
|
|
{
|
|
this->_blinkOnMs = this->_internalBlinkOnMs;
|
|
this->_blinkOffMs = this->_internalBlinkOffMs;
|
|
}
|
|
|
|
void IotWebConf::blinkInternal(unsigned long repeatMs, byte dutyCyclePercent)
|
|
{
|
|
this->blink(repeatMs, dutyCyclePercent);
|
|
this->_internalBlinkOnMs = this->_blinkOnMs;
|
|
this->_internalBlinkOffMs = this->_blinkOffMs;
|
|
}
|
|
|
|
void IotWebConf::doBlink()
|
|
{
|
|
if (IOTWEBCONF_STATUS_ENABLED)
|
|
{
|
|
unsigned long now = millis();
|
|
unsigned long delayMs =
|
|
this->_blinkState == LOW ? this->_blinkOnMs : this->_blinkOffMs;
|
|
if (delayMs < now - this->_lastBlinkTime)
|
|
{
|
|
this->_blinkState = 1 - this->_blinkState;
|
|
this->_lastBlinkTime = now;
|
|
digitalWrite(this->_statusPin, this->_blinkState);
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean IotWebConf::connectAp(const char* apName, const char* password)
|
|
{
|
|
return WiFi.softAP(apName, password);
|
|
}
|
|
void IotWebConf::connectWifi(const char* ssid, const char* password)
|
|
{
|
|
WiFi.begin(ssid, password);
|
|
}
|
|
IotWebConfWifiAuthInfo* IotWebConf::handleConnectWifiFailure()
|
|
{
|
|
return NULL;
|
|
}
|