diff --git a/README.md b/README.md index 3f8e9ba..93dc8ef 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/compress_html.sh b/compress_html.sh new file mode 100755 index 0000000..d71b43b --- /dev/null +++ b/compress_html.sh @@ -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 diff --git a/html/cats/cross-eyed-cat.jpg b/html/cats/cross-eyed-cat.jpg deleted file mode 100644 index e166e87..0000000 Binary files a/html/cats/cross-eyed-cat.jpg and /dev/null differ diff --git a/html/cats/junge-katze-iv.jpg b/html/cats/junge-katze-iv.jpg deleted file mode 100644 index c3cf70b..0000000 Binary files a/html/cats/junge-katze-iv.jpg and /dev/null differ diff --git a/html/cats/kitten-loves-toy.jpg b/html/cats/kitten-loves-toy.jpg deleted file mode 100644 index 569ff56..0000000 Binary files a/html/cats/kitten-loves-toy.jpg and /dev/null differ diff --git a/html/flash/140medley.min.js b/html/flash/140medley.min.js deleted file mode 100644 index d1495d1..0000000 --- a/html/flash/140medley.min.js +++ /dev/null @@ -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){}}; diff --git a/html/flash/index.html b/html/flash/index.html deleted file mode 100644 index 117976b..0000000 --- a/html/flash/index.html +++ /dev/null @@ -1,82 +0,0 @@ - -Upgrade firmware - - - - - -
-

Update firmware

-
Loading...
- - -
- \ No newline at end of file diff --git a/html/flash/style.css b/html/flash/style.css deleted file mode 100644 index 7678ec8..0000000 --- a/html/flash/style.css +++ /dev/null @@ -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; -} - - diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..96a53c1 --- /dev/null +++ b/html/index.html @@ -0,0 +1 @@ +ESP8266 Remote Terminal
ESP8266 Remote Terminal
\ No newline at end of file diff --git a/html/index.tpl b/html/index.tpl deleted file mode 100644 index 3daf3e7..0000000 --- a/html/index.tpl +++ /dev/null @@ -1,28 +0,0 @@ - -Esp8266 web server - - - -
-

It Works

-

-If you see this, it means the tiny li'l website in your ESP8266 does actually work. Fyi, this page has -been loaded %counter% times. -

- -

- -

And because we're on the Internets now, here are the required pictures of cats:
-
-
-
-

-
- diff --git a/html/led.tpl b/html/led.tpl deleted file mode 100644 index 7a9cf89..0000000 --- a/html/led.tpl +++ /dev/null @@ -1,15 +0,0 @@ -Test - - - -
-

The LED

-

-If there's a LED connected to GPIO2, it's now %ledstate%. You can change that using the buttons below. -

-
- - -
-
- diff --git a/html/script.js b/html/script.js new file mode 100644 index 0000000..406dd46 --- /dev/null +++ b/html/script.js @@ -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;t15){r=0}return g[r]}function n(){var u,t=$("#screen");for(var s=0;s0)&&(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()}; \ No newline at end of file diff --git a/html/style.css b/html/style.css index 2a2f758..d00d6b3 100644 --- a/html/style.css +++ b/html/style.css @@ -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} \ No newline at end of file diff --git a/html/test/index.html b/html/test/index.html deleted file mode 100644 index 01f4121..0000000 --- a/html/test/index.html +++ /dev/null @@ -1,9 +0,0 @@ -Webserver test - - - - -
-
Initializing test...
-
- \ No newline at end of file diff --git a/html/test/test.js b/html/test/test.js deleted file mode 100644 index 03c0cd4..0000000 --- a/html/test/test.js +++ /dev/null @@ -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+'
'); -} - - -//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; xthis.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=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; iSuccess!"); - successCnt++; - } else { - log("Test failed!"); - } - } - 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); -} - - - diff --git a/html/websocket/index.html b/html/websocket/index.html deleted file mode 100644 index d7423ec..0000000 --- a/html/websocket/index.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - -WebSocket Test - - - -

WebSocket Test

- -
diff --git a/html/wifi/140medley.min.js b/html/wifi/140medley.min.js deleted file mode 100644 index d1495d1..0000000 --- a/html/wifi/140medley.min.js +++ /dev/null @@ -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){}}; diff --git a/html/wifi/connecting.html b/html/wifi/connecting.html deleted file mode 100644 index 12e3a83..0000000 --- a/html/wifi/connecting.html +++ /dev/null @@ -1,43 +0,0 @@ -Connecting... - - - - - -
-

Connecting to AP...

-

Status:
-

...
-

-
- - diff --git a/html/wifi/icons.png b/html/wifi/icons.png deleted file mode 100644 index 03109e1..0000000 Binary files a/html/wifi/icons.png and /dev/null differ diff --git a/html/wifi/style.css b/html/wifi/style.css deleted file mode 100644 index abcfb10..0000000 --- a/html/wifi/style.css +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/html/wifi/wifi.tpl b/html/wifi/wifi.tpl deleted file mode 100644 index e513025..0000000 --- a/html/wifi/wifi.tpl +++ /dev/null @@ -1,94 +0,0 @@ -WiFi connection - - - - - -
-

-Current WiFi mode: %WiFiMode% -

-

-Note: %WiFiapwarn% -

-
-

-To connect to a WiFi network, please select one of the detected networks...
-

Scanning...
-
-WiFi password, if applicable:
-
- -

-
- - diff --git a/html_orig/index.html b/html_orig/index.html new file mode 100644 index 0000000..be4367b --- /dev/null +++ b/html_orig/index.html @@ -0,0 +1,20 @@ + + + +ESP8266 Remote Terminal + + + + + + + +
ESP8266 Remote Terminal
+ +
+ +
+ +
+ + diff --git a/html_orig/script.js b/html_orig/script.js new file mode 100644 index 0000000..2dcda4c --- /dev/null +++ b/html_orig/script.js @@ -0,0 +1,271 @@ +//region Libs / utils + +/* + * DOM selector + * + * Usage: + * $('div'); + * $('#name'); + * $('.name'); + * + * + * Copyright (C) 2011 Jed Schmidt - 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('

Hello

'); + * document.body.appendChild(el); + * + * + * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + * Version 2, December 2004 + * + * Copyright (C) 2011 Jed Schmidt - 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(); +} diff --git a/html_orig/style.css b/html_orig/style.css new file mode 100644 index 0000000..06f1a5b --- /dev/null +++ b/html_orig/style.css @@ -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; +} diff --git a/include/ets_sys_extra.h b/include/ets_sys_extra.h new file mode 100755 index 0000000..a1a27e1 --- /dev/null +++ b/include/ets_sys_extra.h @@ -0,0 +1,17 @@ +#pragma once +#include + +// 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); diff --git a/user/cgi-test.c b/user/cgi-test.c deleted file mode 100644 index 8817c7a..0000000 --- a/user/cgi-test.c +++ /dev/null @@ -1,86 +0,0 @@ -/* -Cgi routines as used by the tests in the html/test subdirectory. -*/ - -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * Jeroen Domburg 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 -#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; xsendPos>>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; -} diff --git a/user/cgi-test.h b/user/cgi-test.h deleted file mode 100644 index 501e6f3..0000000 --- a/user/cgi-test.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CGI_TEST_H -#define CGI_TEST_H - -#include "httpd.h" - -httpd_cgi_state cgiTestbed(HttpdConnData *connData); - -#endif diff --git a/user/cgi.c b/user/cgi.c deleted file mode 100644 index c73b3ff..0000000 --- a/user/cgi.c +++ /dev/null @@ -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 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 -#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; -} diff --git a/user/cgi.h b/user/cgi.h deleted file mode 100644 index 7ba2a77..0000000 --- a/user/cgi.h +++ /dev/null @@ -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 diff --git a/user/io.c b/user/io.c index a342dbd..fa1ce2d 100644 --- a/user/io.c +++ b/user/io.c @@ -14,6 +14,8 @@ #define LEDGPIO 2 #define BTNGPIO 0 +static bool enable_ap_button = false; + static ETSTimer resetBtntimer; void ICACHE_FLASH_ATTR ioLed(int ena) { @@ -27,25 +29,34 @@ void ICACHE_FLASH_ATTR ioLed(int ena) { static void ICACHE_FLASH_ATTR resetBtnTimerCb(void *arg) { static int resetCnt=0; - if (!GPIO_INPUT_GET(BTNGPIO)) { + if (enable_ap_button && !GPIO_INPUT_GET(BTNGPIO)) { resetCnt++; } else { if (resetCnt>=6) { //3 sec pressed wifi_station_disconnect(); - wifi_set_opmode(0x3); //reset to AP+STA mode - os_printf("Reset to AP mode. Restarting system...\n"); + wifi_set_opmode(STATIONAP_MODE); //reset to AP+STA mode + info("Reset to AP mode from GPIO0, Restarting system..."); system_restart(); } resetCnt=0; } } -void ioInit() { +void ICACHE_FLASH_ATTR ioInit() { PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); gpio_output_set(0, 0, (1< +#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); +} \ No newline at end of file diff --git a/user/serial.h b/user/serial.h new file mode 100644 index 0000000..eda2e5b --- /dev/null +++ b/user/serial.h @@ -0,0 +1,9 @@ +#ifndef SERIAL_H +#define SERIAL_H + +/** Init the uarts */ +void serialInit(void); + +void UART_HandleRxByte(char c); + +#endif //SERIAL_H diff --git a/user/stdout.c b/user/stdout.c deleted file mode 100644 index bb4dddb..0000000 --- a/user/stdout.c +++ /dev/null @@ -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 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 -#include - -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); -} diff --git a/user/stdout.h b/user/stdout.h deleted file mode 100644 index a8d872d..0000000 --- a/user/stdout.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef STDOUT_H -#define STDOUT_H - -void stdoutInit(); - -#endif \ No newline at end of file diff --git a/user/uart_driver.c b/user/uart_driver.c new file mode 100755 index 0000000..48f20f8 --- /dev/null +++ b/user/uart_driver.c @@ -0,0 +1,249 @@ +/* +* Driver file for ESP8266 UART, works with the SDK. +*/ + +#include "uart_driver.h" + +#include + +#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; +} diff --git a/user/uart_driver.h b/user/uart_driver.h new file mode 100755 index 0000000..8a95c0d --- /dev/null +++ b/user/uart_driver.h @@ -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 . + */ + + +#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 + diff --git a/user/uart_handler.c b/user/uart_handler.c new file mode 100755 index 0000000..4c2625f --- /dev/null +++ b/user/uart_handler.c @@ -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 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 +#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); + } +} diff --git a/user/uart_handler.h b/user/uart_handler.h new file mode 100755 index 0000000..de293d5 --- /dev/null +++ b/user/uart_handler.h @@ -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 + +/** 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 diff --git a/user/user_main.c b/user/user_main.c index fb655c2..56c6022 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -7,84 +7,26 @@ * ---------------------------------------------------------------------------- */ -/* -This is example code for the esphttpd library. It's a small-ish demo showing off -the server, including WiFi connection management capabilities, some IO and -some pictures of cats. -*/ +/** + * This is the ESP8266 Remote Terminal project main file. + */ #include -#include "httpd.h" +#include +#include +#include +#include +#include +#include "serial.h" #include "io.h" -#include "httpdespfs.h" -#include "cgi.h" -#include "cgiwifi.h" -#include "cgiflash.h" -#include "stdout.h" -#include "auth.h" -#include "espfs.h" -#include "captdns.h" -#include "webpages-espfs.h" -#include "cgiwebsocket.h" -#include "cgi-test.h" - -//The example can print out the heap use every 3 seconds. You can use this to catch memory leaks. -//#define SHOW_HEAP_USE - -//Function that tells the authentication system what users/passwords live on the system. -//This is disabled in the default build; if you want to try it, enable the authBasic line in -//the builtInUrls below. -int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { - if (no==0) { - os_strcpy(user, "admin"); - os_strcpy(pass, "s3cr3t"); - return 1; -//Add more users this way. Check against incrementing no for each user added. -// } else if (no==1) { -// os_strcpy(user, "user1"); -// os_strcpy(pass, "something"); -// return 1; - } - return 0; -} -static ETSTimer websockTimer; +#define FIRMWARE_VERSION "0.1" -//Broadcast the uptime in seconds every second over connected websockets -static void ICACHE_FLASH_ATTR websockTimerCb(void *arg) { - static int ctr=0; - char buff[128]; - ctr++; - os_sprintf(buff, "Up for %d minutes %d seconds!\n", ctr/60, ctr%60); - cgiWebsockBroadcast("/websocket/ws.cgi", buff, os_strlen(buff), WEBSOCK_FLAG_NONE); -} +#define SHOW_HEAP_USE 1 -//On reception of a message, send "You sent: " plus whatever the other side sent -void myWebsocketRecv(Websock *ws, char *data, int len, int flags) { - int i; - char buff[128]; - os_sprintf(buff, "You sent: "); - for (i=0; irecvCb=myWebsocketRecv; - cgiWebsocketSend(ws, "Hi, Websocket!", 14, WEBSOCK_FLAG_NONE); -} - -//On reception of a message, echo it back verbatim -void myEchoWebsocketRecv(Websock *ws, char *data, int len, int flags) { - os_printf("EchoWs: echo, len=%d\n", len); - cgiWebsocketSend(ws, data, len, flags); -} - -//Echo websocket connected. Install reception handler. -void myEchoWebsocketConnect(Websock *ws) { - os_printf("EchoWs: connect\n"); - ws->recvCb=myEchoWebsocketRecv; + // NOOP } @@ -108,47 +50,14 @@ CgiUploadFlashDef uploadParams={ #define INCLUDE_FLASH_FNS #endif -/* -This is the main url->function dispatching data struct. -In short, it's a struct with various URLs plus their handlers. The handlers can -be 'standard' CGI functions you wrote, or 'special' CGIs requiring an argument. -They can also be auth-functions. An asterisk will match any url starting with -everything before the asterisks; "*" matches everything. The list will be -handled top-down, so make sure to put more specific rules above the more -general ones. Authorization things (like authBasic) act as a 'barrier' and -should be placed above the URLs they protect. -*/ +/** Routes */ HttpdBuiltInUrl builtInUrls[]={ - ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp8266.nonet"), // redirect func for the captive portal - ROUTE_REDIRECT("/", "/index.tpl"), - ROUTE_TPL("/led.tpl", tplLed), - ROUTE_TPL("/index.tpl", tplCounter), - ROUTE_CGI("/led.cgi", cgiLed), -#ifdef INCLUDE_FLASH_FNS - ROUTE_CGI_ARG("/flash/next", cgiGetFirmwareNext, &uploadParams), - ROUTE_CGI_ARG("/flash/upload", cgiUploadFirmware, &uploadParams), -#endif - ROUTE_CGI("/flash/reboot", cgiRebootFirmware), - - //Routines to make the /wifi URL and everything beneath it work. - -//Enable the line below to protect the WiFi configuration with an username/password combo. -// ROUTE_AUTH("/wifi/*", myPassFn), - - ROUTE_REDIRECT("/wifi", "/wifi/wifi.tpl"), - ROUTE_REDIRECT("/wifi/", "/wifi/wifi.tpl"), - ROUTE_CGI("/wifi/wifiscan.cgi", cgiWiFiScan), - ROUTE_TPL("/wifi/wifi.tpl", tplWlan), - ROUTE_CGI("/wifi/connect.cgi", cgiWiFiConnect), - ROUTE_CGI("/wifi/connstatus.cgi", cgiWiFiConnStatus), - ROUTE_CGI("/wifi/setmode.cgi", cgiWiFiSetMode), + // redirect func for the captive portal + ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp8266.nonet"), - ROUTE_WS("/websocket/ws.cgi", myWebsocketConnect), - ROUTE_WS("/websocket/echo.cgi", myEchoWebsocketConnect), + ROUTE_WS("/ws/update.cgi", myWebsocketConnect), - ROUTE_REDIRECT("/test", "/test/index.html"), - ROUTE_REDIRECT("/test/", "/test/index.html"), - ROUTE_CGI("/test/test.cgi", cgiTestbed), + // TODO add funcs for WiFi management (when web UI is added) ROUTE_FILESYSTEM(), ROUTE_END(), @@ -159,15 +68,28 @@ HttpdBuiltInUrl builtInUrls[]={ static ETSTimer prHeapTimer; static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { + static int led = 0; os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); + + cgiWebsockBroadcast("/ws/update.cgi", "HELLO", 5, WEBSOCK_FLAG_NONE); + + ioLed(led); + led = !led; } #endif //Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. void user_init(void) { - stdoutInit(); - ioInit(); + serialInit(); + + banner("ESP8266 Remote Terminal"); + banner_info("Version "FIRMWARE_VERSION", built "__DATE__" at "__TIME__); + dbg("!!! TODO (c) and GitHub link here !!!"); + + //stdoutInit(); + captdnsInit(); + ioInit(); // 0x40200000 is the base address for spi flash memory mapping, ESPFS_POS is the position // where image is written in flash that is defined in Makefile. @@ -176,18 +98,30 @@ void user_init(void) { #else espFsInit((void*)(webpages_espfs_start)); #endif + httpdInit(builtInUrls, 80); + #ifdef SHOW_HEAP_USE os_timer_disarm(&prHeapTimer); os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); os_timer_arm(&prHeapTimer, 3000, 1); #endif - os_timer_disarm(&websockTimer); - os_timer_setfn(&websockTimer, websockTimerCb, NULL); - os_timer_arm(&websockTimer, 1000, 1); - os_printf("\nReady\n"); + + info("System ready!"); } +// ---- unused funcs removed from sdk to save space --- + void user_rf_pre_init() { //Not needed, but some SDK versions want this defined. } + +// вызывается из phy_chip_v6.o +void ICACHE_FLASH_ATTR chip_v6_set_sense(void) { + // ret.n +} + +// вызывается из phy_chip_v6.o +int ICACHE_FLASH_ATTR chip_v6_unset_chanfreq(void) { + return 0; +} \ No newline at end of file