Wifi web based configuration.

This commit is contained in:
ourairquality 2016-09-02 23:02:57 +10:00 committed by Our Air Quality
parent 5c885c7722
commit 26a5eba0ad
19 changed files with 2109 additions and 5 deletions

View file

@ -50,7 +50,7 @@ void user_init(void)
ip_addr_t first_client_ip;
IP4_ADDR(&first_client_ip, 172, 16, 0, 2);
dhcpserver_start(&first_client_ip, 4);
dhcpserver_start(&first_client_ip, 4, false);
xTaskCreate(telnetTask, "telnetTask", 512, NULL, 2, NULL);
}

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,16 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<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,88 @@
/*
* 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.0 200 \r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"\r\n";
static const char *http_index_content[] = {
#include "content/index.html"
};
static void 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;
if (method != HTTP_METHOD_HEAD) {
if (wificfg_write_string(s, http_index_content[0]) < 0) return;
socklen_t addr_len;
struct sockaddr addr;
addr_len = sizeof(addr);
getpeername(s, (struct sockaddr*)&addr, &addr_len);
if (wificfg_write_string(s, "<dl class=\"dlh\">") < 0) return;
if (addr.sa_family == AF_INET) {
struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
if (wificfg_write_string(s, "<dt>Peer address</dt>") < 0) return;
snprintf(buf, len, "<dd>" IPSTR " : %d</dd>",
IP2STR(&sa->sin_addr), ntohs(sa->sin_port));
if (wificfg_write_string(s, buf) < 0) return;
}
if (wificfg_write_string(s, "</dl>") < 0) return;
if (wificfg_write_string(s, http_index_content[1]) < 0) return;
}
}
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

@ -44,6 +44,7 @@ typedef struct {
ip_addr_t first_client_addr;
struct netif *server_if;
dhcp_lease_t *leases; /* length max_leases */
bool dns; /* Enable sending a DNS server option */
} server_state_t;
/* Only one DHCP server task can run at once, so we have global state
@ -77,7 +78,7 @@ inline static void sprintf_ipaddr(const ip_addr_t *addr, char *dest)
ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr));
}
void dhcpserver_start(const ip_addr_t *first_client_addr, uint8_t max_leases)
void dhcpserver_start(const ip_addr_t *first_client_addr, uint8_t max_leases, bool dns)
{
/* Stop any existing running dhcpserver */
if(dhcpserver_task_handle)
@ -88,8 +89,9 @@ void dhcpserver_start(const ip_addr_t *first_client_addr, uint8_t max_leases)
state->leases = calloc(max_leases, sizeof(dhcp_lease_t));
// state->server_if is assigned once the task is running - see comment in dhcpserver_task()
ip_addr_copy(state->first_client_addr, *first_client_addr);
state->dns = dns;
xTaskCreate(dhcpserver_task, "DHCPServer", 768, NULL, 8, &dhcpserver_task_handle);
xTaskCreate(dhcpserver_task, "DHCP Server", 336, NULL, 2, &dhcpserver_task_handle);
}
void dhcpserver_stop(void)
@ -206,6 +208,8 @@ static void handle_dhcp_discover(struct dhcp_msg *dhcpmsg)
opt = add_dhcp_option_byte(opt, DHCP_OPTION_MESSAGE_TYPE, DHCP_OFFER);
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SUBNET_MASK, &state->server_if->netmask, 4);
if (state->dns)
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_DNS_SERVER, &state->server_if->ip_addr, 4);
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);
struct netbuf *netbuf = netbuf_new();
@ -279,6 +283,8 @@ static void handle_dhcp_request(struct dhcp_msg *dhcpmsg)
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_LEASE_TIME, &expiry, 4);
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SERVER_ID, &state->server_if->ip_addr, 4);
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_SUBNET_MASK, &state->server_if->netmask, 4);
if (state->dns)
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_DNS_SERVER, &state->server_if->ip_addr, 4);
opt = add_dhcp_option_bytes(opt, DHCP_OPTION_END, NULL, 0);
struct netbuf *netbuf = netbuf_new();

View file

@ -26,7 +26,7 @@ extern "C" {
to a client. Subsequent lease addresses are calculated by
incrementing the final octet of the IPv4 address, up to max_leases.
*/
void dhcpserver_start(const ip_addr_t *first_client_addr, uint8_t max_leases);
void dhcpserver_start(const ip_addr_t *first_client_addr, uint8_t max_leases, bool dns);
void dhcpserver_get_lease(const ip_addr_t *first_client_addr, uint8_t max_leases);

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,32 @@
"HTTP/1.0 200 \r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"\r\n"
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<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=\"/wificfg/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,9 @@
"HTTP/1.0 200 \r\n"
"Content-Type: image/svg+xml\r\n"
"Cache-Control: max-age=900\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,6 @@
"HTTP/1.0 200 \r\n"
"Content-Type: text/javascript\r\n"
"Cache-Control: max-age=900\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,18 @@
"HTTP/1.0 200 \r\n"
"Content-Type: text/css\r\n"
"Cache-Control: max-age=900\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,16 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<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,89 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<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=\"radio\" name=\"ap_enable\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"disable\">Disable AP mode</label></dt>"
"<dd><input id=\"disable\" type=\"radio\" name=\"ap_enable\" value=\"0\" ",
" /></dd>"
"<dt><label for=\"ssid\">SSID</label></dt>"
"<dd><input id=\"ssid\" type=\"text\" maxlength=\"31\" name=\"ap_ssid\" "
"placeholder=\"my access point\" value=\"",
"\"></dd>"
"<dt><label for=\"ap\">Password</label></dt>"
"<dd><input id=\"ap\" type=\"text\" maxlength=\"63\" name=\"ap_password\" "
"placeholder=\"password\" value=\"",
"\"></dd>"
"<dt><label for=\"hidden\">SSID Hidden</label></dt>"
"<dd><input id=\"hidden\" type=\"radio\" name=\"ap_ssid_hidden\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"visible\">SSID Visible</label></dt>"
"<dd><input id=\"visible\" type=\"radio\" name=\"ap_ssid_hidden\" value=\"0\" ",
" /></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=\"radio\" name=\"ap_dns\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"dns_dis\">DNS disabled</label></dt>"
"<dd><input id=\"dns_dis\" type=\"radio\" name=\"ap_dns\" value=\"0\" ",
" /></dd>"
"</dl>"
"<center><input type=\"reset\">&nbsp;<input type=\"submit\" value=\"Save\"></center>"
"</fieldset>"
"</form>"
"</body></html>"

View file

@ -0,0 +1,41 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<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\">"
"<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\">"
"<fieldset>"
"<legend>Restart device</legend>"
"<p>A restart is necessary for some changes to take effect. Warning: this is currently not reliable and might freeze the device requiring a power cycle.</p>"
"<center><button>Restart</button></center>"
"</fieldset></form>"
"</body></html>"

View file

@ -0,0 +1,60 @@
"<!DOCTYPE html><html lang=\"en\">"
"<head>"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">"
"<script src=\"/script.js\"></script>"
"<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=\"radio\" name=\"sta_enable\" value=\"1\" ",
" /></dd>"
"<dt><label for=\"disable\">Disable Station mode</label></dt>"
"<dd><input id=\"disable\" type=\"radio\" name=\"sta_enable\" value=\"0\" ",
" /></dd>"
"<dt><label for=\"ssid\">SSID</label></dt>"
"<dd><input id=\"ssid\" 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\" maxlength=\"63\" name=\"sta_password\" "
"placeholder=\"password\" 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>"
"</form>"
"</body></html>"

1606
extras/wificfg/wificfg.c Normal file

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,95 @@
/*
* 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
/*
* 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 void (* 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);
#ifdef __cplusplus
}
#endif
#endif // __WIFICFG_H__

View file

@ -105,7 +105,7 @@ static void a_04_wifi_basic(void)
ip_addr_t first_client_ip;
IP4_ADDR(&first_client_ip, 172, 16, 0, 2);
dhcpserver_start(&first_client_ip, 4);
dhcpserver_start(&first_client_ip, 4, false);
xTaskCreate(server_task, "setver_task", 1024, NULL, 2, NULL);
}