mirror of https://github.com/jeelabs/esp-link.git
commit
483800fd8d
@ -1,46 +0,0 @@ |
|||||||
<div id="main"> |
|
||||||
<div class="header"> |
|
||||||
<h1>Microcontroller Console</h1> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="content"> |
|
||||||
<p>The Microcontroller console shows the last 1024 characters |
|
||||||
received from UART0, to which a microcontroller is typically attached. |
|
||||||
The UART is configured for 8 bits, no parity, 1 stop bit (8N1).</p> |
|
||||||
<p> |
|
||||||
<a id="reset-button" class="pure-button button-primary" href="#">Reset µC</a> |
|
||||||
Baud: |
|
||||||
<span id="baud-btns"></span> |
|
||||||
</p> |
|
||||||
<pre class="console" id="console"></pre> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<script type="text/javascript">console_url = "/console/text"</script> |
|
||||||
<script src="console.js"></script> |
|
||||||
<script type="text/javascript"> |
|
||||||
var rates = [57600, 115200, 230400, 460800]; |
|
||||||
|
|
||||||
onLoad(function() { |
|
||||||
fetchText(100, true); |
|
||||||
|
|
||||||
$("#reset-button").addEventListener("click", function(e) { |
|
||||||
e.preventDefault(); |
|
||||||
var co = $("#console"); |
|
||||||
co.innerHTML = ""; |
|
||||||
ajaxSpin('POST', "/console/reset", |
|
||||||
function(resp) { showNotification("uC reset"); co.textEnd = 0; }, |
|
||||||
function(s, st) { showWarning("Error resetting uC"); } |
|
||||||
); |
|
||||||
}); |
|
||||||
|
|
||||||
rates.forEach(function(r) { baudButton(r); }); |
|
||||||
|
|
||||||
ajaxJson('GET', "/console/baud", |
|
||||||
function(data) { showRate(data.rate); }, |
|
||||||
function(s, st) { showNotification(st); } |
|
||||||
); |
|
||||||
}); |
|
||||||
</script> |
|
||||||
</body></html> |
|
@ -1,72 +0,0 @@ |
|||||||
function fetchText(delay, repeat) { |
|
||||||
var el = $("#console"); |
|
||||||
if (el.textEnd == undefined) { |
|
||||||
el.textEnd = 0; |
|
||||||
el.innerHTML = ""; |
|
||||||
} |
|
||||||
window.setTimeout(function() { |
|
||||||
ajaxJson('GET', console_url + "?start=" + el.textEnd, |
|
||||||
function(resp) { |
|
||||||
var dly = updateText(resp); |
|
||||||
if (repeat) fetchText(dly, repeat); |
|
||||||
}, |
|
||||||
function() { retryLoad(repeat); }); |
|
||||||
}, delay); |
|
||||||
} |
|
||||||
|
|
||||||
function updateText(resp) { |
|
||||||
var el = $("#console"); |
|
||||||
|
|
||||||
var 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; |
|
||||||
} |
|
||||||
return delay; |
|
||||||
} |
|
||||||
|
|
||||||
function retryLoad(repeat) { |
|
||||||
fetchText(1000, repeat); |
|
||||||
} |
|
||||||
|
|
||||||
//===== Console page
|
|
||||||
|
|
||||||
function showRate(rate) { |
|
||||||
rates.forEach(function(r) { |
|
||||||
var el = $("#"+r+"-button"); |
|
||||||
el.className = el.className.replace(" button-selected", ""); |
|
||||||
}); |
|
||||||
|
|
||||||
var el = $("#"+rate+"-button"); |
|
||||||
if (el != null) el.className += " button-selected"; |
|
||||||
} |
|
||||||
|
|
||||||
function baudButton(baud) { |
|
||||||
$("#baud-btns").appendChild(m( |
|
||||||
' <a id="'+baud+'-button" href="#" class="pure-button">'+baud+'</a>')); |
|
||||||
|
|
||||||
$("#"+baud+"-button").addEventListener("click", function(e) { |
|
||||||
e.preventDefault(); |
|
||||||
ajaxSpin('POST', "/console/baud?rate="+baud, |
|
||||||
function(resp) { showNotification("" + baud + " baud set"); showRate(baud); }, |
|
||||||
function(s, st) { showWarning("Error setting baud rate: " + st); } |
|
||||||
); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
//===== Log page
|
|
||||||
|
|
||||||
function showDbgMode(mode) { |
|
||||||
var btns = $('.dbg-btn'); |
|
||||||
for (var i=0; i < btns.length; i++) { |
|
||||||
if (btns[i].id === "dbg-"+mode) |
|
||||||
addClass(btns[i], "button-selected"); |
|
||||||
else |
|
||||||
removeClass(btns[i], "button-selected"); |
|
||||||
} |
|
||||||
} |
|
Before Width: | Height: | Size: 874 B |
@ -1,10 +0,0 @@ |
|||||||
<!doctype html> |
|
||||||
<html><head> |
|
||||||
<title>esp-link</title> |
|
||||||
<link rel="stylesheet" href="/pure.css"> |
|
||||||
<link rel="stylesheet" href="/style.css"> |
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|
||||||
<script src="/ui.js"></script> |
|
||||||
</head> |
|
||||||
<body> |
|
||||||
<div id="layout"> |
|
@ -1,87 +0,0 @@ |
|||||||
<div id="main"> |
|
||||||
<div class="header"> |
|
||||||
<div><img src="favicon.ico" height="64"><span class="jl">JEELABS</span></div> |
|
||||||
<h1 style="margin-top:0"><span class="esp">esp</span>-link</h1> |
|
||||||
<h2 id="version"></h2> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="content"> |
|
||||||
<div class="pure-g"> |
|
||||||
<div class="pure-u-1"><div class="card"> |
|
||||||
<p>The JeeLabs esp-link firmware bridges the ESP8266 serial port to Wifi and can |
|
||||||
program microcontrollers over the serial port, in particular Arduinos, AVRs, and |
|
||||||
NXP's LPC800 and other ARM processors.</p> |
|
||||||
<p style="margin-bottom:0;">Program an Arduino/AVR using avrdude using a command |
|
||||||
line similar to:</p> |
|
||||||
<div class="tt">/home/arduino-1.0.5/hardware/tools/avrdude \<br> |
|
||||||
-DV -patmega328p -Pnet:esp-link.local:23 -carduino -b115200 -U \<br> |
|
||||||
-C /home/arduino-1.0.5/hardware/tools/avrdude.conf flash:w:my_sketch.hex:i |
|
||||||
</div> |
|
||||||
<p>where <tt>-Pnet:esp-link.local:23</tt> tells avrdude to connect to port 23 of esp-link. |
|
||||||
You can substitute the IP address of your esp-link for esp-link.local if necessary.</p> |
|
||||||
<p>Please refer to |
|
||||||
<a href="https://github.com/jeelabs/esp-link/blob/master/README.md">the online README</a> |
|
||||||
for up-to-date help and to the forthcoming |
|
||||||
<a href="http://jeelabs.org">JeeLabs blog</a> for an intro to the codebase.</p> |
|
||||||
</div></div> |
|
||||||
</div> |
|
||||||
<div class="pure-g"> |
|
||||||
<div class="pure-u-1 pure-u-md-1-2"> |
|
||||||
<div class="card"> |
|
||||||
<h1>Wifi summary</h1> |
|
||||||
<div id="wifi-spinner" class="spinner spinner-small"></div> |
|
||||||
<table id="wifi-table" class="pure-table pure-table-horizontal" hidden><tbody> |
|
||||||
<tr><td>WiFi mode</td><td id="wifi-mode"></td></tr> |
|
||||||
<tr><td>Configured network</td><td id="wifi-ssid"></td></tr> |
|
||||||
<tr><td>Wifi channel</td><td id="wifi-chan"></td></tr> |
|
||||||
<tr><td>Wifi status</td><td id="wifi-status"></td></tr> |
|
||||||
<tr><td>Wifi address</td><td id="wifi-ip"></td></tr> |
|
||||||
<tr><td>Configured hostname</td><td id="wifi-hostname"></td></tr> |
|
||||||
</tbody> </table> |
|
||||||
</div> |
|
||||||
<div class="card"> |
|
||||||
<h1>TCP client</h1> |
|
||||||
<form action="#" id="tcpform" class="pure-form"> |
|
||||||
<legend>TCP client support in esp-link</legend> |
|
||||||
<div class="form-horizontal"> |
|
||||||
<input type="checkbox" name="tcp_enable"/> |
|
||||||
<label>Enable serial port TCP client</label> |
|
||||||
</div> |
|
||||||
<br> |
|
||||||
<legend>Grovestreams data push</legend> |
|
||||||
<div class="form-horizontal"> |
|
||||||
<input type="checkbox" name="rssi_enable"/> |
|
||||||
<label>Send RSSI</label> |
|
||||||
</div> |
|
||||||
<div class="pure-form-stacked"> |
|
||||||
<label>API key/passwd</label> |
|
||||||
<input type="password" name="api_key"/> |
|
||||||
</div> |
|
||||||
<button id="tcp-button" type="submit" |
|
||||||
class="pure-button button-primary">Change!</button> |
|
||||||
</form> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div class="pure-u-1 pure-u-md-1-2"><div class="card"> |
|
||||||
<h1>Pin assignment</h1> |
|
||||||
<legend>Select one of the following signal/pin assignments to match your hardware</legend> |
|
||||||
<fieldset class='radios' id='pin-mux'> |
|
||||||
<div class="spinner spinner-small"></div> |
|
||||||
</fieldset> |
|
||||||
</div></div> |
|
||||||
</div> |
|
||||||
<div class="pure-g"> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<script type="text/javascript"> |
|
||||||
onLoad(function() { |
|
||||||
fetchPins(); |
|
||||||
getWifiInfo(); |
|
||||||
fetchTcpClient(); |
|
||||||
bnd($("#tcpform"), "submit", changeTcpClient); |
|
||||||
}); |
|
||||||
</script> |
|
||||||
</body></html> |
|
Before Width: | Height: | Size: 4.0 KiB |
@ -1,48 +0,0 @@ |
|||||||
<div id="main"> |
|
||||||
<div class="header"> |
|
||||||
<h1>Debug Log</h1> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="content"> |
|
||||||
<p>The debug log shows the most recent characters printed by the esp-link software itself to |
|
||||||
its own debug log.</p> |
|
||||||
<div class="pure-g"> |
|
||||||
<p class="pure-u-1-4"> |
|
||||||
<a id="refresh-button" class="pure-button button-primary" href="#">Refresh</a> |
|
||||||
</p> |
|
||||||
<p class="pure-u-3-4" style="vertical-align: baseline"> |
|
||||||
UART debug log: |
|
||||||
<a id="dbg-auto" class="dbg-btn pure-button" href="#">auto</a> |
|
||||||
<a id="dbg-off" class="dbg-btn pure-button" href="#">off</a> |
|
||||||
<a id="dbg-on" class="dbg-btn pure-button" href="#">on</a> |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
<pre id="console" class="console" style="margin-top: 0px;"></pre> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<script type="text/javascript">console_url = "/log/text"</script> |
|
||||||
<script src="console.js"></script> |
|
||||||
<script type="text/javascript"> |
|
||||||
onLoad(function() { |
|
||||||
fetchText(100, false); |
|
||||||
|
|
||||||
$("#refresh-button").addEventListener("click", function(e) { |
|
||||||
e.preventDefault(); |
|
||||||
fetchText(100, false); |
|
||||||
}); |
|
||||||
|
|
||||||
["auto", "off", "on"].forEach(function(mode) { |
|
||||||
bnd($('#dbg-'+mode), "click", function(el) { |
|
||||||
ajaxJsonSpin('POST', "/log/dbg?mode="+mode, |
|
||||||
function(data) { showNotification("UART mode " + data.mode); showDbgMode(data.mode); }, |
|
||||||
function(s, st) { showWarning("Error setting UART mode: " + st); } |
|
||||||
); |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
ajaxJson('GET', "/log/dbg", function(data) { showDbgMode(data.mode); }, function() {}); |
|
||||||
}); |
|
||||||
</script> |
|
||||||
</body></html> |
|
File diff suppressed because it is too large
Load Diff
@ -1,380 +0,0 @@ |
|||||||
/* All fonts */ |
|
||||||
html, button, input, select, textarea, .pure-g [class *= "pure-u"] { |
|
||||||
font-family: sans-serif; |
|
||||||
} |
|
||||||
|
|
||||||
body { |
|
||||||
color: #777; |
|
||||||
} |
|
||||||
a:visited, a:link { |
|
||||||
color: #009; |
|
||||||
} |
|
||||||
a:hover { |
|
||||||
color: #00c; |
|
||||||
} |
|
||||||
|
|
||||||
.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; |
|
||||||
} |
|
||||||
fieldset.radios { |
|
||||||
border: none; |
|
||||||
padding-left: 0px; |
|
||||||
} |
|
||||||
fieldset fields { |
|
||||||
clear: both; |
|
||||||
} |
|
||||||
#pin-mux input { |
|
||||||
display: block; |
|
||||||
margin-top: 0.4em; |
|
||||||
float: left; |
|
||||||
} |
|
||||||
#pin-mux label { |
|
||||||
display: block; |
|
||||||
margin: 0em 0.2em 0em 1em; |
|
||||||
width: 90%; |
|
||||||
} |
|
||||||
|
|
||||||
.pure-table td, .pure-table th { |
|
||||||
padding: 0.5em 0.5em; |
|
||||||
} |
|
||||||
|
|
||||||
/* make images size-up */ |
|
||||||
.xx-pure-img-responsive { |
|
||||||
max-width: 100%; |
|
||||||
height: auto; |
|
||||||
} |
|
||||||
|
|
||||||
/* Add transition to containers so they can push in and out */ |
|
||||||
#layout, #menu, .menu-link { |
|
||||||
-webkit-transition: all 0.2s ease-out; |
|
||||||
-moz-transition: all 0.2s ease-out; |
|
||||||
-ms-transition: all 0.2s ease-out; |
|
||||||
-o-transition: all 0.2s ease-out; |
|
||||||
transition: all 0.2s ease-out; |
|
||||||
} |
|
||||||
|
|
||||||
/* This is the parent `<div>` that contains the menu and the content area */ |
|
||||||
#layout { |
|
||||||
position: relative; |
|
||||||
padding-left: 0; |
|
||||||
} |
|
||||||
#layout.active #menu { |
|
||||||
left: 150px; |
|
||||||
width: 150px; |
|
||||||
} |
|
||||||
|
|
||||||
#layout.active .menu-link { |
|
||||||
left: 150px; |
|
||||||
} |
|
||||||
|
|
||||||
div.tt { |
|
||||||
font-family: monospace; |
|
||||||
font-size: 120%; |
|
||||||
color: #390; |
|
||||||
background-color: #ddd; |
|
||||||
padding: 2px; |
|
||||||
margin: 2px 0; |
|
||||||
line-height: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
/* The content `<div>` */ |
|
||||||
.content { |
|
||||||
margin: 0 auto; |
|
||||||
padding: 0 2em; |
|
||||||
max-width: 800px; |
|
||||||
margin-bottom: 50px; |
|
||||||
line-height: 1.6em; |
|
||||||
} |
|
||||||
|
|
||||||
.header { |
|
||||||
margin: 0; |
|
||||||
color: #333; |
|
||||||
text-align: center; |
|
||||||
padding: 2.5em 2em 0; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
background-color: #fc0; |
|
||||||
} |
|
||||||
.header h1 { |
|
||||||
margin: 0.2em 0; |
|
||||||
font-size: 3em; |
|
||||||
font-weight: 300; |
|
||||||
} |
|
||||||
.header h1 .esp { |
|
||||||
font-size: 1.25em; |
|
||||||
} |
|
||||||
.jl { |
|
||||||
font: normal 800 1.5em sans-serif; |
|
||||||
position: relative; |
|
||||||
bottom: 19px; |
|
||||||
color: #9d1414; |
|
||||||
margin-left: 3px; |
|
||||||
} |
|
||||||
|
|
||||||
.content-subhead { |
|
||||||
margin: 50px 0 20px 0; |
|
||||||
font-weight: 300; |
|
||||||
color: #888; |
|
||||||
} |
|
||||||
|
|
||||||
form button { |
|
||||||
margin-top: 0.5em; |
|
||||||
} |
|
||||||
.button-primary { |
|
||||||
background-color: #99f; |
|
||||||
} |
|
||||||
.button-selected { |
|
||||||
background-color: #fc6; |
|
||||||
} |
|
||||||
|
|
||||||
/* Text console */ |
|
||||||
pre.console { |
|
||||||
background-color: #663300; |
|
||||||
-moz-border-radius: 5px; |
|
||||||
-webkit-border-radius: 5px; |
|
||||||
border-radius: 5px; |
|
||||||
border: 0px solid #000000; |
|
||||||
color: #66ff66; |
|
||||||
padding: 5px; |
|
||||||
} |
|
||||||
|
|
||||||
pre.console a { |
|
||||||
color: #66ff66; |
|
||||||
} |
|
||||||
|
|
||||||
/* log page */ |
|
||||||
.dbg-btn, #refresh-button { |
|
||||||
vertical-align: baseline; |
|
||||||
} |
|
||||||
|
|
||||||
.lock-icon { |
|
||||||
background-image: url("/wifi/icons.png"); |
|
||||||
background-color: transparent; |
|
||||||
width: 32px; |
|
||||||
height: 32px; |
|
||||||
display: inline-block; |
|
||||||
} |
|
||||||
|
|
||||||
#menu { |
|
||||||
margin-left: -150px; |
|
||||||
width: 150px; |
|
||||||
position: fixed; |
|
||||||
top: 0; |
|
||||||
left: 0; |
|
||||||
bottom: 0; |
|
||||||
z-index: 1000; |
|
||||||
background: #191818; |
|
||||||
overflow-y: auto; |
|
||||||
-webkit-overflow-scrolling: touch; |
|
||||||
} |
|
||||||
|
|
||||||
#menu a { |
|
||||||
color: #999; |
|
||||||
border: none; |
|
||||||
padding: 0.6em 0 0.6em 0.6em; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu, #menu .pure-menu ul { |
|
||||||
border: none; |
|
||||||
background: transparent; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu ul, #menu .pure-menu .menu-item-divided { |
|
||||||
border-top: 1px solid #333; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu li a:hover, #menu .pure-menu li a:focus { |
|
||||||
background: #333; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu-selected, #menu .pure-menu-heading { |
|
||||||
background: #9d1414; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu-selected a { |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu-heading { |
|
||||||
font-size: 110%; |
|
||||||
color: #fff; |
|
||||||
margin: 0; |
|
||||||
text-transform: none; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu-heading img { |
|
||||||
vertical-align: middle; |
|
||||||
top: -1px; |
|
||||||
position: relative; |
|
||||||
} |
|
||||||
|
|
||||||
#menu .pure-menu-item { |
|
||||||
height:2em; |
|
||||||
} |
|
||||||
|
|
||||||
/* -- Dynamic Button For Responsive Menu -------------------------------------*/ |
|
||||||
|
|
||||||
.menu-link { |
|
||||||
position: fixed; |
|
||||||
display: block; |
|
||||||
top: 0; |
|
||||||
left: 0; |
|
||||||
background: #000; |
|
||||||
background: rgba(0,0,0,0.7); |
|
||||||
font-size: 10px; |
|
||||||
z-index: 10; |
|
||||||
width: 2em; |
|
||||||
height: auto; |
|
||||||
padding: 2.1em 1.6em; |
|
||||||
} |
|
||||||
|
|
||||||
.menu-link:hover, .menu-link:focus { |
|
||||||
background: #000; |
|
||||||
} |
|
||||||
|
|
||||||
.menu-link span { |
|
||||||
position: relative; |
|
||||||
display: block; |
|
||||||
} |
|
||||||
|
|
||||||
.menu-link span, .menu-link span:before, .menu-link span:after { |
|
||||||
background-color: #fff; |
|
||||||
width: 100%; |
|
||||||
height: 0.2em; |
|
||||||
} |
|
||||||
|
|
||||||
.menu-link span:before, .menu-link span:after { |
|
||||||
position: absolute; |
|
||||||
margin-top: -0.6em; |
|
||||||
content: " "; |
|
||||||
} |
|
||||||
|
|
||||||
.menu-link span:after { |
|
||||||
margin-top: 0.6em; |
|
||||||
} |
|
||||||
|
|
||||||
/* -- Responsive Styles (Media Queries) ------------------------------------- */ |
|
||||||
|
|
||||||
@media (min-width: 56em) { |
|
||||||
.header, .content { |
|
||||||
padding-left: 2em; |
|
||||||
padding-right: 2em; |
|
||||||
} |
|
||||||
|
|
||||||
#layout { |
|
||||||
padding-left: 150px; |
|
||||||
left: 0; |
|
||||||
} |
|
||||||
#menu { |
|
||||||
left: 150px; |
|
||||||
} |
|
||||||
|
|
||||||
.menu-link { |
|
||||||
position: fixed; |
|
||||||
left: 150px; |
|
||||||
display: none; |
|
||||||
} |
|
||||||
|
|
||||||
#layout.active .menu-link { |
|
||||||
left: 150px; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@media (max-width: 56em) { |
|
||||||
#layout.active { |
|
||||||
position: relative; |
|
||||||
left: 150px; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/*===== spinners and notification messages */ |
|
||||||
|
|
||||||
#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%; |
|
||||||
} |
|
||||||
.spinner-small { |
|
||||||
display: inline-block; |
|
||||||
height: 1em; |
|
||||||
width: 1em; |
|
||||||
border-width: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
@-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,397 +0,0 @@ |
|||||||
//===== Collection of small utilities
|
|
||||||
|
|
||||||
/* |
|
||||||
* Bind/Unbind events |
|
||||||
* |
|
||||||
* Usage: |
|
||||||
* var el = document.getElementyById('#container'); |
|
||||||
* bnd(el, 'click', function() { |
|
||||||
* console.log('clicked'); |
|
||||||
* }); |
|
||||||
*/ |
|
||||||
|
|
||||||
var bnd = function( |
|
||||||
d, // a DOM element
|
|
||||||
e, // an event name such as "click"
|
|
||||||
f // a handler function
|
|
||||||
){ |
|
||||||
d.addEventListener(e, f, false); |
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
* Create DOM element |
|
||||||
* |
|
||||||
* Usage: |
|
||||||
* var el = m('<h1>Hello</h1>'); |
|
||||||
* document.body.appendChild(el); |
|
||||||
* |
|
||||||
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
|
|
||||||
* More: https://gist.github.com/966233
|
|
||||||
*/ |
|
||||||
|
|
||||||
var m = function( |
|
||||||
a, // an HTML string
|
|
||||||
b, // placeholder
|
|
||||||
c // placeholder
|
|
||||||
){ |
|
||||||
b = document; // get the document,
|
|
||||||
c = b.createElement("p"); // create a container element,
|
|
||||||
c.innerHTML = a; // write the HTML to it, and
|
|
||||||
a = b.createDocumentFragment(); // create a fragment.
|
|
||||||
|
|
||||||
while ( // while
|
|
||||||
b = c.firstChild // the container element has a first child
|
|
||||||
) a.appendChild(b); // append the child to the fragment,
|
|
||||||
|
|
||||||
return a // and then return the fragment.
|
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
* DOM selector |
|
||||||
* |
|
||||||
* Usage: |
|
||||||
* $('div'); |
|
||||||
* $('#name'); |
|
||||||
* $('.name'); |
|
||||||
* |
|
||||||
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
|
|
||||||
* More: https://gist.github.com/991057
|
|
||||||
*/ |
|
||||||
|
|
||||||
var $ = function( |
|
||||||
a, // take a simple selector like "name", "#name", or ".name", and
|
|
||||||
b // an optional context, and
|
|
||||||
){ |
|
||||||
a = a.match(/^(\W)?(.*)/); // split the selector into name and symbol.
|
|
||||||
return( // return an element or list, from within the scope of
|
|
||||||
b // the passed context
|
|
||||||
|| document // or document,
|
|
||||||
)[ |
|
||||||
"getElement" + ( // obtained by the appropriate method calculated by
|
|
||||||
a[1] |
|
||||||
? a[1] == "#" |
|
||||||
? "ById" // the node by ID,
|
|
||||||
: "sByClassName" // the nodes by class name, or
|
|
||||||
: "sByTagName" // the nodes by tag name,
|
|
||||||
) |
|
||||||
]( |
|
||||||
a[2] // called with the name.
|
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
* Get cross browser xhr object |
|
||||||
* |
|
||||||
* Copyright (C) 2011 Jed Schmidt <http://jed.is>
|
|
||||||
* More: https://gist.github.com/993585
|
|
||||||
*/ |
|
||||||
|
|
||||||
var j = function( |
|
||||||
a // cursor placeholder
|
|
||||||
){ |
|
||||||
for( // for all a
|
|
||||||
a=0; // from 0
|
|
||||||
a<4; // to 4,
|
|
||||||
a++ // incrementing
|
|
||||||
) try { // try
|
|
||||||
return a // returning
|
|
||||||
? new ActiveXObject( // a new ActiveXObject
|
|
||||||
[ // reflecting
|
|
||||||
, // (elided)
|
|
||||||
"Msxml2", // the various
|
|
||||||
"Msxml3", // working
|
|
||||||
"Microsoft" // options
|
|
||||||
][a] + // for Microsoft implementations, and
|
|
||||||
".XMLHTTP" // the appropriate suffix,
|
|
||||||
) // but make sure to
|
|
||||||
: new XMLHttpRequest // try the w3c standard first, and
|
|
||||||
} |
|
||||||
|
|
||||||
catch(e){} // ignore when it fails.
|
|
||||||
} |
|
||||||
|
|
||||||
// createElement short-hand
|
|
||||||
|
|
||||||
e = function(a) { return document.createElement(a); } |
|
||||||
|
|
||||||
// chain onload handlers
|
|
||||||
|
|
||||||
function onLoad(f) { |
|
||||||
var old = window.onload; |
|
||||||
if (typeof old != 'function') { |
|
||||||
window.onload = f; |
|
||||||
} else { |
|
||||||
window.onload = function() { |
|
||||||
old(); |
|
||||||
f(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//===== helpers to add/remove/toggle HTML element classes
|
|
||||||
|
|
||||||
function addClass(el, cl) { |
|
||||||
el.className += ' ' + cl; |
|
||||||
} |
|
||||||
function removeClass(el, cl) { |
|
||||||
var cls = el.className.split(/\s+/), |
|
||||||
l = cls.length; |
|
||||||
for (var i=0; i<l; i++) { |
|
||||||
if (cls[i] === cl) cls.splice(i, 1); |
|
||||||
} |
|
||||||
el.className = cls.join(' '); |
|
||||||
return cls.length != l |
|
||||||
} |
|
||||||
function toggleClass(el, cl) { |
|
||||||
if (!removeClass(el, cl)) addClass(el, cl); |
|
||||||
} |
|
||||||
|
|
||||||
//===== AJAX
|
|
||||||
|
|
||||||
function ajaxReq(method, url, ok_cb, err_cb) { |
|
||||||
var xhr = j(); |
|
||||||
xhr.open(method, url, true); |
|
||||||
var timeout = setTimeout(function() { |
|
||||||
xhr.abort(); |
|
||||||
console.log("XHR abort:", method, url); |
|
||||||
xhr.status = 599; |
|
||||||
xhr.responseText = "request time-out"; |
|
||||||
}, 9000); |
|
||||||
xhr.onreadystatechange = function() { |
|
||||||
if (xhr.readyState != 4) { return; } |
|
||||||
clearTimeout(timeout); |
|
||||||
if (xhr.status >= 200 && xhr.status < 300) { |
|
||||||
console.log("XHR done:", method, url, "->", xhr.status); |
|
||||||
ok_cb(xhr.responseText); |
|
||||||
} else { |
|
||||||
console.log("XHR ERR :", method, url, "->", xhr.status, xhr.responseText, xhr); |
|
||||||
err_cb(xhr.status, xhr.responseText); |
|
||||||
} |
|
||||||
} |
|
||||||
console.log("XHR send:", method, url); |
|
||||||
try { |
|
||||||
xhr.send(); |
|
||||||
} catch(err) { |
|
||||||
console.log("XHR EXC :", method, url, "->", err); |
|
||||||
err_cb(599, err); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function dispatchJson(resp, ok_cb, err_cb) { |
|
||||||
var j; |
|
||||||
try { j = JSON.parse(resp); } |
|
||||||
catch(err) { |
|
||||||
console.log("JSON parse error: " + err + ". In: " + resp); |
|
||||||
err_cb(500, "JSON parse error: " + err); |
|
||||||
return; |
|
||||||
} |
|
||||||
ok_cb(j); |
|
||||||
} |
|
||||||
|
|
||||||
function ajaxJson(method, url, ok_cb, err_cb) { |
|
||||||
ajaxReq(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); |
|
||||||
} |
|
||||||
|
|
||||||
function ajaxSpin(method, url, ok_cb, err_cb) { |
|
||||||
$("#spinner").removeAttribute('hidden'); |
|
||||||
ajaxReq(method, url, function(resp) { |
|
||||||
$("#spinner").setAttribute('hidden', ''); |
|
||||||
ok_cb(resp); |
|
||||||
}, function(status, statusText) { |
|
||||||
$("#spinner").setAttribute('hidden', ''); |
|
||||||
//showWarning("Error: " + statusText);
|
|
||||||
err_cb(status, statusText); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function ajaxJsonSpin(method, url, ok_cb, err_cb) { |
|
||||||
ajaxSpin(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); |
|
||||||
} |
|
||||||
|
|
||||||
//===== main menu, header spinner and notification boxes
|
|
||||||
|
|
||||||
onLoad(function() { |
|
||||||
var l = $("#layout"); |
|
||||||
var o = l.childNodes[0]; |
|
||||||
// spinner
|
|
||||||
l.insertBefore(m('<div id="spinner" class="spinner" hidden></div>'), o); |
|
||||||
// notification boxes
|
|
||||||
l.insertBefore(m( |
|
||||||
'<div id="messages"><div id="warning" hidden></div><div id="notification" hidden></div></div>'), o); |
|
||||||
// menu hamburger button
|
|
||||||
l.insertBefore(m('<a href="#menu" id="menuLink" class="menu-link"><span></span></a>'), o); |
|
||||||
// menu left-pane
|
|
||||||
var mm = m( |
|
||||||
'<div id="menu">\ |
|
||||||
<div class="pure-menu">\ |
|
||||||
<a class="pure-menu-heading" href="https://github.com/jeelabs/esp-link">\ |
|
||||||
<img src="/favicon.ico" height="32"> esp-link</a>\ |
|
||||||
<ul id="menu-list" class="pure-menu-list"></ul>\ |
|
||||||
</div>\ |
|
||||||
</div>\ |
|
||||||
'); |
|
||||||
l.insertBefore(mm, o); |
|
||||||
|
|
||||||
// make hamburger button pull out menu
|
|
||||||
var ml = $('#menuLink'), mm = $('#menu'); |
|
||||||
bnd(ml, 'click', function (e) { |
|
||||||
console.log("hamburger time"); |
|
||||||
var active = 'active'; |
|
||||||
e.preventDefault(); |
|
||||||
toggleClass(l, active); |
|
||||||
toggleClass(mm, active); |
|
||||||
toggleClass(ml, active); |
|
||||||
}); |
|
||||||
|
|
||||||
// populate menu via ajax call
|
|
||||||
var getMenu = function() { |
|
||||||
ajaxJson("GET", "/menu", function(data) { |
|
||||||
var html = "", path = window.location.pathname; |
|
||||||
for (var i=0; i<data.menu.length; i+=2) { |
|
||||||
var href = data.menu[i+1]; |
|
||||||
html = html.concat(" <li class=\"pure-menu-item" + |
|
||||||
(path === href ? " pure-menu-selected" : "") + "\">" + |
|
||||||
"<a href=\"" + href + "\" class=\"pure-menu-link\">" + |
|
||||||
data.menu[i] + "</a></li>"); |
|
||||||
} |
|
||||||
$("#menu-list").innerHTML = html; |
|
||||||
|
|
||||||
v = $("#version"); |
|
||||||
if (v != null) { v.innerHTML = data.version; } |
|
||||||
}, function() { setTimeout(getMenu, 1000); }); |
|
||||||
}; |
|
||||||
getMenu(); |
|
||||||
}); |
|
||||||
|
|
||||||
//===== Wifi info
|
|
||||||
|
|
||||||
function showWifiInfo(data) { |
|
||||||
Object.keys(data).forEach(function(v) { |
|
||||||
el = $("#wifi-" + v); |
|
||||||
if (el != null) { |
|
||||||
if (el.nodeName === "INPUT") el.value = data[v]; |
|
||||||
else el.innerHTML = data[v]; |
|
||||||
} |
|
||||||
}); |
|
||||||
var dhcp = $('#dhcp-r'+data.dhcp); |
|
||||||
if (dhcp) dhcp.click(); |
|
||||||
$("#wifi-spinner").setAttribute("hidden", ""); |
|
||||||
$("#wifi-table").removeAttribute("hidden"); |
|
||||||
currAp = data.ssid; |
|
||||||
} |
|
||||||
|
|
||||||
function getWifiInfo() { |
|
||||||
ajaxJson('GET', "/wifi/info", showWifiInfo, |
|
||||||
function(s, st) { window.setTimeout(getWifiInfo, 1000); }); |
|
||||||
} |
|
||||||
|
|
||||||
//===== Notifications
|
|
||||||
|
|
||||||
function showWarning(text) { |
|
||||||
var el = $("#warning"); |
|
||||||
el.innerHTML = text; |
|
||||||
el.removeAttribute('hidden'); |
|
||||||
} |
|
||||||
function hideWarning() { |
|
||||||
el = $("#warning").setAttribute('hidden', ''); |
|
||||||
} |
|
||||||
var notifTimeout = null; |
|
||||||
function showNotification(text) { |
|
||||||
var el = $("#notification"); |
|
||||||
el.innerHTML = text; |
|
||||||
el.removeAttribute('hidden'); |
|
||||||
if (notifTimeout != null) clearTimeout(notifTimeout); |
|
||||||
notifTimout = setTimeout(function() { |
|
||||||
el.setAttribute('hidden', ''); |
|
||||||
notifTimout = null; |
|
||||||
}, 4000); |
|
||||||
} |
|
||||||
|
|
||||||
//===== GPIO Pin mux card
|
|
||||||
|
|
||||||
var currPin; |
|
||||||
// pin={reset:12, isp:13, LED_conn:0, LED_ser:2}
|
|
||||||
function createInputForPin(pin) { |
|
||||||
var input = document.createElement("input"); |
|
||||||
input.type = "radio"; |
|
||||||
input.name = "pins"; |
|
||||||
input.data = pin.name; |
|
||||||
input.className = "pin-input"; |
|
||||||
input.value= pin.value; |
|
||||||
input.id = "opt-" + pin.value; |
|
||||||
if (currPin == pin.name) input.checked = "1"; |
|
||||||
|
|
||||||
var descr = m('<label for="opt-'+pin.value+'"><b>'+pin.name+":</b>"+pin.descr+"</label>"); |
|
||||||
var div = document.createElement("div"); |
|
||||||
div.appendChild(input); |
|
||||||
div.appendChild(descr); |
|
||||||
return div; |
|
||||||
} |
|
||||||
|
|
||||||
function displayPins(resp) { |
|
||||||
var po = $("#pin-mux"); |
|
||||||
po.innerHTML = ""; |
|
||||||
currPin = resp.curr; |
|
||||||
resp.map.forEach(function(v) { |
|
||||||
po.appendChild(createInputForPin(v)); |
|
||||||
}); |
|
||||||
var i, inputs = $(".pin-input"); |
|
||||||
for (i=0; i<inputs.length; i++) { |
|
||||||
inputs[i].onclick = function() { setPins(this.value, this.data) }; |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
function fetchPins() { |
|
||||||
ajaxJson("GET", "/pins", displayPins, function() { |
|
||||||
window.setTimeout(fetchPins, 1000); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function setPins(v, name) { |
|
||||||
ajaxSpin("POST", "/pins?map="+v, function() { |
|
||||||
showNotification("Pin assignment changed to " + name); |
|
||||||
}, function() { |
|
||||||
showNotification("Pin assignment change failed"); |
|
||||||
window.setTimeout(fetchPins, 100); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
//===== TCP client card
|
|
||||||
|
|
||||||
function tcpEn(){return document.querySelector('input[name="tcp_enable"]')} |
|
||||||
function rssiEn(){return document.querySelector('input[name="rssi_enable"]')} |
|
||||||
function apiKey(){return document.querySelector('input[name="api_key"]')} |
|
||||||
|
|
||||||
function changeTcpClient(e) { |
|
||||||
e.preventDefault(); |
|
||||||
var url = "tcpclient"; |
|
||||||
url += "?tcp_enable=" + tcpEn().checked; |
|
||||||
url += "&rssi_enable=" + rssiEn().checked; |
|
||||||
url += "&api_key=" + encodeURIComponent(apiKey().value); |
|
||||||
|
|
||||||
hideWarning(); |
|
||||||
var cb = $("#tcp-button"); |
|
||||||
addClass(cb, 'pure-button-disabled'); |
|
||||||
ajaxSpin("POST", url, function(resp) { |
|
||||||
removeClass(cb, 'pure-button-disabled'); |
|
||||||
getWifiInfo(); |
|
||||||
}, function(s, st) { |
|
||||||
showWarning("Error: "+st); |
|
||||||
removeClass(cb, 'pure-button-disabled'); |
|
||||||
getWifiInfo(); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function displayTcpClient(resp) { |
|
||||||
tcpEn().checked = resp.tcp_enable > 0; |
|
||||||
rssiEn().checked = resp.rssi_enable > 0; |
|
||||||
apiKey().value = resp.api_key; |
|
||||||
} |
|
||||||
|
|
||||||
function fetchTcpClient() { |
|
||||||
ajaxJson("GET", "/tcpclient", displayTcpClient, function() { |
|
||||||
window.setTimeout(fetchTcpClient, 1000); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 914 B |
@ -1,84 +0,0 @@ |
|||||||
<div id="main"> |
|
||||||
<div class="header"> |
|
||||||
<h1>Wifi Configuration</h1> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="content"> |
|
||||||
<div class="pure-g"> |
|
||||||
<div class="pure-u-1 pure-u-md-1-2"><div class="card"> |
|
||||||
<h1>Wifi State</h1> |
|
||||||
<div id="wifi-spinner" class="spinner spinner-small"></div> |
|
||||||
<table id="wifi-table" class="pure-table pure-table-horizontal" hidden><tbody> |
|
||||||
<tr><td>WiFi mode</td><td id="wifi-mode"></td></tr> |
|
||||||
<tr><td>Wifi channel</td><td id="wifi-chan"></td></tr> |
|
||||||
<tr><td>Configured network</td><td id="wifi-ssid"></td></tr> |
|
||||||
<tr><td>Wifi status</td><td id="wifi-status"></td></tr> |
|
||||||
<tr><td>Wifi address</td><td id="wifi-ip"></td></tr> |
|
||||||
<tr><td>Wifi rssi</td><td id="wifi-rssi"></td></tr> |
|
||||||
<tr><td>Wifi phy</td><td id="wifi-phy"></td></tr> |
|
||||||
<tr><td>Wifi MAC</td><td id="wifi-mac"></td></tr> |
|
||||||
<tr><td colspan="2" id="wifi-warn"></td></tr> |
|
||||||
</tbody> </table> |
|
||||||
</div></div> |
|
||||||
<div class="pure-u-1 pure-u-md-1-2"><div class="card"> |
|
||||||
<h1>Wifi Association</h1> |
|
||||||
<p id="reconnect" style="color: #600" hidden></p> |
|
||||||
<form action="#" id="wifiform" class="pure-form pure-form-stacked"> |
|
||||||
<legend>To connect to a WiFi network, please select one of the detected networks, |
|
||||||
enter the password, and hit the connect button...</legend> |
|
||||||
<label>Network SSID</label> |
|
||||||
<div id="aps">Scanning... <div class="spinner spinner-small"></div></div> |
|
||||||
<label>WiFi password, if applicable:</label> |
|
||||||
<input id="wifi-passwd" type="password" name="passwd" placeholder="password"> |
|
||||||
<button id="connect-button" type="submit" class="pure-button button-primary">Connect!</button> |
|
||||||
</form> |
|
||||||
</div></div> |
|
||||||
</div> |
|
||||||
<div class="pure-g"> |
|
||||||
<div class="pure-u-1 pure-u-md-1-2"><div class="card"> |
|
||||||
<h1>Special Settings</h1> |
|
||||||
<form action="#" id="specform" class="pure-form"> |
|
||||||
<legend>Special settings, use with care!</legend> |
|
||||||
<div class="form-horizontal"> |
|
||||||
<label for="dhcp-ron" style="margin-right:1em"> |
|
||||||
<input type="radio" name="dhcp" value="on" id="dhcp-ron"/> |
|
||||||
DHCP</label> |
|
||||||
<label for="dhcp-roff"> |
|
||||||
<input type="radio" name="dhcp" value="off" id="dhcp-roff"/> |
|
||||||
Static IP</label> |
|
||||||
</div> |
|
||||||
<div id="dhcp-on" class="pure-form-stacked"> |
|
||||||
<label>Hostname when requesting DHCP lease</label> |
|
||||||
<input id="wifi-hostname" type="text" name="hostname"/> |
|
||||||
</div> |
|
||||||
<div id="dhcp-off" class="pure-form-stacked"> |
|
||||||
<label>Static IP address</label> |
|
||||||
<input id="wifi-staticip" type="text" name="staticip"/> |
|
||||||
<label>Netmask (for static IP)</label> |
|
||||||
<input id="wifi-netmask" type="text" name="netmask"/> |
|
||||||
<label>Gateway (for static IP)</label> |
|
||||||
<input id="wifi-gateway" type="text" name="gateway"/> |
|
||||||
</div> |
|
||||||
<button id="special-button" type="submit" |
|
||||||
class="pure-button button-primary">Change!</button> |
|
||||||
</form> |
|
||||||
</div></div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<script type="text/javascript"> |
|
||||||
</script> |
|
||||||
<script src="wifi.js"></script> |
|
||||||
<script type="text/javascript"> |
|
||||||
onLoad(function() { |
|
||||||
getWifiInfo(); |
|
||||||
bnd($("#wifiform"), "submit", changeWifiAp); |
|
||||||
bnd($("#specform"), "submit", changeSpecial); |
|
||||||
bnd($("#dhcp-ron"), "click", doDhcp); |
|
||||||
bnd($("#dhcp-roff"), "click", doStatic); |
|
||||||
scanTimeout = window.setTimeout(scanAPs, 500); |
|
||||||
}); |
|
||||||
</script> |
|
||||||
</body></html> |
|
@ -1,200 +0,0 @@ |
|||||||
var currAp = ""; |
|
||||||
var blockScan = 0; |
|
||||||
|
|
||||||
function createInputForAp(ap) { |
|
||||||
if (ap.essid=="" && ap.rssi==0) return; |
|
||||||
|
|
||||||
var input = e("input"); |
|
||||||
input.type = "radio"; |
|
||||||
input.name = "essid"; |
|
||||||
input.value=ap.essid; |
|
||||||
input.id = "opt-" + ap.essid; |
|
||||||
if (currAp == ap.essid) input.checked = "1"; |
|
||||||
|
|
||||||
var bars = e("div"); |
|
||||||
var rssiVal = -Math.floor(ap.rssi/51)*32; |
|
||||||
bars.className = "lock-icon"; |
|
||||||
bars.style.backgroundPosition = "0px "+rssiVal+"px"; |
|
||||||
|
|
||||||
var rssi = e("div"); |
|
||||||
rssi.innerHTML = "" + ap.rssi +"dB"; |
|
||||||
|
|
||||||
var encrypt = e("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 = e("div"); |
|
||||||
label.innerHTML = ap.essid; |
|
||||||
|
|
||||||
var div = m('<label for=\"opt-' + ap.essid + '"></label>').childNodes[0]; |
|
||||||
div.appendChild(input); |
|
||||||
div.appendChild(encrypt); |
|
||||||
div.appendChild(bars); |
|
||||||
div.appendChild(rssi); |
|
||||||
div.appendChild(label); |
|
||||||
return div; |
|
||||||
} |
|
||||||
|
|
||||||
function getSelectedEssid() { |
|
||||||
var e = document.forms.wifiform.elements; |
|
||||||
for (var i=0; i<e.length; i++) { |
|
||||||
if (e[i].type == "radio" && e[i].checked) return e[i].value; |
|
||||||
} |
|
||||||
return currAp; |
|
||||||
} |
|
||||||
|
|
||||||
var scanTimeout = null; |
|
||||||
var scanReqCnt = 0; |
|
||||||
|
|
||||||
function scanResult() { |
|
||||||
if (scanReqCnt > 60) { |
|
||||||
return scanAPs(); |
|
||||||
} |
|
||||||
scanReqCnt += 1; |
|
||||||
ajaxJson('GET', "scan", function(data) { |
|
||||||
currAp = getSelectedEssid(); |
|
||||||
if (data.result.inProgress == "0" && data.result.APs.length > 1) { |
|
||||||
$("#aps").innerHTML = ""; |
|
||||||
var n = 0; |
|
||||||
for (var i=0; i<data.result.APs.length; i++) { |
|
||||||
if (data.result.APs[i].essid == "" && data.result.APs[i].rssi == 0) continue; |
|
||||||
$("#aps").appendChild(createInputForAp(data.result.APs[i])); |
|
||||||
n = n+1; |
|
||||||
} |
|
||||||
showNotification("Scan found " + n + " networks"); |
|
||||||
var cb = $("#connect-button"); |
|
||||||
cb.className = cb.className.replace(" pure-button-disabled", ""); |
|
||||||
if (scanTimeout != null) clearTimeout(scanTimeout); |
|
||||||
scanTimeout = window.setTimeout(scanAPs, 20000); |
|
||||||
} else { |
|
||||||
window.setTimeout(scanResult, 1000); |
|
||||||
} |
|
||||||
}, function(s, st) { |
|
||||||
window.setTimeout(scanResult, 5000); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function scanAPs() { |
|
||||||
console.log("scanning now"); |
|
||||||
if (blockScan) { |
|
||||||
scanTimeout = window.setTimeout(scanAPs, 1000); |
|
||||||
return; |
|
||||||
} |
|
||||||
scanTimeout = null; |
|
||||||
scanReqCnt = 0; |
|
||||||
ajaxReq('POST', "scan", function(data) { |
|
||||||
//showNotification("Wifi scan started");
|
|
||||||
window.setTimeout(scanResult, 1000); |
|
||||||
}, function(s, st) { |
|
||||||
//showNotification("Wifi scan may have started?");
|
|
||||||
window.setTimeout(scanResult, 1000); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function getStatus() { |
|
||||||
ajaxJsonSpin("GET", "connstatus", function(data) { |
|
||||||
if (data.status == "idle" || data.status == "connecting") { |
|
||||||
$("#aps").innerHTML = "Connecting..."; |
|
||||||
showNotification("Connecting..."); |
|
||||||
window.setTimeout(getStatus, 1000); |
|
||||||
} else if (data.status == "got IP address") { |
|
||||||
var txt = "Connected! Got IP "+data.ip; |
|
||||||
showNotification(txt); |
|
||||||
showWifiInfo(data); |
|
||||||
blockScan = 0; |
|
||||||
|
|
||||||
if (data.modechange == "yes") { |
|
||||||
var txt2 = "esp-link will switch to STA-only mode in a few seconds"; |
|
||||||
window.setTimeout(function() { showNotification(txt2); }, 4000); |
|
||||||
} |
|
||||||
|
|
||||||
$("#reconnect").removeAttribute("hidden"); |
|
||||||
$("#reconnect").innerHTML = |
|
||||||
"If you are in the same network, go to <a href=\"http://"+data.ip+ |
|
||||||
"/\">"+data.ip+"</a>, else connect to network "+data.ssid+" first."; |
|
||||||
} else { |
|
||||||
blockScan = 0; |
|
||||||
showWarning("Connection failed: " + data.status + ", " + data.reason); |
|
||||||
$("#aps").innerHTML = |
|
||||||
"Check password and selected AP. <a href=\"wifi.tpl\">Go Back</a>"; |
|
||||||
} |
|
||||||
}, function(s, st) { |
|
||||||
//showWarning("Can't get status: " + st);
|
|
||||||
window.setTimeout(getStatus, 2000); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function changeWifiMode(m) { |
|
||||||
blockScan = 1; |
|
||||||
hideWarning(); |
|
||||||
ajaxSpin("POST", "setmode?mode=" + m, function(resp) { |
|
||||||
showNotification("Mode changed"); |
|
||||||
window.setTimeout(getWifiInfo, 100); |
|
||||||
blockScan = 0; |
|
||||||
}, function(s, st) { |
|
||||||
showWarning("Error changing mode: " + st); |
|
||||||
window.setTimeout(getWifiInfo, 100); |
|
||||||
blockScan = 0; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function changeWifiAp(e) { |
|
||||||
e.preventDefault(); |
|
||||||
var passwd = $("#wifi-passwd").value; |
|
||||||
var essid = getSelectedEssid(); |
|
||||||
showNotification("Connecting to " + essid); |
|
||||||
var url = "connect?essid="+encodeURIComponent(essid)+"&passwd="+encodeURIComponent(passwd); |
|
||||||
|
|
||||||
hideWarning(); |
|
||||||
$("#reconnect").setAttribute("hidden", ""); |
|
||||||
$("#wifi-passwd").value = ""; |
|
||||||
var cb = $("#connect-button"); |
|
||||||
var cn = cb.className; |
|
||||||
cb.className += ' pure-button-disabled'; |
|
||||||
blockScan = 1; |
|
||||||
ajaxSpin("POST", url, function(resp) { |
|
||||||
$("#spinner").removeAttribute('hidden'); // hack
|
|
||||||
showNotification("Waiting for network change..."); |
|
||||||
window.scrollTo(0, 0); |
|
||||||
window.setTimeout(getStatus, 2000); |
|
||||||
}, function(s, st) { |
|
||||||
showWarning("Error switching network: "+st); |
|
||||||
cb.className = cn; |
|
||||||
window.setTimeout(scanAPs, 1000); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function changeSpecial(e) { |
|
||||||
e.preventDefault(); |
|
||||||
var url = "special"; |
|
||||||
url += "?dhcp=" + document.querySelector('input[name="dhcp"]:checked').value; |
|
||||||
url += "&hostname=" + encodeURIComponent($("#wifi-hostname").value); |
|
||||||
url += "&staticip=" + encodeURIComponent($("#wifi-staticip").value); |
|
||||||
url += "&netmask=" + encodeURIComponent($("#wifi-netmask").value); |
|
||||||
url += "&gateway=" + encodeURIComponent($("#wifi-gateway").value); |
|
||||||
|
|
||||||
hideWarning(); |
|
||||||
var cb = $("#special-button"); |
|
||||||
addClass(cb, 'pure-button-disabled'); |
|
||||||
ajaxSpin("POST", url, function(resp) { |
|
||||||
removeClass(cb, 'pure-button-disabled'); |
|
||||||
getWifiInfo(); |
|
||||||
}, function(s, st) { |
|
||||||
showWarning("Error: "+st); |
|
||||||
removeClass(cb, 'pure-button-disabled'); |
|
||||||
getWifiInfo(); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function doDhcp() { |
|
||||||
$('#dhcp-on').removeAttribute('hidden'); |
|
||||||
$('#dhcp-off').setAttribute('hidden', ''); |
|
||||||
} |
|
||||||
|
|
||||||
function doStatic() { |
|
||||||
$('#dhcp-off').removeAttribute('hidden'); |
|
||||||
$('#dhcp-on').setAttribute('hidden', ''); |
|
||||||
} |
|
@ -0,0 +1,274 @@ |
|||||||
|
#include "Time.h" |
||||||
|
|
||||||
|
static tmElements_t tm; // a cache of time elements
|
||||||
|
static time_t cacheTime; // the time the cache was updated
|
||||||
|
static time_t syncInterval = 300; // time sync will be attempted after this many seconds
|
||||||
|
|
||||||
|
static os_timer_t micros_overflow_timer; |
||||||
|
static uint32_t micros_at_last_overflow_tick = 0; |
||||||
|
static uint32_t micros_overflow_count = 0; |
||||||
|
|
||||||
|
static time_t sysTime = 0; |
||||||
|
static time_t prevMillis = 0; |
||||||
|
static time_t nextSyncTime = 0; |
||||||
|
static timeStatus_t Status = timeNotSet; |
||||||
|
|
||||||
|
getExternalTime getTimePtr; |
||||||
|
|
||||||
|
int16_t ICACHE_FLASH_ATTR
|
||||||
|
totalMinutes(int16_t hours, int8_t minutes) |
||||||
|
{ |
||||||
|
return (hours * 60) + minutes; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
micros_overflow_tick(void* arg) { |
||||||
|
uint32_t m = system_get_time(); |
||||||
|
if (m < micros_at_last_overflow_tick) |
||||||
|
++micros_overflow_count; |
||||||
|
micros_at_last_overflow_tick = m; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long ICACHE_FLASH_ATTR
|
||||||
|
millis() { |
||||||
|
uint32_t m = system_get_time(); |
||||||
|
uint32_t c = micros_overflow_count + ((m < micros_at_last_overflow_tick) ? 1 : 0); |
||||||
|
return c * 4294967 + m / 1000; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long ICACHE_FLASH_ATTR
|
||||||
|
micros() { |
||||||
|
return system_get_time(); |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
time_init() { |
||||||
|
os_timer_setfn(µs_overflow_timer, (os_timer_func_t*)µs_overflow_tick, 0); |
||||||
|
os_timer_arm(µs_overflow_timer, 60000, 1); |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
breakTime(time_t time, tmElements_t *tm){ |
||||||
|
|
||||||
|
uint8_t year; |
||||||
|
uint8_t month, monthLength; |
||||||
|
unsigned long days; |
||||||
|
|
||||||
|
tm->Second = time % 60; |
||||||
|
time /= 60; // now it is minutes
|
||||||
|
tm->Minute = time % 60; |
||||||
|
time /= 60; // now it is hours
|
||||||
|
tm->Hour = time % 24; |
||||||
|
time /= 24; // now it is days
|
||||||
|
tm->Wday = ((time + 4) % 7) + 1; // Sunday is day 1
|
||||||
|
|
||||||
|
year = 0; |
||||||
|
days = 0; |
||||||
|
while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { |
||||||
|
year++; |
||||||
|
} |
||||||
|
tm->Year = year; // year is offset from 1970
|
||||||
|
|
||||||
|
days -= LEAP_YEAR(year) ? 366 : 365; |
||||||
|
time -= days; // now it is days in this year, starting at 0
|
||||||
|
|
||||||
|
days = 0; |
||||||
|
month = 0; |
||||||
|
monthLength = 0; |
||||||
|
for (month = 0; month<12; month++) { |
||||||
|
if (month == 1) { // february
|
||||||
|
if (LEAP_YEAR(year)) { |
||||||
|
monthLength = 29; |
||||||
|
} |
||||||
|
else { |
||||||
|
monthLength = 28; |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
monthLength = monthDays[month]; |
||||||
|
} |
||||||
|
|
||||||
|
if (time >= monthLength) { |
||||||
|
time -= monthLength; |
||||||
|
} |
||||||
|
else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
tm->Month = month + 1; // jan is month 1
|
||||||
|
tm->Day = time + 1; // day of month
|
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the "timeInput" time_t count into "struct tm" time components. |
||||||
|
* This is a more compact version of the C library localtime function. |
||||||
|
* Note that year is offset from 1970 !!! |
||||||
|
*/ |
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
timet_to_tm(time_t timeInput, struct tmElements *tmel){ |
||||||
|
|
||||||
|
uint8_t year; |
||||||
|
uint8_t month, monthLength; |
||||||
|
uint32_t time; |
||||||
|
unsigned long days; |
||||||
|
|
||||||
|
time = (uint32_t)timeInput; |
||||||
|
tmel->Second = time % 60; |
||||||
|
time /= 60; // Now it is minutes.
|
||||||
|
tmel->Minute = time % 60; |
||||||
|
time /= 60; // Now it is hours.
|
||||||
|
tmel->Hour = time % 24; |
||||||
|
time /= 24; // Now it is days.
|
||||||
|
tmel->Wday = ((time + 4) % 7) + 1; // Sunday is day 1.
|
||||||
|
|
||||||
|
year = 0; |
||||||
|
days = 0; |
||||||
|
while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { |
||||||
|
year++; |
||||||
|
} |
||||||
|
tmel->Year = year; // The year is offset from 1970.
|
||||||
|
|
||||||
|
days -= LEAP_YEAR(year) ? 366 : 365; |
||||||
|
time -= days; // Now it is days in this year, starting at 0.
|
||||||
|
|
||||||
|
days = 0; |
||||||
|
month = 0; |
||||||
|
monthLength = 0; |
||||||
|
for (month = 0; month<12; month++) { |
||||||
|
if (month == 1) { // February.
|
||||||
|
if (LEAP_YEAR(year)) { |
||||||
|
monthLength = 29; |
||||||
|
} |
||||||
|
else { |
||||||
|
monthLength = 28; |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
monthLength = monthDays[month]; |
||||||
|
} |
||||||
|
|
||||||
|
if (time >= monthLength) { |
||||||
|
time -= monthLength; |
||||||
|
} |
||||||
|
else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
tmel->Month = month + 1; // Jan is month 1.
|
||||||
|
tmel->Day = time + 1; // Day of month.
|
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconstitute "struct tm" elements into a time_t count value. |
||||||
|
* Note that the year argument is offset from 1970. |
||||||
|
*/ |
||||||
|
time_t ICACHE_FLASH_ATTR
|
||||||
|
tm_to_timet(struct tmElements *tmel){ |
||||||
|
|
||||||
|
int i; |
||||||
|
uint32_t seconds; |
||||||
|
|
||||||
|
// Seconds from 1970 till 1st Jan 00:00:00 of the given year.
|
||||||
|
seconds = tmel->Year*(SECS_PER_DAY * 365); |
||||||
|
for (i = 0; i < tmel->Year; i++) { |
||||||
|
if (LEAP_YEAR(i)) { |
||||||
|
seconds += SECS_PER_DAY; // Add extra days for leap years.
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Add the number of elapsed days for the given year. Months start from 1.
|
||||||
|
for (i = 1; i < tmel->Month; i++) { |
||||||
|
if ((i == 2) && LEAP_YEAR(tmel->Year)) { |
||||||
|
seconds += SECS_PER_DAY * 29; |
||||||
|
} |
||||||
|
else { |
||||||
|
seconds += SECS_PER_DAY * monthDays[i - 1]; // "monthDay" array starts from 0.
|
||||||
|
} |
||||||
|
} |
||||||
|
seconds += (tmel->Day - 1) * SECS_PER_DAY; // Days...
|
||||||
|
seconds += tmel->Hour * SECS_PER_HOUR; // Hours...
|
||||||
|
seconds += tmel->Minute * SECS_PER_MIN; // Minutes...
|
||||||
|
seconds += tmel->Second; // ...and finally, Seconds.
|
||||||
|
return (time_t)seconds; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
refreshCache(time_t t){ |
||||||
|
if (t != cacheTime) |
||||||
|
{ |
||||||
|
breakTime(t, &tm); |
||||||
|
cacheTime = t; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int ICACHE_FLASH_ATTR
|
||||||
|
hour() { // the hour now
|
||||||
|
time_t t = now(); |
||||||
|
refreshCache(t); |
||||||
|
return tm.Hour; |
||||||
|
} |
||||||
|
|
||||||
|
int ICACHE_FLASH_ATTR
|
||||||
|
minute() { // the minute now
|
||||||
|
time_t t = now(); |
||||||
|
refreshCache(t); |
||||||
|
return tm.Minute; |
||||||
|
} |
||||||
|
|
||||||
|
int ICACHE_FLASH_ATTR
|
||||||
|
second() { // the second now
|
||||||
|
time_t t = now(); |
||||||
|
refreshCache(t); |
||||||
|
return tm.Second; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
setTime(time_t t){ |
||||||
|
#ifdef TIME_DRIFT_INFO |
||||||
|
if (sysUnsyncedTime == 0) |
||||||
|
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
|
||||||
|
#endif |
||||||
|
|
||||||
|
sysTime = t; |
||||||
|
nextSyncTime = t + syncInterval; |
||||||
|
Status = timeSet; |
||||||
|
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
|
||||||
|
} |
||||||
|
|
||||||
|
time_t ICACHE_FLASH_ATTR
|
||||||
|
now(){ |
||||||
|
while (millis() - prevMillis >= 1000){ |
||||||
|
sysTime++; |
||||||
|
prevMillis += 1000; |
||||||
|
#ifdef TIME_DRIFT_INFO |
||||||
|
sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift
|
||||||
|
#endif |
||||||
|
} |
||||||
|
if (nextSyncTime <= sysTime){ |
||||||
|
if (getTimePtr != 0){ |
||||||
|
time_t t = getTimePtr(); |
||||||
|
if (t != 0) |
||||||
|
setTime(t); |
||||||
|
else |
||||||
|
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync; |
||||||
|
} |
||||||
|
} |
||||||
|
return sysTime; |
||||||
|
} |
||||||
|
|
||||||
|
timeStatus_t ICACHE_FLASH_ATTR
|
||||||
|
timeStatus(){ // indicates if time has been set and recently synchronized
|
||||||
|
return Status; |
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
setSyncProvider(getExternalTime getTimeFunction){ |
||||||
|
getTimePtr = getTimeFunction; |
||||||
|
nextSyncTime = sysTime; |
||||||
|
now(); // this will sync the clock
|
||||||
|
} |
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR
|
||||||
|
setSyncInterval(time_t interval){ // set the number of seconds between re-sync
|
||||||
|
syncInterval = interval; |
||||||
|
} |
@ -0,0 +1,130 @@ |
|||||||
|
#ifndef _TIME_H_ |
||||||
|
#define _TIME_H_ |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
* Constants. |
||||||
|
*/ |
||||||
|
#define SECS_PER_MIN (60UL) |
||||||
|
#define SECS_PER_HOUR (3600UL) |
||||||
|
#define SECS_PER_DAY (SECS_PER_HOUR * 24UL) |
||||||
|
#define DAYS_PER_WEEK (7UL) |
||||||
|
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK) |
||||||
|
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL) |
||||||
|
#define SECS_YR_2000 (946684800UL) // The time_t value at the very start of Y2K.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Days per month. |
||||||
|
* Note that the standard time.h starts months from "1", but this |
||||||
|
* version starts from "0". |
||||||
|
*/ |
||||||
|
static const uint8_t monthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Standard "struct tm" equivalent. |
||||||
|
* =NOTE= Differences:- |
||||||
|
* - Year offset is from 1970 (standard tm is from 1900). |
||||||
|
* - Wday Sunday is day "1" (standard tm has it as day "0"). |
||||||
|
* - The "Day" field is what standard tm refers to as "yday". |
||||||
|
* - There is no "date" or Day-of-Month field. |
||||||
|
* - There is no "isdst" field. |
||||||
|
*/ |
||||||
|
struct tmElements { |
||||||
|
uint8_t Second; |
||||||
|
uint8_t Minute; |
||||||
|
uint8_t Hour; |
||||||
|
uint8_t Wday; // Day of week, with Sunday as day 1.
|
||||||
|
uint8_t Day; // This is "yday" - Day of Year.
|
||||||
|
uint8_t Month; |
||||||
|
uint8_t Year; // Offset from 1970.
|
||||||
|
}; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
timeNotSet, timeNeedsSync, timeSet |
||||||
|
} timeStatus_t; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday |
||||||
|
} timeDayOfWeek_t; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
tmSecond, tmMinute, tmHour, tmWday, tmDay, tmMonth, tmYear, tmNbrFields |
||||||
|
} tmByteFields; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
uint8_t Second; |
||||||
|
uint8_t Minute; |
||||||
|
uint8_t Hour; |
||||||
|
uint8_t Wday; // day of week, sunday is day 1
|
||||||
|
uint8_t Day; |
||||||
|
uint8_t Month; |
||||||
|
uint8_t Year; // offset from 1970;
|
||||||
|
} tmElements_t, TimeElements, *tmElementsPtr_t; |
||||||
|
|
||||||
|
// This leap year calulator expects year argument as years offset from 1970.
|
||||||
|
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience macros to convert to and from tm years. |
||||||
|
*/ |
||||||
|
#define tmYearToCalendar(Y) ((Y) + 1970) // Full, four-digit year.
|
||||||
|
#define CalendarYrToTm(Y) ((Y) - 1970) |
||||||
|
#define tmYearToY2k(Y) ((Y) - 30) // Offset from 2000.
|
||||||
|
#define y2kYearToTm(Y) ((Y) + 30) |
||||||
|
|
||||||
|
typedef time_t(*getExternalTime)(); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Useful Macros for getting elapsed time values. |
||||||
|
*/ |
||||||
|
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) |
||||||
|
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) |
||||||
|
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR) |
||||||
|
#define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday.
|
||||||
|
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // The number of days since Jan 1 1970.
|
||||||
|
#define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // The number of seconds since last midnight.
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971. |
||||||
|
* Always set the correct time before setting alarms. |
||||||
|
*/ |
||||||
|
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // Time at the start of the given day.
|
||||||
|
#define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // Time at the end of the given day.
|
||||||
|
#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // Week starts on day 1.
|
||||||
|
#define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // Time at the start of the week for the given time.
|
||||||
|
#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // Time at the end of the week for the given time.
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Useful Macros for converting elapsed time to a time_t value. |
||||||
|
*/ |
||||||
|
#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN) |
||||||
|
#define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR) |
||||||
|
#define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // Fixed on Jul 22 2011.
|
||||||
|
#define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK) |
||||||
|
|
||||||
|
/*
|
||||||
|
* Time and date function defines. |
||||||
|
* Low-level functions to convert to and from system time. |
||||||
|
*/ |
||||||
|
int16_t totalMinutes(int16_t hours, int8_t minutes); |
||||||
|
void breakTime(time_t time, tmElements_t* tm); |
||||||
|
void timet_to_tm(time_t time, struct tmElements *tmel); // Convert a time_t value into "struct tm" elements.
|
||||||
|
time_t tm_to_timet(struct tmElements *tmel); // Reconstitute "struct tm" elements into a time_t value.
|
||||||
|
time_t now(); |
||||||
|
void setTime(time_t t); |
||||||
|
int hour(); |
||||||
|
int minute(); |
||||||
|
int second(); |
||||||
|
void time_init(); |
||||||
|
unsigned long micros(); |
||||||
|
unsigned long millis(); |
||||||
|
|
||||||
|
/* time sync functions */ |
||||||
|
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
|
||||||
|
void setSyncProvider(getExternalTime getTimeFunction); // identify the external time provider
|
||||||
|
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
|
||||||
|
|
||||||
|
#endif // _TIME_H_
|
@ -0,0 +1,37 @@ |
|||||||
|
#include "user_funcs.h" |
||||||
|
|
||||||
|
bool ICACHE_FLASH_ATTR pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, uint8_t offHour, uint8_t offMinute) { |
||||||
|
uint16_t NumMinsToday = totalMinutes(hour(), minute()); |
||||||
|
bool state = false; |
||||||
|
|
||||||
|
if (totalMinutes(offHour, offMinute) > totalMinutes(onHour, onMinute)) { |
||||||
|
state = (NumMinsToday >= totalMinutes(onHour, onMinute)) ? true : false; |
||||||
|
|
||||||
|
if (NumMinsToday >= totalMinutes(offHour, offMinute)) |
||||||
|
state = false; |
||||||
|
} |
||||||
|
else { |
||||||
|
state = (NumMinsToday >= totalMinutes(offHour, offMinute)) ? false : true; |
||||||
|
|
||||||
|
if (NumMinsToday >= totalMinutes(onHour, onMinute)) |
||||||
|
state = true; |
||||||
|
} |
||||||
|
return state; |
||||||
|
} |
||||||
|
|
||||||
|
const char* ICACHE_FLASH_ATTR byteToBin(uint8_t num) { |
||||||
|
static char b[9]; |
||||||
|
b[0] = '\0'; |
||||||
|
|
||||||
|
int z; |
||||||
|
for (z = 128; z > 0; z >>= 1) { |
||||||
|
strcat(b, ((num & z) == z) ? "1" : "0"); |
||||||
|
} |
||||||
|
return b; |
||||||
|
} |
||||||
|
|
||||||
|
const uint8_t ICACHE_FLASH_ATTR binToByte(char* bin_str) { |
||||||
|
char * tmp; |
||||||
|
long x = strtol(bin_str, &tmp, 2); |
||||||
|
return (x <= 255) ? (uint8_t)x : -1; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
#ifndef _USER_FUNCS_H_ |
||||||
|
#define _USER_FUNCS_H_ |
||||||
|
#include <esp8266.h> |
||||||
|
#include <Time.h> |
||||||
|
|
||||||
|
bool pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, uint8_t offHour, uint8_t offMinute); |
||||||
|
const char* byteToBin(uint8_t num); |
||||||
|
const uint8_t binToByte(char* bin_str); |
||||||
|
|
||||||
|
#endif // _USER_FUNCS_H_
|
@ -0,0 +1,158 @@ |
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright 2013-2014 Espressif Systems (Wuxi) |
||||||
|
* |
||||||
|
* FileName: user_json.c |
||||||
|
* |
||||||
|
* Description: JSON format set up and parse. |
||||||
|
* Check your hardware transmation while use this data format. |
||||||
|
* |
||||||
|
* Modification history: |
||||||
|
* 2014/5/09, v1.0 create this file. |
||||||
|
*******************************************************************************/ |
||||||
|
#include "user_json.h" |
||||||
|
|
||||||
|
LOCAL char* json_buf; |
||||||
|
LOCAL int pos; |
||||||
|
LOCAL int size; |
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* FunctionName : find_json_path |
||||||
|
* Description : find the JSON format tree's path |
||||||
|
* Parameters : json -- A pointer to a JSON set up |
||||||
|
* path -- A pointer to the JSON format tree's path |
||||||
|
* Returns : A pointer to the JSON format tree |
||||||
|
*******************************************************************************/ |
||||||
|
struct jsontree_value*ICACHE_FLASH_ATTR |
||||||
|
find_json_path(struct jsontree_context* json, const char* path) { |
||||||
|
struct jsontree_value* v; |
||||||
|
const char* start; |
||||||
|
const char* end; |
||||||
|
int len; |
||||||
|
|
||||||
|
v = json->values[0]; |
||||||
|
start = path; |
||||||
|
|
||||||
|
do { |
||||||
|
end = (const char *)os_strstr(start, "/"); |
||||||
|
|
||||||
|
if (end == start) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (end != NULL) { |
||||||
|
len = end - start; |
||||||
|
end++; |
||||||
|
} |
||||||
|
else { |
||||||
|
len = os_strlen(start); |
||||||
|
} |
||||||
|
|
||||||
|
if (v->type != JSON_TYPE_OBJECT) { |
||||||
|
v = NULL; |
||||||
|
} |
||||||
|
else { |
||||||
|
struct jsontree_object* o; |
||||||
|
int i; |
||||||
|
|
||||||
|
o = (struct jsontree_object *)v; |
||||||
|
v = NULL; |
||||||
|
|
||||||
|
for (i = 0; i < o->count; i++) { |
||||||
|
if (os_strncmp(start, o->pairs[i].name, len) == 0) { |
||||||
|
v = o->pairs[i].value; |
||||||
|
json->index[json->depth] = i; |
||||||
|
json->depth++; |
||||||
|
json->values[json->depth] = v; |
||||||
|
json->index[json->depth] = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
start = end; |
||||||
|
} |
||||||
|
while (end != NULL && *end != '\0' && v != NULL); |
||||||
|
|
||||||
|
json->callback_state = 0; |
||||||
|
return v; |
||||||
|
} |
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* FunctionName : json_putchar |
||||||
|
* Description : write the value to the JSON format tree |
||||||
|
* Parameters : c -- the value which write the JSON format tree |
||||||
|
* Returns : result |
||||||
|
*******************************************************************************/ |
||||||
|
int ICACHE_FLASH_ATTR |
||||||
|
json_putchar(int c) { |
||||||
|
if (json_buf != NULL && pos <= size) { |
||||||
|
json_buf[pos++] = c; |
||||||
|
return c; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* FunctionName : json_ws_send |
||||||
|
* Description : set up the JSON format tree for string |
||||||
|
* Parameters : tree -- A pointer to the JSON format tree |
||||||
|
* path -- A pointer to the JSON format tree's path |
||||||
|
* pbuf -- A pointer for the data sent |
||||||
|
* Returns : none |
||||||
|
*******************************************************************************/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
json_ws_send(struct jsontree_value* tree, const char* path, char* pbuf) { |
||||||
|
struct jsontree_context json; |
||||||
|
/* maxsize = 128 bytes */ |
||||||
|
json_buf = (char *)os_malloc(JSON_SIZE); |
||||||
|
|
||||||
|
/* reset state and set max-size */ |
||||||
|
/* NOTE: packet will be truncated at 512 bytes */ |
||||||
|
pos = 0; |
||||||
|
size = JSON_SIZE; |
||||||
|
|
||||||
|
json.values[0] = (struct jsontree_value *)tree; |
||||||
|
jsontree_reset(&json); |
||||||
|
find_json_path(&json, path); |
||||||
|
json.path = json.depth; |
||||||
|
json.putchar = json_putchar; |
||||||
|
|
||||||
|
while (jsontree_print_next(&json) && json.path <= json.depth); |
||||||
|
|
||||||
|
json_buf[pos] = 0; |
||||||
|
os_memcpy(pbuf, json_buf, pos); |
||||||
|
os_free(json_buf); |
||||||
|
} |
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* FunctionName : json_parse |
||||||
|
* Description : parse the data as a JSON format |
||||||
|
* Parameters : js_ctx -- A pointer to a JSON set up |
||||||
|
* ptrJSONMessage -- A pointer to the data |
||||||
|
* Returns : none |
||||||
|
*******************************************************************************/ |
||||||
|
void ICACHE_FLASH_ATTR |
||||||
|
json_parse(struct jsontree_context* json, char* ptrJSONMessage) { |
||||||
|
/* Set value */ |
||||||
|
struct jsontree_value* v; |
||||||
|
struct jsontree_callback* c; |
||||||
|
struct jsontree_callback* c_bak = NULL; |
||||||
|
|
||||||
|
while ((v = jsontree_find_next(json, JSON_TYPE_CALLBACK)) != NULL) { |
||||||
|
c = (struct jsontree_callback *)v; |
||||||
|
|
||||||
|
if (c == c_bak) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
c_bak = c; |
||||||
|
|
||||||
|
if (c->set != NULL) { |
||||||
|
struct jsonparse_state js; |
||||||
|
|
||||||
|
jsonparse_setup(&js, ptrJSONMessage, os_strlen(ptrJSONMessage)); |
||||||
|
c->set(json, &js); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
#ifndef __USER_JSON_H__ |
||||||
|
#define __USER_JSON_H__ |
||||||
|
|
||||||
|
#include <esp8266.h> |
||||||
|
#include "json/jsonparse.h" |
||||||
|
#include "json/jsontree.h" |
||||||
|
|
||||||
|
#define JSON_SIZE 1024 |
||||||
|
void json_parse(struct jsontree_context* json, char* ptrJSONMessage); |
||||||
|
void json_ws_send(struct jsontree_value* tree, const char* path, char* pbuf); |
||||||
|
int json_putchar(int c); |
||||||
|
struct jsontree_value* find_json_path(struct jsontree_context* json, const char* path); |
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue