Supports AutoConnectAux

pull/41/head
Hieromon Ikasamo 6 years ago
parent 4de6dfb101
commit dbbb220ffa
  1. 10
      README.md
  2. 8
      keywords.txt
  3. 34
      src/AutoConnect.cpp
  4. 6
      src/AutoConnect.h
  5. 58
      src/AutoConnectAux.cpp
  6. 48
      src/AutoConnectAux.h
  7. 6
      src/AutoConnectPage.cpp

@ -32,6 +32,10 @@ AutoConnect can be embedded easily into your sketch, just "**begin**" and "**han
The sketches which provide the web page using ESP8266WebServer/WebServer there is, AutoConnect will not disturb it. AutoConnect can use an already instantiated ESP8266WebServer object(ESP8266) or WebServer object(ESP32), or itself can assign it. The sketches which provide the web page using ESP8266WebServer/WebServer there is, AutoConnect will not disturb it. AutoConnect can use an already instantiated ESP8266WebServer object(ESP8266) or WebServer object(ESP32), or itself can assign it.
### Adding the user-owned web screen can easily
You can easily add your own web screen with sketch. It can be called from the AutoConnect menu and parameters can be passed.
## Supported hardware ## Supported hardware
Apply the [Arduino core](https://github.com/esp8266/Arduino) of the ESP8266 Community. Apply the [Arduino core](https://github.com/esp8266/Arduino) of the ESP8266 Community.
@ -81,7 +85,11 @@ Full documentation is available on https://Hieromon.github.io/AutoConnect, some
## Change log ## Change log
### [0.9.6] Sep. 27, 2018 ### [0.9.7] Nov. 11, 2018
- Supports addition of AutoConnect menu with user sketch by AutoConnectAux implementation.
- Supports AutoConnectConfig::immediateStart option, to start the portal immediately without first trying WiFi.begin.
### [0.9.6] Sept. 27, 2018
- Improvement of RSSI detection for saved SSIDs. - Improvement of RSSI detection for saved SSIDs.
- Fixed disconnection SoftAP completely at the first connection phase of the AutoConnect::begin. - Fixed disconnection SoftAP completely at the first connection phase of the AutoConnect::begin.

@ -9,10 +9,15 @@ AutoConnectText KEYWORD1
AutoConnectInput KEYWORD1 AutoConnectInput KEYWORD1
AutoConnectButon KEYWORD1 AutoConnectButon KEYWORD1
AutoConnectSubmit KEYWORD1 AutoConnectSubmit KEYWORD1
ACText KEYWORD1
ACInput KEYWORD1
ACButton KEYWORD1
ACSubmit KEYWORD1
####################################### #######################################
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
####################################### #######################################
add KEYWORD2
config KEYWORD2 config KEYWORD2
begin KEYWORD2 begin KEYWORD2
del KEYWORD2 del KEYWORD2
@ -24,9 +29,12 @@ home KEYWORD2
host KEYWORD2 host KEYWORD2
join KEYWORD2 join KEYWORD2
load KEYWORD2 load KEYWORD2
name KEYWORD2
on KEYWORD2
onDetect KEYWORD2 onDetect KEYWORD2
onNotFound KEYWORD2 onNotFound KEYWORD2
save KEYWORD2 save KEYWORD2
value KEYWORD2
####################################### #######################################
# Literals (KEYWORD3) # Literals (KEYWORD3)

@ -111,13 +111,23 @@ bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long
AC_DBG("DHCP client(%s)\n", wifi_station_dhcpc_status() == DHCP_STOPPED ? "STOPPED" : "STARTED"); AC_DBG("DHCP client(%s)\n", wifi_station_dhcpc_status() == DHCP_STOPPED ? "STOPPED" : "STARTED");
#endif #endif
// Try to connect by STA immediately. // If the portal is requested promptly skip the first WiFi.begin and
if (ssid == nullptr && passphrase == nullptr) // immediately start the portal.
WiFi.begin(); if (_apConfig.immediateStart) {
else cs = false;
WiFi.begin(ssid, passphrase); _apConfig.autoReconnect = false;
AC_DBG("WiFi.begin(%s%s%s)\n", ssid == nullptr ? "" : ssid, passphrase == nullptr ? "" : ",", passphrase == nullptr ? "" : passphrase); _apConfig.autoRise = true;
cs = _waitForConnect(_portalTimeout) == WL_CONNECTED; AC_DBG("Start the portal immediately\n");
}
else {
// Try to connect by STA immediately.
if (ssid == nullptr && passphrase == nullptr)
WiFi.begin();
else
WiFi.begin(ssid, passphrase);
AC_DBG("WiFi.begin(%s%s%s)\n", ssid == nullptr ? "" : ssid, passphrase == nullptr ? "" : ",", passphrase == nullptr ? "" : passphrase);
cs = _waitForConnect(_portalTimeout) == WL_CONNECTED;
}
// Reconnect with a valid credential as the autoReconnect option is enabled. // Reconnect with a valid credential as the autoReconnect option is enabled.
if (!cs && _apConfig.autoReconnect && (ssid == nullptr && passphrase == nullptr)) { if (!cs && _apConfig.autoReconnect && (ssid == nullptr && passphrase == nullptr)) {
@ -264,14 +274,12 @@ WebServerClass& AutoConnect::host() {
* the auxiliary page to be added. * the auxiliary page to be added.
*/ */
void AutoConnect::join(AutoConnectAux& aux) { void AutoConnect::join(AutoConnectAux& aux) {
if (_aux) { aux._join(*this);
AutoConnectAux* addon = _aux.get(); AC_DBG("%s on hands\n", aux.uri());
addon->_append(aux); if (_aux)
} _aux->_concat(aux);
else else
_aux.reset(&aux); _aux.reset(&aux);
aux._join(*this);
AC_DBG("%s contained\n", aux._uri);
} }
/** /**

@ -132,6 +132,7 @@ class AutoConnectConfig {
autoRise(true), autoRise(true),
autoReset(true), autoReset(true),
autoReconnect(false), autoReconnect(false),
immediateStart(false),
homeUri(AUTOCONNECT_HOMEURI), homeUri(AUTOCONNECT_HOMEURI),
staip(0U), staip(0U),
staGateway(0U), staGateway(0U),
@ -155,6 +156,7 @@ class AutoConnectConfig {
autoRise(true), autoRise(true),
autoReset(true), autoReset(true),
autoReconnect(false), autoReconnect(false),
immediateStart(false),
homeUri(AUTOCONNECT_HOMEURI), homeUri(AUTOCONNECT_HOMEURI),
staip(0U), staip(0U),
staGateway(0U), staGateway(0U),
@ -178,6 +180,7 @@ class AutoConnectConfig {
autoRise = o.autoRise; autoRise = o.autoRise;
autoReset = o.autoReset; autoReset = o.autoReset;
autoReconnect = o.autoReconnect; autoReconnect = o.autoReconnect;
immediateStart = o.immediateStart;
homeUri = o.homeUri; homeUri = o.homeUri;
staip = o.staip; staip = o.staip;
staGateway = o.staGateway; staGateway = o.staGateway;
@ -200,6 +203,7 @@ class AutoConnectConfig {
bool autoRise; /**< Automatic starting the captive portal */ bool autoRise; /**< Automatic starting the captive portal */
bool autoReset; /**< Reset ESP8266 module automatically when WLAN disconnected. */ bool autoReset; /**< Reset ESP8266 module automatically when WLAN disconnected. */
bool autoReconnect; /**< Automatic reconnect with past SSID */ bool autoReconnect; /**< Automatic reconnect with past SSID */
bool immediateStart; /**< Skips WiFi.begin(), start portal immediately */
String homeUri; /**< A URI of user site */ String homeUri; /**< A URI of user site */
IPAddress staip; /**< Station static IP address */ IPAddress staip; /**< Station static IP address */
IPAddress staGateway; /**< Station gateway address */ IPAddress staGateway; /**< Station gateway address */
@ -208,8 +212,6 @@ class AutoConnectConfig {
IPAddress dns2; /**< Secondary DNS server */ IPAddress dns2; /**< Secondary DNS server */
}; };
class AutoConnectAux;
class AutoConnect { class AutoConnect {
public: public:
AutoConnect(); AutoConnect();

@ -10,7 +10,20 @@
#include "AutoConnect.h" #include "AutoConnect.h"
#include "AutoConnectAux.h" #include "AutoConnectAux.h"
/**
* Template for auxiliary page composed with AutoConnectAux of user sketch.
*
* The structure of the auxiliary page depends on this template for
* the purpose to be incorporated into the AutoConnect Menu.
* The page element implemented by AutoConnectElement is placed at the
* position of {{AUX_ELEMENT}} token. This token is contained in a
* <div> block with a class defined in 'base-panel' and is held by a
* <form> element with an ID '_aux'.
* The JavaScript that named 'sa' at the end of the template determines
* the behavior of AutoConnectSubmit.
*/
const char AutoConnectAux::_PAGE_AUX[] PROGMEM = { const char AutoConnectAux::_PAGE_AUX[] PROGMEM = {
"{{EXIT_HANDLE}}"
"{{HEAD}}" "{{HEAD}}"
"<title>{{AUX_TITLE}}</title>" "<title>{{AUX_TITLE}}</title>"
"<style type=\"text/css\">" "<style type=\"text/css\">"
@ -57,7 +70,7 @@ AutoConnectAux::~AutoConnectAux() {
void AutoConnectAux::add(AutoConnectElement& addon) { void AutoConnectAux::add(AutoConnectElement& addon) {
_addonElm.push_back(addon); _addonElm.push_back(addon);
AC_DBG("ELM:%s placed\n", addon.name().c_str()); AC_DBG("%s placed in %s\n", addon.name().c_str(), uri());
} }
void AutoConnectAux::add(AutoConnectElementVT addons) { void AutoConnectAux::add(AutoConnectElementVT addons) {
@ -65,21 +78,17 @@ void AutoConnectAux::add(AutoConnectElementVT addons) {
add(addons[n]); add(addons[n]);
} }
void AutoConnectAux::_append(AutoConnectAux& aux) { void AutoConnectAux::_concat(AutoConnectAux& aux) {
if (_next) { if (_next)
AutoConnectAux* next = _next.get(); _next->_concat(aux);
next->_append(aux);
}
else else
_next.reset(&aux); _next.reset(&aux);
} }
void AutoConnectAux::_join(AutoConnect& ac) { void AutoConnectAux::_join(AutoConnect& ac) {
_ac.reset(&ac); _ac.reset(&ac);
if (_next) { if (_next)
AutoConnectAux *next = _next.get(); _next->_join(ac);
next->_join(ac);
}
} }
const String AutoConnectAux::_insertElement(PageArgument& args) { const String AutoConnectAux::_insertElement(PageArgument& args) {
@ -105,10 +114,16 @@ PageElement* AutoConnectAux::_setupPage(String uri) {
elm = new PageElement(); elm = new PageElement();
// Overwrite the default menu title // Overwrite the default menu title
mother->_menuTitle = _title; if (_title.length()) {
mother->_menuTitle = _title;
_activeTitle = String();
}
else
_activeTitle = mother->_menuTitle;
// Construct the auxiliary page // Construct the auxiliary page
elm->setMold(_PAGE_AUX); elm->setMold(_PAGE_AUX);
elm->addToken(PSTR("EXIT_HANDLE"), std::bind(&AutoConnectAux::_exitHandle, this, std::placeholders::_1));
elm->addToken(PSTR("HEAD"), std::bind(&AutoConnect::_token_HEAD, mother, std::placeholders::_1)); elm->addToken(PSTR("HEAD"), std::bind(&AutoConnect::_token_HEAD, mother, std::placeholders::_1));
elm->addToken(PSTR("AUX_TITLE"), std::bind(&AutoConnectAux::_injectTitle, this, std::placeholders::_1)); elm->addToken(PSTR("AUX_TITLE"), std::bind(&AutoConnectAux::_injectTitle, this, std::placeholders::_1));
elm->addToken(PSTR("CSS_BASE"), std::bind(&AutoConnect::_token_CSS_BASE, mother, std::placeholders::_1)); elm->addToken(PSTR("CSS_BASE"), std::bind(&AutoConnect::_token_CSS_BASE, mother, std::placeholders::_1));
@ -125,11 +140,20 @@ PageElement* AutoConnectAux::_setupPage(String uri) {
return elm; return elm;
} }
const String AutoConnectAux::_injectMenu(PageArgument& args) { const String AutoConnectAux::_injectMenu(PageArgument& args) {
String menuItem = String(FPSTR("<li class=\"luxbar-item\"><a href=\"")) + String(_uri) + String("\">") + _title + String(FPSTR("</a></li>")); String menuItem;
if (_next) {
AutoConnectAux* next = _next.get(); if (_title.length())
menuItem += next->_injectMenu(args); menuItem = String(FPSTR("<li class=\"luxbar-item\"><a href=\"")) + String(_uri) + String("\">") + _title + String(FPSTR("</a></li>"));
} if (_next)
menuItem += _next->_injectMenu(args);
return menuItem; return menuItem;
} }
const String AutoConnectAux::_exitHandle(PageArgument& args) {
if (_handler) {
AC_DBG("CB %s\n", *this->uri());
(*_handler)(*this, args);
}
return "";
}

@ -20,11 +20,13 @@ class AutoConnect; // Reference to avoid circular
// Add-on element base class // Add-on element base class
class AutoConnectElement { class AutoConnectElement {
public: public:
explicit AutoConnectElement(const char* name = nullptr, const char* value = nullptr) : _name(String(name)), _value(String(value)) {} explicit AutoConnectElement(const char* name, const char* value) : _name(String(name)), _value(String(value)) {}
virtual ~AutoConnectElement() {} virtual ~AutoConnectElement() {}
const String name(void) const { return _name; } /**< Element name */ const String name(void) const { return _name; } /**< Element name */
const String value(void) const { return _value; } /**< Element value */ const String value(void) const { return _value; } /**< Element value */
virtual const String toHTML(void) { return String(); }; /**< HTML code to be generated */ virtual const String toHTML(void) { return String(); }; /**< HTML code to be generated */
void setValue(const char* value) { _value = String(value); }
void setValue(const String value) { _value = value; }
protected: protected:
String _name; String _name;
@ -42,9 +44,11 @@ class AutoConnectElement {
*/ */
class AutoConnectText : public AutoConnectElement { class AutoConnectText : public AutoConnectElement {
public: public:
explicit AutoConnectText(const char* name = nullptr, const char* value = nullptr, const char* style = nullptr) : AutoConnectElement(name, value), _style(String(style)) {} explicit AutoConnectText(const char* name = "", const char* value = "", const char* style = "") : AutoConnectElement(name, value), _style(String(style)) {}
~AutoConnectText() {} ~AutoConnectText() {}
const String style(void) const { return _style; } /**< A modify of HTML 'style' code */ const String style(void) const { return _style; } /**< A modify of HTML 'style' code */
void setStyle(const char* style) { _style = String(style); }
void setStyle(const String style) { _style = style; }
const String toHTML(void) { const String toHTML(void) {
return String(FPSTR("<div style=\"")) + _style + String("\">") + _value + String(FPSTR("</div>")); return String(FPSTR("<div style=\"")) + _style + String("\">") + _value + String(FPSTR("</div>"));
} }
@ -63,9 +67,11 @@ class AutoConnectText : public AutoConnectElement {
*/ */
class AutoConnectInput : public AutoConnectElement { class AutoConnectInput : public AutoConnectElement {
public: public:
explicit AutoConnectInput(const char* name = nullptr, const char* value = nullptr, const char* label = nullptr) : AutoConnectElement(name, value), _label(String(label)) {} explicit AutoConnectInput(const char* name = "", const char* value = "", const char* label = "") : AutoConnectElement(name, value), _label(String(label)) {}
~AutoConnectInput() {} ~AutoConnectInput() {}
const String label(void) const { return _label; } const String label(void) const { return _label; }
void setLabel(const char* label) { _label = String(label); }
void setLabel(const String label) { _label = label; }
const String toHTML(void) { const String toHTML(void) {
return _label + String(FPSTR("<input type=\"text\" name=\"")) + _name + String(FPSTR("\" value=\"")) + _value + String("\"><br>"); return _label + String(FPSTR("<input type=\"text\" name=\"")) + _name + String(FPSTR("\" value=\"")) + _value + String("\"><br>");
} }
@ -83,8 +89,10 @@ class AutoConnectInput : public AutoConnectElement {
*/ */
class AutoConnectButton : public AutoConnectElement { class AutoConnectButton : public AutoConnectElement {
public: public:
explicit AutoConnectButton(const char* name = nullptr, const char* value = nullptr, const String action = String()) : AutoConnectElement(name, value), _action(action) {} explicit AutoConnectButton(const char* name = "", const char* value = "", const String action = String()) : AutoConnectElement(name, value), _action(action) {}
const String action(void) const { return _action; } const String action(void) const { return _action; }
void setAction(const char* action) { _action = String(action); }
void setAction(const String action) { _action = action; }
const String toHTML(void) { const String toHTML(void) {
return String(FPSTR("<button type=\"button\" name=\"")) + _name + String(FPSTR("\" value=\"")) + _value + String(FPSTR("\" onclick=\"")) + _action + String("\">") + _value + String(FPSTR("</button>")); return String(FPSTR("<button type=\"button\" name=\"")) + _name + String(FPSTR("\" value=\"")) + _value + String(FPSTR("\" onclick=\"")) + _action + String("\">") + _value + String(FPSTR("</button>"));
} }
@ -104,8 +112,10 @@ class AutoConnectButton : public AutoConnectElement {
*/ */
class AutoConnectSubmit : public AutoConnectElement { class AutoConnectSubmit : public AutoConnectElement {
public: public:
explicit AutoConnectSubmit(const char* name = nullptr, const char* value = nullptr, const char* uri = nullptr) : AutoConnectElement(name, value), _uri(String(uri)) {} explicit AutoConnectSubmit(const char* name = "", const char* value = "", const char* uri = "") : AutoConnectElement(name, value), _uri(String(uri)) {}
const String uri(void) const { return _uri; } const String uri(void) const { return _uri; }
void setUri(const char* uri) { _uri = String(uri); }
void setUri(const String uri) { _uri = uri; }
const String toHTML(void) { const String toHTML(void) {
return String(FPSTR("<button type=\"submit\" name=\"")) + _name + String(FPSTR("\" value=\"")) + _value + String(FPSTR("\" onclick=\"sa('")) + _uri + String("')\">") + _value + String(FPSTR("</button>")); return String(FPSTR("<button type=\"submit\" name=\"")) + _name + String(FPSTR("\" value=\"")) + _value + String(FPSTR("\" onclick=\"sa('")) + _uri + String("')\">") + _value + String(FPSTR("</button>"));
} }
@ -114,9 +124,25 @@ class AutoConnectSubmit : public AutoConnectElement {
String _uri; String _uri;
}; };
/**
* Support declare the AutoConnectElement variable with reducing the
* arguments. These macros declare the AutoConnectElement variable
* with the same name as a "name" argument.
*/
#define ACText(name, ...) AutoConnectText name(#name, ## __VA_ARGS__)
#define ACInput(name, ...) AutoConnectInput name(#name, ## __VA_ARGS__)
#define ACButton(name, ...) AutoConnectButton name(#name, ## __VA_ARGS__)
#define ACSubmit(name, ...) AutoConnectSubmit name(#name, ## __VA_ARGS__)
// Manage placed AutoConnectElement with a vector // Manage placed AutoConnectElement with a vector
typedef std::vector<std::reference_wrapper<AutoConnectElement>> AutoConnectElementVT; typedef std::vector<std::reference_wrapper<AutoConnectElement>> AutoConnectElementVT;
// To solve the forward reference.
class AutoConnectAux;
// A type of callback function when AutoConnectAux page requested.
typedef std::function<void(AutoConnectAux&, PageArgument&)> AuxHandleFuncT;
/** /**
* A class that handles an auxiliary page with AutoConnectElement * A class that handles an auxiliary page with AutoConnectElement
* that placed on it by binding it to the AutoConnect menu. * that placed on it by binding it to the AutoConnect menu.
@ -126,26 +152,30 @@ typedef std::vector<std::reference_wrapper<AutoConnectElement>> AutoConnectEleme
*/ */
class AutoConnectAux : public PageBuilder { class AutoConnectAux : public PageBuilder {
public: public:
explicit AutoConnectAux(const char* uri = nullptr, const char* title = nullptr, const AutoConnectElementVT addons = AutoConnectElementVT()) : explicit AutoConnectAux(const char* uri = nullptr, const char* title = "", const AutoConnectElementVT addons = AutoConnectElementVT()) :
_title(String(title)), _addonElm(addons) { setUri(uri); _next.release(); _ac.release(); } _title(String(title)), _addonElm(addons) { setUri(uri); _next.release(); _ac.release(); _handler = nullptr; }
~AutoConnectAux(); ~AutoConnectAux();
void add(AutoConnectElement& addon); /**< Add an element to the auxiliary page. */ void add(AutoConnectElement& addon); /**< Add an element to the auxiliary page. */
void add(AutoConnectElementVT addons); /**< Add the element set to the auxiliary page. */ void add(AutoConnectElementVT addons); /**< Add the element set to the auxiliary page. */
void setTitle(const char* title) { _title = String(title); } /**< Set a title of the auxiliary page. */ void setTitle(const char* title) { _title = String(title); } /**< Set a title of the auxiliary page. */
void on(const AuxHandleFuncT handler) { _handler = &handler; } /**< Set user handler */
protected: protected:
void _append(AutoConnectAux& aux); void _concat(AutoConnectAux& aux);
void _join(AutoConnect& ac); void _join(AutoConnect& ac);
PageElement* _setupPage(String uri); PageElement* _setupPage(String uri);
const String _insertElement(PageArgument& args); const String _insertElement(PageArgument& args);
const String _injectTitle(PageArgument& args) { return _title; } const String _injectTitle(PageArgument& args) { return _title.length() > 0 ? _title : _activeTitle; }
const String _injectMenu(PageArgument& args); const String _injectMenu(PageArgument& args);
const String _exitHandle(PageArgument& args);
String _title; /**< A title of the page */ String _title; /**< A title of the page */
String _activeTitle; /**< Previous title of the page */
AutoConnectElementVT _addonElm; /**< A vector set of AutoConnectElements placed on this auxiliary page */ AutoConnectElementVT _addonElm; /**< A vector set of AutoConnectElements placed on this auxiliary page */
std::unique_ptr<AutoConnectAux> _next; /**< Auxiliary pages chain list */ std::unique_ptr<AutoConnectAux> _next; /**< Auxiliary pages chain list */
std::unique_ptr<AutoConnect> _ac; /**< Hosted AutoConnect instance */ std::unique_ptr<AutoConnect> _ac; /**< Hosted AutoConnect instance */
const AuxHandleFuncT* _handler; /**< User sketch callback function when AutoConnectAux page requested. */
static const char _PAGE_AUX[] PROGMEM; /**< Auxiliary page template */ static const char _PAGE_AUX[] PROGMEM; /**< Auxiliary page template */

@ -805,10 +805,8 @@ String AutoConnect::_token_MENU_PRE(PageArgument& args) {
String AutoConnect::_token_MENU_AUX(PageArgument& args) { String AutoConnect::_token_MENU_AUX(PageArgument& args) {
String menuItem; String menuItem;
if (_aux) { if (_aux)
AutoConnectAux* aux = _aux.get(); menuItem = _aux->_injectMenu(args);
menuItem = aux->_injectMenu(args);
}
return menuItem; return menuItem;
} }

Loading…
Cancel
Save