Improved stability of connection attempts on ESP32

pull/41/head
Hieromon Ikasamo 6 years ago
parent 659b68d3ff
commit 7d5bed6183
  1. 196
      src/AutoConnect.cpp
  2. 16
      src/AutoConnect.h
  3. 16
      src/AutoConnectDefs.h
  4. 93
      src/AutoConnectPage.cpp

@ -3,7 +3,7 @@
* @file AutoConnect.cpp * @file AutoConnect.cpp
* @author hieromon@gmail.com * @author hieromon@gmail.com
* @version 0.9.7 * @version 0.9.7
* @date 2018-11-17 * @date 2019-01-21
* @copyright MIT license. * @copyright MIT license.
*/ */
@ -54,6 +54,9 @@ void AutoConnect::_initialize() {
_menuTitle = String(AUTOCONNECT_MENU_TITLE); _menuTitle = String(AUTOCONNECT_MENU_TITLE);
_connectTimeout = AUTOCONNECT_TIMEOUT; _connectTimeout = AUTOCONNECT_TIMEOUT;
memset(&_credential, 0x00, sizeof(struct station_config)); memset(&_credential, 0x00, sizeof(struct station_config));
#ifdef ARDUINO_ARCH_ESP32
_disconnectEventId = -1; // The member available for ESP32 only
#endif
_aux.release(); _aux.release();
} }
@ -81,12 +84,11 @@ bool AutoConnect::begin() {
* @param ssid SSID to be connected. * @param ssid SSID to be connected.
* @param passphrase Password for connection. * @param passphrase Password for connection.
* @param timeout A time out value in milliseconds for waiting connection. * @param timeout A time out value in milliseconds for waiting connection.
* @retval true Connection established, AutoConnect service started with WIFI_STA mode. * @return true Connection established, AutoConnect service started with WIFI_STA mode.
* @retval false Could not connected, Captive portal started with WIFI_AP_STA mode. * @return false Could not connected, Captive portal started with WIFI_AP_STA mode.
*/ */
bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long timeout) { bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long timeout) {
bool cs; bool cs;
bool hasTimeout;
// Overwrite for the current timeout value. // Overwrite for the current timeout value.
_connectTimeout = timeout; _connectTimeout = timeout;
@ -166,8 +168,10 @@ bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long
// Activate the AP mode with configured softAP and start the access point. // Activate the AP mode with configured softAP and start the access point.
WiFi.mode(WIFI_AP_STA); WiFi.mode(WIFI_AP_STA);
while (WiFi.getMode() != WIFI_AP_STA) while (WiFi.getMode() != WIFI_AP_STA) {
delay(1);
yield(); yield();
}
// Connection unsuccessful, launch the captive portal. // Connection unsuccessful, launch the captive portal.
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
@ -176,15 +180,21 @@ bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long
} }
#endif #endif
WiFi.softAP(_apConfig.apid.c_str(), _apConfig.psk.c_str(), _apConfig.channel, _apConfig.hidden); WiFi.softAP(_apConfig.apid.c_str(), _apConfig.psk.c_str(), _apConfig.channel, _apConfig.hidden);
while (WiFi.softAPIP() == IPAddress(0, 0, 0, 0)) { do {
delay(100); delay(100);
yield(); yield();
} } while (WiFi.softAPIP() == IPAddress(0, 0, 0, 0));
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
if (!(_apConfig.apip == IPAddress(0, 0, 0, 0) || _apConfig.gateway == IPAddress(0, 0, 0, 0) || _apConfig.netmask == IPAddress(0, 0, 0, 0))) { if (!(_apConfig.apip == IPAddress(0, 0, 0, 0) || _apConfig.gateway == IPAddress(0, 0, 0, 0) || _apConfig.netmask == IPAddress(0, 0, 0, 0))) {
_config(); _config();
} }
#endif #endif
if (_apConfig.apip != IPAddress(0, 0, 0, 0)) {
do {
delay(100);
yield();
} while (WiFi.softAPIP() != _apConfig.apip);
}
_currentHostIP = WiFi.softAPIP(); _currentHostIP = WiFi.softAPIP();
AC_DBG("SoftAP %s/%s CH(%d) H(%d) IP:%s\n", _apConfig.apid.c_str(), _apConfig.psk.c_str(), _apConfig.channel, _apConfig.hidden, _currentHostIP.toString().c_str()); AC_DBG("SoftAP %s/%s CH(%d) H(%d) IP:%s\n", _apConfig.apid.c_str(), _apConfig.psk.c_str(), _apConfig.channel, _apConfig.hidden, _currentHostIP.toString().c_str());
@ -201,15 +211,14 @@ bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long
_startDNSServer(); _startDNSServer();
// Start the captive portal to make a new connection // Start the captive portal to make a new connection
hasTimeout = false; bool hasTimeout = false;
_portalAccessPeriod = millis(); _portalAccessPeriod = millis();
while (WiFi.status() != WL_CONNECTED && !_rfReset) { while (WiFi.status() != WL_CONNECTED && !_rfReset) {
handleClient(); handleClient();
// Force execution of queued processes. // Force execution of queued processes.
yield(); yield();
// Check timeout // Check timeout
if (_hasTimeout(_apConfig.portalTimeout)) { if ((hasTimeout = _hasTimeout(_apConfig.portalTimeout))) {
hasTimeout = true;
AC_DBG("CP timeout exceeded:%ld\n", millis() - _portalAccessPeriod); AC_DBG("CP timeout exceeded:%ld\n", millis() - _portalAccessPeriod);
break; break;
} }
@ -220,6 +229,7 @@ bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long
if (cs) { if (cs) {
_dnsServer->stop(); _dnsServer->stop();
_dnsServer.reset(); _dnsServer.reset();
AC_DBG("DNS server stopped\n");
} }
// Captive portal staying time exceeds timeout, // Captive portal staying time exceeds timeout,
// Close the portal if an option for keeping the portal is false. // Close the portal if an option for keeping the portal is false.
@ -305,8 +315,10 @@ void AutoConnect::end() {
if (_webServer) { if (_webServer) {
switch (_webServerAlloc) { switch (_webServerAlloc) {
case AC_WEBSERVER_HOSTED: case AC_WEBSERVER_HOSTED:
if (_dnsServer) if (_dnsServer) {
_dnsServer->stop();
_dnsServer.reset(); _dnsServer.reset();
}
_webServer.reset(); _webServer.reset();
break; break;
case AC_WEBSERVER_PARASITIC: case AC_WEBSERVER_PARASITIC:
@ -379,7 +391,7 @@ void AutoConnect::_startWebServer() {
// here, Prepare PageBuilders for captive portal // here, Prepare PageBuilders for captive portal
if (!_responsePage) { if (!_responsePage) {
_responsePage = new PageBuilder(); _responsePage = new PageBuilder();
_responsePage->chunked(PB_ByteStream); _responsePage->chunked(AUTOCONNECT_HTTP_TRANSFER);
_responsePage->exitCanHandle(std::bind(&AutoConnect::_classifyHandle, this, std::placeholders::_1, std::placeholders::_2)); _responsePage->exitCanHandle(std::bind(&AutoConnect::_classifyHandle, this, std::placeholders::_1, std::placeholders::_2));
_responsePage->insert(*_webServer); _responsePage->insert(*_webServer);
@ -428,8 +440,8 @@ void AutoConnect::handleRequest() {
// Handling processing requests to AutoConnect. // Handling processing requests to AutoConnect.
if (_rfConnect) { if (_rfConnect) {
// Leave from the AP currently. // Leave from the AP currently.
// if (WiFi.status() == WL_CONNECTED) if (WiFi.status() == WL_CONNECTED)
// _disconnectWiFi(true); _disconnectWiFi(true);
// An attempt to establish a new AP. // An attempt to establish a new AP.
AC_DBG("Request for %s\n", reinterpret_cast<const char*>(_credential.ssid)); AC_DBG("Request for %s\n", reinterpret_cast<const char*>(_credential.ssid));
@ -446,6 +458,9 @@ void AutoConnect::handleRequest() {
credit.save(&_credential); credit.save(&_credential);
AC_DBG("%s credential saved\n", reinterpret_cast<const char*>(_credential.ssid)); AC_DBG("%s credential saved\n", reinterpret_cast<const char*>(_credential.ssid));
} }
// Ensures that keeps a connection with the current AP while the portal behaves.
_setReconnect(AC_RECONNECT_SET);
} }
else else
AC_DBG("%s has no BSSID, saving is unavailable\n", reinterpret_cast<const char*>(_credential.ssid)); AC_DBG("%s has no BSSID, saving is unavailable\n", reinterpret_cast<const char*>(_credential.ssid));
@ -456,7 +471,7 @@ void AutoConnect::handleRequest() {
_rsConnect = WiFi.status(); _rsConnect = WiFi.status();
_disconnectWiFi(false); _disconnectWiFi(false);
while (WiFi.status() != WL_IDLE_STATUS && WiFi.status() != WL_DISCONNECTED) { while (WiFi.status() != WL_IDLE_STATUS && WiFi.status() != WL_DISCONNECTED) {
delay(10); delay(1);
yield(); yield();
} }
} }
@ -474,6 +489,7 @@ void AutoConnect::handleRequest() {
if (_rfDisconnect) { if (_rfDisconnect) {
// Disconnect from the current AP. // Disconnect from the current AP.
_waitForEndTransmission();
_stopPortal(); _stopPortal();
_disconnectWiFi(false); _disconnectWiFi(false);
while (WiFi.status() == WL_CONNECTED) { while (WiFi.status() == WL_CONNECTED) {
@ -533,7 +549,7 @@ void AutoConnect::onNotFound(WebServerClass::THandlerFunction fn) {
/** /**
* Load stored credentials that match nearby WLANs. * Load stored credentials that match nearby WLANs.
* @retval true A matched credential of BSSID was loaded. * @return true A matched credential of BSSID was loaded.
*/ */
bool AutoConnect::_loadAvailCredential() { bool AutoConnect::_loadAvailCredential() {
AutoConnectCredential credential(_apConfig.boundaryOffset); AutoConnectCredential credential(_apConfig.boundaryOffset);
@ -570,8 +586,9 @@ void AutoConnect::_stopPortal() {
delay(1000); delay(1000);
} }
_setReconnect(AC_RECONNECT_RESET);
WiFi.softAPdisconnect(false); WiFi.softAPdisconnect(false);
AC_DBG("SoftAP stopped\n"); AC_DBG("Portal stopped\n");
} }
/** /**
@ -593,6 +610,10 @@ bool AutoConnect::_captivePortal() {
/** /**
* Check whether the stay-time in the captive portal has a timeout. * Check whether the stay-time in the captive portal has a timeout.
* If the station is connected, the time measurement will be reset.
* @param timeout The time limit for keeping the captive portal.
* @return true There is no connection from the station even the time limit exceeds.
* @return false Connectionless duration has not exceeded yet.
*/ */
bool AutoConnect::_hasTimeout(unsigned long timeout) { bool AutoConnect::_hasTimeout(unsigned long timeout) {
uint8_t staNum; uint8_t staNum;
@ -629,11 +650,11 @@ void AutoConnect::_handleNotFound() {
else { else {
PageElement page404(_PAGE_404, { { "HEAD", std::bind(&AutoConnect::_token_HEAD, this, std::placeholders::_1) } }); PageElement page404(_PAGE_404, { { "HEAD", std::bind(&AutoConnect::_token_HEAD, this, std::placeholders::_1) } });
String html = page404.build(); String html = page404.build();
_webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate", true); _webServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"), true);
_webServer->sendHeader("Pragma", "no-cache"); _webServer->sendHeader(F("Pragma"), F("no-cache"));
_webServer->sendHeader("Expires", "-1"); _webServer->sendHeader(F("Expires"), F("-1"));
_webServer->sendHeader("Content-Length", String(html.length())); _webServer->sendHeader(F("Content-Length"), String(html.length()));
_webServer->send(404, "text/html", html); _webServer->send(404, F("text/html"), html);
} }
} }
} }
@ -664,7 +685,7 @@ String AutoConnect::_induceDisconnect(PageArgument& args) {
* handling of the current request by PageBuilder triggered by handleClient(). * handling of the current request by PageBuilder triggered by handleClient().
* If "Credential" exists in POST parameter, it reads from EEPROM. * If "Credential" exists in POST parameter, it reads from EEPROM.
* @param args http request arguments. * @param args http request arguments.
* @retval A redirect response including "Location:" header. * @return A redirect response including "Location:" header.
*/ */
String AutoConnect::_induceConnect(PageArgument& args) { String AutoConnect::_induceConnect(PageArgument& args) {
// Retrieve credential from the post method content. // Retrieve credential from the post method content.
@ -675,7 +696,7 @@ String AutoConnect::_induceConnect(PageArgument& args) {
credential.load(args.arg(AUTOCONNECT_PARAMID_CRED).c_str(), &entry); credential.load(args.arg(AUTOCONNECT_PARAMID_CRED).c_str(), &entry);
strncpy(reinterpret_cast<char*>(_credential.ssid), reinterpret_cast<const char*>(entry.ssid), sizeof(_credential.ssid)); strncpy(reinterpret_cast<char*>(_credential.ssid), reinterpret_cast<const char*>(entry.ssid), sizeof(_credential.ssid));
strncpy(reinterpret_cast<char*>(_credential.password), reinterpret_cast<const char*>(entry.password), sizeof(_credential.password)); strncpy(reinterpret_cast<char*>(_credential.password), reinterpret_cast<const char*>(entry.password), sizeof(_credential.password));
AC_DBG("Credential loaded:%s %s\n", _credential.ssid, _credential.password); AC_DBG("Credential loaded:%s\n", _credential.ssid);
} }
else { else {
// Credential had by the post parameter. // Credential had by the post parameter.
@ -685,17 +706,34 @@ String AutoConnect::_induceConnect(PageArgument& args) {
// Turn on the trigger to start WiFi.begin(). // Turn on the trigger to start WiFi.begin().
_rfConnect = true; _rfConnect = true;
_menuTitle = String("Connecting");
// Since v0.9.7, the redirect method changed from a 302 response to the
// meta tag with refresh attribute.
// This approach for ESP32 makes an inefficient choice. The waiting
// procedure for a connection attempt should be the server side. Also,
// the proper value of waiting time until refreshing is unknown. But
// AutoConnect cannot avoid the following error as affairs stand now
// that occurs at connection establishment.
// [WiFiClient.cpp:463] connected(): Disconnected: RES: 0, ERR: 128
// When connecting as a station, TCP reset caused by switching of the
// radio channel occurs. Although the Espressif's view is true. However,
// the actual TCP reset occurs not at the time of switching the channel.
// It occurs at the connection from the ESP32 to the AP is established
// and it is possible that TCP reset is occurring in other situations.
// So, it may not be the real cause. Client-origin redirects with HTML
// refresh depend on the behavior of the arduino-esp32 library. Thus,
// the implementations for redirects with HTML will continue until
// the arduino-esp32 core supports reconnection.
// Redirect to waiting URI while executing connection request. // Redirect to waiting URI while executing connection request.
//_webServer->sendHeader("Location", String("http://") + _webServer->client().localIP().toString() + String(AUTOCONNECT_URI_RESULT), true); // String url = String(F("http://")) + _webServer->client().localIP().toString() + String(AUTOCONNECT_URI_RESULT);
String url = String("http://") + _webServer->client().localIP().toString() + String(AUTOCONNECT_URI_RESULT); // _webServer->sendHeader(F("Location"), url, true);
_webServer->sendHeader("Location", url, true); // _webServer->send(302, F("text/plain"), "");
//_webServer->sendHeader("Connection", "keep-alive"); // _webServer->client().stop();
_webServer->send(302, "text/plain", ""); // _webServer->client().flush();
_webServer->client().flush(); // _waitForEndTransmission(); // Wait for response transmission complete
_webServer->client().stop(); // _responsePage->cancel();
_responsePage->cancel();
return String(""); return String("");
} }
@ -704,11 +742,25 @@ String AutoConnect::_induceConnect(PageArgument& args) {
* A destination as _redirectURI is indicated by loop to establish connection. * A destination as _redirectURI is indicated by loop to establish connection.
*/ */
String AutoConnect::_invokeResult(PageArgument& args) { String AutoConnect::_invokeResult(PageArgument& args) {
String redirect = String("http://") + _currentHostIP.toString() + _redirectURI; String redirect = String(F("http://"));
_webServer->sendHeader("Location", redirect, true); // The host address to which the connection result for ESP32 responds
_webServer->send(302, "text/plain", ""); // changed from v0.9.7. This change is a measure according to the
_webServer->client().flush(); // implementation of the arduino-esp32 1.0.1 core.
#if defined(ARDUINO_ARCH_ESP32)
// In ESP32, the station IP address just established could not be reached.
redirect += _webServer->client().localIP().toString();
#elif defined(ARDUINO_ARCH_ESP8266)
// In ESP8266, the host address that responds for the connection
// successful is the IP address of ESP8266 as a station.
// This is the specification as before.
redirect += _currentHostIP.toString();
#endif
redirect += _redirectURI;
_webServer->sendHeader(F("Location"), redirect, true);
_webServer->send(302, F("text/plain"), "");
_webServer->client().stop(); _webServer->client().stop();
_webServer->client().flush();
_waitForEndTransmission(); // Wait for response transmission complete
_responsePage->cancel(); _responsePage->cancel();
return String(""); return String("");
} }
@ -724,8 +776,8 @@ bool AutoConnect::_classifyHandle(HTTPMethod method, String uri) {
_portalAccessPeriod = millis(); _portalAccessPeriod = millis();
AC_DBG("Host:%s, URI:%s", _webServer->hostHeader().c_str(), uri.c_str()); AC_DBG("Host:%s, URI:%s", _webServer->hostHeader().c_str(), uri.c_str());
// When handleClient calls RequestHandler, the parsed http argument remains // When handleClient calls RequestHandler, the parsed http argument
// the previous request. // remains the previous request.
// If the current request argument contains AutoConnectElement, it is // If the current request argument contains AutoConnectElement, it is
// the form data of the AutoConnectAux page and with this timing save // the form data of the AutoConnectAux page and with this timing save
// the value of each element. // the value of each element.
@ -796,7 +848,7 @@ bool AutoConnect::_isIP(String ipStr) {
/** /**
* Convert MAC address in uint8_t array to Sting XX:XX:XX:XX:XX:XX format. * Convert MAC address in uint8_t array to Sting XX:XX:XX:XX:XX:XX format.
* @param mac Array of MAC address 6 bytes. * @param mac Array of MAC address 6 bytes.
* @retval MAC address string in XX:XX:XX:XX:XX:XX format. * @return MAC address string in XX:XX:XX:XX:XX:XX format.
*/ */
String AutoConnect::_toMACAddressString(const uint8_t mac[]) { String AutoConnect::_toMACAddressString(const uint8_t mac[]) {
String macAddr = String(""); String macAddr = String("");
@ -813,7 +865,7 @@ String AutoConnect::_toMACAddressString(const uint8_t mac[]) {
/** /**
* Convert dBm to the wifi signal quality. * Convert dBm to the wifi signal quality.
* @param rssi dBm. * @param rssi dBm.
* @retval a signal quality percentage. * @return A signal quality percentage.
*/ */
unsigned int AutoConnect::_toWiFiQuality(int32_t rssi) { unsigned int AutoConnect::_toWiFiQuality(int32_t rssi) {
unsigned int qu; unsigned int qu;
@ -831,7 +883,7 @@ unsigned int AutoConnect::_toWiFiQuality(int32_t rssi) {
/** /**
* Wait for establishment of the connection until the specified time expires. * Wait for establishment of the connection until the specified time expires.
* @param timeout Expiration time by millisecond unit. * @param timeout Expiration time by millisecond unit.
* @retval wl_status_t * @return wl_status_t
*/ */
wl_status_t AutoConnect::_waitForConnect(unsigned long timeout) { wl_status_t AutoConnect::_waitForConnect(unsigned long timeout) {
wl_status_t wifiStatus; wl_status_t wifiStatus;
@ -850,6 +902,60 @@ wl_status_t AutoConnect::_waitForConnect(unsigned long timeout) {
return wifiStatus; return wifiStatus;
} }
/**
* Control the automatic reconnection behaves. Reconnection behavior
* to the AP connected during captive portal operation is activated
* by an order as the argument.
* @param order AC_RECONNECT_SET or AC_RECONNECT_RESET
*/
void AutoConnect::_setReconnect(const AC_STARECONNECT_t order) {
#if defined(ARDUINO_ARCH_ESP32)
if (order == AC_RECONNECT_SET) {
_disconnectEventId = WiFi.onEvent([](WiFiEvent_t e, WiFiEventInfo_t info) {
AC_DBG("STA lost connection:%d\n", info.disconnected.reason);
bool rst = WiFi.reconnect();
AC_DBG("STA connection %s\n", rst ? "restored" : "failed");
}, WiFiEvent_t::SYSTEM_EVENT_AP_STADISCONNECTED);
AC_DBG("Event<%d> handler registered\n", static_cast<int>(WiFiEvent_t::SYSTEM_EVENT_AP_STADISCONNECTED));
}
else if (order == AC_RECONNECT_RESET) {
if (_disconnectEventId) {
WiFi.removeEvent(_disconnectEventId);
AC_DBG("Event<%d> handler released\n", static_cast<int>(WiFiEvent_t::SYSTEM_EVENT_AP_STADISCONNECTED));
}
}
#elif defined(ARDUINO_ARCH_ESP8266)
bool strc = order == AC_RECONNECT_SET ? true : false;
WiFi.setAutoReconnect(strc);
AC_DBG("STA reconnection:%s\n", strc ? "EN" : "DIS");
#endif
}
/**
* Wait for the end of transmission of the http response by closed
* from the http client.
*/
void AutoConnect::_waitForEndTransmission() {
#ifdef AC_DEBUG
AC_DBG("Leaves:");
unsigned long lt = millis();
#endif
while (_webServer->client().connected()) {
delay(1);
yield();
}
#ifdef AC_DEBUG
// Notifies of the time taken to end the session. If the http client
// times out, AC_DEBUG must be enabled and it is necessary to confirm
// that the http response is being transmitted correctly.
// To trace the correctness the close sequence of TCP connection,
// enable the debug log of the Arduino core side. If the normal,
// a message of closed the TCP connection will be logged between
// "Leaves:" and "the time taken to end the session" of the log.
AC_DBG_DUMB("%d[ms]\n", static_cast<int>(millis() - lt));
#endif
}
/** /**
* Disconnects the station from an associated access point. * Disconnects the station from an associated access point.
* @param wifiOff The station mode turning switch. * @param wifiOff The station mode turning switch.
@ -860,6 +966,8 @@ void AutoConnect::_disconnectWiFi(bool wifiOff) {
#elif defined(ARDUINO_ARCH_ESP32) #elif defined(ARDUINO_ARCH_ESP32)
WiFi.disconnect(wifiOff, true); WiFi.disconnect(wifiOff, true);
#endif #endif
while (WiFi.status() == WL_CONNECTED) while (WiFi.status() == WL_CONNECTED) {
delay(100); delay(1);
yield();
}
} }

@ -3,7 +3,7 @@
* @file AutoConnect.h * @file AutoConnect.h
* @author hieromon@gmail.com * @author hieromon@gmail.com
* @version 0.9.7 * @version 0.9.7
* @date 2018-11-17 * @date 2019-01-21
* @copyright MIT license. * @copyright MIT license.
*/ */
@ -199,6 +199,10 @@ class AutoConnect {
AC_WEBSERVER_HOSTED AC_WEBSERVER_HOSTED
}; };
typedef enum _webServerAllocateType AC_WEBSERVER_TYPE; typedef enum _webServerAllocateType AC_WEBSERVER_TYPE;
typedef enum {
AC_RECONNECT_SET,
AC_RECONNECT_RESET
} AC_STARECONNECT_t;
void _initialize(); void _initialize();
bool _config(); bool _config();
void _startWebServer(); void _startWebServer();
@ -224,7 +228,9 @@ class AutoConnect {
bool _hasTimeout(unsigned long timeout); bool _hasTimeout(unsigned long timeout);
bool _isIP(String ipStr); bool _isIP(String ipStr);
wl_status_t _waitForConnect(unsigned long timeout); wl_status_t _waitForConnect(unsigned long timeout);
void _waitForEndTransmission(void);
void _disconnectWiFi(bool wifiOff); void _disconnectWiFi(bool wifiOff);
void _setReconnect(const AC_STARECONNECT_t order);
/** Utilities */ /** Utilities */
static uint32_t _getChipId(); static uint32_t _getChipId();
@ -262,6 +268,9 @@ class AutoConnect {
bool _rfDisconnect; /**< URI /disc requested */ bool _rfDisconnect; /**< URI /disc requested */
bool _rfReset; /**< URI /reset requested */ bool _rfReset; /**< URI /reset requested */
wl_status_t _rsConnect; /**< connection result */ wl_status_t _rsConnect; /**< connection result */
#ifdef ARDUINO_ARCH_ESP32
WiFiEventId_t _disconnectEventId; /**< STA disconnection event handler registered id */
#endif
/** HTTP header information of the currently requested page. */ /** HTTP header information of the currently requested page. */
String _uri; /**< Requested URI */ String _uri; /**< Requested URI */
@ -276,6 +285,7 @@ class AutoConnect {
static const char _CSS_INPUT_BUTTON[] PROGMEM; static const char _CSS_INPUT_BUTTON[] PROGMEM;
static const char _CSS_INPUT_TEXT[] PROGMEM; static const char _CSS_INPUT_TEXT[] PROGMEM;
static const char _CSS_TABLE[] PROGMEM; static const char _CSS_TABLE[] PROGMEM;
static const char _CSS_SPINNER[] PROGMEM;
static const char _CSS_LUXBAR[] PROGMEM; static const char _CSS_LUXBAR[] PROGMEM;
static const char _ELM_HTML_HEAD[] PROGMEM; static const char _ELM_HTML_HEAD[] PROGMEM;
static const char _ELM_MENU_PRE[] PROGMEM; static const char _ELM_MENU_PRE[] PROGMEM;
@ -283,6 +293,7 @@ class AutoConnect {
static const char _ELM_MENU_POST[] PROGMEM; static const char _ELM_MENU_POST[] PROGMEM;
static const char _PAGE_STAT[] PROGMEM; static const char _PAGE_STAT[] PROGMEM;
static const char _PAGE_CONFIGNEW[] PROGMEM; static const char _PAGE_CONFIGNEW[] PROGMEM;
static const char _PAGE_CONNECTING[] PROGMEM;
static const char _PAGE_OPENCREDT[] PROGMEM; static const char _PAGE_OPENCREDT[] PROGMEM;
static const char _PAGE_SUCCESS[] PROGMEM; static const char _PAGE_SUCCESS[] PROGMEM;
static const char _PAGE_RESETTING[] PROGMEM; static const char _PAGE_RESETTING[] PROGMEM;
@ -297,6 +308,7 @@ class AutoConnect {
String _token_CSS_INPUT_BUTTON(PageArgument& args); String _token_CSS_INPUT_BUTTON(PageArgument& args);
String _token_CSS_INPUT_TEXT(PageArgument& args); String _token_CSS_INPUT_TEXT(PageArgument& args);
String _token_CSS_TABLE(PageArgument& args); String _token_CSS_TABLE(PageArgument& args);
String _token_CSS_SPINNER(PageArgument& args);
String _token_CSS_LUXBAR(PageArgument& args); String _token_CSS_LUXBAR(PageArgument& args);
String _token_HEAD(PageArgument& args); String _token_HEAD(PageArgument& args);
String _token_MENU_PRE(PageArgument& args); String _token_MENU_PRE(PageArgument& args);
@ -323,6 +335,8 @@ class AutoConnect {
String _token_OPEN_SSID(PageArgument& args); String _token_OPEN_SSID(PageArgument& args);
String _token_UPTIME(PageArgument& args); String _token_UPTIME(PageArgument& args);
String _token_BOOTURI(PageArgument& args); String _token_BOOTURI(PageArgument& args);
String _token_CURRENT_SSID(PageArgument& args);
String _token_RESULT_URI(PageArgument& args);
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
friend class ESP8266WebServer; friend class ESP8266WebServer;

@ -86,21 +86,26 @@
#define AUTOCONNECT_URI_SUCCESS AUTOCONNECT_URI "/success" #define AUTOCONNECT_URI_SUCCESS AUTOCONNECT_URI "/success"
#define AUTOCONNECT_URI_FAIL AUTOCONNECT_URI "/fail" #define AUTOCONNECT_URI_FAIL AUTOCONNECT_URI "/fail"
// Time-out limitation when AutoConnect::begin // Time-out limitation when AutoConnect::begin [ms]
#ifndef AUTOCONNECT_TIMEOUT #ifndef AUTOCONNECT_TIMEOUT
#define AUTOCONNECT_TIMEOUT 30000 #define AUTOCONNECT_TIMEOUT 30000
#endif // !AUTOCONNECT_TIMEOUT #endif // !AUTOCONNECT_TIMEOUT
// Captive portal timeout value // Captive portal timeout value [ms]
#ifndef AUTOCONNECT_CAPTIVEPORTAL_TIMEOUT #ifndef AUTOCONNECT_CAPTIVEPORTAL_TIMEOUT
#define AUTOCONNECT_CAPTIVEPORTAL_TIMEOUT 0 #define AUTOCONNECT_CAPTIVEPORTAL_TIMEOUT 0
#endif // !AUTOCONNECT_CAPTIVEPORTAL_TIMEOUT #endif // !AUTOCONNECT_CAPTIVEPORTAL_TIMEOUT
// Advance wait time // Advance wait time [s]
#ifndef AUTOCONNECT_STARTUPTIME #ifndef AUTOCONNECT_STARTUPTIME
#define AUTOCONNECT_STARTUPTIME (AUTOCONNECT_TIMEOUT/1000) #define AUTOCONNECT_STARTUPTIME (AUTOCONNECT_TIMEOUT/1000)
#endif // !AUTOCONNECT_STARTUPTIME #endif // !AUTOCONNECT_STARTUPTIME
// Response wait time until requesting a result of connection attempt [s] as String
#ifndef AUTOCONNECT_RESPONSEREQUEST_TIMEOUT
#define AUTOCONNECT_RESPONSEREQUEST_TIMEOUT "7"
#endif // !AUTOCONNECT_RESPONSEREQUEST_TIMEOUT
// Default HTTP port // Default HTTP port
#ifndef AUTOCONNECT_HTTPPORT #ifndef AUTOCONNECT_HTTPPORT
#define AUTOCONNECT_HTTPPORT 80 #define AUTOCONNECT_HTTPPORT 80
@ -111,6 +116,11 @@
#define AUTOCONNECT_DNSPORT 53 #define AUTOCONNECT_DNSPORT 53
#endif // !AUTOCONNECT_DNSPORT #endif // !AUTOCONNECT_DNSPORT
// http response transfer method
#ifndef AUTOCONNECT_HTTP_TRANSFER
#define AUTOCONNECT_HTTP_TRANSFER PB_ByteStream
#endif // !AUTOCONNECT_HTTP_TRANSFER
// Explicitly avoiding unused warning with token handler of PageBuilder // Explicitly avoiding unused warning with token handler of PageBuilder
#define AC_UNUSED(expr) do { (void)(expr); } while (0) #define AC_UNUSED(expr) do { (void)(expr); } while (0)

@ -270,6 +270,45 @@ const char AutoConnect::_CSS_TABLE[] PROGMEM = {
"}" "}"
}; };
/**< SVG animation for spinner */
const char AutoConnect::_CSS_SPINNER[] PROGMEM = {
".spinner{"
"width:40px;"
"height:40px;"
"position:relative;"
"margin:100px auto;"
"}"
".double-bounce1, .double-bounce2{"
"width:100%;"
"height:100%;"
"border-radius:50%;"
"background-color:#333;"
"opacity:0.6;"
"position:absolute;"
"top:0;"
"left:0;"
"-webkit-animation:sk-bounce 2.0s infinite ease-in-out;"
"animation:sk-bounce 2.0s infinite ease-in-out;"
"}"
".double-bounce2{"
"-webkit-animation-delay: -1.0s;"
"animation-delay: -1.0s;"
"}"
"@-webkit-keyframes sk-bounce{"
"0%, 100% {-webkit-transform:scale(0.0)}"
"50% {-webkit-transform:scale(1.0)}"
"}"
"@keyframes sk-bounce{"
"0%, 100% {"
"transform:scale(0.0);"
"-webkit-transform:scale(0.0);"
"} 50% {"
"transform:scale(1.0);"
"-webkit-transform:scale(1.0);"
"}"
"}"
};
/**< Common menu bar. This style quotes LuxBar. */ /**< Common menu bar. This style quotes LuxBar. */
/**< balzss/luxbar is licensed under the MIT License https://github.com/balzss/luxbar */ /**< balzss/luxbar is licensed under the MIT License https://github.com/balzss/luxbar */
const char AutoConnect::_CSS_LUXBAR[] PROGMEM = { const char AutoConnect::_CSS_LUXBAR[] PROGMEM = {
@ -668,6 +707,35 @@ const char AutoConnect::_PAGE_OPENCREDT[] PROGMEM = {
"</html>" "</html>"
}; };
/**< A page that informs during a connection attempting. */
const char AutoConnect::_PAGE_CONNECTING[] PROGMEM = {
"{{REQ}}"
"{{HEAD}}"
"<meta http-equiv=\"refresh\" content=\"0;url=http://{{URI_RESULT}}\">"
"<title>AutoConnect connecting</title>"
"<style type=\"text/css\">"
"{{CSS_BASE}}"
"{{CSS_SPINNER}}"
"{{CSS_LUXBAR}}"
"</style>"
"<script type=\"text/javascript\">"
"setTimeout(\"link()\"," AUTOCONNECT_RESPONSEREQUEST_TIMEOUT " );"
"function link(){"
"location.href='http://{{URI_RESULT}}';"
"</script>"
"</head>"
"<body style=\"padding-top:58px;\">"
"<div class=\"container\">"
"{{MENU_PRE}}"
"{{MENU_POST}}"
"<div class=\"spinner\">"
"<div style=\"position:absolute;left:-100%;right:-100%;text-align:center;margin:10px auto;font-weight:bold;color:#4169e1;\">{{CUR_SSID}}</div>"
"<div class=\"double-bounce1\"></div>"
"<div class=\"double-bounce2\"></div>"
"</body>"
"</html>"
};
/**< A page announcing that a connection has been established. */ /**< A page announcing that a connection has been established. */
const char AutoConnect::_PAGE_SUCCESS[] PROGMEM = { const char AutoConnect::_PAGE_SUCCESS[] PROGMEM = {
"{{HEAD}}" "{{HEAD}}"
@ -802,6 +870,7 @@ String AutoConnect::_token_CSS_ICON_LOCK(PageArgument& args) {
AC_UNUSED(args); AC_UNUSED(args);
return String(FPSTR(_CSS_ICON_LOCK)); return String(FPSTR(_CSS_ICON_LOCK));
} }
String AutoConnect::_token_CSS_INPUT_BUTTON(PageArgument& args) { String AutoConnect::_token_CSS_INPUT_BUTTON(PageArgument& args) {
AC_UNUSED(args); AC_UNUSED(args);
return String(FPSTR(_CSS_INPUT_BUTTON)); return String(FPSTR(_CSS_INPUT_BUTTON));
@ -817,6 +886,11 @@ String AutoConnect::_token_CSS_TABLE(PageArgument& args) {
return String(FPSTR(_CSS_TABLE)); return String(FPSTR(_CSS_TABLE));
} }
String AutoConnect::_token_CSS_SPINNER(PageArgument& args) {
AC_UNUSED(args);
return String(FPSTR(_CSS_SPINNER));
}
String AutoConnect::_token_HEAD(PageArgument& args) { String AutoConnect::_token_HEAD(PageArgument& args) {
AC_UNUSED(args); AC_UNUSED(args);
return String(FPSTR(_ELM_HTML_HEAD)); return String(FPSTR(_ELM_HTML_HEAD));
@ -1093,6 +1167,15 @@ String AutoConnect::_token_BOOTURI(PageArgument& args) {
return String(""); return String("");
} }
String AutoConnect::_token_CURRENT_SSID(PageArgument& args) {
AC_UNUSED(args);
return String(reinterpret_cast<char*>(_credential.ssid));
}
String AutoConnect::_token_RESULT_URI(PageArgument& args) {
AC_UNUSED(args);
return _webServer->client().localIP().toString() + String(AUTOCONNECT_URI_RESULT);
}
/** /**
* This function dynamically build up the response pages that conform to * This function dynamically build up the response pages that conform to
@ -1156,8 +1239,16 @@ PageElement* AutoConnect::_setupPage(String uri) {
else if (uri == String(AUTOCONNECT_URI_CONNECT)) { else if (uri == String(AUTOCONNECT_URI_CONNECT)) {
// Setup /auto/connect // Setup /auto/connect
elm->setMold("{{REQ}}"); elm->setMold(_PAGE_CONNECTING);
elm->addToken(String(FPSTR("REQ")), std::bind(&AutoConnect::_induceConnect, this, std::placeholders::_1)); elm->addToken(String(FPSTR("REQ")), std::bind(&AutoConnect::_induceConnect, this, std::placeholders::_1));
elm->addToken(String(FPSTR("HEAD")), std::bind(&AutoConnect::_token_HEAD, this, std::placeholders::_1));
elm->addToken(String(FPSTR("URI_RESULT")), std::bind(&AutoConnect::_token_RESULT_URI, this, std::placeholders::_1));
elm->addToken(String(FPSTR("CSS_BASE")), std::bind(&AutoConnect::_token_CSS_BASE, this, std::placeholders::_1));
elm->addToken(String(FPSTR("CSS_SPINNER")), std::bind(&AutoConnect::_token_CSS_SPINNER, this, std::placeholders::_1));
elm->addToken(String(FPSTR("CSS_LUXBAR")), std::bind(&AutoConnect::_token_CSS_LUXBAR, this, std::placeholders::_1));
elm->addToken(String(FPSTR("MENU_PRE")), std::bind(&AutoConnect::_token_MENU_PRE, this, std::placeholders::_1));
elm->addToken(String(FPSTR("MENU_POST")), std::bind(&AutoConnect::_token_MENU_POST, this, std::placeholders::_1));
elm->addToken(String(FPSTR("CUR_SSID")), std::bind(&AutoConnect::_token_CURRENT_SSID, this, std::placeholders::_1));
} }
else if (uri == String(AUTOCONNECT_URI_OPEN)) { else if (uri == String(AUTOCONNECT_URI_OPEN)) {

Loading…
Cancel
Save