initial work on a wifi/http configuration module
11
components/wifi-manager/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
set(COMPONENT_ADD_INCLUDEDIRS .)
|
||||
|
||||
set(COMPONENT_SRCS "dns_server.c" "http_server.c" "json.c" "wifi_manager.c")
|
||||
set(REQUIRES esp_common)
|
||||
set(COMPONENT_EMBED_FILES "style.css jquery.gz code.js index.html")
|
||||
|
||||
set(REQUIRES_COMPONENTS freertos )
|
||||
|
||||
register_component()
|
||||
|
||||
|
||||
67
components/wifi-manager/Kconfig.projbuild
Normal file
@@ -0,0 +1,67 @@
|
||||
menu "Wifi Manager Configuration"
|
||||
|
||||
config WIFI_MANAGER_TASK_PRIORITY
|
||||
int "RTOS Task Priority for the wifi_manager"
|
||||
default 5
|
||||
help
|
||||
Tasks spawn by the manager will have a priority of WIFI_MANAGER_TASK_PRIORITY-1. For this particular reason, minimum recommended task priority is 2.
|
||||
|
||||
config WIFI_MANAGER_MAX_RETRY
|
||||
int "Max Retry on failed connection"
|
||||
default 2
|
||||
help
|
||||
Defines when a connection is lost/attempt to connect is made, how many retries should be made before giving up.
|
||||
|
||||
config DEFAULT_AP_SSID
|
||||
string "Access Point SSID"
|
||||
default "esp32"
|
||||
help
|
||||
SSID (network name) the the esp32 will broadcast.
|
||||
|
||||
config DEFAULT_AP_PASSWORD
|
||||
string "Access Point Password"
|
||||
default "esp32pwd"
|
||||
help
|
||||
Password used for the Access Point. Leave empty and set AUTH MODE to WIFI_AUTH_OPEN for no password.
|
||||
|
||||
config DEFAULT_AP_CHANNEL
|
||||
int "Access Point WiFi Channel"
|
||||
default 1
|
||||
help
|
||||
Be careful you might not see the access point if you use a channel not allowed in your country.
|
||||
|
||||
config DEFAULT_AP_IP
|
||||
string "Access Point IP Address"
|
||||
default "10.10.0.1"
|
||||
help
|
||||
This is used for the redirection to the captive portal. It is recommended to leave unchanged.
|
||||
|
||||
config DEFAULT_AP_GATEWAY
|
||||
string "Access Point IP Gateway"
|
||||
default "10.10.0.1"
|
||||
help
|
||||
This is used for the redirection to the captive portal. It is recommended to leave unchanged.
|
||||
|
||||
config DEFAULT_AP_NETMASK
|
||||
string "Access Point Netmask"
|
||||
default "255.255.255.0"
|
||||
help
|
||||
This is used for the redirection to the captive portal. It is recommended to leave unchanged.
|
||||
|
||||
config DEFAULT_AP_MAX_CONNECTIONS
|
||||
int "Access Point Max Connections"
|
||||
default 4
|
||||
help
|
||||
Max is 4.
|
||||
|
||||
config DEFAULT_AP_BEACON_INTERVAL
|
||||
int "Access Point Beacon Interval (ms)"
|
||||
default 100
|
||||
help
|
||||
100ms is the recommended default.
|
||||
config DEFAULT_COMMAND_LINE
|
||||
string "Default command line to execute"
|
||||
default "squeezelite -o I2S -b 500:2000 -d all=info"
|
||||
help
|
||||
This is the command to run when starting the device
|
||||
endmenu
|
||||
19
components/wifi-manager/LICENSE.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
41
components/wifi-manager/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# What is esp32-wifi-manager?
|
||||
*esp32-wifi-manager* is an esp32 program that enables easy management of wifi networks through a web application.
|
||||
|
||||
*esp32-wifi-manager* is **lightweight** (8KB of task stack in total) and barely uses any CPU power through a completely event driven architecture. It's an all in one wifi scanner, http server & dns daemon living in the least amount of RAM possible.
|
||||
|
||||
For real time constrained applications, *esp32-wifi-manager* can live entirely on PRO CPU, leaving the entire APP CPU untouched for your own needs.
|
||||
|
||||
*esp32-wifi-manager* will automatically attempt to re-connect to a previously saved network on boot, and it will start its own wifi access point through which you can manage wifi networks if a saved network cannot be found and/or if the connection is lost.
|
||||
|
||||
*esp32-wifi-manager* is an esp-idf project that compiles successfully with the esp-idf 3.2 release. You can simply copy the project and start adding your own code to it.
|
||||
|
||||
# Demo
|
||||
[](http://www.youtube.com/watch?v=hxlZi15bym4)
|
||||
|
||||
# Look and Feel
|
||||
 
|
||||
|
||||
# Adding esp32-wifi-manager to your code
|
||||
Ther are effectively three different ways you can embed esp32-wifi-manager with your code:
|
||||
* Just forget about it and poll in your code for wifi connectivity status
|
||||
* Use event callbacks
|
||||
* Modify esp32-wifi-manager code directly to fit your needs
|
||||
|
||||
**Event callbacks** are the cleanest way to use the wifi manager and that's the recommended way to do it. A typical use-case would be to get notified when wifi manager finally gets a connection an access point. In order to do this you can simply define a callback function:
|
||||
|
||||
```c
|
||||
void cb_connection_ok(void *pvParameter){
|
||||
ESP_LOGI(TAG, "I have a connection!");
|
||||
}
|
||||
```
|
||||
|
||||
Then just register it by calling:
|
||||
|
||||
```c
|
||||
wifi_manager_set_callback(EVENT_STA_GOT_IP, &cb_connection_ok);
|
||||
```
|
||||
|
||||
That's it! Now everytime the event is triggered it will call this function.
|
||||
|
||||
# License
|
||||
*esp32-wifi-manager* is MIT licensed. As such, it can be included in any project, commercial or not, as long as you retain original copyright. Please make sure to read the license file.
|
||||
12
components/wifi-manager/ap.json
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{"ssid":"Pantum-AP-A6D49F","chan":11,"rssi":-55,"auth":4},
|
||||
{"ssid":"a0308","chan":1,"rssi":-56,"auth":3},
|
||||
{"ssid":"dlink-D9D8","chan":11,"rssi":-82,"auth":4},
|
||||
{"ssid":"Linksys06730","chan":7,"rssi":-85,"auth":3},
|
||||
{"ssid":"SINGTEL-5171","chan":9,"rssi":-88,"auth":4},
|
||||
{"ssid":"1126-1","chan":11,"rssi":-89,"auth":4},
|
||||
{"ssid":"The Shah 5GHz-2","chan":1,"rssi":-90,"auth":3},
|
||||
{"ssid":"SINGTEL-1D28 (2G)","chan":11,"rssi":-91,"auth":3},
|
||||
{"ssid":"dlink-F864","chan":1,"rssi":-92,"auth":4},
|
||||
{"ssid":"dlink-74F0","chan":1,"rssi":-93,"auth":4}
|
||||
]
|
||||
454
components/wifi-manager/code.js
Normal file
@@ -0,0 +1,454 @@
|
||||
// First, checks if it isn't implemented yet.
|
||||
if (!String.prototype.format) {
|
||||
String.prototype.format = function() {
|
||||
var args = arguments;
|
||||
return this.replace(/{(\d+)}/g, function(match, number) {
|
||||
return typeof args[number] != 'undefined'
|
||||
? args[number]
|
||||
: match
|
||||
;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var apList = null;
|
||||
var selectedSSID = "";
|
||||
var refreshAPInterval = null;
|
||||
var checkStatusInterval = null;
|
||||
var checkConfigInterval = null;
|
||||
|
||||
var StatusIntervalActive = false;
|
||||
var ConfigIntervalActive = false;
|
||||
var RefreshAPIIntervalActive = false;
|
||||
|
||||
|
||||
function stopCheckStatusInterval(){
|
||||
if(checkStatusInterval != null){
|
||||
clearTimeout(checkStatusInterval);
|
||||
checkStatusInterval = null;
|
||||
}
|
||||
StatusIntervalActive = false;
|
||||
}
|
||||
function stopCheckConfigInterval(){
|
||||
if(checkConfigInterval != null){
|
||||
clearTimeout(checkConfigInterval);
|
||||
checkConfigInterval = null;
|
||||
}
|
||||
ConfigIntervalActive=false;
|
||||
}
|
||||
|
||||
function stopRefreshAPInterval(){
|
||||
|
||||
if(refreshAPInterval != null){
|
||||
clearTimeout(refreshAPInterval);
|
||||
refreshAPInterval = null;
|
||||
}
|
||||
RefreshAPIIntervalActive = false;
|
||||
}
|
||||
|
||||
|
||||
function startCheckStatusInterval(){
|
||||
StatusIntervalActive = true;
|
||||
checkStatusInterval = setTimeout(checkStatus, 950);
|
||||
}
|
||||
function startCheckConfigInterval(){
|
||||
ConfigIntervalActive = true;
|
||||
checkConfigInterval = setTimeout(checkConfig, 950);
|
||||
}
|
||||
|
||||
function startRefreshAPInterval(){
|
||||
RefreshAPIIntervalActive = true;
|
||||
refreshAPInterval = setTimeout(refreshAP, 2800);
|
||||
}
|
||||
|
||||
|
||||
function RepeatCheckStatusInterval(){
|
||||
if(StatusIntervalActive)
|
||||
startCheckStatusInterval();
|
||||
}
|
||||
|
||||
function RepeatCheckConfigInterval(){
|
||||
if(ConfigIntervalActive)
|
||||
startCheckConfigInterval();
|
||||
}
|
||||
|
||||
function RepeatRefreshAPInterval(){
|
||||
if(RefreshAPIIntervalActive)
|
||||
startRefreshAPInterval()
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
|
||||
$("#wifi-status").on("click", ".ape", function() {
|
||||
$( "#wifi" ).slideUp( "fast", function() {});
|
||||
$( "#connect-details" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#manual_add").on("click", ".ape", function() {
|
||||
selectedSSID = $(this).text();
|
||||
$( "#ssid-pwd" ).text(selectedSSID);
|
||||
$( "#wifi" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideDown( "fast", function() {});
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
$( "#connect-fail" ).hide();
|
||||
});
|
||||
|
||||
$("#wifi-list").on("click", ".ape", function() {
|
||||
selectedSSID = $(this).text();
|
||||
$( "#ssid-pwd" ).text(selectedSSID);
|
||||
$( "#wifi" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#connect" ).slideDown( "fast", function() {});
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
$( "#connect-fail" ).hide();
|
||||
});
|
||||
|
||||
$("#cancel").on("click", function() {
|
||||
selectedSSID = "";
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#manual_cancel").on("click", function() {
|
||||
selectedSSID = "";
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#join").on("click", function() {
|
||||
performConnect();
|
||||
});
|
||||
|
||||
$("#manual_join").on("click", function() {
|
||||
performConnect($(this).data('connect'));
|
||||
});
|
||||
|
||||
$("#ok-details").on("click", function() {
|
||||
$( "#connect-details" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
|
||||
});
|
||||
$("#update").on("click", function() {
|
||||
|
||||
performUpdate();
|
||||
});
|
||||
$("#factory").on("click", function() {
|
||||
|
||||
performFactory();
|
||||
});
|
||||
|
||||
$("#ok-credits").on("click", function() {
|
||||
$( "#credits" ).slideUp( "fast", function() {});
|
||||
$( "#app" ).slideDown( "fast", function() {});
|
||||
|
||||
});
|
||||
|
||||
$("#acredits").on("click", function(event) {
|
||||
event.preventDefault();
|
||||
$( "#app" ).slideUp( "fast", function() {});
|
||||
$( "#credits" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#ok-connect").on("click", function() {
|
||||
$( "#connect-wait" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#disconnect").on("click", function() {
|
||||
$( "#connect-details-wrap" ).addClass('blur');
|
||||
$( "#diag-disconnect" ).slideDown( "fast", function() {});
|
||||
});
|
||||
|
||||
$("#no-disconnect").on("click", function() {
|
||||
$( "#diag-disconnect" ).slideUp( "fast", function() {});
|
||||
$( "#connect-details-wrap" ).removeClass('blur');
|
||||
});
|
||||
|
||||
$("#yes-disconnect").on("click", function() {
|
||||
|
||||
stopCheckStatusInterval();
|
||||
selectedSSID = "";
|
||||
|
||||
$( "#diag-disconnect" ).slideUp( "fast", function() {});
|
||||
$( "#connect-details-wrap" ).removeClass('blur');
|
||||
|
||||
$.ajax({
|
||||
url: '/connect.json',
|
||||
dataType: 'json',
|
||||
method: 'DELETE',
|
||||
cache: false,
|
||||
data: { 'timestamp': Date.now()}
|
||||
});
|
||||
|
||||
startCheckStatusInterval();
|
||||
|
||||
$( "#connect-details" ).slideUp( "fast", function() {});
|
||||
$( "#wifi" ).slideDown( "fast", function() {})
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//first time the page loads: attempt get the connection status and start the wifi scan
|
||||
refreshAP();
|
||||
startCheckStatusInterval();
|
||||
startRefreshAPInterval();
|
||||
startCheckConfigInterval();
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
function performUpdate(){
|
||||
autoexec1 = $("#autoexec1").val();
|
||||
//reset connection
|
||||
//
|
||||
// $( "#ok-connect" ).prop("disabled",true);
|
||||
// $( "#ssid-wait" ).text(selectedSSID);
|
||||
// $( "#connect" ).slideUp( "fast", function() {});
|
||||
// $( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
// $( "#connect-wait" ).slideDown( "fast", function() {});
|
||||
// // todo: should we update the UI here?
|
||||
|
||||
$.ajax({
|
||||
url: '/config.json',
|
||||
dataType: 'json',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { 'X-Custom-autoexec1': autoexec1 },
|
||||
data: { 'timestamp': Date.now()}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function performFactory(){
|
||||
|
||||
// $( "#ok-connect" ).prop("disabled",true);
|
||||
// $( "#ssid-wait" ).text(selectedSSID);
|
||||
// $( "#connect" ).slideUp( "fast", function() {});
|
||||
// $( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
// $( "#connect-wait" ).slideDown( "fast", function() {});
|
||||
// // todo: should we update the UI here?
|
||||
|
||||
$.ajax({
|
||||
url: '/factory.json',
|
||||
dataType: 'json',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
data: { 'timestamp': Date.now()}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function performConnect(conntype){
|
||||
|
||||
//stop the status refresh. This prevents a race condition where a status
|
||||
//request would be refreshed with wrong ip info from a previous connection
|
||||
//and the request would automatically shows as succesful.
|
||||
stopCheckStatusInterval();
|
||||
|
||||
//stop refreshing wifi list
|
||||
stopRefreshAPInterval();
|
||||
|
||||
var pwd;
|
||||
if (conntype == 'manual') {
|
||||
//Grab the manual SSID and PWD
|
||||
selectedSSID=$('#manual_ssid').val();
|
||||
pwd = $("#manual_pwd").val();
|
||||
}else{
|
||||
pwd = $("#pwd").val();
|
||||
}
|
||||
//reset connection
|
||||
$( "#loading" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
$( "#connect-fail" ).hide();
|
||||
|
||||
$( "#ok-connect" ).prop("disabled",true);
|
||||
$( "#ssid-wait" ).text(selectedSSID);
|
||||
$( "#connect" ).slideUp( "fast", function() {});
|
||||
$( "#connect_manual" ).slideUp( "fast", function() {});
|
||||
$( "#connect-wait" ).slideDown( "fast", function() {});
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: '/connect.json',
|
||||
dataType: 'json',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd },
|
||||
data: { 'timestamp': Date.now()}
|
||||
});
|
||||
|
||||
|
||||
//now we can re-set the intervals regardless of result
|
||||
startCheckStatusInterval();
|
||||
startRefreshAPInterval();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function rssiToIcon(rssi){
|
||||
if(rssi >= -60){
|
||||
return 'w0';
|
||||
}
|
||||
else if(rssi >= -67){
|
||||
return 'w1';
|
||||
}
|
||||
else if(rssi >= -75){
|
||||
return 'w2';
|
||||
}
|
||||
else{
|
||||
return 'w3';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function refreshAP(){
|
||||
$.getJSON( "/ap.json", function( data ) {
|
||||
if(data.length > 0){
|
||||
//sort by signal strength
|
||||
data.sort(function (a, b) {
|
||||
var x = a["rssi"]; var y = b["rssi"];
|
||||
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
|
||||
});
|
||||
apList = data;
|
||||
refreshAPHTML(apList);
|
||||
|
||||
}
|
||||
});
|
||||
RepeatRefreshAPInterval();
|
||||
|
||||
}
|
||||
|
||||
function refreshAPHTML(data){
|
||||
var h = "";
|
||||
data.forEach(function(e, idx, array) {
|
||||
h += '<div class="ape{0}"><div class="{1}"><div class="{2}">{3}</div></div></div>'.format(idx === array.length - 1?'':' brdb', rssiToIcon(e.rssi), e.auth==0?'':'pw',e.ssid);
|
||||
h += "\n";
|
||||
});
|
||||
|
||||
$( "#wifi-list" ).html(h)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function checkStatus(){
|
||||
$.getJSON( "/status.json", function( data ) {
|
||||
if(data.hasOwnProperty('autoexec1') && data['autoexec1'] != ""){
|
||||
$("#autoexec1_current").text(data["autoexec1"]);
|
||||
}
|
||||
if(data.hasOwnProperty('ssid') && data['ssid'] != ""){
|
||||
if(data["ssid"] === selectedSSID){
|
||||
//that's a connection attempt
|
||||
if(data["urc"] === 0){
|
||||
//got connection
|
||||
$("#connected-to span").text(data["ssid"]);
|
||||
$("#connect-details h1").text(data["ssid"]);
|
||||
$("#ip").text(data["ip"]);
|
||||
$("#netmask").text(data["netmask"]);
|
||||
$("#gw").text(data["gw"]);
|
||||
$("#wifi-status").slideDown( "fast", function() {});
|
||||
|
||||
//unlock the wait screen if needed
|
||||
$( "#ok-connect" ).prop("disabled",false);
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).hide();
|
||||
$( "#connect-success" ).show();
|
||||
$( "#connect-fail" ).hide();
|
||||
}
|
||||
else if(data["urc"] === 1){
|
||||
//failed attempt
|
||||
$("#connected-to span").text('');
|
||||
$("#connect-details h1").text('');
|
||||
$("#ip").text('0.0.0.0');
|
||||
$("#netmask").text('0.0.0.0');
|
||||
$("#gw").text('0.0.0.0');
|
||||
|
||||
//don't show any connection
|
||||
$("#wifi-status").slideUp( "fast", function() {});
|
||||
|
||||
//unlock the wait screen
|
||||
$( "#ok-connect" ).prop("disabled",false);
|
||||
|
||||
//update wait screen
|
||||
$( "#loading" ).hide();
|
||||
$( "#connect-fail" ).show();
|
||||
$( "#connect-success" ).hide();
|
||||
}
|
||||
}
|
||||
else if(data.hasOwnProperty('urc') && data['urc'] === 0){
|
||||
//ESP32 is already connected to a wifi without having the user do anything
|
||||
if( !($("#wifi-status").is(":visible")) ){
|
||||
$("#connected-to span").text(data["ssid"]);
|
||||
$("#connect-details h1").text(data["ssid"]);
|
||||
$("#ip").text(data["ip"]);
|
||||
$("#netmask").text(data["netmask"]);
|
||||
$("#gw").text(data["gw"]);
|
||||
$("#wifi-status").slideDown( "fast", function() {});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(data.hasOwnProperty('urc') && data['urc'] === 2){
|
||||
//that's a manual disconnect
|
||||
if($("#wifi-status").is(":visible")){
|
||||
$("#wifi-status").slideUp( "fast", function() {});
|
||||
}
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
//don't do anything, the server might be down while esp32 recalibrates radio
|
||||
});
|
||||
|
||||
RepeatCheckStatusInterval();
|
||||
}
|
||||
|
||||
|
||||
function checkConfig(){
|
||||
var h = "";
|
||||
//{ "autoexec" : 0, "list" : [{ 'autoexec1' : 'squeezelite -o "I2S" -b 500:2000 -d all=info -M esp32' }]}
|
||||
$.getJSON( "/config.json", function( data ) {
|
||||
if(data.hasOwnProperty('autoexec')) {
|
||||
h+= '<div id="autoexec">Autoexec: {0}</div>'.format(data["autoexec"]===1?"Active":"Inactive");
|
||||
}
|
||||
if(data.hasOwnProperty('list')) {
|
||||
data["list"].forEach(function(e, idx, array) {
|
||||
for (const [key, value] of Object.entries(e)) {
|
||||
h+= '<input id="{0}" type="text" maxlength="201" value="{1}"><br>'.format(key,value);
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
h += "\n";
|
||||
$( "#command-list" ).html(h);
|
||||
}
|
||||
|
||||
})
|
||||
.fail(function() {
|
||||
//don't do anything, the server might be down while esp32 recalibrates radio
|
||||
});
|
||||
|
||||
RepeatCheckConfigInterval();
|
||||
|
||||
}
|
||||
11
components/wifi-manager/component.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
|
||||
# this will take the sources in the src/ directory, compile them and link them into
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
COMPONENT_EMBED_FILES := style.css jquery.gz code.js index.html
|
||||
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
2
components/wifi-manager/compress.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
gzip index.html style.css jquery.js --best --keep --force
|
||||
pause
|
||||
2
components/wifi-manager/connect
Normal file
@@ -0,0 +1,2 @@
|
||||
<html>
|
||||
</html>
|
||||
184
components/wifi-manager/dns_server.c
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright (c) 2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file dns_server.c
|
||||
@author Tony Pottier
|
||||
@brief Defines an extremely basic DNS server for captive portal functionality.
|
||||
It's basically a DNS hijack that replies to the esp's address no matter which
|
||||
request is sent to it.
|
||||
|
||||
Contains the freeRTOS task for the DNS server that processes the requests.
|
||||
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#include "dns_server.h"
|
||||
|
||||
#include <lwip/sockets.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_event_loop.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#include <lwip/err.h>
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/sys.h>
|
||||
#include <lwip/netdb.h>
|
||||
#include <lwip/dns.h>
|
||||
|
||||
#include <byteswap.h>
|
||||
|
||||
#include "wifi_manager.h"
|
||||
|
||||
static const char TAG[] = "dns_server";
|
||||
static TaskHandle_t task_dns_server = NULL;
|
||||
int socket_fd;
|
||||
|
||||
void dns_server_start() {
|
||||
xTaskCreate(&dns_server, "dns_server", 3072, NULL, WIFI_MANAGER_TASK_PRIORITY-1, &task_dns_server);
|
||||
}
|
||||
|
||||
void dns_server_stop(){
|
||||
if(task_dns_server){
|
||||
vTaskDelete(task_dns_server);
|
||||
close(socket_fd);
|
||||
task_dns_server = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dns_server(void *pvParameters) {
|
||||
|
||||
|
||||
|
||||
struct sockaddr_in sa, ra;
|
||||
|
||||
/* Set redirection DNS hijack to the access point IP */
|
||||
ip4_addr_t ip_resolved;
|
||||
inet_pton(AF_INET, DEFAULT_AP_IP, &ip_resolved);
|
||||
|
||||
|
||||
/* Create UDP socket */
|
||||
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_fd < 0){
|
||||
ESP_LOGE(TAG, "Failed to create socket");
|
||||
exit(0);
|
||||
}
|
||||
memset(&sa, 0, sizeof(struct sockaddr_in));
|
||||
|
||||
/* Bind to port 53 (typical DNS Server port) */
|
||||
tcpip_adapter_ip_info_t ip;
|
||||
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip);
|
||||
ra.sin_family = AF_INET;
|
||||
ra.sin_addr.s_addr = ip.ip.addr;
|
||||
ra.sin_port = htons(53);
|
||||
if (bind(socket_fd, (struct sockaddr *)&ra, sizeof(struct sockaddr_in)) == -1) {
|
||||
ESP_LOGE(TAG, "Failed to bind to 53/udp");
|
||||
close(socket_fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct sockaddr_in client;
|
||||
socklen_t client_len;
|
||||
client_len = sizeof(client);
|
||||
int length;
|
||||
uint8_t data[DNS_QUERY_MAX_SIZE]; /* dns query buffer */
|
||||
uint8_t response[DNS_ANSWER_MAX_SIZE]; /* dns response buffer */
|
||||
char ip_address[INET_ADDRSTRLEN]; /* buffer to store IPs as text. This is only used for debug and serves no other purpose */
|
||||
char *domain; /* This is only used for debug and serves no other purpose */
|
||||
int err;
|
||||
|
||||
ESP_LOGI(TAG, "DNS Server listening on 53/udp");
|
||||
|
||||
/* Start loop to process DNS requests */
|
||||
for(;;) {
|
||||
|
||||
memset(data, 0x00, sizeof(data)); /* reset buffer */
|
||||
length = recvfrom(socket_fd, data, sizeof(data), 0, (struct sockaddr *)&client, &client_len); /* read udp request */
|
||||
|
||||
/*if the query is bigger than the buffer size we simply ignore it. This case should only happen in case of multiple
|
||||
* queries within the same DNS packet and is not supported by this simple DNS hijack. */
|
||||
if ( length > 0 && ((length + sizeof(dns_answer_t)-1) < DNS_ANSWER_MAX_SIZE) ) {
|
||||
|
||||
data[length] = '\0'; /*in case there's a bogus domain name that isn't null terminated */
|
||||
|
||||
/* Generate header message */
|
||||
memcpy(response, data, sizeof(dns_header_t));
|
||||
dns_header_t *dns_header = (dns_header_t*)response;
|
||||
dns_header->QR = 1; /*response bit */
|
||||
dns_header->OPCode = DNS_OPCODE_QUERY; /* no support for other type of response */
|
||||
dns_header->AA = 1; /*authoritative answer */
|
||||
dns_header->RCode = DNS_REPLY_CODE_NO_ERROR; /* no error */
|
||||
dns_header->TC = 0; /*no truncation */
|
||||
dns_header->RD = 0; /*no recursion */
|
||||
dns_header->ANCount = dns_header->QDCount; /* set answer count = question count -- duhh! */
|
||||
dns_header->NSCount = 0x0000; /* name server resource records = 0 */
|
||||
dns_header->ARCount = 0x0000; /* resource records = 0 */
|
||||
|
||||
|
||||
/* copy the rest of the query in the response */
|
||||
memcpy(response + sizeof(dns_header_t), data + sizeof(dns_header_t), length - sizeof(dns_header_t));
|
||||
|
||||
|
||||
/* extract domain name and request IP for debug */
|
||||
inet_ntop(AF_INET, &(client.sin_addr), ip_address, INET_ADDRSTRLEN);
|
||||
domain = (char*) &data[sizeof(dns_header_t) + 1];
|
||||
for(char* c=domain; *c != '\0'; c++){
|
||||
if(*c < ' ' || *c > 'z') *c = '.'; /* technically we should test if the first two bits are 00 (e.g. if( (*c & 0xC0) == 0x00) *c = '.') but this makes the code a lot more readable */
|
||||
}
|
||||
ESP_LOGI(TAG, "Replying to DNS request for %s from %s", domain, ip_address);
|
||||
|
||||
|
||||
/* create DNS answer at the end of the query*/
|
||||
dns_answer_t *dns_answer = (dns_answer_t*)&response[length];
|
||||
dns_answer->NAME = __bswap_16(0xC00C); /* This is a pointer to the beginning of the question. As per DNS standard, first two bits must be set to 11 for some odd reason hence 0xC0 */
|
||||
dns_answer->TYPE = __bswap_16(DNS_ANSWER_TYPE_A);
|
||||
dns_answer->CLASS = __bswap_16(DNS_ANSWER_CLASS_IN);
|
||||
dns_answer->TTL = (uint32_t)0x00000000; /* no caching. Avoids DNS poisoning since this is a DNS hijack */
|
||||
dns_answer->RDLENGTH = __bswap_16(0x0004); /* 4 byte => size of an ipv4 address */
|
||||
dns_answer->RDATA = ip_resolved.addr;
|
||||
|
||||
err = sendto(socket_fd, response, length+sizeof(dns_answer_t), 0, (struct sockaddr *)&client, client_len);
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "UDP sendto failed: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
taskYIELD(); /* allows the freeRTOS scheduler to take over if needed. DNS daemon should not be taxing on the system */
|
||||
|
||||
}
|
||||
close(socket_fd);
|
||||
|
||||
vTaskDelete ( NULL );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
140
components/wifi-manager/dns_server.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright (c) 2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file dns_server.h
|
||||
@author Tony Pottier
|
||||
@brief Defines an extremly basic DNS server for captive portal functionality.
|
||||
|
||||
Contains the freeRTOS task for the DNS server that processes the requests.
|
||||
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
@see http://www.zytrax.com/books/dns/ch15
|
||||
*/
|
||||
|
||||
#ifndef MAIN_DNS_SERVER_H_
|
||||
#define MAIN_DNS_SERVER_H_
|
||||
#include <esp_system.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** 12 byte header, 64 byte domain name, 4 byte qtype/qclass. This NOT compliant with the RFC, but it's good enough for a captive portal
|
||||
* if a DNS query is too big it just wont be processed. */
|
||||
#define DNS_QUERY_MAX_SIZE 80
|
||||
|
||||
/** Query + 2 byte ptr, 2 byte type, 2 byte class, 4 byte TTL, 2 byte len, 4 byte data */
|
||||
#define DNS_ANSWER_MAX_SIZE (DNS_QUERY_MAX_SIZE+16)
|
||||
|
||||
|
||||
/**
|
||||
* @brief RCODE values used in a DNS header message
|
||||
*/
|
||||
typedef enum dns_reply_code_t {
|
||||
DNS_REPLY_CODE_NO_ERROR = 0,
|
||||
DNS_REPLY_CODE_FORM_ERROR = 1,
|
||||
DNS_REPLY_CODE_SERVER_FAILURE = 2,
|
||||
DNS_REPLY_CODE_NON_EXISTANT_DOMAIN = 3,
|
||||
DNS_REPLY_CODE_NOT_IMPLEMENTED = 4,
|
||||
DNS_REPLY_CODE_REFUSED = 5,
|
||||
DNS_REPLY_CODE_YXDOMAIN = 6,
|
||||
DNS_REPLY_CODE_YXRRSET = 7,
|
||||
DNS_REPLY_CODE_NXRRSET = 8
|
||||
}dns_reply_code_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief OPCODE values used in a DNS header message
|
||||
*/
|
||||
typedef enum dns_opcode_code_t {
|
||||
DNS_OPCODE_QUERY = 0,
|
||||
DNS_OPCODE_IQUERY = 1,
|
||||
DNS_OPCODE_STATUS = 2
|
||||
}dns_opcode_code_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Represents a 12 byte DNS header.
|
||||
* __packed__ is needed to prevent potential unwanted memory alignments
|
||||
*/
|
||||
typedef struct __attribute__((__packed__)) dns_header_t{
|
||||
uint16_t ID; // identification number
|
||||
uint8_t RD : 1; // recursion desired
|
||||
uint8_t TC : 1; // truncated message
|
||||
uint8_t AA : 1; // authoritive answer
|
||||
uint8_t OPCode : 4; // message_type
|
||||
uint8_t QR : 1; // query/response flag
|
||||
uint8_t RCode : 4; // response code
|
||||
uint8_t Z : 3; // its z! reserved
|
||||
uint8_t RA : 1; // recursion available
|
||||
uint16_t QDCount; // number of question entries
|
||||
uint16_t ANCount; // number of answer entries
|
||||
uint16_t NSCount; // number of authority entries
|
||||
uint16_t ARCount; // number of resource entries
|
||||
}dns_header_t;
|
||||
|
||||
|
||||
|
||||
typedef enum dns_answer_type_t {
|
||||
DNS_ANSWER_TYPE_A = 1,
|
||||
DNS_ANSWER_TYPE_NS = 2,
|
||||
DNS_ANSWER_TYPE_CNAME = 5,
|
||||
DNS_ANSWER_TYPE_SOA = 6,
|
||||
DNS_ANSWER_TYPE_WKS = 11,
|
||||
DNS_ANSWER_TYPE_PTR = 12,
|
||||
DNS_ANSWER_TYPE_MX = 15,
|
||||
DNS_ANSWER_TYPE_SRV = 33,
|
||||
DNS_ANSWER_TYPE_AAAA = 28
|
||||
}dns_answer_type_t;
|
||||
|
||||
typedef enum dns_answer_class_t {
|
||||
DNS_ANSWER_CLASS_IN = 1
|
||||
}dns_answer_class_t;
|
||||
|
||||
|
||||
|
||||
typedef struct __attribute__((__packed__)) dns_answer_t{
|
||||
uint16_t NAME; /* for the sake of simplicity only 16 bit pointers are supported */
|
||||
uint16_t TYPE; /* Unsigned 16 bit value. The resource record types - determines the content of the RDATA field. */
|
||||
uint16_t CLASS; /* Class of response. */
|
||||
uint32_t TTL; /* The time in seconds that the record may be cached. A value of 0 indicates the record should not be cached. */
|
||||
uint16_t RDLENGTH; /* Unsigned 16-bit value that defines the length in bytes of the RDATA record. */
|
||||
uint32_t RDATA; /* For the sake of simplicity only ipv4 is supported, and as such it's a unsigned 32 bit */
|
||||
}dns_answer_t;
|
||||
|
||||
void dns_server(void *pvParameters);
|
||||
void dns_server_start();
|
||||
void dns_server_stop();
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* MAIN_DNS_SERVER_H_ */
|
||||
367
components/wifi-manager/http_server.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file http_server.c
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for the HTTP server to run.
|
||||
|
||||
Contains the freeRTOS task for the HTTP listener and all necessary support
|
||||
function to process requests, decode URLs, serve files, etc. etc.
|
||||
|
||||
@note http_server task cannot run without the wifi_manager task!
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#include "http_server.h"
|
||||
#include "cmd_system.h"
|
||||
|
||||
|
||||
|
||||
/* @brief tag used for ESP serial console messages */
|
||||
static const char TAG[] = "http_server";
|
||||
static const char json_start[] = "{ \"autoexec\" : %u, \"list\" : [";
|
||||
static const char json_end[] = "]}";
|
||||
static const char template[] = "{ '%s' : '%s' }";
|
||||
static const char array_separator[]=",";
|
||||
|
||||
/* @brief task handle for the http server */
|
||||
static TaskHandle_t task_http_server = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* @brief embedded binary data.
|
||||
* @see file "component.mk"
|
||||
* @see https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#embedding-binary-data
|
||||
*/
|
||||
extern const uint8_t style_css_start[] asm("_binary_style_css_start");
|
||||
extern const uint8_t style_css_end[] asm("_binary_style_css_end");
|
||||
extern const uint8_t jquery_gz_start[] asm("_binary_jquery_gz_start");
|
||||
extern const uint8_t jquery_gz_end[] asm("_binary_jquery_gz_end");
|
||||
extern const uint8_t code_js_start[] asm("_binary_code_js_start");
|
||||
extern const uint8_t code_js_end[] asm("_binary_code_js_end");
|
||||
extern const uint8_t index_html_start[] asm("_binary_index_html_start");
|
||||
extern const uint8_t index_html_end[] asm("_binary_index_html_end");
|
||||
|
||||
|
||||
/* const http headers stored in ROM */
|
||||
const static char http_html_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n";
|
||||
const static char http_css_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/css\nCache-Control: public, max-age=31536000\n\n";
|
||||
const static char http_js_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\n\n";
|
||||
const static char http_jquery_gz_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\nAccept-Ranges: bytes\nContent-Length: 29995\nContent-Encoding: gzip\n\n";
|
||||
const static char http_400_hdr[] = "HTTP/1.1 400 Bad Request\nContent-Length: 0\n\n";
|
||||
const static char http_404_hdr[] = "HTTP/1.1 404 Not Found\nContent-Length: 0\n\n";
|
||||
const static char http_503_hdr[] = "HTTP/1.1 503 Service Unavailable\nContent-Length: 0\n\n";
|
||||
const static char http_ok_json_no_cache_hdr[] = "HTTP/1.1 200 OK\nContent-type: application/json\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\n\n";
|
||||
const static char http_redirect_hdr_start[] = "HTTP/1.1 302 Found\nLocation: http://";
|
||||
const static char http_redirect_hdr_end[] = "/\n\n";
|
||||
|
||||
|
||||
|
||||
void http_server_start(){
|
||||
if(task_http_server == NULL){
|
||||
xTaskCreate(&http_server, "http_server", 1024*3, NULL, WIFI_MANAGER_TASK_PRIORITY-1, &task_http_server);
|
||||
}
|
||||
}
|
||||
|
||||
void http_server(void *pvParameters) {
|
||||
|
||||
struct netconn *conn, *newconn;
|
||||
err_t err;
|
||||
conn = netconn_new(NETCONN_TCP);
|
||||
netconn_bind(conn, IP_ADDR_ANY, 80);
|
||||
netconn_listen(conn);
|
||||
ESP_LOGI(TAG, "HTTP Server listening on 80/tcp");
|
||||
do {
|
||||
err = netconn_accept(conn, &newconn);
|
||||
if (err == ERR_OK) {
|
||||
http_server_netconn_serve(newconn);
|
||||
netconn_delete(newconn);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG,"Error accepting new connection. Terminating HTTP server");
|
||||
}
|
||||
taskYIELD(); /* allows the freeRTOS scheduler to take over if needed. */
|
||||
} while(err == ERR_OK);
|
||||
|
||||
netconn_close(conn);
|
||||
netconn_delete(conn);
|
||||
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
|
||||
char* http_server_get_header(char *request, char *header_name, int *len) {
|
||||
*len = 0;
|
||||
char *ret = NULL;
|
||||
char *ptr = NULL;
|
||||
|
||||
ptr = strstr(request, header_name);
|
||||
if (ptr) {
|
||||
ret = ptr + strlen(header_name);
|
||||
ptr = ret;
|
||||
while (*ptr != '\0' && *ptr != '\n' && *ptr != '\r') {
|
||||
(*len)++;
|
||||
ptr++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void http_server_netconn_serve(struct netconn *conn) {
|
||||
|
||||
struct netbuf *inbuf;
|
||||
char *buf = NULL;
|
||||
u16_t buflen;
|
||||
err_t err;
|
||||
const char new_line[2] = "\n";
|
||||
|
||||
err = netconn_recv(conn, &inbuf);
|
||||
if (err == ERR_OK) {
|
||||
|
||||
netbuf_data(inbuf, (void**)&buf, &buflen);
|
||||
|
||||
/* extract the first line of the request */
|
||||
char *save_ptr = buf;
|
||||
char *line = strtok_r(save_ptr, new_line, &save_ptr);
|
||||
ESP_LOGD(TAG,"Processing line %s",line);
|
||||
|
||||
if(line) {
|
||||
|
||||
/* captive portal functionality: redirect to access point IP for HOST that are not the access point IP OR the STA IP */
|
||||
int lenH = 0;
|
||||
char *host = http_server_get_header(save_ptr, "Host: ", &lenH);
|
||||
/* determine if Host is from the STA IP address */
|
||||
wifi_manager_lock_sta_ip_string(portMAX_DELAY);
|
||||
bool access_from_sta_ip = lenH > 0?strstr(host, wifi_manager_get_sta_ip_string()):false;
|
||||
wifi_manager_unlock_sta_ip_string();
|
||||
|
||||
if (lenH > 0 && !strstr(host, DEFAULT_AP_IP) && !access_from_sta_ip) {
|
||||
ESP_LOGI(TAG,"Redirecting to default AP IP Address : %s", DEFAULT_AP_IP);
|
||||
netconn_write(conn, http_redirect_hdr_start, sizeof(http_redirect_hdr_start) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, DEFAULT_AP_IP, sizeof(DEFAULT_AP_IP) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, http_redirect_hdr_end, sizeof(http_redirect_hdr_end) - 1, NETCONN_NOCOPY);
|
||||
|
||||
}
|
||||
else{
|
||||
/* default page */
|
||||
if(strstr(line, "GET / ")) {
|
||||
netconn_write(conn, http_html_hdr, sizeof(http_html_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, index_html_start, index_html_end - index_html_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /jquery.js ")) {
|
||||
netconn_write(conn, http_jquery_gz_hdr, sizeof(http_jquery_gz_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, jquery_gz_start, jquery_gz_end - jquery_gz_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /code.js ")) {
|
||||
netconn_write(conn, http_js_hdr, sizeof(http_js_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, code_js_start, code_js_end - code_js_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /ap.json ")) {
|
||||
/* if we can get the mutex, write the last version of the AP list */
|
||||
ESP_LOGI(TAG,"Processing ap.json request");
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)){
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
char *buff = wifi_manager_get_ap_list_json();
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
wifi_manager_unlock_json_buffer();
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /ap.json failed to obtain mutex");
|
||||
}
|
||||
/* request a wifi scan */
|
||||
ESP_LOGI(TAG,"Starting wifi scan");
|
||||
wifi_manager_scan_async();
|
||||
}
|
||||
else if(strstr(line, "GET /style.css ")) {
|
||||
netconn_write(conn, http_css_hdr, sizeof(http_css_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, style_css_start, style_css_end - style_css_start, NETCONN_NOCOPY);
|
||||
}
|
||||
else if(strstr(line, "GET /status.json ")){
|
||||
ESP_LOGI(TAG,"Serving status.json");
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)){
|
||||
char *buff = wifi_manager_get_ip_info_json();
|
||||
if(buff){
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
|
||||
wifi_manager_unlock_json_buffer();
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /status failed to obtain mutex");
|
||||
}
|
||||
}
|
||||
else if(strstr(line, "GET /config.json ")){
|
||||
ESP_LOGI(TAG,"Serving config.json");
|
||||
char autoexec_name[21]={0};
|
||||
char * autoexec_value=NULL;
|
||||
char * autoexec_flag_s=NULL;
|
||||
uint8_t autoexec_flag=0;
|
||||
int buflen=MAX_COMMAND_LINE_SIZE+strlen(template)+1;
|
||||
char * buff = malloc(buflen);
|
||||
if(!buff)
|
||||
{
|
||||
ESP_LOGE(TAG,"Unable to allocate buffer for config.json!");
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i=1;
|
||||
size_t l = 0;
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY);
|
||||
|
||||
autoexec_flag = wifi_manager_get_flag();
|
||||
snprintf(buff,buflen-1, json_start, autoexec_flag);
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
do {
|
||||
snprintf(autoexec_name,sizeof(autoexec_name)-1,"autoexec%u",i);
|
||||
ESP_LOGD(TAG,"Getting command name %s", autoexec_name);
|
||||
autoexec_value= wifi_manager_alloc_get_config(autoexec_name, &l);
|
||||
if(autoexec_value!=NULL ){
|
||||
if(i>1)
|
||||
{
|
||||
netconn_write(conn, array_separator, strlen(array_separator), NETCONN_NOCOPY);
|
||||
ESP_LOGD(TAG,"%s", array_separator);
|
||||
}
|
||||
ESP_LOGI(TAG,"found command %s = %s", autoexec_name, autoexec_value);
|
||||
snprintf(buff,buflen-1,template, autoexec_name,autoexec_value);
|
||||
netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY);
|
||||
ESP_LOGD(TAG,"%s", buff);
|
||||
ESP_LOGD(TAG,"Freeing memory for command %s name", autoexec_name);
|
||||
free(autoexec_value);
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG,"No matching command found for name %s", autoexec_name);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
} while(1);
|
||||
free(buff);
|
||||
netconn_write(conn, json_end, strlen(json_end), NETCONN_NOCOPY);
|
||||
ESP_LOGD(TAG,"%s", json_end);
|
||||
}
|
||||
}
|
||||
else if(strstr(line, "POST /factory.json ")){
|
||||
guided_factory();
|
||||
}
|
||||
else if(strstr(line, "POST /config.json ")){
|
||||
ESP_LOGI(TAG,"Serving POST config.json");
|
||||
|
||||
if(wifi_manager_lock_json_buffer(( TickType_t ) 10)){
|
||||
int i=1;
|
||||
int lenS = 0, lenA=0;
|
||||
char autoexec_name[21]={0};
|
||||
char * autoexec_value=NULL;
|
||||
char * autoexec_flag_s=NULL;
|
||||
uint8_t autoexec_flag=0;
|
||||
autoexec_flag_s = http_server_get_header(save_ptr, "X-Custom-autoexec: ", &lenA);
|
||||
if(autoexec_flag_s!=NULL && lenA > 0)
|
||||
{
|
||||
autoexec_flag = atoi(autoexec_flag_s);
|
||||
wifi_manager_save_autoexec_flag(autoexec_flag);
|
||||
}
|
||||
|
||||
do {
|
||||
snprintf(autoexec_name,sizeof(autoexec_name)-1,"X-Custom-autoexec%u:",i++);
|
||||
ESP_LOGD(TAG,"Looking for command name %s", autoexec_name);
|
||||
autoexec_value = http_server_get_header(save_ptr, autoexec_name, &lenS);
|
||||
|
||||
if(autoexec_value ){
|
||||
if(lenS < MAX_COMMAND_LINE_SIZE ){
|
||||
ESP_LOGD(TAG, "http_server_netconn_serve: config.json/ call, with %s: %s", autoexec_name, autoexec_value);
|
||||
wifi_manager_save_autoexec_config(autoexec_value,autoexec_name,lenS);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG,"command line length is too long : %s = %s", autoexec_name, autoexec_value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG,"No matching command found for name %s", autoexec_name);
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
|
||||
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_503_hdr, sizeof(http_503_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "http_server_netconn_serve: GET /status failed to obtain mutex");
|
||||
}
|
||||
}
|
||||
|
||||
else if(strstr(line, "DELETE /connect.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: DELETE /connect.json");
|
||||
/* request a disconnection from wifi and forget about it */
|
||||
wifi_manager_disconnect_async();
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); /* 200 ok */
|
||||
}
|
||||
else if(strstr(line, "POST /connect.json ")) {
|
||||
ESP_LOGI(TAG, "http_server_netconn_serve: POST /connect.json");
|
||||
bool found = false;
|
||||
int lenS = 0, lenP = 0;
|
||||
char *ssid = NULL, *password = NULL;
|
||||
ssid = http_server_get_header(save_ptr, "X-Custom-ssid: ", &lenS);
|
||||
password = http_server_get_header(save_ptr, "X-Custom-pwd: ", &lenP);
|
||||
|
||||
if(ssid && lenS <= MAX_SSID_SIZE && password && lenP <= MAX_PASSWORD_SIZE){
|
||||
wifi_config_t* config = wifi_manager_get_wifi_sta_config();
|
||||
memset(config, 0x00, sizeof(wifi_config_t));
|
||||
memcpy(config->sta.ssid, ssid, lenS);
|
||||
memcpy(config->sta.password, password, lenP);
|
||||
ESP_LOGD(TAG, "http_server_netconn_serve: wifi_manager_connect_async() call, with ssid: %s, password: %s", ssid, password);
|
||||
wifi_manager_connect_async();
|
||||
netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); //200ok
|
||||
found = true;
|
||||
}
|
||||
|
||||
if(!found){
|
||||
/* bad request the authentification header is not complete/not the correct format */
|
||||
netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "bad request the authentification header is not complete/not the correct format");
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
netconn_write(conn, http_400_hdr, sizeof(http_400_hdr) - 1, NETCONN_NOCOPY);
|
||||
ESP_LOGE(TAG, "bad request");
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
ESP_LOGE(TAG, "URL Not found. Sending 404.");
|
||||
netconn_write(conn, http_404_hdr, sizeof(http_404_hdr) - 1, NETCONN_NOCOPY);
|
||||
}
|
||||
}
|
||||
|
||||
/* free the buffer */
|
||||
netbuf_delete(inbuf);
|
||||
}
|
||||
95
components/wifi-manager/http_server.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file http_server.h
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for the HTTP server to run.
|
||||
|
||||
Contains the freeRTOS task for the HTTP listener and all necessary support
|
||||
function to process requests, decode URLs, serve files, etc. etc.
|
||||
|
||||
@note http_server task cannot run without the wifi_manager task!
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#ifndef HTTP_SERVER_H_INCLUDED
|
||||
#define HTTP_SERVER_H_INCLUDED
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "wifi_manager.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event_loop.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mdns.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/priv/api_msg.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief RTOS task for the HTTP server. Do not start manually.
|
||||
* @see void http_server_start()
|
||||
*/
|
||||
void http_server(void *pvParameters);
|
||||
|
||||
/* @brief helper function that processes one HTTP request at a time */
|
||||
void http_server_netconn_serve(struct netconn *conn);
|
||||
|
||||
/* @brief create the task for the http server */
|
||||
void http_server_start();
|
||||
|
||||
/**
|
||||
* @brief gets a char* pointer to the first occurence of header_name withing the complete http request request.
|
||||
*
|
||||
* For optimization purposes, no local copy is made. memcpy can then be used in coordination with len to extract the
|
||||
* data.
|
||||
*
|
||||
* @param request the full HTTP raw request.
|
||||
* @param header_name the header that is being searched.
|
||||
* @param len the size of the header value if found.
|
||||
* @return pointer to the beginning of the header value.
|
||||
*/
|
||||
char* http_server_get_header(char *request, char *header_name, int *len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
310
components/wifi-manager/index.html
Normal file
@@ -0,0 +1,310 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<script src="/jquery.js"></script>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<script src="/code.js"></script>
|
||||
<title>esp32-wifi-manager</title>
|
||||
</head>
|
||||
<script>
|
||||
var ws, sel, host, old, once = 0, jso, m;
|
||||
var to = 0, set_int = 0;
|
||||
|
||||
|
||||
|
||||
function get_radio(name)
|
||||
{
|
||||
var s = document.getElementsByName(name), sel;
|
||||
for ( var i = 0; i < s.length; i++)
|
||||
if (s[i].checked) {
|
||||
sel = s[i].value;
|
||||
break;
|
||||
}
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
function get_radio_index(name)
|
||||
{
|
||||
var s = document.getElementsByName(name), i;
|
||||
|
||||
for (i = 0; i < s.length; i++)
|
||||
if (s[i].checked)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
function do_reset()
|
||||
{
|
||||
var s = "{\"reset\":\"1\"}";
|
||||
try {
|
||||
ws.send(s);
|
||||
} catch(exception) {
|
||||
alert('Sorry, there was a problem' + exception);
|
||||
}
|
||||
|
||||
ws.close();
|
||||
alert("Rebooting...");
|
||||
}
|
||||
|
||||
function file_change()
|
||||
{
|
||||
document.getElementById('update').disabled = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function do_upload(f)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
document.getElementById('update').disabled = 1;
|
||||
//ws.close();
|
||||
document.getElementById("progr").class = "progr-ok";
|
||||
|
||||
xhr.upload.addEventListener("progress", function(e) {
|
||||
document.getElementById("progr").value = parseInt(e.loaded / e.total * 100);
|
||||
|
||||
if (e.loaded == e.total) {
|
||||
// document.getElementById("realpage").style.display = "none";
|
||||
// document.getElementById("waiting").style.display = "block";
|
||||
}
|
||||
|
||||
}, false);
|
||||
|
||||
xhr.onreadystatechange = function(e) {
|
||||
console.log("rs" + xhr.readyState + " status " + xhr.status);
|
||||
if (xhr.readyState == 4) {
|
||||
/* it completed, for good or for ill */
|
||||
// document.getElementById("realpage").style.display = "none";
|
||||
// document.getElementById("waiting").style.display = "block";
|
||||
document.getElementById("progr").class = "progr-ok";
|
||||
console.log("upload reached state 4: xhr status " + xhr.status);
|
||||
setTimeout(function() { window.location.href = location.origin + "/"; }, 9000 );
|
||||
}
|
||||
};
|
||||
|
||||
/* kill the heart timer */
|
||||
clearInterval(set_int);
|
||||
|
||||
xhr.open("POST", f.action, true);
|
||||
xhr.send(new FormData(f));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function do_settings(f)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.onreadystatechange = function(e) {
|
||||
console.log("do_settings" + xhr.readyState + " status " + xhr.status);
|
||||
if (xhr.readyState == 4) {
|
||||
document.getElementById("updsettings").style.opacity = "1.0";
|
||||
document.getElementById("updsettings").disabled = 0;
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open("POST", f.action, true);
|
||||
document.getElementById("updsettings").style.opacity = "0.3";
|
||||
document.getElementById("updsettings").disabled = 1;
|
||||
xhr.send(new FormData(f));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_latest(n)
|
||||
{
|
||||
if (n == 0)
|
||||
ws.send("update-ota");
|
||||
else
|
||||
ws.send("update-factory");
|
||||
}
|
||||
|
||||
|
||||
function heart_timer() {
|
||||
var s;
|
||||
|
||||
s = Math.round((95 * to) / (40 * 10)) / 100;
|
||||
|
||||
if (s < 0) {
|
||||
clearInterval(set_int);
|
||||
set_int = 0;
|
||||
|
||||
ws.close();
|
||||
|
||||
document.getElementById("realpage").style.opacity = "0.3";
|
||||
}
|
||||
|
||||
|
||||
to--;
|
||||
document.getElementById("heart").style.opacity = s;
|
||||
}
|
||||
|
||||
|
||||
function heartbeat()
|
||||
{
|
||||
to = 40 * 10;
|
||||
if (!set_int) {
|
||||
set_int = setInterval(heart_timer, 100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="app-wrap">
|
||||
<div id="wifi">
|
||||
<header>
|
||||
<h1>Wi-Fi</h1>
|
||||
</header>
|
||||
<div id="wifi-status">
|
||||
<h2>Connected to:</h2>
|
||||
<section id="connected-to">
|
||||
<div class="ape"><div class="w0"><div class="pw"><span></span></div></div></div>
|
||||
</section>
|
||||
</div>
|
||||
<h2>Manual connect</h2>
|
||||
<section id="manual_add">
|
||||
<div class="ape">ADD (HIDDEN) SSID<div>
|
||||
</section>
|
||||
<h2>or choose a network...</h2>
|
||||
<section id="wifi-list">
|
||||
</section>
|
||||
<div id="pwrdby"><em>Powered by </em><a id="acredits" href="#"><strong>esp32-wifi-manager</strong></a>.</div>
|
||||
</div>
|
||||
<div id="command_line">
|
||||
<header>
|
||||
<h1>Startup command</h1>
|
||||
</header>
|
||||
<h2>Squeezelite</span></h2>
|
||||
|
||||
<div id="autoexec1_current" ></div>
|
||||
<section id="command-list">
|
||||
<input id="autoexec1" type="text" maxlength="201" placeholder="squeezelite -o I2S -b 500:2000 -d all=info" value="">
|
||||
</section>
|
||||
|
||||
<div class="buttons">
|
||||
<input id="update_command" type="button" value="Update" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="ota">
|
||||
<header><h1>Application</h1></header>
|
||||
<form name="multipart" action="otaform" method="post" enctype="multipart/form-data" onsubmit="do_upload(this); return false;">
|
||||
<progress id="progr" value="0" max="100" >Upload Progress</progress>
|
||||
<input type="file" name="ota" id="ota" size="20" accept=".bin" onchange="file_change();" style="font-size: 12pt">
|
||||
<span id="file_info" style="font-size:12pt;"></span>
|
||||
<input type="submit" id="update" disabled="" value="upload">
|
||||
<input type="submit" id="factory" disabled="" value="factory">
|
||||
</form>
|
||||
</div>
|
||||
<div id="connect_manual">
|
||||
<header>
|
||||
<h1>Enter Details</h1>
|
||||
</header>
|
||||
<h2>Manual Connection</span></h2>
|
||||
<section>
|
||||
<input id="manual_ssid" type="text" placeholder="SSID" value="">
|
||||
<input id="manual_pwd" type="password" placeholder="Password" value="">
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="manual_join" type="button" value="Join" data-connect="manual" />
|
||||
<input id="manual_cancel" type="button" value="Cancel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="connect">
|
||||
<header>
|
||||
<h1>Enter Password</h1>
|
||||
</header>
|
||||
<h2>Password for <span id="ssid-pwd"></span></h2>
|
||||
<section>
|
||||
<input id="pwd" type="password" placeholder="Password" value="">
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="join" type="button" value="Join" />
|
||||
<input id="cancel" type="button" value="Cancel"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="connect-wait">
|
||||
<header>
|
||||
<h1>Please wait...</h1>
|
||||
</header>
|
||||
<h2>Connecting to <span id="ssid-wait"></span></h2>
|
||||
<section>
|
||||
<div id="loading">
|
||||
<div class="spinner"><div class="double-bounce1"></div><div class="double-bounce2"></div></div>
|
||||
<p class="tctr">You may lose wifi access while the esp32 recalibrates its radio. Please wait until your device automatically reconnects. This can take up to 30s.</p>
|
||||
</div>
|
||||
<div id="connect-success">
|
||||
<h3 class="gr">Success!</h3>
|
||||
</div>
|
||||
<div id="connect-fail">
|
||||
<h3 class="rd">Connection failed</h3>
|
||||
<p class="tctr">Please double-check wifi password if any and make sure the access point has good signal.</p>
|
||||
</div>
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="ok-connect" type="button" value="OK" class="ctr" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="connect-details">
|
||||
<div id="connect-details-wrap">
|
||||
<header>
|
||||
<h1></h1>
|
||||
</header>
|
||||
<h2></h2>
|
||||
<section>
|
||||
<div class="buttons">
|
||||
<input id="disconnect" type="button" value="Disconnect" class="ctr"/>
|
||||
</div>
|
||||
</section>
|
||||
<h2>IP Address</h2>
|
||||
<section>
|
||||
<div class="ape brdb">IP Address:<div id="ip" class="fr"></div></div>
|
||||
<div class="ape brdb">Subnet Mask:<div id="netmask" class="fr"></div></div>
|
||||
<div class="ape">Default Gateway:<div id="gw" class="fr"></div></div>
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="ok-details" type="button" value="OK" class="ctr" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="diag-disconnect" class="diag-box">
|
||||
<div class="diag-box-win">
|
||||
<p>Are you sure you would like to disconnect from this wifi?</p>
|
||||
<div class="buttons">
|
||||
<input id="no-disconnect" type="button" value="No" />
|
||||
<input id="yes-disconnect" type="button" value="Yes" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="credits">
|
||||
<header>
|
||||
<h1>About this app...</h1>
|
||||
</header>
|
||||
<h2></h2>
|
||||
<section>
|
||||
<p><strong>esp32-wifi-manager</strong>, © 2017-2019, Tony Pottier<br />Licender under the MIT License.</p>
|
||||
<p>
|
||||
This app would not be possible without the following libraries:
|
||||
</p>
|
||||
<ul>
|
||||
<li>SpinKit, © 2015, Tobias Ahlin. Licensed under the MIT License.</li>
|
||||
<li>jQuery, The jQuery Foundation. Licensed under the MIT License.</li>
|
||||
<li>cJSON, © 2009-2017, Dave Gamble and cJSON contributors. Licensed under the MIT License.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="buttons">
|
||||
<input id="ok-credits" type="button" value="OK" class="ctr" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<html>
|
||||
BIN
components/wifi-manager/jquery.gz
Normal file
4
components/wifi-manager/jquery.js
vendored
Normal file
144
components/wifi-manager/json.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
@file json.c
|
||||
@brief handles very basic JSON with a minimal footprint on the system
|
||||
|
||||
This code is a lightly modified version of cJSON 1.4.7. cJSON is licensed under the MIT license:
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
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.
|
||||
|
||||
@see https://github.com/DaveGamble/cJSON
|
||||
*/
|
||||
|
||||
#include "json.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
bool json_print_string(const unsigned char *input, unsigned char *output_buffer)
|
||||
{
|
||||
const unsigned char *input_pointer = NULL;
|
||||
unsigned char *output = NULL;
|
||||
unsigned char *output_pointer = NULL;
|
||||
size_t output_length = 0;
|
||||
/* numbers of additional characters needed for escaping */
|
||||
size_t escape_characters = 0;
|
||||
|
||||
if (output_buffer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* empty string */
|
||||
if (input == NULL)
|
||||
{
|
||||
//output = ensure(output_buffer, sizeof("\"\""), hooks);
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "\"\"");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* set "flag" to 1 if something needs to be escaped */
|
||||
for (input_pointer = input; *input_pointer; input_pointer++)
|
||||
{
|
||||
if (strchr("\"\\\b\f\n\r\t", *input_pointer))
|
||||
{
|
||||
/* one character escape sequence */
|
||||
escape_characters++;
|
||||
}
|
||||
else if (*input_pointer < 32)
|
||||
{
|
||||
/* UTF-16 escape sequence uXXXX */
|
||||
escape_characters += 5;
|
||||
}
|
||||
}
|
||||
output_length = (size_t)(input_pointer - input) + escape_characters;
|
||||
|
||||
/* in the original cJSON it is possible to realloc here in case output buffer is too small.
|
||||
* This is overkill for an embedded system. */
|
||||
output = output_buffer;
|
||||
|
||||
/* no characters have to be escaped */
|
||||
if (escape_characters == 0)
|
||||
{
|
||||
output[0] = '\"';
|
||||
memcpy(output + 1, input, output_length);
|
||||
output[output_length + 1] = '\"';
|
||||
output[output_length + 2] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
output[0] = '\"';
|
||||
output_pointer = output + 1;
|
||||
/* copy the string */
|
||||
for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
|
||||
{
|
||||
if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
|
||||
{
|
||||
/* normal character, copy */
|
||||
*output_pointer = *input_pointer;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* character needs to be escaped */
|
||||
*output_pointer++ = '\\';
|
||||
switch (*input_pointer)
|
||||
{
|
||||
case '\\':
|
||||
*output_pointer = '\\';
|
||||
break;
|
||||
case '\"':
|
||||
*output_pointer = '\"';
|
||||
break;
|
||||
case '\b':
|
||||
*output_pointer = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
*output_pointer = 'f';
|
||||
break;
|
||||
case '\n':
|
||||
*output_pointer = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*output_pointer = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
*output_pointer = 't';
|
||||
break;
|
||||
default:
|
||||
/* escape and print as unicode codepoint */
|
||||
sprintf((char*)output_pointer, "u%04x", *input_pointer);
|
||||
output_pointer += 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
output[output_length + 1] = '\"';
|
||||
output[output_length + 2] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
47
components/wifi-manager/json.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
@file json.h
|
||||
@brief handles very basic JSON with a minimal footprint on the system
|
||||
|
||||
This code is a lightly modified version of cJSON 1.4.7. cJSON is licensed under the MIT license:
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
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.
|
||||
|
||||
@see https://github.com/DaveGamble/cJSON
|
||||
*/
|
||||
|
||||
#ifndef JSON_H_INCLUDED
|
||||
#define JSON_H_INCLUDED
|
||||
#include <stdbool.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Render the cstring provided to a JSON escaped version that can be printed.
|
||||
* @param input the input buffer to be escaped.
|
||||
* @param output_buffer the output buffer to write to. You must ensure it is big enough to contain the final string.
|
||||
* @see cJSON equivlaent static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
|
||||
*/
|
||||
bool json_print_string(const unsigned char *input, unsigned char *output_buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JSON_H_INCLUDED */
|
||||
BIN
components/wifi-manager/lock.png
Normal file
|
After Width: | Height: | Size: 433 B |
29
components/wifi-manager/main.c.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file main.c
|
||||
@author Tony Pottier
|
||||
@brief Entry point for the ESP32 application.
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
|
||||
BIN
components/wifi-manager/settings.png
Normal file
|
After Width: | Height: | Size: 901 B |
1
components/wifi-manager/status
Normal file
@@ -0,0 +1 @@
|
||||
{"ssid":"zodmgbbq","ip":"192.168.1.119","netmask":"255.255.255.0","gw":"192.168.1.1","urc":0}
|
||||
250
components/wifi-manager/style.css
Normal file
@@ -0,0 +1,250 @@
|
||||
body {
|
||||
background-color: #eee;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
font: 1.1em tahoma, arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: darkblue;
|
||||
transition: color .2s ease-out;
|
||||
text-decoration: none
|
||||
}
|
||||
a:hover {
|
||||
color: red;
|
||||
}
|
||||
input {
|
||||
display: none;
|
||||
font: 1.1em tahoma, arial, sans-serif;
|
||||
}
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus,
|
||||
button:focus {
|
||||
outline: none;
|
||||
}
|
||||
input[type="button"] {
|
||||
width: 100px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
padding: 10px;
|
||||
}
|
||||
#credits {
|
||||
display: none;
|
||||
}
|
||||
#app {} #app-wrap {} #disconnect {
|
||||
width: 150px;
|
||||
}
|
||||
.diag-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
.diag-box-win {
|
||||
position: absolute;
|
||||
left: 10%;
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
border: 2px outset #888;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
top: 20%;
|
||||
}
|
||||
.blur {
|
||||
-webkit-filter: blur(2px);
|
||||
-moz-filter: blur(2px);
|
||||
-ms-filter: blur(2px);
|
||||
-o-filter: blur(2px);
|
||||
filter: blur(2px);
|
||||
}
|
||||
.ape {
|
||||
margin-left: 20px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
}
|
||||
.ape:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.brdb {
|
||||
border-bottom: 1px solid #888;
|
||||
}
|
||||
header {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #888;
|
||||
}
|
||||
section {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #888;
|
||||
border-top: 1px solid #888;
|
||||
}
|
||||
h1 {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 15px;
|
||||
font-size: 1.4em
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
text-transform: uppercase;
|
||||
color: #888;
|
||||
font-size: 1.0em
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding: 20px 0px 20px 0px;
|
||||
}
|
||||
.gr {
|
||||
color: green;
|
||||
}
|
||||
.rd {
|
||||
color: red;
|
||||
}
|
||||
#wifi-status {
|
||||
display: none;
|
||||
}
|
||||
#connect {
|
||||
display: none;
|
||||
}
|
||||
#connect_manual {
|
||||
display: none;
|
||||
}
|
||||
#manual_ssid {
|
||||
border: none;
|
||||
width: 80%;
|
||||
margin-left: 35px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
display: block
|
||||
}
|
||||
#manual_pwd {
|
||||
border: none;
|
||||
width: 80%;
|
||||
margin-left: 35px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
display: block
|
||||
}
|
||||
#pwd {
|
||||
border: none;
|
||||
width: 80%;
|
||||
margin-left: 35px;
|
||||
padding: 10px 0px 10px 10px;
|
||||
display: block
|
||||
}
|
||||
.buttons {
|
||||
padding: 15px;
|
||||
}
|
||||
#join {
|
||||
float: right;
|
||||
}
|
||||
#manual_join {
|
||||
float: right;
|
||||
}
|
||||
#yes-disconnect {
|
||||
display: inline-block;
|
||||
margin-left: 20px;
|
||||
}
|
||||
#no-disconnect {
|
||||
display: inline-block;
|
||||
}
|
||||
.ctr {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.tctr {
|
||||
text-align: center;
|
||||
}
|
||||
#connect-wait {
|
||||
display: none;
|
||||
}
|
||||
#connect-success {
|
||||
display: none;
|
||||
}
|
||||
#connect-fail {
|
||||
display: none;
|
||||
}
|
||||
#connect-details {
|
||||
display: none;
|
||||
}
|
||||
.fr {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w0 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTJDBGvsAAABzUlEQVRIS+WUTShEURTH3zyRhjQ+8hWxmCJMoSzEwsbCgi1LZRYW9pONptiwka9iI81CWFpYaEqNMkVKmpWN1IhYKN9ZDL/z3p3mxZh5g9X4168799xz/vPefedeLeuVC+3gdTgc07CsmCQ2DI2gg21Jci30wSpGt/CeghickTsHPVACDkgqp67rPgpO4E0ZZMIj7OHhxSvPtEyomcVDeFXJv+EZNvEsNa01rZfAuSUhThR2wU+ObJkbyhRNMMDaDIThBqy1MdZ3wAPawqfFC2Lj0Ab5kpBGxdAJs9TeW72ITUhCPZMjFYwwbwXpnkwlDzOIx50yXwP5c0MeggHGanNqSDqqBqQ7/Kxvg2zHAfMN8IE8uZhYO6eBnBXGKnOakLWfaQZ9jMRjSPXhZUuC5A9JjVFpKkeNSVVA0Tq8KJN0yFl4gilqbW2tm+SQKoybXIG8jcT34RSsh1Byt6iVg2ZLlRCg6JpROqEDpFheXZ5S9rcLFsl5YJwHad+MVA5y13w5lRY5oRsKjdm/Vz/7LR86zG+5wr+9NX+iOowjEO+aELEic+lv1ILppeUPosRst6QduTANgnE2mC+BnYswI1VwfYzCCL9dZij7pWkf6UeSTYAuE/QAAAAASUVORK5CYII=') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w1 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEQAACxEBf2RfkQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xNkRpr/UAAAHiSURBVEhL5dRPKINxHMfxPVskpA35F3FYEVasHBYHFwcHrhyVZ8vBfblIceEi/4qLtINwdHCQUpQVKcnJRYqIg/J3OYz399nv0YPNtuzEt149+31/v+/n4fGYLVHpup4Rnyregd+K27TIghe63+8fx7wySqsPdbAj3qzha0MOV6ETiwTd4u0HUZxydgrtKISGj0xreG4gEAgycIRXFZCOR2yTQZSebeaa4Q1s7iOiDv/GM1bJLDJv0EHjzHLAdIFNjHBGHpkbxUo9utmbQBg3sM5G2d+AR24w82XznN4QmpGjXrCExRkXfJhk9t6aRW9YDtSwOFDNE9ZNyFLzKRczOegh406FL8ElG8JDM8S1Qtaq7KhEO0Y0TVtHGHusVxCEDy5oMLNqyVrgWm5kqaYw3mdVdmqQsENE8JbAPbY43yszMqiyHOr66QayL5XH0DJeVEgyUTxhjNmPR/vtBpZyc3hHDZohV5DfRvq7OMYtrDdZY7YwFpG8yhBi6JrrMFogww7IT1mOVsxy5oHrNIqRVpWgDtnGKn7log35xurfVxfPW/7QYT57Ybz7mapqgk9gvjU79ApiW5mpRkIvLTe4oJfyK5lKOQndgvG/wXoOSb8I061Svj4G0M9nZ6z198tmeweYtIrMYP17VAAAAABJRU5ErkJggg==') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w2 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEQAACxEBf2RfkQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xNkRpr/UAAAHkSURBVEhL3dRLKERRGMDxuSPSkLzyilgo8iiUhVjYWFiwZancmSzsZSPFho28io1kISwtLKQURZGSrGykiFgo7yyG/zdzznRm5iK5Sk79uvd85/u++5hzx2Pb9q9yDLrJMWhIRB1sv98/ghlliFAXyuGFU21IbECSi9CKORrd4O0TQZyQO45mZMJCpKfZ3BcIBPooOMSravAdD9ikB63sJN1XN69kcQ8vKvknnrBMzyx9gRYCp0aCdo51DJIjr6wU2UoF2lkbxS6uYdYGWV9DtVxgMmbxjFg/apEM/ZQfyUADxqi9M3sRG5CEEib7KnjMvAaye2IbfUVupoMet6r5PDL0YjXBBY4Fai5kRxVCdscg66uQ17HDfAl9kDuXJzB3Thk5sxzzZa6DumHknN3QS+IBPvvh5ZVskN8ZU5+gz3XAlELRIp5Vk6/It/CIYWrjXm3URCkleUsV6iaXkKeR+DaOYH6EkrtCrXxoUf2iJoY8LFB0xXEA9ZBieXS5S3m/jZgi557jBGT7xvWKCxhyIP81ka/SgQ9NSDViURyDbvpTo82yrAPscl4HKxR1aRTT+BhvyhaxtPCSO6OKphfGBc6JZYaX3BnpNN1AUC7AfBrJoRUXR67X6+1BN+fp4dD/Hx7PO4o9VGuAapKIAAAAAElFTkSuQmCC') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.w3 {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAACAElEQVRIS7XUP0gbYRjH8VSpiBZJWvEflXYQKtqACg6ig4uDg651LPQSHLpLlyDoUhdpVdBFxEG0YwcHCQgRFJSCFKcuUohY6iC0VYtD9PuE9w3vXZ74h16HD3fv733e53KX9y7ied5/pYZhUkPHQ3TBSyQS7zFvTBC9RivKoK3NCwZS3IxBLNLoBFc3yOEbtR/Qj8d4gEJPt3lVMpkcY8E+Lk2D+/iDTXrQyquwfW3zdiZ38dcU/4tzrNHzib3AAMGhU2BlsYFxauSRtaDWaMMwc1PYwU+4a3PMryMuF5gJTH4ne4dOVMLeZSkx9GCatb/cXmQpKXjOYM+EB4w7ILsn2Og28mNe0ePUNF9CzE7GCZc5NpmxkB31FLI7xpn/DHkc24xXMQb55XIH7s55Qc0Cx0YZ29A2LJyzG95S+AU3/fHySNLUjwTWl9tzG7iqWbSCC9PkNvIunGGStUWP1jcwWijOmIW2yTHkbiTfwle4L6HUfmKtvGi+fr6BowHLLPrBMYVuyGK5dfmV8nx7MUvNb44fIdu3qFdR4KiDfGsKb6WiCn145GQ+ahgmNQyTGpYwxPOWP3qHc/mE+76apaih4hmND2B3TYasJlCjUkPFS5oeORfIkhVtSY0aKqI0TSP/bjCew10+hPf6D+r5fIziDefRwFxJahgmNQyPF7kGEsc1es+A2E4AAAAASUVORK5CYII=') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.pw {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTJDBGvsAAABIUlEQVRIS+3VsU7CUBTGcYhBJCwqwcmEJ2DkCQgzb8ADmLgYWXTzMVjcGNjYGEAXgoSRhTg5OroYIyFY/h+hWGwvtzQ0LpzkF8i5l/uRQ2kTjuPEKrC5T79vzHWJO4wxwzeGuMY5AitsQBFvmEObvNQboQBfhQk4gQ5wD+zgBrcYrHrSwzE2KkxAHVrQWB6QgiqJLB7xA+2pYaNsAWm8QAsa0Sn+1gU+oT1NHGFdtoAcJtBCSw1DuaPqQiNdly0gj1doQaMwleavPc+IJUDffKeADO7Rxxe08A4dEOQD2qPXJ1xh+VuYAirQVaNGFFPov2MM0OXm/UAUZRwCtjoEWP1vQBXuLTgKPYRKMAacoY0oIboDNLB8+PgC4hLY3B8nsQCQEf56jLJoQAAAAABJRU5ErkJggg==') no-repeat right top;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
height: 24px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
/* SpinKit is licensed under the MIT License. Copyright (c) 2015 Tobias Ahlin */
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
margin: 100px auto;
|
||||
}
|
||||
.double-bounce1,
|
||||
.double-bounce2 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: #333;
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
animation: sk-bounce 2.0s infinite ease-in-out;
|
||||
}
|
||||
.double-bounce2 {
|
||||
-webkit-animation-delay: -1.0s;
|
||||
animation-delay: -1.0s;
|
||||
}
|
||||
@-webkit-keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
-webkit-transform: scale(0.0)
|
||||
}
|
||||
50% {
|
||||
-webkit-transform: scale(1.0)
|
||||
}
|
||||
}
|
||||
@keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(0.0);
|
||||
-webkit-transform: scale(0.0);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.0);
|
||||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
/* end of SpinKit */
|
||||
BIN
components/wifi-manager/wifi0.png
Normal file
|
After Width: | Height: | Size: 605 B |
BIN
components/wifi-manager/wifi1.png
Normal file
|
After Width: | Height: | Size: 613 B |
BIN
components/wifi-manager/wifi2.png
Normal file
|
After Width: | Height: | Size: 615 B |
BIN
components/wifi-manager/wifi24.png
Normal file
|
After Width: | Height: | Size: 605 B |
BIN
components/wifi-manager/wifi3.png
Normal file
|
After Width: | Height: | Size: 656 B |
1138
components/wifi-manager/wifi_manager.c
Normal file
397
components/wifi-manager/wifi_manager.h
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
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.
|
||||
|
||||
@file wifi_manager.h
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for esp32 to connect to a wifi/scan wifis
|
||||
|
||||
Contains the freeRTOS task and all necessary support
|
||||
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#ifndef WIFI_MANAGER_H_INCLUDED
|
||||
#define WIFI_MANAGER_H_INCLUDED
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_types.h"
|
||||
|
||||
|
||||
#define DEFAULT_COMMAND_LINE CONFIG_DEFAULT_COMMAND_LINE
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum size of a SSID name. 32 is IEEE standard.
|
||||
* @warning limit is also hard coded in wifi_config_t. Never extend this value.
|
||||
*/
|
||||
#define MAX_SSID_SIZE 32
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum size of a WPA2 passkey. 64 is IEEE standard.
|
||||
* @warning limit is also hard coded in wifi_config_t. Never extend this value.
|
||||
*/
|
||||
#define MAX_PASSWORD_SIZE 64
|
||||
#define MAX_COMMAND_LINE_SIZE 201
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum number of access points that can be scanned.
|
||||
*
|
||||
* To save memory and avoid nasty out of memory errors,
|
||||
* we can limit the number of APs detected in a wifi scan.
|
||||
*/
|
||||
#define MAX_AP_NUM 15
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines when a connection is lost/attempt to connect is made, how many retries should be made before giving up.
|
||||
* Setting it to 2 for instance means there will be 3 attempts in total (original request + 2 retries)
|
||||
*/
|
||||
#define WIFI_MANAGER_MAX_RETRY CONFIG_WIFI_MANAGER_MAX_RETRY
|
||||
|
||||
/** @brief Defines the task priority of the wifi_manager.
|
||||
*
|
||||
* Tasks spawn by the manager will have a priority of WIFI_MANAGER_TASK_PRIORITY-1.
|
||||
* For this particular reason, minimum task priority is 1. It it highly not recommended to set
|
||||
* it to 1 though as the sub-tasks will now have a priority of 0 which is the priority
|
||||
* of freeRTOS' idle task.
|
||||
*/
|
||||
#define WIFI_MANAGER_TASK_PRIORITY CONFIG_WIFI_MANAGER_TASK_PRIORITY
|
||||
|
||||
/** @brief Defines the auth mode as an access point
|
||||
* Value must be of type wifi_auth_mode_t
|
||||
* @see esp_wifi_types.h
|
||||
* @warning if set to WIFI_AUTH_OPEN, passowrd me be empty. See DEFAULT_AP_PASSWORD.
|
||||
*/
|
||||
#define AP_AUTHMODE WIFI_AUTH_WPA2_PSK
|
||||
|
||||
/** @brief Defines visibility of the access point. 0: visible AP. 1: hidden */
|
||||
#define DEFAULT_AP_SSID_HIDDEN 0
|
||||
|
||||
/** @brief Defines access point's name. Default value: esp32. Run 'make menuconfig' to setup your own value or replace here by a string */
|
||||
#define DEFAULT_AP_SSID CONFIG_DEFAULT_AP_SSID
|
||||
|
||||
/** @brief Defines access point's password.
|
||||
* @warning In the case of an open access point, the password must be a null string "" or "\0" if you want to be verbose but waste one byte.
|
||||
* In addition, the AP_AUTHMODE must be WIFI_AUTH_OPEN
|
||||
*/
|
||||
#define DEFAULT_AP_PASSWORD CONFIG_DEFAULT_AP_PASSWORD
|
||||
|
||||
/** @brief Defines the hostname broadcasted by mDNS */
|
||||
#define DEFAULT_HOSTNAME "esp32"
|
||||
|
||||
/** @brief Defines access point's bandwidth.
|
||||
* Value: WIFI_BW_HT20 for 20 MHz or WIFI_BW_HT40 for 40 MHz
|
||||
* 20 MHz minimize channel interference but is not suitable for
|
||||
* applications with high data speeds
|
||||
*/
|
||||
#define DEFAULT_AP_BANDWIDTH WIFI_BW_HT20
|
||||
|
||||
/** @brief Defines access point's channel.
|
||||
* Channel selection is only effective when not connected to another AP.
|
||||
* Good practice for minimal channel interference to use
|
||||
* For 20 MHz: 1, 6 or 11 in USA and 1, 5, 9 or 13 in most parts of the world
|
||||
* For 40 MHz: 3 in USA and 3 or 11 in most parts of the world
|
||||
*/
|
||||
#define DEFAULT_AP_CHANNEL CONFIG_DEFAULT_AP_CHANNEL
|
||||
|
||||
|
||||
|
||||
/** @brief Defines the access point's default IP address. Default: "10.10.0.1 */
|
||||
#define DEFAULT_AP_IP CONFIG_DEFAULT_AP_IP
|
||||
|
||||
/** @brief Defines the access point's gateway. This should be the same as your IP. Default: "10.10.0.1" */
|
||||
#define DEFAULT_AP_GATEWAY CONFIG_DEFAULT_AP_GATEWAY
|
||||
|
||||
/** @brief Defines the access point's netmask. Default: "255.255.255.0" */
|
||||
#define DEFAULT_AP_NETMASK CONFIG_DEFAULT_AP_NETMASK
|
||||
|
||||
/** @brief Defines access point's maximum number of clients. Default: 4 */
|
||||
#define DEFAULT_AP_MAX_CONNECTIONS CONFIG_DEFAULT_AP_MAX_CONNECTIONS
|
||||
|
||||
/** @brief Defines access point's beacon interval. 100ms is the recommended default. */
|
||||
#define DEFAULT_AP_BEACON_INTERVAL CONFIG_DEFAULT_AP_BEACON_INTERVAL
|
||||
|
||||
/** @brief Defines if esp32 shall run both AP + STA when connected to another AP.
|
||||
* Value: 0 will have the own AP always on (APSTA mode)
|
||||
* Value: 1 will turn off own AP when connected to another AP (STA only mode when connected)
|
||||
* Turning off own AP when connected to another AP minimize channel interference and increase throughput
|
||||
*/
|
||||
#define DEFAULT_STA_ONLY 1
|
||||
|
||||
/** @brief Defines if wifi power save shall be enabled.
|
||||
* Value: WIFI_PS_NONE for full power (wifi modem always on)
|
||||
* Value: WIFI_PS_MODEM for power save (wifi modem sleep periodically)
|
||||
* Note: Power save is only effective when in STA only mode
|
||||
*/
|
||||
#define DEFAULT_STA_POWER_SAVE WIFI_PS_NONE
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum length in bytes of a JSON representation of an access point.
|
||||
*
|
||||
* maximum ap string length with full 32 char ssid: 75 + \\n + \0 = 77\n
|
||||
* example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","chan":12,"rssi":-100,"auth":4},\n
|
||||
* BUT: we need to escape JSON. Imagine a ssid full of \" ? so it's 32 more bytes hence 77 + 32 = 99.\n
|
||||
* this is an edge case but I don't think we should crash in a catastrophic manner just because
|
||||
* someone decided to have a funny wifi name.
|
||||
*/
|
||||
#define JSON_ONE_APP_SIZE 99
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum length in bytes of a JSON representation of the IP information
|
||||
* assuming all ips are 4*3 digits, and all characters in the ssid require to be escaped.
|
||||
* example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","ip":"192.168.1.119","netmask":"255.255.255.0","gw":"192.168.1.1","urc":0}
|
||||
*/
|
||||
#define JSON_IP_INFO_SIZE 150
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines the complete list of all messages that the wifi_manager can process.
|
||||
*
|
||||
* Some of these message are events ("EVENT"), and some of them are action ("ORDER")
|
||||
* Each of these messages can trigger a callback function and each callback function is stored
|
||||
* in a function pointer array for convenience. Because of this behavior, it is extremely important
|
||||
* to maintain a strict sequence and the top level special element 'MESSAGE_CODE_COUNT'
|
||||
*
|
||||
* @see wifi_manager_set_callback
|
||||
*/
|
||||
typedef enum message_code_t {
|
||||
NONE = 0,
|
||||
ORDER_START_HTTP_SERVER = 1,
|
||||
ORDER_STOP_HTTP_SERVER = 2,
|
||||
ORDER_START_DNS_SERVICE = 3,
|
||||
ORDER_STOP_DNS_SERVICE = 4,
|
||||
ORDER_START_WIFI_SCAN = 5,
|
||||
ORDER_LOAD_AND_RESTORE_STA = 6,
|
||||
ORDER_CONNECT_STA = 7,
|
||||
ORDER_DISCONNECT_STA = 8,
|
||||
ORDER_START_AP = 9,
|
||||
ORDER_START_HTTP = 10,
|
||||
ORDER_START_DNS_HIJACK = 11,
|
||||
EVENT_STA_DISCONNECTED = 12,
|
||||
EVENT_SCAN_DONE = 13,
|
||||
EVENT_STA_GOT_IP = 14,
|
||||
MESSAGE_CODE_COUNT = 15 /* important for the callback array */
|
||||
|
||||
}message_code_t;
|
||||
|
||||
/**
|
||||
* @brief simplified reason codes for a lost connection.
|
||||
*
|
||||
* esp-idf maintains a big list of reason codes which in practice are useless for most typical application.
|
||||
*/
|
||||
typedef enum update_reason_code_t {
|
||||
UPDATE_CONNECTION_OK = 0,
|
||||
UPDATE_FAILED_ATTEMPT = 1,
|
||||
UPDATE_USER_DISCONNECT = 2,
|
||||
UPDATE_LOST_CONNECTION = 3
|
||||
}update_reason_code_t;
|
||||
|
||||
typedef enum connection_request_made_by_code_t{
|
||||
CONNECTION_REQUEST_NONE = 0,
|
||||
CONNECTION_REQUEST_USER = 1,
|
||||
CONNECTION_REQUEST_AUTO_RECONNECT = 2,
|
||||
CONNECTION_REQUEST_RESTORE_CONNECTION = 3,
|
||||
CONNECTION_REQUEST_MAX = 0x7fffffff /*force the creation of this enum as a 32 bit int */
|
||||
}connection_request_made_by_code_t;
|
||||
|
||||
/**
|
||||
* The actual WiFi settings in use
|
||||
*/
|
||||
struct wifi_settings_t{
|
||||
uint8_t ap_ssid[MAX_SSID_SIZE];
|
||||
uint8_t ap_pwd[MAX_PASSWORD_SIZE];
|
||||
uint8_t ap_channel;
|
||||
uint8_t ap_ssid_hidden;
|
||||
wifi_bandwidth_t ap_bandwidth;
|
||||
bool sta_only;
|
||||
wifi_ps_type_t sta_power_save;
|
||||
bool sta_static_ip;
|
||||
tcpip_adapter_ip_info_t sta_static_ip_config;
|
||||
};
|
||||
extern struct wifi_settings_t wifi_settings;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Structure used to store one message in the queue.
|
||||
*/
|
||||
typedef struct{
|
||||
message_code_t code;
|
||||
void *param;
|
||||
} queue_message;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Allocate heap memory for the wifi manager and start the wifi_manager RTOS task
|
||||
*/
|
||||
void wifi_manager_start();
|
||||
|
||||
/**
|
||||
* Frees up all memory allocated by the wifi_manager and kill the task.
|
||||
*/
|
||||
void wifi_manager_destroy();
|
||||
|
||||
/**
|
||||
* Filters the AP scan list to unique SSIDs
|
||||
*/
|
||||
void filter_unique( wifi_ap_record_t * aplist, uint16_t * ap_num);
|
||||
|
||||
/**
|
||||
* Main task for the wifi_manager
|
||||
*/
|
||||
void wifi_manager( void * pvParameters );
|
||||
|
||||
|
||||
char* wifi_manager_get_ap_list_json();
|
||||
char* wifi_manager_get_ip_info_json();
|
||||
|
||||
uint8_t wifi_manager_get_flag();
|
||||
char * wifi_manager_alloc_get_config(char * name, size_t * l);
|
||||
|
||||
|
||||
/**
|
||||
* @brief saves the current STA wifi config to flash ram storage.
|
||||
*/
|
||||
esp_err_t wifi_manager_save_sta_config();
|
||||
|
||||
/**
|
||||
* @brief saves the current configuration to flash ram storage
|
||||
*/
|
||||
esp_err_t wifi_manager_save_autoexec_config(char * value, char * name, int len);
|
||||
esp_err_t wifi_manager_save_autoexec_flag(uint8_t flag);
|
||||
|
||||
|
||||
/**
|
||||
* @brief fetch a previously STA wifi config in the flash ram storage.
|
||||
* @return true if a previously saved config was found, false otherwise.
|
||||
*/
|
||||
bool wifi_manager_fetch_wifi_sta_config();
|
||||
|
||||
wifi_config_t* wifi_manager_get_wifi_sta_config();
|
||||
|
||||
/**
|
||||
* @brief A standard wifi event handler as recommended by Espressif
|
||||
*/
|
||||
esp_err_t wifi_manager_event_handler(void *ctx, system_event_t *event);
|
||||
|
||||
|
||||
/**
|
||||
* @brief requests a connection to an access point that will be process in the main task thread.
|
||||
*/
|
||||
void wifi_manager_connect_async();
|
||||
|
||||
/**
|
||||
* @brief requests a wifi scan
|
||||
*/
|
||||
void wifi_manager_scan_async();
|
||||
|
||||
/**
|
||||
* @brief requests to disconnect and forget about the access point.
|
||||
*/
|
||||
void wifi_manager_disconnect_async();
|
||||
|
||||
/**
|
||||
* @brief Tries to get access to json buffer mutex.
|
||||
*
|
||||
* The HTTP server can try to access the json to serve clients while the wifi manager thread can try
|
||||
* to update it. These two tasks are synchronized through a mutex.
|
||||
*
|
||||
* The mutex is used by both the access point list json and the connection status json.\n
|
||||
* These two resources should technically have their own mutex but we lose some flexibility to save
|
||||
* on memory.
|
||||
*
|
||||
* This is a simple wrapper around freeRTOS function xSemaphoreTake.
|
||||
*
|
||||
* @param xTicksToWait The time in ticks to wait for the semaphore to become available.
|
||||
* @return true in success, false otherwise.
|
||||
*/
|
||||
bool wifi_manager_lock_json_buffer(TickType_t xTicksToWait);
|
||||
|
||||
/**
|
||||
* @brief Releases the json buffer mutex.
|
||||
*/
|
||||
void wifi_manager_unlock_json_buffer();
|
||||
|
||||
/**
|
||||
* @brief Generates the connection status json: ssid and IP addresses.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code);
|
||||
/**
|
||||
* @brief Clears the connection status json.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_clear_ip_info_json();
|
||||
|
||||
/**
|
||||
* @brief Generates the list of access points after a wifi scan.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_generate_acess_points_json();
|
||||
|
||||
/**
|
||||
* @brief Clear the list of access points.
|
||||
* @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
|
||||
*/
|
||||
void wifi_manager_clear_access_points_json();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start the mDNS service
|
||||
*/
|
||||
void wifi_manager_initialise_mdns();
|
||||
|
||||
|
||||
bool wifi_manager_lock_sta_ip_string(TickType_t xTicksToWait);
|
||||
void wifi_manager_unlock_sta_ip_string();
|
||||
|
||||
/**
|
||||
* @brief gets the string representation of the STA IP address, e.g.: "192.168.1.69"
|
||||
*/
|
||||
char* wifi_manager_get_sta_ip_string();
|
||||
|
||||
/**
|
||||
* @brief thread safe char representation of the STA IP update
|
||||
*/
|
||||
void wifi_manager_safe_update_sta_ip_string(uint32_t ip);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Register a callback to a custom function when specific event message_code happens.
|
||||
*/
|
||||
void wifi_manager_set_callback(message_code_t message_code, void (*func_ptr)(void*) );
|
||||
|
||||
|
||||
BaseType_t wifi_manager_send_message(message_code_t code, void *param);
|
||||
BaseType_t wifi_manager_send_message_to_front(message_code_t code, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WIFI_MANAGER_H_INCLUDED */
|
||||