switch all pages to ajax; wifi not complete

pull/25/head
Thorsten von Eicken 10 years ago
parent 955f9fc29b
commit ccd91bd150
  1. 2
      Makefile
  2. 2
      html/140medley.min.js
  3. 30
      html/console.js
  4. 87
      html/console.tpl
  5. 9
      html/head.tpl
  6. 67
      html/help.tpl
  7. 6
      html/home.tpl
  8. 16
      html/led.tpl
  9. 13
      html/log.tpl
  10. 92
      html/style.css
  11. 70
      html/ui.js
  12. 44
      html/wifi/connecting.html
  13. 192
      html/wifi/wifi.tpl
  14. 4
      httpd/httpd.c
  15. 68
      httpd/httpdespfs.c
  16. 1
      httpd/httpdespfs.h
  17. 32
      serial/console.c
  18. 148
      user/cgi.c
  19. 8
      user/cgi.h
  20. 142
      user/cgiwifi.c
  21. 2
      user/cgiwifi.h
  22. 55
      user/log.c
  23. 2
      user/log.h
  24. 24
      user/user_main.c

@ -46,7 +46,7 @@ MCU_ISP_PIN ?= 13
# GPIO pin used for "connectivity" LED, active low # GPIO pin used for "connectivity" LED, active low
LED_CONN_PIN ?= 0 LED_CONN_PIN ?= 0
# GPIO pin used for "serial activity" LED, active low # GPIO pin used for "serial activity" LED, active low
LED_SERIAL_PIN ?= 2 LED_SERIAL_PIN ?= 14
# --------------- esp-link version --------------- # --------------- esp-link version ---------------

@ -1,2 +1,2 @@
var t=function(a,b){return function(c,d){return a.replace(/#{([^}]*)}/g,function(a,f){return Function("x","with(x)return "+f).call(c,d||b||{})})}},s=function(a,b){return b?{get:function(c){return a[c]&&b.parse(a[c])},set:function(c,d){a[c]=b.stringify(d)}}:{}}(this.localStorage||{},JSON),p=function(a,b,c,d){c=c||document;d=c[b="on"+b];a=c[b]=function(e){d=d&&d(e=e||c.event);return(a=a&&b(e))?b:d};c=this},m=function(a,b,c){b=document;c=b.createElement("p");c.innerHTML=a;for(a=b.createDocumentFragment();b= var t=function(a,b){return function(c,d){return a.replace(/#{([^}]*)}/g,function(a,f){return Function("x","with(x)return "+f).call(c,d||b||{})})}},s=function(a,b){return b?{get:function(c){return a[c]&&b.parse(a[c])},set:function(c,d){a[c]=b.stringify(d)}}:{}}(this.localStorage||{},JSON),b=function(a,b,c,d){c=c||document;d=c[b="on"+b];a=c[b]=function(e){d=d&&d(e=e||c.event);return(a=a&&b(e))?b:d};c=this},m=function(a,b,c){b=document;c=b.createElement("p");c.innerHTML=a;for(a=b.createDocumentFragment();b=
c.firstChild;)a.appendChild(b);return a},$=function(a,b){a=a.match(/^(\W)?(.*)/);return(b||document)["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2])},j=function(a){for(a=0;a<4;a++)try{return a?new ActiveXObject([,"Msxml2","Msxml3","Microsoft"][a]+".XMLHTTP"):new XMLHttpRequest}catch(b){}}; c.firstChild;)a.appendChild(b);return a},$=function(a,b){a=a.match(/^(\W)?(.*)/);return(b||document)["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2])},j=function(a){for(a=0;a<4;a++)try{return a?new ActiveXObject([,"Msxml2","Msxml3","Microsoft"][a]+".XMLHTTP"):new XMLHttpRequest}catch(b){}};

@ -0,0 +1,30 @@
function fetchText(delay) {
el = $("#console");
if (el.textEnd == undefined) {
el.textEnd = 0;
el.innerHTML = "";
}
window.setTimeout(function() {
ajaxJson('GET', console_url + "?start=" + el.textEnd, updateText, retryLoad);
}, delay);
}
function updateText(resp) {
el = $("#console");
delay = 3000;
if (resp != null && resp.len > 0) {
console.log("updateText got", resp.len, "chars at", resp.start);
if (resp.start > el.textEnd) {
el.innerHTML = el.innerHTML.concat("\r\n<missing lines\r\n");
}
el.innerHTML = el.innerHTML.concat(resp.text);
el.textEnd = resp.start + resp.len;
delay = 500;
}
fetchText(delay);
}
function retryLoad() {
fetchText(1000);
}

@ -1,5 +1,3 @@
%head%
<div id="main"> <div id="main">
<div class="header"> <div class="header">
<h1><span class="esp">esp</span> link - Microcontroller Console</h1> <h1><span class="esp">esp</span> link - Microcontroller Console</h1>
@ -22,88 +20,29 @@
</div> </div>
<script src="ui.js"></script> <script src="ui.js"></script>
<script type="text/javascript">console_url = "/console/text"</script>
<script src="console.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function loadJSON(url, okCb, errCb) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleEv;
function handleEv() {
try {
//console.log("handleEv", xhr.readyState);
if(xhr.readyState < 4) {
return;
}
if(xhr.status !== 200) {
//console.log("handleEv error cb");
if (errCb != null) errCb(xhr);
return;
}
// all is well
if(xhr.readyState === 4) {
//console.log("handleEv success cb");
if (okCb != null) okCb(xhr, JSON.parse(xhr.responseText));
}
} catch(e) {return;}
}
xhr.open('GET', url, true);
xhr.send('');
}
function fetchText(delay) {
el = document.getElementById("console");
if (el.textEnd == undefined) {
el.textEnd = 0;
el.innerHTML = "";
}
window.setTimeout(function() {
loadJSON("/console/text?start=" + el.textEnd, updateText, retryLoad);
}, delay);
}
function updateText(xhr, resp) {
el = document.getElementById("console");
delay = 3000;
if (el == null || resp == null) {
//console.log("updateText got null response? xhr=", xhr);
} else if (resp.len == 0) {
//console.log("updateText got no new text");
} else {
console.log("updateText got", resp.len, "chars at", resp.start);
if (resp.start > el.textEnd) {
el.innerHTML = el.innerHTML.concat("\r\n<missing lines\r\n");
}
el.innerHTML = el.innerHTML.concat(resp.text);
el.textEnd = resp.start + resp.len;
delay = 500;
}
fetchText(delay);
}
function retryLoad(xhr) {
fetchText(1000);
}
function baudButton(baud) { function baudButton(baud) {
document.getElementById(""+baud+"-button").addEventListener("click", function(e) { $("#"+baud+"-button").addEventListener("click", function(e) {
console.log("switching to", baud, "baud");
e.preventDefault(); e.preventDefault();
loadJSON("/console/baud?rate="+baud); ajaxSpin('GET', "/console/baud?rate="+baud,
function(resp) { showNotification("" + baud + " baud set"); },
function(s, st) { showWarning("Error setting baud rate: ", st); }
);
}); });
} }
window.onload = function() { window.onload = function() {
fetchText(100); fetchText(100);
document.getElementById("reset-button").addEventListener("click", function(e) { $("#reset-button").addEventListener("click", function(e) {
el = document.getElementById("console");
e.preventDefault(); e.preventDefault();
//console.log("reset click"); $("#console").innerHTML = "";
el.innerHTML = ""; ajaxSpin('GET', "/console/reset",
loadJSON("/console/reset"); function(resp) { showNotification("uC reset"); },
function(s, st) { showWarning("Error resetting uC"); }
);
}); });
baudButton(57600); baudButton(57600);
baudButton(115200); baudButton(115200);

@ -3,14 +3,21 @@
<link rel="stylesheet" href="/pure-min.css"> <link rel="stylesheet" href="/pure-min.css">
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/140medley.min.js"></script>
</head> </head>
<body> <body>
<div id="layout"> <div id="layout">
<div id="spinner" class="spinner" hidden></div>
<div id="messages">
<div id="warning" hidden>Warning message</div>
<div id="notification" hidden>Notification</div>
</div>
<a href="#menu" id="menuLink" class="menu-link"><span></span></a> <a href="#menu" id="menuLink" class="menu-link"><span></span></a>
<div id="menu"> <div id="menu">
<div class="pure-menu"> <div class="pure-menu">
<a class="pure-menu-heading" href="/">esp-link</a> <a class="pure-menu-heading" href="/">esp-link</a>
<ul class="pure-menu-list">%topnav%</ul> <ul id="menu-list" class="pure-menu-list"></ul>
</div> </div>
</div> </div>

@ -1,5 +1,3 @@
%head%
<div id="main"> <div id="main">
<div class="header"> <div class="header">
<h1><span class="esp">esp</span> link - Help</h1> <h1><span class="esp">esp</span> link - Help</h1>
@ -7,70 +5,9 @@
<div class="content"> <div class="content">
<p>This text is somewhat out of date, please refer to <p>Please refer to
<a href="https://github.com/jeelabs/esp-link/blob/master/README.md">the online README</a> <a href="https://github.com/jeelabs/esp-link/blob/master/README.md">the online README</a>
for the time being.</p> for up-to-date help.</p>
<p>The ESP Link functions in two wifi modes: Station+AccessPoint (STA+AP) and Station (STA).
In the STA+AP mode it presents a network called esp8266 that you can connect to using the
password jeelabs8266. This mode is intended for initial configuration, but it is
fully functional. Typically the easiest way to connect to the esp8266 network is using a phone,
tablet, or laptop.</p>
<p>The recommended next step is to configure the ESP Link to connect to your "normal"
Wifi network so you can access it from any of your machines. Once you have connected the ESP Link
to your network and pointed your browser at it successfully, you should
switch the ESP Link to STA mode, which is more secure (no canned password).<p>
<p>In STA mode the ESP Link connects to the configured network (the info is saved in flash).
If, after a reset, it cannot connect for one minute, it automatically reverts to STA+AP mode
allowing you to reconnect to the esp8266 network to change configuration.</p>
<p>In STA mode the most tricky part usually is the IP address. On most networks, the ESP Link
will get a dynamic IP address assigned via DHCP and you now need to enter that IP address into
your browser to connect. The good news is that after you reset your ESP Link it will continue to
have the same IP address. However, if you leave it off for the week-end it will most likely get a
fresh IP the next time it starts up. On many Wifi routers you can enter a fixed mapping from
the ESP Link's hardware MAC address to a static IP address so it always gets the same IP
address. This is the recommended method of operation.</p>
<h2 class="content-subhead">Using your esp-link</h2>
<p>
The esp-link can used in several distinct ways:
<ul>
<li>as a transparent bridge between TCP port 23 and the serial port</li>
<li>as a web console to see input from the serial port</li>
<li>as an Arduino, AVR, or ARM processor programmer using serial-over-TCP</li>
<li>as an Arduino, AVR, or ARM processor programmer by uploading HEX files (not yet functional)</li>
</ul>
</p>
<h2 class="content-subhead">Transparent bridge</h2>
<p>The ESP accepts TCP connections to port 23 and "connects" through to the serial port.
Up to 5 simultaneous TCP connections are supported and characters coming in on the serial
port get passed through to all connections. Characters coming in on a connection get copied
through to the serial port.</p>
<p>When using Linux a simple way to use this is <tt>nc esp8266 23</tt></p>
<h2 class="content-subhead">Programmer using serial-over-TCP</h2>
<p>By hooking up the ESP's GPIO lines to the reset line of an Arduino (or AVR in general) that is
preloaded with the Optiboot bootloader/flasher it is possible to reprogram these processors over
Wifi. The way is works is that the ESP toggles the reset line each time a connection is established
and the first characters are the flash programming synchronization sequence.</p>
<p>When using Linux avrdude can be instructed to program an AVR over TCP by using a special syntax
for the serial port: <tt>-Pnet:esp8266:23</tt>, where <tt>esp8266</tt> is the hostname of the ESP
Serial Programmer (an IP address could have been used instead).</p>
<p>NXP's LPC800-serial ARM processors can be programmed similarly by hooking up GPIO pins to the
ARM's reset and ISP lines. The ESP Serial Programmer issues the correct reset/isp pulses to put
the ARM chip into firmware programming mode.</p>
<h2 class="content-subhead">Web Console</h2>
<p>The output of an attached Arduino/AVR/ARM can also be monitored via the console web page.
When connecting, it shows the most recent 10KB of characters received on the serial port and
then continues to print everything that comes in on the serial port. Eventually the page refreshes
when it gets very long. (Yes, this could be improved with some javascript...)</p>
<h2 class="content-subhead">Programmer using HEX upload</h2>
<p><i>(Not yet functional)</i> Instead of using the wifi-to-serial bridge to program
microcontrollers it is often faster to upload the HEX file to the ESP Serial Programmer and
have it perform the actual programming protocol.</p>
</div> </div>
</div> </div>

@ -1,13 +1,11 @@
%head%
<div id="main"> <div id="main">
<div class="header"> <div class="header">
<h1><span class="esp">esp</span> link</h1> <h1><span class="esp">esp</span> link</h1>
<h2>%version%</h2> <h2 id="version"></h2>
</div> </div>
<div class="content"> <div class="content">
<p>The ESP Link connects the ESP's serial port to Wifi and it can <p>The ESP Link bridges the ESP8266 serial port to Wifi and it can
program microcontrollers over the serial port, in particular Arduinos, AVRs, and program microcontrollers over the serial port, in particular Arduinos, AVRs, and
NXP's LPC800-series ARM processors.</p> NXP's LPC800-series ARM processors.</p>
</div> </div>

@ -1,16 +0,0 @@
<html><head><title>LED test - ESP Link</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="main">
<div id="topnav">%topnav%</div>
<h1><span class="esp">esp</span> link - LED test</h1>
<p>
If there's a LED connected to GPIO2, it's now %ledstate%. You can change that using the buttons below.
</p>
<form method="post" action="led.cgi">
<input type="submit" name="led" value="1">
<input type="submit" name="led" value="0">
</form>
</div>
</body></html>

@ -1,5 +1,3 @@
%head%
<div id="main"> <div id="main">
<div class="header"> <div class="header">
<h1><span class="esp">esp</span> link - Debug Log</h1> <h1><span class="esp">esp</span> link - Debug Log</h1>
@ -8,12 +6,17 @@
<div class="content"> <div class="content">
<p>The debug log shows the 1024 last characters printed by the esp-link software itself to <p>The debug log shows the 1024 last characters printed by the esp-link software itself to
its own debug log.</p> its own debug log.</p>
<pre class="console"> <pre id="console" class="console"></pre>
%log%
</pre>
</div> </div>
</div> </div>
</div> </div>
<script src="ui.js"></script> <script src="ui.js"></script>
<script type="text/javascript">console_url = "/log/text"</script>
<script src="console.js"></script>
<script type="text/javascript">
window.onload = function() {
fetchText(100);
}
</script>
</body></html> </body></html>

@ -7,6 +7,26 @@ body {
color: #777; color: #777;
} }
.card {
background-color: #eee;
padding: 1em;
margin: 0.5em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
border-radius: 0.5em;
border: 0px solid #000000;
}
/* wifi AP selection form */
#aps label div {
display: inline-block;
margin: 0em 0.2em;
}
.pure-table td, .pure-table th {
padding: 0.5em 0.5em;
}
/* make images size-up */ /* make images size-up */
.xx-pure-img-responsive { .xx-pure-img-responsive {
max-width: 100%; max-width: 100%;
@ -237,3 +257,75 @@ pre.console a {
} }
} }
#messages {
position: absolute;
left: 25%;
width: 50%;
top: 10;
z-index: 200;
font-size: 110%;
text-align: center;
}
#warning {
background-color: #933;
color: #fcc;
padding: 0.1em 0.4em;
}
#notification {
background-color: #693;
color: #cfc;
padding: 0.1em 0.4em;
}
#spinner {
position: absolute;
right: 10%;
top: 20;
z-index: 1000;
}
.spinner {
height: 50px;
width: 50px;
-webkit-animation: rotation 1s infinite linear;
-moz-animation: rotation 1s infinite linear;
-o-animation: rotation 1s infinite linear;
animation: rotation 1s infinite linear;
border-left: 10px solid rgba(204, 51, 0, 0.15);
border-right: 10px solid rgba(204, 51, 0, 0.15);
border-bottom: 10px solid rgba(204, 51, 0, 0.15);
border-top: 10px solid rgba(204, 51, 0, 0.8);
border-radius: 100%;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
}
@-moz-keyframes rotation {
from {
-moz-transform: rotate(0deg);
}
to {
-moz-transform: rotate(359deg);
}
}
@-o-keyframes rotation {
from {
-o-transform: rotate(0deg);
}
to {
-o-transform: rotate(359deg);
}
}
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}

@ -1,3 +1,16 @@
// fill out menu items
(function() {
console.log("Filling out menu with", menu.length, "items");
html = "";
for (var i=0; i<menu.length; i+=2) {
html = html.concat(" <li class=\"pure-menu-item\"><a href=\"" + menu[i+1] +
"\" class=\"pure-menu-link\">" + menu[i] + "</a></li>");
}
document.getElementById("menu-list").innerHTML = html;
v = document.getElementById("version");
if (v != null) { v.innerHTML = version; }
}());
(function (window, document) { (function (window, document) {
var layout = document.getElementById('layout'), var layout = document.getElementById('layout'),
@ -33,3 +46,60 @@
}; };
}(this, this.document)); }(this, this.document));
function showWarning(text) {
var el = $("#warning");
el.innerHTML = text;
el.removeAttribute('hidden');
}
function hideWarning(text) {
el = $("#warning").setAttribute('hidden', '');
}
function showNotification(text) {
var el = $("#notification");
el.innerHTML = text;
el.removeAttribute('hidden');
setTimeout(function() { el.setAttribute('hidden', ''); }, 4000);
}
function ajaxReq(method, url, ok_cb, err_cb) {
var xhr = j();
xhr.open(method, url);
var timeout = setTimeout(function() {
xhr.abort();
err_cb(599, "Request timeout");
}, 20000);
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
clearTimeout(timeout);
if (xhr.status >= 200 && xhr.status < 300) {
ok_cb(xhr.responseText);
} else {
err_cb(xhr.status, xhr.statusText);
}
}
xhr.send();
}
function ajaxJson(method, url, ok_cb, err_cb) {
ajaxReq(method, url, function(resp) { ok_cb(JSON.parse(resp)); }, err_cb);
}
function ajaxSpin(method, url, ok_cb, err_cb) {
$("#spinner").removeAttribute('hidden');
console.log("starting spinner");
ajaxReq(method, url, function(resp) {
$("#spinner").setAttribute('hidden', '');
ok_cb(resp);
}, function(status, statusText) {
$("#spinner").setAttribute('hidden', '');
showWarning("Request error: " + statusText);
err_cb(status, statusText);
});
}
function ajaxJsonSpin(method, url, ok_cb, err_cb) {
ajaxSpin(method, url, function(resp) { ok_cb(JSON.parse(resp)); }, err_cb);
}

@ -1,44 +0,0 @@
<html><head><title>Connecting... - ESP Link</title>
<link rel="stylesheet" type="text/css" href="/style.css">
<script type="text/javascript" src="140medley.min.js"></script>
<script type="text/javascript">
var xhr=j();
function getStatus() {
xhr.open("GET", "connstatus.cgi");
xhr.onreadystatechange=function() {
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) {
var data=JSON.parse(xhr.responseText);
if (data.status=="idle" || data.status=="connecting") {
$("#status").innerHTML="Connecting...";
window.setTimeout(getStatus, 1000);
} else if (data.status=="got IP address") {
$("#status").innerHTML="Connected! Got IP "+data.ip+ ".<br/>" +
"If you're in the same network, you can access it <a href=\"http://"+data.ip+
"/\">here</a>.<br/>ESP Link will switch to STA-only mode in a few seconds.";
} else {
$("#status").innerHTML="Oops: " + data.status + ". Reason: " + data.reason +
"<br/>Check password and selected AP.<br/><a href=\"wifi.tpl\">Go Back</a>";
}
}
}
xhr.send();
}
window.onload=function(e) {
window.setTimeout(getStatus, 2000);
};
</script>
</head>
<body>
<div id="main">
<h1>ESP Link - Connecting</h1>
<h2>Connecting to AP...</h2>
<p>Status:
<span id="status">...</span>
</p>
</div>
</body>
</html>

@ -1,103 +1,177 @@
%head%
<div id="main"> <div id="main">
<div class="header"> <div class="header">
<h1><span class="esp">esp</span> link - Wifi Configuration</h1> <h1><span class="esp">esp</span> link - Wifi Configuration</h1>
</div> </div>
<div class="content"> <div class="content pure-g">
<h2 class="content-subhead">Current Wifi State</h2> <div class="pure-u-12-24"><div class="card">
<p> <h1>Wifi State</h2>
WiFi mode: %WiFiMode%<br> <table class="pure-table pure-table-horizontal"><tbody>
Configured network: %currSsid% Status: %currStatus% Phy:%currPhy% <tr><td>WiFi mode</td><td id="wifi-mode"></td></tr>
</p> <tr><td>Configured network</td><td id="wifi-ssid"></td></tr>
<p>%WiFiapwarn%</p> <tr><td>Wifi status</td><td id="wifi-status"></td></tr>
<tr><td>Wifi rssi</td><td id="wifi-rssi"></td></tr>
<h2 class="content-subhead">Change Wifi association</h2> <tr><td>Wifi phy</td><td id="wifi-phy"></td></tr>
<form name="wifiform" action="connect.cgi" method="post"> <tr><td colspan="2" id="wifi-warn"></td></tr>
<p>To connect to a WiFi network, please select one of the detected networks, </tbody> </table>
enter the password, and hit the connect button...<p> </div></div>
<div id="aps">Scanning...</div> <div class="pure-u-12-24"><div class="card">
<p>WiFi password, if applicable: <br /> <h1>Wifi Association</h2>
<input type="text" name="passwd" val="%WiFiPasswd%"> <form action="#" id="wifiform" class="pure-form pure-form-stacked">
<input type="submit" name="connect" value="Connect!"> <!--form name="wifiform" action="connect.cgi" method="post"-->
</p> <legend>To connect to a WiFi network, please select one of the detected networks,
</form> enter the password, and hit the connect button...</legend>
<label>Network SSID</label>
<div id="aps">Scanning...</div>
<label>WiFi password, if applicable:</label>
<input id="wifi-passwd" type="text" name="passwd" placeholder="password">
<button id="connect-button" type="submit" class="pure-button button-primary">Connect!</button>
</form>
</div></div>
</div> </div>
</div> </div>
</div> </div>
<script src="ui.js"></script> <script src="/ui.js"></script>
<script src="140medley.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var xhr=j(); var currAp = "";
var currAp="%currSsid%";
function createInputForAp(ap) { function createInputForAp(ap) {
if (ap.essid=="" && ap.rssi==0) return; if (ap.essid=="" && ap.rssi==0) return;
var div=document.createElement("div");
div.id="apdiv"; var input = document.createElement("input");
var rssi=document.createElement("div"); input.type = "radio";
var rssiVal=-Math.floor(ap.rssi/51)*32; input.name = "essid";
rssi.className="lock-icon";
rssi.style.backgroundPosition="0px "+rssiVal+"px";
var encrypt=document.createElement("div");
var encVal="-64"; //assume wpa/wpa2
if (ap.enc=="0") encVal="0"; //open
if (ap.enc=="1") encVal="-32"; //wep
encrypt.className="lock-icon";
encrypt.style.backgroundPosition="-32px "+encVal+"px";
var input=document.createElement("input");
input.type="radio";
input.name="essid";
input.value=ap.essid; input.value=ap.essid;
if (currAp==ap.essid) input.checked="1"; input.id = "opt-" + ap.essid;
input.id="opt-"+ap.essid; if (currAp == ap.essid) input.checked = "1";
var label=document.createElement("label");
label.htmlFor="opt-"+ap.essid; var bars = document.createElement("div");
label.textContent=ap.essid+" ("+ap.rssi+")"; var rssiVal = -Math.floor(ap.rssi/51)*32;
bars.className = "lock-icon";
bars.style.backgroundPosition = "0px "+rssiVal+"px";
var rssi = document.createElement("div");
rssi.innerHTML = "" + ap.rssi +"dB";
var encrypt = document.createElement("div");
var encVal = "-64"; //assume wpa/wpa2
if (ap.enc == "0") encVal = "0"; //open
if (ap.enc == "1") encVal = "-32"; //wep
encrypt.className = "lock-icon";
encrypt.style.backgroundPosition = "-32px "+encVal+"px";
var label = document.createElement("div");
label.innerHTML = ap.essid;
var div = document.createElement("label");
div.for = "opt-" + ap.essid;
div.appendChild(input); div.appendChild(input);
div.appendChild(rssi);
div.appendChild(encrypt); div.appendChild(encrypt);
div.appendChild(bars);
div.appendChild(rssi);
div.appendChild(label); div.appendChild(label);
return div; return div;
} }
function getSelectedEssid() { function getSelectedEssid() {
var e=document.forms.wifiform.elements; var e = document.forms.wifiform.elements;
for (var i=0; i<e.length; i++) { for (var i=0; i<e.length; i++) {
if (e[i].type=="radio" && e[i].checked) return e[i].value; if (e[i].type == "radio" && e[i].checked) return e[i].value;
} }
return currAp; return currAp;
} }
var scan_xhr = j();
function scanAPs() { function scanAPs() {
xhr.open("GET", "wifiscan.cgi"); scan_xhr.open("GET", "wifiscan");
xhr.onreadystatechange=function() { scan_xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { if (scan_xhr.readyState == 4 && scan_xhr.status >= 200 && scan_xhr.status < 300) {
var data=JSON.parse(xhr.responseText); var data = JSON.parse(scan_xhr.responseText);
currAp=getSelectedEssid(); currAp = getSelectedEssid();
if (data.result.inProgress=="0" && data.result.APs.length>1) { if (data.result.inProgress == "0" && data.result.APs.length > 1) {
$("#aps").innerHTML=""; $("#aps").innerHTML = "";
for (var i=0; i<data.result.APs.length; i++) { for (var i=0; i<data.result.APs.length; i++) {
if (data.result.APs[i].essid=="" && data.result.APs[i].rssi==0) continue; if (data.result.APs[i].essid == "" && data.result.APs[i].rssi == 0) continue;
$("#aps").appendChild(createInputForAp(data.result.APs[i])); $("#aps").appendChild(createInputForAp(data.result.APs[i]));
} }
window.setTimeout(scanAPs, 20000); //window.setTimeout(scanAPs, 10000);
} else { } else {
window.setTimeout(scanAPs, 1000); window.setTimeout(scanAPs, 1000);
} }
} }
} }
scan_xhr.send();
}
function getWifiInfo() {
var xhr = j();
xhr.open("GET", "info");
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
if (xhr.status >= 200 && xhr.status < 300) {
var data = JSON.parse(xhr.responseText);
Object.keys(data).forEach(function(v) {
el = document.getElementById("wifi-" + v);
if (el != null) el.innerHTML = data[v];
});
currAp = data.ssid;
} else {
window.setTimeout(getWifiInfo, 1000);
}
}
xhr.send();
}
function getStatus() {
var xhr = j();
xhr.open("GET", "connstatus");
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
if (xhr.status >= 200 && xhr.status < 300) {
var data = JSON.parse(xhr.responseText);
if (data.status == "idle" || data.status == "connecting") {
$("#aps").innerHTML = "Connecting...";
window.setTimeout(getStatus, 1000);
} else if (data.status == "got IP address") {
$("#aps").innerHTML="Connected! Got IP "+data.ip+ ".<br/>" +
"If you're in the same network, you can access it <a href=\"http://"+data.ip+
"/\">here</a>.<br/>ESP Link will switch to STA-only mode in a few seconds.";
} else {
$("#aps").innerHTML="Oops: " + data.status + ". Reason: " + data.reason +
"<br/>Check password and selected AP.<br/><a href=\"wifi.tpl\">Go Back</a>";
}
} else {
window.setTimeout(getStatus, 2000);
}
}
xhr.send(); xhr.send();
} }
function changeWifiAp(e) {
e.preventDefault();
var xhr = j();
var passwd = $("#wifi-passwd").value;
var essid = getSelectedEssid();
console.log("Posting form", "essid=" + essid, "pwd="+passwd);
xhr.open("POST", "connect");
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
if (xhr.status >= 200 && xhr.status < 300) {
window.setTimeout(getStatus, 2000);
} else {
window.setTimeout(scanAPs, 1000);
}
}
xhr.setRequestHeader("Content-type", "application/x-form-urlencoded");
xhr.send("essid="+encodeURIComponent(essid)+"&passwd="+encodeURIComponent(passwd));
}
window.onload=function(e) { window.onload=function(e) {
scanAPs(); getWifiInfo();
$("#wifiform").onsubmit = changeWifiAp;
window.setTimeout(scanAPs, 500);
}; };
</script> </script>
</body></html> </body></html>

@ -19,7 +19,7 @@ Esp8266 http server - core routines
//Max length of request head //Max length of request head
#define MAX_HEAD_LEN 1024 #define MAX_HEAD_LEN 1024
//Max amount of connections //Max amount of connections
#define MAX_CONN 4 #define MAX_CONN 6
//Max post buffer len //Max post buffer len
#define MAX_POST 1024 #define MAX_POST 1024
//Max send buffer len //Max send buffer len
@ -56,7 +56,7 @@ typedef struct {
//add it here. //add it here.
static const MimeMap mimeTypes[]={ static const MimeMap mimeTypes[]={
{"htm", "text/htm"}, {"htm", "text/htm"},
{"html", "text/html"}, {"html", "text/html; charset=UTF-8"},
{"css", "text/css"}, {"css", "text/css"},
{"js", "text/javascript"}, {"js", "text/javascript"},
{"txt", "text/plain"}, {"txt", "text/plain"},

@ -15,6 +15,7 @@ Connector to let httpd use the espfs filesystem to serve the files in it.
#include "httpdespfs.h" #include "httpdespfs.h"
#include "espfs.h" #include "espfs.h"
#include "espfsformat.h" #include "espfsformat.h"
#include "cgi.h"
// The static files marked with FLAG_GZIP are compressed and will be served with GZIP compression. // The static files marked with FLAG_GZIP are compressed and will be served with GZIP compression.
// If the client does not advertise that he accepts GZIP send following warning message (telnet users for e.g.) // If the client does not advertise that he accepts GZIP send following warning message (telnet users for e.g.)
@ -87,6 +88,72 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
} }
//cgiEspFsHtml is a simple HTML file that gets prefixed by head.tpl
int ICACHE_FLASH_ATTR cgiEspFsHtml(HttpdConnData *connData) {
EspFsFile *file = connData->cgiData;
char buff[2048];
if (connData->conn==NULL) {
// Connection aborted. Clean up.
if (file != NULL) espFsClose(file);
return HTTPD_CGI_DONE;
}
// The first time around we send the head template in one go and we open the file
if (file == NULL) {
int status = 200;
// open file, return error on failure
file = espFsOpen("/head.tpl");
if (file == NULL) {
os_strcpy(buff, "Header file 'head.tpl' not found\n");
os_printf(buff);
status = 500;
} else {
// read file and return it
int len = espFsRead(file, buff, sizeof(buff));
if (len == sizeof(buff)) {
os_strcpy(buff, "Header file 'head.tpl' too large!\n");
os_printf(buff);
status = 500;
}
// open the real file for next time around
file = espFsOpen(connData->url);
if (file == NULL) {
os_strcpy(buff, connData->url);
os_strcat(buff, " not found\n");
os_printf(buff);
status = 404;
} else {
connData->cgiData = file;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/html; charset=UTF-8");
httpdEndHeaders(connData);
httpdSend(connData, buff, len);
printGlobalJSON(connData);
return HTTPD_CGI_MORE;
}
}
// error response
httpdStartResponse(connData, status);
httpdHeader(connData, "text/plain", "text/html; charset=UTF-8");
httpdEndHeaders(connData);
httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE;
}
// The second time around send actual file
int len = espFsRead(file, buff, sizeof(buff));
httpdSend(connData, buff, len);
if (len == sizeof(buff)) {
return HTTPD_CGI_MORE;
} else {
connData->cgiData = NULL;
espFsClose(file);
return HTTPD_CGI_DONE;
}
}
#if 0
//cgiEspFsTemplate can be used as a template. //cgiEspFsTemplate can be used as a template.
typedef struct { typedef struct {
@ -186,4 +253,5 @@ int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData) {
return HTTPD_CGI_MORE; return HTTPD_CGI_MORE;
} }
} }
#endif

@ -5,5 +5,6 @@
int cgiEspFsHook(HttpdConnData *connData); int cgiEspFsHook(HttpdConnData *connData);
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData); int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData);
int ICACHE_FLASH_ATTR cgiEspFsHtml(HttpdConnData *connData);
#endif #endif

@ -44,13 +44,6 @@ console_write_char(char c) {
console_write(c); console_write(c);
} }
void ICACHE_FLASH_ATTR
jsonHeader(HttpdConnData *connData, int code) {
httpdStartResponse(connData, code);
httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData);
}
int ICACHE_FLASH_ATTR int ICACHE_FLASH_ATTR
ajaxConsoleReset(HttpdConnData *connData) { ajaxConsoleReset(HttpdConnData *connData) {
jsonHeader(connData, 200); jsonHeader(connData, 200);
@ -119,31 +112,6 @@ ajaxConsole(HttpdConnData *connData) {
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
//===== Display a web page with the console
int ICACHE_FLASH_ATTR
tplConsole(HttpdConnData *connData, char *token, void **arg) {
if (token==NULL) return HTTPD_CGI_DONE;
/*
if (os_strcmp(token, "console") == 0) {
if (console_wr > console_rd) {
httpdSend(connData, console_buf+console_rd, console_wr-console_rd);
} else if (console_rd != console_wr) {
httpdSend(connData, console_buf+console_rd, BUF_MAX-console_rd);
httpdSend(connData, console_buf, console_wr);
} else {
httpdSend(connData, "<buffer empty>", -1);
}
} else if (os_strcmp(token, "head")==0) {
*/
if (os_strcmp(token, "head")==0) {
printHead(connData);
} else {
httpdSend(connData, "Unknown\n", -1);
}
return HTTPD_CGI_DONE;
}
void ICACHE_FLASH_ATTR consoleInit() { void ICACHE_FLASH_ATTR consoleInit() {
console_wr = 0; console_wr = 0;
console_rd = 0; console_rd = 0;

@ -1,6 +1,5 @@
/* /*
Some random cgi routines. Used in the LED example and the page that returns the entire Some random cgi routines.
flash as a binary. Also handles the hit counter on the main page.
*/ */
/* /*
@ -10,6 +9,8 @@ flash as a binary. Also handles the hit counter on the main page.
* this notice you can do whatever you want with this stuff. If we meet some day, * this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return. * and you think this stuff is worth it, you can buy me a beer in return.
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
* Heavily modified and enhanced by Thorsten von Eicken in 2015
* ----------------------------------------------------------------------------
*/ */
@ -17,134 +18,19 @@ flash as a binary. Also handles the hit counter on the main page.
#include "cgi.h" #include "cgi.h"
#include "espfs.h" #include "espfs.h"
void ICACHE_FLASH_ATTR
//cause I can't be bothered to write an ioGetLed() jsonHeader(HttpdConnData *connData, int code) {
static char currLedState=0; httpdStartResponse(connData, code);
httpdHeader(connData, "Content-Type", "application/json");
//Cgi that turns the LED on or off according to the 'led' param in the POST data httpdEndHeaders(connData);
int ICACHE_FLASH_ATTR cgiLed(HttpdConnData *connData) {
int len;
char buff[1024];
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
}
len=httpdFindArg(connData->post->buff, "led", buff, sizeof(buff));
if (len!=0) {
currLedState=atoi(buff);
//ioLed(currLedState);
}
httpdRedirect(connData, "led.tpl");
return HTTPD_CGI_DONE;
}
//Template code for the led page.
int ICACHE_FLASH_ATTR tplLed(HttpdConnData *connData, char *token, void **arg) {
char buff[512];
if (token==NULL) return HTTPD_CGI_DONE;
os_strcpy(buff, "Unknown");
if (os_strcmp(token, "ledstate")==0) {
if (currLedState) {
os_strcpy(buff, "on");
} else {
os_strcpy(buff, "off");
}
} else if (os_strcmp(token, "topnav")==0) {
printNav(buff);
}
httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE;
}
static long hitCounter=0;
//Template code for the counter on the index page.
int ICACHE_FLASH_ATTR tplCounter(HttpdConnData *connData, char *token, void **arg) {
char buff[64];
if (token==NULL) return HTTPD_CGI_DONE;
if (printSysInfo(buff, token) > 0) {
// awesome...
} else if (os_strcmp(token, "head")==0) {
printHead(connData);
buff[0] = 0;
} else if (os_strcmp(token, "version")==0) {
# define VERS_STR_STR(V) #V
# define VERS_STR(V) VERS_STR_STR(V)
os_sprintf(buff, "%s", VERS_STR(VERSION));
} else if (os_strcmp(token, "counter")==0) {
hitCounter++;
os_sprintf(buff, "%ld", hitCounter);
}
httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE;
}
static char *navLinks[][2] = {
{ "Home", "/index.tpl" }, { "Wifi", "/wifi/wifi.tpl" }, { "\xC2\xB5""C Console", "/console.tpl" },
{ "Debug log", "/log.tpl" }, { "Help", "/help.tpl" },
{ 0, 0 },
};
// Print the navigation links into the buffer and return the length of what got added
int ICACHE_FLASH_ATTR printNav(char *buff) {
int len = 0;
for (uint8_t i=0; navLinks[i][0] != NULL; i++) {
//os_printf("nav %d: %s -> %s\n", i, navLinks[i][0], navLinks[i][1]);
len += os_sprintf(buff+len,
" <li class=\"pure-menu-item\"><a href=\"%s\" class=\"pure-menu-link\">%s</a></li>",
navLinks[i][1], navLinks[i][0]);
}
len += os_sprintf(buff+len, " <li class=\"pure-menu-item\">%dKB</li>",
system_get_free_heap_size()/1024);
//os_printf("nav(%d): %s\n", len, buff);
return len;
}
void ICACHE_FLASH_ATTR printHead(HttpdConnData *connData) {
char buff[1024];
struct EspFsFile *file = espFsOpen("/head.tpl");
if (file == NULL) {
os_printf("Header file 'head.tpl' not found\n");
return;
}
int len = espFsRead(file, buff, 1024);
if (len == 1024) {
os_printf("Header file 'head.tpl' too large!\n");
buff[1023] = 0;
} else {
buff[len] = 0; // ensure null termination
}
if (len > 0) {
char *p = os_strstr(buff, "%topnav%");
if (p != NULL) {
char navBuf[512];
int n = p - buff;
httpdSend(connData, buff, n);
printNav(navBuf);
httpdSend(connData, navBuf, -1);
httpdSend(connData, buff+n+8, len-n-8);
} else {
httpdSend(connData, buff, len);
}
}
espFsClose(file);
} }
#define TOKEN(x) (os_strcmp(token, x) == 0) #define TOKEN(x) (os_strcmp(token, x) == 0)
#if 0
// Handle system information variables and print their value, returns the number of // Handle system information variables and print their value, returns the number of
// characters appended to buff // characters appended to buff
int ICACHE_FLASH_ATTR printSysInfo(char *buff, char *token) { int ICACHE_FLASH_ATTR printGlobalInfo(char *buff, int buflen, char *token) {
if (TOKEN("si_chip_id")) { if (TOKEN("si_chip_id")) {
return os_sprintf(buff, "0x%x", system_get_chip_id()); return os_sprintf(buff, "0x%x", system_get_chip_id());
} else if (TOKEN("si_freeheap")) { } else if (TOKEN("si_freeheap")) {
@ -162,4 +48,16 @@ int ICACHE_FLASH_ATTR printSysInfo(char *buff, char *token) {
return 0; return 0;
} }
} }
#endif
void ICACHE_FLASH_ATTR printGlobalJSON(HttpdConnData *connData) {
httpdSend(connData,
"<script type=\"text/javascript\">\n"
"menu = [\"Home\", \"/home.tpl\", \"Wifi\", \"/wifi/wifi.tpl\","
"\"\xC2\xB5" "C Console\", \"/console.tpl\", \"Debug log\", \"/log.tpl\", "
"\"Help\", \"/help.tpl\" ];\n", -1);
# define VERS_STR_STR(V) #V
# define VERS_STR(V) VERS_STR_STR(V)
httpdSend(connData, "version = \"" VERS_STR(VERSION) "\";\n", -1);
httpdSend(connData, "</script>\n", -1);
}

@ -3,11 +3,7 @@
#include "httpd.h" #include "httpd.h"
int cgiLed(HttpdConnData *connData); void ICACHE_FLASH_ATTR jsonHeader(HttpdConnData *connData, int code);
int tplLed(HttpdConnData *connData, char *token, void **arg); void ICACHE_FLASH_ATTR printGlobalJSON(HttpdConnData *connData);
int tplCounter(HttpdConnData *connData, char *token, void **arg);
int printNav(char *buff);
void ICACHE_FLASH_ATTR printHead(HttpdConnData *connData);
int ICACHE_FLASH_ATTR printSysInfo(char *buff, char *token);
#endif #endif

@ -21,6 +21,8 @@ Cgi/template routines for the /wifi url.
//Enable this to disallow any changes in AP settings //Enable this to disallow any changes in AP settings
//#define DEMO_MODE //#define DEMO_MODE
#define SLEEP_MODE LIGHT_SLEEP_T
// ===== wifi status change callback // ===== wifi status change callback
uint8_t wifiState = wifiIsDisconnected; uint8_t wifiState = wifiIsDisconnected;
@ -35,6 +37,7 @@ static char *wifiReasons[] = {
"beacon_timeout", "no_ap_found" }; "beacon_timeout", "no_ap_found" };
static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" };
static char *wifiPhy[] = { 0, "11b", "11g", "11n" };
static char* ICACHE_FLASH_ATTR wifiGetReason(void) { static char* ICACHE_FLASH_ATTR wifiGetReason(void) {
if (wifiReason <= 24) return wifiReasons[wifiReason]; if (wifiReason <= 24) return wifiReasons[wifiReason];
@ -89,7 +92,7 @@ static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) {
//WiFi access point data //WiFi access point data
typedef struct { typedef struct {
char ssid[32]; char ssid[32];
char rssi; sint8 rssi;
char enc; char enc;
} ApData; } ApData;
@ -175,7 +178,7 @@ int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) {
if (!cgiWifiAps.scanInProgress && pos!=0) { if (!cgiWifiAps.scanInProgress && pos!=0) {
//Fill in json code for an access point //Fill in json code for an access point
if (pos-1<cgiWifiAps.noAps) { if (pos-1<cgiWifiAps.noAps) {
len=os_sprintf(buff, "{\"essid\": \"%s\", \"rssi\": \"%d\", \"enc\": \"%d\"}%s\n", len=os_sprintf(buff, "{\"essid\": \"%s\", \"rssi\": %d, \"enc\": \"%d\"}%s\n",
cgiWifiAps.apData[pos-1]->ssid, cgiWifiAps.apData[pos-1]->rssi, cgiWifiAps.apData[pos-1]->ssid, cgiWifiAps.apData[pos-1]->rssi,
cgiWifiAps.apData[pos-1]->enc, (pos-1==cgiWifiAps.noAps-1)?"":","); cgiWifiAps.apData[pos-1]->enc, (pos-1==cgiWifiAps.noAps-1)?"":",");
httpdSend(connData, buff, len); httpdSend(connData, buff, len);
@ -214,6 +217,14 @@ int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) {
// ===== timers to change state and rescue from failed associations // ===== timers to change state and rescue from failed associations
#if 0
static ETSTimer deepTimer;
static void ICACHE_FLASH_ATTR deepSleepCb(void *arg) {
system_deep_sleep_set_option(2);
system_deep_sleep(100*1000);
}
#endif
//#define CONNTRY_IDLE 0 //#define CONNTRY_IDLE 0
//#define CONNTRY_WORKING 1 //#define CONNTRY_WORKING 1
//#define CONNTRY_SUCCESS 2 //#define CONNTRY_SUCCESS 2
@ -237,7 +248,11 @@ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
// We're happily connected, go to STA mode // We're happily connected, go to STA mode
os_printf("Wifi got IP. Going into STA mode..\n"); os_printf("Wifi got IP. Going into STA mode..\n");
wifi_set_opmode(1); wifi_set_opmode(1);
wifi_set_sleep_type(SLEEP_MODE);
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
//os_timer_disarm(&deepTimer);
//os_timer_setfn(&deepTimer, deepSleepCb, NULL);
//os_timer_arm(&deepTimer, 1000, 1);
} }
log_uart(false); log_uart(false);
// no more resetTimer at this point, gotta use physical reset to recover if in trouble // no more resetTimer at this point, gotta use physical reset to recover if in trouble
@ -269,8 +284,8 @@ static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) {
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
} }
//This cgi uses the routines above to connect to a specific access point with the // This cgi uses the routines above to connect to a specific access point with the
//given ESSID using the given password. // given ESSID using the given password.
int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) {
char essid[128]; char essid[128];
char passwd[128]; char passwd[128];
@ -280,23 +295,25 @@ int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) {
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
httpdFindArg(connData->post->buff, "essid", essid, sizeof(essid)); int el = httpdFindArg(connData->post->buff, "essid", essid, sizeof(essid));
httpdFindArg(connData->post->buff, "passwd", passwd, sizeof(passwd)); int pl = httpdFindArg(connData->post->buff, "passwd", passwd, sizeof(passwd));
os_strncpy((char*)stconf.ssid, essid, 32); if (el > 0 && pl > 0) {
os_strncpy((char*)stconf.password, passwd, 64); //Set to 0 if you want to disable the actual reconnecting bit
os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd); #ifndef DEMO_MODE
os_strncpy((char*)stconf.ssid, essid, 32);
//Schedule disconnect/connect os_strncpy((char*)stconf.password, passwd, 64);
os_timer_disarm(&reassTimer); os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd);
os_timer_setfn(&reassTimer, reassTimerCb, NULL);
//Set to 0 if you want to disable the actual reconnecting bit //Schedule disconnect/connect
#ifdef DEMO_MODE os_timer_disarm(&reassTimer);
httpdRedirect(connData, "/wifi"); os_timer_setfn(&reassTimer, reassTimerCb, NULL);
#else os_timer_arm(&reassTimer, 1000, 0);
os_timer_arm(&reassTimer, 1000, 0);
httpdRedirect(connData, "connecting.html");
#endif #endif
jsonHeader(connData, 200);
} else {
jsonHeader(connData, 400);
}
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
@ -315,16 +332,19 @@ int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) {
int m = atoi(buff); int m = atoi(buff);
os_printf("Wifi switching to mode %d\n", m); os_printf("Wifi switching to mode %d\n", m);
#ifndef DEMO_MODE #ifndef DEMO_MODE
wifi_set_opmode(m); wifi_set_opmode(m&3);
if (m == 1) { if (m == 1) {
wifi_set_sleep_type(SLEEP_MODE);
// STA-only mode, reset into STA+AP after a timeout // STA-only mode, reset into STA+AP after a timeout
os_timer_disarm(&resetTimer); os_timer_disarm(&resetTimer);
os_timer_setfn(&resetTimer, resetTimerCb, NULL); os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
} }
jsonHeader(connData, 200);
#endif #endif
} else {
jsonHeader(connData, 400);
} }
httpdRedirect(connData, "/wifi");
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
@ -336,9 +356,8 @@ int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
int len; int len;
struct ip_info info; struct ip_info info;
int st=wifi_station_get_connect_status(); int st=wifi_station_get_connect_status();
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/json"); jsonHeader(connData, 200);
httpdEndHeaders(connData);
len = os_sprintf(buff, "{\"status\": \"%s\"", len = os_sprintf(buff, "{\"status\": \"%s\"",
st > 0 && st < sizeof(connStatuses) ? connStatuses[st] : "unknown"); st > 0 && st < sizeof(connStatuses) ? connStatuses[st] : "unknown");
@ -368,49 +387,42 @@ int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
//Template code for the WLAN page. static char *wifiWarn[] = { 0,
int ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) { "Switch to <a href=\\\"setmode?mode=3\\\">STA+AP mode</a>",
"<b>Can't scan in this mode!</b> Switch to <a href=\\\"setmode?mode=3\\\">STA+AP mode</a>",
"Switch to <a href=\\\"setmode?mode=1\\\">STA mode</a>",
};
// Cgi to return various Wifi information
int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) {
char buff[1024]; char buff[1024];
int x; int len;
static struct station_config stconf;
if (token==NULL) return HTTPD_CGI_DONE;
wifi_station_get_config(&stconf);
os_strcpy(buff, "Unknown"); if (connData->conn==NULL) {
if (os_strcmp(token, "WiFiMode")==0) { return HTTPD_CGI_DONE; // Connection aborted
x = wifi_get_opmode() & 0x3;
os_strcpy(buff, wifiMode[x]);
} else if (os_strcmp(token, "currSsid")==0) {
os_strcpy(buff, (char*)stconf.ssid);
} else if (os_strcmp(token, "currStatus")==0) {
int st=wifi_station_get_connect_status();
if (st > 0 && st < sizeof(connStatuses))
os_strcpy(buff, connStatuses[st]);
else
os_strcpy(buff, "unknown");
} else if (os_strcmp(token, "currPhy")==0) {
int m = wifi_get_phy_mode();
os_sprintf(buff, "%d", m);
} else if (os_strcmp(token, "WiFiPasswd")==0) {
os_strcpy(buff, (char*)stconf.password);
} else if (os_strcmp(token, "WiFiapwarn")==0) {
x=wifi_get_opmode();
switch (x) {
case 1:
os_strcpy(buff, "Click <a href=\"setmode.cgi?mode=3\">here</a> to go to STA+AP mode.");
break;
case 2:
os_strcpy(buff, "<b>Can't scan in this mode.</b> Click <a href=\"setmode.cgi?mode=3\">here</a> to go to STA+AP mode.");
break;
case 3:
os_strcpy(buff, "Click <a href=\"setmode.cgi?mode=1\">here</a> to go to STA mode.");
break;
}
} else if (os_strcmp(token, "head")==0) {
printHead(connData);
buff[0] = 0;
} }
httpdSend(connData, buff, -1);
struct station_config stconf;
wifi_station_get_config(&stconf);
uint8_t op = wifi_get_opmode() & 0x3;
char *mode = wifiMode[op];
char *status = "unknown";
int st = wifi_station_get_connect_status();
if (st > 0 && st < sizeof(connStatuses)) status = connStatuses[st];
int p = wifi_get_phy_mode();
char *phy = wifiPhy[p&3];
char *warn = wifiWarn[op];
sint8 rssi = wifi_station_get_rssi();
if (rssi > 0) rssi = 0;
len = os_sprintf(buff,
"{\"mode\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", "
"\"rssi\": \"%ddB\", \"warn\": \"%s\", \"passwd\": \"%s\"}",
mode, (char*)stconf.ssid, status, phy, rssi, warn, (char*)stconf.password);
jsonHeader(connData, 200);
httpdSend(connData, buff, len);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }

@ -6,7 +6,7 @@
enum { wifiIsDisconnected, wifiIsConnected, wifiGotIP }; enum { wifiIsDisconnected, wifiIsConnected, wifiGotIP };
int cgiWiFiScan(HttpdConnData *connData); int cgiWiFiScan(HttpdConnData *connData);
int tplWlan(HttpdConnData *connData, char *token, void **arg); int cgiWifiInfo(HttpdConnData *connData);
int cgiWiFi(HttpdConnData *connData); int cgiWiFi(HttpdConnData *connData);
int cgiWiFiConnect(HttpdConnData *connData); int cgiWiFiConnect(HttpdConnData *connData);
int cgiWiFiSetMode(HttpdConnData *connData); int cgiWiFiSetMode(HttpdConnData *connData);

@ -11,6 +11,7 @@
#define BUF_MAX (1024) #define BUF_MAX (1024)
static char log_buf[BUF_MAX]; static char log_buf[BUF_MAX];
static int log_wr, log_rd; static int log_wr, log_rd;
static int log_pos;
static bool log_no_uart; // start out printing to uart static bool log_no_uart; // start out printing to uart
static bool log_newline; // at start of a new line static bool log_newline; // at start of a new line
@ -30,8 +31,10 @@ static void ICACHE_FLASH_ATTR
log_write(char c) { log_write(char c) {
log_buf[log_wr] = c; log_buf[log_wr] = c;
log_wr = (log_wr+1) % BUF_MAX; log_wr = (log_wr+1) % BUF_MAX;
if (log_wr == log_rd) if (log_wr == log_rd) {
log_rd = (log_rd+1) % BUF_MAX; // full, eat first char log_rd = (log_rd+1) % BUF_MAX; // full, eat first char
log_pos++;
}
} }
#if 0 #if 0
@ -63,25 +66,47 @@ log_write_char(char c) {
log_write(c); log_write(c);
} }
//===== Display a web page with the log
int ICACHE_FLASH_ATTR int ICACHE_FLASH_ATTR
tplLog(HttpdConnData *connData, char *token, void **arg) { ajaxLog(HttpdConnData *connData) {
if (token==NULL) return HTTPD_CGI_DONE; char buff[2048];
int len; // length of text in buff
int log_len = (log_wr+BUF_MAX-log_rd) % BUF_MAX; // num chars in log_buf
int start = 0; // offset onto log_wr to start sending out chars
jsonHeader(connData, 200);
// figure out where to start in buffer based on URI param
len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff));
if (len > 0) {
start = atoi(buff);
if (start < log_pos) {
start = 0;
} else if (start >= log_pos+log_len) {
start = log_len;
} else {
start = start - log_pos;
}
}
// start outputting
len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"",
log_len-start, log_pos+start);
if (os_strcmp(token, "log") == 0) { int rd = (log_rd+start) % BUF_MAX;
if (log_wr > log_rd) { while (len < 2040 && rd != log_wr) {
httpdSend(connData, log_buf+log_rd, log_wr-log_rd); uint8_t c = log_buf[rd];
} else if (log_rd != log_wr) { if (c == '\\' || c == '"') {
httpdSend(connData, log_buf+log_rd, BUF_MAX-log_rd); buff[len++] = '\\';
httpdSend(connData, log_buf, log_wr); buff[len++] = c;
} else if (c < ' ') {
len += os_sprintf(buff+len, "\\u%04x", c);
} else { } else {
httpdSend(connData, "<buffer empty>", -1); buff[len++] = c;
} }
} else if (os_strcmp(token, "head")==0) { rd = (rd + 1) % BUF_MAX;
printHead(connData);
} else {
httpdSend(connData, "Unknown\n", -1);
} }
os_strcpy(buff+len, "\"}"); len+=2;
httpdSend(connData, buff, len);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }

@ -5,6 +5,6 @@
void logInit(void); void logInit(void);
void ICACHE_FLASH_ATTR log_uart(bool enable); void ICACHE_FLASH_ATTR log_uart(bool enable);
int tplLog(HttpdConnData *connData, char *token, void **arg); int ICACHE_FLASH_ATTR ajaxLog(HttpdConnData *connData);
#endif #endif

@ -59,18 +59,19 @@ general ones. Authorization things (like authBasic) act as a 'barrier' and
should be placed above the URLs they protect. should be placed above the URLs they protect.
*/ */
HttpdBuiltInUrl builtInUrls[]={ HttpdBuiltInUrl builtInUrls[]={
{"/", cgiRedirect, "/index.tpl"}, {"/", cgiRedirect, "/home.tpl"},
//{"/flash/read", cgiReadFlash, NULL},
{"/flash/next", cgiGetFirmwareNext, NULL}, {"/flash/next", cgiGetFirmwareNext, NULL},
{"/flash/upload", cgiUploadFirmware, NULL}, {"/flash/upload", cgiUploadFirmware, NULL},
{"/flash/reboot", cgiRebootFirmware, NULL}, {"/flash/reboot", cgiRebootFirmware, NULL},
{"/index.tpl", cgiEspFsTemplate, tplCounter}, {"/home.tpl", cgiEspFsHtml, NULL},
{"/help.tpl", cgiEspFsTemplate, tplCounter}, //{"/help.tpl", cgiEspFsTemplate, tplCounter},
{"/log.tpl", cgiEspFsTemplate, tplLog}, {"/log.tpl", cgiEspFsHtml, NULL},
{"/console.tpl", cgiEspFsTemplate, tplConsole}, {"/log/text", ajaxLog, NULL},
{"/console.tpl", cgiEspFsHtml, NULL},
{"/console/reset", ajaxConsoleReset, NULL}, {"/console/reset", ajaxConsoleReset, NULL},
{"/console/baud", ajaxConsoleBaud, NULL}, {"/console/baud", ajaxConsoleBaud, NULL},
{"/console/text", ajaxConsole, NULL}, {"/console/text", ajaxConsole, NULL},
{"/help.tpl", cgiEspFsHtml, NULL},
//Routines to make the /wifi URL and everything beneath it work. //Routines to make the /wifi URL and everything beneath it work.
@ -79,11 +80,12 @@ HttpdBuiltInUrl builtInUrls[]={
{"/wifi", cgiRedirect, "/wifi/wifi.tpl"}, {"/wifi", cgiRedirect, "/wifi/wifi.tpl"},
{"/wifi/", cgiRedirect, "/wifi/wifi.tpl"}, {"/wifi/", cgiRedirect, "/wifi/wifi.tpl"},
{"/wifi/wifiscan.cgi", cgiWiFiScan, NULL}, {"/wifi/wifi.tpl", cgiEspFsHtml, NULL},
{"/wifi/wifi.tpl", cgiEspFsTemplate, tplWlan}, {"/wifi/info", cgiWifiInfo, NULL},
{"/wifi/connect.cgi", cgiWiFiConnect, NULL}, {"/wifi/wifiscan", cgiWiFiScan, NULL},
{"/wifi/connstatus.cgi", cgiWiFiConnStatus, NULL}, {"/wifi/connect", cgiWiFiConnect, NULL},
{"/wifi/setmode.cgi", cgiWiFiSetMode, NULL}, {"/wifi/connstatus", cgiWiFiConnStatus, NULL},
{"/wifi/setmode", cgiWiFiSetMode, NULL},
{"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem {"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem
{NULL, NULL, NULL} {NULL, NULL, NULL}

Loading…
Cancel
Save