Add Wificfg

Uses the sysparam store to save the wifi configuration.
Adds a basic http server for configuration.
This commit is contained in:
Our Air Quality 2017-08-13 15:16:50 +10:00
parent af4ac44cb5
commit 8b0a1ae362
17 changed files with 2596 additions and 0 deletions

1
examples/wificfg/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
!local.mk

View file

@ -0,0 +1,7 @@
#define configUSE_TRACE_FACILITY 1
#define configGENERATE_RUN_TIME_STATS 1
#define portGET_RUN_TIME_COUNTER_VALUE() (RTC.COUNTER)
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() {}
/* Use the defaults for everything else */
#include_next<FreeRTOSConfig.h>

View file

@ -0,0 +1,5 @@
# Makefile for wificfg example
PROGRAM=wificfg
EXTRA_COMPONENTS=extras/dhcpserver extras/wificfg
include ../../common.mk

View file

@ -0,0 +1,18 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<title>",
"</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"</head>"
"<body>"
"<ul class=\"topnav\" id=\"myTopnav\">"
"<li class=\"active\"><a href=\"/\">Home</a></li>"
"<li><a href=\"/wificfg/\">WiFi Config</a></li>"
"<li><a href=\"/tasks.html\">Tasks</a></li>"
"<li class=\"icon\">"
"<a href=\"javascript:void(0);\" onclick=\"myFunction()\">&#9776;</a>"
"</li>"
"</ul>",
"</body></html>"

View file

@ -0,0 +1 @@
FLASH_SIZE ?= 32

View file

@ -0,0 +1,94 @@
/*
* Example Wifi configuration via an access point.
*
* Copyright (C) 2016 OurAirQuality.org
*
* Licensed under the Apache License, Version 2.0, January 2004 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/
*
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
*
*/
#include <string.h>
#include <ctype.h>
#include <espressif/esp_common.h>
#include <espressif/user_interface.h>
#include <esp/uart.h>
#include <FreeRTOS.h>
#include <task.h>
#include "lwip/sockets.h"
#include "wificfg/wificfg.h"
#include "sysparam.h"
static const char http_success_header[] = "HTTP/1.1 200 \r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n"
"\r\n";
static const char *http_index_content[] = {
#include "content/index.html"
};
static int handle_index(int s, wificfg_method method,
uint32_t content_length,
wificfg_content_type content_type,
char *buf, size_t len)
{
if (wificfg_write_string(s, http_success_header) < 0) return -1;
if (method != HTTP_METHOD_HEAD) {
if (wificfg_write_string_chunk(s, http_index_content[0], buf, len) < 0) return -1;
if (wificfg_write_html_title(s, buf, len, "Home") < 0) return -1;
if (wificfg_write_string_chunk(s, http_index_content[1], buf, len) < 0) return -1;
socklen_t addr_len;
struct sockaddr addr;
addr_len = sizeof(addr);
getpeername(s, (struct sockaddr*)&addr, &addr_len);
if (wificfg_write_string_chunk(s, "<dl class=\"dlh\">", buf, len) < 0) return -1;
if (addr.sa_family == AF_INET) {
struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
if (wificfg_write_string_chunk(s, "<dt>Peer address</dt>", buf, len) < 0) return -1;
snprintf(buf, len, "<dd>" IPSTR " : %d</dd>",
IP2STR((ip4_addr_t *)&sa->sin_addr.s_addr), ntohs(sa->sin_port));
if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
}
if (wificfg_write_string_chunk(s, "</dl>", buf, len) < 0) return -1;
if (wificfg_write_string_chunk(s, http_index_content[2], buf, len) < 0) return -1;
if (wificfg_write_chunk_end(s) < 0) return -1;
}
return 0;
}
static const wificfg_dispatch dispatch_list[] = {
{"/", HTTP_METHOD_GET, handle_index, false},
{"/index.html", HTTP_METHOD_GET, handle_index, false},
{NULL, HTTP_METHOD_ANY, NULL}
};
void user_init(void)
{
uart_set_baud(0, 115200);
printf("SDK version:%s\n", sdk_system_get_sdk_version());
sdk_wifi_set_sleep_type(WIFI_SLEEP_MODEM);
wificfg_init(80, dispatch_list);
}

View file

@ -0,0 +1,10 @@
# Component makefile for extras/wificfg
# Expected anyone using wificfg includes it as 'wificfg/wificfg.h'
INC_DIRS += $(wificfg_ROOT)..
# args for passing into compile rule generation
wificfg_INC_DIR =
wificfg_SRC_DIR = $(wificfg_ROOT)
$(eval $(call component_compile_rules,wificfg))

View file

@ -0,0 +1,30 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<title>",
"</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"</head>"
"<body>"
"<ul class=\"topnav\" id=\"myTopnav\">"
"<li><a href=\"/\">Home</a></li>"
"<li class=\"active\"><a href=\"/wificfg/\">WiFi Config</a></li>"
"<li><a href=\"/wificfg/sta.html\">WiFi Station</a></li>"
"<li><a href=\"/wificfg/ap.html\">WiFi Access Point</a></li>"
"<li class=\"icon\">"
"<a href=\"javascript:void(0);\" onclick=\"myFunction()\">&#9776;</a>"
"</li>"
"</ul>"
"<form action=\"/challenge.html\" method=\"post\">"
"<fieldset>"
"<legend>Unlock the configuration interface</legend>"
"<dl class=\"dlh\">"
"<dt><label for=\"pw\">Password</label></dt>"
"<dd><input id=\"pw\" type=\"text\" maxlength=\"32\" name=\"cfg_password\" "
"placeholder=\"unlock-password\" value=\"\"></dd>"
"</dl>"
"<center><input type=\"submit\" value=\"Unlock\"></center>"
"</fieldset>"
"</form>"
"</body></html>"

View file

@ -0,0 +1,11 @@
"HTTP/1.1 200 \r\n"
"Content-Type: image/svg+xml\r\n"
"Cache-Control: max-age=900\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n"
"\r\n",
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
"<svg xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\" version=\"1.1\">"
"<defs><linearGradient id=\"g1\" y2=\"248.63\" gradientUnits=\"userSpaceOnUse\" x2=\"153\" gradientTransform=\"matrix(.20068 0 0 .20068 -54.336 -1.0508)\" y1=\"15.424\" x1=\"99.777\"><stop style=\"stop-color:#0088FF\" offset=\"0\"/><stop style=\"stop-color:#b2dbff\" offset=\"1\"/></linearGradient></defs>"
"<path style=\"stroke-linejoin:round;color:#000000;stroke:#aaaaaa;stroke-linecap:round;fill:url(#g1)\" d=\"m22.7 0.94747c-0.474 0.03238-0.934 0.10563-1.398 0.15883h-0.032l-1.112 6.068c-1.812 0.4127-3.517 1.1132-5.051 2.065l-4.988-3.59c-1.3485 1.0468-2.5754 2.2677-3.6536 3.59l3.4628 5.0517c-1.0514 1.606-1.842 3.441-2.2874 5.369v0.031l-6.0361 0.953c-0.1104 0.902-0.1589 1.833-0.1589 2.764 0 0.762 0.021 1.514 0.0953 2.256l6.0362 1.08c0.4293 2.096 1.2448 4.054 2.3827 5.782l-3.5899 4.924c1.0281 1.277 2.2151 2.439 3.4946 3.463l5.0833-3.494c1.776 1.133 3.759 1.928 5.909 2.319l0.953 6.004c0.677 0.062 1.372 0.064 2.065 0.064 0.979 0 1.914-0.037 2.859-0.159l1.144-6.132c2.041-0.507 3.958-1.389 5.623-2.573l4.893 3.558c1.268-1.079 2.429-2.32 3.431-3.653l-3.558-5.147c0.963-1.664 1.631-3.5 1.969-5.464l6.005-0.953c0.052-0.627 0.063-1.234 0.063-1.875 0-1.112-0.129-2.203-0.286-3.272l-6.099-1.112c-0.478-1.765-1.263-3.412-2.256-4.892l3.59-4.9245c-1.113-1.3608-2.382-2.618-3.781-3.6852l-5.178 3.5581c-1.488-0.8802-3.09-1.5556-4.829-1.9379l-0.953-6.0362c-0.868-0.102-1.742-0.15883-2.637-0.15883-0.242 0-0.491-0.00761-0.731 0-0.117 0.00371-0.232-0.00681-0.349 0-0.032 0.00184-0.064-0.00216-0.095 0zm0.826 15.44c0.116-0.006 0.231 0 0.349 0 3.761 0 6.83 3.07 6.83 6.831 0 3.76-3.069 6.798-6.83 6.798s-6.799-3.038-6.799-6.798c0-3.643 2.852-6.648 6.45-6.831z\"/>"
"</svg>"

View file

@ -0,0 +1,8 @@
"HTTP/1.1 200 \r\n"
"Content-Type: text/javascript\r\n"
"Cache-Control: max-age=900\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n"
"\r\n",
"function myFunction() { var x = document.getElementById(\"myTopnav\");"
"if (x.className === \"topnav\") { x.className += \" responsive\"; } else { x.className = \"topnav\"; } }"

View file

@ -0,0 +1,19 @@
"HTTP/1.1 200 \r\n"
"Content-Type: text/css\r\n"
"Cache-Control: max-age=900\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n",
".dlh dd,h1{font-weight:300}.dlh{font-size:0;text-align:center}"
".dlh dd,.dlh dt{width:48%;width:calc(50% - 10px);margin:8px 0;display:inline-block;font-size:16px;vertical-align:middle}"
".dlh dt{text-align:right;padding-right:10px}"
".dlh dd{font-size:18px;text-align:left;padding-left:10px}"
"ul.topnav{list-style-type:none;margin:0;padding:0;overflow:hidden;background-color:#bbb}"
"ul.topnav li{float:left}"
"ul.topnav li a{display:inline-block;color:#444;text-align:center;padding:14px 16px;text-decoration:none;transition:.3s;font-size:17px}"
"ul.topnav li a:hover{background-color:#ddd}ul.topnav li.icon{display:none}"
"@media screen and (max-width:680px){ul.topnav li:not(.active){display:none}ul.topnav li.icon{float:right;display:inline-block}ul.topnav.responsive{position:relative}ul.topnav.responsive li.icon{position:absolute;right:0;top:0}ul.topnav.responsive li{float:none;display:inline}ul.topnav.responsive li a{display:block;text-align:left}}"
"html{min-height:100%}"
"body{background:#d0e4f7;background:-moz-linear-gradient(top, #d0e4f7 0%, #73b1e7 24%, #0a77d5 50%, #539fe1 79%, #87bcea 100%);background:-webkit-linear-gradient(top, #d0e4f7 0%,#73b1e7 24%,#0a77d5 50%,#539fe1 79%,#87bcea 100%);background:linear-gradient(to bottom, #d0e4f7 0%,#73b1e7 24%,#0a77d5 50%,#539fe1 79%,#87bcea 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#d0e4f7', endColorstr='#87bcea',GradientType=0)}"
"body{font-family:helvetica,arial,sans-serif;font-size:16px}"
"h1{font-size:26px}"
"p{font-size:14px}"

View file

@ -0,0 +1,18 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<title>",
"</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"</head>"
"<body>"
"<ul class=\"topnav\" id=\"myTopnav\">"
"<li><a href=\"/\">Home</a></li>"
"<li><a href=\"/wificfg/\">WiFi Config</a></li>"
"<li class=\"active\"><a href=\"/tasks.html\">Tasks</a></li>"
"<li class=\"icon\">"
"<a href=\"javascript:void(0);\" onclick=\"myFunction()\">&#9776;</a>"
"</li>"
"</ul>",
"</body></html>"

View file

@ -0,0 +1,90 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<title>",
"</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"</head>"
"<body>"
"<ul class=\"topnav\" id=\"myTopnav\">"
"<li><a href=\"/\">Home</a></li>"
"<li><a href=\"/wificfg/\">WiFi Config</a></li>"
"<li><a href=\"/wificfg/sta.html\">WiFi Station</a></li>"
"<li class=\"active\"><a href=\"/wificfg/ap.html\">WiFi Access Point</a></li>"
"<li class=\"icon\">"
"<a href=\"javascript:void(0);\" onclick=\"myFunction()\">&#9776;</a>"
"</li>"
"</ul>"
"<form action=\"/wificfg/ap.html\" method=\"post\">"
"<fieldset>"
"<legend>WiFi Access Point configuration</legend>"
"<dl class=\"dlh\">"
"<dt><label for=\"enable\">Enable AP mode</label></dt>"
"<dd><input id=\"enable\" type=\"checkbox\" name=\"ap_enable\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"disable_if_sta\">Auto disable if station connection</label></dt>"
"<dd><input id=\"hidden\" type=\"checkbox\" name=\"ap_disable_if_sta\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"disabled_restarts\">Disabled restarts</label></dt>"
"<dd><input id=\"disabled_restarts\" type=\"number\" size=\"2\" min=\"0\" step=\"1\" "
"name=\"ap_disabled_restarts\" placeholder=\"0\" value=\"",
"\"/></dd>"
"<dt><label for=\"ssid\">SSID</label></dt>"
"<dd><input id=\"ssid\" type=\"text\" minlength=\"1\" maxlength=\"31\" name=\"ap_ssid\" "
"placeholder=\"my access point\" value=\"",
"\"></dd>"
"<dt><label for=\"ap\">Password</label></dt>"
"<dd><input id=\"ap\" type=\"text\" minlength=\"8\" maxlength=\"63\" name=\"ap_password\" "
"placeholder=\"password\" value=\"",
"\"></dd>"
"<dt><label for=\"hidden\">SSID Hidden</label></dt>"
"<dd><input id=\"hidden\" type=\"checkbox\" name=\"ap_ssid_hidden\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"ch\">Channel</label></dt>"
"<dd><input id=\"ch\" type=\"number\" size=\"2\" min=\"1\" max=\"14\" step=\"1\" "
"name=\"ap_channel\" placeholder=\"6\" value=\"",
"\"></dd>"
"<dt><label for=\"am\">Authentication Mode</label></dt>"
"<dd><select id=\"am\" name=\"ap_authmode\">"
"<option value=\"0\"",
">Open</option>"
"<option value=\"1\"",
">WEP</option>"
"<option value=\"2\"",
">WPA_PSK</option>"
"<option value=\"3\"",
">WPA2_PSK</option>"
"<option value=\"4\"",
">WPA_WPA2_PSK</option></select></dd>"
"<dt><label for=\"mc\">Max connections</label></dt>"
"<dd><input id=\"mc\" type=\"number\" size=\"2\" min=\"1\" max=\"8\" step=\"1\" "
"name=\"ap_max_conn\" placeholder=\"3\" value=\"",
"\"></dd>"
"<dt><label for=\"bi\">Beacon interval</label></dt>"
"<dd><input id=\"bi\" type=\"number\" size=\"6\" min=\"0\" max=\"10000\" step=\"1\" "
"name=\"ap_beacon_interval\" placeholder=\"100\" value=\"",
"\"></dd>"
"<dt><label for=\"ip\">IP Address</label></dt>"
"<dd><input id=\"ip\" type=\"text\" maxlength=\"15\" size=\"15\" "
"pattern=\"(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*){3}\" "
"name=\"ap_ip_addr\" placeholder=\"192.168.4.1\" value=\"",
"\"></dd>"
"<dt><label for=\"nm\">Netmask</label></dt>"
"<dd><input id=\"nm\" type=\"text\" maxlength=\"15\" size=\"15\" "
"pattern=\"(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*){3}\" "
"name=\"ap_netmask\" placeholder=\"255.255.255.0\" value=\"",
"\"></dd>"
"<dt><label for=\"dhcp\">DHCP Server Max Leases</label></dt>"
"<dd><input id=\"dhcp\" type=\"number\" size=\"2\" min=\"0\" max=\"16\" step=\"1\" "
"name=\"ap_dhcp_leases\" placeholder=\"4\" value=\"",
"\"></dd>"
"<dt><label for=\"dns_en\">DNS enabled</label></dt>"
"<dd><input id=\"dns_en\" type=\"checkbox\" name=\"ap_dns\" value=\"1\" ",
" /></dd>"
"</dl>"
"<center><input type=\"reset\">&nbsp;<input type=\"submit\" value=\"Save\"></center>"
"</fieldset>"
"<input type=\"hidden\" name=\"done\">"
"</form>"
"</body></html>"

View file

@ -0,0 +1,52 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<title>",
"</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"</head>"
"<body>"
"<ul class=\"topnav\" id=\"myTopnav\">"
"<li><a href=\"/\">Home</a></li>"
"<li class=\"active\"><a href=\"/wificfg/\">WiFi Config</a></li>"
"<li><a href=\"/wificfg/sta.html\">WiFi Station</a></li>"
"<li><a href=\"/wificfg/ap.html\">WiFi Access Point</a></li>"
"<li class=\"icon\">"
"<a href=\"javascript:void(0);\" onclick=\"myFunction()\">&#9776;</a>"
"</li>"
"</ul>"
"<h1>WiFi Status</h1>"
"<dl class=\"dlh\">",
"</dl>"
"<br>"
"<form action=\"/wificfg/\" method=\"post\" onsubmit=\"return confirm('Are you sure you want to lock the configuration interface, and have you noted the password?');\">"
"<fieldset>"
"<legend>Lock the configuration interface</legend>"
"<p>These WiFi configuration pages can be disabled for security on a shared network. If a password is supplied then they can be unlocked. Warning: if no password is supplied then it will not be possible to unlock these pages via this interface.</p>"
"<dl class=\"dlh\">"
"<dt><label for=\"pw\">Password</label></dt>"
"<dd><input id=\"pw\" type=\"text\" maxlength=\"32\" name=\"cfg_password\" "
"placeholder=\"unlock-password\" value=\"",
"\"></dd>"
"</dl>"
"<input type=\"hidden\" name=\"cfg_enable\" value=\"0\">"
"<center><input type=\"submit\" value=\"Lock\"></center>"
"</fieldset>"
"</form>"
"<form action=\"/wificfg/restart.html\" method=\"post\" onsubmit=\"return confirm('Are you sure you want to restart the device?');\">"
"<fieldset>"
"<legend>Restart device</legend>"
"<p>A restart is necessary for some changes to take effect.</p>"
"<center><button>Restart</button></center>"
"</fieldset></form>"
"<form action=\"/wificfg/erase.html\" method=\"post\" onsubmit=\"return confirm('Are you sure you want to erase the configuration?');\">"
"<fieldset>"
"<legend>Erase configuration</legend>"
"<p>Erases the device configuration stored in the flash memory and restarts the device. "
"This might be useful to clear stored passwords and private configuration information."
"</p>"
"<center><button>Erase Configuration</button></center>"
"</fieldset>"
"</form>",
"</body></html>"

View file

@ -0,0 +1,68 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<title>",
"</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"</head>"
"<body>"
"<ul class=\"topnav\" id=\"myTopnav\">"
"<li><a href=\"/\">Home</a></li>"
"<li><a href=\"/wificfg/\">WiFi Config</a></li>"
"<li class=\"active\"><a href=\"/wificfg/sta.html\">WiFi Station</a></li>"
"<li><a href=\"/wificfg/ap.html\">WiFi Access Point</a></li>"
"<li class=\"icon\">"
"<a href=\"javascript:void(0);\" onclick=\"myFunction()\">&#9776;</a>"
"</li>"
"</ul>"
"<form action=\"/wificfg/sta.html\" method=\"post\">"
"<fieldset>"
"<legend>WiFi Station configuration</legend>"
"<dl class=\"dlh\">"
"<dt><label for=\"enable\">Enable Station mode</label></dt>"
"<dd><input id=\"enable\" type=\"checkbox\" name=\"sta_enable\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"disabled_restarts\">Disabled restarts</label></dt>"
"<dd><input id=\"disabled_restarts\" type=\"number\" size=\"2\" min=\"0\" step=\"1\" "
"name=\"sta_disabled_restarts\" placeholder=\"0\" value=\"",
"\" /></dd>"
"<dt><label for=\"ssid\">SSID</label></dt>"
"<dd><input id=\"ssid\" minlength=\"1\" maxlength=\"31\" type=\"text\" name=\"sta_ssid\" "
"placeholder=\"my access point\" value=\"",
"\"></dd>"
"<dt><label for=\"pw\">Password</label></dt>"
"<dd><input id=\"pw\" type=\"text\" minlength=\"8\" maxlength=\"63\" name=\"sta_password\" "
"placeholder=\"password\" value=\"",
"\"></dd>"
"<dt><label for=\"hostname\">Hostname</label></dt>"
"<dd><input id=\"hostname\" type=\"text\" maxlength=\"63\" name=\"hostname\" "
"placeholder=\"device-hostname\" value=\"",
"\"></dd>"
"<dt><label for=\"dhcp\">Enable DHCP</label></dt>"
"<dd><input id=\"dhcp\" type=\"radio\" name=\"sta_dhcp\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"static\">Disable DHCP (static address below)</label></dt>"
"<dd><input id=\"static\" type=\"radio\" name=\"sta_dhcp\" value=\"0\" ",
" /></dd>"
"<dt><label for=\"ip\">Static IP Address</label></dt>"
"<dd><input id=\"ip\" type=\"text\" maxlength=\"15\" size=\"15\" "
"pattern=\"(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*){3}\" "
"name=\"sta_ip_addr\" placeholder=\"192.168.1.50\" value=\"",
"\"></dd>"
"<dt><label for=\"nm\">Static Netmask</label></dt>"
"<dd><input id=\"nm\" type=\"text\" maxlength=\"15\" size=\"15\" "
"pattern=\"(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*){3}\" "
"name=\"sta_netmask\" placeholder=\"255.255.255.0\" value=\"",
"\"></dd>"
"<dt><label for=\"gw\">Static Gateway</label></dt>"
"<dd><input id=\"gw\" type=\"text\" maxlength=\"15\" size=\"15\" "
"pattern=\"(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)_*){3}\" "
"name=\"sta_gateway\" placeholder=\"192.168.1.1\" value=\"",
"\"></dd>"
"</dl>"
"<center><input type=\"reset\">&nbsp;<input type=\"submit\" value=\"Save\"></center>"
"</fieldset>"
"<input type=\"hidden\" name=\"done\">"
"</form>"
"</body></html>"

2027
extras/wificfg/wificfg.c Normal file

File diff suppressed because it is too large Load diff

137
extras/wificfg/wificfg.h Normal file
View file

@ -0,0 +1,137 @@
/*
* WiFi configuration via a simple web server.
*
* Copyright (C) 2016 OurAirQuality.org
*
* Licensed under the Apache License, Version 2.0, January 2004 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/
*
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
*
*/
#ifndef __WIFICFG_H__
#define __WIFICFG_H__
#ifdef __cplusplus
extern "C" {
#endif
/*
* Printf format used to initialize a default AP ssid. It is passed the last
* three bytes of the mac address. This may be NULL to not default the ssid,
* but the AP network will not run without a ssid.
*/
extern char *wificfg_default_ssid;
/*
* A default password for the AP interface. This may be NULL to not default the
* password, but the AP network will not run without a password. The minimum
* length is 8 characters.
*/
extern char *wificfg_default_password;
/*
* A default hostname printf format string. This may be NULL to not default the
* hostname.
*/
extern char *wificfg_default_hostname;
/*
* The web server parses the http method string in these enums. The ANY method
* is only use for dispatch. The method enum is passed to the handler functions.
*/
typedef enum {
HTTP_METHOD_GET,
HTTP_METHOD_POST,
HTTP_METHOD_HEAD,
HTTP_METHOD_OTHER,
HTTP_METHOD_ANY,
} wificfg_method;
/*
* The web server parses these content-type header values. This is passed to the
* dispatch function.
*/
typedef enum {
HTTP_CONTENT_TYPE_WWW_FORM_URLENCODED,
HTTP_CONTENT_TYPE_OTHER
} wificfg_content_type;
/*
* The function signature for the http server request handler functions.
*
* The buffer, with its length, is usable by the handler.
*/
typedef int (* wificfg_handler)(int s, wificfg_method method,
uint32_t content_length,
wificfg_content_type content_type,
char *buf, size_t len);
typedef struct {
const char *path;
wificfg_method method;
wificfg_handler handler;
bool secure;
} wificfg_dispatch;
/*
* Start the Wifi Configuration http server task. The IP port number
* and a path dispatch list are needed. The dispatch list can not be
* stack allocated as it is passed to another task.
*/
void wificfg_init(uint32_t port, const wificfg_dispatch *dispatch);
/*
* Support for reading a form name or value from the socket. The name or value
* is truncated to the buffer length. The number of characters read is limited
* to the remainder which is updated. The 'valp' flag is set if a value follows.
*/
int wificfg_form_name_value(int s, bool *valp, size_t *rem, char *buf, size_t len);
/* Support for form url-encoding decoder. */
void wificfg_form_url_decode(char *string);
/* Support for html-escaping of form values. */
void wificfg_html_escape(char *string, char *buf, size_t len);
/* Support for writing a string in a response. */
int wificfg_write_string(int s, const char *str);
/* Support for writing a string in a response, with chunk transfer encoding.
* An optional buffer may be supplied to use to construct a chunk with the
* header and trailer, reducing the number of write() calls, and the str may be
* at the start of this buffer.
*/
int wificfg_write_string_chunk(int s, const char *str, char *buf, size_t len);
/* Write a chunk transfer encoding end marker. */
int wificfg_write_chunk_end(int s);
/* Write a chunk offset 4 bytes into the buffer. */
int wificfg_write_buffer_chunk(int s, char *buf);
/* Write a html title meta data, using the hostname or AP SSI. */
int wificfg_write_html_title(int s, char *buf, size_t len, const char *str);
/* Callback to notify the wificfg logic that a station connection has been
* successfully established. It might use this to disable the AP interface after
* a restart.
*/
void wificfg_got_sta_connect(void);
#ifdef __cplusplus
}
#endif
#endif // __WIFICFG_H__