parent
be3c0fe926
commit
aaec5323cd
@ -1,96 +1,2 @@ |
||||
# esp-httpd README # |
||||
|
||||
This is the demonstration project for the small but powerful libesphttpd webserver |
||||
for ESP8266(EX) chips. It is an example of how to make a module that can have |
||||
the AP it connects to configured over a webbrowser. It also illustrates multiple |
||||
flash layouts and some OTA update functionality. |
||||
|
||||
## ABOUT THE WEBSERVER ## |
||||
|
||||
The Good (aka: what's awesome) |
||||
- Supports multiple connections, for eg simultaneous html/css/js/images downloading |
||||
- Static files stored in flash, in an (optionally compressed) RO filesystem |
||||
- Pluggable using external cgi routines |
||||
- Simple template engine for mixed c and html things |
||||
- Usable as an embedded library - should be easy to drop into your existing projects |
||||
- Includes websocket support |
||||
|
||||
The Bad (aka: what can be improved) |
||||
- Not built for speediness, although it's reasonable fast. |
||||
- Built according to what I remember of the HTTP protocol, not according to the |
||||
RFCs. Should work with most modern browsers, though. |
||||
- No support for https. |
||||
|
||||
The Ugly (aka: bugs, misbehaviour) |
||||
- Possible buffer overflows (usually not remotely exploitable) due to no os_snprintf |
||||
This can be theoretically remedied by either Espressif including an os_snprintf in |
||||
their libs or by using some alternate printf lib, like elm-chans xprintf |
||||
|
||||
## SOURCE OF THIS CODE ## |
||||
The official esphttpd repo lives at http://git.spritesserver.nl/esphttpd.git/ and |
||||
http://git.spritesserver.nl/libesphttpd.git/ . If you're a fan of Github, you can also |
||||
peruse the official mirror at https://github.com/Spritetm/esphttpd and https://github.com/Spritetm/libesphttpd . If |
||||
you want to discuss this code, there is a subforum at esp8266.com: http://www.esp8266.com/viewforum.php?f=34 . |
||||
|
||||
|
||||
## ABOUT THE EXAMPLE ## |
||||
|
||||
When you flash the example into an ESP8266(EX) module, you get a small webserver with a few example |
||||
pages. If you've already connected your module to your WLAN before, it'll keep those settings. When |
||||
you haven't or the settings are wrong, keep GPIO0 for >5 seconds. The module will reboot into |
||||
its STA+AP mode. Connect a computer to the newly formed access point and browse to |
||||
http://192.168.4.1/wifi in order to connect the module to your WiFi network. The example also |
||||
allows you to control a LED that's connected to GPIO2. |
||||
|
||||
## BUILDING EVERYTHING ## |
||||
|
||||
For this, you need an environment that can compile ESP8266 firmware. Environments for this still |
||||
are in flux at the moment, but I'm using esp-open-sdk: https://github.com/pfalcon/esp-open-sdk . |
||||
You probably also need an UNIX-like system; I'm working on Debian Linux myself. |
||||
|
||||
To manage the paths to all this, you can source a small shell fragment into your current session. For |
||||
example, I source a file with these contents: |
||||
|
||||
export PATH=${PWD}/esp-open-sdk/xtensa-lx106-elf/bin:$PATH |
||||
export SDK_BASE=${PWD}/esp-open-sdk/sdk |
||||
export ESPTOOL=${PWD}/esptool/esptool.py |
||||
export ESPPORT=/dev/ttyUSB0 |
||||
export ESPBAUD=460800 |
||||
|
||||
Actual setup of the SDK and toolchain is out of the scope of this document, so I hope this helps you |
||||
enough to set up your own if you haven't already. |
||||
|
||||
If you have that, you can clone out the source code: |
||||
git clone http://git.spritesserver.nl/esphttpd.git/ |
||||
|
||||
This project makes use of heatshrink, which is a git submodule. To fetch the code: |
||||
|
||||
cd esphttpd |
||||
git submodule init |
||||
git submodule update |
||||
|
||||
Now, build the code: |
||||
|
||||
make |
||||
|
||||
Depending on the way you built it, esp-open-sdk sometimes patches Espressifs SDK, needing a slightly different |
||||
compiling process. If this is needed, you will get errors during compiling complaining about uint8_t being |
||||
undeclared. If this happens, try building like this: |
||||
|
||||
make USE_OPENSDK=yes |
||||
|
||||
You can also edit the Makefile to change this more permanently. |
||||
|
||||
After the compile process, flash the code happens in 2 steps. First the code itself gets flashed. Reset the module into bootloader |
||||
mode and enter 'make flash'. |
||||
|
||||
The 2nd step is to pack the static files the webserver will serve and flash that. Reset the module into |
||||
bootloader mode again and enter `make htmlflash`. |
||||
|
||||
You should have a working webserver now. |
||||
|
||||
## WRITING CODE FOR THE WEBSERVER ## |
||||
|
||||
Please see the README.md of the libesphttpd project for the programming manual. |
||||
|
||||
|
||||
# esp-vt100-firmware |
||||
ESP8266 Remote Terminal project |
||||
|
@ -0,0 +1,5 @@ |
||||
#!/bin/bash |
||||
|
||||
yuicompressor html_orig/style.css > html/style.css |
||||
yuicompressor html_orig/script.js > html/script.js |
||||
minify --type=html html_orig/index.html -o html/index.html |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 48 KiB |
@ -1,2 +0,0 @@ |
||||
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= |
||||
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){}}; |
@ -1,82 +0,0 @@ |
||||
<html> |
||||
<head><title>Upgrade firmware</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 doReboot() { |
||||
//Grab the /flash/reboot url in order to reboot into the new firmware. |
||||
xhr.open("GET", "reboot"); |
||||
xhr.onreadystatechange=function() { |
||||
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { |
||||
//Reload the window after 5 seconds. |
||||
window.setTimeout(function() { |
||||
location.reload(true); |
||||
}, 5000); |
||||
} |
||||
} |
||||
xhr.send(); |
||||
} |
||||
|
||||
//Sets the progress bar to a specific level. Amt is between 0 and 1. |
||||
function setProgress(amt) { |
||||
$("#progressbarinner").style.width=String(amt*200)+"px"; |
||||
} |
||||
|
||||
//Called when the submit button is called. |
||||
function doUpgrade() { |
||||
//Grab the file, see if it's a real file. |
||||
var f=$("#file").files[0]; |
||||
if (typeof f=='undefined') { |
||||
$("#remark").innerHTML="Can't read file!"; |
||||
return |
||||
} |
||||
xhr.open("POST", "upload"); |
||||
xhr.onreadystatechange=function() { |
||||
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { |
||||
setProgress(1); |
||||
if (xhr.responseText!="") { |
||||
$("#remark").innerHTML="Error: "+xhr.responseText; |
||||
} else { |
||||
$("#remark").innerHTML="Uploading done. Rebooting."; |
||||
doReboot(); |
||||
} |
||||
} |
||||
} |
||||
//If the webbrowser enables it, make progress bar show progress. |
||||
if (typeof xhr.upload.onprogress != 'undefined') { |
||||
xhr.upload.onprogress=function(e) { |
||||
setProgress(e.loaded / e.total); |
||||
} |
||||
} |
||||
//Upload the file |
||||
xhr.send(f); |
||||
return false; |
||||
} |
||||
|
||||
|
||||
window.onload=function(e) { |
||||
//Grab the initial remark telling user which userx.bin file to upload. |
||||
xhr.open("GET", "next"); |
||||
xhr.onreadystatechange=function() { |
||||
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { |
||||
var txt="Please upload "+xhr.responseText+" or ota file."; |
||||
$("#remark").innerHTML=txt; |
||||
setProgress(0); |
||||
} |
||||
} |
||||
xhr.send(); |
||||
} |
||||
|
||||
</script> |
||||
</head> |
||||
<body> |
||||
<div id="main"> |
||||
<h1>Update firmware</h1> |
||||
<div id="remark">Loading...</div> |
||||
<input type="file" id="file" /> |
||||
<input type="submit" value="Upgrade!" onclick="doUpgrade()" /> |
||||
<div id="progressbar"><div id="progressbarinner"></div></div> |
||||
</body> |
@ -1,34 +0,0 @@ |
||||
|
||||
body { |
||||
background-color: #404040; |
||||
font-family: sans-serif; |
||||
} |
||||
|
||||
#main { |
||||
background-color: #d0d0FF; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
border: 2px solid #000000; |
||||
width: 800px; |
||||
margin: 0 auto; |
||||
padding: 20px |
||||
} |
||||
|
||||
#progressbar { |
||||
margin: 10px; |
||||
padding: 0; |
||||
border: 1px solid #000000; |
||||
height: 20px; |
||||
width: 200px; |
||||
background-color: #808080; |
||||
} |
||||
|
||||
#progressbarinner { |
||||
width: 10px; |
||||
height: 20px; |
||||
border: none; |
||||
background-color: #00ff00; |
||||
} |
||||
|
||||
|
@ -0,0 +1 @@ |
||||
<!doctype html><meta charset=utf-8><title>ESP8266 Remote Terminal</title><meta name=viewport content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel=stylesheet href=style.css><script src=script.js></script><header>ESP8266 Remote Terminal</header><div id=screen></div><div id=buttons><button>1</button><button>2</button><button>3</button><button>4</button></div><script>init()</script> |
@ -1,28 +0,0 @@ |
||||
<html> |
||||
<head><title>Esp8266 web server</title> |
||||
<link rel="stylesheet" type="text/css" href="style.css"> |
||||
</head> |
||||
<body> |
||||
<div id="main"> |
||||
<h1>It Works</h1> |
||||
<p> |
||||
If you see this, it means the tiny li'l website in your ESP8266 does actually work. Fyi, this page has |
||||
been loaded <b>%counter%</b> times. |
||||
<ul> |
||||
<li>If you haven't connected this device to your WLAN network now, you can <a href="/wifi">do so.</a></li> |
||||
<li>You can also control the <a href="led.tpl">LED</a>.</li> |
||||
<li>If you have a firmware upgrade file and the module is compiled for firmware upgrades, you |
||||
can <a href="flash/index.html">upgrade the firmware</a> of this module.</li> |
||||
<li>Esphttpd now also supports <a href="websocket/index.html">websockets</a>.</li> |
||||
<li>Test esphttpd using the built-in <a href="test/">test suite</a></li> |
||||
<li>And because I can, here's a link to my <a href="http://spritesmods.com/?f=esphttpd">website</a></ul> |
||||
</ul> |
||||
</p> |
||||
|
||||
<p>And because we're on the Internets now, here are the required pictures of cats:<br /> |
||||
<img src="cats/cross-eyed-cat.jpg"><br /> |
||||
<img src="cats/junge-katze-iv.jpg"><br /> |
||||
<img src="cats/kitten-loves-toy.jpg"><br /> |
||||
</p> |
||||
</div> |
||||
</body></html> |
@ -1,15 +0,0 @@ |
||||
<html><head><title>Test</title> |
||||
<link rel="stylesheet" type="text/css" href="style.css"> |
||||
</head> |
||||
<body> |
||||
<div id="main"> |
||||
<h1>The LED</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> |
@ -0,0 +1 @@ |
||||
var $=function(d,c){d=d.match(/^(\W)?(.*)/);return(c||document)["getElement"+(d[1]?d[1]=="#"?"ById":"sByClassName":"sByTagName")](d[2])};var m=function(e,d,f){d=document;f=d.createElement("p");f.innerHTML=e;e=d.createDocumentFragment();while(d=f.firstChild){e.appendChild(d)}return e};(function(){function a(r){return document.createElement(r)}var b=26,k=10;var l={a:false,x:0,y:0,suppress:false,hidden:false};var o=[];var g=["#111213","#CC0000","#4E9A06","#C4A000","#3465A4","#75507B","#06989A","#D3D7CF","#555753","#EF2929","#8AE234","#FCE94F","#729FCF","#AD7FA8","#34E2E2","#EEEEEC"];function p(){o.forEach(function(r,s){r.t=" ";r.fg=7;r.bg=0;d(r)})}function c(s,r){return o[s*b+r]}function i(){return c(l.y,l.x)}function q(r){l.hidden=!r;l.a&=r;d(i(),l.a)}function f(s,r){l.suppress=true;d(i(),false);l.x=r;l.y=s;l.suppress=false;d(i(),l.a)}function d(s,r){var v=s.e,t,u;t=r?s.bg:s.fg;u=r?s.fg:s.bg;v.innerText=(s.t+" ")[0];v.style.color=e(t);v.style.backgroundColor=e(u);v.style.fontWeight=t>7?"bold":"normal"}function h(){o.forEach(function(s,t){var r=l.a&&(t==l.y*b+l.x);d(s,r)})}function j(s){if(s.length!=b*k*3){throw"Bad data format."}for(var t=0;t<b*k;t++){var r=o[t];r.t=s[t*3];r.fg=parseInt(s[t*3+1],16);r.bg=parseInt(s[t*3+2],16)}h()}function e(r){var r=parseInt(r);if(r<0||r>15){r=0}return g[r]}function n(){var u,t=$("#screen");for(var s=0;s<b*k;s++){u=a("span");if((s>0)&&(s%b==0)){t.appendChild(a("br"))}t.appendChild(u);var r={t:" ",fg:7,bg:0,e:u};o.push(r);d(r)}setInterval(function(){l.a=!l.a;if(l.hidden){l.a=false}if(!l.suppress){d(i(),l.a)}},500)}window.Term={init:n,load:j,setCursor:f,enableCursor:q,clear:p}})();(function(){var g="ws://"+window.location.host+"/ws/update.cgi";var d;function c(i){console.log("CONNECTED")}function b(i){console.error("SOCKET CLOSED")}function f(i){console.log("Message received!",i.data)}function e(i){console.error(i.data)}function a(i){d.send(i)}function h(){d=new WebSocket(g);d.onopen=c;d.onclose=b;d.onmessage=f;d.onerror=e;console.log("Opening socket.")}window.Conn={ws:null,init:h}})();function init(){Term.init();Conn.init()}; |
@ -1,17 +1 @@ |
||||
|
||||
body { |
||||
background-color: #404040; |
||||
font-family: sans-serif; |
||||
} |
||||
|
||||
#main { |
||||
background-color: #d0d0FF; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
border: 2px solid #000000; |
||||
width: 800px; |
||||
margin: 0 auto; |
||||
padding: 20px |
||||
} |
||||
|
||||
html,body{background:#48505f;color:#eee;font-family:monospace;font-size:16pt;text-align:center}header{font-weight:bold;font-size:14pt;padding:6px;display:block}#screen{display:block;white-space:nowrap;background:#111213;border-radius:3px;padding:6px;box-shadow:inset 0 0 5px black;display:inline-block}#screen span{white-space:pre}#buttons{margin-top:10px}button{margin:0 2px;padding:10px 0;width:22%;max-width:80px;cursor:pointer} |
@ -1,9 +0,0 @@ |
||||
<html><head><title>Webserver test</title></head> |
||||
<link rel="stylesheet" type="text/css" href="../wifi/style.css"> |
||||
<script type="text/javascript" src="../wifi/140medley.min.js"></script> |
||||
<script type="text/javascript" src="test.js"></script> |
||||
<body> |
||||
<div id="main"> |
||||
<div id="log">Initializing test...</div> |
||||
</div> |
||||
</body> |
@ -1,205 +0,0 @@ |
||||
|
||||
/* |
||||
Code to test the webserver. This depends on: |
||||
- The cat images being available, for concurrent espfs testing |
||||
- the test.cgi script available, for generic data mangling tests |
||||
|
||||
|
||||
This test does a max of 4 requests in parallel. The nonos SDK supports a max of |
||||
5 connections; the default libesphttpd setting is 4 sockets at a time. Unfortunately, |
||||
the nonos sdk just closes all sockets opened after the available sockets are opened, |
||||
instead of queueing them until a socket frees up. |
||||
*/ |
||||
|
||||
|
||||
function log(line) { |
||||
$("#log").insertAdjacentHTML('beforeend', line+'<br />'); |
||||
} |
||||
|
||||
|
||||
//Load an image multiple times in parallel
|
||||
function testParLdImg(url, ct, doneFn) { |
||||
var im=[]; |
||||
var state={"loaded":0, "count":ct, "doneFn": doneFn, "error":false}; |
||||
for (var x=0; x<ct; x++) { |
||||
im[x]=new Image(); |
||||
im[x].onload=function(no) { |
||||
log("File "+no+" loaded successfully."); |
||||
this.loaded++; |
||||
if (this.loaded==this.count) this.doneFn(!this.error); |
||||
}.bind(state, x); |
||||
im[x].onerror=function(no) { |
||||
log("Error loading image "+no+"!"); |
||||
this.loaded++; |
||||
this.error++; |
||||
if (this.loaded==this.count) this.doneFn(!this.error); |
||||
}.bind(state, x); |
||||
im[x].src=url+"?"+Math.floor(Math.random()*100000).toString(); |
||||
} |
||||
} |
||||
|
||||
function testDownloadCgi(len, doneFn) { |
||||
var xhr=j(); |
||||
var state={"len":len, "doneFn":doneFn, "ts": Date.now()}; |
||||
xhr.open("GET", "test.cgi?len="+len+"&nocache="+Math.floor(Math.random()*100000).toString()); |
||||
xhr.onreadystatechange=function() { |
||||
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { |
||||
if (xhr.response.length==this.len) { |
||||
log("Downloaded "+this.len+" bytes successfully."); |
||||
this.doneFn(true); |
||||
} else { |
||||
log("Downloaded "+xhr.response.length+" bytes successfully, but needed "+this.len+"!"); |
||||
this.doneFn(false); |
||||
} |
||||
} else if (xhr.readyState==4) { |
||||
log("Failed! Error "+xhr.status); |
||||
this.doneFn(false); |
||||
} |
||||
}.bind(state); |
||||
//If the webbrowser enables it, show progress.
|
||||
if (typeof xhr.onprogress != 'undefined') { |
||||
xhr.onprogress=function(e) { |
||||
if (Date.now()>this.ts+2000) { |
||||
log("..."+Math.floor(e.loaded*100/this.len).toString()+"%"); |
||||
this.ts=Date.now(); |
||||
} |
||||
}.bind(state); |
||||
} |
||||
xhr.send(); |
||||
} |
||||
|
||||
|
||||
function testUploadCgi(len, doneFn) { |
||||
var xhr=j(); |
||||
var state={"len":len, "doneFn":doneFn, "ts": Date.now()}; |
||||
var data=""; |
||||
for (var x=0; x<len; x++) data+="X"; |
||||
xhr.open("POST", "test.cgi"); |
||||
xhr.onreadystatechange=function() { |
||||
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { |
||||
var ulen=parseInt(xhr.responseText); |
||||
if (ulen==this.len) { |
||||
log("Uploaded "+this.len+" bytes successfully."); |
||||
this.doneFn(true); |
||||
} else { |
||||
log("Webserver received "+ulen+" bytes successfully, but sent "+this.len+"!"); |
||||
this.doneFn(false); |
||||
} |
||||
} else if (xhr.readyState==4) { |
||||
log("Failed! Error "+xhr.status); |
||||
this.doneFn(false); |
||||
} |
||||
}.bind(state); |
||||
//If the webbrowser enables it, show progress.
|
||||
if (typeof xhr.upload.onprogress != 'undefined') { |
||||
xhr.upload.onprogress=function(e) { |
||||
if (Date.now()>this.ts+2000) { |
||||
log("..."+Math.floor(e.loaded*100/e.total).toString()+"%"); |
||||
this.ts=Date.now(); |
||||
} |
||||
}.bind(state); |
||||
} |
||||
//Upload the file
|
||||
xhr.send(data); |
||||
} |
||||
|
||||
function hammerNext(state, xhr) { |
||||
if (state.done==state.count) { |
||||
state.doneFn(!state.error); |
||||
} |
||||
if (state.started==state.count) return; |
||||
xhr.open("GET", "test.cgi?len="+state.len+"&nocache="+Math.floor(Math.random()*100000).toString()); |
||||
xhr.onreadystatechange=function(xhr) { |
||||
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { |
||||
if (xhr.response.length==this.len) { |
||||
state.done++; |
||||
hammerNext(this, xhr); |
||||
} else { |
||||
log("Downloaded "+xhr.response.length+" bytes successfully, but needed "+this.len+"!"); |
||||
state.done++; |
||||
hammerNext(this, xhr); |
||||
} |
||||
} else if (xhr.readyState==4) { |
||||
log("Failed! Error "+xhr.status); |
||||
state.done++; |
||||
hammerNext(this, xhr); |
||||
} |
||||
}.bind(state, xhr); |
||||
//If the webbrowser enables it, show progress.
|
||||
if (typeof xhr.onprogress != 'undefined') { |
||||
xhr.onprogress=function(e) { |
||||
if (Date.now()>this.ts+2000) { |
||||
log("..."+state.done+"/"+state.count); |
||||
this.ts=Date.now(); |
||||
} |
||||
}.bind(state); |
||||
} |
||||
state.started++; |
||||
xhr.send(); |
||||
} |
||||
|
||||
function testHammer(count, par, len, doneFn) { |
||||
var state={"count":count, "started":0, "done":0, "par":par, "len":len, "doneFn":doneFn, "ts": Date.now(), "error":false}; |
||||
var xhr=[]; |
||||
for (var i=0; i<par; i++) { |
||||
xhr[i]=j(); |
||||
hammerNext(state, xhr[i]); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
var tstState=0; |
||||
var successCnt=0; |
||||
|
||||
function nextTest(lastOk) { |
||||
if (tstState!=0) { |
||||
if (lastOk) { |
||||
log("<b>Success!</b>");
|
||||
successCnt++; |
||||
} else { |
||||
log("<b>Test failed!</b>"); |
||||
} |
||||
} |
||||
tstState++; |
||||
if (tstState==1) { |
||||
log("Testing parallel load of espfs files..."); |
||||
testParLdImg("../cats/kitten-loves-toy.jpg", 3, nextTest); |
||||
} else if (tstState==2) { |
||||
log("Testing GET request of 32K..."); |
||||
testDownloadCgi(32*1024, nextTest); |
||||
} else if (tstState==3) { |
||||
log("Testing GET request of 128K..."); |
||||
testDownloadCgi(128*1024, nextTest); |
||||
} else if (tstState==4) { |
||||
log("Testing GET request of 512K..."); |
||||
testDownloadCgi(512*1024, nextTest); |
||||
} else if (tstState==5) { |
||||
log("Testing POST request of 512 bytes..."); |
||||
testUploadCgi(512, nextTest); |
||||
} else if (tstState==6) { |
||||
log("Testing POST request of 16K bytes..."); |
||||
testUploadCgi(16*1024, nextTest); |
||||
} else if (tstState==7) { |
||||
log("Testing POST request of 512K bytes..."); |
||||
testUploadCgi(512*1024, nextTest); |
||||
} else if (tstState==8) { |
||||
log("Hammering webserver with 500 requests of size 512..."); |
||||
testHammer(500, 3, 512, nextTest); |
||||
} else if (tstState==9) { |
||||
log("Hammering webserver with 500 requests of size 2048..."); |
||||
testHammer(500, 3, 2048, nextTest); |
||||
} else { |
||||
log("Tests done! "+successCnt+" out of "+(tstState-1)+" tests were successful."); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
window.onload=function(e) { |
||||
log("Starting tests."); |
||||
nextTest(false); |
||||
} |
||||
|
||||
|
||||
|
@ -1,69 +0,0 @@ |
||||
<!DOCTYPE html> |
||||
|
||||
<meta charset="utf-8" /> |
||||
|
||||
<title>WebSocket Test</title> |
||||
|
||||
<script language="javascript" type="text/javascript"> |
||||
|
||||
var wsUri = "ws://"+window.location.host+"/websocket/ws.cgi"; |
||||
var output; |
||||
|
||||
function init() |
||||
{ |
||||
output = document.getElementById("output"); |
||||
testWebSocket(); |
||||
} |
||||
|
||||
function testWebSocket() |
||||
{ |
||||
websocket = new WebSocket(wsUri); |
||||
websocket.onopen = function(evt) { onOpen(evt) }; |
||||
websocket.onclose = function(evt) { onClose(evt) }; |
||||
websocket.onmessage = function(evt) { onMessage(evt) }; |
||||
websocket.onerror = function(evt) { onError(evt) }; |
||||
} |
||||
|
||||
function onOpen(evt) |
||||
{ |
||||
writeToScreen("CONNECTED"); |
||||
doSend("WebSocket rocks"); |
||||
} |
||||
|
||||
function onClose(evt) |
||||
{ |
||||
writeToScreen("DISCONNECTED"); |
||||
} |
||||
|
||||
function onMessage(evt) |
||||
{ |
||||
writeToScreen('<span style="color: blue;">RECEIVED: ' + evt.data+'</span>'); |
||||
// websocket.close(); |
||||
} |
||||
|
||||
function onError(evt) |
||||
{ |
||||
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); |
||||
} |
||||
|
||||
function doSend(message) |
||||
{ |
||||
writeToScreen("SENT: " + message); |
||||
websocket.send(message); |
||||
} |
||||
|
||||
function writeToScreen(message) |
||||
{ |
||||
var pre = document.createElement("p"); |
||||
pre.style.wordWrap = "break-word"; |
||||
pre.innerHTML = message; |
||||
output.appendChild(pre); |
||||
} |
||||
|
||||
window.addEventListener("load", init, false); |
||||
|
||||
</script> |
||||
|
||||
<h2>WebSocket Test</h2> |
||||
|
||||
<div id="output"></div> |
@ -1,2 +0,0 @@ |
||||
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= |
||||
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){}}; |
@ -1,43 +0,0 @@ |
||||
<html><head><title>Connecting...</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") { |
||||
$("#status").innerHTML="Preparing to connect..."; |
||||
window.setTimeout(getStatus, 1000); |
||||
} else if (data.status=="success") { |
||||
$("#status").innerHTML="Connected! Got IP "+data.ip+". If you're in the same network, you can access it <a href=\"http://"+data.ip+"/\">here</a>."; |
||||
} else if (data.status=="working") { |
||||
$("#status").innerHTML="Trying to connect to selected access point..."; |
||||
window.setTimeout(getStatus, 1000); |
||||
} else if (data.status=="fail") { |
||||
$("#status").innerHTML="Connection failed. Check password and selected AP.<br /><a href=\"wifi.tpl\">Go Back</a>"; |
||||
} |
||||
} |
||||
} |
||||
xhr.send(); |
||||
} |
||||
|
||||
window.onload=function(e) { |
||||
getStatus(); |
||||
}; |
||||
</script> |
||||
</head> |
||||
<body> |
||||
<div id="main"> |
||||
<h2>Connecting to AP...</h2> |
||||
<p>Status:<br /> |
||||
<div id="status">...</div> |
||||
</p> |
||||
</div> |
||||
</body> |
||||
</html> |
Before Width: | Height: | Size: 914 B |
@ -1,24 +0,0 @@ |
||||
|
||||
body { |
||||
background-color: #404040; |
||||
font-family: sans-serif; |
||||
} |
||||
|
||||
#main { |
||||
background-color: #d0d0FF; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
border: 2px solid #000000; |
||||
width: 800px; |
||||
margin: 0 auto; |
||||
padding: 20px |
||||
} |
||||
|
||||
.icon { |
||||
background-image: url("icons.png"); |
||||
background-color: transparent; |
||||
width: 32px; |
||||
height: 32px; |
||||
display: inline-block; |
||||
} |
@ -1,94 +0,0 @@ |
||||
<html><head><title>WiFi connection</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(); |
||||
var currAp="%currSsid%"; |
||||
|
||||
function createInputForAp(ap) { |
||||
if (ap.essid=="" && ap.rssi==0) return; |
||||
var div=document.createElement("div"); |
||||
div.id="apdiv"; |
||||
var rssi=document.createElement("div"); |
||||
var rssiVal=-Math.floor(ap.rssi/51)*32; |
||||
rssi.className="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="icon"; |
||||
encrypt.style.backgroundPosition="-32px "+encVal+"px"; |
||||
var input=document.createElement("input"); |
||||
input.type="radio"; |
||||
input.name="essid"; |
||||
input.value=ap.essid; |
||||
if (currAp==ap.essid) input.checked="1"; |
||||
input.id="opt-"+ap.essid; |
||||
var label=document.createElement("label"); |
||||
label.htmlFor="opt-"+ap.essid; |
||||
label.textContent=ap.essid; |
||||
div.appendChild(input); |
||||
div.appendChild(rssi); |
||||
div.appendChild(encrypt); |
||||
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; |
||||
} |
||||
|
||||
|
||||
function scanAPs() { |
||||
xhr.open("GET", "wifiscan.cgi"); |
||||
xhr.onreadystatechange=function() { |
||||
if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) { |
||||
var data=JSON.parse(xhr.responseText); |
||||
currAp=getSelectedEssid(); |
||||
if (data.result.inProgress=="0" && data.result.APs.length>1) { |
||||
$("#aps").innerHTML=""; |
||||
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])); |
||||
} |
||||
window.setTimeout(scanAPs, 20000); |
||||
} else { |
||||
window.setTimeout(scanAPs, 1000); |
||||
} |
||||
} |
||||
} |
||||
xhr.send(); |
||||
} |
||||
|
||||
|
||||
window.onload=function(e) { |
||||
scanAPs(); |
||||
}; |
||||
</script> |
||||
</head> |
||||
<body> |
||||
<div id="main"> |
||||
<p> |
||||
Current WiFi mode: %WiFiMode% |
||||
</p> |
||||
<p> |
||||
Note: %WiFiapwarn% |
||||
</p> |
||||
<form name="wifiform" action="connect.cgi" method="post"> |
||||
<p> |
||||
To connect to a WiFi network, please select one of the detected networks...<br> |
||||
<div id="aps">Scanning...</div> |
||||
<br> |
||||
WiFi password, if applicable: <br /> |
||||
<input type="text" name="passwd" val="%WiFiPasswd%"> <br /> |
||||
<input type="submit" name="connect" value="Connect!"> |
||||
</p> |
||||
</div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,20 @@ |
||||
<!DOCTYPE html> |
||||
|
||||
<meta charset="UTF-8"> |
||||
<title>ESP8266 Remote Terminal</title> |
||||
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"> |
||||
<link rel="stylesheet" href="style.css"> |
||||
<script src="script.js"></script> |
||||
|
||||
<!-- Katedra mereni FEL, 2017 --> |
||||
<!-- TODO GitHub link --> |
||||
|
||||
<header>ESP8266 Remote Terminal</header> |
||||
|
||||
<div id="screen"></div> |
||||
|
||||
<div id="buttons"> |
||||
<button>1</button><button>2</button><button>3</button><button>4</button> |
||||
</div> |
||||
|
||||
<script>init()</script> |
@ -0,0 +1,271 @@ |
||||
//region Libs / utils
|
||||
|
||||
/* |
||||
* 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.
|
||||
) |
||||
}; |
||||
|
||||
/* |
||||
* Create DOM element |
||||
* |
||||
* Usage: |
||||
* var el = m('<h1>Hello</h1>'); |
||||
* document.body.appendChild(el); |
||||
* |
||||
* |
||||
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
||||
* Version 2, December 2004 |
||||
* |
||||
* 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.
|
||||
}; |
||||
|
||||
//endregion
|
||||
|
||||
|
||||
//
|
||||
// Terminal class
|
||||
//
|
||||
(function () { |
||||
function make(e) {return document.createElement(e)} |
||||
|
||||
var W = 26, H = 10; //26, 10
|
||||
var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false}; |
||||
var screen = []; |
||||
|
||||
/** Colors table */ |
||||
var CLR = [// dark gray #2E3436
|
||||
// 0 black, 1 red, 2 green, 3 yellow
|
||||
// 4 blue, 5 mag, 6 cyan, 7 white
|
||||
'#111213','#CC0000','#4E9A06','#C4A000', |
||||
'#3465A4','#75507B','#06989A','#D3D7CF', |
||||
// BRIGHT
|
||||
// 8 black, 9 red, 10 green, 11 yellow
|
||||
// 12 blue, 13 mag, 14 cyan, 15 white
|
||||
'#555753','#EF2929','#8AE234','#FCE94F', |
||||
'#729FCF','#AD7FA8','#34E2E2','#EEEEEC' |
||||
]; |
||||
|
||||
/** Clear screen */ |
||||
function cls() { |
||||
screen.forEach(function(cell, i) { |
||||
cell.t = ' '; |
||||
cell.fg = 7; |
||||
cell.bg = 0; |
||||
blit(cell); |
||||
}); |
||||
} |
||||
|
||||
/** Set text and color at XY */ |
||||
function cellAt(y, x) { |
||||
return screen[y*W+x]; |
||||
} |
||||
|
||||
/** Get cell under cursor */ |
||||
function cursorCell() { |
||||
return cellAt(cursor.y, cursor.x); |
||||
} |
||||
|
||||
/** Enable or disable cursor visibility */ |
||||
function cursorEnable(enable) { |
||||
cursor.hidden = !enable; |
||||
cursor.a &= enable; |
||||
blit(cursorCell(), cursor.a); |
||||
} |
||||
|
||||
/** Safely move cursor */ |
||||
function cursorSet(y, x) { |
||||
// Hide and prevent from showing up during the move
|
||||
cursor.suppress = true; |
||||
blit(cursorCell(), false); |
||||
|
||||
cursor.x = x; |
||||
cursor.y = y; |
||||
|
||||
// Show again
|
||||
cursor.suppress = false; |
||||
blit(cursorCell(), cursor.a); |
||||
} |
||||
|
||||
/** Update cell on display. inv = invert (for cursor) */ |
||||
function blit(cell, inv) { |
||||
var e = cell.e, fg, bg; |
||||
// Colors
|
||||
fg = inv ? cell.bg : cell.fg; |
||||
bg = inv ? cell.fg : cell.bg; |
||||
// Update
|
||||
e.innerText = (cell.t+' ')[0]; |
||||
e.style.color = colorHex(fg); |
||||
e.style.backgroundColor = colorHex(bg); |
||||
e.style.fontWeight = fg > 7 ? 'bold' : 'normal'; |
||||
} |
||||
|
||||
/** Show entire screen */ |
||||
function blitAll() { |
||||
screen.forEach(function(cell, i) { |
||||
/* Invert if under cursor & cursor active */ |
||||
var inv = cursor.a && (i == cursor.y*W+cursor.x); |
||||
blit(cell, inv); |
||||
}); |
||||
} |
||||
|
||||
/** Load screen content from a 'binary' sequence */ |
||||
function load(seq) { |
||||
if (seq.length != W*H*3) throw "Bad data format."; |
||||
|
||||
// primitive format with 3 chars per cell: letter, fg [hex], bg [hex]
|
||||
for (var i = 0; i < W * H; i++) { |
||||
var cell = screen[i]; |
||||
cell.t = seq[i*3]; |
||||
cell.fg = parseInt(seq[i*3+1], 16); |
||||
cell.bg = parseInt(seq[i*3+2], 16); |
||||
} |
||||
|
||||
blitAll(); |
||||
} |
||||
|
||||
/** Parse color */ |
||||
function colorHex(c) { |
||||
var c = parseInt(c); |
||||
if (c < 0 || c > 15) c = 0; |
||||
return CLR[c]; |
||||
} |
||||
|
||||
/** Init the terminal */ |
||||
function init() { |
||||
/* Build screen & show */ |
||||
var e, scr = $('#screen'); |
||||
for(var i = 0; i < W*H; i++) { |
||||
e = make('span'); |
||||
|
||||
/* End of line */ |
||||
if ((i > 0) && (i % W == 0)) { |
||||
scr.appendChild(make('br')); |
||||
} |
||||
/* The cell */ |
||||
scr.appendChild(e); |
||||
|
||||
var cell = {t: ' ', fg: 7, bg: 0, e: e}; |
||||
screen.push(cell); |
||||
blit(cell); |
||||
} |
||||
|
||||
/* Cursor blinking */ |
||||
setInterval(function() { |
||||
cursor.a = !cursor.a; |
||||
if (cursor.hidden) { |
||||
cursor.a = false; |
||||
} |
||||
|
||||
if (!cursor.suppress) { |
||||
blit(cursorCell(), cursor.a); |
||||
} |
||||
}, 500); |
||||
} |
||||
|
||||
// publish
|
||||
window.Term = { |
||||
init: init, |
||||
load: load, |
||||
setCursor: cursorSet, |
||||
enableCursor: cursorEnable, |
||||
clear: cls, |
||||
}; |
||||
})(); |
||||
|
||||
//
|
||||
// Connection class
|
||||
//
|
||||
(function() { |
||||
var wsUri = "ws://"+window.location.host+"/ws/update.cgi"; |
||||
var ws; |
||||
|
||||
function onOpen(evt) { |
||||
console.log("CONNECTED"); |
||||
} |
||||
|
||||
function onClose(evt) { |
||||
console.error("SOCKET CLOSED"); |
||||
} |
||||
|
||||
function onMessage(evt) { |
||||
console.log("Message received!", evt.data); |
||||
// TODO process
|
||||
} |
||||
|
||||
function onError(evt) { |
||||
console.error(evt.data); |
||||
} |
||||
|
||||
function doSend(message) { |
||||
ws.send(message); |
||||
} |
||||
|
||||
function init() { |
||||
ws = new WebSocket(wsUri); |
||||
ws.onopen = onOpen; |
||||
ws.onclose = onClose |
||||
ws.onmessage = onMessage; |
||||
ws.onerror = onError; |
||||
|
||||
console.log("Opening socket."); |
||||
} |
||||
|
||||
window.Conn = { |
||||
ws: null, |
||||
init: init, |
||||
}; |
||||
})(); |
||||
|
||||
function init() { |
||||
Term.init(); |
||||
Conn.init(); |
||||
} |
@ -0,0 +1,40 @@ |
||||
html, body { |
||||
background: #48505F; |
||||
color: #eee; |
||||
font-family: monospace; |
||||
font-size: 16pt; |
||||
text-align: center; |
||||
} |
||||
|
||||
header { |
||||
font-weight: bold; |
||||
font-size: 14pt; |
||||
padding: 6px; |
||||
display: block; |
||||
} |
||||
|
||||
#screen { |
||||
display: block; |
||||
white-space: nowrap; |
||||
background: #111213; |
||||
border-radius: 3px; |
||||
padding: 6px; |
||||
box-shadow: inset 0 0 5px black; |
||||
display: inline-block; |
||||
} |
||||
|
||||
#screen span { |
||||
white-space: pre; |
||||
} |
||||
|
||||
#buttons { |
||||
margin-top: 10px; |
||||
} |
||||
|
||||
button { |
||||
margin: 0 2px; |
||||
padding: 10px 0; |
||||
width: 22%; |
||||
max-width: 80px; |
||||
cursor: pointer; |
||||
} |
@ -0,0 +1,17 @@ |
||||
#pragma once |
||||
#include <c_types.h> |
||||
|
||||
// copied from nodemcu source
|
||||
extern uint32_t system_get_time(); |
||||
extern uint32_t platform_tmr_exists(uint32_t t); |
||||
extern uint32_t system_rtc_clock_cali_proc(); |
||||
extern uint32_t system_get_rtc_time(); |
||||
extern void system_restart(); |
||||
extern void system_soft_wdt_feed(); |
||||
|
||||
// https://github.com/mziwisky/esp8266-dev/blob/master/esphttpd/include/espmissingincludes.h
|
||||
extern int ets_str2macaddr(void *, void *); |
||||
extern void ets_update_cpu_frequency(int freqmhz); |
||||
extern int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); |
||||
//extern uint8 wifi_get_opmode(void);
|
||||
extern void ets_bzero(void *s, size_t n); |
@ -1,86 +0,0 @@ |
||||
/*
|
||||
Cgi routines as used by the tests in the html/test subdirectory. |
||||
*/ |
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------------------- |
||||
* "THE BEER-WARE LICENSE" (Revision 42): |
||||
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
|
||||
* 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.
|
||||
* ---------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
|
||||
#include <esp8266.h> |
||||
#include "cgi-test.h" |
||||
|
||||
|
||||
typedef struct { |
||||
int len; |
||||
int sendPos; |
||||
} TestbedState; |
||||
|
||||
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR cgiTestbed(HttpdConnData *connData) { |
||||
char buff[1024]; |
||||
int first=0; |
||||
int l, x; |
||||
TestbedState *state=(TestbedState*)connData->cgiData; |
||||
|
||||
if (connData->conn==NULL) { |
||||
//Connection aborted. Clean up.
|
||||
if (state) free(state); |
||||
return HTTPD_CGI_DONE; |
||||
} |
||||
|
||||
if (state==NULL) { |
||||
//First call
|
||||
state=malloc(sizeof(TestbedState)); |
||||
memset(state, 0, sizeof(state)); |
||||
connData->cgiData=state; |
||||
first=1; |
||||
} |
||||
|
||||
if (connData->requestType==HTTPD_METHOD_GET) { |
||||
if (first) { |
||||
httpdStartResponse(connData, 200); |
||||
httpdHeader(connData, "content-type", "application/data"); |
||||
httpdEndHeaders(connData); |
||||
l=httpdFindArg(connData->getArgs, "len", buff, sizeof(buff)); |
||||
state->len=1024; |
||||
if (l!=-1) state->len=atoi(buff); |
||||
state->sendPos=0; |
||||
return HTTPD_CGI_MORE; |
||||
} else { |
||||
l=sizeof(buff); |
||||
if (l>(state->len-state->sendPos)) l=(state->len-state->sendPos); |
||||
//Fill with semi-random data
|
||||
for (x=0; x<l; x++) buff[x]=((x^(state->sendPos>>10))&0x1F)+'0'; |
||||
httpdSend(connData, buff, l); |
||||
state->sendPos+=l; |
||||
printf("Test: Uploaded %d/%d bytes\n", state->sendPos, state->len); |
||||
if (state->len<=state->sendPos) { |
||||
if (state) free(state); |
||||
return HTTPD_CGI_DONE;
|
||||
} else { |
||||
return HTTPD_CGI_MORE; |
||||
} |
||||
} |
||||
} |
||||
if (connData->requestType==HTTPD_METHOD_POST) { |
||||
if (connData->post->len!=connData->post->received) { |
||||
//Still receiving data. Ignore this.
|
||||
printf("Test: got %d/%d bytes\n", connData->post->received, connData->post->len); |
||||
return HTTPD_CGI_MORE; |
||||
} else { |
||||
httpdStartResponse(connData, 200); |
||||
httpdHeader(connData, "content-type", "text/plain"); |
||||
httpdEndHeaders(connData); |
||||
l=sprintf(buff, "%d", connData->post->received); |
||||
httpdSend(connData, buff, l); |
||||
return HTTPD_CGI_DONE; |
||||
} |
||||
} |
||||
return HTTPD_CGI_DONE; |
||||
} |
@ -1,8 +0,0 @@ |
||||
#ifndef CGI_TEST_H |
||||
#define CGI_TEST_H |
||||
|
||||
#include "httpd.h" |
||||
|
||||
httpd_cgi_state cgiTestbed(HttpdConnData *connData); |
||||
|
||||
#endif |
@ -1,76 +0,0 @@ |
||||
/*
|
||||
Some random cgi routines. Used in the LED example and the page that returns the entire |
||||
flash as a binary. Also handles the hit counter on the main page. |
||||
*/ |
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------------------- |
||||
* "THE BEER-WARE LICENSE" (Revision 42): |
||||
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
|
||||
* 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.
|
||||
* ---------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
|
||||
#include <esp8266.h> |
||||
#include "cgi.h" |
||||
#include "io.h" |
||||
|
||||
|
||||
//cause I can't be bothered to write an ioGetLed()
|
||||
static char currLedState=0; |
||||
|
||||
//Cgi that turns the LED on or off according to the 'led' param in the POST data
|
||||
httpd_cgi_state 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.
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR tplLed(HttpdConnData *connData, char *token, void **arg) { |
||||
char buff[128]; |
||||
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"); |
||||
} |
||||
} |
||||
httpdSend(connData, buff, -1); |
||||
return HTTPD_CGI_DONE; |
||||
} |
||||
|
||||
static long hitCounter=0; |
||||
|
||||
//Template code for the counter on the index page.
|
||||
httpd_cgi_state ICACHE_FLASH_ATTR tplCounter(HttpdConnData *connData, char *token, void **arg) { |
||||
char buff[128]; |
||||
if (token==NULL) return HTTPD_CGI_DONE; |
||||
|
||||
if (os_strcmp(token, "counter")==0) { |
||||
hitCounter++; |
||||
os_sprintf(buff, "%ld", hitCounter); |
||||
} |
||||
httpdSend(connData, buff, -1); |
||||
return HTTPD_CGI_DONE; |
||||
} |
@ -1,10 +0,0 @@ |
||||
#ifndef CGI_H |
||||
#define CGI_H |
||||
|
||||
#include "httpd.h" |
||||
|
||||
httpd_cgi_state cgiLed(HttpdConnData *connData); |
||||
httpd_cgi_state tplLed(HttpdConnData *connData, char *token, void **arg); |
||||
httpd_cgi_state tplCounter(HttpdConnData *connData, char *token, void **arg); |
||||
|
||||
#endif |
@ -1,7 +1,7 @@ |
||||
#ifndef IO_H |
||||
#define IO_H |
||||
|
||||
void ICACHE_FLASH_ATTR ioLed(int ena); |
||||
void ioLed(int ena); |
||||
void ioInit(void); |
||||
|
||||
#endif |
@ -0,0 +1,28 @@ |
||||
#include <esp8266.h> |
||||
#include "uart_driver.h" |
||||
#include "uart_handler.h" |
||||
|
||||
// Here the bitrates are defined
|
||||
#define UART0_BAUD BIT_RATE_115200 |
||||
#define UART1_BAUD BIT_RATE_115200 |
||||
|
||||
/**
|
||||
* Init the serial ports |
||||
*/ |
||||
void ICACHE_FLASH_ATTR serialInit(void) |
||||
{ |
||||
UART_Init(UART0_BAUD, UART1_BAUD); |
||||
UART_SetPrintPort(UART0); |
||||
UART_SetupAsyncReceiver(); |
||||
} |
||||
|
||||
/**
|
||||
* Handle a byte received from UART. |
||||
* Might do some buffering here maybe |
||||
* |
||||
* @param c |
||||
*/ |
||||
void ICACHE_FLASH_ATTR UART_HandleRxByte(char c) |
||||
{ |
||||
printf("'%c',", c); |
||||
} |
@ -0,0 +1,9 @@ |
||||
#ifndef SERIAL_H |
||||
#define SERIAL_H |
||||
|
||||
/** Init the uarts */ |
||||
void serialInit(void); |
||||
|
||||
void UART_HandleRxByte(char c); |
||||
|
||||
#endif //SERIAL_H
|
@ -1,47 +0,0 @@ |
||||
//Stupid bit of code that does the bare minimum to make os_printf work.
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------------------- |
||||
* "THE BEER-WARE LICENSE" (Revision 42): |
||||
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
|
||||
* 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.
|
||||
* ---------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include <esp8266.h> |
||||
#include <uart_hw.h> |
||||
|
||||
static void ICACHE_FLASH_ATTR stdoutUartTxd(char c) { |
||||
//Wait until there is room in the FIFO
|
||||
while (((READ_PERI_REG(UART_STATUS(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; |
||||
//Send the character
|
||||
WRITE_PERI_REG(UART_FIFO(0), c); |
||||
} |
||||
|
||||
static void ICACHE_FLASH_ATTR stdoutPutchar(char c) { |
||||
//convert \n -> \r\n
|
||||
if (c=='\n') stdoutUartTxd('\r'); |
||||
stdoutUartTxd(c); |
||||
} |
||||
|
||||
|
||||
void stdoutInit() { |
||||
//Enable TxD pin
|
||||
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); |
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); |
||||
|
||||
//Set baud rate and other serial parameters to 115200,n,8,1
|
||||
uart_div_modify(0, UART_CLK_FREQ/BIT_RATE_115200); |
||||
WRITE_PERI_REG(UART_CONF0(0), (STICK_PARITY_DIS)|(ONE_STOP_BIT << UART_STOP_BIT_NUM_S)| \
|
||||
(EIGHT_BITS << UART_BIT_NUM_S)); |
||||
|
||||
//Reset tx & rx fifo
|
||||
SET_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST|UART_TXFIFO_RST); |
||||
CLEAR_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST|UART_TXFIFO_RST); |
||||
//Clear pending interrupts
|
||||
WRITE_PERI_REG(UART_INT_CLR(0), 0xffff); |
||||
|
||||
//Install our own putchar handler
|
||||
os_install_putc1((void *)stdoutPutchar); |
||||
} |
@ -1,6 +0,0 @@ |
||||
#ifndef STDOUT_H |
||||
#define STDOUT_H |
||||
|
||||
void stdoutInit(); |
||||
|
||||
#endif |
@ -0,0 +1,249 @@ |
||||
/*
|
||||
* Driver file for ESP8266 UART, works with the SDK. |
||||
*/ |
||||
|
||||
#include "uart_driver.h" |
||||
|
||||
#include <esp8266.h> |
||||
|
||||
#include "ets_sys.h" |
||||
#include "osapi.h" |
||||
#include "mem.h" |
||||
#include "os_type.h" |
||||
|
||||
#include "ets_sys_extra.h" |
||||
#include "uart_register.h" |
||||
|
||||
//========================================================
|
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetWordLength(UARTn uart_no, UartBitsNum4Char len) |
||||
{ |
||||
SET_PERI_REG_BITS(UART_CONF0(uart_no), UART_BIT_NUM, len, UART_BIT_NUM_S); |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetStopBits(UARTn uart_no, UartStopBitsNum bit_num) |
||||
{ |
||||
SET_PERI_REG_BITS(UART_CONF0(uart_no), UART_STOP_BIT_NUM, bit_num, UART_STOP_BIT_NUM_S); |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetLineInverse(UARTn uart_no, UART_LineLevelInverse inverse_mask) |
||||
{ |
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_LINE_INV_MASK); |
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), inverse_mask); |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetParity(UARTn uart_no, UartParityMode Parity_mode) |
||||
{ |
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_PARITY | UART_PARITY_EN); |
||||
if (Parity_mode == PARITY_NONE) { |
||||
} else { |
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), Parity_mode | UART_PARITY_EN); |
||||
} |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetBaudrate(UARTn uart_no, uint32 baud_rate) |
||||
{ |
||||
uart_div_modify(uart_no, UART_CLK_FREQ / baud_rate); |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetFlowCtrl(UARTn uart_no, UART_HwFlowCtrl flow_ctrl, uint8 rx_thresh) |
||||
{ |
||||
if (flow_ctrl & USART_HWFlow_RTS) { |
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS); |
||||
SET_PERI_REG_BITS(UART_CONF1(uart_no), UART_RX_FLOW_THRHD, rx_thresh, UART_RX_FLOW_THRHD_S); |
||||
SET_PERI_REG_MASK(UART_CONF1(uart_no), UART_RX_FLOW_EN); |
||||
} else { |
||||
CLEAR_PERI_REG_MASK(UART_CONF1(uart_no), UART_RX_FLOW_EN); |
||||
} |
||||
|
||||
if (flow_ctrl & USART_HWFlow_CTS) { |
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS); |
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_TX_FLOW_EN); |
||||
} else { |
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_TX_FLOW_EN); |
||||
} |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_WaitTxFifoEmpty(UARTn uart_no , uint32 time_out_us) //do not use if tx flow control enabled
|
||||
{ |
||||
uint32 t_s = system_get_time(); |
||||
while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S)) { |
||||
|
||||
if ((system_get_time() - t_s) > time_out_us) { |
||||
break; |
||||
} |
||||
|
||||
system_soft_wdt_feed(); |
||||
} |
||||
} |
||||
|
||||
|
||||
bool ICACHE_FLASH_ATTR UART_CheckOutputFinished(UARTn uart_no, uint32 time_out_us) |
||||
{ |
||||
uint32 t_start = system_get_time(); |
||||
uint8 tx_fifo_len; |
||||
|
||||
while (1) { |
||||
tx_fifo_len = UART_TxQueLen(uart_no); |
||||
|
||||
// TODO If using output circbuf, check if empty
|
||||
|
||||
if (tx_fifo_len == 0) { |
||||
return TRUE; |
||||
} |
||||
|
||||
if (system_get_time() - t_start > time_out_us) { |
||||
return FALSE; |
||||
} |
||||
|
||||
system_soft_wdt_feed(); |
||||
} |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_ResetFifo(UARTn uart_no) |
||||
{ |
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); |
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_ClearIntrStatus(UARTn uart_no, uint32 clr_mask) |
||||
{ |
||||
WRITE_PERI_REG(UART_INT_CLR(uart_no), clr_mask); |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetIntrEna(UARTn uart_no, uint32 ena_mask) |
||||
{ |
||||
SET_PERI_REG_MASK(UART_INT_ENA(uart_no), ena_mask); |
||||
} |
||||
|
||||
|
||||
LOCAL void u0_putc_crlf(char c) |
||||
{ |
||||
UART_WriteCharCRLF(UART0, (u8)c, UART_TIMEOUT_US); |
||||
} |
||||
|
||||
|
||||
LOCAL void u1_putc_crlf(char c) |
||||
{ |
||||
UART_WriteCharCRLF(UART1, (u8)c, UART_TIMEOUT_US); |
||||
} |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_SetPrintPort(UARTn uart_no) |
||||
{ |
||||
if (uart_no == UART0) { |
||||
os_install_putc1((void *)u0_putc_crlf); |
||||
} else { |
||||
os_install_putc1((void *)u1_putc_crlf); |
||||
} |
||||
} |
||||
|
||||
|
||||
// -------------- Custom UART functions -------------------------
|
||||
|
||||
// !!! write handlers are not ICACHE_FLASH_ATTR -> can be used in IRQ !!!
|
||||
|
||||
/**
|
||||
* @brief Write a char to UART. |
||||
* @param uart_no |
||||
* @param c |
||||
* @param timeout_us - how long to max wait for space in FIFO. |
||||
* @return write success |
||||
*/ |
||||
STATUS UART_WriteChar(UARTn uart_no, uint8 c, uint32 timeout_us) |
||||
{ |
||||
if (timeout_us == 0) { |
||||
timeout_us = UART_TIMEOUT_US; |
||||
} |
||||
|
||||
|
||||
uint32 t_s = system_get_time(); |
||||
|
||||
while ((system_get_time() - t_s) < timeout_us) { |
||||
uint8 fifo_cnt = UART_TxQueLen(uart_no); |
||||
|
||||
if (fifo_cnt < UART_TX_FULL_THRESH_VAL) { |
||||
WRITE_PERI_REG(UART_FIFO(uart_no), c); |
||||
return OK; |
||||
} |
||||
|
||||
system_soft_wdt_feed(); |
||||
} |
||||
|
||||
return FAIL; |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @brief Write a char to UART, translating LF to CRLF and discarding CR. |
||||
* @param uart_no |
||||
* @param c |
||||
* @param timeout_us - how long to max wait for space in FIFO. |
||||
* @return write success |
||||
*/ |
||||
STATUS UART_WriteCharCRLF(UARTn uart_no, uint8 c, uint32 timeout_us) |
||||
{ |
||||
STATUS st; |
||||
|
||||
if (c == '\r') { |
||||
return OK; |
||||
} else if (c == '\n') { |
||||
|
||||
st = UART_WriteChar(uart_no, '\r', timeout_us); |
||||
if (st != OK) return st; |
||||
|
||||
st = UART_WriteChar(uart_no, '\n', timeout_us); |
||||
return st; |
||||
|
||||
} else { |
||||
return UART_WriteChar(uart_no, c, timeout_us); |
||||
} |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @brief Send a string to UART. |
||||
* @param uart_no |
||||
* @param str |
||||
* @param timeout_us - how long to max wait for space in FIFO. |
||||
* @return write success |
||||
*/ |
||||
STATUS UART_WriteString(UARTn uart_no, const char *str, uint32 timeout_us) |
||||
{ |
||||
while (*str) { |
||||
STATUS suc = UART_WriteChar(uart_no, (u8) * str++, timeout_us); |
||||
if (suc != OK) return suc; |
||||
} |
||||
|
||||
return OK; |
||||
} |
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send a buffer |
||||
* @param uart_no |
||||
* @param buffer - buffer to send |
||||
* @param len - buffer size |
||||
* @param timeout_us - how long to max wait for space in FIFO. |
||||
* @return write success |
||||
*/ |
||||
STATUS UART_WriteBuffer(UARTn uart_no, const uint8 *buffer, size_t len, uint32 timeout_us) |
||||
{ |
||||
for (size_t i = 0; i < len; i++) { |
||||
STATUS suc = UART_WriteChar(uart_no, (u8) * buffer++, timeout_us); |
||||
if (suc != OK) return suc; |
||||
} |
||||
|
||||
return OK; |
||||
} |
@ -0,0 +1,201 @@ |
||||
/**
|
||||
* Low level UART peripheral support functions |
||||
*/ |
||||
|
||||
/*
|
||||
* File : uart.h |
||||
* Copyright (C) 2013 - 2016, Espressif Systems |
||||
* Copyright (C) 2016, Ondřej Hruška (cleaning, modif.) |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of version 3 of the GNU General Public License as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License along |
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
|
||||
#ifndef UART_APP_H |
||||
#define UART_APP_H |
||||
|
||||
#include "uart_register.h" |
||||
|
||||
#include "eagle_soc.h" |
||||
#include "c_types.h" |
||||
|
||||
|
||||
// ===========
|
||||
|
||||
// timeout for sending / receiving a char (default)
|
||||
#define UART_TIMEOUT_US 5000 |
||||
|
||||
#define UART_TX_FULL_THRESH_VAL (UART_FIFO_LEN - 2) // if more than this many bytes in queue, don't write more
|
||||
#define UART_TX_EMPTY_THRESH_VAL 16 |
||||
|
||||
// ===========
|
||||
|
||||
|
||||
typedef enum { |
||||
UART0 = 0, |
||||
UART1 = 1 |
||||
} UARTn; |
||||
|
||||
|
||||
typedef enum { |
||||
FIVE_BITS = 0x0, |
||||
SIX_BITS = 0x1, |
||||
SEVEN_BITS = 0x2, |
||||
EIGHT_BITS = 0x3 |
||||
} UartBitsNum4Char; |
||||
|
||||
|
||||
typedef enum { |
||||
ONE_STOP_BIT = 0x1, |
||||
ONE_HALF_STOP_BIT = 0x2, |
||||
TWO_STOP_BIT = 0x3 |
||||
} UartStopBitsNum; |
||||
|
||||
|
||||
typedef enum { |
||||
PARITY_NONE = 0x2, |
||||
PARITY_ODD = 1, |
||||
PARITY_EVEN = 0 |
||||
} UartParityMode; |
||||
|
||||
|
||||
typedef enum { |
||||
PARITY_DIS = 0, |
||||
PARITY_EN = 1 |
||||
} UartExistParity; |
||||
|
||||
|
||||
typedef enum { |
||||
UART_None_Inverse = 0x0, |
||||
UART_Rxd_Inverse = UART_RXD_INV, |
||||
UART_CTS_Inverse = UART_CTS_INV, |
||||
UART_Txd_Inverse = UART_TXD_INV, |
||||
UART_RTS_Inverse = UART_RTS_INV, |
||||
} UART_LineLevelInverse; |
||||
|
||||
|
||||
typedef enum { |
||||
BIT_RATE_300 = 300, |
||||
BIT_RATE_600 = 600, |
||||
BIT_RATE_1200 = 1200, |
||||
BIT_RATE_2400 = 2400, |
||||
BIT_RATE_4800 = 4800, |
||||
BIT_RATE_9600 = 9600, |
||||
BIT_RATE_19200 = 19200, |
||||
BIT_RATE_38400 = 38400, |
||||
BIT_RATE_57600 = 57600, |
||||
BIT_RATE_74880 = 74880, |
||||
BIT_RATE_115200 = 115200, |
||||
BIT_RATE_230400 = 230400, |
||||
BIT_RATE_460800 = 460800, |
||||
BIT_RATE_921600 = 921600, |
||||
BIT_RATE_1843200 = 1843200, |
||||
BIT_RATE_3686400 = 3686400, |
||||
} UartBautRate; |
||||
|
||||
|
||||
typedef enum { |
||||
NONE_CTRL, |
||||
HARDWARE_CTRL, |
||||
XON_XOFF_CTRL |
||||
} UartFlowCtrl; |
||||
|
||||
|
||||
typedef enum { |
||||
USART_HWFlow_None = 0x0, |
||||
USART_HWFlow_RTS = 0x1, |
||||
USART_HWFlow_CTS = 0x2, |
||||
USART_HWFlow_CTS_RTS = 0x3 |
||||
} UART_HwFlowCtrl; |
||||
|
||||
|
||||
typedef enum { |
||||
EMPTY, |
||||
UNDER_WRITE, |
||||
WRITE_OVER |
||||
} RcvMsgBuffState; |
||||
|
||||
|
||||
typedef struct { |
||||
uint32 RcvBuffSize; |
||||
uint8 *pRcvMsgBuff; |
||||
uint8 *pWritePos; |
||||
uint8 *pReadPos; |
||||
uint8 TrigLvl; //JLU: may need to pad
|
||||
RcvMsgBuffState BuffState; |
||||
} RcvMsgBuff; |
||||
|
||||
|
||||
typedef struct { |
||||
uint32 TrxBuffSize; |
||||
uint8 *pTrxBuff; |
||||
} TrxMsgBuff; |
||||
|
||||
|
||||
typedef enum { |
||||
BAUD_RATE_DET, |
||||
WAIT_SYNC_FRM, |
||||
SRCH_MSG_HEAD, |
||||
RCV_MSG_BODY, |
||||
RCV_ESC_CHAR, |
||||
} RcvMsgState; |
||||
|
||||
|
||||
typedef struct { |
||||
UartBautRate baut_rate; |
||||
UartBitsNum4Char data_bits; |
||||
UartExistParity exist_parity; |
||||
UartParityMode parity; |
||||
UartStopBitsNum stop_bits; |
||||
UartFlowCtrl flow_ctrl; |
||||
RcvMsgBuff rcv_buff; |
||||
TrxMsgBuff trx_buff; |
||||
RcvMsgState rcv_state; |
||||
int received; |
||||
int buff_uart_no; //indicate which uart use tx/rx buffer
|
||||
} UartDevice; |
||||
|
||||
// UartDev is defined and initialized in rom code.
|
||||
extern UartDevice UartDev; |
||||
|
||||
|
||||
//==============================================
|
||||
|
||||
// FIFO used count
|
||||
#define UART_TxQueLen(uart_no) ((READ_PERI_REG(UART_STATUS((uart_no))) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) |
||||
#define UART_RxQueLen(uart_no) ((READ_PERI_REG(UART_STATUS((uart_no))) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT) |
||||
|
||||
STATUS UART_WriteCharCRLF(UARTn uart_no, uint8 c, uint32 timeout_us); |
||||
STATUS UART_WriteChar(UARTn uart_no, uint8 c, uint32 timeout_us); |
||||
STATUS UART_WriteString(UARTn uart_no, const char *str, uint32 timeout_us); |
||||
STATUS UART_WriteBuffer(UARTn uart_no, const uint8 *buffer, size_t len, uint32 timeout_us); |
||||
|
||||
//==============================================
|
||||
|
||||
void UART_SetWordLength(UARTn uart_no, UartBitsNum4Char len); |
||||
void UART_SetStopBits(UARTn uart_no, UartStopBitsNum bit_num); |
||||
void UART_SetLineInverse(UARTn uart_no, UART_LineLevelInverse inverse_mask); |
||||
void UART_SetParity(UARTn uart_no, UartParityMode Parity_mode); |
||||
void UART_SetBaudrate(UARTn uart_no, uint32 baud_rate); |
||||
void UART_SetFlowCtrl(UARTn uart_no, UART_HwFlowCtrl flow_ctrl, uint8 rx_thresh); |
||||
void UART_WaitTxFifoEmpty(UARTn uart_no , uint32 time_out_us); //do not use if tx flow control enabled
|
||||
void UART_ResetFifo(UARTn uart_no); |
||||
void UART_ClearIntrStatus(UARTn uart_no, uint32 clr_mask); |
||||
void UART_SetIntrEna(UARTn uart_no, uint32 ena_mask); |
||||
void UART_SetPrintPort(UARTn uart_no); |
||||
bool UART_CheckOutputFinished(UARTn uart_no, uint32 time_out_us); |
||||
|
||||
//==============================================
|
||||
|
||||
#endif |
||||
|
@ -0,0 +1,185 @@ |
||||
//Stupid bit of code that does the bare minimum to make os_printf work.
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------------------- |
||||
* "THE BEER-WARE LICENSE" (Revision 42): |
||||
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain |
||||
* 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. |
||||
* ---------------------------------------------------------------------------- |
||||
*/ |
||||
|
||||
#include <esp8266.h> |
||||
#include "uart_driver.h" |
||||
#include "uart_handler.h" |
||||
|
||||
// messy irq/task based UART handling below
|
||||
|
||||
static void uart0_rx_intr_handler(void *para); |
||||
static void uart_recvTask(os_event_t *events); |
||||
|
||||
#define uart_recvTaskPrio 1 |
||||
#define uart_recvTaskQueueLen 10 |
||||
static os_event_t uart_recvTaskQueue[uart_recvTaskQueueLen]; |
||||
|
||||
/** Clear the fifos */ |
||||
void ICACHE_FLASH_ATTR clear_rxtx(int uart_no) |
||||
{ |
||||
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); |
||||
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @brief Configure UART 115200-8-N-1 |
||||
* @param uart_no |
||||
*/ |
||||
static void ICACHE_FLASH_ATTR my_uart_init(UARTn uart_no, uint32 baud) |
||||
{ |
||||
UART_SetParity(uart_no, PARITY_NONE); |
||||
UART_SetStopBits(uart_no, ONE_STOP_BIT); |
||||
UART_SetWordLength(uart_no, EIGHT_BITS); |
||||
UART_SetBaudrate(uart_no, baud); |
||||
UART_ResetFifo(uart_no); |
||||
} |
||||
|
||||
|
||||
/** Configure basic UART func and pins */ |
||||
void ICACHE_FLASH_ATTR UART_Init(uint32_t baud0, uint32_t baud1) |
||||
{ |
||||
// U0TXD
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); |
||||
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); |
||||
|
||||
// U0RXD
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD); |
||||
|
||||
// U1TXD (GPIO2)
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); |
||||
|
||||
// Configure the UART peripherals
|
||||
my_uart_init(UART0, baud0); // main
|
||||
my_uart_init(UART1, baud1); // debug (output only)
|
||||
} |
||||
|
||||
/** Configure Rx on UART0 */ |
||||
void ICACHE_FLASH_ATTR UART_SetupAsyncReceiver(void) |
||||
{ |
||||
// Start the Rx reading task
|
||||
system_os_task(uart_recvTask, uart_recvTaskPrio, uart_recvTaskQueue, uart_recvTaskQueueLen); |
||||
// set handler
|
||||
ETS_UART_INTR_ATTACH((void *)uart0_rx_intr_handler, &(UartDev.rcv_buff)); // the buf will be used as an arg
|
||||
|
||||
// fifo threshold config
|
||||
uint32_t conf = ((100 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S); |
||||
conf |= ((0x10 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S); |
||||
// timeout config
|
||||
conf |= ((0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S); // timeout threshold
|
||||
conf |= UART_RX_TOUT_EN; // enable timeout
|
||||
WRITE_PERI_REG(UART_CONF1(UART0), conf); |
||||
|
||||
// enable TOUT and ERR irqs
|
||||
SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA); |
||||
/* clear interrupt flags */ |
||||
WRITE_PERI_REG(UART_INT_CLR(UART0), 0xffff); |
||||
/* enable RX interrupts */ |
||||
SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_OVF_INT_ENA); |
||||
|
||||
// Enable IRQ in Extensa
|
||||
ETS_UART_INTR_ENABLE(); |
||||
} |
||||
|
||||
|
||||
// ---- async receive stuff ----
|
||||
|
||||
|
||||
void uart_rx_intr_disable(uint8 uart_no) |
||||
{ |
||||
CLEAR_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); |
||||
} |
||||
|
||||
|
||||
void uart_rx_intr_enable(uint8 uart_no) |
||||
{ |
||||
SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); |
||||
} |
||||
|
||||
|
||||
/**
|
||||
* @brief get number of bytes in UART tx fifo |
||||
* @param UART number |
||||
*/ |
||||
#define UART_GetRxFifoCount(uart_no) ((READ_PERI_REG(UART_STATUS((uart_no))) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT) |
||||
|
||||
|
||||
void ICACHE_FLASH_ATTR UART_PollRx(void) |
||||
{ |
||||
uint8 fifo_len = UART_GetRxFifoCount(UART0); |
||||
|
||||
for (uint8 idx = 0; idx < fifo_len; idx++) { |
||||
uint8 d_tmp = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; |
||||
UART_HandleRxByte(d_tmp); |
||||
} |
||||
} |
||||
|
||||
|
||||
static void ICACHE_FLASH_ATTR uart_recvTask(os_event_t *events) |
||||
{ |
||||
if (events->sig == 0) { |
||||
UART_PollRx(); |
||||
|
||||
// clear irq flags
|
||||
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); |
||||
// enable rx irq again
|
||||
uart_rx_intr_enable(UART0); |
||||
} else if (events->sig == 1) { |
||||
// ???
|
||||
} |
||||
} |
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : uart0_rx_intr_handler |
||||
* Description : Internal used function |
||||
* UART0 interrupt handler, add self handle code inside |
||||
* Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg |
||||
* Returns : NONE |
||||
*******************************************************************************/ |
||||
static void |
||||
uart0_rx_intr_handler(void *para) |
||||
{ |
||||
(void)para; |
||||
|
||||
uint32_t status_reg = READ_PERI_REG(UART_INT_ST(UART0)); |
||||
|
||||
if (status_reg & UART_FRM_ERR_INT_ST) { |
||||
// Framing Error
|
||||
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_FRM_ERR_INT_CLR); |
||||
} |
||||
|
||||
if (status_reg & UART_RXFIFO_FULL_INT_ST) { |
||||
// RX fifo full
|
||||
uart_rx_intr_disable(UART0); |
||||
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR); |
||||
|
||||
// run handler
|
||||
system_os_post(uart_recvTaskPrio, 0, 0); /* -> notify the polling thread */ |
||||
} |
||||
|
||||
if (status_reg & UART_RXFIFO_TOUT_INT_ST) { |
||||
// Fifo timeout
|
||||
uart_rx_intr_disable(UART0); |
||||
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_TOUT_INT_CLR); |
||||
|
||||
// run handler
|
||||
system_os_post(uart_recvTaskPrio, 0, 0); /* -> notify the polling thread */ |
||||
} |
||||
|
||||
if (status_reg & UART_TXFIFO_EMPTY_INT_ST) { |
||||
CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA); |
||||
} |
||||
|
||||
if (status_reg & UART_RXFIFO_OVF_INT_ST) { |
||||
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_OVF_INT_CLR); |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
/**
|
||||
* UART init & async rx module. |
||||
* |
||||
* Call UART_Init(), UART_SetupAsyncReceiver() and |
||||
* define UART_HandleRxByte() somewhere in application code. |
||||
* |
||||
* Call UART_PollRx() to allow rx in a blocking handler. |
||||
*/ |
||||
|
||||
#ifndef UART_HANDLER_H |
||||
#define UART_HANDLER_H |
||||
|
||||
#include <esp8266.h> |
||||
|
||||
/** Configure basic UART func and pins */ |
||||
void UART_Init(uint32_t baud0, uint32_t baud1); |
||||
|
||||
/** Configure Rx on UART0 */ |
||||
void UART_SetupAsyncReceiver(void); |
||||
|
||||
/** User must provide this func for handling received bytes */ |
||||
extern void UART_HandleRxByte(char c); |
||||
|
||||
/** poll uart while waiting for something */ |
||||
void UART_PollRx(void); |
||||
|
||||
#endif |
Loading…
Reference in new issue