@ -0,0 +1,33 @@ |
||||
sudo: required |
||||
dist: trusty |
||||
group: deprecated-2017Q4 |
||||
language: c |
||||
env: |
||||
- BD=esp8266:esp8266:nodemcuv2:CpuFrequency=160,FlashSize=4M3M |
||||
before_install: |
||||
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" |
||||
- sleep 3 |
||||
- export DISPLAY=:1.0 |
||||
- wget http://downloads.arduino.cc/arduino-1.8.2-linux64.tar.xz |
||||
- tar xf arduino-1.8.2-linux64.tar.xz |
||||
- sudo mv arduino-1.8.2 /usr/local/share/arduino |
||||
- sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino |
||||
install: |
||||
- ln -s $PWD /usr/local/share/arduino/libraries/AutoConnect |
||||
- arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs |
||||
- arduino --install-boards esp8266:esp8266 |
||||
- arduino --board $BD --save-prefs |
||||
- arduino --pref "compiler.warning_level=all" --save-prefs |
||||
- arduino --install-library "PageBuilder" |
||||
script: |
||||
- arduino --verify --board $BD $PWD/examples/Credential/Credential.ino |
||||
- arduino --verify --board $BD $PWD/examples/FSBrowser/FSBrowser.ino |
||||
- arduino --verify --board $BD $PWD/examples/HandleClient/HandleClient.ino |
||||
- arduino --verify --board $BD $PWD/examples/HandlePortal/HandlePortal.ino |
||||
- arduino --verify --board $BD $PWD/examples/HandlePortalEX/HandlePortalEX.ino |
||||
- arduino --verify --board $BD $PWD/examples/Simple/Simple.ino |
||||
|
||||
notifications: |
||||
email: |
||||
on_success: change |
||||
on_failure: change |
@ -0,0 +1,21 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2018 Hieromon Ikasamo |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,341 @@ |
||||
## Include headers |
||||
|
||||
### <i class="fa fa-code"></i> AutoConnect.h |
||||
|
||||
```cpp |
||||
#include <AutoConnect.h> |
||||
``` |
||||
|
||||
#### Define macros |
||||
|
||||
```cpp |
||||
#define AC_DEBUG // Monitor message output activation |
||||
#define AC_DEBUG_PORT Serial // Default message output device |
||||
#define AUTOCONNECT_AP_IP 0x01F4A8C0 // Default SoftAP IP |
||||
#define AUTOCONNECT_AP_GW 0x01F4A8C0 // Default SoftAP Gateway IP |
||||
#define AUTOCONNECT_AP_NM 0x00FFFFFF // Default subnet mask |
||||
#define AUTOCONNECT_DNSPORT 53 // Default DNS port at captive portal |
||||
#define AUTOCONNECT_MENU_TITLE "AutoConnect" // Default AutoConnect menu title |
||||
#define AUTOCONNECT_URI "/_ac" // Default AutoConnect root path |
||||
``` |
||||
|
||||
## AutoConnect API |
||||
|
||||
### <i class="fa fa-code"></i> Constructors |
||||
|
||||
#### AutoConnect |
||||
|
||||
```cpp |
||||
AutoConnect() |
||||
``` |
||||
|
||||
AutoConnect default constructor. This entry activates WebServer internally and the web server is allocated internal. |
||||
|
||||
```cpp |
||||
AutoConnect(ESP8266WebServer& webServer) |
||||
``` |
||||
|
||||
Run the AutoConnect site using the externally ensured ESP 8266 WebServer. User's added URI handler response can be included in handleClient method. |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">webServer</span>A reference of ESP8266WebServer instance.</dd> |
||||
</dl> |
||||
|
||||
### <i class="fa fa-code"></i> Public member functions |
||||
|
||||
#### begin |
||||
|
||||
```cpp |
||||
bool begin() |
||||
``` |
||||
```cpp |
||||
bool begin(const char* ssid, const char* passphraase) |
||||
``` |
||||
```cpp |
||||
bool begin(const char* ssid, const char* passphraase, unsinged long timeout) |
||||
``` |
||||
|
||||
Starts establishing WiFi connection. Before establishing, start the Web server and DNS server for the captive portal. Then begins connection establishment in WIFI_STA mode. If connection can not established with the specified SSID and password, switch to WIFI_AP_STA mode and activate SoftAP. |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">ssid</span>SSID to be connected.</dd> |
||||
<dd><span class="apidef">passphrase</span>Password for connection.</dd> |
||||
<dd><span class="apidef">timeout</span>A time out value in milliseconds for waiting connection.</dd> |
||||
<dt>**Return value**</dt> |
||||
<dd><span class="apidef">true</span>Connection established, AutoConnect service started with WIFI_STA mode.</dd> |
||||
<dd><span class="apidef">false</span>Could not connected, Captive portal started with WIFI_AP_STA mode.</dd> |
||||
</dl> |
||||
|
||||
#### config |
||||
|
||||
```cpp |
||||
bool config(AutoConnectConfig& config) |
||||
``` |
||||
```cpp |
||||
bool config(const char* ap, const char* password = nullptr) |
||||
``` |
||||
|
||||
Sets SoftAP's WiFi configuration. |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">config</span>Reference to AutoConnectConfig containing SoftAP's parameters.</dd> |
||||
<dd><span class="apidef">ap</span>SSID for SoftAP. The default value is **esp8266ap**.</dd> |
||||
<dd><span class="apidef">password</span>Password for SodtAP. The default value is **12345678**.</dd> |
||||
<dt>**Return value**</dt> |
||||
<dd><span class="apidef">true</span>Successfully configured.</dd> |
||||
<dd><span class="apidef">false</span>Configuration parameter is invalid, some values out of range.</dd> |
||||
</dl> |
||||
|
||||
#### end |
||||
|
||||
```cpp |
||||
void end() |
||||
``` |
||||
|
||||
Stops AutoConnect captive portal service. Release ESP8266WebServer and DNSServer. |
||||
|
||||
!!! warning "Attention to end" |
||||
The end function releases the instance of ESP8266WebServer and DNSServer. It can not process them after the end function. |
||||
|
||||
#### handleClient |
||||
|
||||
```cpp |
||||
void handleClient() |
||||
``` |
||||
|
||||
Handling for the AutoConnect web interface. Invoke the handleClient of the parent web server to process client request of the AutoConnect WEB interface. No effects when the web server is not available. |
||||
|
||||
#### handleRequest |
||||
|
||||
```cpp |
||||
void handleRequest() |
||||
``` |
||||
|
||||
Handling for the AutoConnect menu request. |
||||
|
||||
!!! warning "About used in combination with handleClient" |
||||
The handleRequest function is not supposed to use with AutoConnect::handleClient. It should be used with ESP8266::handleClient. |
||||
|
||||
#### home |
||||
|
||||
```cpp |
||||
void home(String uri) |
||||
``` |
||||
|
||||
Put a user site's home URI. The URI specified by home is linked from "HOME" in the AutoConnect portal menu. |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">uri</span> A URI string of user site's home path.</dd> |
||||
</dl> |
||||
|
||||
#### host |
||||
|
||||
Returns the reference of the ESP8266WebServer which is allocated in AutoConnect automatically. |
||||
|
||||
```cpp |
||||
ESP8266WebServer& host() |
||||
``` |
||||
<dl class="apidl"> |
||||
<dt>**Return value**</dt> |
||||
<dd>A reference of the ESP8266WebServer.</dd> |
||||
</dl> |
||||
|
||||
!!! note "&reference is not a pointer" |
||||
A reference cannot be re-assigned, and must be assigned at initialization. It's like as bind as alias. |
||||
```cpp |
||||
ESP8266WebServer& server = portal.host(); |
||||
server.handleClient(); |
||||
``` |
||||
or |
||||
```cpp |
||||
portal.host().handleClient(); |
||||
``` |
||||
|
||||
#### onDetect |
||||
|
||||
```cpp |
||||
void onDetect(DetectExit_ft fn) |
||||
``` |
||||
Register the function which will call from AutoConnect at the start of the captive portal. |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">fn</span>Function called at the captive portal start.</dd> |
||||
|
||||
</dl> |
||||
|
||||
An *fn* specifies the function called when the captive portal starts. Its prototype declaration is defined as "*DetectFunc_ft*". |
||||
|
||||
```cpp |
||||
typedef std::function<bool(IPAddress softapIP)> DetectExit_ft |
||||
``` |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">softapIP</span>An IP address of SoftAP for the captive portal.</dd> |
||||
<dt>**Retuen value**</dt> |
||||
<dd><span class="apidef">true</span>Continues captive portal handling.</dd> |
||||
<dd><span class="apidef">false</span>Cancel the captive portal. AutoConnect::begin function will return with a false.</dd> |
||||
</dl> |
||||
|
||||
#### onNotFound |
||||
|
||||
```cpp |
||||
void onNotFound(ESP8266WebServer::THandlerFunction fn) |
||||
``` |
||||
Register the handler function for undefined URL request detected. |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">fn</span>A function of the "not found" handler.</dd> |
||||
</dl> |
||||
|
||||
## AutoConnectConfig API |
||||
|
||||
### <i class="fa fa-code"></i> Constructor |
||||
|
||||
#### AutoConnectConfig |
||||
|
||||
```cpp |
||||
AutoConnectConfig(); |
||||
``` |
||||
```cpp |
||||
AutoConnectConfig(const char* ap, const char* password); |
||||
``` |
||||
<dl class="apidl"> |
||||
<dt>**Parameters**</dt> |
||||
<dd><span class="apidef">ap</span>SSID for SoftAP. The length should be up to 31. The default value is **esp8266ap**.</dd> |
||||
<dd><span class="apidef">password</span>Password for SodtAP. The length should be from 8 to up to 63. The default value is **12345678**.</dd> |
||||
</dl> |
||||
|
||||
### <i class="fa fa-code"></i> Public member variables |
||||
|
||||
#### apid |
||||
SoftAP's SSID. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>String</dd> |
||||
</dl> |
||||
|
||||
#### apip |
||||
|
||||
Sets IP address for Soft AP in captive portal. When AutoConnect fails the initial WiFi.begin, it starts the captive portal with the IP address specified this. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>IPAddress</dd> |
||||
</dl> |
||||
|
||||
#### autoReset |
||||
|
||||
Reset ESP8266 module automatically when WLAN disconnected. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>bool</dd> |
||||
<dt>**Value**</dt> |
||||
<dd><span class="apidef" style="width:230px;">true</span>Reset after WiFi disconnected automatically.</dd> |
||||
<dd><span class="apidef" style="width:230px;">false</span>No reset.</dd> |
||||
</dl> |
||||
|
||||
#### autoSave |
||||
|
||||
The credential saved automatically at the connection establishment. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>AC_SAVECREDENTIAL_t</dd> |
||||
<dt>**Value**</dt> |
||||
<dd><span class="apidef" style="width:230px;">AC_SAVECREDENTIAL_AUTO</span>The credential saved automatically.</dd> |
||||
<dd><span class="apidef" style="width:230px;">AC_SAVECREDENTIAL_NEVER</span>The credential no saved.</dd> |
||||
</dl> |
||||
|
||||
#### channel |
||||
|
||||
The channel number of WIFi when SoftAP starts. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>uint8_t</dd> |
||||
<dt>**Value**</dt> |
||||
<dd>1 ~ 14. The default value is 1.</dd> |
||||
</dl> |
||||
|
||||
!!! info "See Application note" |
||||
Espressif Systems had announced the [application note](https://www.espressif.com/sites/default/files/esp8266_wi-fi_channel_selection_guidelines.pdf) about Wi-Fi channel selection. |
||||
|
||||
#### gateway |
||||
|
||||
Sets gateway address for Soft AP in captive portal. When AutoConnect fails the initial WiFi.begin, it starts the captive portal with the IP address specified this. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>IPAddress</dd> |
||||
</dl> |
||||
|
||||
#### hidden |
||||
|
||||
Sets SoftAP to hidden SSID. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>uint8_t</dd> |
||||
<dt>**Value**</dt> |
||||
<dd><span class="apidef" style="width:230px;">0</span>SSID will be appeared. This is the default.</dd> |
||||
<dd><span class="apidef" style="width:230px;">1</span>SSID will be hidden.</dd> |
||||
</dl> |
||||
|
||||
#### homeUri |
||||
|
||||
Sets the home path of user sketch. This path would be linked from 'HOME' in the AutoConnect menu. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>String</dd> |
||||
</dl> |
||||
|
||||
#### netmask |
||||
|
||||
Sets subnet mask for Soft AP in captive portal. When AutoConnect fails the initial WiFi.begin, it starts the captive portal with the IP address specified this. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>IPAddress</dd> |
||||
</dl> |
||||
|
||||
#### psk |
||||
|
||||
Sets password for SoftAP. The length should be from 8 to up to 63. The default value is **12345678**. |
||||
<dl class="apidl"> |
||||
<dt>**Type**</dt> |
||||
<dd>String</dd> |
||||
</dl> |
||||
|
||||
### <i class="fa fa-code"></i> AutoConnectConfig example |
||||
|
||||
```arduino |
||||
AutoConenct Portal; |
||||
AutoConenctConfig Config("", "passpass"); // SoftAp name is determined at runtime |
||||
Config.apid = ESP.hostname(); // Retrieve host name to SotAp identification |
||||
Config.apip = IPAddress(192,168,10,101); // Sets SoftAP IP address |
||||
Config.gateway = IPAddress(192,168,10,1); // Sets WLAN router IP address |
||||
Config.netmask = IPAddress(255,255,255,0); // Sets WLAN scope |
||||
Config.autoSave = AC_SAVECREDENTIAL_NEVER; // No save credential |
||||
Config.homeUri = "/index.html" // Sets home path of the sketch application |
||||
Portal.config(Config); // Configure AutoConnect |
||||
Portal.begin(); // Starts and behaves captive portal |
||||
``` |
||||
|
||||
## <i class="fa fa-gift"></i> Something extra |
||||
|
||||
The library presents two PNG icons which can be used to embed a hyperlink to the AutoConnect menu. |
||||
|
||||
- Bar type <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAApklEQVRYR2NkGGDAOMD2M4w6YDQEkEMgEJggZwCxGI0T5mug+alAvBFkD7IDXtLBcpjfXgEZ4ugOeAETpHEIgIwHeVYC3QH+0CgAS9AQgCwHRcFmdAfQ0E7cRo9mw0EVAqPlAKhwEKVTVsBZDsyiQ2k4Wg6gxPKgyoZ0Sn+o1iCHQBBQaiYQi9DYJTjbAyAJWluOtz0wWg7QOOqxGz+aDUdDYMBDAACA0x4hs/MPrwAAAABJRU5ErkJggg==" title="AutoConnect menu" alt="AutoConnect menu" /> |
||||
- Cog type <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAC2klEQVRIS61VvWsUQRSfmU2pon9BUIkQUaKFaCBKgooSb2d3NSSFKbQR/KrEIiIKBiGF2CgRxEpjQNHs7mwOUcghwUQ7g58IsbGxEBWsb2f8zR177s3t3S2cA8ftzPu993vzvoaSnMu2vRKlaqgKp74Q/tE8qjQPyHGcrUrRjwlWShmDbFMURd/a6TcQwNiYUmpFCPElUebcuQ2vz6aNATMVReHEPwzfSSntDcNwNo2rI+DcvQzhpAbA40VKyV0p1Q9snzBG1qYVcYufXV1sREraDcxpyHdXgkfpRBj6Uwm2RsC5dxxmZ9pdOY9cKTISRcHTCmGiUCh4fYyplTwG2mAUbtMTBMHXOgK9QfyXEZr+TkgQ1oUwDA40hEgfIAfj+HuQRaBzAs9eKyUZ5Htx+T3ZODKG8DzOJMANhmGomJVMXPll+hx9UUAlzZrJJ4QNCDG3VEfguu7mcpmcB/gkBOtShhQhchAlu5jlLUgc9ENgyP5gf9+y6LTv+58p5zySkgwzLNOIGc8sEoT1Lc53NMlbCQQuvMxeCME1NNPVVkmH/i3IzzXDtCSA0qQQwZWOCJDY50jsQRjJmkslEOxvTcDRO6zPxOh5xZglKkYLhWM9jMVnkIsTyMT6NBj7IbOCEjm6HxNVVTo2WXqEWJZ1T8rytB6GxizyDkPhWVpBqfiXUtbo/HywYJSpA9kMamNNPZ71R9Hcm+TMHHZNGw3EuraXEUldbfvw25UdOjqOt+JhMwJd7+jSTpZaEiIcaCDwPK83jtWnTkwnunFMtxeL/ge9r4XItt1RNNaj/0GAcV2bR3U5sG3nEh6M61US+Qrfd9Bs31GGulI2GOS/8dgcQZV1w+ApjIxB7TDwF9GcNzJzoA+rD0/8HvPnXQJCt2qFCwbBTfRI7UyXumWVt+HJ9NO4XI++bdsb0YyrqXmlh+AWOLHaLqS5CLQR5EggR3YlcVS9gKeH2hnX8r8Kmi1CAsl36QAAAABJRU5ErkJggg==" title="AutoConnect menu" alt="AutoConnect menu" /> |
||||
|
||||
To reference the icon, use the **AUTOCONNECT_LINK** macro in the sketch. It expands into the string literal as an HTML ```<a></a>``` tag with PNG embedded of the AutoConnect menu hyperlinks. Icon type is specified by the parameter of the macro. |
||||
|
||||
<dl class="apidl"> |
||||
<dd><span class="apidef">BAR_32</span>Bars icon, 32x32.</dd> |
||||
<dd><span class="apidef">BAR_48</span>Bars icon, 48x48.</dd> |
||||
<dd><span class="apidef">COG_24</span>Cog icon, 24x24.</dd> |
||||
<dd><span class="apidef">COG_32</span>Cog icon, 32x32.</dd> |
||||
</dl> |
||||
|
||||
!!! note "Usage" |
||||
```arduino |
||||
String html = "<html>"; |
||||
html += AUTOCONNECT_LINK(BAR_32); |
||||
html += "</html>"; |
||||
server.send(200, "text/html", html); |
||||
``` |
||||
|
@ -0,0 +1,38 @@ |
||||
.lead { |
||||
color: gray; |
||||
font-size: 15px; |
||||
} |
||||
|
||||
.md-typeset h2 { |
||||
border-bottom: solid 1px #d3d3d3; |
||||
padding-bottom: 5px; |
||||
} |
||||
|
||||
.md-typeset pre { |
||||
font-size: 12px; |
||||
} |
||||
|
||||
.md-typeset .codehilitetable { |
||||
margin-left:-20px; |
||||
margin-right: -20px; |
||||
border-radius: 0; |
||||
} |
||||
|
||||
.md-typeset .codehilitetable .linenodiv { |
||||
background-color: #364549 !important; |
||||
} |
||||
|
||||
.md-typeset .codehilitetable .linenodiv pre { |
||||
background-color: #364549 !important; |
||||
color: #aaa; |
||||
margin: 0; |
||||
} |
||||
|
||||
.apidef { |
||||
display: inline-block; |
||||
width: 100px; |
||||
} |
||||
|
||||
.apidl { |
||||
margin-left: 20px; |
||||
} |
@ -0,0 +1,36 @@ |
||||
## How to embed the AutoConnect |
||||
|
||||
Here holds two case examples. Both examples perform the same function. Only how to incorporate the **AutoConnect** into the sketch differs. Also included in the sample folder, HandlePortal.ino also shows how to use the [PageBuilder](https://github.com/Hieromon/PageBuilder) library for HTML assemblies. |
||||
|
||||
## What does this example do? |
||||
|
||||
Uses the web interface to light the LED connected to the **[NodeMCU](https://github.com/nodemcu/nodemcu-devkit-v1.0)** module D0 port (which could be referred to as *BUILTIN_LED*), the following animation is it. |
||||
|
||||
Access to the ESP8266 module connected WiFi from the browser then the page contains the current value of the D0 port would be displayed. The page has the buttons to switch the port value. The LED blinks according to the value of the button that was clicked. This example is a typical sketch of manipulating ESP8266's GPIO via WLN. |
||||
|
||||
<img data-gifffer="/images/ac2.gif" /> |
||||
|
||||
Embed AutoConnect library into this sketch. There are few places to be changed. And you can use AutoConnect's captive portal function to establish a connection freely to other WiFi spots. |
||||
|
||||
## Embed AutoConnect |
||||
|
||||
### <i class="fa fa-code" aria-hidden="true"></i> Pattern A. |
||||
|
||||
Bind to ESP8266WebServer, performs handleClient with handleRequest. |
||||
|
||||
<img src="/images/handleClient.svg" /> |
||||
|
||||
!!! hint "In what situations should the handleRequest be used." |
||||
It is something needs to be done immediately after the handle client. It is better to call only AutoConnect::handleClient whenever possible. |
||||
|
||||
### <i class="fa fa-code" aria-hidden="true"></i> Pattern B. |
||||
|
||||
Declare only AutoConnect, performs handleClient. |
||||
|
||||
<img src="/images/handlePortal.svg" /> |
||||
|
||||
<script> |
||||
window.onload = function() { |
||||
Gifffer(); |
||||
} |
||||
</script> |
@ -0,0 +1,156 @@ |
||||
## <i class="fa fa-question-circle"></i> After connected, AutoConnect menu performs but no happens. |
||||
|
||||
If you can access the **AutoConnect root path** as http://ESP8266IPADDRESS/_ac from browser, probably the sketch uses *ESP8266WebServer::handleClient()* without *AutoConnect::handleRequest()*. |
||||
For AutoConnect menus to work properly, call *AutoConnect::handleRequest()* after *ESP8266WebServer::handleClient()* invoked, or use *AutoConnect::handleClient()*. *AutoConnect::handleClient()* is equivalent *ESP8266WebServer::handleClient* combinated *AutoConnect::handleRequest()*. |
||||
|
||||
## <i class="fa fa-question-circle"></i> An esp8266ap as SoftAP was connected but Captive portal does not start. |
||||
|
||||
Captive portal detection could not be trapped. It is necessary to disconnect and reset ESP8266 to clear memorized connection data in ESP8266. Also, It may be displayed on the smartphone if the connection information of esp8266ap is wrong. In that case, delete the connection information of esp8266ap memorized by the smartphone once. |
||||
|
||||
## <i class="fa fa-question-circle"></i> Does not appear esp8266ap in smartphone. |
||||
|
||||
Maybe it is successfully connected at the **first WiFi.begin**. ESP8266 remembers the SSID successfully connected and will use at the next. It means SoftAP will only start up when the first *WiFi.begin()* fails. |
||||
|
||||
The saved SSID would be cleared by *WiFi.disconnect()* with WIFI_STA mode. If you do not want automatic reconnection, you can erase the memorized SSID with the following simple sketch. |
||||
|
||||
```arduino |
||||
#include <ESP8266WiFi.h> |
||||
|
||||
void setup() { |
||||
delay(1000); |
||||
Serial.begin(115200); |
||||
WiFi.mode(WIFI_STA); |
||||
delay(100); |
||||
WiFi.begin(); |
||||
if (WiFi.waitForConnectResult() == WL_CONNECTED) { |
||||
WiFi.disconnect(); |
||||
while (WiFi.status() == WL_CONNECTED) |
||||
delay(100); |
||||
} |
||||
Serial.println("WiFi disconnected."); |
||||
} |
||||
|
||||
void loop() { |
||||
delay(1000); |
||||
} |
||||
``` |
||||
|
||||
??? hint "You can interactively check the WiFi state of ESP8266." |
||||
Please try [**ESPShaker**](https://github.com/Hieromon/ESPShaker). It is ESP8266 interactive serial command processor. |
||||
|
||||
<img src="/images/espshaker.gif" /> |
||||
|
||||
## <i class="fa fa-question-circle"></i> Does not response from \_ac. |
||||
|
||||
Probably **WiFi.begin** failed with the specified SSID. Activating the [debug printing](usage.md#debug-print) will help you to track down the cause. |
||||
|
||||
## <i class="fa fa-question-circle"></i> How change esp8266ap for SSID name in Captive portal? |
||||
|
||||
An **esp8266** is default SSID name for SoftAP of captive portal and password is **12345678**. You can change both by using [AutoConnectConfig](api.md#string-apid). |
||||
|
||||
## <i class="fa fa-question-circle"></i> Hang up after Reset? |
||||
|
||||
If ESP8266 hang up after reset by AutoConnect menu, perhaps manual reset is not yet. Especially if it is not manual reset yet after uploading the sketch, the boot mode will stay 'Uart Download'. There is some discussion about this on the Github's ESP8266 core: https://github.com/esp8266/Arduino/issues/1017 |
||||
|
||||
If you received the following message, the boot mode is still sketch uploaded. It needs to the manual reset once. |
||||
|
||||
``` |
||||
ets Jan 8 2013,rst cause:2, boot mode:(1,6) or (1,7) |
||||
ets Jan 8 2013,rst cause:4, boot mode:(1,6) or (1,7) |
||||
wdt reset |
||||
``` |
||||
|
||||
The correct boot mode for starting the sketch is **(3, x)**. |
||||
|
||||
## <i class="fa fa-question-circle"></i> How erase the credentials saved in EEPROM? |
||||
|
||||
Make some sketches for erasing the EEPROM area, or some erasing utility is needed. You can prepare the sketch to erase the saved credential with *AutoConnectCredential*. The *AutoConnectCrendential* class provides the access method to the saved credential in EEPROM and library source file is including it. |
||||
|
||||
A class description of AutoConnectCredentail is follows. |
||||
|
||||
### Constructor |
||||
|
||||
```cpp |
||||
#include <AutoConnectCredential.h> |
||||
|
||||
AutoConnectCredential(); |
||||
``` |
||||
|
||||
### Public member functions |
||||
|
||||
- uint8_t **entries()** |
||||
Returns number of entries as contained credentials. |
||||
|
||||
- int8_t **load(const char\* _ssid_, struct station_config\* _config_)** |
||||
Load a credential entry specified *ssid* to *config*. Returns -1 as unsuccessfully loaded. |
||||
|
||||
- bool **load(int8_t _entry_, struct station_config\* _config_)** |
||||
Load a credential entry to *config*. The *entry* parameter specify to index of the entry. |
||||
|
||||
- bool **save(const struct station_config\* _config_)** |
||||
Save a credential entry stored in *config* to EEPROM. Returns the true as succeeded. |
||||
|
||||
- bool **del(const char\* _ssid_)** |
||||
Delete a credential entry specified *ssid*. Returns the true as successfully deleted. |
||||
|
||||
### Data structures |
||||
|
||||
- station_config |
||||
A structure is included in the ESP8266 SDK. You can use it in the sketch like as follows. |
||||
|
||||
```cpp |
||||
extern "C" { |
||||
#include <user_interface.h> |
||||
} |
||||
``` |
||||
|
||||
```cpp |
||||
struct station_config { |
||||
uint8 ssid[32]; |
||||
uint8 password[64]; |
||||
uint8 bssid_set; |
||||
uint8 bssid[6]; |
||||
}; |
||||
``` |
||||
|
||||
- EEPROM data structure |
||||
A data structure of the credential saving area in EEPROM as the below. [^1] |
||||
|
||||
[^1]: |
||||
There may be 0xff as an invalid data in the credential saving area. The 0xff area would be reused. |
||||
|
||||
| Byte offset | Length | Value | |
||||
|-------------|----------|---------------------------------------------------------------------| |
||||
| 0 | 8 | AC_CREDT | |
||||
| 8 | 1 | Number of contained entries (uint8_t) | |
||||
| 9 | 2 | Container size, excluding size of AC_CREDT and size of the number of entries(width for uint16_t type). | |
||||
| 11 | variable | SSID terminated by 0x00. Max length is 32 bytes. | |
||||
| variable | variable | Password plain text terminated by 0x00. Max length is 64 bytes. | |
||||
| variable | 6 | BSSID | |
||||
| variable | | Contained the next entries. (Continuation SSID+Password+BSSID) | |
||||
| variable | 1 | 0x00. End of container. | |
||||
|
||||
!!! hint |
||||
With the [**ESPShaker**](https://github.com/Hieromon/ESPShaker), you can access EEPROM interactively from the serial monitor, and of course you can erase saved credentials. |
||||
|
||||
## <i class="fa fa-question-circle"></i> How locate the link button to the AutoConnect menu? |
||||
|
||||
Link button to AutoConnect menu can be embedded into Sketch's web page. The root path of the menu is **/_ac** by default and embed the following ```<a></a>``` tag in the generating HTML. |
||||
|
||||
```html |
||||
<a style="background-color:SteelBlue; display:inline-block; padding:7px 13px; text-decoration:none;" href="/_ac">MENU</a> |
||||
``` |
||||
|
||||
## <i class="fa fa-question-circle"></i> How much memory consumption is AutoConnect? |
||||
|
||||
### Sketch size |
||||
|
||||
It increases about 57K bytes compared to the case without AutoConnect. A sketch size of the most simple example introduced at the Getting started is about 330K bytes. (270K byte without AutoConnect) |
||||
|
||||
### Heap size |
||||
|
||||
It consumes about 2K bytes in the static and about 12K bytes are consumed at the moment when menu executed. |
||||
|
||||
## <i class="fa fa-question-circle"></i> I cannot complete to Wi-Fi login from smartphone. |
||||
|
||||
Because AutoConnect does not send a login success response to the captive portal requests from the smartphone. The login success response varies iOS, Android and Windows. By analyzing the request URL of different login success inquiries for each OS, the correct behavior can be implemented, but not yet. Please resets ESP8266 from the AutoConnect menu. |
@ -0,0 +1,67 @@ |
||||
## Let's do the most simple sketch |
||||
|
||||
Open the Arduino IDE, write the following sketch and upload it. The feature of this sketch is that the SSID and Password are not coded. |
||||
|
||||
```arduino |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <AutoConnect.h> |
||||
|
||||
ESP8266WebServer Server; |
||||
AutoConnect Portal(Server); |
||||
|
||||
void rootPage() { |
||||
char content[] = "Hello, world"; |
||||
Server.send(200, "text/plain", content); |
||||
} |
||||
|
||||
void setup() { |
||||
delay(1000); |
||||
Serial.begin(115200); |
||||
Serial.println(); |
||||
|
||||
Server.on("/", rootPage); |
||||
if (Portal.begin()) { |
||||
Serial.println("WiFi connected: " + WiFi.localIP().toString()); |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
Portal.handleClient(); |
||||
} |
||||
``` |
||||
|
||||
### <i class="fa fa-play-circle"></i> Run at first |
||||
|
||||
After about 30 seconds, if the ESP8266 cannot connect to nearby Wi-Fi spot, you pull out your smartphone and open *Wi-Fi settings* from the *Settings* Apps. You can see the **esp8266ap** in the list of *"CHOOSE A NETWORK..."*. Then tap the esp8266ap and enter password **12345678**, a something screen pops up automatically as shown below. |
||||
|
||||
<span style="display:inline-block;width:282px;height:501px;border:1px solid lightgrey;"><img data-gifffer="/images/login_ani.gif" data-gifffer-width="280" style="width:280px;" /></span><img src="/images/arrow_right.svg" style="vertical-align:top;padding-top:120px;width:48px;margin-left:30px;margin-right:30px;" /><img src="/images/stat.png" style="border:1px solid lightgrey;width:280px;" /></span> |
||||
|
||||
This is the AutoConnect statistics screen. This screen displays the current status of the established connection, WiFi mode, IP address, free memory size, and etc. Also, the **hamburger icon** is the control menu of AutoConnect seems at the upper right. By tap the hamburger icon, the control menu appears as the below. |
||||
|
||||
### <i class="fa fa-cog"></i> Join to the new access point |
||||
|
||||
Here, tap *"Configure new AP"* to connect the new access point then the SSID configuration screen would be shown. Enter the **SSID** and **Passphrase** and tap **apply** to start connecting the access point. |
||||
|
||||
<img src="/images/menu_login.png" style="border:1px solid lightgrey;width:280px;" /><img src="/images/arrow_right.svg" style="vertical-align:top;padding-top:120px;width:48px;margin-left:30px;margin-right:30px;" /><img src="/images/config_ssid.png" style="border:1px solid lightgrey;width:280px;" /> |
||||
|
||||
### <i class="fa fa-rss"></i> Connection establishment |
||||
|
||||
After connection established, the current status screen will appear. It is already connected to WLAN with WiFi mode as WIFI\_AP\_STA and the IP connection status is displayed there including the SSID. Then at this screen, you have two options for the next step. |
||||
|
||||
For one, continues execution of the sketch while keeping this connection. You can access ESP8266 via browser through the established IP address after cancel to "**Log in**" by upper right on the screen. |
||||
Or, "**RESET**" can be selected. The ESP8266 resets and reboots. After that, immediately before the connection will be restored automatically with WIFI\_STA mode. |
||||
|
||||
<img src="/images/established.png" style="border:1px solid lightgrey;width:280px;" /><img src="/images/arrow_right.svg" style="vertical-align:top;padding-top:120px;width:48px;margin-left:30px;margin-right:30px;" /><img src="/images/reset.png" style="border:1px solid lightgrey;width:280px;" /> |
||||
|
||||
### <i class="fa fa-play-circle"></i> Run for usually |
||||
|
||||
The IP address of ESP8266 would be displayed on the serial monitor after connection recovered. Please access its address from the browser. The "Hello, world" page will respond. It's the page that was handled by in the sketch with "**on**" function of *ESP8266WebServer*. |
||||
|
||||
<img src="/images/serial.png" style="vertical-align:top;" /><img src="/images/arrow_right.svg" style="vertical-align:top;padding-top:60px;width:48px;margin-left:45px;margin-right:30px;" /><img src="/images/hello_world.png" style="border:1px solid lightgrey;width:280px;" /> |
||||
|
||||
<script> |
||||
window.onload = function() { |
||||
Gifffer(); |
||||
} |
||||
</script> |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 259 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 275 KiB |
@ -0,0 +1,93 @@ |
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
<svg:svg |
||||
xmlns:dc="http://purl.org/dc/elements/1.1/" |
||||
xmlns:cc="http://creativecommons.org/ns#" |
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
||||
xmlns:svg="http://www.w3.org/2000/svg" |
||||
id="svg8" |
||||
version="1.1" |
||||
viewBox="0 0 190.78812 90.019508" |
||||
height="90.019508mm" |
||||
width="190.78812mm"> |
||||
<svg:defs |
||||
id="defs2" /> |
||||
<svg:metadata |
||||
id="metadata5"> |
||||
<rdf:RDF> |
||||
<cc:Work |
||||
rdf:about=""> |
||||
<dc:format>image/svg+xml</dc:format> |
||||
<dc:type |
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
||||
<dc:title></dc:title> |
||||
</cc:Work> |
||||
</rdf:RDF> |
||||
</svg:metadata> |
||||
<svg:g |
||||
transform="translate(-10.346796,-89.855804)" |
||||
id="layer1"> |
||||
<svg:g |
||||
transform="matrix(0.26458333,0,0,0.26458333,10.755441,89.855804)" |
||||
id="g4" |
||||
style="fill:none;stroke:#ffffff;stroke-width:60;stroke-opacity:1"> |
||||
<svg:path |
||||
style="stroke:#ffffff;stroke-opacity:1" |
||||
id="path6" |
||||
d="m 174,30 a 145.54455,140 0 0 0 0,280 C 364,320 344,30 544,30 a 145.54455,140 0 0 1 0,280 C 354,320 374,30 174,30" /> |
||||
</svg:g> |
||||
<svg:g |
||||
style="font-size:167px;font-family:sans-serif;text-anchor:middle;fill:#00979c;stroke:#ffffff;stroke-width:20;stroke-opacity:1" |
||||
transform="matrix(0.26458333,0,0,0.26458333,10.755441,92.501637)" |
||||
id="g8" |
||||
font-size="167"> |
||||
<svg:g |
||||
style="stroke:#ffffff;stroke-opacity:1" |
||||
id="text10"> |
||||
<svg:path |
||||
style="stroke:#ffffff;stroke-opacity:1" |
||||
id="path3075" |
||||
d="M 528.06885,194.67432 V 161.89404 H 495.5332 v -13.69922 h 32.53565 v -32.53564 h 13.8623 v 32.53564 h 32.53565 v 13.69922 h -32.53565 v 32.78028 z" /> |
||||
</svg:g> |
||||
<svg:text |
||||
style="stroke:#ffffff;stroke-opacity:1" |
||||
id="text12" |
||||
dy="0" |
||||
y="218" |
||||
x="181"> |
||||
<ndash /> |
||||
</svg:text> |
||||
</svg:g> |
||||
<svg:g |
||||
transform="matrix(0.26458333,0,0,0.26458333,10.755441,89.855804)" |
||||
id="g14" |
||||
font-size="40" |
||||
style="font-size:40px;font-family:Arial;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"> |
||||
<svg:g |
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" |
||||
id="text16"> |
||||
<svg:path |
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1" |
||||
id="path3072" |
||||
d="M 681.55078,29.285156 V 15.730469 h -5.15625 v -2.363281 h 13.00781 v 2.363281 h -5.19531 v 13.554687 z m 9.62891,0 V 13.367188 h 3.90625 l 4.02343,12.714843 3.88672,-12.714843 h 3.82813 v 15.917968 h -2.42188 V 15.925781 l -4.10156,13.359375 h -2.40234 l -4.27735,-13.554687 v 13.554687 z" /> |
||||
</svg:g> |
||||
</svg:g> |
||||
<svg:g |
||||
style="font-size:167px;font-family:sans-serif;text-anchor:middle;fill:#00979c;stroke:#ffffff;stroke-width:20;stroke-opacity:1" |
||||
transform="matrix(0.26458333,0,0,0.26458333,-86.027691,89.995434)" |
||||
id="g8-1" |
||||
font-size="167"> |
||||
<svg:g |
||||
id="text10-7" |
||||
style="font-size:197.21893311px;stroke:#ffffff;stroke-width:23.61903381;stroke-opacity:1" |
||||
transform="scale(1.5645974,0.63914206)"> |
||||
<svg:path |
||||
id="path3078" |
||||
style="font-size:197.21893311px;stroke:#ffffff;stroke-width:23.61903381;stroke-opacity:1" |
||||
d="m 321.41756,266.30877 v -17.43 h 53.25297 v 17.43 z" /> |
||||
</svg:g> |
||||
<svg:g |
||||
style="stroke:#ffffff;stroke-opacity:1" |
||||
id="text12-4" /> |
||||
</svg:g> |
||||
</svg:g> |
||||
</svg:svg> |
After Width: | Height: | Size: 924 B |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 8.1 MiB |
After Width: | Height: | Size: 548 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 142 KiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 841 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 162 KiB |
@ -0,0 +1,84 @@ |
||||
# AutoConnect <small>for ESP8266</small> |
||||
|
||||
An Arduino library for ESP8266 WLAN configuration at run time with web interface. |
||||
|
||||
## Overview |
||||
|
||||
To the dynamic configuration for joining to WLAN with SSID and PSK accordingly. It an Arduino library united with *ESP8266WebServer* class. |
||||
Easily implementing the Web interface constituting the WLAN for ESP8266 WiFi connection. With this library to make a sketch easily which connects from ESP8266 to the access point at runtime by the web interface without hard-coded SSID and password. |
||||
|
||||
<img style="display:inline-block;width:460px;margin-right:30px;" src="/images/ov.png" /><span style="display:inline-block;width:182px;height:322px;border:solid 1px lightgrey;"><img data-gifffer="/images/ov.gif" data-gifffer-width="180" style="width:180px;" /></span> |
||||
|
||||
### <i class="fa fa-arrow-circle-right" aria-hidden="true"></i> No need pre-coded SSID & password |
||||
|
||||
<span class="lead">It is no needed hard-coding in advance the SSID and Password into the sketch to connect between ESP8266 and WLAN. You can input SSID & Password from a smartphone via the web interface at runtime.</span> |
||||
|
||||
### <i class="fa fa-arrow-circle-right" aria-hidden="true"></i> Simple usage |
||||
|
||||
<span class="lead">AutoConnect control screen will be displayed automatically for establishing new connections. It aids by the <a href="https://en.wikipedia.org/wiki/Captive_portal">captive portal</a> when vested the connection cannot be detected.<br>By using the [AutoConnect menu](menu.md), to manage the connections convenient.</span> |
||||
|
||||
### <i class="fa fa-arrow-circle-right" aria-hidden="true"></i> Store the established connection |
||||
|
||||
<span class="lead">The connection authentication data as credentials are saved automatically in EEPROM of ESP8266 and You can select the past SSID from the [AutoConnect menu](menu.md).</span> |
||||
|
||||
### <i class="fa fa-arrow-circle-right" aria-hidden="true"></i> Easy to embed in |
||||
|
||||
<span class="lead">AutoConnect can be embedded easily into your sketch, just "**begin**" and "**handleClient**".</span> |
||||
|
||||
### <i class="fa fa-arrow-circle-right" aria-hidden="true"></i> Lives with the your sketches |
||||
|
||||
<span class="lead">The sketches which provide the web page using ESP8266WebServer there is, AutoConnect will not disturb it. AutoConnect can use an already instantiated ESP8266WebServer object, or itself can assign it.</span> |
||||
|
||||
## Installation |
||||
|
||||
### Requirements |
||||
|
||||
#### Supported hardware |
||||
|
||||
* [X] Generic ESP8266 modules (applying the ESP8266 Community's Arduino core) |
||||
* [X] Adafruit HUZZAH ESP8266 (ESP-12) |
||||
* [X] ESP-WROOM-02 |
||||
* [X] Heltec WiFi Kit 8 |
||||
* [X] NodeMCU 0.9 (ESP-12) / NodeMCU 1.0 (ESP-12E) |
||||
* [X] Olimex MOD-WIFI-ESP8266 |
||||
* [X] SparkFun Thing |
||||
* [X] SweetPea ESP-210 |
||||
|
||||
!!! info "About flash size on the module" |
||||
The AutoConnect sketch size is relatively large. Large flash capacity is necessary. 512Kbyte (4Mbits) flash inclusion module such as ESP-01 is not recommended. |
||||
|
||||
#### Required libraries |
||||
|
||||
AutoConnect requires the following environment and libraries. |
||||
|
||||
<i class="fa fa-download"></i> <strong>Arduino IDE</strong> |
||||
|
||||
The current upstream at the 1.8 level or later is needed. Please install from the [official Arduino IDE download page](https://www.arduino.cc/en/Main/Software). This step is not required if you already have a modern version. |
||||
|
||||
<i class="fa fa-download"></i> <strong>ESP8266 Arduino core</strong> |
||||
|
||||
AutoConnect targets sketches made on the assumption of [ESP8266 Community's Arduino core](https://github.com/esp8266/Arduino). The [latest release](https://github.com/esp8266/Arduino/releases/latest) is recommended. |
||||
Install third-party platform using the *Boards Manager* of Arduino IDE. Package URL is http://arduino.esp8266.com/stable/package_esp8266com_index.json |
||||
|
||||
<i class="fa fa-download"></i> <strong>Additional necessary library</strong> |
||||
|
||||
The [PageBuilder](https://github.com/Hieromon/PageBuilder) library to build HTML for ESP8266WebServer is needed. |
||||
To install the PageBuilder library into your Arduino IDE, you can use the *Library Manager*. Select the board of ESP8266 series in the Arduino IDE, open the library manager and search keyword '**pagebuilder**' in the topic '**Communication**', then you can see the *PageBuilder*. The latest version or 1.0.0 later is required. |
||||
|
||||
<img src="/images/lm.png" width="640"/> |
||||
|
||||
### Install the AutoConnect |
||||
|
||||
Clone or download from the [AutoConnect GitHub repository](https://github.com/Hieromon/AutoConnect). |
||||
|
||||
<img src="/images/gitrepo.png" width="640"/> |
||||
|
||||
When you select Download, you can import it to Arduino IDE immediately. After downloaded, the AutoConnect-master.zip file will be saved in your download folder. Then in the Arduino IDE, navigate to *"Sketch > Include Library"*. At the top of the drop down list, select the option to *"Add .ZIP Library..."*. Details for [Arduino official page](https://www.arduino.cc/en/Guide/Libraries#toc4). |
||||
|
||||
<img src="/images/ins_lib.png" /> |
||||
|
||||
<script> |
||||
window.onload = function() { |
||||
Gifffer(); |
||||
} |
||||
</script> |
@ -0,0 +1 @@ |
||||
(function webpackUniversalModuleDefinition(root,factory){if(typeof exports==="object"&&typeof module==="object")module.exports=factory();else if(typeof define==="function"&&define.amd)define("Gifffer",[],factory);else if(typeof exports==="object")exports["Gifffer"]=factory();else root["Gifffer"]=factory()})(this,function(){var d=document;var playSize=60;var Gifffer=function(options){var images,i=0,gifs=[];images=d.querySelectorAll("[data-gifffer]");for(;i<images.length;++i)process(images[i],gifs,options);return gifs};function formatUnit(v){return v+(v.toString().indexOf("%")>0?"":"px")}function parseStyles(styles){var stylesStr="";for(prop in styles)stylesStr+=prop+":"+styles[prop]+";";return stylesStr}function createContainer(w,h,el,altText,opts){var alt;var con=d.createElement("BUTTON");var cls=el.getAttribute("class");var id=el.getAttribute("id");var playButtonStyles=opts&&opts.playButtonStyles?parseStyles(opts.playButtonStyles):["width:"+playSize+"px","height:"+playSize+"px","border-radius:"+playSize/2+"px","background:rgba(0, 0, 0, 0.3)","position:absolute","top:50%","left:50%","margin:-"+playSize/2+"px"].join(";");var playButtonIconStyles=opts&&opts.playButtonIconStyles?parseStyles(opts.playButtonIconStyles):["width: 0","height: 0","border-top: 14px solid transparent","border-bottom: 14px solid transparent","border-left: 14px solid rgba(0, 0, 0, 0.5)","position: absolute","left: 26px","top: 16px"].join(";");cls?con.setAttribute("class",el.getAttribute("class")):null;id?con.setAttribute("id",el.getAttribute("id")):null;con.setAttribute("style","position:relative;cursor:pointer;background:none;border:none;padding:0;");con.setAttribute("aria-hidden","true");var play=d.createElement("DIV");play.setAttribute("class","gifffer-play-button");play.setAttribute("style",playButtonStyles);var trngl=d.createElement("DIV");trngl.setAttribute("style",playButtonIconStyles);play.appendChild(trngl);if(altText){alt=d.createElement("p");alt.setAttribute("class","gifffer-alt");alt.setAttribute("style","border:0;clip:rect(0 0 0 0);height:1px;overflow:hidden;padding:0;position:absolute;width:1px;");alt.innerText=altText+", image"}con.appendChild(play);el.parentNode.replaceChild(con,el);altText?con.parentNode.insertBefore(alt,con.nextSibling):null;return{c:con,p:play}}function calculatePercentageDim(el,w,h,wOrig,hOrig){var parentDimW=el.parentNode.offsetWidth;var parentDimH=el.parentNode.offsetHeight;var ratio=wOrig/hOrig;if(w.toString().indexOf("%")>0){w=parseInt(w.toString().replace("%",""));w=w/100*parentDimW;h=w/ratio}else if(h.toString().indexOf("%")>0){h=parseInt(h.toString().replace("%",""));h=h/100*parentDimW;w=h*ratio}return{w:w,h:h}}function process(el,gifs,options){var url,con,c,w,h,duration,play,gif,playing=false,cc,isC,durationTimeout,dims,altText;url=el.getAttribute("data-gifffer");w=el.getAttribute("data-gifffer-width");h=el.getAttribute("data-gifffer-height");duration=el.getAttribute("data-gifffer-duration");altText=el.getAttribute("data-gifffer-alt");el.style.display="block";c=document.createElement("canvas");isC=!!(c.getContext&&c.getContext("2d"));if(w&&h&&isC)cc=createContainer(w,h,el,altText,options);el.onload=function(){if(!isC)return;w=w||el.width;h=h||el.height;if(!cc)cc=createContainer(w,h,el,altText,options);con=cc.c;play=cc.p;dims=calculatePercentageDim(con,w,h,el.width,el.height);gifs.push(con);con.addEventListener("click",function(){clearTimeout(durationTimeout);if(!playing){playing=true;gif=document.createElement("IMG");gif.setAttribute("style","width:100%;height:100%;");gif.setAttribute("data-uri",Math.floor(Math.random()*1e5)+1);setTimeout(function(){gif.src=url},0);con.removeChild(play);con.removeChild(c);con.appendChild(gif);if(parseInt(duration)>0){durationTimeout=setTimeout(function(){playing=false;con.appendChild(play);con.removeChild(gif);con.appendChild(c);gif=null},duration)}}else{playing=false;con.appendChild(play);con.removeChild(gif);con.appendChild(c);gif=null}});c.width=dims.w;c.height=dims.h;c.getContext("2d").drawImage(el,0,0,dims.w,dims.h);con.appendChild(c);con.setAttribute("style","position:relative;cursor:pointer;width:"+dims.w+"px;height:"+dims.h+"px;background:none;border:none;padding:0;");c.style.width="100%";c.style.height="100%";if(w.toString().indexOf("%")>0&&h.toString().indexOf("%")>0){con.style.width=w;con.style.height=h}else if(w.toString().indexOf("%")>0){con.style.width=w;con.style.height="inherit"}else if(h.toString().indexOf("%")>0){con.style.width="inherit";con.style.height=h}else{con.style.width=dims.w+"px";con.style.height=dims.h+"px"}};el.src=url}return Gifffer}); |
@ -0,0 +1,15 @@ |
||||
**MIT License** |
||||
|
||||
Copyright © 2018 Hieromon Ikasamo |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||
IN THE SOFTWARE. |
||||
|
||||
**Acknowledgments** |
||||
|
||||
The **Luxbar** is licensed under the MIT License. |
||||
https://github.com/balzss/luxbar |
@ -0,0 +1,52 @@ |
||||
!!! info "Luxbar" |
||||
The AutoConnect menu is developed using the [LuxBar](https://github.com/balzss/luxbar) which is licensed under the MIT License. See the [License](license.md). |
||||
|
||||
## <i class="fa fa-external-link"></i> Where the from |
||||
|
||||
The AutoConnect menu appears when you access the **AutoConnect root path**. It is assigned to "**/_ac**" located on ESP8266 *local IP address* by default. This location can be changed in the sketch. The following screen will appear at access to `http://{localIP}/_ac` as the root path. This is the statistics of the current WiFi connection. You can access the menu from the here. (e.g. `http://192.168.244.1/_ac` for SoftAP mode.) |
||||
To invoke the menu tap <i class="fa fa-bars"></i> at right on top. |
||||
|
||||
<img src="/images/_ac.png" style="border-style:solid;border-width:1px;border-color:lightgrey;width:280px;" /> |
||||
|
||||
!!! note "What's local IP?" |
||||
A local IP means Local IP at connection established or SoftAP's IP. |
||||
|
||||
## <i class="fa fa-bars"></i> Right on top |
||||
|
||||
Currently, AutoConnect supports four menus. Undermost menu returns to home path of its sketch. |
||||
|
||||
- **Configure new AP** : Configure SSID and Password for new access point. |
||||
- **Open SSIDs** : Opens the past SSID which has been established connection from EEPROM. |
||||
- **Disconnect** : Disconnects current connection. |
||||
- **Reset...** : Rest the ESP8266 module. |
||||
- **HOME** : Return to user home page. |
||||
|
||||
<img src="/images/menu.png" style="width:280px;" /> |
||||
|
||||
## <i class="fa fa-bars"></i> Configure new AP |
||||
|
||||
Scan all available access point and display it. Strength and security of the detected AP are marked. The <i class="fa fa-lock"></i> is indicated for the SSID that needs a security key. "**Hidden:**" means the number of hidden SSIDs discovered. |
||||
Enter SSID and Passphrase and tap "**apply**" to try connection. |
||||
|
||||
<img src="/images/newap.png" style="border-style:solid;border-width:1px;border-color:lightgrey;width:280px;" /> |
||||
|
||||
## <i class="fa fa-bars"></i> Open SSIDs |
||||
|
||||
Once it was established connection, its SSID and Password will be stored to the EEPROM of ESP8266 automatically. The **Open SSIDs** menu reads the saved SSID credentials from the EEPROM. The stored credential data are listed by the SSID as shown below. Its label is a clickable button. To tap the SSID button starts connection it. |
||||
|
||||
<img src="/images/open.png" style="border-style:solid;border-width:1px;border-color:lightgrey;width:280px;" /> |
||||
|
||||
## <i class="fa fa-bars"></i> Disconnect |
||||
|
||||
Disconnect ESP8266 from the current connection. After the menu tapped, AutoConnect menu cannot be accessed. Once disconnected, you will need to set the SSID again to connect to the WLAN. |
||||
|
||||
It can also reset the ESP8266 automatically after being disconnected from the [API](api.md#autoreset) used in the sketch. |
||||
|
||||
## <i class="fa fa-bars"></i> Reset... |
||||
|
||||
Reset the ESP8266, it will be rebooted. After rebooting complete, the ESP8266 begins establishing the previous connection by WIFI_STA mode and the *esp8266ap* as SoftAP disappears from the WLAN. |
||||
|
||||
<img src="/images/resetting.png" style="width:280px;" /> |
||||
|
||||
!!! warning "Not every module will be rebooted normally" |
||||
The Reset menu is using the **ESP.reset()** function. This is an almost hardware reset. In order to resume the sketch normally, the [state of GPIO0](https://github.com/esp8266/esp8266-wiki/wiki/Boot-Process#esp-boot-modes) is important. Since this depends on the circuit implementation for each module, not every module will be rebooted normally. See also [FAQ](faq.md#hang-up-after-reset). |
@ -0,0 +1,282 @@ |
||||
## Simple usage |
||||
|
||||
### <i class="fa fa-edit"></i> Embed to the sketches |
||||
|
||||
How embed the AutoConnect to the sketches you have. Most simple approach to applying AutoConnect for the existing sketches, follow the below steps. |
||||
|
||||
<img src="/images/BeforeAfter.svg" /> |
||||
|
||||
<i class="fa fa-edit"></i> Insert `#include <AutoConnect.h>` to behind of `#include <ESP8266WebServer.h>`. |
||||
<i class="fa fa-edit"></i> Insert `AutoConnect`*`PORTAL(WEBSERVER);`* to behind of `ESP8266WebServer`*`WEBSERVER;`* declaration.[^1] |
||||
<i class="fa fa-edit"></i> Remove `WiFi.begin(`*`SSID`*`,`*`PSK`*`)` and the subsequent logic for the connection status check. |
||||
<i class="fa fa-edit"></i> Replace *`WEBSERVER`*`.begin()` to *`PORTAL`*`.begin()`.[^2] |
||||
<i class="fa fa-edit"></i> Replace *`WEBSERVER`*`.handleClient()` to *`PORTAL`*`.handleClient()`.[^3] |
||||
<i class="fa fa-edit"></i> If the connection successful logic is needed, you can check the return value as `true` or `false` of *`PORTAL`*`.begin()`. |
||||
|
||||
[^1]: |
||||
Each *VARIABLE* conforms to the actual declaration in the sketches. |
||||
|
||||
[^2]: |
||||
WiFi SSID and Password can be specified AutoConnect::begin() too. |
||||
|
||||
[^3]: |
||||
Replacement the **handleClient** method is not indispensable. AutoConnect can still connect with the captive portal as it is ESP8266WebServer::handleClient. But it can **not valid AutoConnect menu**. |
||||
|
||||
## Basic usage |
||||
|
||||
### <i class="fa fa-caret-right"></i> Basic logic sequence for the user sketches |
||||
|
||||
#### 1. A typical logic sequence |
||||
|
||||
!!! note "" |
||||
1. <strong>Include headers,</strong> `ESP8266WebServer.h` and `AutoConnect.h` |
||||
2. <strong>Declare ESP8266WebServer variable.</strong> |
||||
3. <strong>Declare AutoConnect variable.</strong> |
||||
4. <strong>Implements the URL handler *function()*.</strong> |
||||
5. <strong>setup()</strong> |
||||
5.1 <strong>Sets URL handler *function()* to ESP8266WebServer by</strong>`ESP8266WebServer::on`<strong>.</strong> |
||||
5.2 <strong>Starts </strong>`AutoConnect::begin()`<strong>.</strong> |
||||
5.3 <strong>Check connection status.</strong> |
||||
6. <strong>loop()</strong> |
||||
6.1 <strong>Invokes </strong>`AutoConnect::handleClient()`<strong>,<br>or invokes </strong>`ESP8266WebServer::handleClient()`<strong> then </strong>`AutoConnect::handleRequest()`<strong>.</strong> |
||||
6.2 <strong>Do the process for actual sketch.</strong> |
||||
|
||||
#### 2. Declare AutoConnect object |
||||
|
||||
[Two options](#esp8266webserver-hosted-or-parasitic) are available for [AutoConnect constructor](api.md#constructors). |
||||
|
||||
```arduino |
||||
AutoConnect VARIABLE(&ESP8266WebServer); |
||||
``` |
||||
or |
||||
|
||||
```arduino |
||||
AutoConnect VARIABLE; |
||||
``` |
||||
|
||||
- **Parameter with ESP8266WebServer variable:** An ESP8266WebServer object variable must be declared in the sketch. AutoConnect uses its variable for handling the AutoConnect menu. |
||||
|
||||
- **With no parameter:** The sketch does not declare ESP8266WebServer object. In this case, AutoConnect allocates an instance of the ESP8266WebServer internally and the logic sequence of the sketch is somewhat different as the above. To register a URL handler function by *ESP8266WebServer::on* should be performed after [*AutoConnect::begin*](api.md#begin). |
||||
|
||||
#### 3. No need WiFI.begin(...) |
||||
|
||||
AutoConnect performs *WiFi.begin* for establishing a connection with WLAN internally. There is no need for a general process to establish a connection with *WiFi.begin* in a sketch. |
||||
|
||||
#### 4. Alternate ESP8266WebServer::begin() |
||||
|
||||
[*AutoConnect::begin*](api.md#begin) internally executes *ESP8266WebServer::begin* too and it starts DNS server to behave as a captive portal. So the sketch does not need to call *ESP8266WebServer::begin*. |
||||
|
||||
!!! info "Why DNS Server Starts" |
||||
AutoConnect traps the detection of captive portals and directs them to the AutoConnect menu to achieve a connection with the WLAN interactively. In order to trap, it temporarily responds SoftAP address to all DNS queries. When the connection with the WLAN is successfully established, the DNS server will stop. |
||||
|
||||
#### 5. AutoConnect::begin with SSID and Password |
||||
|
||||
SSID and Password can also specify by [AutoConnect::begin](api.me#begin). ESP8266 uses provided SSID and Password explicitly. If the connection false with specified SSID with Password then a captive portal is activated. SSID and Password are not present, ESP8266 SDK will attempt to connect using the still effectual SSID and password. Usually, it succeeds. |
||||
|
||||
#### 6. Use ESP8266WebServer::on to handle URL |
||||
|
||||
AutoConnect is designed to coexist with the process for handling the web pages by user sketches. The page processing function which will send an HTML to the client invoked by the "**on**" function is the same as when using ESP8266WebServer natively. |
||||
|
||||
#### 7. Use either ESP8266WebServer::handleClient() or AutoConnect::handleClient() |
||||
|
||||
Both classes member function name is the same: *handleClient*, but behavior is different. Using the AutoConnect embedded along with ESP8266WebServer::handleClient has limitations. Refer to the below section for details. |
||||
|
||||
### <i class="fa fa-caret-right"></i> ESP8266WebServer hosted or parasitic |
||||
|
||||
The interoperable process with an ESP8266WebServer depends on the parameters of the [AutoConnect constructor](api.md#constructors). |
||||
|
||||
Declaration parameter | Use ESP8266WebServer::handleClient | Use AutoConnect::handleClient |
||||
----|----|--- |
||||
None | AutoConnect menu not available.<br>host() is needed. | AutoConnect menu available.<br>host() is needed. |
||||
Reference to ESP8266WebServer | AutoConnect menu not available.<br>host() not necessary. | AutoConnect menu available.<br>host() not necessary. |
||||
|
||||
- **By declaration for the AutoConnect variable with no parameter**: The ESP8266WebServer instance is hosted by AutoConnect automatically then the sketches use [*AutoConnect::host*](api.md#host) as API to get it after [*AutoConnect::begin*](api.md#begin) performed. |
||||
|
||||
- **By declaration for the AutoConnect variable with the reference of ESP8266WebServer**: AutoConnect will use it. The sketch can use it is too. |
||||
|
||||
- **In use ESP8266WebServer::handleClient()**: AutoConnect menu can be dispatched but not works normally. It is necessary to call [*AutoConnect::handleRequest*](api.md#void-handlerequest) after *ESP8255WebServer::handleClient* invoking. |
||||
|
||||
- **In use [AutoConnect::handleClient()](api.md#void-handleclient)**: The handleClient() process and the AutoConnect menu is available without calling *ESP8266WebServer::handleClient*. |
||||
|
||||
!!! info "Why AutoConnect::handleRequest is needed when using ESP8266::handleClient" |
||||
The AutoConnect menu function may affect WiFi connection state. It follows that the menu process must execute outside *ESP8266WebServer::handleClient*. |
||||
[*AutoConnect::handleClient*](api.md#void-handleclient) is equivalent *ESP8266WebServer::handleClient* included [*AutoConnect::handleRequest*](api.md#void-handlerequest). |
||||
|
||||
## Advanced usage |
||||
|
||||
### <i class="fa fa-caret-right"></i> 404 handler |
||||
|
||||
Registering the "not found" handler is a different way than ESP8266. The *onNotFound* of ESP8266WebServer does not work with AutoConnect. AutoConnect overrides *ESP8266WebServer::onNotFound* to handle a captive portal. To register "not found" handler, use [*AutoConnect::onNotFound*](api.md#onnotfound). |
||||
|
||||
### <i class="fa fa-caret-right"></i> Captive portal start detection |
||||
|
||||
The captive portal will only be activated if the first *WiFi::begin* fails. Sketch can detect with the [*onDetect*](api.md#ondetect) funciton that the captive portal has started. For example, the sketch can be written like as follows that turns on the LED at the start captive portal. |
||||
|
||||
```arduino hl_lines="3 13" |
||||
AutoConnect Portal; |
||||
|
||||
bool startCP(IPAddress ip) { |
||||
digitalWrite(BUILTIN_LED, HIGH); |
||||
Serial.println("C.P. started, IP:" + WiFi.localIP().toString()); |
||||
return true; |
||||
} |
||||
|
||||
void setup() { |
||||
Serial.begin(115200); |
||||
pinMode(BUILTIN_LED, output); |
||||
digitalWrite(BUILTIN_LED, LOW); |
||||
Portal.onDetect(startCP); |
||||
if (Portal.begin()) { |
||||
digitalWrite(BUILTIN_LED, LOW); |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
Portal.handleClient(); |
||||
} |
||||
``` |
||||
|
||||
### <i class="fa fa-caret-right"></i> Combination with mDNS |
||||
|
||||
With [mDNS library](https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266mDNS), you can access to ESP8266 by name instead of IP address after connection. The sketch can start the MDNS responder after AutoConnect::begin. |
||||
|
||||
```arduino hl_lines="8 9" |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266mDNS.h> |
||||
#include <ESP8266WebServer.h> |
||||
AutoConnect Portal; |
||||
|
||||
void setup() { |
||||
if (Portal.begin()) { |
||||
if (MDNS.begin("esp8266")) { |
||||
MDNS.addService("http", "tcp", 80); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
Portal.handleClient(); |
||||
} |
||||
``` |
||||
|
||||
### <i class="fa fa-caret-right"></i> Credential data |
||||
|
||||
By default, AutoConnect saves the credentials of the established connection in EEPROM. You can disable it with the **autoSave** parameter specified by [*AutoConnect::config*](api.md#config). |
||||
|
||||
```arduino hl_lines="3" |
||||
AutoConnect Portal; |
||||
AutoConnectConfig Config; |
||||
Config.autoSave = AC_SAVECREDENTIAL_NEVER; |
||||
Portal.config(Config); |
||||
Portal.begin(); |
||||
``` |
||||
|
||||
!!! note "AutoConnect::config before AutoConnect::begin" |
||||
*AutoConnect::config* must be executed before *AutoConnect::begin*. |
||||
|
||||
### <i class="fa fa-caret-right"></i> Debug print |
||||
|
||||
You can output AutoConnect monitor messages to the **Serial**. A monitor message activation switch is in an include header file [*AutoConnect.h*](https://github.com/Hieromon/AutoConnect/blob/master/src/AutoConnect.h) of library source. Define **AC_DEBUG** macro to output monitor messages. |
||||
|
||||
```cpp |
||||
#define AC_DEBUG |
||||
``` |
||||
|
||||
### <i class="fa fa-caret-right"></i> Refers the hosted ESP8266WebServer |
||||
|
||||
Constructing an AutoConnect object variable without parameters then creates and starts an ESP8266WebServer inside the AutoConnect. This object variable could be referred by [*AutoConnect::host()*](api.md#host) function to access ESP8266WebServer instance as like below. |
||||
|
||||
```arduino hl_lines="4" |
||||
AutoConnect Portal; |
||||
|
||||
Portal.begin(); |
||||
ESP8266WebServer& server = Portal.host(); |
||||
server.send(200, "text/plain", "Hello, world"); |
||||
``` |
||||
|
||||
!!! info "When host() is valid" |
||||
The host() can be referred at after *AutoConnect::begin*. |
||||
|
||||
### <i class="fa fa-caret-right"></i> Usage for automatically instantiated ESP8266WebServer |
||||
|
||||
The sketch can handle URL requests using ESP8266WebServer that AutoConnect started internally. ESP8266WebServer instantiated dynamically by AutoConnect can be referred to by [*AutoConnect::host*](api.md#host) function. The sketch can use the '**on**' function, '**send**' function, '**client**' function and others by ESP8266WebServer reference of its return value. |
||||
|
||||
```arduino hl_lines="8 9 13 14 20 21 27" |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <AutoConnect.h> |
||||
|
||||
AutoConnect Portal; |
||||
|
||||
void handleRoot() { |
||||
ESP8266WebServer& IntServer = Portal.host(); |
||||
IntServer.send(200, "text/html", "Hello, world"); |
||||
} |
||||
|
||||
void handleNotFound() { |
||||
ESP8266WebServer& IntServer = Portal.host(); |
||||
IntServer.send(404, "text/html", "Unknown."); |
||||
} |
||||
|
||||
void setup() { |
||||
bool r = Portal.begin(); |
||||
if (r) { |
||||
ESP8266WebServer& IntServer = Portal.host(); |
||||
IntServer.on("/", handleRoot); |
||||
Portal.onNotFound(handleNotFound); // For only onNotFound. |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
Portal.host().handleClient(); |
||||
Portal.handleRequest(); |
||||
/* or following one line code is equ. |
||||
Portal.handleClient(); |
||||
*/ |
||||
} |
||||
``` |
||||
|
||||
!!! note "ESP8266WebServer function should be called after AutoConnect::begin" |
||||
The sketch cannot refer to an instance of ESP8266WebServer until AutoConnect::begin completes successfully. |
||||
|
||||
!!! warning "Do not use with ESP8266WebServer::begin" |
||||
ESP8266WebServer is already running inside the AutoConnect. |
||||
|
||||
### <i class="fa fa-caret-right"></i> Use with the [PageBuilder](https://github.com/Hieromon/PageBuilder) library |
||||
|
||||
In ordinary, the URL handler will respond the request by sending some HTML. [PageBuilder](https://github.com/Hieromon/PageBuilder) library is HTML assembly aid. it can handle predefined HTML as like a template and simplify an HTML string assemble logic, and also the generated HTML send automatically. |
||||
|
||||
An example sketch used with the PageBuilder as follows and it explains how it aids for the HTML generating. Details for [Github repository](https://github.com/Hieromon/PageBuilder). |
||||
|
||||
<img src="/images/PageBuilder.png" style="width:640px;"/> |
||||
|
||||
## Configuration functions |
||||
|
||||
### <i class="fa fa-caret-right"></i> Configuration for Soft AP |
||||
|
||||
AutoConnect will activate SoftAP at failed initial WiFi.Begin. It SoftAP settings are stored in [*AutoConnectConfig*](api.md#autoconnectconfig-api) as the following parameters. The sketch could be configured SoftAP using these parameters, refer [AutoConnectConfig API](api.md#autoconnectconfig-api) for details. |
||||
|
||||
- IP address of SoftAP activated. |
||||
- Gateway IP address. |
||||
- Subnet mask. |
||||
- SSID for SoftAP. |
||||
- Password for SoftAP. |
||||
- Channel. |
||||
- Hidden attribute. |
||||
- Auto save credential. |
||||
- Auto reset after connection establishment. |
||||
- Home URL of the user sketch application. |
||||
|
||||
### <i class="fa fa-caret-right"></i> Assign user sketch's home path |
||||
|
||||
"**HOME**" for returning to the user's sketch homepage is displayed at the bottom of the AutoConnect menu. It could be set using the [*AutoConnect::home*](api.md#home) function. |
||||
|
||||
<img src="/images/menu_home.png" /> |
||||
|
||||
### <i class="fa fa-caret-right"></i> Relocate the AutoConnect home path |
||||
|
||||
A home path of AutoConnect is **\_ac** by default. You can access from the browser with http://IPADDRESS/\_ac. You can change the home path by revising **AUTOCONNECT_URI** macro in the include header file as [AutoConnect.h](https://github.com/Hieromon/AutoConnect/blob/master/src/AutoConnect.h). |
||||
|
||||
```cpp |
||||
#define AUTOCONNECT_URI "/_ac" |
||||
``` |
@ -0,0 +1,144 @@ |
||||
/**
|
||||
* AutoConnect for ESP8266. |
||||
* https://github.com/Hieromon/AutoConnect
|
||||
* Copyright 2018, Hieromon Ikasamo. |
||||
* Licensed under The MIT License. |
||||
* https://opensource.org/licenses/mit-license.php
|
||||
* An example sketch for an Arduino library for ESP8266 WLAN configuration |
||||
* via the Web interface. This sketch provides a conservation measures |
||||
* utility for saved credentials in EEPROM. |
||||
* By accessing the root path, you can see the list of currently saved |
||||
* credentials via the browser. Enter an entry number of the credential, |
||||
* that entry will be deleted from EEPROM. |
||||
* This sketch uses PageBuilder to support handling of operation pages. |
||||
*/ |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <AutoConnect.h> |
||||
#include <AutoConnectCredentail.h> |
||||
#include <PageBuilder.h> |
||||
|
||||
ESP8266WebServer Server; |
||||
AutoConnect Portal(Server); |
||||
String viewCredential(PageArgument&); |
||||
String delCredential(PageArgument&); |
||||
|
||||
/**
|
||||
* An HTML for the operation page. |
||||
* In PageBuilder, the token {{SSID}} contained in an HTML template below is |
||||
* replaced by the actual SSID due to the action of the token handler's |
||||
* 'viewCredential' function. |
||||
* The number of the entry to be deleted is passed to the function in the |
||||
* POST method. |
||||
*/ |
||||
static const char html[] PROGMEM = { |
||||
"<!DOCTYPE html>" |
||||
"<html>" |
||||
"<head>" |
||||
"<meta charset=\"UTF-8\" name=\"viewport\" content=\"width=device-width, initial-scale=1\">" |
||||
"<style>" |
||||
"html {" |
||||
"font-family:Helvetica,Arial,sans-serif;" |
||||
"-ms-text-size-adjust:100%;" |
||||
"-webkit-text-size-adjust:100%;" |
||||
"}" |
||||
"a:link, a:visited {" |
||||
"background-color: #2f4f4f;" |
||||
"border-radius: 4px;" |
||||
"color: white;" |
||||
"display: inline-block;" |
||||
"padding: 6px 12px;" |
||||
"text-align: center;" |
||||
"text-decoration: none;" |
||||
"}" |
||||
"a:hover, a:active {" |
||||
"background-color: #132020;" |
||||
"}" |
||||
"</style>" |
||||
"</head>" |
||||
"<body>" |
||||
"<form action=\"/del\" method=\"POST\">" |
||||
"<ol>" |
||||
"{{SSID}}" |
||||
"</ol>" |
||||
"<p>Enter deleting entry:</p>" |
||||
"<input type=\"number\" min=\"1\" name=\"num\">" |
||||
"<input type=\"submit\">" |
||||
"</form>" |
||||
"<p><a href=\"" AUTOCONNECT_URI "\">Menu</a></p>" |
||||
"</body>" |
||||
"</html>" |
||||
}; |
||||
|
||||
// URL path as '/'
|
||||
PageElement elmList(html, {{ "SSID", viewCredential }}); |
||||
PageBuilder rootPage("/", { elmList }); |
||||
|
||||
// URL path as '/del'
|
||||
PageElement elmDel("{{DEL}}", {{ "DEL", delCredential }}); |
||||
PageBuilder delPage("/del", { elmDel }); |
||||
|
||||
// Retrieve the credential entries from EEPROM, Build the SSID line
|
||||
// with the <li> tag.
|
||||
String viewCredential(PageArgument& args) { |
||||
AutoConnectCredential ac; |
||||
struct station_config entry; |
||||
String content = ""; |
||||
uint8_t count = ac.entries(); // Get number of entries.
|
||||
|
||||
for (int8_t i = 0; i < count; i++) { // Loads all entries.
|
||||
ac.load(i, &entry); |
||||
// Build a SSID line of an HTML.
|
||||
content += String("<li>") + String((char *)entry.ssid) + String("</li>"); |
||||
} |
||||
|
||||
// Returns the '<li>SSID</li>' container.
|
||||
return content; |
||||
} |
||||
|
||||
// Delete a credential entry, the entry to be deleted is passed in the
|
||||
// request parameter 'num'.
|
||||
String delCredential(PageArgument& args) { |
||||
AutoConnectCredential ac; |
||||
if (args.hasArg("num")) { |
||||
int8_t e = args.arg("num").toInt(); |
||||
if (e > 0) { |
||||
struct station_config entry; |
||||
|
||||
// If the input number is valid, delete that entry.
|
||||
int8_t de = ac.load(e, &entry); |
||||
if (de > 0) { |
||||
ac.del((char *)entry.ssid); |
||||
|
||||
// Returns the redirect response. The page is reloaded and its contents
|
||||
// are updated to the state after deletion. It returns 302 response
|
||||
// from inside this token handler.
|
||||
Server.sendHeader("Location", String("http://") + Server.client().localIP().toString() + String("/")); |
||||
Server.send(302, "text/plain", ""); |
||||
Server.client().flush(); |
||||
Server.client().stop(); |
||||
|
||||
// Cancel automatic submission by PageBuilder.
|
||||
delPage.cancel(); |
||||
} |
||||
} |
||||
} |
||||
return ""; |
||||
} |
||||
|
||||
void setup() { |
||||
delay(1000); |
||||
Serial.begin(115200); |
||||
Serial.println(); |
||||
|
||||
rootPage.insert(Server); // Instead of Server.on("/", ...);
|
||||
delPage.insert(Server); // Instead of Server.on("/del", ...);
|
||||
|
||||
if (Portal.begin()) { |
||||
Serial.println("WiFi connected: " + WiFi.localIP().toString()); |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
Portal.handleClient(); |
||||
} |
@ -0,0 +1,251 @@ |
||||
/*
|
||||
FSWebServer - Example WebServer with SPIFFS backend for esp8266 |
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved. |
||||
This file is part of the ESP8266WebServer library for Arduino environment. |
||||
|
||||
This library is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU Lesser General Public |
||||
License as published by the Free Software Foundation; either |
||||
version 2.1 of the License, or (at your option) any later version. |
||||
This library is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
Lesser General Public License for more details. |
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
|
||||
upload the contents of the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu in Arduino IDE) |
||||
or you can upload the contents of a folder if you CD in that folder and run the following command: |
||||
for file in `ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done |
||||
|
||||
access the sample web page at http://esp8266fs.local
|
||||
edit the page by going to http://esp8266fs.local/edit
|
||||
*/ |
||||
#include <ESP8266WiFi.h> |
||||
#include <WiFiClient.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <ESP8266mDNS.h> |
||||
#include <FS.h> |
||||
//Add a below line for AutoConnect.
|
||||
#include <AutoConnect.h> |
||||
|
||||
#define DBG_OUTPUT_PORT Serial |
||||
|
||||
const char* ssid = "wifi-ssid"; |
||||
const char* password = "wifi-password"; |
||||
const char* host = "esp8266fs"; |
||||
|
||||
ESP8266WebServer server(80); |
||||
//Add a below line for AutoConnect.
|
||||
AutoConnect portal(server); |
||||
//holds the current upload
|
||||
File fsUploadFile; |
||||
|
||||
//format bytes
|
||||
String formatBytes(size_t bytes){ |
||||
if (bytes < 1024){ |
||||
return String(bytes)+"B"; |
||||
} else if(bytes < (1024 * 1024)){ |
||||
return String(bytes/1024.0)+"KB"; |
||||
} else if(bytes < (1024 * 1024 * 1024)){ |
||||
return String(bytes/1024.0/1024.0)+"MB"; |
||||
} else { |
||||
return String(bytes/1024.0/1024.0/1024.0)+"GB"; |
||||
} |
||||
} |
||||
|
||||
String getContentType(String filename){ |
||||
if(server.hasArg("download")) return "application/octet-stream"; |
||||
else if(filename.endsWith(".htm")) return "text/html"; |
||||
else if(filename.endsWith(".html")) return "text/html"; |
||||
else if(filename.endsWith(".css")) return "text/css"; |
||||
else if(filename.endsWith(".js")) return "application/javascript"; |
||||
else if(filename.endsWith(".png")) return "image/png"; |
||||
else if(filename.endsWith(".gif")) return "image/gif"; |
||||
else if(filename.endsWith(".jpg")) return "image/jpeg"; |
||||
else if(filename.endsWith(".ico")) return "image/x-icon"; |
||||
else if(filename.endsWith(".xml")) return "text/xml"; |
||||
else if(filename.endsWith(".pdf")) return "application/x-pdf"; |
||||
else if(filename.endsWith(".zip")) return "application/x-zip"; |
||||
else if(filename.endsWith(".gz")) return "application/x-gzip"; |
||||
return "text/plain"; |
||||
} |
||||
|
||||
bool handleFileRead(String path){ |
||||
DBG_OUTPUT_PORT.println("handleFileRead: " + path); |
||||
if(path.endsWith("/")) path += "index.htm"; |
||||
String contentType = getContentType(path); |
||||
String pathWithGz = path + ".gz"; |
||||
if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){ |
||||
if(SPIFFS.exists(pathWithGz)) |
||||
path += ".gz"; |
||||
File file = SPIFFS.open(path, "r"); |
||||
size_t sent = server.streamFile(file, contentType); |
||||
file.close(); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
void handleFileUpload(){ |
||||
if(server.uri() != "/edit") return; |
||||
HTTPUpload& upload = server.upload(); |
||||
if(upload.status == UPLOAD_FILE_START){ |
||||
String filename = upload.filename; |
||||
if(!filename.startsWith("/")) filename = "/"+filename; |
||||
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename); |
||||
fsUploadFile = SPIFFS.open(filename, "w"); |
||||
filename = String(); |
||||
} else if(upload.status == UPLOAD_FILE_WRITE){ |
||||
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
|
||||
if(fsUploadFile) |
||||
fsUploadFile.write(upload.buf, upload.currentSize); |
||||
} else if(upload.status == UPLOAD_FILE_END){ |
||||
if(fsUploadFile) |
||||
fsUploadFile.close(); |
||||
DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); |
||||
} |
||||
} |
||||
|
||||
void handleFileDelete(){ |
||||
if(server.args() == 0) return server.send(500, "text/plain", "BAD ARGS"); |
||||
String path = server.arg(0); |
||||
DBG_OUTPUT_PORT.println("handleFileDelete: " + path); |
||||
if(path == "/") |
||||
return server.send(500, "text/plain", "BAD PATH"); |
||||
if(!SPIFFS.exists(path)) |
||||
return server.send(404, "text/plain", "FileNotFound"); |
||||
SPIFFS.remove(path); |
||||
server.send(200, "text/plain", ""); |
||||
path = String(); |
||||
} |
||||
|
||||
void handleFileCreate(){ |
||||
if(server.args() == 0) |
||||
return server.send(500, "text/plain", "BAD ARGS"); |
||||
String path = server.arg(0); |
||||
DBG_OUTPUT_PORT.println("handleFileCreate: " + path); |
||||
if(path == "/") |
||||
return server.send(500, "text/plain", "BAD PATH"); |
||||
if(SPIFFS.exists(path)) |
||||
return server.send(500, "text/plain", "FILE EXISTS"); |
||||
File file = SPIFFS.open(path, "w"); |
||||
if(file) |
||||
file.close(); |
||||
else |
||||
return server.send(500, "text/plain", "CREATE FAILED"); |
||||
server.send(200, "text/plain", ""); |
||||
path = String(); |
||||
} |
||||
|
||||
void handleFileList() { |
||||
if(!server.hasArg("dir")) {server.send(500, "text/plain", "BAD ARGS"); return;} |
||||
|
||||
String path = server.arg("dir"); |
||||
DBG_OUTPUT_PORT.println("handleFileList: " + path); |
||||
Dir dir = SPIFFS.openDir(path); |
||||
path = String(); |
||||
|
||||
String output = "["; |
||||
while(dir.next()){ |
||||
File entry = dir.openFile("r"); |
||||
if (output != "[") output += ','; |
||||
bool isDir = false; |
||||
output += "{\"type\":\""; |
||||
output += (isDir)?"dir":"file"; |
||||
output += "\",\"name\":\""; |
||||
output += String(entry.name()).substring(1); |
||||
output += "\"}"; |
||||
entry.close(); |
||||
} |
||||
|
||||
output += "]"; |
||||
server.send(200, "text/json", output); |
||||
} |
||||
|
||||
void setup(void){ |
||||
DBG_OUTPUT_PORT.begin(115200); |
||||
DBG_OUTPUT_PORT.print("\n"); |
||||
DBG_OUTPUT_PORT.setDebugOutput(true); |
||||
SPIFFS.begin(); |
||||
{ |
||||
Dir dir = SPIFFS.openDir("/"); |
||||
while (dir.next()) {
|
||||
String fileName = dir.fileName(); |
||||
size_t fileSize = dir.fileSize(); |
||||
DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); |
||||
} |
||||
DBG_OUTPUT_PORT.printf("\n"); |
||||
} |
||||
|
||||
|
||||
//WIFI INIT
|
||||
DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); |
||||
//Comment out as follows to make AutoConnect recognition.
|
||||
//if (String(WiFi.SSID()) != String(ssid)) {
|
||||
// WiFi.mode(WIFI_STA);
|
||||
// WiFi.begin(ssid, password);
|
||||
//}
|
||||
|
||||
//while (WiFi.status() != WL_CONNECTED) {
|
||||
// delay(500);
|
||||
// DBG_OUTPUT_PORT.print(".");
|
||||
//}
|
||||
|
||||
//SERVER INIT
|
||||
//list directory
|
||||
server.on("/list", HTTP_GET, handleFileList); |
||||
//load editor
|
||||
server.on("/edit", HTTP_GET, [](){ |
||||
if(!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound"); |
||||
}); |
||||
//create file
|
||||
server.on("/edit", HTTP_PUT, handleFileCreate); |
||||
//delete file
|
||||
server.on("/edit", HTTP_DELETE, handleFileDelete); |
||||
//first callback is called after the request has ended with all parsed arguments
|
||||
//second callback handles file uploads at that location
|
||||
server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }, handleFileUpload); |
||||
|
||||
//called when the url is not defined here
|
||||
//use it to load content from SPIFFS
|
||||
//Replacement as follows to make AutoConnect recognition.
|
||||
//server.onNotFound([](){
|
||||
portal.onNotFound([](){ |
||||
if(!handleFileRead(server.uri())) |
||||
server.send(404, "text/plain", "FileNotFound"); |
||||
}); |
||||
|
||||
//get heap status, analog input value and all GPIO statuses in one json call
|
||||
server.on("/all", HTTP_GET, [](){ |
||||
String json = "{"; |
||||
json += "\"heap\":"+String(ESP.getFreeHeap()); |
||||
json += ", \"analog\":"+String(analogRead(A0)); |
||||
json += ", \"gpio\":"+String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16))); |
||||
json += "}"; |
||||
server.send(200, "text/json", json); |
||||
json = String(); |
||||
}); |
||||
//Replacement as follows to make AutoConnect recognition.
|
||||
//server.begin();
|
||||
portal.begin(); |
||||
DBG_OUTPUT_PORT.println("HTTP server started"); |
||||
|
||||
//Relocation as follows to make AutoConnect recognition.
|
||||
DBG_OUTPUT_PORT.println(""); |
||||
DBG_OUTPUT_PORT.print("Connected! IP address: "); |
||||
DBG_OUTPUT_PORT.println(WiFi.localIP()); |
||||
|
||||
//Relocation as follows to make AutoConnect recognition.
|
||||
MDNS.begin(host); |
||||
DBG_OUTPUT_PORT.print("Open http://"); |
||||
DBG_OUTPUT_PORT.print(host); |
||||
DBG_OUTPUT_PORT.println(".local/edit to see the file browser"); |
||||
} |
||||
|
||||
void loop(void){ |
||||
//Replacement as follows to make AutoConnect recognition.
|
||||
//server.handleClient();
|
||||
portal.handleClient(); |
||||
} |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,98 @@ |
||||
<!-- |
||||
FSWebServer - Example Index Page |
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved. |
||||
This file is part of the ESP8266WebServer library for Arduino environment. |
||||
|
||||
This library is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU Lesser General Public |
||||
License as published by the Free Software Foundation; either |
||||
version 2.1 of the License, or (at your option) any later version. |
||||
This library is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
Lesser General Public License for more details. |
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
--> |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> |
||||
<title>ESP Monitor</title> |
||||
<script type="text/javascript" src="graphs.js"></script> |
||||
<script type="text/javascript"> |
||||
var heap,temp,digi; |
||||
var reloadPeriod = 1000; |
||||
var running = false; |
||||
|
||||
function loadValues(){ |
||||
if(!running) return; |
||||
var xh = new XMLHttpRequest(); |
||||
xh.onreadystatechange = function(){ |
||||
if (xh.readyState == 4){ |
||||
if(xh.status == 200) { |
||||
var res = JSON.parse(xh.responseText); |
||||
heap.add(res.heap); |
||||
temp.add(res.analog); |
||||
digi.add(res.gpio); |
||||
if(running) setTimeout(loadValues, reloadPeriod); |
||||
} else running = false; |
||||
} |
||||
}; |
||||
xh.open("GET", "/all", true); |
||||
xh.send(null); |
||||
}; |
||||
|
||||
function run(){ |
||||
if(!running){ |
||||
running = true; |
||||
loadValues(); |
||||
} |
||||
} |
||||
|
||||
function onBodyLoad(){ |
||||
var refreshInput = document.getElementById("refresh-rate"); |
||||
refreshInput.value = reloadPeriod; |
||||
refreshInput.onchange = function(e){ |
||||
var value = parseInt(e.target.value); |
||||
reloadPeriod = (value > 0)?value:0; |
||||
e.target.value = reloadPeriod; |
||||
} |
||||
var stopButton = document.getElementById("stop-button"); |
||||
stopButton.onclick = function(e){ |
||||
running = false; |
||||
} |
||||
var startButton = document.getElementById("start-button"); |
||||
startButton.onclick = function(e){ |
||||
run(); |
||||
} |
||||
|
||||
// Example with 10K thermistor |
||||
//function calcThermistor(v) { |
||||
// var t = Math.log(((10230000 / v) - 10000)); |
||||
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15; |
||||
// return (t>120)?0:Math.round(t*10)/10; |
||||
//} |
||||
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor); |
||||
|
||||
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan"); |
||||
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange"); |
||||
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold"); |
||||
run(); |
||||
} |
||||
</script> |
||||
</head> |
||||
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()"> |
||||
<div><p><a href="/_ac">MENU</a></p></div> |
||||
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);"> |
||||
<label>Period (ms):</label> |
||||
<input type="number" id="refresh-rate"/> |
||||
<input type="button" id="start-button" value="Start"/> |
||||
<input type="button" id="stop-button" value="Stop"/> |
||||
</div> |
||||
<div id="heap"></div> |
||||
<div id="analog"></div> |
||||
<div id="digital"></div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,97 @@ |
||||
<!-- |
||||
FSWebServer - Example Index Page |
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved. |
||||
This file is part of the ESP8266WebServer library for Arduino environment. |
||||
|
||||
This library is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU Lesser General Public |
||||
License as published by the Free Software Foundation; either |
||||
version 2.1 of the License, or (at your option) any later version. |
||||
This library is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
Lesser General Public License for more details. |
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
--> |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> |
||||
<title>ESP Monitor</title> |
||||
<script type="text/javascript" src="graphs.js"></script> |
||||
<script type="text/javascript"> |
||||
var heap,temp,digi; |
||||
var reloadPeriod = 1000; |
||||
var running = false; |
||||
|
||||
function loadValues(){ |
||||
if(!running) return; |
||||
var xh = new XMLHttpRequest(); |
||||
xh.onreadystatechange = function(){ |
||||
if (xh.readyState == 4){ |
||||
if(xh.status == 200) { |
||||
var res = JSON.parse(xh.responseText); |
||||
heap.add(res.heap); |
||||
temp.add(res.analog); |
||||
digi.add(res.gpio); |
||||
if(running) setTimeout(loadValues, reloadPeriod); |
||||
} else running = false; |
||||
} |
||||
}; |
||||
xh.open("GET", "/all", true); |
||||
xh.send(null); |
||||
}; |
||||
|
||||
function run(){ |
||||
if(!running){ |
||||
running = true; |
||||
loadValues(); |
||||
} |
||||
} |
||||
|
||||
function onBodyLoad(){ |
||||
var refreshInput = document.getElementById("refresh-rate"); |
||||
refreshInput.value = reloadPeriod; |
||||
refreshInput.onchange = function(e){ |
||||
var value = parseInt(e.target.value); |
||||
reloadPeriod = (value > 0)?value:0; |
||||
e.target.value = reloadPeriod; |
||||
} |
||||
var stopButton = document.getElementById("stop-button"); |
||||
stopButton.onclick = function(e){ |
||||
running = false; |
||||
} |
||||
var startButton = document.getElementById("start-button"); |
||||
startButton.onclick = function(e){ |
||||
run(); |
||||
} |
||||
|
||||
// Example with 10K thermistor |
||||
//function calcThermistor(v) { |
||||
// var t = Math.log(((10230000 / v) - 10000)); |
||||
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15; |
||||
// return (t>120)?0:Math.round(t*10)/10; |
||||
//} |
||||
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor); |
||||
|
||||
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan"); |
||||
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange"); |
||||
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold"); |
||||
run(); |
||||
} |
||||
</script> |
||||
</head> |
||||
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()"> |
||||
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);"> |
||||
<label>Period (ms):</label> |
||||
<input type="number" id="refresh-rate"/> |
||||
<input type="button" id="start-button" value="Start"/> |
||||
<input type="button" id="stop-button" value="Stop"/> |
||||
</div> |
||||
<div id="heap"></div> |
||||
<div id="analog"></div> |
||||
<div id="digital"></div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,97 @@ |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <AutoConnect.h> |
||||
|
||||
ESP8266WebServer server; |
||||
AutoConnect portal(server); |
||||
|
||||
void handleRoot() { |
||||
String page = PSTR( |
||||
"<html>" |
||||
"</head>" |
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" |
||||
"<style type=\"text/css\">" |
||||
"body {" |
||||
"-webkit-appearance:none;" |
||||
"-moz-appearance:none;" |
||||
"font-family:'Arial',sans-serif;" |
||||
"text-align:center;" |
||||
"}" |
||||
".menu {" |
||||
"text-align:right;" |
||||
"}" |
||||
".button {" |
||||
"display:inline-block;" |
||||
"border-radius:7px;" |
||||
"background:#73ad21;" |
||||
"margin:0 10px 0 10px;" |
||||
"padding:10px 20px 10px 20px;" |
||||
"text-decoration:none;" |
||||
"color:#000000;" |
||||
"}" |
||||
"</style>" |
||||
"</head>" |
||||
"<body>" |
||||
"<p class=\"menu\">" AUTOCONNECT_LINK(BAR_32) "</p>" |
||||
"BUILT-IN LED<br>" |
||||
"GPIO("); |
||||
page += String(BUILTIN_LED); |
||||
page += String(F(") : <span style=\"font-weight:bold;color:")); |
||||
page += digitalRead(BUILTIN_LED) ? String("Tomato\">HIGH") : String("SlateBlue\">LOW"); |
||||
page += String(F("</span>")); |
||||
page += String(F("<p><a class=\"button\" href=\"/io?v=low\">low</a><a class=\"button\" href=\"/io?v=high\">high</a></p>")); |
||||
page += String(F("</body></html>")); |
||||
server.send(200, "text/html", page); |
||||
} |
||||
|
||||
void handleGPIO() { |
||||
if (server.arg("v") == "low") |
||||
digitalWrite(BUILTIN_LED, LOW); |
||||
else if (server.arg("v") == "high") |
||||
digitalWrite(BUILTIN_LED, HIGH); |
||||
sendRedirect("/"); |
||||
} |
||||
|
||||
void sendRedirect(String uri) { |
||||
server.sendHeader("Location", uri, true); |
||||
server.send(302, "text/plain", ""); |
||||
server.client().stop(); |
||||
} |
||||
|
||||
bool atDetect(IPAddress softapIP) { |
||||
Serial.println("Captive portal started, SoftAP IP:" + softapIP.toString()); |
||||
return true; |
||||
} |
||||
|
||||
void setup() { |
||||
delay(1000); |
||||
Serial.begin(115200); |
||||
Serial.println(); |
||||
pinMode(BUILTIN_LED, OUTPUT); |
||||
|
||||
// Put the home location of the web site.
|
||||
// But in usually, setting the home uri is not needed cause default location is "/".
|
||||
//portal.home("/");
|
||||
|
||||
server.on("/", handleRoot); |
||||
server.on("/io", handleGPIO); |
||||
|
||||
// Starts user web site included the AutoConnect portal.
|
||||
portal.onDetect(atDetect); |
||||
if (portal.begin()) { |
||||
Serial.println("Started, IP:" + WiFi.localIP().toString()); |
||||
} |
||||
else { |
||||
Serial.println("Connection failed."); |
||||
while (true) { yield(); } |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
server.handleClient(); |
||||
portal.handleRequest(); // Need to handle AutoConnect menu.
|
||||
if (WiFi.status() == WL_IDLE_STATUS) { |
||||
ESP.reset(); |
||||
delay(1000); |
||||
} |
||||
} |
@ -0,0 +1,97 @@ |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <AutoConnect.h> |
||||
|
||||
AutoConnect portal; |
||||
|
||||
void handleRoot() { |
||||
String page = PSTR( |
||||
"<html>" |
||||
"</head>" |
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" |
||||
"<style type=\"text/css\">" |
||||
"body {" |
||||
"-webkit-appearance:none;" |
||||
"-moz-appearance:none;" |
||||
"font-family:'Arial',sans-serif;" |
||||
"text-align:center;" |
||||
"}" |
||||
".menu {" |
||||
"text-align:right;" |
||||
"}" |
||||
".button {" |
||||
"display:inline-block;" |
||||
"border-radius:7px;" |
||||
"background:#73ad21;" |
||||
"margin:0 10px 0 10px;" |
||||
"padding:10px 20px 10px 20px;" |
||||
"text-decoration:none;" |
||||
"color:#000000;" |
||||
"}" |
||||
"</style>" |
||||
"</head>" |
||||
"<body>" |
||||
"<p class=\"menu\">" AUTOCONNECT_LINK(BAR_32) "</p>" |
||||
"BUILT-IN LED<br>" |
||||
"GPIO("); |
||||
page += String(BUILTIN_LED); |
||||
page += String(F(") : <span style=\"font-weight:bold;color:")); |
||||
page += digitalRead(BUILTIN_LED) ? String("Tomato\">HIGH") : String("SlateBlue\">LOW"); |
||||
page += String(F("</span>")); |
||||
page += String(F("<p><a class=\"button\" href=\"/io?v=low\">low</a><a class=\"button\" href=\"/io?v=high\">high</a></p>")); |
||||
page += String(F("</body></html>")); |
||||
portal.host().send(200, "text/html", page); |
||||
} |
||||
|
||||
void handleGPIO() { |
||||
ESP8266WebServer& server = portal.host(); |
||||
if (server.arg("v") == "low") |
||||
digitalWrite(BUILTIN_LED, LOW); |
||||
else if (server.arg("v") == "high") |
||||
digitalWrite(BUILTIN_LED, HIGH); |
||||
sendRedirect("/"); |
||||
} |
||||
|
||||
void sendRedirect(String uri) { |
||||
ESP8266WebServer& server = portal.host(); |
||||
server.sendHeader("Location", uri, true); |
||||
server.send(302, "text/plain", ""); |
||||
server.client().stop(); |
||||
} |
||||
|
||||
bool atDetect(IPAddress softapIP) { |
||||
Serial.println("Captive portal started, SoftAP IP:" + softapIP.toString()); |
||||
return true; |
||||
} |
||||
|
||||
void setup() { |
||||
delay(1000); |
||||
Serial.begin(115200); |
||||
Serial.println(); |
||||
pinMode(BUILTIN_LED, OUTPUT); |
||||
|
||||
// Put the home location of the web site.
|
||||
// But in usually, setting the home uri is not needed cause default location is "/".
|
||||
//portal.home("/");
|
||||
|
||||
// Starts user web site included the AutoConnect portal.
|
||||
portal.onDetect(atDetect); |
||||
if (portal.begin()) { |
||||
ESP8266WebServer& server = portal.host(); |
||||
server.on("/", handleRoot); |
||||
server.on("/io", handleGPIO); |
||||
Serial.println("Started, IP:" + WiFi.localIP().toString()); |
||||
} |
||||
else { |
||||
Serial.println("Connection failed."); |
||||
while (true) { yield(); } |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
portal.handleClient(); |
||||
if (WiFi.status() == WL_IDLE_STATUS) { |
||||
ESP.reset(); |
||||
delay(1000); |
||||
} |
||||
} |
@ -0,0 +1,117 @@ |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <PageBuilder.h> |
||||
#include "AutoConnect.h" |
||||
|
||||
ESP8266WebServer server; |
||||
AutoConnect portal(server); |
||||
|
||||
static const char mold_page[] PROGMEM = { |
||||
"<html>" |
||||
"</head>" |
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" |
||||
"<style type=\"text/css\">" |
||||
"body {" |
||||
"-webkit-appearance:none;" |
||||
"-moz-appearance:none;" |
||||
"font-family:'Arial',sans-serif;" |
||||
"text-align:center;" |
||||
"}" |
||||
".menu {" |
||||
"text-align:right;" |
||||
"}" |
||||
".button {" |
||||
"display:inline-block;" |
||||
"border-radius:7px;" |
||||
"background:#73ad21;" |
||||
"margin:0 10px 0 10px;" |
||||
"padding:10px 20px 10px 20px;" |
||||
"text-decoration:none;" |
||||
"color:#000000;" |
||||
"}" |
||||
"</style>" |
||||
"</head>" |
||||
"<body>" |
||||
"<p class=\"menu\">" AUTOCONNECT_LINK(BAR_32) "</p>" |
||||
"BUILT-IN LED<br>" |
||||
"GPIO({{LED}}) : <span style=\"font-weight:bold;color:{{COLOR}}\">{{GPIO}}</span>" |
||||
"<p><a class=\"button\" href=\"/io?v=low\">low</a><a class=\"button\" href=\"/io?v=high\">high</a></p>" |
||||
"</body>" |
||||
"</html>" |
||||
}; |
||||
|
||||
String getLEDPort(PageArgument& args) { |
||||
return String(BUILTIN_LED); |
||||
} |
||||
|
||||
String setColor(PageArgument& args) { |
||||
return digitalRead(BUILTIN_LED) ? "Tomato" : "SlateBlue"; |
||||
} |
||||
|
||||
String readLEDPort(PageArgument& args) { |
||||
return digitalRead(BUILTIN_LED) ? "HIGH" : "LOW"; |
||||
} |
||||
|
||||
PageElement elm_gpio(mold_page, { |
||||
{"LED", getLEDPort}, |
||||
{"COLOR", setColor}, |
||||
{"GPIO", readLEDPort} |
||||
}); |
||||
PageBuilder root("/", { elm_gpio }); |
||||
|
||||
|
||||
String gpio(PageArgument& args) { |
||||
if (args.arg("v") == "low") |
||||
digitalWrite(BUILTIN_LED, LOW); |
||||
else if (args.arg("v") == "high") |
||||
digitalWrite(BUILTIN_LED, HIGH); |
||||
sendRedirect("/"); |
||||
return ""; |
||||
} |
||||
|
||||
PageElement elm_io("{{IO}}", { {"IO", gpio} }); |
||||
PageBuilder io("/io", { elm_io }); |
||||
|
||||
void sendRedirect(String uri) { |
||||
server.sendHeader("Location", uri, true); |
||||
server.send(302, "text/plain", ""); |
||||
server.client().stop(); |
||||
io.cancel(); |
||||
} |
||||
|
||||
bool atDetect(IPAddress softapIP) { |
||||
Serial.println("Captive portal started, SoftAP IP:" + softapIP.toString()); |
||||
return true; |
||||
} |
||||
|
||||
void setup() { |
||||
delay(1000); |
||||
Serial.begin(115200); |
||||
Serial.println(); |
||||
pinMode(BUILTIN_LED, OUTPUT); |
||||
|
||||
// Put the home location of the web site.
|
||||
// But in usually, setting the home uri is not needed cause default location is "/".
|
||||
//portal.home("/");
|
||||
|
||||
// Starts user web site included the AutoConnect portal.
|
||||
portal.onDetect(atDetect); |
||||
if (portal.begin()) { |
||||
// Register the page request handlers.
|
||||
root.insert(server); |
||||
io.insert(server); |
||||
Serial.println("Started, IP:" + WiFi.localIP().toString()); |
||||
} |
||||
else { |
||||
Serial.println("Connection failed."); |
||||
while (true) { yield(); } |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
portal.handleClient(); |
||||
if (WiFi.status() == WL_IDLE_STATUS) { |
||||
ESP.reset(); |
||||
delay(1000); |
||||
} |
||||
} |
@ -0,0 +1,55 @@ |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <time.h> |
||||
#include <AutoConnect.h> |
||||
|
||||
ESP8266WebServer Server; |
||||
AutoConnect Portal(Server); |
||||
|
||||
#define TIMEZONE (3600 * 9) // Tokyo
|
||||
#define NTPServer1 "ntp.nict.jp" // NICT japan.
|
||||
#define NTPServer2 "time1.google.com" |
||||
|
||||
void rootPage() { |
||||
String content =
|
||||
"<html>" |
||||
"<head>" |
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" |
||||
"</head>" |
||||
"<body>" |
||||
"<h2 align=\"center\" style=\"color:blue;margin:20px;\">Hello, world</h2>" |
||||
"<h3 align=\"center\" style=\"color:gray;margin:10px;\">{{DateTime}}</h3>" |
||||
"<p><a href=\"" AUTOCONNECT_URI "\">AutoConnect menu</a></p>" |
||||
"</body>" |
||||
"</html>"; |
||||
static const char *wd[7] = { "Sun","Mon","Tue","Wed","Thr","Fri","Sat" }; |
||||
struct tm *tm; |
||||
time_t t; |
||||
char dateTime[26]; |
||||
|
||||
t = time(NULL); |
||||
tm = localtime(&t); |
||||
sprintf(dateTime, "%04d/%02d/%02d(%s) %02d:%02d:%02d.", |
||||
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, |
||||
wd[tm->tm_wday], |
||||
tm->tm_hour, tm->tm_min, tm->tm_sec); |
||||
content.replace("{{DateTime}}", String(dateTime)); |
||||
Server.send(200, "text/html", content); |
||||
} |
||||
|
||||
void setup() { |
||||
delay(1000); |
||||
Serial.begin(115200); |
||||
Serial.println(); |
||||
|
||||
Server.on("/", rootPage); |
||||
if (Portal.begin()) { |
||||
Serial.println("WiFi connected: " + WiFi.localIP().toString()); |
||||
configTime(TIMEZONE, 0, NTPServer1, NTPServer2); |
||||
} |
||||
} |
||||
|
||||
void loop() { |
||||
Portal.handleClient(); |
||||
} |
||||
|
@ -0,0 +1,31 @@ |
||||
####################################### |
||||
# Datatypes (KEYWORD1) |
||||
####################################### |
||||
AutoConnect KEYWORD1 |
||||
AutoConnectConfig KEYWORD1 |
||||
AutoConnectCredential KEYWORD1 |
||||
|
||||
####################################### |
||||
# Methods and Functions (KEYWORD2) |
||||
####################################### |
||||
config KEYWORD2 |
||||
begin KEYWORD2 |
||||
del KEYWORD2 |
||||
end KEYWORD2 |
||||
entries KEYWORD2 |
||||
handleClient KEYWORD2 |
||||
handleRequest KEYWORD2 |
||||
home KEYWORD2 |
||||
host KEYWORD2 |
||||
load KEYWORD2 |
||||
onDetect KEYWORD2 |
||||
onNotFound KEYWORD2 |
||||
save KEYWORD2 |
||||
|
||||
####################################### |
||||
# Literals (KEYWORD3) |
||||
####################################### |
||||
AC_WEBSERVER_PARASITIC LITERAL1 |
||||
AC_WEBSERVER_HOSTED LITERAL1 |
||||
AC_SAVECREDENTIAL_NEVER LITERAL1 |
||||
AC_SAVECREDENTIAL_AUTO LITERAL1 |
@ -0,0 +1,13 @@ |
||||
{ |
||||
"name": "AutoConnect", |
||||
"keywords": "communication, http, server, web, wifi, wi-fi", |
||||
"description": "ESP8266 WLAN configuration at runtime with web interface.", |
||||
"repository": |
||||
{ |
||||
"type": "git", |
||||
"url": "https://github.com/Hieromon/AutoConnect.git" |
||||
}, |
||||
"frameworks": "arduino", |
||||
"platforms": "espressif8266", |
||||
"version": "0.9.1" |
||||
} |
@ -0,0 +1,10 @@ |
||||
name=AutoConnect |
||||
version=0.9.1 |
||||
author=Hieromon Ikasamo <hieromon@gmail.com> |
||||
maintainer=Hieromon Ikasamo <hieromon@gmail.com> |
||||
sentence=ESP8266 WLAN configuration at runtime with web interface. |
||||
paragraph=A library for easily implementing the Web interface constituting the WLAN for ESP8266 WiFi connection. With this library to make a sketch which connect from ESP8266 to the access point at runtime by the web interface without hardcoded SSID and password. |
||||
category=Communication |
||||
url=https://github.com/Hieromon/AutoConnect.git |
||||
dependencies=ESP8266WebServer, ESP8266WiFi |
||||
architectures=esp8266 |
@ -0,0 +1,76 @@ |
||||
# Project information |
||||
site_name: 'AutoConnect for ESP8266' |
||||
site_description: 'ESP8266 WLAN configuration at run time with web interface' |
||||
site_author: 'Hieromon Ikasamo' |
||||
site_url: 'https://Hieromon.github.io/AutoConnect/' |
||||
|
||||
# Docs |
||||
docs_dir: 'docs' |
||||
|
||||
# Pages |
||||
pages: |
||||
- 'Overview' : index.md |
||||
- 'Getting started' : gettingstarted.md |
||||
- 'AutoConnect menu' : menu.md |
||||
- 'Usage the library' : usage.md |
||||
- 'Library APIs' : api.md |
||||
- 'Examples' : examples.md |
||||
- 'FAQ' : faq.md |
||||
- 'License' : license.md |
||||
|
||||
# Repository |
||||
repo_name: 'Hieromon/AutoConnect' |
||||
repo_url: 'https://github.com/Hieromon/AutoConnect' |
||||
|
||||
# Copyright |
||||
copyright: 'Copyright © 2018 Hieromon Ikasamo' |
||||
|
||||
# Configuration |
||||
theme: |
||||
name: 'material' |
||||
feature: |
||||
tabs: true |
||||
language: 'en' |
||||
logo: 'images/arduino-logo.svg' |
||||
palette: |
||||
primary: 'indigo' |
||||
accent: 'indigo' |
||||
font: |
||||
text: 'Roboto' |
||||
code: 'Roboto Mono' |
||||
|
||||
# Customization |
||||
extra_css: |
||||
- 'css/paragraph.css' |
||||
extra_javascript: |
||||
- 'js/gifffer.min.js' |
||||
extra: |
||||
social: |
||||
- type: 'github' |
||||
link: 'https://github.com/Hieromon' |
||||
- type: 'twitter' |
||||
link: 'https://twitter.com/hieromon' |
||||
|
||||
# Google Analytics |
||||
google_analytics: |
||||
- 'UA-XXXXXXXX-X' |
||||
- 'auto' |
||||
|
||||
# Extensions |
||||
markdown_extensions: |
||||
- admonition |
||||
- footnotes |
||||
- pymdownx.betterem: |
||||
smart_enable: all |
||||
- pymdownx.critic |
||||
- pymdownx.details |
||||
- pymdownx.inlinehilite |
||||
- pymdownx.magiclink |
||||
- pymdownx.smartsymbols |
||||
- pymdownx.superfences |
||||
- pymdownx.tasklist |
||||
- codehilite: |
||||
linenums: none |
||||
use_pygments: true |
||||
- toc: |
||||
permalink: true |
@ -0,0 +1,584 @@ |
||||
/**
|
||||
* AutoConnect class implementation. |
||||
* @file AutoConnect.cpp |
||||
* @author hieromon@gmail.com |
||||
* @version 0.9.1 |
||||
* @date 2018-02-13 |
||||
* @copyright MIT license. |
||||
*/ |
||||
|
||||
#include "AutoConnect.h" |
||||
#include "AutoConnectCredentail.h" |
||||
|
||||
/**
|
||||
* AutoConnect default constructor. This entry activates WebServer |
||||
* internally and the web server is allocated internal. |
||||
*/ |
||||
AutoConnect::AutoConnect() { |
||||
_initialize(); |
||||
_webServer.reset(nullptr); |
||||
_dnsServer.reset(nullptr); |
||||
_webServerAlloc = AC_WEBSERVER_HOSTED; |
||||
} |
||||
|
||||
/**
|
||||
* Run the AutoConnect site using the externally ensured ESP 8266 WebServer. |
||||
* User's added URI handler response can be included in handleClient method. |
||||
* @param webServer A reference of ESP8266WebServer instance. |
||||
*/ |
||||
AutoConnect::AutoConnect(ESP8266WebServer& webServer) { |
||||
_initialize(); |
||||
_webServer.reset(&webServer); |
||||
_dnsServer.reset(nullptr); |
||||
_webServerAlloc = AC_WEBSERVER_PARASITIC; |
||||
} |
||||
|
||||
void AutoConnect::_initialize() { |
||||
_rfConnect = false; |
||||
_rfReset = false; |
||||
_currentPageElement = nullptr; |
||||
_menuTitle = String(AUTOCONNECT_MENU_TITLE); |
||||
_portalTimeout = AUTOCONNECT_TIMEOUT; |
||||
memset(&_credential, 0x00, sizeof(struct station_config)); |
||||
} |
||||
|
||||
/**
|
||||
* A destructor. Free AutoConnect web pages and release Web server. |
||||
* When the server is hosted it will be purged. |
||||
*/ |
||||
AutoConnect::~AutoConnect() { |
||||
end(); |
||||
} |
||||
|
||||
/**
|
||||
* Starts establishing WiFi connection without SSID and password. |
||||
*/ |
||||
bool AutoConnect::begin() { |
||||
return begin(nullptr, nullptr); |
||||
} |
||||
|
||||
/**
|
||||
* Starts establishing WiFi connection. |
||||
* Before establishing, start the Web server and DNS server for the captive |
||||
* portal. Then begins connection establishment in WIFI_STA mode. If |
||||
* connection can not established with the specified SSID and password, |
||||
* switch to WIFI_AP_STA mode and activate SoftAP. |
||||
* @param ssid SSID to be connected. |
||||
* @param passphrase Password for connection. |
||||
* @param timeout A time out value in milliseconds for waiting connection. |
||||
* @retval true Connection established, AutoConnect service started with WIFI_STA mode. |
||||
* @retval false Could not connected, Captive portal started with WIFI_AP_STA mode. |
||||
*/ |
||||
bool AutoConnect::begin(const char* ssid, const char* passphrase, unsigned long timeout) { |
||||
_portalTimeout = timeout; |
||||
return begin(ssid, passphrase); |
||||
} |
||||
|
||||
/**
|
||||
* Starts establishing WiFi connection. |
||||
* Before establishing, start the Web server and DNS server for the captive |
||||
* portal. Then begins connection establishment in WIFI_STA mode. If |
||||
* connection can not established with the specified SSID and password, |
||||
* switch to WIFI_AP_STA mode and activate SoftAP. |
||||
* @param ssid SSID to be connected. |
||||
* @param passphrase Password for connection. |
||||
* @retval true Connection established, AutoConnect service started with WIFI_STA mode. |
||||
* @retval false Could not connected, Captive portal started with WIFI_AP_STA mode. |
||||
*/ |
||||
bool AutoConnect::begin(const char* ssid, const char* passphrase) { |
||||
bool cs; |
||||
|
||||
WiFi.mode(WIFI_STA); |
||||
delay(100); |
||||
// 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; |
||||
_currentHostIP = WiFi.localIP(); |
||||
|
||||
// Rushing into the portal.
|
||||
_startWebServer(); |
||||
if (!cs) { |
||||
// Change WiFi working mode, Enable AP with STA
|
||||
WiFi.setAutoConnect(false); |
||||
WiFi.disconnect(); |
||||
WiFi.mode(WIFI_AP_STA); |
||||
delay(100); |
||||
|
||||
// Connection unsuccessful, launch the captive portal.
|
||||
if (!(_apConfig.apip == IPAddress(0, 0, 0, 0) || _apConfig.gateway == IPAddress(0, 0, 0, 0) || _apConfig.netmask == IPAddress(0, 0, 0, 0))) { |
||||
_config(); |
||||
} |
||||
WiFi.softAP(_apConfig.apid.c_str(), _apConfig.psk.c_str(), _apConfig.channel, _apConfig.hidden); |
||||
while (WiFi.softAPIP() == IPAddress(0, 0, 0, 0)) |
||||
yield(); |
||||
_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, WiFi.softAPIP().toString().c_str()); |
||||
|
||||
// Fork to the exit routine that starts captive portal.
|
||||
cs = _onDetectExit ? _onDetectExit(_currentHostIP) : true;
|
||||
|
||||
// Start captive portal without cancellation by DetectExit.
|
||||
if (cs) { |
||||
// Prepare for redirecting captive portal detection.
|
||||
// Pass all URL requests to _captivePortal to disguise the captive portal.
|
||||
_startDNSServer(); |
||||
|
||||
// Start the captive portal to make a new connection
|
||||
while (WiFi.status() != WL_CONNECTED && !_rfReset) { |
||||
handleClient(); |
||||
// Force execution of queued processes.
|
||||
yield(); |
||||
} |
||||
cs = WiFi.status() == WL_CONNECTED; |
||||
|
||||
// If WLAN successfully connected, release DNS server.
|
||||
if (cs) { |
||||
_dnsServer->stop(); |
||||
_dnsServer.reset(); |
||||
} |
||||
} |
||||
} |
||||
return cs; |
||||
} |
||||
|
||||
/**
|
||||
* Configure AutoConnect portal access point. |
||||
* @param ap SSID for access point. |
||||
* @param psk Password for access point. |
||||
*/ |
||||
bool AutoConnect::config(const char* ap, const char* password) { |
||||
_apConfig.apid = String(ap); |
||||
_apConfig.psk = String(password); |
||||
return _config(); |
||||
} |
||||
|
||||
/**
|
||||
* Configure AutoConnect portal access point. |
||||
* @param Config AutoConnectConfig class instance. |
||||
*/ |
||||
bool AutoConnect::config(AutoConnectConfig& Config) { |
||||
_apConfig = Config; |
||||
return _config(); |
||||
} |
||||
|
||||
/**
|
||||
* Configure access point. |
||||
* Set up access point with internal AucoConnectConfig parameter corrected |
||||
* by Config method. |
||||
*/ |
||||
bool AutoConnect::_config() { |
||||
return WiFi.softAPConfig(_apConfig.apip, _apConfig.gateway, _apConfig.netmask); |
||||
} |
||||
|
||||
/**
|
||||
* Put a user site's home URI. |
||||
* The URI specified by home is linked from "HOME" in the AutoConnect |
||||
* portal menu. |
||||
* @param uri A URI string of user site's home. |
||||
*/ |
||||
void AutoConnect::home(String uri) { |
||||
_apConfig.homeUri = uri; |
||||
} |
||||
|
||||
/**
|
||||
* Stops AutoConnect captive portal service. |
||||
*/ |
||||
void AutoConnect::end() { |
||||
if (_responsePage != nullptr) { |
||||
_responsePage->~PageBuilder(); |
||||
delete _responsePage; |
||||
} |
||||
if (_currentPageElement != nullptr) |
||||
_currentPageElement->~PageElement(); |
||||
|
||||
_stopPortal(); |
||||
if (_webServer) { |
||||
switch (_webServerAlloc) { |
||||
case AC_WEBSERVER_HOSTED: |
||||
if (_dnsServer) |
||||
_dnsServer.reset(); |
||||
_webServer.reset(); |
||||
break; |
||||
case AC_WEBSERVER_PARASITIC: |
||||
_webServer.release(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Returns the current hosted ESP8266WebServer. |
||||
*/ |
||||
ESP8266WebServer& AutoConnect::host() { |
||||
return *_webServer; |
||||
} |
||||
|
||||
/**
|
||||
* Starts Web server for AutoConnect service. |
||||
*/ |
||||
void AutoConnect::_startWebServer() { |
||||
// Boot Web server
|
||||
if (!_webServer) { |
||||
// Only when hosting WebServer internally
|
||||
_webServer.reset(new ESP8266WebServer(80)); |
||||
_webServerAlloc = AC_WEBSERVER_HOSTED; |
||||
AC_DBG("ESP8266WebServer allocated\n"); |
||||
} |
||||
// Discard the original the not found handler to redirect captive portal detection.
|
||||
// It is supposed to evacuate but ESP8266WebServer::_notFoundHandler is not accessible.
|
||||
_webServer->onNotFound(std::bind(&AutoConnect::_handleNotFound, this)); |
||||
// here, Prepare PageBuilders for captive portal
|
||||
_responsePage = new PageBuilder(); |
||||
_responsePage->exitCanHandle(std::bind(&AutoConnect::_classifyHandle, this, std::placeholders::_1, std::placeholders::_2)); |
||||
_responsePage->insert(*_webServer); |
||||
|
||||
_webServer->begin(); |
||||
AC_DBG("http server started\n"); |
||||
} |
||||
|
||||
/**
|
||||
* Starts DNS server for Captive portal. |
||||
*/ |
||||
void AutoConnect::_startDNSServer() { |
||||
// Boot DNS server, set up for captive portal redirection.
|
||||
if (!_dnsServer) { |
||||
_dnsServer.reset(new DNSServer()); |
||||
_dnsServer->setErrorReplyCode(DNSReplyCode::NoError); |
||||
_dnsServer->start(AUTOCONNECT_DNSPORT, "*", WiFi.softAPIP()); |
||||
AC_DBG("DNS server started\n"); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Handling for the AutoConnect web interface. |
||||
* Invoke the handleClient of parent web server to process client request of |
||||
* AutoConnect WEB interface. |
||||
* No effects when the web server is not available. |
||||
*/ |
||||
void AutoConnect::handleClient() { |
||||
// Is there DNS Server process next request?
|
||||
if (_dnsServer) |
||||
_dnsServer->processNextRequest(); |
||||
// handleClient valid only at _webServer activated.
|
||||
if (_webServer) |
||||
_webServer->handleClient(); |
||||
|
||||
handleRequest(); |
||||
} |
||||
|
||||
/**
|
||||
* Handling for the AutoConnect menu request. |
||||
*/ |
||||
void AutoConnect::handleRequest() { |
||||
// Handling processing requests to AutoConnect.
|
||||
if (_rfConnect) { |
||||
// Leave from the AP currently.
|
||||
if (WiFi.status() == WL_CONNECTED) { |
||||
WiFi.disconnect(); |
||||
delay(100); |
||||
} |
||||
|
||||
// An attempt to establish a new AP.
|
||||
AC_DBG("Request for %s\n", (const char*)_credential.ssid); |
||||
WiFi.begin((const char*)_credential.ssid, (const char*)_credential.password); |
||||
if (_waitForConnect(_portalTimeout) == WL_CONNECTED) { |
||||
memcpy(_credential.bssid, WiFi.BSSID(), sizeof(station_config::bssid)); |
||||
_currentHostIP = WiFi.localIP(); |
||||
_redirectURI = String(AUTOCONNECT_URI_SUCCESS); |
||||
|
||||
// Save current credential
|
||||
if (_apConfig.autoSave == AC_SAVECREDENTIAL_AUTO) { |
||||
AutoConnectCredential credit; |
||||
credit.save(&_credential); |
||||
AC_DBG("%s credential saved\n", _credential.ssid); |
||||
} |
||||
} |
||||
else { |
||||
_currentHostIP = WiFi.softAPIP(); |
||||
_redirectURI = String(AUTOCONNECT_URI_FAIL); |
||||
} |
||||
_rfConnect = false; |
||||
} |
||||
|
||||
if (_rfReset) { |
||||
// Reset or disconnect by portal operation result
|
||||
_stopPortal(); |
||||
AC_DBG("Reset"); |
||||
delay(1000); |
||||
ESP.reset(); |
||||
delay(1000); |
||||
} |
||||
|
||||
if (_rfDisconnect) { |
||||
// Disconnect from the current AP.
|
||||
_stopPortal(); |
||||
WiFi.disconnect(); |
||||
while (WiFi.status() == WL_CONNECTED) { |
||||
delay(100); |
||||
yield(); |
||||
} |
||||
AC_DBG("Disconnected"); |
||||
// Reset disconnection request, restore the menu title.
|
||||
_rfDisconnect = false; |
||||
_menuTitle = String(AUTOCONNECT_MENU_TITLE); |
||||
|
||||
if (_apConfig.autoReset) { |
||||
delay(1000); |
||||
ESP.reset(); |
||||
delay(1000); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Register the exit routine for the starting captive portal. |
||||
* @param fn A function of the exit routine. |
||||
*/ |
||||
void AutoConnect::onDetect(DetectExit_ft fn) { |
||||
_onDetectExit = fn; |
||||
} |
||||
|
||||
/**
|
||||
* Register the handler function for undefined url request detected. |
||||
* @param fn A function of the not found handler. |
||||
*/ |
||||
void AutoConnect::onNotFound(ESP8266WebServer::THandlerFunction fn) { |
||||
_notFoundHandler = fn; |
||||
} |
||||
|
||||
/**
|
||||
* Disconnect from the AP and stop the AutoConnect portal. |
||||
* Stops DNS server and flush tcp sending. |
||||
*/ |
||||
void AutoConnect::_stopPortal() { |
||||
if (_dnsServer && _webServerAlloc == AC_WEBSERVER_HOSTED) |
||||
_dnsServer->stop(); |
||||
|
||||
if (_webServer) { |
||||
_webServer->client().stop(); |
||||
delay(1000); |
||||
} |
||||
|
||||
WiFi.softAPdisconnect(false); |
||||
} |
||||
|
||||
/**
|
||||
* 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. |
||||
*/ |
||||
bool AutoConnect::_captivePortal() { |
||||
String hostHeader = _webServer->hostHeader(); |
||||
if (!_isIP(hostHeader) && (hostHeader != WiFi.localIP().toString())) { |
||||
String location = String("http://") + _webServer->client().localIP().toString() + String(AUTOCONNECT_URI); |
||||
_webServer->sendHeader("Location", location, true); |
||||
_webServer->send(302, "text/plain", ""); |
||||
_webServer->client().flush(); |
||||
_webServer->client().stop(); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/**
|
||||
* A handler that redirects access to the captive portal to the connection |
||||
* configuration page. |
||||
*/ |
||||
void AutoConnect::_handleNotFound() { |
||||
if (!_captivePortal()) { |
||||
if (_notFoundHandler) { |
||||
_notFoundHandler(); |
||||
} |
||||
else { |
||||
PageElement page404(_PAGE_404, { { "HEAD", std::bind(&AutoConnect::_token_HEAD, this, std::placeholders::_1) } }); |
||||
String html = page404.build(); |
||||
_webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate", true); |
||||
_webServer->sendHeader("Pragma", "no-cache"); |
||||
_webServer->sendHeader("Expires", "-1"); |
||||
_webServer->sendHeader("Content-Length", String(html.length())); |
||||
_webServer->send(404, "text/html", html); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Reset the ESP8266 module. |
||||
* It is called from the PageBuilder of the disconnect page and indicates |
||||
* the request for disconnection. It will be into progress after handleClient. |
||||
*/ |
||||
String AutoConnect::_induceReset(PageArgument& args) { |
||||
_rfReset = true; |
||||
return String("Reset in progress..."); |
||||
} |
||||
|
||||
/**
|
||||
* Disconnect from AP. |
||||
* It is called from the PageBuilder of the disconnect page and indicates |
||||
* the request for disconnection. It will be into progress after handleClient. |
||||
*/ |
||||
String AutoConnect::_induceDisconnect(PageArgument& args) { |
||||
_rfDisconnect = true; |
||||
_menuTitle = String("Disconnect"); |
||||
return ""; |
||||
} |
||||
|
||||
/**
|
||||
* Indicates a connection establishment request and returns a redirect |
||||
* response to the waiting for connection page. This is called from |
||||
* handling of the current request by PageBuilder triggered by handleClient(). |
||||
* If "Credential" exists in POST parameter, it reads from EEPROM. |
||||
* @param args http request arguments. |
||||
* @retval A redirect response including "Location:" header. |
||||
*/ |
||||
String AutoConnect::_induceConnect(PageArgument& args) { |
||||
// Retrieve credential from the post method content.
|
||||
if (args.hasArg(AUTOCONNECT_PARAMID_CRED)) { |
||||
// Read from EEPROM
|
||||
AutoConnectCredential credential; |
||||
struct station_config entry; |
||||
AC_DBG("Load credential:%s\n", args.arg(AUTOCONNECT_PARAMID_CRED).c_str()); |
||||
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.password), reinterpret_cast<const char*>(entry.password), sizeof(_credential.password)); |
||||
} |
||||
else { |
||||
// Credential had by the post parameter.
|
||||
strncpy(reinterpret_cast<char*>(_credential.ssid), args.arg(AUTOCONNECT_PARAMID_SSID).c_str(), sizeof(_credential.ssid)); |
||||
strncpy(reinterpret_cast<char*>(_credential.password), args.arg(AUTOCONNECT_PARAMID_PASS).c_str(), sizeof(_credential.password)); |
||||
} |
||||
|
||||
// Turn on the trigger to start WiFi.begin().
|
||||
_rfConnect = true; |
||||
|
||||
// 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("http://") + _webServer->client().localIP().toString() + String(AUTOCONNECT_URI_RESULT); |
||||
_webServer->sendHeader("Location", url, true); |
||||
//_webServer->sendHeader("Connection", "keep-alive");
|
||||
_webServer->send(302, "text/plain", ""); |
||||
_webServer->client().flush(); |
||||
_webServer->client().stop(); |
||||
_responsePage->cancel(); |
||||
|
||||
return ""; |
||||
} |
||||
|
||||
/**
|
||||
* Responds response as redirect to the connection result page. |
||||
* A destination as _redirectURI is indicated by loop to establish connection. |
||||
*/ |
||||
String AutoConnect::_invokeResult(PageArgument& args) { |
||||
String redirect = String("http://") + _currentHostIP.toString() + _redirectURI; |
||||
_webServer->sendHeader("Location", redirect, true); |
||||
_webServer->send(302, "text/plain", ""); |
||||
_webServer->client().flush(); |
||||
_webServer->client().stop(); |
||||
_responsePage->cancel(); |
||||
return ""; |
||||
} |
||||
|
||||
/**
|
||||
* Classify the requested URI to responsive page builder. |
||||
* There is always only one PageBuilder instance that can exist in |
||||
* AutoConnect for saving RAM. Invokes a subordinate function that |
||||
* dynamically generates a response page at handleRequest. This is |
||||
* a part of the handling of http request originated from handleClient. |
||||
*/ |
||||
bool AutoConnect::_classifyHandle(HTTPMethod method, String uri) { |
||||
AC_DBG("%s%s\n", _webServer->hostHeader().c_str(), uri.c_str()); |
||||
if (uri == _uri) { |
||||
return true; // The response page already exists.
|
||||
} |
||||
|
||||
// Dispose decrepit page
|
||||
_responsePage->clearElement(); |
||||
if (_currentPageElement != nullptr) |
||||
delete _currentPageElement; |
||||
_uri = String(); |
||||
|
||||
// Create the page dynamically
|
||||
if ((_currentPageElement = _setupPage(uri)) != nullptr) { |
||||
_uri = String(uri); |
||||
_responsePage->addElement(*_currentPageElement); |
||||
} |
||||
_responsePage->setUri(_uri.c_str()); |
||||
AC_DBG("Page[%s] allocated\n", _responsePage->uri()); |
||||
|
||||
return _currentPageElement != nullptr ? true : false; |
||||
} |
||||
|
||||
/**
|
||||
* It checks whether the specified character string is a valid IP address. |
||||
* @param ipStr IP string for validation. |
||||
* @return true Valid. |
||||
*/ |
||||
bool AutoConnect::_isIP(String ipStr) { |
||||
for (uint8_t i = 0; i < ipStr.length(); i++) { |
||||
char c = ipStr.charAt(i); |
||||
if (c != '.' && (c < '0' || c > '9')) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/**
|
||||
* Convert MAC address in uint8_t array to Sting XX:XX:XX:XX:XX:XX format. |
||||
* @param mac Array of MAC address 6 bytes. |
||||
* @retval MAC address string in XX:XX:XX:XX:XX:XX format. |
||||
*/ |
||||
String AutoConnect::_toMACAddressString(const uint8_t mac[]) { |
||||
String macAddr = ""; |
||||
for (uint8_t i = 0; i < 6; i++) { |
||||
char buf[3]; |
||||
sprintf(buf, "%02X", mac[i]); |
||||
macAddr += buf; |
||||
if (i < 5) |
||||
macAddr += ':'; |
||||
} |
||||
return macAddr; |
||||
} |
||||
|
||||
/**
|
||||
* Convert dBm to the wifi signal quality. |
||||
* @param rssi dBm. |
||||
* @retval a signal quality percentage. |
||||
*/ |
||||
unsigned int AutoConnect::_toWiFiQuality(int32_t rssi) { |
||||
unsigned int qu; |
||||
if (rssi == 31) // WiFi signal is weak and RSSI value is unreliable.
|
||||
qu = 0; |
||||
else if (rssi <= -100) |
||||
qu = 0; |
||||
else if (rssi >= -50) |
||||
qu = 100; |
||||
else |
||||
qu = 2 * (rssi + 100); |
||||
return qu; |
||||
} |
||||
|
||||
/**
|
||||
* Wait for establishment of the connection until the specified time expires. |
||||
* @param timeout Expiration time by millisecond unit. |
||||
* @retval wl_status_t |
||||
*/ |
||||
wl_status_t AutoConnect::_waitForConnect(unsigned long timeout) { |
||||
wl_status_t wifiStatus; |
||||
|
||||
AC_DBG("Connecting"); |
||||
unsigned long st = millis(); |
||||
while ((wifiStatus = WiFi.status()) != WL_CONNECTED) { |
||||
if (timeout) { |
||||
if (millis() - st > timeout) |
||||
break; |
||||
} |
||||
#ifdef AC_DEBUG |
||||
AC_DEBUG_PORT.print('.'); |
||||
#endif // AC_DEBUG
|
||||
delay(300); |
||||
} |
||||
AC_DBG("%s IP:%s\n", wifiStatus == WL_CONNECTED ? "established" : "time out", WiFi.localIP().toString().c_str()); |
||||
return wifiStatus; |
||||
} |
@ -0,0 +1,290 @@ |
||||
/**
|
||||
* Declaration of AutoConnect class and accompanying AutoConnectConfig class. |
||||
* @file AutoConnect.h |
||||
* @author hieromon@gmail.com |
||||
* @version 0.9.1 |
||||
* @date 2018-02-13 |
||||
* @copyright MIT license. |
||||
*/ |
||||
|
||||
#ifndef _AUTOCONNECT_H_ |
||||
#define _AUTOCONNECT_H_ |
||||
|
||||
#include <vector> |
||||
#include <memory> |
||||
#include <functional> |
||||
#include <DNSServer.h> |
||||
#include <ESP8266WiFi.h> |
||||
#include <ESP8266WebServer.h> |
||||
#include <EEPROM.h> |
||||
#include <PageBuilder.h> |
||||
extern "C" { |
||||
#include <user_interface.h> |
||||
} |
||||
#include "AutoConnectPage.h" |
||||
|
||||
// Uncomment the following AC_DEBUG to enable debug output.
|
||||
//#define AC_DEBUG
|
||||
|
||||
// Debug output destination can be defined externally with AC_DEBUG_PORT
|
||||
#ifndef AC_DEBUG_PORT |
||||
#define AC_DEBUG_PORT Serial |
||||
#endif |
||||
#ifdef AC_DEBUG |
||||
#define AC_DBG(...) do {AC_DEBUG_PORT.print("[AC] "); AC_DEBUG_PORT.printf( __VA_ARGS__ );} while (0) |
||||
#else |
||||
#define AC_DBG(...) |
||||
#endif |
||||
|
||||
#ifndef AUTOCONNECT_APID |
||||
#define AUTOCONNECT_APID "esp8266ap" |
||||
#endif |
||||
|
||||
#ifndef AUTOCONNECT_PSK |
||||
#define AUTOCONNECT_PSK "12345678" |
||||
#endif |
||||
|
||||
#ifndef AUTOCONNECT_AP_IP |
||||
#define AUTOCONNECT_AP_IP 0x01F4A8C0 //*< 192.168.244.1 */
|
||||
#endif // !AUTOCONNECT_AP_IP
|
||||
#ifndef AUTOCONNECT_AP_GW |
||||
#define AUTOCONNECT_AP_GW 0x01F4A8C0 //*< 192.168.244.1 */
|
||||
#endif // !AUTOCONNECT_AP_GW
|
||||
#ifndef AUTOCONNECT_AP_NM |
||||
#define AUTOCONNECT_AP_NM 0x00FFFFFF //*< 255.255.255.0 */
|
||||
#endif // !AUTOCONNECT_AP_NM
|
||||
|
||||
#ifndef AUTOCONNECT_URI |
||||
#define AUTOCONNECT_URI "/_ac" |
||||
#endif |
||||
|
||||
#ifndef AUTOCONNECT_HOMEURI |
||||
#define AUTOCONNECT_HOMEURI "/" |
||||
#endif |
||||
|
||||
#ifndef AUTOCONNECT_MENU_TITLE |
||||
#define AUTOCONNECT_MENU_TITLE "AutoConnect" |
||||
#endif |
||||
#define AUTOCONNECT_MENU_TITLE_CONNETED "Connected" |
||||
|
||||
#define AUTOCONNECT_URI_CONFIG AUTOCONNECT_URI "/config" |
||||
#define AUTOCONNECT_URI_CONNECT AUTOCONNECT_URI "/connect" |
||||
#define AUTOCONNECT_URI_RESULT AUTOCONNECT_URI "/result" |
||||
#define AUTOCONNECT_URI_OPEN AUTOCONNECT_URI "/open" |
||||
#define AUTOCONNECT_URI_DISCON AUTOCONNECT_URI "/disc" |
||||
#define AUTOCONNECT_URI_RESET AUTOCONNECT_URI "/reset" |
||||
#define AUTOCONNECT_URI_SUCCESS AUTOCONNECT_URI "/success" |
||||
#define AUTOCONNECT_URI_FAIL AUTOCONNECT_URI "/fail" |
||||
|
||||
#ifndef AUTOCONNECT_TIMEOUT |
||||
#define AUTOCONNECT_TIMEOUT 30000 |
||||
#endif |
||||
|
||||
#ifndef AUTOCONNECT_STARTUPTIME |
||||
#define AUTOCONNECT_STARTUPTIME 10 |
||||
#endif |
||||
|
||||
#ifndef AUTOCONNECT_DNSPORT |
||||
#define AUTOCONNECT_DNSPORT 53 |
||||
#endif |
||||
|
||||
/**< A type to save established credential at WiFi.begin automatically. */ |
||||
typedef enum AC_SAVECREDENTIAL { |
||||
AC_SAVECREDENTIAL_NEVER, |
||||
AC_SAVECREDENTIAL_AUTO |
||||
} AC_SAVECREDENTIAL_t; |
||||
|
||||
class AutoConnectConfig { |
||||
public: |
||||
/**
|
||||
* AutoConnectConfig default constructor. |
||||
* SSID for the captive portal access point assumes AUTOCONNECT_APID which |
||||
* assigned from macro. Password is same as above too. |
||||
*/ |
||||
AutoConnectConfig() : |
||||
apip(AUTOCONNECT_AP_IP), |
||||
gateway(AUTOCONNECT_AP_GW), |
||||
netmask(AUTOCONNECT_AP_NM), |
||||
apid(String(AUTOCONNECT_APID)), |
||||
psk(String(AUTOCONNECT_PSK)), |
||||
channel(1), |
||||
hidden(0), |
||||
autoSave(AC_SAVECREDENTIAL_AUTO), |
||||
autoReset(true), |
||||
uptime(AUTOCONNECT_STARTUPTIME), |
||||
homeUri(AUTOCONNECT_HOMEURI) {} |
||||
/**
|
||||
* Configure by SSID for the captive portal access point and password. |
||||
*/ |
||||
AutoConnectConfig(const char* ap, const char* password) : |
||||
apip(AUTOCONNECT_AP_IP), |
||||
gateway(AUTOCONNECT_AP_GW), |
||||
netmask(AUTOCONNECT_AP_NM), |
||||
apid(String(ap)), |
||||
psk(String(password)), |
||||
channel(1), |
||||
hidden(0), |
||||
autoSave(AC_SAVECREDENTIAL_AUTO), |
||||
autoReset(true), |
||||
uptime(AUTOCONNECT_STARTUPTIME), |
||||
homeUri(AUTOCONNECT_HOMEURI) {} |
||||
|
||||
~AutoConnectConfig() {} |
||||
|
||||
AutoConnectConfig& operator=(const AutoConnectConfig& o) { |
||||
apip = o.apip; |
||||
gateway = o.gateway; |
||||
netmask = o.netmask; |
||||
apid = o.apid; |
||||
psk = o.psk; |
||||
channel = o.channel; |
||||
hidden = o.hidden; |
||||
autoSave = o.autoSave; |
||||
autoReset = o.autoReset; |
||||
uptime = o.uptime; |
||||
homeUri = o.homeUri; |
||||
return *this; |
||||
} |
||||
|
||||
IPAddress apip; /**< SoftAP IP address */ |
||||
IPAddress gateway; /**< SoftAP gateway address */ |
||||
IPAddress netmask; /**< SoftAP subnet mask */ |
||||
String apid; /**< SoftAP SSID */ |
||||
String psk; /**< SoftAP password */ |
||||
uint8_t channel; /**< SoftAP used wifi channel */ |
||||
uint8_t hidden; /**< SoftAP SSID hidden */ |
||||
AC_SAVECREDENTIAL_t autoSave; /**< Auto save credential */ |
||||
bool autoReset; /**< Reset ESP8266 module automatically when WLAN disconnected. */ |
||||
int uptime; /**< Length of start up time */ |
||||
String homeUri; /**< A URI of user site */ |
||||
}; |
||||
|
||||
class AutoConnect { |
||||
public: |
||||
AutoConnect(); |
||||
AutoConnect(ESP8266WebServer& webServer); |
||||
~AutoConnect(); |
||||
bool config(AutoConnectConfig& Config); |
||||
bool config(const char* ap, const char* password = nullptr); |
||||
void home(String uri); |
||||
bool begin(); |
||||
bool begin(const char* ssid, const char* passphrase); |
||||
bool begin(const char* ssid, const char* passphrase, unsigned long timeout); |
||||
void end(); |
||||
void handleClient(); |
||||
void handleRequest(); |
||||
ESP8266WebServer& host(); |
||||
|
||||
typedef std::function<bool(IPAddress)> DetectExit_ft; |
||||
void onDetect(DetectExit_ft fn); |
||||
void onNotFound(ESP8266WebServer::THandlerFunction fn); |
||||
|
||||
protected: |
||||
enum _webServerAllocateType { |
||||
AC_WEBSERVER_PARASITIC, |
||||
AC_WEBSERVER_HOSTED |
||||
}; |
||||
typedef enum _webServerAllocateType AC_WEBSERVER_TYPE; |
||||
void _initialize(); |
||||
bool _config(); |
||||
void _startWebServer(); |
||||
void _startDNSServer(); |
||||
void _handleNotFound(); |
||||
void _stopPortal(); |
||||
bool _classifyHandle(HTTPMethod mothod, String uri); |
||||
PageElement* _setupPage(String uri); |
||||
|
||||
/** Request handlers implemented by Page Builder */ |
||||
String _induceConnect(PageArgument& args); |
||||
String _induceDisconnect(PageArgument& args); |
||||
String _induceReset(PageArgument& args); |
||||
String _invokeResult(PageArgument& args); |
||||
|
||||
/** For portal control */ |
||||
bool _captivePortal(); |
||||
bool _isIP(String ipStr); |
||||
wl_status_t _waitForConnect(unsigned long timeout); |
||||
|
||||
/** Utilities */ |
||||
static String _toMACAddressString(const uint8_t mac[]); |
||||
static unsigned int _toWiFiQuality(int32_t rssi); |
||||
DetectExit_ft _onDetectExit; |
||||
ESP8266WebServer::THandlerFunction _notFoundHandler; |
||||
|
||||
std::unique_ptr<ESP8266WebServer> _webServer; |
||||
std::unique_ptr<DNSServer> _dnsServer; |
||||
AC_WEBSERVER_TYPE _webServerAlloc; |
||||
|
||||
PageBuilder* _responsePage; |
||||
PageElement* _currentPageElement; |
||||
|
||||
/** configurations */ |
||||
AutoConnectConfig _apConfig; |
||||
struct station_config _credential; |
||||
uint8_t _hiddenSSIDCount; |
||||
unsigned long _portalTimeout; |
||||
|
||||
/** The control indicators */ |
||||
bool _rfConnect; /**< URI /connect requested */ |
||||
bool _rfDisconnect; /**< URI /disc requested */ |
||||
bool _rfReset; /**< URI /reset requested */ |
||||
|
||||
String _uri; |
||||
String _redirectURI; |
||||
IPAddress _currentHostIP; |
||||
String _menuTitle; |
||||
|
||||
/** PegeElements of AutoConnect site. */ |
||||
static const char _CSS_BASE[] PROGMEM; |
||||
static const char _CSS_UL[] PROGMEM; |
||||
static const char _CSS_ICON_LOCK[] PROGMEM; |
||||
static const char _CSS_INPUT_BUTTON[] PROGMEM; |
||||
static const char _CSS_INPUT_TEXT[] PROGMEM; |
||||
static const char _CSS_TABLE[] PROGMEM; |
||||
static const char _CSS_LUXBAR[] PROGMEM; |
||||
static const char _ELM_HTML_HEAD[] PROGMEM; |
||||
static const char _ELM_MENU[] PROGMEM; |
||||
static const char _PAGE_STAT[] PROGMEM; |
||||
static const char _PAGE_CONFIGNEW[] PROGMEM; |
||||
static const char _PAGE_OPENCREDT[] PROGMEM; |
||||
static const char _PAGE_SUCCESS[] PROGMEM; |
||||
static const char _PAGE_RESETTING[] PROGMEM; |
||||
static const char _PAGE_DISCONN[] PROGMEM; |
||||
static const char _PAGE_FAIL[] PROGMEM; |
||||
static const char _PAGE_404[] PROGMEM; |
||||
|
||||
/** Token handlers for PageBuilder */ |
||||
String _token_CSS_BASE(PageArgument& args); |
||||
String _token_CSS_UL(PageArgument& args); |
||||
String _token_CSS_ICON_LOCK(PageArgument& args); |
||||
String _token_CSS_INPUT_BUTTON(PageArgument& args); |
||||
String _token_CSS_INPUT_TEXT(PageArgument& args); |
||||
String _token_CSS_TABLE(PageArgument& args); |
||||
String _token_CSS_LUXBAR(PageArgument& args); |
||||
String _token_HEAD(PageArgument& args); |
||||
String _token_MENU(PageArgument& args); |
||||
String _token_ESTAB_SSID(PageArgument& args); |
||||
String _token_WIFI_MODE(PageArgument& args); |
||||
String _token_WIFI_STATUS(PageArgument& args); |
||||
String _token_STATION_STATUS(PageArgument& args); |
||||
String _token_LOCAL_IP(PageArgument& args); |
||||
String _token_SOFTAP_IP(PageArgument& args); |
||||
String _token_GATEWAY(PageArgument& args); |
||||
String _token_NETMASK(PageArgument& args); |
||||
String _token_AP_MAC(PageArgument& args); |
||||
String _token_STA_MAC(PageArgument& args); |
||||
String _token_CHANNEL(PageArgument& args); |
||||
String _token_DBM(PageArgument& args); |
||||
String _token_CPU_FREQ(PageArgument& args); |
||||
String _token_FLASH_SIZE(PageArgument& args); |
||||
String _token_CHIP_ID(PageArgument& args); |
||||
String _token_FREE_HEAP(PageArgument& args); |
||||
String _token_LIST_SSID(PageArgument& args); |
||||
String _token_HIDDEN_COUNT(PageArgument& args); |
||||
String _token_OPEN_SSID(PageArgument& args); |
||||
String _token_UPTIME(PageArgument& args); |
||||
|
||||
friend class ESP8266WebServer; |
||||
}; |
||||
|
||||
#endif // _AUTOCONNECT_H_
|
@ -0,0 +1,276 @@ |
||||
/**
|
||||
* AutoConnectCredentail class implementation. |
||||
* @file AutoConnectCredentail.cpp |
||||
* @author hieromon@gmail.com |
||||
* @version 1.0.0 |
||||
* @date 2018-02-17 |
||||
* @copyright MIT license. |
||||
*/ |
||||
|
||||
#include <EEPROM.h> |
||||
#include "AutoConnectCredentail.h" |
||||
|
||||
#define AC_IDENTIFIER "AC_CREDT" |
||||
#define AC_HEADERSIZE ((int)(AC_IDENTIFIER_OFFSET + sizeof(AC_IDENTIFIER) - 1 + sizeof(uint8_t) + sizeof(uint16_t))) |
||||
|
||||
/**
|
||||
* AutoConnectCredential constructor takes the available count of saved |
||||
* entries. |
||||
* A stored credential data structure in EEPROM. |
||||
* 0 7 8 9a b (t) |
||||
* +--------+-+--+-----------------+-----------------+--+ |
||||
* |AC_CREDT|e|ss|ssid\0pass\0bssid|ssid\0pass\0bssid|\0| |
||||
* +--------+-+--+-----------------+-----------------+--+ |
||||
* AC_CREDT : Identifier. 8 characters. |
||||
* e : Number of contained entries(uint8_t). |
||||
* ss : Container size, excluding ID and number of entries(uint16_t). |
||||
* ssid: SSID string with null termination. |
||||
* password : Password string with null termination. |
||||
* bssid : BSSID 6 bytes. |
||||
* t : The end of the container is a continuous '\0'. |
||||
* The AC_CREDT identifier is at the beginning of the area. |
||||
* SSID and PASSWORD are terminated by '\ 0'. |
||||
* Free area are filled with FF, which is reused as an area for insertion. |
||||
*/ |
||||
AutoConnectCredential::AutoConnectCredential() { |
||||
char id_c[sizeof(AC_IDENTIFIER) - 1]; |
||||
uint8_t c; |
||||
|
||||
EEPROM.begin(AC_HEADERSIZE); |
||||
|
||||
// Validate the save area of the EEPROM.
|
||||
// If it is a valid area, retrieve the stored number of entries,
|
||||
// if the identifier is not saved, initialize the EEPROM area.
|
||||
for (c = AC_IDENTIFIER_OFFSET; c < AC_IDENTIFIER_OFFSET + sizeof(id_c); c++) { |
||||
id_c[c] = static_cast<char>(EEPROM.read(c)); |
||||
} |
||||
if (!strncmp(id_c, AC_IDENTIFIER, sizeof(id_c))) { |
||||
_entries = EEPROM.read(static_cast<int>(c++)); |
||||
_containSize = EEPROM.read(static_cast<int>(c++)); |
||||
_containSize += EEPROM.read(static_cast<int>(c)) << 8; |
||||
} |
||||
else { |
||||
_entries = 0; |
||||
_containSize = 0; |
||||
} |
||||
|
||||
EEPROM.end(); |
||||
} |
||||
|
||||
/**
|
||||
* The destructor ends EEPROM access. |
||||
*/ |
||||
AutoConnectCredential::~AutoConnectCredential() { |
||||
EEPROM.end(); |
||||
} |
||||
|
||||
/**
|
||||
* Delete the credential entry for the specified SSID in the EEPROM. |
||||
* @param ssid A SSID character string to be deleted. |
||||
* @retval true The entry successfully delete. |
||||
* false Could not deleted. |
||||
*/ |
||||
bool AutoConnectCredential::del(const char* ssid) { |
||||
struct station_config entry; |
||||
bool rc = false; |
||||
|
||||
if (load(ssid, &entry) >= 0) { |
||||
// Saved credential detected, _ep has the entry location.
|
||||
EEPROM.begin(AC_HEADERSIZE + _containSize); |
||||
_dp = _ep; |
||||
|
||||
// Erase SSID
|
||||
while (EEPROM.read(_dp) != 0x00) |
||||
EEPROM.write(_dp++, 0xff); |
||||
|
||||
// Erase Password
|
||||
EEPROM.write(_dp++, 0xff); |
||||
while (EEPROM.read(_dp) != 0x00) |
||||
EEPROM.write(_dp++, 0xff); |
||||
|
||||
// Erase BSSID
|
||||
EEPROM.write(_dp++, 0xff); |
||||
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++) |
||||
EEPROM.write(_dp++, 0xff); |
||||
|
||||
// End 0xff writing, update headers.
|
||||
_entries--; |
||||
EEPROM.write(static_cast<int>(sizeof(AC_IDENTIFIER)) - 1, _entries); |
||||
|
||||
// commit it.
|
||||
rc = EEPROM.commit(); |
||||
delay(10); |
||||
EEPROM.end(); |
||||
} |
||||
return rc; |
||||
} |
||||
|
||||
/**
|
||||
* Load the credential entry for the specified SSID from the EEPROM. |
||||
* The credentials are stored to the station_config structure which specified |
||||
* by *config as the SSID and password. |
||||
* @param ssid A SSID character string to be loaded. |
||||
* @param config A station_config structure pointer. |
||||
* @retval The entry number of the SSID in EEPROM. If the number less than 0, |
||||
* the specified SSID was not found. |
||||
*/ |
||||
int8_t AutoConnectCredential::load(const char* ssid, struct station_config* config) { |
||||
int8_t entry = -1; |
||||
|
||||
_dp = AC_HEADERSIZE; |
||||
if (_entries) { |
||||
EEPROM.begin(AC_HEADERSIZE + _containSize); |
||||
for (uint8_t i = 0; i < _entries; i++) { |
||||
_retrieveEntry(reinterpret_cast<char*>(config->ssid), reinterpret_cast<char*>(config->password), config->bssid); |
||||
if (!strcmp(ssid, (const char*)config->ssid)) { |
||||
entry = i; |
||||
break; |
||||
} |
||||
} |
||||
EEPROM.end(); |
||||
} |
||||
return entry; |
||||
} |
||||
|
||||
/**
|
||||
* Load the credential entry for the specified number from the EEPROM. |
||||
* The credentials are stored to the station_config structure which specified |
||||
* by *config as the SSID and password. |
||||
* @param entry A number of entry to be loaded. |
||||
* @param config A station_config structure pointer. |
||||
* @retval true The entry number of the SSID in EEPROM. |
||||
* false The number is not available. |
||||
*/ |
||||
bool AutoConnectCredential::load(int8_t entry, struct station_config* config) { |
||||
_dp = AC_HEADERSIZE; |
||||
if (_entries && entry < _entries) { |
||||
EEPROM.begin(AC_HEADERSIZE + _containSize); |
||||
while (entry-- >= 0) |
||||
_retrieveEntry(reinterpret_cast<char*>(config->ssid), reinterpret_cast<char*>(config->password), config->bssid); |
||||
EEPROM.end(); |
||||
return true; |
||||
} |
||||
else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Save SSID and password to EEPROM. |
||||
* When the same SSID already exists, it will be replaced. If the current |
||||
* entry size insufficient for the new entry, the entry will be appended |
||||
* and increase whole size. Its previous areas are freed with FF and reused. |
||||
* @param config A pointer to the station_config structure storing SSID and password. |
||||
* @retval true Successfully saved. |
||||
* @retval false EEPROM commit failed. |
||||
*/ |
||||
bool AutoConnectCredential::save(const struct station_config* config) { |
||||
static const char _id[] = AC_IDENTIFIER; |
||||
struct station_config stage; |
||||
int8_t entry; |
||||
bool rep = false; |
||||
|
||||
// Detect same entry for replacement.
|
||||
entry = load((const char*)(config->ssid), &stage); |
||||
|
||||
// Saving start.
|
||||
EEPROM.begin(AC_HEADERSIZE + _containSize + sizeof(struct station_config)); |
||||
|
||||
// Determine insertion or replacement.
|
||||
if (entry >= 0) { |
||||
// An entry with the same SSID was found, release the area for replacement.
|
||||
_dp = _ep; |
||||
for (uint8_t dm = 2; dm; _dp++) { |
||||
if (EEPROM.read(_dp) == '\0') |
||||
dm--; |
||||
EEPROM.write(_dp, 0xff); // Clear SSID, Passphrase
|
||||
} |
||||
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++) |
||||
EEPROM.write(_dp++, 0xff); // Clear BSSID
|
||||
} |
||||
else { |
||||
// Same entry not found. increase the entry.
|
||||
_entries++; |
||||
int i; |
||||
for (i = AC_IDENTIFIER_OFFSET; i < static_cast<int>(sizeof(_id)) - 1; i++) |
||||
EEPROM.write(i, (uint8_t)_id[i]); |
||||
EEPROM.write(i, _entries); |
||||
} |
||||
|
||||
// Seek insertion point, evaluate capacity to insert the new entry.
|
||||
uint8_t eSize = strlen((const char*)config->ssid) + strlen((const char*)config->password) + sizeof(station_config::bssid) + 2; |
||||
for (_dp = AC_HEADERSIZE; _dp < _containSize + AC_HEADERSIZE; _dp++) { |
||||
if (EEPROM.read(_dp) == 0xff) { |
||||
uint8_t fp = _dp; |
||||
while (EEPROM.read(++_dp) == 0xff) {} |
||||
if (_dp - fp >= eSize) { |
||||
_dp = fp; |
||||
rep = true; |
||||
break; |
||||
} |
||||
_dp--; |
||||
} |
||||
} |
||||
|
||||
// Save new entry
|
||||
uint8_t c; |
||||
const uint8_t* dt; |
||||
dt = config->ssid; |
||||
do { // Write SSID
|
||||
c = *dt++; |
||||
EEPROM.write(_dp++, c); |
||||
} while (c != '\0'); |
||||
dt = config->password; |
||||
do { // Write password
|
||||
c = *dt++; |
||||
EEPROM.write(_dp++, c); |
||||
} while (c != '\0'); |
||||
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++) |
||||
EEPROM.write(_dp++, config->bssid[i]); // write BSSID
|
||||
// Terminate container, mark to the end of credential area.
|
||||
// When the entry is replaced, not mark a terminator.
|
||||
if (!rep) { |
||||
EEPROM.write(_dp, '\0'); |
||||
|
||||
// Update container size
|
||||
_containSize = _dp - AC_HEADERSIZE; |
||||
EEPROM.write(sizeof(AC_IDENTIFIER) - 1 + sizeof(uint8_t), (uint8_t)_containSize); |
||||
EEPROM.write(sizeof(AC_IDENTIFIER) - 1 + sizeof(uint8_t) + 1, (uint8_t)(_containSize >> 8)); |
||||
} |
||||
|
||||
bool rc = EEPROM.commit(); |
||||
delay(10); |
||||
EEPROM.end(); |
||||
|
||||
return rc; |
||||
} |
||||
|
||||
/**
|
||||
* Get the SSID and password from EEPROM indicated by _dp as the pointer |
||||
* of current read address. FF is skipped as unavailable area. |
||||
* @param ssid A SSID storing address. |
||||
* @param password A password storing address. |
||||
*/ |
||||
void AutoConnectCredential::_retrieveEntry(char* ssid, char* password, uint8_t* bssid) { |
||||
uint8_t ec; |
||||
|
||||
// Skip unavailable entry.
|
||||
while ((ec = EEPROM.read(_dp++)) == 0xff) {} |
||||
|
||||
// Retrieve SSID
|
||||
_ep = _dp - 1; |
||||
*ssid++ = ec; |
||||
do { |
||||
ec = EEPROM.read(_dp++); |
||||
*ssid++ = ec; |
||||
} while (ec != '\0'); |
||||
// Retrieve Password
|
||||
do { |
||||
ec = EEPROM.read(_dp++); |
||||
*password++ = ec; |
||||
} while (ec != '\0'); |
||||
// Retrieve BSSID
|
||||
for (uint8_t i = 0; i < sizeof(station_config::bssid); i++) { |
||||
bssid[i] = EEPROM.read(_dp++); |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
/**
|
||||
* Declaration of AutoConnectCredential class. |
||||
* @file AutoConnectCredential.h |
||||
* @author hieromon@gmail.com |
||||
* @version 1.0.0 |
||||
* @date 2018-02-17 |
||||
* @copyright MIT license. |
||||
*/ |
||||
|
||||
#ifndef _AUTOCONNECTCREDENTAIL_H_ |
||||
#define _AUTOCONNECTCREDENTAIL_H_ |
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100 |
||||
#include "arduino.h" |
||||
#else |
||||
#include "WProgram.h" |
||||
#endif |
||||
extern "C" { |
||||
#include <user_interface.h> |
||||
} |
||||
|
||||
/** Credential storage area offset specifier in EEPROM.
|
||||
* By defining AC_IDENTIFIER_OFFSET macro in the user sketch, the credential |
||||
* storage area can be shifted in EEPROM. |
||||
*/ |
||||
#ifndef AC_IDENTIFIER_OFFSET |
||||
#define AC_IDENTIFIER_OFFSET 0 |
||||
#endif |
||||
|
||||
/** AutoConnectCredential class. */ |
||||
class AutoConnectCredential { |
||||
public: |
||||
AutoConnectCredential(); |
||||
~AutoConnectCredential(); |
||||
uint8_t entries() { return _entries; } |
||||
bool del(const char* ssid); |
||||
int8_t load(const char* ssid, struct station_config* config); |
||||
bool load(int8_t entry, struct station_config* config); |
||||
bool save(const struct station_config* config); |
||||
|
||||
private: |
||||
void _retrieveEntry(char* ssid, char* password, uint8_t* bssid); /**< Read an available entry. */ |
||||
uint8_t _entries; /**< Count of the available entry */ |
||||
uint16_t _containSize; /**< Container size */ |
||||
int _dp; /**< The current address in EEPROM */ |
||||
int _ep; /**< The current entry address in EEPROM */ |
||||
}; |
||||
|
||||
#endif // _AUTOCONNECTCREDENTAIL_H_
|
@ -0,0 +1,71 @@ |
||||
/**
|
||||
* AutoConnect portal site web page declaration. |
||||
* @file AutoConnectPage.h |
||||
* @author hieromon@gmail.com |
||||
* @version 0.9.1 |
||||
* @date 2018-02-13 |
||||
* @copyright MIT license. |
||||
*/ |
||||
|
||||
#ifndef _AUTOCONNECTPAGE_H_ |
||||
#define _AUTOCONNECTPAGE_H_ |
||||
|
||||
#define AUTOCONNECT_PARAMID_SSID "SSID" |
||||
#define AUTOCONNECT_PARAMID_PASS "Passphrase" |
||||
#define AUTOCONNECT_PARAMID_CRED "Credential" |
||||
|
||||
// AutoConnect menu hyper-link as image
|
||||
#define AUTOCONNECT_GLYPH_COG_24 "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAC2klEQVRIS61VvWsUQRSfmU2p" \ |
||||
"on9BUIkQUaKFaCBKgooSb2d3NSSFKbQR/KrEIiIKBiGF2CgRxEpjQNHs7mwOUcghwUQ7g58I" \
|
||||
"sbGxEBWsb2f8zR177s3t3S2cA8ftzPu993vzvoaSnMu2vRKlaqgKp74Q/tE8qjQPyHGcrUrR" \
|
||||
"jwlWShmDbFMURd/a6TcQwNiYUmpFCPElUebcuQ2vz6aNATMVReHEPwzfSSntDcNwNo2rI+Dc" \
|
||||
"vQzhpAbA40VKyV0p1Q9snzBG1qYVcYufXV1sREraDcxpyHdXgkfpRBj6Uwm2RsC5dxxmZ9pd" \
|
||||
"OY9cKTISRcHTCmGiUCh4fYyplTwG2mAUbtMTBMHXOgK9QfyXEZr+TkgQ1oUwDA40hEgfIAfj" \
|
||||
"+HuQRaBzAs9eKyUZ5Htx+T3ZODKG8DzOJMANhmGomJVMXPll+hx9UUAlzZrJJ4QNCDG3VEfg" \
|
||||
"uu7mcpmcB/gkBOtShhQhchAlu5jlLUgc9ENgyP5gf9+y6LTv+58p5zySkgwzLNOIGc8sEoT1" \
|
||||
"Lc53NMlbCQQuvMxeCME1NNPVVkmH/i3IzzXDtCSA0qQQwZWOCJDY50jsQRjJmkslEOxvTcDR" \
|
||||
"O6zPxOh5xZglKkYLhWM9jMVnkIsTyMT6NBj7IbOCEjm6HxNVVTo2WXqEWJZ1T8rytB6Gxizy" \
|
||||
"DkPhWVpBqfiXUtbo/HywYJSpA9kMamNNPZ71R9Hcm+TMHHZNGw3EuraXEUldbfvw25UdOjqO" \
|
||||
"t+JhMwJd7+jSTpZaEiIcaCDwPK83jtWnTkwnunFMtxeL/ge9r4XItt1RNNaj/0GAcV2bR3U5" \
|
||||
"sG3nEh6M61US+Qrfd9Bs31GGulI2GOS/8dgcQZV1w+ApjIxB7TDwF9GcNzJzoA+rD0/8HvPn" \
|
||||
"XQJCt2qFCwbBTfRI7UyXumWVt+HJ9NO4XI++bdsb0YyrqXmlh+AWOLHaLqS5CLQR5EggR3Yl" \
|
||||
"cVS9gKeH2hnX8r8Kmi1CAsl36QAAAABJRU5ErkJggg==" |
||||
#define AUTOCONNECT_GLYPH_COG_32 "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAETElEQVRYR61XX0hbVxg/5yYx" \ |
||||
"QlEnzM46HYw+7KVuq826Vba1UIRmevNHltHCZEKn7g9lLzJwD8W+dAXZy6i2jaV92BRcMZpc" \
|
||||
"g7Q+zLaTPXQb7E9toaCUwVbWOZlBk5jcc/Y7WCW5uffuXNvAJSH3933f73zn933nO5Rs4xMI" \
|
||||
"BN4kRLlebMpeSiQSvzh1R50aCHwgEPoGX5FiWz6cSMS7nfpzTACrr8Pq7yOQ2xBsTddz9clk" \
|
||||
"ctkJCVMCoVBoP2P8bcbcw1NT4/cKHapq8BSl9KRFkN5EYvKLwnfw9TJjrJtz90VNi/1ktCsh" \
|
||||
"IILrOrtGqVIlwJyTWQS84PW6Y+l0ukZRXD8QQmstCCzmcp5X0+kdqaqq5Xdg+yGwrz3CLisK" \
|
||||
"aZmcnPyx0LaIgKq2v0JIfmYzuCHIMlZSqYCBXYqBWQdmFZhqE1wJiSICwWBoEKw/crKH28B+" \
|
||||
"jm36bNOuiAAE9iIE9vM2nMqacM713ZqmLZoSEH+2tYXmsFfNsh4d4q5i9UcsNfCoxt/F91cO" \
|
||||
"HUvBKeXt8Xh8wpaA3+/3ejzePy1EJOoiw7lyjlJ9NJPJ3Flfr9MrKlYaFYV1oHp6IMAyczb8" \
|
||||
"wcrKvw2zs7N5WwKqqj7HOV0wUztWcB8qb8Ue3jYLgipqArGkWZnCLo39f9bYqLZE6PdHatzu" \
|
||||
"9S4EQe0q9cYAwoHLRX1I4bxdviFkH+y/B8bYKQlj5A/oK+p2K9FYLCayTCgazz68OIHfR/F4" \
|
||||
"rZ3zAfT6T2U2W1XD57GQHissFpMDEaGFQYqDhcs4JUT+tAOB10HgpoxfWQK8tnanNxqN5mSc" \
|
||||
"tra2Vrtcnn9ksFIEkDLm8zV5+vv7mYzTJ05ABIWC91ip30gqGAy+gUq6IUOWtrWFD6CGP8bx" \
|
||||
"G7GuYUGAn9G0eJ+M00AgGIW+u2ywa/A4gufsVhmGw+Gd+Tx7H8w/gEIbTIzXOHft07Txu3Yk" \
|
||||
"NmYJMmdWhhDyAkp0CIPLpc1+UDIPIH0Nus4XrRoRHL9l1QvEcU4p04B5xoQkjmi2C3NjyrYT" \
|
||||
"RiKRsmw29wAgs/NcqCGD9A6hHY+kUpXzFRVL4Ko0Yovew+pE2ksakAgIHf9VV1dbb6ykkgyo" \
|
||||
"augYpWRUZq+dYtAbjiJ7Y7YZAIFvQeCQU+cyeFEZmjZx0JIA+vgLSKOtyGQC2WGM5WwYycID" \
|
||||
"2Mvexw1iT4B/iXL+ZBNjHMl8jCkzKMOnjE4goiX8J4ZSz/8QRI0ToXSzSvgbcjxceIMqEaE4" \
|
||||
"TotJsO+g+KHycs94NputxrtbFn2CiHkhn8/vXV1dTVVWVgdQkj3Y9xaQQRz2EOP9YYjwV1sR" \
|
||||
"ipcbZzrt1HXlfDI58VuhAU5P0Q1Pm2UBAfowcZ0pfIcB53no6jhIjxmDC5zjqxkcPo17w+8w" \
|
||||
"LTeQyOJS0jA9feWhEw05JiCc4/5wGfeHzuJA7GvsbYeT4NvKgDDamP1Y0RWLMdo8NTUhRjFH" \
|
||||
"n/8AoRLGHM6hJDMAAAAASUVORK5CYII=" |
||||
#define AUTOCONNECT_GLYPH_BAR_32 "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAApklEQVRYR2NkGGDAOMD2M4w6" \ |
||||
"YDQEkEMgEJggZwCxGI0T5mug+alAvBFkD7IDXtLBcpjfXgEZ4ugOeAETpHEIgIwHeVYC3QH+" \
|
||||
"0CgAS9AQgCwHRcFmdAfQ0E7cRo9mw0EVAqPlAKhwEKVTVsBZDsyiQ2k4Wg6gxPKgyoZ0Sn+o" \
|
||||
"1iCHQBBQaiYQi9DYJTjbAyAJWluOtz0wWg7QOOqxGz+aDUdDYMBDAACA0x4hs/MPrwAAAABJ" \
|
||||
"RU5ErkJggg==" |
||||
#define AUTOCONNECT_GLYPH_BAR_48 "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABIUlEQVRoQ+1YywrCQBCzF72J" \ |
||||
"fqD6AYKKIPgLKiKKd/1DH0dPpgdBilvtTMp2JcJcLMnsJNtu2qyV+C9LfP0tDRDbQTkgB5wK" \
|
||||
"aAs5BXTDiw50wLhGDVE9NzuX4A66M2qBeryoiwPscWHC7UtnO4BxGhrg0kDliwpc8Uf/bwfY" \
|
||||
"YbIZ3XQuYb7GeciBNi6sUKN3m7j9zWz51jmhlmU3sZk9FlAHWSzlQ/dA7PVU7q8tVFkyMuBT" \
|
||||
"FtqixwDVJffy0v2UhY7oMvZ2qhlfmoVuDVS+UhZKfoAU4vTXLLSBZ018oVEWqvnhYqPXSWzT" \
|
||||
"jYeSAzwtbUzKQjbdXChlIZd8BHDphy1lIYLCIQploRrFtVPrJLZrx0HKAY6OdhY5YNeOg0ze" \
|
||||
"gScMDDAxQXzA7QAAAABJRU5ErkJggg==" |
||||
|
||||
// AutoConnect menu href
|
||||
#define AUTOCONNECT_LINK(s) "<a href=\"" AUTOCONNECT_URI "\"><img src=\"data:image/png;base64," AUTOCONNECT_GLYPH_ ##s "\" border=\"0\" title=\"AutoConnect menu\" alt=\"AutoConnect menu\"/></a>" |
||||
|
||||
#endif // _AutoConnectPage_H_
|