From 9ee1182d04570c23a620a0751fbcf6c48a3087f5 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 13 Jul 2015 10:25:29 -0700 Subject: [PATCH 01/20] reduce size of icons --- html/favicon.ico | Bin 622 -> 874 bytes html/home.html | 2 +- html/jl-200x55.png | Bin 2751 -> 0 bytes html/style.css | 7 + html/ui.js | 358 --------------------------------------------- 5 files changed, 8 insertions(+), 359 deletions(-) delete mode 100755 html/jl-200x55.png delete mode 100644 html/ui.js diff --git a/html/favicon.ico b/html/favicon.ico index ea4eca11c5ea71459cf3dcecea6e9c4c6bf90fa3..fe24a7b535b3d0acfd1db7f56d08b424ef11f880 100755 GIT binary patch literal 874 zcmV-w1C{(oNk%w1VL$*t0P6q%|NsC0{{UlaYpoa|=HA|SZf>NktgRs>r5G6Aq^!kV zV*l^&R#sN6EiJ7W7^MspA^8LW00031EC2ui06+jh000F4Fvv-(wIb^+uK!?I6(nbp zg{ZEh>Aoo|Tg+?U-OMrt?w>kU4OcK2kA|Kv__-AcfT(l|AS7?h%FVE#dc6w+Hf!U4 z&tI`Crc-hX==j;aVYBEOs64Kp64o*=U3+K*3||Cz87wD+az$v0D~*nE0)1bSA$V|= z01B83nn0Vsd5Sf0|x^NqKAZkbsj?+ot_Q50}oP3m1-Lo zCZ16V2)wn zS4jY|&=Txy^$gv*0*c4URS~k3zN^KqjE4Lefdvb$f5En_*)gyK;$ocs4mq#|tPmh@ z;H7t1;Nl2OmA7<)OSgW#s5a`g@h;QUxWfkj9i4D@04Ai#)J#D84ImF56h!oi1NkVB zKmrO}lM+Y3*+kNXAchzsCbVQ_*8jg_yR@t#1B&+Nf3(Q9PEVR)wlH3m^RQsz9+IH(L1rY!MJGW+g AmH+?% literal 622 zcmZ?wbhEHbRA5kGSj)ih|NsAg|Nf<=r>~MzDkv=69~QZC)vC>#H$Qs(_~y-W_-g;e`3n{tO-Zk;s@iI0ckI})UmriddGqGh z+)qmoN^8pc?NdZa4UcGWFSz5y!S41)Ps2yzJm~o!NdHTtYwwY~5 zE9EXQh_$6j|5P)1Q6iAa$eUg5=EKrlEF_mEB~Z+f9qv=#puj9H#I9Jv>)XM^){w@> zEW;qiE!dY{$>iEu)F8;rEX^q|xm`4!dx~?xQby73LLw5|m<5YB1Tl3N?_u62#>l!| za*~g;H_H*}?Sc{_LVLNdr^os;_;4MOVdi6$=M>&9E3qV<>+U-y9T^Fa4+lDb0$Sv;b6n2XlXCSAL=Z98!MNyiEub191P`SJ=B)_ z?TkjFP-MR4gbNK!TcZmqnHEQtPHEvxG|yRlVL|ha{mG}DgbQAZTO_49Tr}0rm4C!A zS&aW>a75!4|F%hcBu%%7$l2x{nZP(Dt+&BBa6`_+M^C#0wbsT;M_yVo_s#9FCI$v; E07DSuRsaA1 diff --git a/html/home.html b/html/home.html index 64d80e1..59e9ab2 100644 --- a/html/home.html +++ b/html/home.html @@ -1,6 +1,6 @@
-
+
JEELABS

esp-link

diff --git a/html/jl-200x55.png b/html/jl-200x55.png deleted file mode 100755 index 3bc4a42cdfe781b8e1b0a9e2dc48e6b6e8577a4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2751 zcmV;w3PAOVP) zPly}W9ml^;?Ze8jU9tsd-LX> z+}Q5}Vl^{QZ+t06Na=I)FySFdAvfA(2F7Pm-M# zbv+N@1MDh7SULbsR}7<-luQyy91>BGqONaZU@4O1YQ->4C)}q*5{g&~Qq=W3h;A)J zX;lp4aLVV9NT@_okfN?H0(c`?Q36@27{=n5&nbDt39TSSUH@97ou+9x@xlv2eg5_1 zk71goNYh@$Fb)WHDv>0Bu!0nIJqzFtuT2=GU{SBvajjktYIdMv7`<30l1Ky)QV=IQ zb&GX;?(A6{IdUX1p#Y%SY@%AL@tPc#*)B=i1S$wmIdZuiUcY!TaiQESudHBeYm0SQ zDv2ajzQsGpQ;t%pgrC0nVq$_UT)c>HPflWXcGk21=+UG2ZZ3yrvq}5d{12agx_0yC zP3dk)QsJ?H<5cBXU0ucO%uJ$!^!t6Lo0iMvu)4a+`Y1Opl1SpfR1j9S$|#qSnV3ja zh-S0tvm7Lui3u3xGWD2fW=$dq3spgix=y`abME4S{QdRcFppJhHCz;oWmqyewHb*d zQCJq_0=c79Dn+&&t)*pj-g_S#Pdtf@C!VA#i)i1Oi3yZSCEB5J7cM1_06qnAhL3p2 z<#MrNj(`3B9pvp@I?fcu?xcY;`OgD_={Q2{~eMMbo z7BnT2u#grcOE#dnGGX}glTSU{ZtJmLE_F=XNI{Ca zK1UvOU%XN1efGI;U-lP2AHCn&+VaF7_qVq_Z42-IG4LET8H>7pDvX>G2@AIc@kP>c z$5%C8dDXMM-xcV$Zr{fA^z>-E^WOWu&mVm5*}!u!WMpRrB$5y?3x)2BH!5VneeB&o zjNY%WuX|z$GUk~2{`Z8>lgVVrT~opzmPj}-6-3jtSRK<+yZeb$F)wv2;$98W&YTe! zOd<)vUVt8dFalsUEr--%bCL*5*6E+Lg0f ztKr0n6H!k2vnU^ugu_(S0%iA(PSu#&icjq(wG3|990?)VuI1NrBPUH}uoDQcOqq(B)oiPi@aa47Dq`ql3=LXhfvO<=en0XMliS_@ zdbXdr{3afE`dTlSJ+%*VoR&Kc+-x>6Jv|+JsEZ^u$UOeg#c9BMN56vvm$)?*brppB ztl1W81Zh0`QN5lDhoRm3=6$uesGhH~%wyv6r$~t@jzy;UwHO;ns@!l|t)esbh z3$GeZPOB9uB*v`f%hgWOgJnI2G>GlAGFm+ZB$%9=hsd>X!AbLa;;Ny7{E79cukPK$ zm!EvN|p_Gq`;NvyNQT~tdF4O3BlvrS2h7$Tz-mni^uOhx5RanNk* zu%jp<8Nq^+1OTw>VpTyz{Oxudxm*st&p!91V$b~N-@aaHwGVeS9N8& z!yWh!sTf98xPyd9!PTKw+#fK$%#ffvbPFM35#D#{q?g@y`n;*Ai)4n1(&rimvB$8V zIZ)EZaHXgp0|44Gp612|0I?CZfIt3lTLf}i?cX?)sG=~GRgx~w@R~TEFC-y{W3kqV z-Jys5Xtq_=Z0i6+87}~w!HS&K4DpvA9UHk?Uwrrxbz&25TJ0Afe&lKEE+j^QCo2t8 zQE!DX`Pais^w1J_VGLH)JqiVpMy8?;hmZ)hchLj9S*!&R=5uR%8?!Sr*grGlnap&5 zdz)JUcW16>(`rdtOUu*&i!9tO9?iW1$JcZfnQobi+5vD*v#o}ysHZS^I+lkZvrY6} z5N#`%$(jn$XGbJcB-$S-AI*A*xRjKPRzc){=g64_-7V@+`k2FRLSfU%Sb5UB&bTX| z;yUiwu7XqyV~t!7Yk7GYM!AfaZd?zX{_4g(Fp)Q{HVJ42!ir&#<`)3fSI%Phi=)vA zDNdL>q=-L09Dypr5`aa4cB1`}@{!iVH}aK12i~dNo0Pt3gta!F3b=FO(0KI~ z;TzJ$^2H)U3vlY6!D}Jxo1F9o@IljRiJ#jJmX45E9QZ5;|N)I*4=sJ;u$8KMyg z?K`B^QDSm{Ecj`*)pLIDM##Q8Zkb61@jdG7x0fyf*uD6lf4l6fR~&rqS-f=Px=)Ej zrq#w_C>sIyqISNtLttBn4uC^p=QBLy%n{q7)?-MA*iK?fbQOc&X%DX&uG!Xc=Qlr1 zk;6?314Z*(KfXIEZJpbfPoBid2xGJK`FWH|rNkGy2AB`pkx!tKBnmBy<-5SH)$8c* z>_j?28p_e%*B`xh2OrzH#^r`0A2LzaeoESn^0m_aM=@dqLV z%_EPBK2I+)Rt#gT1NJ132D}I8P~jzSkA+^V7)H3@NFoV==wqOnN&*a?`jqrz z+4?&>L5TsW9Fo)#4s4yp<(LJYOeTZUrAx7wEbVr?SYBB{zu)IIJNF=lvr8U5LS=r6 zx;_QqZcw{?J`e588DVAVcDpb)HqdGbO#(b1V-Crlh;1mX{th3^k76vP-T&);}T&71Nkx0Ht{14hL^?zhM5i|e*002ovPDHLk FV1fk~Lhb+n diff --git a/html/style.css b/html/style.css index 4fa393a..29d15a3 100644 --- a/html/style.css +++ b/html/style.css @@ -114,6 +114,13 @@ div.tt { .header h1 .esp { font-size: 1.25em; } +.jl { + font: normal 800 1.5em sans-serif; + position: relative; + bottom: 19px; + color: #9d1414; + margin-left: 3px; +} .content-subhead { margin: 50px 0 20px 0; diff --git a/html/ui.js b/html/ui.js deleted file mode 100644 index b67f077..0000000 --- a/html/ui.js +++ /dev/null @@ -1,358 +0,0 @@ -//===== Collection of small utilities - -/* - * Bind/Unbind events - * - * Usage: - * var el = document.getElementyById('#container'); - * bnd(el, 'click', function() { - * console.log('clicked'); - * }); - */ - -var bnd = function( - d, // a DOM element - e, // an event name such as "click" - f // a handler function -){ - d.addEventListener(e, f, false); -} - -/* - * Create DOM element - * - * Usage: - * var el = m('

Hello

'); - * document.body.appendChild(el); - * - * 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. -} - -/* - * 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. - ) -} - -/* - * Get cross browser xhr object - * - * Copyright (C) 2011 Jed Schmidt - * More: https://gist.github.com/993585 - */ - -var j = function( - a // cursor placeholder -){ - for( // for all a - a=0; // from 0 - a<4; // to 4, - a++ // incrementing - ) try { // try - return a // returning - ? new ActiveXObject( // a new ActiveXObject - [ // reflecting - , // (elided) - "Msxml2", // the various - "Msxml3", // working - "Microsoft" // options - ][a] + // for Microsoft implementations, and - ".XMLHTTP" // the appropriate suffix, - ) // but make sure to - : new XMLHttpRequest // try the w3c standard first, and - } - - catch(e){} // ignore when it fails. -} - -// createElement short-hand - -e = function(a) { return document.createElement(a); } - -// chain onload handlers - -function onLoad(f) { - var old = window.onload; - if (typeof old != 'function') { - window.onload = f; - } else { - window.onload = function() { - old(); - f(); - } - } -} - -//===== helpers to add/remove/toggle HTML element classes - -function addClass(el, cl) { - el.className += ' ' + cl; -} -function removeClass(el, cl) { - var cls = el.className.split(/\s+/), - l = cls.length; - for (var i=0; i= 200 && xhr.status < 300) { - console.log("XHR done:", method, url, "->", xhr.status); - ok_cb(xhr.responseText); - } else { - console.log("XHR ERR :", method, url, "->", xhr.status, xhr.responseText, xhr); - err_cb(xhr.status, xhr.responseText); - } - } - console.log("XHR send:", method, url); - try { - xhr.send(); - } catch(err) { - console.log("XHR EXC :", method, url, "->", err); - err_cb(599, err); - } -} - -function dispatchJson(resp, ok_cb, err_cb) { - var j; - try { j = JSON.parse(resp); } - catch(err) { - console.log("JSON parse error: " + err + ". In: " + resp); - err_cb(500, "JSON parse error: " + err); - return; - } - ok_cb(j); -} - -function ajaxJson(method, url, ok_cb, err_cb) { - ajaxReq(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); -} - -function ajaxSpin(method, url, ok_cb, err_cb) { - $("#spinner").removeAttribute('hidden'); - ajaxReq(method, url, function(resp) { - $("#spinner").setAttribute('hidden', ''); - ok_cb(resp); - }, function(status, statusText) { - $("#spinner").setAttribute('hidden', ''); - //showWarning("Error: " + statusText); - err_cb(status, statusText); - }); -} - -function ajaxJsonSpin(method, url, ok_cb, err_cb) { - ajaxSpin(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); -} - -//===== main menu, header spinner and notification boxes - -onLoad(function() { - var l = $("#layout"); - var o = l.childNodes[0]; - // spinner - l.insertBefore(m(''), o); - // notification boxes - l.insertBefore(m( - '
'), o); - // menu hamburger button - l.insertBefore(m(''), o); - // menu left-pane - var mm = m( - '\ - '); - l.insertBefore(mm, o); - - // make hamburger button pull out menu - var ml = $('#menuLink'), mm = $('#menu'); - bnd(ml, 'click', function (e) { - console.log("hamburger time"); - var active = 'active'; - e.preventDefault(); - toggleClass(l, active); - toggleClass(mm, active); - toggleClass(ml, active); - }); - - // populate menu via ajax call - var getMenu = function() { - ajaxJson("GET", "/menu", function(data) { - var html = "", path = window.location.pathname; - for (var i=0; i" + - "" + - data.menu[i] + ""); - } - $("#menu-list").innerHTML = html; - - v = $("#version"); - if (v != null) { v.innerHTML = data.version; } - }, function() { setTimeout(getMenu, 1000); }); - }; - getMenu(); -}); - -//===== Wifi info - -function showWifiInfo(data) { - Object.keys(data).forEach(function(v) { - el = $("#wifi-" + v); - if (el != null) { - if (el.nodeName === "INPUT") el.value = data[v]; - else el.innerHTML = data[v]; - } - }); - var dhcp = $('#dhcp-r'+data.dhcp); - if (dhcp) dhcp.click(); - $("#wifi-spinner").setAttribute("hidden", ""); - $("#wifi-table").removeAttribute("hidden"); - currAp = data.ssid; -} - -function getWifiInfo() { - ajaxJson('GET', "/wifi/info", showWifiInfo, - function(s, st) { window.setTimeout(getWifiInfo, 1000); }); -} - -//===== Notifications - -function showWarning(text) { - var el = $("#warning"); - el.innerHTML = text; - el.removeAttribute('hidden'); -} -function hideWarning() { - el = $("#warning").setAttribute('hidden', ''); -} -var notifTimeout = null; -function showNotification(text) { - var el = $("#notification"); - el.innerHTML = text; - el.removeAttribute('hidden'); - if (notifTimeout != null) clearTimeout(notifTimeout); - notifTimout = setTimeout(function() { - el.setAttribute('hidden', ''); - notifTimout = null; - }, 4000); -} - -//===== GPIO Pin mux card - -var currPin; -// pin={reset:12, isp:13, LED_conn:0, LED_ser:2} -function createInputForPin(pin) { - var input = document.createElement("input"); - input.type = "radio"; - input.name = "pins"; - input.data = pin.name; - input.className = "pin-input"; - input.value= pin.value; - input.id = "opt-" + pin.value; - if (currPin == pin.name) input.checked = "1"; - - var descr = m('"); - var div = document.createElement("div"); - div.appendChild(input); - div.appendChild(descr); - return div; -} - -function displayPins(resp) { - var po = $("#pin-mux"); - po.innerHTML = ""; - currPin = resp.curr; - resp.map.forEach(function(v) { - po.appendChild(createInputForPin(v)); - }); - var i, inputs = $(".pin-input"); - for (i=0; i Date: Tue, 14 Jul 2015 00:09:21 -0700 Subject: [PATCH 02/20] oops, readd mistakenly deleted file --- html/ui.js | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 html/ui.js diff --git a/html/ui.js b/html/ui.js new file mode 100644 index 0000000..5de2f4c --- /dev/null +++ b/html/ui.js @@ -0,0 +1,358 @@ +//===== Collection of small utilities + +/* + * Bind/Unbind events + * + * Usage: + * var el = document.getElementyById('#container'); + * bnd(el, 'click', function() { + * console.log('clicked'); + * }); + */ + +var bnd = function( + d, // a DOM element + e, // an event name such as "click" + f // a handler function +){ + d.addEventListener(e, f, false); +} + +/* + * Create DOM element + * + * Usage: + * var el = m('

Hello

'); + * document.body.appendChild(el); + * + * 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. +} + +/* + * 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. + ) +} + +/* + * Get cross browser xhr object + * + * Copyright (C) 2011 Jed Schmidt + * More: https://gist.github.com/993585 + */ + +var j = function( + a // cursor placeholder +){ + for( // for all a + a=0; // from 0 + a<4; // to 4, + a++ // incrementing + ) try { // try + return a // returning + ? new ActiveXObject( // a new ActiveXObject + [ // reflecting + , // (elided) + "Msxml2", // the various + "Msxml3", // working + "Microsoft" // options + ][a] + // for Microsoft implementations, and + ".XMLHTTP" // the appropriate suffix, + ) // but make sure to + : new XMLHttpRequest // try the w3c standard first, and + } + + catch(e){} // ignore when it fails. +} + +// createElement short-hand + +e = function(a) { return document.createElement(a); } + +// chain onload handlers + +function onLoad(f) { + var old = window.onload; + if (typeof old != 'function') { + window.onload = f; + } else { + window.onload = function() { + old(); + f(); + } + } +} + +//===== helpers to add/remove/toggle HTML element classes + +function addClass(el, cl) { + el.className += ' ' + cl; +} +function removeClass(el, cl) { + var cls = el.className.split(/\s+/), + l = cls.length; + for (var i=0; i= 200 && xhr.status < 300) { + console.log("XHR done:", method, url, "->", xhr.status); + ok_cb(xhr.responseText); + } else { + console.log("XHR ERR :", method, url, "->", xhr.status, xhr.responseText, xhr); + err_cb(xhr.status, xhr.responseText); + } + } + console.log("XHR send:", method, url); + try { + xhr.send(); + } catch(err) { + console.log("XHR EXC :", method, url, "->", err); + err_cb(599, err); + } +} + +function dispatchJson(resp, ok_cb, err_cb) { + var j; + try { j = JSON.parse(resp); } + catch(err) { + console.log("JSON parse error: " + err + ". In: " + resp); + err_cb(500, "JSON parse error: " + err); + return; + } + ok_cb(j); +} + +function ajaxJson(method, url, ok_cb, err_cb) { + ajaxReq(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); +} + +function ajaxSpin(method, url, ok_cb, err_cb) { + $("#spinner").removeAttribute('hidden'); + ajaxReq(method, url, function(resp) { + $("#spinner").setAttribute('hidden', ''); + ok_cb(resp); + }, function(status, statusText) { + $("#spinner").setAttribute('hidden', ''); + //showWarning("Error: " + statusText); + err_cb(status, statusText); + }); +} + +function ajaxJsonSpin(method, url, ok_cb, err_cb) { + ajaxSpin(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); +} + +//===== main menu, header spinner and notification boxes + +onLoad(function() { + var l = $("#layout"); + var o = l.childNodes[0]; + // spinner + l.insertBefore(m(''), o); + // notification boxes + l.insertBefore(m( + '
'), o); + // menu hamburger button + l.insertBefore(m(''), o); + // menu left-pane + var mm = m( + '\ + '); + l.insertBefore(mm, o); + + // make hamburger button pull out menu + var ml = $('#menuLink'), mm = $('#menu'); + bnd(ml, 'click', function (e) { + console.log("hamburger time"); + var active = 'active'; + e.preventDefault(); + toggleClass(l, active); + toggleClass(mm, active); + toggleClass(ml, active); + }); + + // populate menu via ajax call + var getMenu = function() { + ajaxJson("GET", "/menu", function(data) { + var html = "", path = window.location.pathname; + for (var i=0; i" + + "" + + data.menu[i] + ""); + } + $("#menu-list").innerHTML = html; + + v = $("#version"); + if (v != null) { v.innerHTML = data.version; } + }, function() { setTimeout(getMenu, 1000); }); + }; + getMenu(); +}); + +//===== Wifi info + +function showWifiInfo(data) { + Object.keys(data).forEach(function(v) { + el = $("#wifi-" + v); + if (el != null) { + if (el.nodeName === "INPUT") el.value = data[v]; + else el.innerHTML = data[v]; + } + }); + var dhcp = $('#dhcp-r'+data.dhcp); + if (dhcp) dhcp.click(); + $("#wifi-spinner").setAttribute("hidden", ""); + $("#wifi-table").removeAttribute("hidden"); + currAp = data.ssid; +} + +function getWifiInfo() { + ajaxJson('GET', "/wifi/info", showWifiInfo, + function(s, st) { window.setTimeout(getWifiInfo, 1000); }); +} + +//===== Notifications + +function showWarning(text) { + var el = $("#warning"); + el.innerHTML = text; + el.removeAttribute('hidden'); +} +function hideWarning() { + el = $("#warning").setAttribute('hidden', ''); +} +var notifTimeout = null; +function showNotification(text) { + var el = $("#notification"); + el.innerHTML = text; + el.removeAttribute('hidden'); + if (notifTimeout != null) clearTimeout(notifTimeout); + notifTimout = setTimeout(function() { + el.setAttribute('hidden', ''); + notifTimout = null; + }, 4000); +} + +//===== GPIO Pin mux card + +var currPin; +// pin={reset:12, isp:13, LED_conn:0, LED_ser:2} +function createInputForPin(pin) { + var input = document.createElement("input"); + input.type = "radio"; + input.name = "pins"; + input.data = pin.name; + input.className = "pin-input"; + input.value= pin.value; + input.id = "opt-" + pin.value; + if (currPin == pin.name) input.checked = "1"; + + var descr = m('"); + var div = document.createElement("div"); + div.appendChild(input); + div.appendChild(descr); + return div; +} + +function displayPins(resp) { + var po = $("#pin-mux"); + po.innerHTML = ""; + currPin = resp.curr; + resp.map.forEach(function(v) { + po.appendChild(createInputForPin(v)); + }); + var i, inputs = $(".pin-input"); + for (i=0; i Date: Tue, 14 Jul 2015 23:38:18 -0700 Subject: [PATCH 03/20] make esp-12 with 4MB flash work --- Makefile | 55 +++++++++++++++++++++++++++++++----------------- user/cgiflash.c | 4 +++- user/user_main.c | 1 + 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index cad8624..99d5617 100644 --- a/Makefile +++ b/Makefile @@ -29,11 +29,28 @@ ESPBAUD ?= 460800 # --------------- chipset configuration --------------- +# Pick your flash size: "512KB" or "4MB" +FLASH_SIZE ?= 4MB + +ifeq ("$(FLASH_SIZE)","512KB") +# Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 ESP_SPI_SIZE ?= 0 # 0->512KB ESP_FLASH_MODE ?= 0 # 0->QIO ESP_FLASH_FREQ_DIV ?= 0 # 0->40Mhz ESP_FLASH_MAX ?= 241664 # max bin file for 512KB flash: 236KB +else +# Winbond 25Q32 4MB flash, typ for esp-12 +# Here we're using two partitions of approx 0.5MB because that's what's easily available in terms +# of linker scripts in the SDK. Ideally we'd use two partitions of approx 1MB, the remaining 2MB +# cannot be used for code. +ESP_SPI_SIZE ?= 4 # 6->4MB (1MB+1MB) or 4->4MB (512KB+512KB) +ESP_FLASH_MODE ?= 0 # 0->QIO, 2->DIO +ESP_FLASH_FREQ_DIV ?= 15 # 15->80Mhz +ESP_FLASH_MAX ?= 503808 # max bin file for 512KB flash partition: 492KB +#ESP_FLASH_MAX ?= 1028096 # max bin file for 1MB flash partition: 1004KB +endif + # hostname or IP address for wifi flashing ESP_HOSTNAME ?= esp-link @@ -163,7 +180,6 @@ SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) $(BUILD_BASE)/espfs_img.o LIBS := $(addprefix -l,$(LIBS)) APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) -TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) USER1_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).user1.out) USER2_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).user2.out) @@ -202,16 +218,11 @@ endef .PHONY: all checkdirs clean webpages.espfs wiflash -all: echo_version checkdirs $(FW_BASE) firmware/user1.bin firmware/user2.bin +all: echo_version checkdirs $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin echo_version: @echo VERSION: $(VERSION) -$(TARGET_OUT): $(APP_AR) $(LD_SCRIPT) - $(vecho) "LD $@" - $(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ -# $(OBJDP) -x $(TARGET_OUT) | egrep '(espfs_img)' - $(USER1_OUT): $(APP_AR) $(LD_SCRIPT1) $(vecho) "LD $@" $(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT1) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ @@ -224,12 +235,11 @@ $(USER2_OUT): $(APP_AR) $(LD_SCRIPT2) $(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT2) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ # $(Q) $(OBJDP) -x $(TARGET_OUT) | egrep espfs_img -$(FW_BASE): $(TARGET_OUT) +$(FW_BASE): $(vecho) "FW $@" $(Q) mkdir -p $@ - $(Q) $(ESPTOOL) elf2image $(TARGET_OUT) --output $@/ -firmware/user1.bin: $(USER1_OUT) +$(FW_BASE)/user1.bin: $(USER1_OUT) $(FW_BASE) $(Q) $(OBJCP) --only-section .text -O binary $(USER1_OUT) eagle.app.v6.text.bin $(Q) $(OBJCP) --only-section .data -O binary $(USER1_OUT) eagle.app.v6.data.bin $(Q) $(OBJCP) --only-section .rodata -O binary $(USER1_OUT) eagle.app.v6.rodata.bin @@ -241,7 +251,7 @@ firmware/user1.bin: $(USER1_OUT) @echo "** user1.bin uses $$(stat -c '%s' $@) bytes of" $(ESP_FLASH_MAX) "available" $(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi -firmware/user2.bin: $(USER2_OUT) +$(FW_BASE)/user2.bin: $(USER2_OUT) $(FW_BASE) $(Q) $(OBJCP) --only-section .text -O binary $(USER2_OUT) eagle.app.v6.text.bin $(Q) $(OBJCP) --only-section .data -O binary $(USER2_OUT) eagle.app.v6.data.bin $(Q) $(OBJCP) --only-section .rodata -O binary $(USER2_OUT) eagle.app.v6.rodata.bin @@ -251,7 +261,6 @@ firmware/user2.bin: $(USER2_OUT) $(Q) mv eagle.app.flash.bin $@ $(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi - $(APP_AR): $(OBJ) $(vecho) "AR $@" $(Q) $(AR) cru $@ $^ @@ -262,7 +271,7 @@ $(BUILD_DIR): $(Q) mkdir -p $@ wiflash: all - ./wiflash $(ESP_HOSTNAME) firmware/user1.bin firmware/user2.bin + ./wiflash $(ESP_HOSTNAME) $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin flash: all $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash \ @@ -302,24 +311,32 @@ endif # we also adjust the sizes of the segments 'cause we need more irom0 # in the end the only thing that matters wrt size is that the whole shebang fits into the # 236KB available (in a 512KB flash) -build/eagle.esphttpd.v6.ld: $(SDK_LDDIR)/eagle.app.v6.ld - $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \ - $(SDK_LDDIR)/eagle.app.v6.ld >$@ +ifeq ("$(FLASH_SIZE)","512KB") build/eagle.esphttpd1.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.512.app1.ld $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \ -e '/^ irom0_0_seg/ s/2B000/38000/' \ - $(SDK_LDDIR)/eagle.app.v6.new.512.app1.ld >$@ + $(SDK_LDDIR)/eagle.app.v6.new.512.app1.ld >$@ build/eagle.esphttpd2.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \ -e '/^ irom0_0_seg/ s/2B000/38000/' \ - $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld >$@ + $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld >$@ +else +build/eagle.esphttpd1.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.1024.app1.ld + $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \ + -e '/^ irom0_0_seg/ s/6B000/7C000/' \ + $(SDK_LDDIR)/eagle.app.v6.new.1024.app1.ld >$@ +build/eagle.esphttpd2.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.1024.app2.ld + $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \ + -e '/^ irom0_0_seg/ s/6B000/7C000/' \ + $(SDK_LDDIR)/eagle.app.v6.new.1024.app2.ld >$@ +endif espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/ $(Q) $(MAKE) -C espfs/mkespfsimage USE_HEATSHRINK="$(USE_HEATSHRINK)" GZIP_COMPRESSION="$(GZIP_COMPRESSION)" release: all $(Q) rm -rf release; mkdir -p release/esp-link - $(Q) cp firmware/user1.bin firmware/user2.bin $(SDK_BASE)/bin/blank.bin \ + $(Q) cp $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin $(SDK_BASE)/bin/blank.bin \ "$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link $(Q) tar zcf esp-link.tgz -C release esp-link $(Q) rm -rf release diff --git a/user/cgiflash.c b/user/cgiflash.c index 65bfd21..70d4b1c 100644 --- a/user/cgiflash.c +++ b/user/cgiflash.c @@ -22,8 +22,10 @@ Some flash handling cgi routines. Used for reading the existing flash and updati // Check that the header of the firmware blob looks like actual firmware... static char* ICACHE_FLASH_ATTR check_header(void *buf) { uint8_t *cd = (uint8_t *)buf; + uint32_t *buf32 = buf; + os_printf("%p: %08lX %08lX %08lX %08lX\n", buf, buf32[0], buf32[1], buf32[2], buf32[3]); if (cd[0] != 0xEA) return "IROM magic missing"; - if (cd[1] != 4 || cd[2] > 3 || cd[3] > 0x40) return "bad flash header"; + if (cd[1] != 4 || cd[2] > 3 || (cd[3]>>4) > 6) return "bad flash header"; if (((uint16_t *)buf)[3] != 0x4010) return "Invalid entry addr"; if (((uint32_t *)buf)[2] != 0) return "Invalid start offset"; return NULL; diff --git a/user/user_main.c b/user/user_main.c index 57443ae..645d215 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -156,6 +156,7 @@ void user_init(void) { os_printf("exccause=%d epc1=0x%x epc2=0x%x epc3=0x%x excvaddr=0x%x depc=0x%x\n", rst_info->exccause, rst_info->epc1, rst_info->epc2, rst_info->epc3, rst_info->excvaddr, rst_info->depc); + os_printf("Flash map %d, chip %08X\n", system_get_flash_size_map(), spi_flash_get_id()); os_printf("** esp-link ready\n"); } From 886ce62df0bd1d3df8f932ed7409bb026c6a66d6 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Tue, 28 Jul 2015 11:29:20 -0700 Subject: [PATCH 04/20] initial support for TCP client connections from uC --- Makefile | 2 +- html/home.html | 22 ++++ serial/serbridge.c | 261 ++++++++++++++++++++++++++++--------------- serial/serbridge.h | 8 +- serial/tcpclient.c | 272 +++++++++++++++++++++++++++++++++++++++++++++ serial/tcpclient.h | 8 ++ user/cgiflash.c | 1 + user/status.c | 119 +++++++++++++++++++- 8 files changed, 597 insertions(+), 96 deletions(-) create mode 100644 serial/tcpclient.c create mode 100644 serial/tcpclient.h diff --git a/Makefile b/Makefile index 99d5617..32cce5c 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ ESPBAUD ?= 460800 # --------------- chipset configuration --------------- # Pick your flash size: "512KB" or "4MB" -FLASH_SIZE ?= 4MB +FLASH_SIZE ?= 512KB ifeq ("$(FLASH_SIZE)","512KB") # Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 diff --git a/html/home.html b/html/home.html index 59e9ab2..35c8436 100644 --- a/html/home.html +++ b/html/home.html @@ -44,6 +44,28 @@
+
+
+

HTTP service

+
+ External web service configuration +
+ + + + +
+
+ + + + +
+ +
+
+
diff --git a/serial/serbridge.c b/serial/serbridge.c index 6d45610..528c486 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -13,20 +13,21 @@ #include "serled.h" #include "config.h" #include "console.h" +#include "tcpclient.h" static struct espconn serbridgeConn; static esp_tcp serbridgeTcp; static int8_t mcu_reset_pin, mcu_isp_pin; -sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len); +static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len); // Connection pool serbridgeConnData connData[MAX_CONN]; -// Transmit buffers for the connection pool -static char txbuffer[MAX_CONN][MAX_TXBUFFER]; - // Given a pointer to an espconn struct find the connection that correcponds to it static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) { + struct espconn *conn = arg; + return (serbridgeConnData *)conn->reverse; +#if 0 for (int i=0; itxbuffer -// returns result from espconn_sent if data in buffer or ESPCONN_OK (0) -// Use only internally from espbuffsend and serbridgeSentCb -static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) { - sint8 result = ESPCONN_OK; - if (conn->txbufferlen != 0) { - //os_printf("%d TX %d\n", system_get_time(), conn->txbufferlen); - conn->readytosend = false; - result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); - conn->txbufferlen = 0; - if (result != ESPCONN_OK) { - os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); - } - } - return result; -} - -// espbuffsend adds data to the send buffer. If the previous send was completed it calls -// sendtxbuffer and espconn_sent. -// Returns ESPCONN_OK (0) for success, -128 if buffer is full or error from espconn_sent -// Use espbuffsend instead of espconn_sent as it solves the problem that espconn_sent must -// only be called *after* receiving an espconn_sent_callback for the previous packet. -sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) { - if (conn->txbufferlen + len > MAX_TXBUFFER) { - os_printf("espbuffsend: txbuffer full on conn %p\n", conn); - return -128; - } - os_memcpy(conn->txbuffer + conn->txbufferlen, data, len); - conn->txbufferlen += len; - if (conn->readytosend) { - return sendtxbuffer(conn); - } else { - //os_printf("%d QU %d\n", system_get_time(), conn->txbufferlen); - } - return ESPCONN_OK; -} - -//callback after the data are sent -static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) { - serbridgeConnData *conn = serbridgeFindConnData(arg); - //os_printf("Sent callback on conn %p\n", conn); - if (conn == NULL) return; - //os_printf("%d ST\n", system_get_time()); - conn->readytosend = true; - sendtxbuffer(conn); // send possible new data in txbuffer -} +//===== TCP -> UART // Telnet protocol characters #define IAC 255 // escape @@ -189,7 +146,6 @@ void ICACHE_FLASH_ATTR serbridgeReset() { } else os_printf("MCU reset: no pin\n"); } - // Receive callback static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) { serbridgeConnData *conn = serbridgeFindConnData(arg); @@ -233,6 +189,9 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh } else { conn->conn_mode = cmTransparent; } + + // Process return data on TCP client connections + } else if (conn->conn_mode == cmTcpClient) { } // write the buffer to the uart @@ -245,41 +204,181 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh serledFlash(50); // short blink on serial LED } +//===== UART -> TCP + +// Transmit buffers for the connection pool +static char txbuffer[MAX_CONN][MAX_TXBUFFER]; + +// Send all data in conn->txbuffer +// returns result from espconn_sent if data in buffer or ESPCONN_OK (0) +// Use only internally from espbuffsend and serbridgeSentCb +static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) { + sint8 result = ESPCONN_OK; + if (conn->txbufferlen != 0) { + //os_printf("%d TX %d\n", system_get_time(), conn->txbufferlen); + conn->readytosend = false; + result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); + conn->txbufferlen = 0; + if (result != ESPCONN_OK) { + os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); + } + } + return result; +} + +// espbuffsend adds data to the send buffer. If the previous send was completed it calls +// sendtxbuffer and espconn_sent. +// Returns ESPCONN_OK (0) for success, -128 if buffer is full or error from espconn_sent +// Use espbuffsend instead of espconn_sent as it solves the problem that espconn_sent must +// only be called *after* receiving an espconn_sent_callback for the previous packet. +static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) { + if (conn->txbufferlen + len > MAX_TXBUFFER) { + os_printf("espbuffsend: txbuffer full on conn %p\n", conn); + return -128; + } + os_memcpy(conn->txbuffer + conn->txbufferlen, data, len); + conn->txbufferlen += len; + if (conn->readytosend) { + return sendtxbuffer(conn); + } else { + //os_printf("%d QU %d\n", system_get_time(), conn->txbufferlen); + } + return ESPCONN_OK; +} + +//callback after the data are sent +static void ICACHE_FLASH_ATTR +serbridgeSentCb(void *arg) { + serbridgeConnData *conn = serbridgeFindConnData(arg); + //os_printf("Sent callback on conn %p\n", conn); + if (conn == NULL) return; + //os_printf("%d ST\n", system_get_time()); + conn->readytosend = true; + sendtxbuffer(conn); // send possible new data in txbuffer +} + +// TCP client connection state machine +// This processes commands from the attached uC to open outboud TCP connections +enum { + TC_idle, // in-between commands + TC_newline, // newline seen + TC_start, // start character (~) seen + TC_cmd, // command character (0) seen + TC_cmdLine, // accumulating command + TC_tdata, // data character (1-9) seen, and in text data mode +}; +static uint8_t tcState = TC_newline; +static uint8_t tcChan; // channel for current command (index into tcConn) + +#define CMD_MAX 256 +static char tcCmdBuf[CMD_MAX]; +static short tcCmdBufLen = 0; +static char tcCmdChar; + +// scan a buffer for tcp client commands +static int ICACHE_FLASH_ATTR +tcpClientProcess(char *buf, int len) +{ + char *in=buf, *out=buf; + for (short i=0; i= '1' && c <= '9') { + tcChan = c-'1'; + tcState = TC_tdata; + continue; + } + *out++ = '~'; // make up for '~' we skipped + break; + case TC_cmd: // saw channel number 0 (command), expect command char + tcCmdChar = c; // save command character + tcCmdBufLen = 0; // empty the command buffer + tcState = TC_cmdLine; + continue; + case TC_cmdLine: // accumulating command in buffer + if (c != '\n') { + if (tcCmdBufLen < CMD_MAX) tcCmdBuf[tcCmdBufLen++] = c; + } else { + tcpClientCommand(tcCmdChar, tcCmdBuf); + tcState = TC_newline; + } + continue; + case TC_tdata: // saw channel number, getting data characters + if (c != 0) { + tcpClientSendChar(tcChan, c); + } else { + tcpClientSendPush(tcChan); + tcState = TC_idle; + } + continue; + } + *out++ = c; + } + if (tcState != TC_idle) os_printf("tcState=%d\n", tcState); + return out-buf; +} + +// callback with a buffer of characters that have arrived on the uart +void ICACHE_FLASH_ATTR +serbridgeUartCb(char *buf, int length) { + // push the buffer into the microcontroller console + for (int i=0; i 0) { + for (int i = 0; i < MAX_CONN; ++i) { + if (connData[i].conn && connData[i].conn_mode != cmTcpClient) { + espbuffsend(&connData[i], buf, length); + } + } + } + serledFlash(50); // short blink on serial LED +} + +//===== Connect / disconnect + // Error callback (it's really poorly named, it's not a "connection reconnected" callback, // it's really a "connection broken, please reconnect" callback) static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) { - serbridgeConnData *conn=serbridgeFindConnData(arg); - if (conn == NULL) return; + serbridgeConnData *sbConn = serbridgeFindConnData(arg); + if (sbConn == NULL) return; // Close the connection - espconn_disconnect(conn->conn); - conn->conn = NULL; + espconn_disconnect(sbConn->conn); + // free connection slot + sbConn->conn = NULL; } // Disconnection callback static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) { - // Iterate through all the connections and deallocate the ones that are in a state that - // indicates that they're closed - for (int i=0; istate == ESPCONN_NONE || connData[i].conn->state == ESPCONN_CLOSE)) - { - if (connData[i].conn_mode == cmAVR) { - // send reset to arduino/ARM - if (mcu_reset_pin >= 0) { - GPIO_OUTPUT_SET(mcu_reset_pin, 0); - os_delay_us(100L); - GPIO_OUTPUT_SET(mcu_reset_pin, 1); - } - } - connData[i].conn = NULL; - } + serbridgeConnData *sbConn = serbridgeFindConnData(arg); + if (sbConn == NULL) return; + // send reset to arduino/ARM + if (sbConn->conn_mode == cmAVR && mcu_reset_pin >= 0) { + GPIO_OUTPUT_SET(mcu_reset_pin, 0); + os_delay_us(100L); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); } + // free connection slot + sbConn->conn = NULL; } // New connection callback, use one of the connection descriptors, if we have one left. static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { struct espconn *conn = arg; - //Find empty conndata in pool + // Find empty conndata in pool int i; for (i=0; ireverse = connData+i; + connData[i].conn = conn; connData[i].txbufferlen = 0; connData[i].readytosend = true; connData[i].telnet_state = 0; @@ -304,20 +404,7 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); } -// callback with a buffer of characters that have arrived on the uart -void ICACHE_FLASH_ATTR -serbridgeUartCb(char *buf, int length) { - // push the buffer into the microcontroller console - for (int i=0; i +#include "config.h" +#include "uart.h" +#include "serled.h" +#include "tcpclient.h" + +// max number of channels the client can open +#define MAX_CHAN 8 +// size of tx buffer +#define MAX_TXBUF 1024 + +enum TcpState { + TCP_idle, // unused connection + TCP_dns, // doing gethostbyname + TCP_conn, // connecting to remote server + TCP_data, // connected +}; + +// Connections +typedef struct { + struct espconn *conn; // esp connection structure + esp_tcp *tcp; // esp TCP parameters + char *txBuf; // buffer to accumulate into + char *txBufSent; // buffer held by espconn + uint8_t txBufLen; // number of chars in txbuf + enum TcpState state; +} TcpConn; + +#define MAX_CONN (8) +static TcpConn tcpConn[MAX_CONN]; + +// Free a connection dynamically. +static void ICACHE_FLASH_ATTR +tcpConnFree(TcpConn* tci) { + if (tci->conn != NULL) os_free(tci->conn); + if (tci->tcp != NULL) os_free(tci->tcp); + if (tci->txBuf != NULL) os_free(tci->txBuf); + if (tci->txBufSent != NULL) os_free(tci->txBufSent); + memset(tci, 0, sizeof(TcpConn)); +} + +// DNS name resolution callback +static void ICACHE_FLASH_ATTR +tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP dns CB (%p %p)\n", arg, tci); + if (ipaddr == NULL) { + os_printf("TCP %s not found\n", name); + } else { + os_printf("TCP %s -> %d.%d.%d.%d\n", name, IP2STR(ipaddr)); + tci->tcp->remote_ip[0] = ip4_addr1(ipaddr); + tci->tcp->remote_ip[1] = ip4_addr2(ipaddr); + tci->tcp->remote_ip[2] = ip4_addr3(ipaddr); + tci->tcp->remote_ip[3] = ip4_addr4(ipaddr); + os_printf("TCP connect %d.%d.%d.%d (%p)\n", IP2STR(tci->tcp->remote_ip), tci); + if (espconn_connect(tci->conn) == ESPCONN_OK) { + tci->state = TCP_conn; + return; + } + os_printf("TCP connect failure\n"); + } + // oops + tcpConnFree(tci); +} + +// Send the next buffer (assumes that the connection is in a state that allows it) +static void tcpDoSend(TcpConn *tci) { + sint8 err = espconn_sent(tci->conn, (uint8*)tci->txBuf, tci->txBufLen); + if (err == ESPCONN_OK) { + // send successful + os_printf("TCP sent (%p %p)\n", tci->conn, tci); + tci->txBufSent = tci->txBuf; + tci->txBuf = NULL; + tci->txBufLen = 0; + } else { + // send error, leave as-is and try again later... + os_printf("TCP send err (%p %p) %d\n", tci->conn, tci, err); + } +} + +// Connected callback +static void ICACHE_FLASH_ATTR tcpConnectCb(void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP connect CB (%p %p)\n", arg, tci); + tci->state = TCP_data; + // send any buffered data + if (tci->txBuf != NULL && tci->txBufLen > 0) tcpDoSend(tci); +} + +// Sent callback +static void ICACHE_FLASH_ATTR tcpSentCb(void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP sent CB (%p %p)\n", arg, tci); + if (tci->txBufSent != NULL) os_free(tci->txBufSent); + tci->txBufSent = NULL; + + if (tci->txBuf != NULL && tci->txBufLen == MAX_TXBUF) { + // next buffer is full, send it now + tcpDoSend(tci); + } +} + +// Recv callback +static void ICACHE_FLASH_ATTR tcpRecvCb(void *arg, char *data, uint16_t len) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP recv CB (%p %p)\n", arg, tci); + if (tci->state == TCP_data) { + uart0_tx_buffer(data, len); + } + serledFlash(50); // short blink on serial LED +} + +// Disconnect callback +static void ICACHE_FLASH_ATTR tcpDisconCb(void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP disconnect CB (%p %p)\n", arg, tci); + tcpConnFree(tci); +} + +// Connection reset callback +static void ICACHE_FLASH_ATTR tcpResetCb(void *arg, sint8 err) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP reset CB (%p %p) err=%d\n", arg, tci, err); + tcpConnFree(tci); +} + +// Allocate a new connection dynamically and return it. Returns NULL if no +// connection could be allocated. +static TcpConn* ICACHE_FLASH_ATTR +tcpConnAlloc(void) { + int i; + for (i=0; iconn = os_malloc(sizeof(struct espconn)); + if (tci->conn == NULL) goto fail; + memset(tci->conn, 0, sizeof(struct espconn)); + // malloc esp_tcp struct + tci->tcp = os_malloc(sizeof(esp_tcp)); + if (tci->tcp == NULL) goto fail; + memset(tci->tcp, 0, sizeof(esp_tcp)); + + // common init + tci->state = TCP_dns; + tci->conn->type = ESPCONN_TCP; + tci->conn->state = ESPCONN_NONE; + tci->conn->proto.tcp = tci->tcp; + tci->tcp->remote_port = 80; + tci->tcp->remote_ip[0] = 173; + espconn_regist_connectcb(tci->conn, tcpConnectCb); + espconn_regist_reconcb(tci->conn, tcpResetCb); + espconn_regist_sentcb(tci->conn, tcpSentCb); + espconn_regist_recvcb(tci->conn, tcpRecvCb); + espconn_regist_disconcb(tci->conn, tcpDisconCb); + tci->conn->reverse = tci; + + return tci; + +fail: + tcpConnFree(tci); + return NULL; +} + +static TcpConn *tcConn[MAX_CHAN]; + +// Perform a TCP command: parse the command and do the right thing. +// Returns true on success. +bool ICACHE_FLASH_ATTR +tcpClientCommand(char cmd, char *cmdBuf) { + uint8_t tcChan; + TcpConn *tci; + + switch (cmd) { + //== TCP Connect command + case 'T': + if (*cmdBuf < '1' || *cmdBuf > ('0'+MAX_CHAN)) break; + tcChan = *cmdBuf++ - '1'; + char *hostname = cmdBuf; + char *port = hostname; + while (*port != 0 && *port != ':') port++; + if (*port != ':') break; + *port = 0; + port++; + int portInt = atoi(port); + if (portInt < 1 || portInt > 65535) break; + + // allocate a connection + tci = tcpConnAlloc(); + if (tci == NULL) break; + tci->state = TCP_dns; + tci->tcp->remote_port = portInt; + + // start the DNS resolution + os_printf("TCP %p resolving %s (conn=%p)\n", tci, hostname, tci->conn); + ip_addr_t ip; + err_t err = espconn_gethostbyname(tci->conn, hostname, &ip, tcpClientHostnameCb); + if (err == ESPCONN_OK) { + // dns cache hit, got the IP address, fake the callback (sigh) + os_printf("TCP DNS hit\n"); + tcpClientHostnameCb(hostname, &ip, tci->conn); + } else if (err != ESPCONN_INPROGRESS) { + tcpConnFree(tci); + break; + } + + tcConn[tcChan] = tci; + return true; + + //== TCP Close/disconnect command + case 'C': + if (*cmdBuf < '1' || *cmdBuf > ('0'+MAX_CHAN)) break; + tcChan = *cmdBuf++ - '1'; + tci = tcConn[tcChan]; + if (tci->state > TCP_idle) { + tci->state = TCP_idle; // hackish... + espconn_disconnect(tci->conn); + } + break; + + } + return false; +} + +void ICACHE_FLASH_ATTR +tcpClientSendChar(uint8_t chan, char c) { + TcpConn *tci = tcConn[chan]; + if (tci->state == TCP_idle) return; + + if (tci->txBuf != NULL) { + // we have a buffer + if (tci->txBufLen < MAX_TXBUF) { + // buffer has space, add char and return + tci->txBuf[tci->txBufLen++] = c; + return; + } else if (tci->txBufSent == NULL) { + // we don't have a send pending, send full buffer off + tcpDoSend(tci); + if (tci->txBuf != NULL) return; // something went wrong + } else { + // buffers all backed-up, drop char + return; + } + } + // we do not have a buffer (either didn't have one or sent it off) + // allocate one + tci->txBuf = os_malloc(MAX_TXBUF); + tci->txBufLen = 0; + if (tci->txBuf != NULL) { + tci->txBuf[tci->txBufLen++] = c; + } +} + +void ICACHE_FLASH_ATTR +tcpClientSendPush(uint8_t chan) { + TcpConn *tci = tcConn[chan]; + if (tci->state == TCP_idle) return; // no active connection on this channel + if (tci->txBuf == NULL || tci->txBufLen == 0) return; // no chars accumulated to send + if (tci->txBufSent != NULL) return; // already got a send in progress + tcpDoSend(tci); +} diff --git a/serial/tcpclient.h b/serial/tcpclient.h new file mode 100644 index 0000000..c80d2d3 --- /dev/null +++ b/serial/tcpclient.h @@ -0,0 +1,8 @@ +#ifndef __TCP_CLIENT_H__ +#define __TCP_CLIENT_H__ + +bool tcpClientCommand(char cmd, char *cmdBuf); +void tcpClientSendChar(uint8_t chan, char c); +void tcpClientSendPush(uint8_t chan); + +#endif /* __TCP_CLIENT_H__ */ diff --git a/user/cgiflash.c b/user/cgiflash.c index 70d4b1c..847b5ab 100644 --- a/user/cgiflash.c +++ b/user/cgiflash.c @@ -160,6 +160,7 @@ int ICACHE_FLASH_ATTR cgiRebootFirmware(HttpdConnData *connData) { int address = id == 1 ? 4*1024 // either start after 4KB boot partition : 4*1024 + FIRMWARE_SIZE + 16*1024 + 4*1024; // 4KB boot, fw1, 16KB user param, 4KB reserved uint32 buf[8]; + os_printf("Checking %p\n", (void *)address); spi_flash_read(address, buf, sizeof(buf)); char *err = check_header(buf); if (err != NULL) { diff --git a/user/status.c b/user/status.c index 23f693f..7b46d72 100644 --- a/user/status.c +++ b/user/status.c @@ -5,8 +5,11 @@ #include "serled.h" #include "cgiwifi.h" +//===== "CONN" LED status indication + static ETSTimer ledTimer; +// Set the LED on or off, respecting the defined polarity static void ICACHE_FLASH_ATTR setLed(int on) { int8_t pin = flashConfig.conn_led_pin; if (pin < 0) return; // disabled @@ -21,11 +24,12 @@ static void ICACHE_FLASH_ATTR setLed(int on) { static uint8_t ledState = 0; static uint8_t wifiState = 0; +// Timer callback to update the LED static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { int time = 1000; if (wifiState == wifiGotIP) { - // connected, all is good, solid light + // connected, all is good, solid light with a short dark blip every 3 seconds ledState = 1-ledState; time = ledState ? 2900 : 100; } else if (wifiState == wifiIsConnected) { @@ -33,7 +37,7 @@ static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { ledState = 1 - ledState; time = 1000; } else { - // idle + // not connected switch (wifi_get_opmode()) { case 1: // STA ledState = 0; @@ -53,7 +57,7 @@ static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { os_timer_arm(&ledTimer, time, 0); } -// change the wifi state +// change the wifi state indication void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { wifiState = state; // schedule an update (don't want to run into concurrency issues) @@ -62,6 +66,111 @@ void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { os_timer_arm(&ledTimer, 500, 0); } +//===== RSSI Status update sent to GroveStreams + +#define RSSI_INTERVAL (60*1000) + +static ETSTimer rssiTimer; + +static uint8_t rssiSendState = 0; +static sint8 rssiLast = 0; //last RSSI value + +static esp_tcp rssiTcp; +static struct espconn rssiConn; + +#define GS_API_KEY "2eb868a8-224f-3faa-939d-c79bd605912a" +#define GS_COMP_ID "esp-link" +#define GS_STREAM "rssi" + +// Connected callback +static void ICACHE_FLASH_ATTR rssiConnectCb(void *arg) { + struct espconn *conn = (struct espconn *)arg; + os_printf("RSSI connect CB (%p %p)\n", arg, conn->reverse); + + char buf[2000]; + + // http header + int hdrLen = os_sprintf(buf, + "PUT /api/feed?api_key=%s HTTP/1.0\r\n" + "Content-Type: application/json\r\n" + "Content-Length: XXXXX\r\n\r\n", + GS_API_KEY); + + // http body + int dataLen = os_sprintf(buf+hdrLen, + "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]", + GS_COMP_ID, GS_STREAM, rssiLast); + + // hackish way to fill in the content-length + os_sprintf(buf+hdrLen-9, "%5d", dataLen); + buf[hdrLen-4] = '\r'; + + // send it off + if (espconn_sent(conn, (uint8*)buf, hdrLen+dataLen) == ESPCONN_OK) { + os_printf("RSSI sent rssi=%d\n", rssiLast); + os_printf("RSSI sent <<%s>>\n", buf); + } +} + +// Sent callback +static void ICACHE_FLASH_ATTR rssiSentCb(void *arg) { + struct espconn *conn = (struct espconn *)arg; + os_printf("RSSI sent CB (%p %p)\n", arg, conn->reverse); +} + +// Recv callback +static void ICACHE_FLASH_ATTR rssiRecvCb(void *arg, char *data, uint16_t len) { + struct espconn *conn = (struct espconn *)arg; + os_printf("RSSI recv CB (%p %p)\n", arg, conn->reverse); + data[len] = 0; // hack!!! + os_printf("GOT %d: <<%s>>\n", len, data); + + espconn_disconnect(conn); +} + +// Disconnect callback +static void ICACHE_FLASH_ATTR rssiDisconCb(void *arg) { + struct espconn *conn = (struct espconn *)arg; + os_printf("RSSI disconnect CB (%p %p)\n", arg, conn->reverse); + rssiSendState = 0; +} + +// Connection reset callback +static void ICACHE_FLASH_ATTR rssiResetCb(void *arg, sint8 err) { + struct espconn *conn = (struct espconn *)arg; + os_printf("RSSI reset CB (%p %p) err=%d\n", arg, conn->reverse, err); + rssiSendState = 0; +} + +// Timer callback to send an RSSI update to a monitoring system +static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { + sint8 rssi = wifi_station_get_rssi(); + if (rssi >= 0) return; // not connected or other error + rssiLast = rssi; + if (rssiSendState > 0) return; // not done with previous rssi report + + rssiConn.type = ESPCONN_TCP; + rssiConn.state = ESPCONN_NONE; + rssiConn.proto.tcp = &rssiTcp; + rssiTcp.remote_port = 80; + rssiTcp.remote_ip[0] = 173; + rssiTcp.remote_ip[1] = 236; + rssiTcp.remote_ip[2] = 12; + rssiTcp.remote_ip[3] = 163; + espconn_regist_connectcb(&rssiConn, rssiConnectCb); + espconn_regist_reconcb(&rssiConn, rssiResetCb); + espconn_regist_sentcb(&rssiConn, rssiSentCb); + espconn_regist_recvcb(&rssiConn, rssiRecvCb); + espconn_regist_disconcb(&rssiConn, rssiDisconCb); + rssiConn.reverse = (void *)0xdeadf00d; + os_printf("RSSI connect (%p)\n", &rssiConn); + if (espconn_connect(&rssiConn) == ESPCONN_OK) { + rssiSendState++; + } +} + +//===== Init status stuff + void ICACHE_FLASH_ATTR statusInit(void) { if (flashConfig.conn_led_pin >= 0) { makeGpio(flashConfig.conn_led_pin); @@ -72,6 +181,10 @@ void ICACHE_FLASH_ATTR statusInit(void) { os_timer_disarm(&ledTimer); os_timer_setfn(&ledTimer, ledTimerCb, NULL); os_timer_arm(&ledTimer, 2000, 0); + + os_timer_disarm(&rssiTimer); + os_timer_setfn(&rssiTimer, rssiTimerCb, NULL); + os_timer_arm(&rssiTimer, RSSI_INTERVAL, 1); // recurring timer } From 2fbd9953d592be54fc8e54652f2612a9ae1787f6 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 1 Aug 2015 13:56:13 -0700 Subject: [PATCH 05/20] TCP client support; Grovestreams RSSI submission support --- html/home.html | 66 ++++++----- html/ui.js | 39 +++++++ httpd/httpd.c | 6 +- serial/serbridge.c | 66 +++++++++-- serial/tcpclient.c | 272 ++++++++++++++++++++++++++------------------- serial/tcpclient.h | 10 +- user/cgipins.c | 3 +- user/cgitcp.c | 74 ++++++++++++ user/cgitcp.h | 8 ++ user/config.c | 13 ++- user/config.h | 3 + user/status.c | 102 ++++------------- user/user_main.c | 7 +- 13 files changed, 422 insertions(+), 247 deletions(-) create mode 100644 user/cgitcp.c create mode 100644 user/cgitcp.h diff --git a/html/home.html b/html/home.html index 35c8436..ddc8e4a 100644 --- a/html/home.html +++ b/html/home.html @@ -26,16 +26,40 @@
-
-

Wifi summary

-
- - - - - - -
+
+
+

Wifi summary

+
+ + + + + + +
+
+

TCP client

+
+ TCP client support in esp-link +
+ + +
+
+ Grovestreams data push +
+ + +
+
+ + +
+ +
+
+

Pin assignment

Select one of the following signal/pin assignments to match your hardware @@ -45,26 +69,6 @@
-
-

HTTP service

-
- External web service configuration -
- - - - -
-
- - - - -
- -
-
@@ -74,6 +78,8 @@ onLoad(function() { fetchPins(); getWifiInfo(); + fetchTcpClient(); + bnd($("#tcpform"), "submit", changeTcpClient); }); diff --git a/html/ui.js b/html/ui.js index 5de2f4c..56cc256 100644 --- a/html/ui.js +++ b/html/ui.js @@ -356,3 +356,42 @@ function setPins(v, name) { }); } +//===== TCP client card + +function tcpEn(){return document.querySelector('input[name="tcp_enable"]')} +function rssiEn(){return document.querySelector('input[name="rssi_enable"]')} +function apiKey(){return document.querySelector('input[name="api_key"]')} + +function changeTcpClient(e) { + e.preventDefault(); + var url = "tcpclient"; + url += "?tcp_enable=" + tcpEn().checked; + url += "&rssi_enable=" + rssiEn().checked; + url += "&api_key=" + encodeURIComponent(apiKey().value); + + hideWarning(); + var cb = $("#tcp-button"); + addClass(cb, 'pure-button-disabled'); + ajaxSpin("POST", url, function(resp) { + removeClass(cb, 'pure-button-disabled'); + getWifiInfo(); + }, function(s, st) { + showWarning("Error: "+st); + removeClass(cb, 'pure-button-disabled'); + getWifiInfo(); + }); +} + +function displayTcpClient(resp) { + tcpEn().checked = resp.tcp_enable > 0; + rssiEn().checked = resp.rssi_enable > 0; + apiKey().value = resp.api_key; +} + +function fetchTcpClient() { + ajaxJson("GET", "/tcpclient", displayTcpClient, function() { + window.setTimeout(fetchTcpClient, 1000); + }); +} + + diff --git a/httpd/httpd.c b/httpd/httpd.c index 3249889..fe22047 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -314,13 +314,13 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { conn->cgi=NULL; //mark for destruction. } if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { - os_printf("%s ERROR! CGI fn returns code %d after sending data! Bad CGI!\n", connStr, r); + os_printf("%s ERROR! Bad CGI code %d\n", connStr, r); conn->cgi=NULL; //mark for destruction. } xmitSendBuff(conn); } -static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n"; +static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n"; //This is called when the headers have been received and the connection is ready to send //the result headers and data. @@ -419,7 +419,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { if (conn->getArgs!=0) { *conn->getArgs=0; conn->getArgs++; - os_printf("%s GET args = %s\n", connStr, conn->getArgs); + os_printf("%s args = %s\n", connStr, conn->getArgs); } else { conn->getArgs=NULL; } diff --git a/serial/serbridge.c b/serial/serbridge.c index 528c486..ca6e0af 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -263,9 +263,13 @@ enum { TC_idle, // in-between commands TC_newline, // newline seen TC_start, // start character (~) seen - TC_cmd, // command character (0) seen + TC_cmd, // command start (@) seen + TC_cmdChar, // command character seen TC_cmdLine, // accumulating command - TC_tdata, // data character (1-9) seen, and in text data mode + TC_tdchan, // saw data channel character + TC_tdlen1, // saw first data length character + TC_tdata0, // accumulate data, zero-terminated + TC_tdataN, // accumulate data, length-terminated }; static uint8_t tcState = TC_newline; static uint8_t tcChan; // channel for current command (index into tcConn) @@ -274,6 +278,7 @@ static uint8_t tcChan; // channel for current command (index into tcConn) static char tcCmdBuf[CMD_MAX]; static short tcCmdBufLen = 0; static char tcCmdChar; +static short tcLen; // scan a buffer for tcp client commands static int ICACHE_FLASH_ATTR @@ -291,17 +296,27 @@ tcpClientProcess(char *buf, int len) if (c == '~') tcState = TC_start; continue; // gobble up the ~ case TC_start: // saw ~, expect channel number - if (c == '0') { + if (c == '@') { tcState = TC_cmd; continue; - } else if (c >= '1' && c <= '9') { - tcChan = c-'1'; - tcState = TC_tdata; + } else if (c >= '0' && c <= '9') { + tcChan = c-'0'; + tcState = TC_tdchan; continue; } *out++ = '~'; // make up for '~' we skipped break; - case TC_cmd: // saw channel number 0 (command), expect command char + case TC_cmd: // saw control char (@), expect channel char + if (c >= '0' && c <= '9') { + tcChan = c-'0'; + tcState = TC_cmdChar; + continue; + } else { + *out++ = '~'; // make up for '~' we skipped + *out++ = '@'; // make up for '@' we skipped + break; + } + case TC_cmdChar: // saw channel number, expect command char tcCmdChar = c; // save command character tcCmdBufLen = 0; // empty the command buffer tcState = TC_cmdLine; @@ -310,11 +325,36 @@ tcpClientProcess(char *buf, int len) if (c != '\n') { if (tcCmdBufLen < CMD_MAX) tcCmdBuf[tcCmdBufLen++] = c; } else { - tcpClientCommand(tcCmdChar, tcCmdBuf); + tcpClientCommand(tcChan, tcCmdChar, tcCmdBuf); tcState = TC_newline; } continue; - case TC_tdata: // saw channel number, getting data characters + case TC_tdchan: // saw channel number, getting first length char + if (c >= '0' && c <= '9') { + tcLen = c-'0'; + } else if (c >= 'A' && c <= 'F') { + tcLen = c-'A'+10; + } else { + *out++ = '~'; // make up for '~' we skipped + *out++ = '0'+tcChan; + break; + } + tcState = TC_tdlen1; + continue; + case TC_tdlen1: // saw first length char, get second + tcLen *= 16; + if (c >= '0' && c <= '9') { + tcLen += c-'0'; + } else if (c >= 'A' && c <= 'F') { + tcLen += c-'A'+10; + } else { + *out++ = '~'; // make up for '~' we skipped + *out++ = '0'+tcChan; + break; + } + tcState = tcLen == 0 ? TC_tdata0 : TC_tdataN; + continue; + case TC_tdata0: // saw data length, getting data characters zero-terminated if (c != 0) { tcpClientSendChar(tcChan, c); } else { @@ -322,6 +362,14 @@ tcpClientProcess(char *buf, int len) tcState = TC_idle; } continue; + case TC_tdataN: // saw data length, getting data characters length-terminated + tcpClientSendChar(tcChan, c); + tcLen--; + if (tcLen == 0) { + tcpClientSendPush(tcChan); + tcState = TC_idle; + } + continue; } *out++ = c; } diff --git a/serial/tcpclient.c b/serial/tcpclient.c index 9580b18..3515459 100644 --- a/serial/tcpclient.c +++ b/serial/tcpclient.c @@ -1,4 +1,8 @@ // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// +// TCP client library allowing uControllers attached to the serial port to send commands +// to open/close TCP connections and send/recv data. +// The serial protocol is described in https://gist.github.com/tve/a46c44bf1f6b42bc572e #include #include "config.h" @@ -7,7 +11,7 @@ #include "tcpclient.h" // max number of channels the client can open -#define MAX_CHAN 8 +#define MAX_CHAN MAX_TCP_CHAN // size of tx buffer #define MAX_TXBUF 1024 @@ -28,8 +32,54 @@ typedef struct { enum TcpState state; } TcpConn; -#define MAX_CONN (8) -static TcpConn tcpConn[MAX_CONN]; +static TcpConn tcpConn[MAX_CHAN]; + +// forward declarations +static void tcpConnFree(TcpConn* tci); +static TcpConn* tcpConnAlloc(uint8_t chan); +static void tcpDoSend(TcpConn *tci); +static void tcpConnectCb(void *arg); +static void tcpDisconCb(void *arg); +static void tcpResetCb(void *arg, sint8 err); +static void tcpSentCb(void *arg); +static void tcpRecvCb(void *arg, char *data, uint16_t len); + +//===== allocate / free connections + +// Allocate a new connection dynamically and return it. Returns NULL if buf alloc failed +static TcpConn* ICACHE_FLASH_ATTR +tcpConnAlloc(uint8_t chan) { + TcpConn *tci = tcpConn+chan; + if (tci->state != TCP_idle && tci->conn != NULL) return tci; + + // malloc and return espconn struct + tci->conn = os_malloc(sizeof(struct espconn)); + if (tci->conn == NULL) goto fail; + memset(tci->conn, 0, sizeof(struct espconn)); + // malloc esp_tcp struct + tci->tcp = os_malloc(sizeof(esp_tcp)); + if (tci->tcp == NULL) goto fail; + memset(tci->tcp, 0, sizeof(esp_tcp)); + + // common init + tci->state = TCP_dns; + tci->conn->type = ESPCONN_TCP; + tci->conn->state = ESPCONN_NONE; + tci->conn->proto.tcp = tci->tcp; + tci->tcp->remote_port = 80; + espconn_regist_connectcb(tci->conn, tcpConnectCb); + espconn_regist_reconcb(tci->conn, tcpResetCb); + espconn_regist_sentcb(tci->conn, tcpSentCb); + espconn_regist_recvcb(tci->conn, tcpRecvCb); + espconn_regist_disconcb(tci->conn, tcpDisconCb); + tci->conn->reverse = tci; + + return tci; + +fail: + tcpConnFree(tci); + return NULL; +} // Free a connection dynamically. static void ICACHE_FLASH_ATTR @@ -41,6 +91,8 @@ tcpConnFree(TcpConn* tci) { memset(tci, 0, sizeof(TcpConn)); } +//===== DNS + // DNS name resolution callback static void ICACHE_FLASH_ATTR tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { @@ -66,12 +118,59 @@ tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { tcpConnFree(tci); } +//===== Connect / disconnect + +// Connected callback +static void ICACHE_FLASH_ATTR +tcpConnectCb(void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP connect CB (%p %p)\n", arg, tci); + tci->state = TCP_data; + // send any buffered data + if (tci->txBuf != NULL && tci->txBufLen > 0) tcpDoSend(tci); + // reply to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dC\n", tci-tcpConn); + uart0_tx_buffer(buf, l); +} + +// Disconnect callback +static void ICACHE_FLASH_ATTR tcpDisconCb(void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP disconnect CB (%p %p)\n", arg, tci); + // notify to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); + uart0_tx_buffer(buf, l); + // free + tcpConnFree(tci); +} + +// Connection reset callback +static void ICACHE_FLASH_ATTR tcpResetCb(void *arg, sint8 err) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP reset CB (%p %p) err=%d\n", arg, tci, err); + // notify to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); + uart0_tx_buffer(buf, l); + // free + tcpConnFree(tci); +} + +//===== Sending and receiving + // Send the next buffer (assumes that the connection is in a state that allows it) -static void tcpDoSend(TcpConn *tci) { +static void ICACHE_FLASH_ATTR +tcpDoSend(TcpConn *tci) { sint8 err = espconn_sent(tci->conn, (uint8*)tci->txBuf, tci->txBufLen); if (err == ESPCONN_OK) { // send successful os_printf("TCP sent (%p %p)\n", tci->conn, tci); + tci->txBuf[tci->txBufLen] = 0; os_printf("TCP data: %s\n", tci->txBuf); tci->txBufSent = tci->txBuf; tci->txBuf = NULL; tci->txBufLen = 0; @@ -81,18 +180,9 @@ static void tcpDoSend(TcpConn *tci) { } } -// Connected callback -static void ICACHE_FLASH_ATTR tcpConnectCb(void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP connect CB (%p %p)\n", arg, tci); - tci->state = TCP_data; - // send any buffered data - if (tci->txBuf != NULL && tci->txBufLen > 0) tcpDoSend(tci); -} - // Sent callback -static void ICACHE_FLASH_ATTR tcpSentCb(void *arg) { +static void ICACHE_FLASH_ATTR +tcpSentCb(void *arg) { struct espconn *conn = arg; TcpConn *tci = conn->reverse; os_printf("TCP sent CB (%p %p)\n", arg, tci); @@ -111,84 +201,71 @@ static void ICACHE_FLASH_ATTR tcpRecvCb(void *arg, char *data, uint16_t len) { TcpConn *tci = conn->reverse; os_printf("TCP recv CB (%p %p)\n", arg, tci); if (tci->state == TCP_data) { + uint8_t chan; + for (chan=0; chan= MAX_CHAN) return; // oops!? + char buf[6]; + short l = os_sprintf(buf, "\n~%d", chan); + uart0_tx_buffer(buf, l); uart0_tx_buffer(data, len); + uart0_tx_buffer("\0\n", 2); } serledFlash(50); // short blink on serial LED } -// Disconnect callback -static void ICACHE_FLASH_ATTR tcpDisconCb(void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP disconnect CB (%p %p)\n", arg, tci); - tcpConnFree(tci); -} - -// Connection reset callback -static void ICACHE_FLASH_ATTR tcpResetCb(void *arg, sint8 err) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP reset CB (%p %p) err=%d\n", arg, tci, err); - tcpConnFree(tci); -} +void ICACHE_FLASH_ATTR +tcpClientSendChar(uint8_t chan, char c) { + TcpConn *tci = tcpConn+chan; + if (tci->state == TCP_idle) return; -// Allocate a new connection dynamically and return it. Returns NULL if no -// connection could be allocated. -static TcpConn* ICACHE_FLASH_ATTR -tcpConnAlloc(void) { - int i; - for (i=0; itxBuf != NULL) { + // we have a buffer + if (tci->txBufLen < MAX_TXBUF) { + // buffer has space, add char and return + tci->txBuf[tci->txBufLen++] = c; + return; + } else if (tci->txBufSent == NULL) { + // we don't have a send pending, send full buffer off + if (tci->state == TCP_data) tcpDoSend(tci); + if (tci->txBuf != NULL) return; // something went wrong + } else { + // buffers all backed-up, drop char + return; + } } - if (i == MAX_CONN) return NULL; - - // found an empty slot, malloc and return espconn struct - TcpConn *tci = tcpConn+i; - tci->conn = os_malloc(sizeof(struct espconn)); - if (tci->conn == NULL) goto fail; - memset(tci->conn, 0, sizeof(struct espconn)); - // malloc esp_tcp struct - tci->tcp = os_malloc(sizeof(esp_tcp)); - if (tci->tcp == NULL) goto fail; - memset(tci->tcp, 0, sizeof(esp_tcp)); - - // common init - tci->state = TCP_dns; - tci->conn->type = ESPCONN_TCP; - tci->conn->state = ESPCONN_NONE; - tci->conn->proto.tcp = tci->tcp; - tci->tcp->remote_port = 80; - tci->tcp->remote_ip[0] = 173; - espconn_regist_connectcb(tci->conn, tcpConnectCb); - espconn_regist_reconcb(tci->conn, tcpResetCb); - espconn_regist_sentcb(tci->conn, tcpSentCb); - espconn_regist_recvcb(tci->conn, tcpRecvCb); - espconn_regist_disconcb(tci->conn, tcpDisconCb); - tci->conn->reverse = tci; - - return tci; + // we do not have a buffer (either didn't have one or sent it off) + // allocate one + tci->txBuf = os_malloc(MAX_TXBUF); + tci->txBufLen = 0; + if (tci->txBuf != NULL) { + tci->txBuf[tci->txBufLen++] = c; + } +} -fail: - tcpConnFree(tci); - return NULL; +void ICACHE_FLASH_ATTR +tcpClientSendPush(uint8_t chan) { + TcpConn *tci = tcpConn+chan; + if (tci->state != TCP_data) return; // no active connection on this channel + if (tci->txBuf == NULL || tci->txBufLen == 0) return; // no chars accumulated to send + if (tci->txBufSent != NULL) return; // already got a send in progress + tcpDoSend(tci); } -static TcpConn *tcConn[MAX_CHAN]; +//===== Command parsing // Perform a TCP command: parse the command and do the right thing. // Returns true on success. bool ICACHE_FLASH_ATTR -tcpClientCommand(char cmd, char *cmdBuf) { - uint8_t tcChan; +tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf) { TcpConn *tci; + char *hostname; + char *port; switch (cmd) { //== TCP Connect command case 'T': - if (*cmdBuf < '1' || *cmdBuf > ('0'+MAX_CHAN)) break; - tcChan = *cmdBuf++ - '1'; - char *hostname = cmdBuf; - char *port = hostname; + hostname = cmdBuf; + port = hostname; while (*port != 0 && *port != ':') port++; if (*port != ':') break; *port = 0; @@ -197,13 +274,13 @@ tcpClientCommand(char cmd, char *cmdBuf) { if (portInt < 1 || portInt > 65535) break; // allocate a connection - tci = tcpConnAlloc(); + tci = tcpConnAlloc(chan); if (tci == NULL) break; tci->state = TCP_dns; tci->tcp->remote_port = portInt; // start the DNS resolution - os_printf("TCP %p resolving %s (conn=%p)\n", tci, hostname, tci->conn); + os_printf("TCP %p resolving %s for chan %d (conn=%p)\n", tci, hostname, chan ,tci->conn); ip_addr_t ip; err_t err = espconn_gethostbyname(tci->conn, hostname, &ip, tcpClientHostnameCb); if (err == ESPCONN_OK) { @@ -215,14 +292,12 @@ tcpClientCommand(char cmd, char *cmdBuf) { break; } - tcConn[tcChan] = tci; return true; //== TCP Close/disconnect command case 'C': - if (*cmdBuf < '1' || *cmdBuf > ('0'+MAX_CHAN)) break; - tcChan = *cmdBuf++ - '1'; - tci = tcConn[tcChan]; + os_printf("TCP closing chan %d\n", chan); + tci = tcpConn+chan; if (tci->state > TCP_idle) { tci->state = TCP_idle; // hackish... espconn_disconnect(tci->conn); @@ -233,40 +308,3 @@ tcpClientCommand(char cmd, char *cmdBuf) { return false; } -void ICACHE_FLASH_ATTR -tcpClientSendChar(uint8_t chan, char c) { - TcpConn *tci = tcConn[chan]; - if (tci->state == TCP_idle) return; - - if (tci->txBuf != NULL) { - // we have a buffer - if (tci->txBufLen < MAX_TXBUF) { - // buffer has space, add char and return - tci->txBuf[tci->txBufLen++] = c; - return; - } else if (tci->txBufSent == NULL) { - // we don't have a send pending, send full buffer off - tcpDoSend(tci); - if (tci->txBuf != NULL) return; // something went wrong - } else { - // buffers all backed-up, drop char - return; - } - } - // we do not have a buffer (either didn't have one or sent it off) - // allocate one - tci->txBuf = os_malloc(MAX_TXBUF); - tci->txBufLen = 0; - if (tci->txBuf != NULL) { - tci->txBuf[tci->txBufLen++] = c; - } -} - -void ICACHE_FLASH_ATTR -tcpClientSendPush(uint8_t chan) { - TcpConn *tci = tcConn[chan]; - if (tci->state == TCP_idle) return; // no active connection on this channel - if (tci->txBuf == NULL || tci->txBufLen == 0) return; // no chars accumulated to send - if (tci->txBufSent != NULL) return; // already got a send in progress - tcpDoSend(tci); -} diff --git a/serial/tcpclient.h b/serial/tcpclient.h index c80d2d3..9bfeb01 100644 --- a/serial/tcpclient.h +++ b/serial/tcpclient.h @@ -1,8 +1,16 @@ #ifndef __TCP_CLIENT_H__ #define __TCP_CLIENT_H__ -bool tcpClientCommand(char cmd, char *cmdBuf); +// max number of channels the client can open +#define MAX_TCP_CHAN 8 + +// Parse and perform the commandm cmdBuf must be null-terminated +bool tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf); + +// Append a character to the specified channel void tcpClientSendChar(uint8_t chan, char c); + +// Enqueue the buffered characters for transmission on the specified channel void tcpClientSendPush(uint8_t chan); #endif /* __TCP_CLIENT_H__ */ diff --git a/user/cgipins.c b/user/cgipins.c index cfee33f..459f03e 100644 --- a/user/cgipins.c +++ b/user/cgipins.c @@ -1,3 +1,4 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt #include #include "cgi.h" @@ -93,11 +94,9 @@ int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { statusInit(); if (configSave()) { - os_printf("New config saved\n"); httpdStartResponse(connData, 200); httpdEndHeaders(connData); } else { - os_printf("*** Failed to save config ***\n"); httpdStartResponse(connData, 500); httpdEndHeaders(connData); httpdSend(connData, "Failed to save config", -1); diff --git a/user/cgitcp.c b/user/cgitcp.c new file mode 100644 index 0000000..f8d9036 --- /dev/null +++ b/user/cgitcp.c @@ -0,0 +1,74 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// // TCP Client settings + +#include +#include "cgi.h" +#include "config.h" +#include "cgitcp.h" + +// Cgi to return TCP client settings +int ICACHE_FLASH_ATTR cgiTcpGet(HttpdConnData *connData) { + char buff[1024]; + int len; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + len = os_sprintf(buff, "{ \"tcp_enable\":%d, \"rssi_enable\": %d, \"api_key\":\"%s\" }", + flashConfig.tcp_enable, flashConfig.rssi_enable, flashConfig.api_key); + + jsonHeader(connData, 200); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; +} + +// Cgi to change choice of pin assignments +int ICACHE_FLASH_ATTR cgiTcpSet(HttpdConnData *connData) { + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + // Handle tcp_enable flag + char buff[128]; + int len = httpdFindArg(connData->getArgs, "tcp_enable", buff, sizeof(buff)); + if (len <= 0) { + jsonHeader(connData, 400); + return HTTPD_CGI_DONE; + } + flashConfig.tcp_enable = os_strcmp(buff, "true") == 0; + + // Handle rssi_enable flag + len = httpdFindArg(connData->getArgs, "rssi_enable", buff, sizeof(buff)); + if (len <= 0) { + jsonHeader(connData, 400); + return HTTPD_CGI_DONE; + } + flashConfig.rssi_enable = os_strcmp(buff, "true") == 0; + + // Handle api_key flag + len = httpdFindArg(connData->getArgs, "api_key", buff, sizeof(buff)); + if (len < 0) { + jsonHeader(connData, 400); + return HTTPD_CGI_DONE; + } + buff[sizeof(flashConfig.api_key)-1] = 0; // ensure we don't get an overrun + os_strcpy(flashConfig.api_key, buff); + + if (configSave()) { + httpdStartResponse(connData, 200); + httpdEndHeaders(connData); + } else { + httpdStartResponse(connData, 500); + httpdEndHeaders(connData); + httpdSend(connData, "Failed to save config", -1); + } + return HTTPD_CGI_DONE; +} + +int ICACHE_FLASH_ATTR cgiTcp(HttpdConnData *connData) { + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiTcpGet(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiTcpSet(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } +} diff --git a/user/cgitcp.h b/user/cgitcp.h new file mode 100644 index 0000000..2b7b2a9 --- /dev/null +++ b/user/cgitcp.h @@ -0,0 +1,8 @@ +#ifndef CGITCP_H +#define CGITCP_H + +#include "httpd.h" + +int cgiTcp(HttpdConnData *connData); + +#endif diff --git a/user/config.c b/user/config.c index b840c8f..6023f5d 100644 --- a/user/config.c +++ b/user/config.c @@ -17,6 +17,9 @@ FlashConfig flashDefault = { "esp-link\0 ", // hostname 0, 0x00ffffff, 0, // static ip, netmask, gateway 0, // log mode + 0, // swap_uart + 1, 0, // tcp_enable, rssi_enable + "\0", // api_key }; typedef union { @@ -40,7 +43,6 @@ static void memDump(void *addr, int len) { #endif bool ICACHE_FLASH_ATTR configSave(void) { - FlashFull ff; memset(&ff, 0, sizeof(ff)); memcpy(&ff, &flashConfig, sizeof(FlashConfig)); @@ -48,7 +50,7 @@ bool ICACHE_FLASH_ATTR configSave(void) { // erase secondary uint32_t addr = FLASH_ADDR + (1-flash_pri)*FLASH_SECT; if (spi_flash_erase_sector(addr>>12) != SPI_FLASH_RESULT_OK) - return false; // no harm done, give up + goto fail; // no harm done, give up // calculate CRC ff.fc.seq = seq; ff.fc.magic = FLASH_MAGIC; @@ -60,11 +62,11 @@ bool ICACHE_FLASH_ATTR configSave(void) { // write primary with incorrect seq ff.fc.seq = 0xffffffff; if (spi_flash_write(addr, (void *)&ff, sizeof(ff)) != SPI_FLASH_RESULT_OK) - return false; // no harm done, give up + goto fail; // no harm done, give up // fill in correct seq ff.fc.seq = seq; if (spi_flash_write(addr, (void *)&ff, sizeof(uint32_t)) != SPI_FLASH_RESULT_OK) - return false; // most likely failed, but no harm if successful + goto fail; // most likely failed, but no harm if successful // now that we have safely written the new version, erase old primary addr = FLASH_ADDR + flash_pri*FLASH_SECT; flash_pri = 1-flash_pri; @@ -77,6 +79,9 @@ bool ICACHE_FLASH_ATTR configSave(void) { ff.fc.seq = seq; spi_flash_write(addr, (void *)&ff, sizeof(uint32_t)); return true; +fail: + os_printf("*** Failed to save config ***\n"); + return false; } void ICACHE_FLASH_ATTR configWipe(void) { diff --git a/user/config.h b/user/config.h index c326a37..51b0995 100644 --- a/user/config.h +++ b/user/config.h @@ -9,6 +9,9 @@ typedef struct { char hostname[32]; // if using DHCP uint32_t staticip, netmask, gateway; // using DHCP if staticip==0 uint8_t log_mode; // UART log debug mode + uint8_t swap_uart; // swap uart0 to gpio 13&15 + uint8_t tcp_enable, rssi_enable; // TCP client settings + char api_key[48]; // RSSI submission API key (Grovestreams for now) } FlashConfig; extern FlashConfig flashConfig; diff --git a/user/status.c b/user/status.c index 7b46d72..6d7747e 100644 --- a/user/status.c +++ b/user/status.c @@ -4,6 +4,7 @@ #include "config.h" #include "serled.h" #include "cgiwifi.h" +#include "tcpclient.h" //===== "CONN" LED status indication @@ -72,101 +73,44 @@ void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { static ETSTimer rssiTimer; -static uint8_t rssiSendState = 0; -static sint8 rssiLast = 0; //last RSSI value - -static esp_tcp rssiTcp; -static struct espconn rssiConn; - -#define GS_API_KEY "2eb868a8-224f-3faa-939d-c79bd605912a" -#define GS_COMP_ID "esp-link" #define GS_STREAM "rssi" -// Connected callback -static void ICACHE_FLASH_ATTR rssiConnectCb(void *arg) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI connect CB (%p %p)\n", arg, conn->reverse); +// Timer callback to send an RSSI update to a monitoring system +static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { + if (!flashConfig.rssi_enable || !flashConfig.tcp_enable || flashConfig.api_key[0]==0) + return; + + sint8 rssi = wifi_station_get_rssi(); + if (rssi >= 0) return; // not connected or other error - char buf[2000]; + // compose TCP command + uint8_t chan = MAX_TCP_CHAN-1; + tcpClientCommand(chan, 'T', "grovestreams.com:80"); - // http header + // compose http header + char buf[1024]; int hdrLen = os_sprintf(buf, "PUT /api/feed?api_key=%s HTTP/1.0\r\n" "Content-Type: application/json\r\n" "Content-Length: XXXXX\r\n\r\n", - GS_API_KEY); + flashConfig.api_key); // http body int dataLen = os_sprintf(buf+hdrLen, - "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]", - GS_COMP_ID, GS_STREAM, rssiLast); + "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", + flashConfig.hostname, GS_STREAM, rssi); + buf[hdrLen+dataLen++] = 0; + buf[hdrLen+dataLen++] = '\r'; // hackish way to fill in the content-length os_sprintf(buf+hdrLen-9, "%5d", dataLen); - buf[hdrLen-4] = '\r'; - - // send it off - if (espconn_sent(conn, (uint8*)buf, hdrLen+dataLen) == ESPCONN_OK) { - os_printf("RSSI sent rssi=%d\n", rssiLast); - os_printf("RSSI sent <<%s>>\n", buf); - } -} - -// Sent callback -static void ICACHE_FLASH_ATTR rssiSentCb(void *arg) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI sent CB (%p %p)\n", arg, conn->reverse); -} + buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) -// Recv callback -static void ICACHE_FLASH_ATTR rssiRecvCb(void *arg, char *data, uint16_t len) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI recv CB (%p %p)\n", arg, conn->reverse); - data[len] = 0; // hack!!! - os_printf("GOT %d: <<%s>>\n", len, data); - - espconn_disconnect(conn); -} - -// Disconnect callback -static void ICACHE_FLASH_ATTR rssiDisconCb(void *arg) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI disconnect CB (%p %p)\n", arg, conn->reverse); - rssiSendState = 0; -} - -// Connection reset callback -static void ICACHE_FLASH_ATTR rssiResetCb(void *arg, sint8 err) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI reset CB (%p %p) err=%d\n", arg, conn->reverse, err); - rssiSendState = 0; -} - -// Timer callback to send an RSSI update to a monitoring system -static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { - sint8 rssi = wifi_station_get_rssi(); - if (rssi >= 0) return; // not connected or other error - rssiLast = rssi; - if (rssiSendState > 0) return; // not done with previous rssi report - - rssiConn.type = ESPCONN_TCP; - rssiConn.state = ESPCONN_NONE; - rssiConn.proto.tcp = &rssiTcp; - rssiTcp.remote_port = 80; - rssiTcp.remote_ip[0] = 173; - rssiTcp.remote_ip[1] = 236; - rssiTcp.remote_ip[2] = 12; - rssiTcp.remote_ip[3] = 163; - espconn_regist_connectcb(&rssiConn, rssiConnectCb); - espconn_regist_reconcb(&rssiConn, rssiResetCb); - espconn_regist_sentcb(&rssiConn, rssiSentCb); - espconn_regist_recvcb(&rssiConn, rssiRecvCb); - espconn_regist_disconcb(&rssiConn, rssiDisconCb); - rssiConn.reverse = (void *)0xdeadf00d; - os_printf("RSSI connect (%p)\n", &rssiConn); - if (espconn_connect(&rssiConn) == ESPCONN_OK) { - rssiSendState++; + // send the request off and forget about it... + for (short i=0; i Date: Sat, 1 Aug 2015 21:55:32 -0700 Subject: [PATCH 06/20] updated readme --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 78d0027..e7898ae 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ ESP-LINK ======== -This firmware implements a transparent bridge between Wifi and serial using an ESP8266 module. -It also provides support for flash-programming Arduino/AVR microcontrollers as well as -LPC800-series and other ARM microcontrollers via the ESP8266. +This firmware connects an attached micro-controller to the internet using a ESP8266 Wifi module. +It implements a number of features: +- transparent bridge between Wifi and serial, useful for debugging or inputting into a uC +- flash-programming attached Arduino/AVR microcontrollers as well as LPC800-series and other + ARM microcontrollers via Wifi +- outbound TCP (and thus HTTP) connections from the attached micro-controller to the internet The firmware includes a tiny HTTP server based on [esphttpd](http://www.esp8266.com/viewforum.php?f=34) @@ -12,6 +15,8 @@ with a simple web interface, many thanks to Jeroen Domburg for making it availab [![Chat at https://gitter.im/jeelabs/esp-link](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jeelabs/esp-link?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ###[Latest release](https://github.com/jeelabs/esp-link/releases) +Note that the [stable V1.0 release](https://github.com/jeelabs/esp-link/releases/tag/v1.0.0) is +recommended if you do not need the outbound TCP connections and have a 512KB flash chip. Eye Candy --------- @@ -215,6 +220,12 @@ modes are supported that can be set in the web UI (and the mode is saved in flas Note that even if the UART log is always off the bootloader prints to uart0 whenever the esp8266 comes out of reset. This cannot be disabled. +Outbound TCP connections +------------------------ +The attached micro-controller can open outbound TCP connections using a simple +[serial protocol](https://gist.github.com/tve/a46c44bf1f6b42bc572e). +More info and sample code forthcoming... + Contact ------- If you find problems with esp-link, please create a github issue. If you have a question, please From 5fc65bd7ee3ad1f767c3c7907770d656635c2416 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 1 Aug 2015 23:43:01 -0700 Subject: [PATCH 07/20] add support for wifi-link-12 with swapped uart pins --- Makefile | 2 +- serial/serbridge.c | 13 +++++++++++-- user/cgipins.c | 20 ++++++++++++-------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 32cce5c..99d5617 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ ESPBAUD ?= 460800 # --------------- chipset configuration --------------- # Pick your flash size: "512KB" or "4MB" -FLASH_SIZE ?= 512KB +FLASH_SIZE ?= 4MB ifeq ("$(FLASH_SIZE)","512KB") # Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 diff --git a/serial/serbridge.c b/serial/serbridge.c index 449afbd..c4306f7 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -457,9 +457,18 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { void ICACHE_FLASH_ATTR serbridgeInitPins() { mcu_reset_pin = flashConfig.reset_pin; mcu_isp_pin = flashConfig.isp_pin; - os_printf("Serbridge pins: reset=%d isp=%d\n", mcu_reset_pin, mcu_isp_pin); + os_printf("Serbridge pins: reset=%d isp=%d swap=%d\n", + mcu_reset_pin, mcu_isp_pin, flashConfig.swap_uart); - if (flashConfig.swap_uart) system_uart_swap(); else system_uart_de_swap(); + if (flashConfig.swap_uart) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4); + system_uart_swap(); + } else { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, 0); + system_uart_de_swap(); + } // set both pins to 1 before turning them on so we don't cause a reset if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); diff --git a/user/cgipins.c b/user/cgipins.c index fd8b554..2d71849 100644 --- a/user/cgipins.c +++ b/user/cgipins.c @@ -24,15 +24,13 @@ static const int num_map_func = sizeof(map_func)/sizeof(char*); // Cgi to return choice of pin assignments int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) { - char buff[1024]; - int len; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted - if (connData->conn==NULL) { - return HTTPD_CGI_DONE; // Connection aborted - } + char buff[2048]; + int len; // figure out current mapping - int curr = 99; + int curr = 0; for (int i=0; i= 0) len += os_sprintf(buff+len, " %s:gpio%d", map_func[f], p); - else len += os_sprintf(buff+len, " %s:n/a", map_func[f]); + if (f == 4) + len += os_sprintf(buff+len, " %s:%s", map_func[f], p?"yes":"no"); + else if (p >= 0) + len += os_sprintf(buff+len, " %s:gpio%d", map_func[f], p); + else + len += os_sprintf(buff+len, " %s:n/a", map_func[f]); } len += os_sprintf(buff+len, "\" }"); } @@ -89,6 +92,7 @@ int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { flashConfig.isp_pin = map[1]; flashConfig.conn_led_pin = map[2]; flashConfig.ser_led_pin = map[3]; + flashConfig.swap_uart = map[4]; serbridgeInitPins(); serledInit(); From 71bc355038d08733f4454645849c95011a213770 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 3 Aug 2015 20:56:39 -0700 Subject: [PATCH 08/20] clear bssid before reassociating --- user/cgiwifi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/user/cgiwifi.c b/user/cgiwifi.c index 7292aa4..0d96e96 100644 --- a/user/cgiwifi.c +++ b/user/cgiwifi.c @@ -261,6 +261,7 @@ static ETSTimer reassTimer; static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { os_printf("Wifi changing association\n"); wifi_station_disconnect(); + stconf.bssid_set = 0; wifi_station_set_config(&stconf); wifi_station_connect(); // Schedule check From 29100391b1c84d970b2ee4d9709fd56c87d97248 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 3 Aug 2015 22:20:36 -0700 Subject: [PATCH 09/20] fix TCP client request buffer parsing --- serial/tcpclient.c | 7 ++++++- user/status.c | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/serial/tcpclient.c b/serial/tcpclient.c index 3515459..166f533 100644 --- a/serial/tcpclient.c +++ b/serial/tcpclient.c @@ -261,10 +261,15 @@ tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf) { char *hostname; char *port; + // copy the command so we can modify it + char buf[128]; + os_strncpy(buf, cmdBuf, 128); + buf[127] = 0; + switch (cmd) { //== TCP Connect command case 'T': - hostname = cmdBuf; + hostname = buf; port = hostname; while (*port != 0 && *port != ':') port++; if (*port != ':') break; diff --git a/user/status.c b/user/status.c index 6d7747e..50afb85 100644 --- a/user/status.c +++ b/user/status.c @@ -81,6 +81,7 @@ static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { return; sint8 rssi = wifi_station_get_rssi(); + os_printf("timer rssi=%d\n", rssi); if (rssi >= 0) return; // not connected or other error // compose TCP command @@ -100,7 +101,7 @@ static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", flashConfig.hostname, GS_STREAM, rssi); buf[hdrLen+dataLen++] = 0; - buf[hdrLen+dataLen++] = '\r'; + buf[hdrLen+dataLen++] = '\n'; // hackish way to fill in the content-length os_sprintf(buf+hdrLen-9, "%5d", dataLen); From a74177383a2ebb2458ba3a341daba015ed5d6ba3 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 8 Aug 2015 00:53:55 -0700 Subject: [PATCH 10/20] fix serial bridge problem --- serial/serbridge.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/serial/serbridge.c b/serial/serbridge.c index c4306f7..5c00175 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -26,16 +26,7 @@ serbridgeConnData connData[MAX_CONN]; // Given a pointer to an espconn struct find the connection that correcponds to it static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) { struct espconn *conn = arg; - return (serbridgeConnData *)conn->reverse; -#if 0 - for (int i=0; ireverse; } //===== TCP -> UART @@ -293,8 +284,12 @@ tcpClientProcess(char *buf, int len) if (c == '\n') tcState = TC_newline; break; case TC_newline: // saw newline, expect ~ - if (c == '~') tcState = TC_start; - continue; // gobble up the ~ + if (c == '~') { + tcState = TC_start; + continue; // gobble up the ~ + } else { + break; + } case TC_start: // saw ~, expect channel number if (c == '@') { tcState = TC_cmd; @@ -373,7 +368,7 @@ tcpClientProcess(char *buf, int len) } *out++ = c; } - if (tcState != TC_idle) os_printf("tcState=%d\n", tcState); + //if (tcState != TC_idle) os_printf("tcState=%d\n", tcState); return out-buf; } @@ -387,7 +382,7 @@ serbridgeUartCb(char *buf, int length) { length = tcpClientProcess(buf, length); // push the buffer into each open connection if (length > 0) { - for (int i = 0; i < MAX_CONN; ++i) { + for (int i=0; i Date: Wed, 19 Aug 2015 13:39:01 -0700 Subject: [PATCH 11/20] tweak \n handling on console page --- serial/console.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/serial/console.c b/serial/console.c index d38e73a..6fa4374 100644 --- a/serial/console.c +++ b/serial/console.c @@ -34,16 +34,18 @@ console_write(char c) { } } +#if 0 // return previous character in console, 0 if at start static char ICACHE_FLASH_ATTR console_prev(void) { if (console_wr == console_rd) return 0; return console_buf[(console_wr-1+BUF_MAX)%BUF_MAX]; } +#endif void ICACHE_FLASH_ATTR console_write_char(char c) { - if (c == '\n' && console_prev() != '\r') console_write('\r'); + //if (c == '\n' && console_prev() != '\r') console_write('\r'); // does more harm than good console_write(c); } @@ -112,6 +114,8 @@ ajaxConsole(HttpdConnData *connData) { if (c == '\\' || c == '"') { buff[len++] = '\\'; buff[len++] = c; + } else if (c == '\r') { + // this is crummy, but browsers display a newline for \r\n sequences } else if (c < ' ') { len += os_sprintf(buff+len, "\\u%04x", c); } else { From dd7f9a16a906e0beea00608b919b2969ae327a87 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:13:30 -0700 Subject: [PATCH 12/20] better support for 4MB flash, add 1MB flash --- Makefile | 30 +++++++++++++++++++++++------- user/config.c | 8 +++++++- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 99d5617..df0bd6d 100644 --- a/Makefile +++ b/Makefile @@ -34,21 +34,37 @@ FLASH_SIZE ?= 4MB ifeq ("$(FLASH_SIZE)","512KB") # Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 -ESP_SPI_SIZE ?= 0 # 0->512KB -ESP_FLASH_MODE ?= 0 # 0->QIO -ESP_FLASH_FREQ_DIV ?= 0 # 0->40Mhz -ESP_FLASH_MAX ?= 241664 # max bin file for 512KB flash: 236KB +ESP_SPI_SIZE ?= 0 # 0->512KB (256KB+256KB) +ESP_FLASH_MODE ?= 0 # 0->QIO +ESP_FLASH_FREQ_DIV ?= 0 # 0->40Mhz +ESP_FLASH_MAX ?= 241664 # max bin file for 512KB flash: 236KB +ET_FS ?= 4m # 4Mbit flash size in esptool flash command +ET_FF ?= 40m # 40Mhz flash speed in esptool flash command +ET_BLANK ?= 0x7E000 # where to flash blank.bin to erase wireless settings + +else ifeq ("$(FLASH_SIZE)","1MB") +# ESP-01E +ESP_SPI_SIZE ?= 2 # 2->1MB (512KB+512KB) +ESP_FLASH_MODE ?= 0 # 0->QIO +ESP_FLASH_FREQ_DIV ?= 15 # 15->80MHz +ESP_FLASH_MAX ?= 503808 # max bin file for 1MB flash: 492KB +ET_FS ?= 8m # 8Mbit flash size in esptool flash command +ET_FF ?= 80m # 80Mhz flash speed in esptool flash command +ET_BLANK ?= 0xFE000 # where to flash blank.bin to erase wireless settings else # Winbond 25Q32 4MB flash, typ for esp-12 # Here we're using two partitions of approx 0.5MB because that's what's easily available in terms # of linker scripts in the SDK. Ideally we'd use two partitions of approx 1MB, the remaining 2MB -# cannot be used for code. +# cannot be used for code (esp8266 limitation). ESP_SPI_SIZE ?= 4 # 6->4MB (1MB+1MB) or 4->4MB (512KB+512KB) ESP_FLASH_MODE ?= 0 # 0->QIO, 2->DIO ESP_FLASH_FREQ_DIV ?= 15 # 15->80Mhz ESP_FLASH_MAX ?= 503808 # max bin file for 512KB flash partition: 492KB #ESP_FLASH_MAX ?= 1028096 # max bin file for 1MB flash partition: 1004KB +ET_FS ?= 32m # 32Mbit flash size in esptool flash command +ET_FF ?= 80m # 80Mhz flash speed in esptool flash command +ET_BLANK ?= 0x3FE000 # where to flash blank.bin to erase wireless settings endif # hostname or IP address for wifi flashing @@ -274,9 +290,9 @@ wiflash: all ./wiflash $(ESP_HOSTNAME) $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin flash: all - $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash \ + $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) -fs $(ET_FS) -ff $(ET_FF) write_flash \ 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \ - 0x7E000 $(SDK_BASE)/bin/blank.bin + $(ET_BLANK) $(SDK_BASE)/bin/blank.bin yui/$(YUI-COMPRESSOR): $(Q) mkdir -p yui diff --git a/user/config.c b/user/config.c index 6023f5d..c44a188 100644 --- a/user/config.c +++ b/user/config.c @@ -27,10 +27,16 @@ typedef union { uint8_t block[128]; } FlashFull; +// magic number to recognize thet these are our flash settings as opposed to some random stuff #define FLASH_MAGIC (0xaa55) -#define FLASH_ADDR (0x3E000) +// size of the setting sector #define FLASH_SECT (4096) + +// address where to flash the settings: there are 16KB of reserved space at the end of the first +// flash partition, we use the upper 8KB (2 sectors) +#define FLASH_ADDR (FLASH_SECT + FIRMWARE_SIZE + 2*FLASH_SECT) + static int flash_pri; // primary flash sector (0 or 1, or -1 for error) #if 0 From a58904f1be34c6b1f76a8b53e23666d93fb9da81 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:13:55 -0700 Subject: [PATCH 13/20] cgiflash cleanup --- user/cgiflash.c | 24 ------------------------ user/config.c | 4 ++-- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/user/cgiflash.c b/user/cgiflash.c index 597b73a..e07443b 100644 --- a/user/cgiflash.c +++ b/user/cgiflash.c @@ -31,30 +31,6 @@ static char* ICACHE_FLASH_ATTR check_header(void *buf) { return NULL; } -#if 0 -//===== Cgi that reads the SPI flash. Assumes 512KByte flash. -int ICACHE_FLASH_ATTR cgiReadFlash(HttpdConnData *connData) { - int *pos=(int *)&connData->cgiData; - if (connData->conn==NULL) { - //Connection aborted. Clean up. - return HTTPD_CGI_DONE; - } - - if (*pos==0) { - os_printf("Start flash download.\n"); - httpdStartResponse(connData, 200); - httpdHeader(connData, "Content-Type", "application/bin"); - httpdEndHeaders(connData); - *pos=0x40200000; - return HTTPD_CGI_MORE; - } - //Send 1K of flash per call. We will get called again if we haven't sent 512K yet. - espconn_sent(connData->conn, (uint8 *)(*pos), 1024); - *pos+=1024; - if (*pos>=0x40200000+(512*1024)) return HTTPD_CGI_DONE; else return HTTPD_CGI_MORE; -} -#endif - //===== Cgi to query which firmware needs to be uploaded next int ICACHE_FLASH_ATTR cgiGetFirmwareNext(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. diff --git a/user/config.c b/user/config.c index c44a188..c15f2ef 100644 --- a/user/config.c +++ b/user/config.c @@ -95,7 +95,7 @@ void ICACHE_FLASH_ATTR configWipe(void) { spi_flash_erase_sector((FLASH_ADDR+FLASH_SECT)>>12); } -static uint32_t ICACHE_FLASH_ATTR selectPrimary(FlashFull *fc0, FlashFull *fc1); +static int ICACHE_FLASH_ATTR selectPrimary(FlashFull *fc0, FlashFull *fc1); bool ICACHE_FLASH_ATTR configRestore(void) { FlashFull ff0, ff1; @@ -117,7 +117,7 @@ bool ICACHE_FLASH_ATTR configRestore(void) { return true; } -static uint32_t ICACHE_FLASH_ATTR selectPrimary(FlashFull *ff0, FlashFull *ff1) { +static int ICACHE_FLASH_ATTR selectPrimary(FlashFull *ff0, FlashFull *ff1) { // check CRC of ff0 uint16_t crc = ff0->fc.crc; ff0->fc.crc = 0; From bc063ef71dfbd314db2f2c4e661985e2463d24f4 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:14:10 -0700 Subject: [PATCH 14/20] correct reset codes --- user/user_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/user_main.c b/user/user_main.c index 4b8bdab..54ca59e 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -113,7 +113,7 @@ void user_rf_pre_init(void) { extern uint32_t _binary_espfs_img_start; static char *rst_codes[] = { - "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "???", + "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", }; # define VERS_STR_STR(V) #V From aa7eadb382340cef5886aad69d53122e7e966983 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:30:59 -0700 Subject: [PATCH 15/20] small flash config improvement plus more comments Conflicts: user/config.c --- user/config.c | 2 +- user/config.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/user/config.c b/user/config.c index c15f2ef..835e74f 100644 --- a/user/config.c +++ b/user/config.c @@ -17,7 +17,7 @@ FlashConfig flashDefault = { "esp-link\0 ", // hostname 0, 0x00ffffff, 0, // static ip, netmask, gateway 0, // log mode - 0, // swap_uart + 0, // swap_uart (don't by default) 1, 0, // tcp_enable, rssi_enable "\0", // api_key }; diff --git a/user/config.h b/user/config.h index 51b0995..18284a5 100644 --- a/user/config.h +++ b/user/config.h @@ -1,6 +1,10 @@ #ifndef CONFIG_H #define CONFIG_H +// Flash configuration settings. When adding new items always add them at the end and formulate +// them such that a value of zero is an appropriate default or backwards compatible. Existing +// modules that are upgraded will have zero in the new fields. This ensures that an upgrade does +// not wipe out the old settings. typedef struct { uint32_t seq; // flash write sequence number uint16_t magic, crc; From a2990767e129bfffa008551663f0cc7ff247a570 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 21:43:08 -0700 Subject: [PATCH 16/20] makefile tweak Conflicts: Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index df0bd6d..8629ddb 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,8 @@ ESPBAUD ?= 460800 # --------------- chipset configuration --------------- -# Pick your flash size: "512KB" or "4MB" -FLASH_SIZE ?= 4MB +# Pick your flash size: "512KB", "1MB", or "4MB" +FLASH_SIZE ?= 512KB ifeq ("$(FLASH_SIZE)","512KB") # Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 From 6867989b477da5fc5fdd652e9aea131b95f5d3b8 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 21 Aug 2015 23:10:41 -0700 Subject: [PATCH 17/20] remove heatshrink submodule; switch to SDK 1.3 --- .gitmodules | 3 --- Makefile | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 772d424..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "lib/heatshrink"] - path = lib/heatshrink - url = https://github.com/atomicobject/heatshrink.git diff --git a/Makefile b/Makefile index 8629ddb..5b694ae 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ XTENSA_TOOLS_ROOT ?= $(abspath ../esp-open-sdk/xtensa-lx106-elf/bin)/ # Base directory of the ESP8266 SDK package, absolute # Typically you'll download from Espressif's BBS, http://bbs.espressif.com/viewforum.php?f=5 -SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.2.0) +SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.3.0) # Esptool.py path and port, only used for 1-time serial flashing # Typically you'll use https://github.com/themadinventor/esptool @@ -30,7 +30,7 @@ ESPBAUD ?= 460800 # --------------- chipset configuration --------------- # Pick your flash size: "512KB", "1MB", or "4MB" -FLASH_SIZE ?= 512KB +FLASH_SIZE ?= 4MB ifeq ("$(FLASH_SIZE)","512KB") # Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 From 7490e45aab45854e675830b1da22635343e773e4 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 22 Aug 2015 10:29:51 -0700 Subject: [PATCH 18/20] fix indentation to use spaces --- serial/console.c | 160 ++++++++--------- serial/serbridge.h | 20 +-- serial/tcpclient.c | 422 ++++++++++++++++++++++----------------------- 3 files changed, 301 insertions(+), 301 deletions(-) diff --git a/serial/console.c b/serial/console.c index 6fa4374..d0d0dd7 100644 --- a/serial/console.c +++ b/serial/console.c @@ -25,112 +25,112 @@ static int console_pos; // offset since reset of buffer static void ICACHE_FLASH_ATTR console_write(char c) { - console_buf[console_wr] = c; - console_wr = (console_wr+1) % BUF_MAX; - if (console_wr == console_rd) { - // full, we write anyway and loose the oldest char - console_rd = (console_rd+1) % BUF_MAX; // full, eat first char - console_pos++; - } + console_buf[console_wr] = c; + console_wr = (console_wr+1) % BUF_MAX; + if (console_wr == console_rd) { + // full, we write anyway and loose the oldest char + console_rd = (console_rd+1) % BUF_MAX; // full, eat first char + console_pos++; + } } #if 0 // return previous character in console, 0 if at start static char ICACHE_FLASH_ATTR console_prev(void) { - if (console_wr == console_rd) return 0; - return console_buf[(console_wr-1+BUF_MAX)%BUF_MAX]; + if (console_wr == console_rd) return 0; + return console_buf[(console_wr-1+BUF_MAX)%BUF_MAX]; } #endif void ICACHE_FLASH_ATTR console_write_char(char c) { - //if (c == '\n' && console_prev() != '\r') console_write('\r'); // does more harm than good - console_write(c); + //if (c == '\n' && console_prev() != '\r') console_write('\r'); // does more harm than good + console_write(c); } int ICACHE_FLASH_ATTR ajaxConsoleReset(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - console_rd = console_wr = console_pos = 0; - serbridgeReset(); - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + console_rd = console_wr = console_pos = 0; + serbridgeReset(); + return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR ajaxConsoleBaud(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[512]; - int len, status = 400; - len = httpdFindArg(connData->getArgs, "rate", buff, sizeof(buff)); - if (len > 0) { - int rate = atoi(buff); - if (rate >= 9600 && rate <= 1000000) { - uart0_baud(rate); - flashConfig.baud_rate = rate; - status = configSave() ? 200 : 400; - } - } else if (connData->requestType == HTTPD_METHOD_GET) { - status = 200; - } - - jsonHeader(connData, status); - os_sprintf(buff, "{\"rate\": %ld}", flashConfig.baud_rate); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[512]; + int len, status = 400; + len = httpdFindArg(connData->getArgs, "rate", buff, sizeof(buff)); + if (len > 0) { + int rate = atoi(buff); + if (rate >= 9600 && rate <= 1000000) { + uart0_baud(rate); + flashConfig.baud_rate = rate; + status = configSave() ? 200 : 400; + } + } else if (connData->requestType == HTTPD_METHOD_GET) { + status = 200; + } + + jsonHeader(connData, status); + os_sprintf(buff, "{\"rate\": %ld}", flashConfig.baud_rate); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR ajaxConsole(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[2048]; - int len; // length of text in buff - int console_len = (console_wr+BUF_MAX-console_rd) % BUF_MAX; // num chars in console_buf - int start = 0; // offset onto console_wr to start sending out chars - - jsonHeader(connData, 200); - - // figure out where to start in buffer based on URI param - len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); - if (len > 0) { - start = atoi(buff); - if (start < console_pos) { - start = 0; - } else if (start >= console_pos+console_len) { - start = console_len; - } else { - start = start - console_pos; - } - } - - // start outputting - len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", - console_len-start, console_pos+start); - - int rd = (console_rd+start) % BUF_MAX; - while (len < 2040 && rd != console_wr) { - uint8_t c = console_buf[rd]; - if (c == '\\' || c == '"') { - buff[len++] = '\\'; - buff[len++] = c; - } else if (c == '\r') { - // this is crummy, but browsers display a newline for \r\n sequences - } else if (c < ' ') { - len += os_sprintf(buff+len, "\\u%04x", c); - } else { - buff[len++] = c; - } - rd = (rd + 1) % BUF_MAX; - } - os_strcpy(buff+len, "\"}"); len+=2; - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[2048]; + int len; // length of text in buff + int console_len = (console_wr+BUF_MAX-console_rd) % BUF_MAX; // num chars in console_buf + int start = 0; // offset onto console_wr to start sending out chars + + jsonHeader(connData, 200); + + // figure out where to start in buffer based on URI param + len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); + if (len > 0) { + start = atoi(buff); + if (start < console_pos) { + start = 0; + } else if (start >= console_pos+console_len) { + start = console_len; + } else { + start = start - console_pos; + } + } + + // start outputting + len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", + console_len-start, console_pos+start); + + int rd = (console_rd+start) % BUF_MAX; + while (len < 2040 && rd != console_wr) { + uint8_t c = console_buf[rd]; + if (c == '\\' || c == '"') { + buff[len++] = '\\'; + buff[len++] = c; + } else if (c == '\r') { + // this is crummy, but browsers display a newline for \r\n sequences + } else if (c < ' ') { + len += os_sprintf(buff+len, "\\u%04x", c); + } else { + buff[len++] = c; + } + rd = (rd + 1) % BUF_MAX; + } + os_strcpy(buff+len, "\"}"); len+=2; + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } void ICACHE_FLASH_ATTR consoleInit() { - console_wr = 0; - console_rd = 0; + console_wr = 0; + console_rd = 0; } diff --git a/serial/serbridge.h b/serial/serbridge.h index fea8602..33c96a2 100644 --- a/serial/serbridge.h +++ b/serial/serbridge.h @@ -12,21 +12,21 @@ #define MAX_TXBUFFER 1024 enum connModes { - cmInit = 0, // initialization mode: nothing received yet - cmTransparent, // transparent mode - cmAVR, // Arduino/AVR programming mode - cmARM, // ARM (LPC8xx) programming - cmEcho, // simply echo characters (used for debugging latency) + cmInit = 0, // initialization mode: nothing received yet + cmTransparent, // transparent mode + cmAVR, // Arduino/AVR programming mode + cmARM, // ARM (LPC8xx) programming + cmEcho, // simply echo characters (used for debugging latency) cmTelnet, // use telnet escape sequences for programming mode cmTcpClient, // client connection (initiated via serial) }; typedef struct serbridgeConnData { - struct espconn *conn; - enum connModes conn_mode; // connection mode - char *txbuffer; // buffer for the data to send - uint16 txbufferlen; // length of data in txbuffer - bool readytosend; // true, if txbuffer can send by espconn_sent + struct espconn *conn; + enum connModes conn_mode; // connection mode + char *txbuffer; // buffer for the data to send + uint16 txbufferlen; // length of data in txbuffer + bool readytosend; // true, if txbuffer can send by espconn_sent uint8_t telnet_state; } serbridgeConnData; diff --git a/serial/tcpclient.c b/serial/tcpclient.c index 166f533..fa1b458 100644 --- a/serial/tcpclient.c +++ b/serial/tcpclient.c @@ -16,20 +16,20 @@ #define MAX_TXBUF 1024 enum TcpState { - TCP_idle, // unused connection - TCP_dns, // doing gethostbyname - TCP_conn, // connecting to remote server - TCP_data, // connected + TCP_idle, // unused connection + TCP_dns, // doing gethostbyname + TCP_conn, // connecting to remote server + TCP_data, // connected }; // Connections typedef struct { - struct espconn *conn; // esp connection structure - esp_tcp *tcp; // esp TCP parameters - char *txBuf; // buffer to accumulate into - char *txBufSent; // buffer held by espconn - uint8_t txBufLen; // number of chars in txbuf - enum TcpState state; + struct espconn *conn; // esp connection structure + esp_tcp *tcp; // esp TCP parameters + char *txBuf; // buffer to accumulate into + char *txBufSent; // buffer held by espconn + uint8_t txBufLen; // number of chars in txbuf + enum TcpState state; } TcpConn; static TcpConn tcpConn[MAX_CHAN]; @@ -49,46 +49,46 @@ static void tcpRecvCb(void *arg, char *data, uint16_t len); // Allocate a new connection dynamically and return it. Returns NULL if buf alloc failed static TcpConn* ICACHE_FLASH_ATTR tcpConnAlloc(uint8_t chan) { - TcpConn *tci = tcpConn+chan; - if (tci->state != TCP_idle && tci->conn != NULL) return tci; - - // malloc and return espconn struct - tci->conn = os_malloc(sizeof(struct espconn)); - if (tci->conn == NULL) goto fail; - memset(tci->conn, 0, sizeof(struct espconn)); - // malloc esp_tcp struct - tci->tcp = os_malloc(sizeof(esp_tcp)); - if (tci->tcp == NULL) goto fail; - memset(tci->tcp, 0, sizeof(esp_tcp)); - - // common init - tci->state = TCP_dns; - tci->conn->type = ESPCONN_TCP; - tci->conn->state = ESPCONN_NONE; - tci->conn->proto.tcp = tci->tcp; - tci->tcp->remote_port = 80; - espconn_regist_connectcb(tci->conn, tcpConnectCb); - espconn_regist_reconcb(tci->conn, tcpResetCb); - espconn_regist_sentcb(tci->conn, tcpSentCb); - espconn_regist_recvcb(tci->conn, tcpRecvCb); - espconn_regist_disconcb(tci->conn, tcpDisconCb); - tci->conn->reverse = tci; - - return tci; + TcpConn *tci = tcpConn+chan; + if (tci->state != TCP_idle && tci->conn != NULL) return tci; + + // malloc and return espconn struct + tci->conn = os_malloc(sizeof(struct espconn)); + if (tci->conn == NULL) goto fail; + memset(tci->conn, 0, sizeof(struct espconn)); + // malloc esp_tcp struct + tci->tcp = os_malloc(sizeof(esp_tcp)); + if (tci->tcp == NULL) goto fail; + memset(tci->tcp, 0, sizeof(esp_tcp)); + + // common init + tci->state = TCP_dns; + tci->conn->type = ESPCONN_TCP; + tci->conn->state = ESPCONN_NONE; + tci->conn->proto.tcp = tci->tcp; + tci->tcp->remote_port = 80; + espconn_regist_connectcb(tci->conn, tcpConnectCb); + espconn_regist_reconcb(tci->conn, tcpResetCb); + espconn_regist_sentcb(tci->conn, tcpSentCb); + espconn_regist_recvcb(tci->conn, tcpRecvCb); + espconn_regist_disconcb(tci->conn, tcpDisconCb); + tci->conn->reverse = tci; + + return tci; fail: - tcpConnFree(tci); - return NULL; + tcpConnFree(tci); + return NULL; } // Free a connection dynamically. static void ICACHE_FLASH_ATTR tcpConnFree(TcpConn* tci) { - if (tci->conn != NULL) os_free(tci->conn); - if (tci->tcp != NULL) os_free(tci->tcp); - if (tci->txBuf != NULL) os_free(tci->txBuf); - if (tci->txBufSent != NULL) os_free(tci->txBufSent); - memset(tci, 0, sizeof(TcpConn)); + if (tci->conn != NULL) os_free(tci->conn); + if (tci->tcp != NULL) os_free(tci->tcp); + if (tci->txBuf != NULL) os_free(tci->txBuf); + if (tci->txBufSent != NULL) os_free(tci->txBufSent); + memset(tci, 0, sizeof(TcpConn)); } //===== DNS @@ -96,26 +96,26 @@ tcpConnFree(TcpConn* tci) { // DNS name resolution callback static void ICACHE_FLASH_ATTR tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP dns CB (%p %p)\n", arg, tci); - if (ipaddr == NULL) { - os_printf("TCP %s not found\n", name); - } else { - os_printf("TCP %s -> %d.%d.%d.%d\n", name, IP2STR(ipaddr)); - tci->tcp->remote_ip[0] = ip4_addr1(ipaddr); - tci->tcp->remote_ip[1] = ip4_addr2(ipaddr); - tci->tcp->remote_ip[2] = ip4_addr3(ipaddr); - tci->tcp->remote_ip[3] = ip4_addr4(ipaddr); - os_printf("TCP connect %d.%d.%d.%d (%p)\n", IP2STR(tci->tcp->remote_ip), tci); - if (espconn_connect(tci->conn) == ESPCONN_OK) { - tci->state = TCP_conn; - return; - } - os_printf("TCP connect failure\n"); - } - // oops - tcpConnFree(tci); + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP dns CB (%p %p)\n", arg, tci); + if (ipaddr == NULL) { + os_printf("TCP %s not found\n", name); + } else { + os_printf("TCP %s -> %d.%d.%d.%d\n", name, IP2STR(ipaddr)); + tci->tcp->remote_ip[0] = ip4_addr1(ipaddr); + tci->tcp->remote_ip[1] = ip4_addr2(ipaddr); + tci->tcp->remote_ip[2] = ip4_addr3(ipaddr); + tci->tcp->remote_ip[3] = ip4_addr4(ipaddr); + os_printf("TCP connect %d.%d.%d.%d (%p)\n", IP2STR(tci->tcp->remote_ip), tci); + if (espconn_connect(tci->conn) == ESPCONN_OK) { + tci->state = TCP_conn; + return; + } + os_printf("TCP connect failure\n"); + } + // oops + tcpConnFree(tci); } //===== Connect / disconnect @@ -123,42 +123,42 @@ tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { // Connected callback static void ICACHE_FLASH_ATTR tcpConnectCb(void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP connect CB (%p %p)\n", arg, tci); - tci->state = TCP_data; - // send any buffered data - if (tci->txBuf != NULL && tci->txBufLen > 0) tcpDoSend(tci); - // reply to serial - char buf[6]; - short l = os_sprintf(buf, "\n~@%dC\n", tci-tcpConn); - uart0_tx_buffer(buf, l); + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP connect CB (%p %p)\n", arg, tci); + tci->state = TCP_data; + // send any buffered data + if (tci->txBuf != NULL && tci->txBufLen > 0) tcpDoSend(tci); + // reply to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dC\n", tci-tcpConn); + uart0_tx_buffer(buf, l); } // Disconnect callback static void ICACHE_FLASH_ATTR tcpDisconCb(void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP disconnect CB (%p %p)\n", arg, tci); - // notify to serial - char buf[6]; - short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); - uart0_tx_buffer(buf, l); - // free - tcpConnFree(tci); + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP disconnect CB (%p %p)\n", arg, tci); + // notify to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); + uart0_tx_buffer(buf, l); + // free + tcpConnFree(tci); } // Connection reset callback static void ICACHE_FLASH_ATTR tcpResetCb(void *arg, sint8 err) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP reset CB (%p %p) err=%d\n", arg, tci, err); - // notify to serial - char buf[6]; - short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); - uart0_tx_buffer(buf, l); - // free - tcpConnFree(tci); + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP reset CB (%p %p) err=%d\n", arg, tci, err); + // notify to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); + uart0_tx_buffer(buf, l); + // free + tcpConnFree(tci); } //===== Sending and receiving @@ -166,89 +166,89 @@ static void ICACHE_FLASH_ATTR tcpResetCb(void *arg, sint8 err) { // Send the next buffer (assumes that the connection is in a state that allows it) static void ICACHE_FLASH_ATTR tcpDoSend(TcpConn *tci) { - sint8 err = espconn_sent(tci->conn, (uint8*)tci->txBuf, tci->txBufLen); - if (err == ESPCONN_OK) { - // send successful - os_printf("TCP sent (%p %p)\n", tci->conn, tci); - tci->txBuf[tci->txBufLen] = 0; os_printf("TCP data: %s\n", tci->txBuf); - tci->txBufSent = tci->txBuf; - tci->txBuf = NULL; - tci->txBufLen = 0; - } else { - // send error, leave as-is and try again later... - os_printf("TCP send err (%p %p) %d\n", tci->conn, tci, err); - } + sint8 err = espconn_sent(tci->conn, (uint8*)tci->txBuf, tci->txBufLen); + if (err == ESPCONN_OK) { + // send successful + os_printf("TCP sent (%p %p)\n", tci->conn, tci); + tci->txBuf[tci->txBufLen] = 0; os_printf("TCP data: %s\n", tci->txBuf); + tci->txBufSent = tci->txBuf; + tci->txBuf = NULL; + tci->txBufLen = 0; + } else { + // send error, leave as-is and try again later... + os_printf("TCP send err (%p %p) %d\n", tci->conn, tci, err); + } } // Sent callback static void ICACHE_FLASH_ATTR tcpSentCb(void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP sent CB (%p %p)\n", arg, tci); - if (tci->txBufSent != NULL) os_free(tci->txBufSent); - tci->txBufSent = NULL; - - if (tci->txBuf != NULL && tci->txBufLen == MAX_TXBUF) { - // next buffer is full, send it now - tcpDoSend(tci); - } + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP sent CB (%p %p)\n", arg, tci); + if (tci->txBufSent != NULL) os_free(tci->txBufSent); + tci->txBufSent = NULL; + + if (tci->txBuf != NULL && tci->txBufLen == MAX_TXBUF) { + // next buffer is full, send it now + tcpDoSend(tci); + } } // Recv callback static void ICACHE_FLASH_ATTR tcpRecvCb(void *arg, char *data, uint16_t len) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP recv CB (%p %p)\n", arg, tci); - if (tci->state == TCP_data) { - uint8_t chan; - for (chan=0; chan= MAX_CHAN) return; // oops!? - char buf[6]; - short l = os_sprintf(buf, "\n~%d", chan); - uart0_tx_buffer(buf, l); - uart0_tx_buffer(data, len); - uart0_tx_buffer("\0\n", 2); - } - serledFlash(50); // short blink on serial LED + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP recv CB (%p %p)\n", arg, tci); + if (tci->state == TCP_data) { + uint8_t chan; + for (chan=0; chan= MAX_CHAN) return; // oops!? + char buf[6]; + short l = os_sprintf(buf, "\n~%d", chan); + uart0_tx_buffer(buf, l); + uart0_tx_buffer(data, len); + uart0_tx_buffer("\0\n", 2); + } + serledFlash(50); // short blink on serial LED } void ICACHE_FLASH_ATTR tcpClientSendChar(uint8_t chan, char c) { - TcpConn *tci = tcpConn+chan; - if (tci->state == TCP_idle) return; - - if (tci->txBuf != NULL) { - // we have a buffer - if (tci->txBufLen < MAX_TXBUF) { - // buffer has space, add char and return - tci->txBuf[tci->txBufLen++] = c; - return; - } else if (tci->txBufSent == NULL) { - // we don't have a send pending, send full buffer off - if (tci->state == TCP_data) tcpDoSend(tci); - if (tci->txBuf != NULL) return; // something went wrong - } else { - // buffers all backed-up, drop char - return; - } - } - // we do not have a buffer (either didn't have one or sent it off) - // allocate one - tci->txBuf = os_malloc(MAX_TXBUF); - tci->txBufLen = 0; - if (tci->txBuf != NULL) { - tci->txBuf[tci->txBufLen++] = c; - } + TcpConn *tci = tcpConn+chan; + if (tci->state == TCP_idle) return; + + if (tci->txBuf != NULL) { + // we have a buffer + if (tci->txBufLen < MAX_TXBUF) { + // buffer has space, add char and return + tci->txBuf[tci->txBufLen++] = c; + return; + } else if (tci->txBufSent == NULL) { + // we don't have a send pending, send full buffer off + if (tci->state == TCP_data) tcpDoSend(tci); + if (tci->txBuf != NULL) return; // something went wrong + } else { + // buffers all backed-up, drop char + return; + } + } + // we do not have a buffer (either didn't have one or sent it off) + // allocate one + tci->txBuf = os_malloc(MAX_TXBUF); + tci->txBufLen = 0; + if (tci->txBuf != NULL) { + tci->txBuf[tci->txBufLen++] = c; + } } void ICACHE_FLASH_ATTR tcpClientSendPush(uint8_t chan) { - TcpConn *tci = tcpConn+chan; - if (tci->state != TCP_data) return; // no active connection on this channel - if (tci->txBuf == NULL || tci->txBufLen == 0) return; // no chars accumulated to send - if (tci->txBufSent != NULL) return; // already got a send in progress - tcpDoSend(tci); + TcpConn *tci = tcpConn+chan; + if (tci->state != TCP_data) return; // no active connection on this channel + if (tci->txBuf == NULL || tci->txBufLen == 0) return; // no chars accumulated to send + if (tci->txBufSent != NULL) return; // already got a send in progress + tcpDoSend(tci); } //===== Command parsing @@ -257,59 +257,59 @@ tcpClientSendPush(uint8_t chan) { // Returns true on success. bool ICACHE_FLASH_ATTR tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf) { - TcpConn *tci; - char *hostname; - char *port; - - // copy the command so we can modify it - char buf[128]; - os_strncpy(buf, cmdBuf, 128); - buf[127] = 0; - - switch (cmd) { - //== TCP Connect command - case 'T': - hostname = buf; - port = hostname; - while (*port != 0 && *port != ':') port++; - if (*port != ':') break; - *port = 0; - port++; - int portInt = atoi(port); - if (portInt < 1 || portInt > 65535) break; - - // allocate a connection - tci = tcpConnAlloc(chan); - if (tci == NULL) break; - tci->state = TCP_dns; - tci->tcp->remote_port = portInt; - - // start the DNS resolution - os_printf("TCP %p resolving %s for chan %d (conn=%p)\n", tci, hostname, chan ,tci->conn); - ip_addr_t ip; - err_t err = espconn_gethostbyname(tci->conn, hostname, &ip, tcpClientHostnameCb); - if (err == ESPCONN_OK) { - // dns cache hit, got the IP address, fake the callback (sigh) - os_printf("TCP DNS hit\n"); - tcpClientHostnameCb(hostname, &ip, tci->conn); - } else if (err != ESPCONN_INPROGRESS) { - tcpConnFree(tci); - break; - } - - return true; - - //== TCP Close/disconnect command - case 'C': - os_printf("TCP closing chan %d\n", chan); - tci = tcpConn+chan; - if (tci->state > TCP_idle) { - tci->state = TCP_idle; // hackish... - espconn_disconnect(tci->conn); - } - break; - - } - return false; + TcpConn *tci; + char *hostname; + char *port; + + // copy the command so we can modify it + char buf[128]; + os_strncpy(buf, cmdBuf, 128); + buf[127] = 0; + + switch (cmd) { + //== TCP Connect command + case 'T': + hostname = buf; + port = hostname; + while (*port != 0 && *port != ':') port++; + if (*port != ':') break; + *port = 0; + port++; + int portInt = atoi(port); + if (portInt < 1 || portInt > 65535) break; + + // allocate a connection + tci = tcpConnAlloc(chan); + if (tci == NULL) break; + tci->state = TCP_dns; + tci->tcp->remote_port = portInt; + + // start the DNS resolution + os_printf("TCP %p resolving %s for chan %d (conn=%p)\n", tci, hostname, chan ,tci->conn); + ip_addr_t ip; + err_t err = espconn_gethostbyname(tci->conn, hostname, &ip, tcpClientHostnameCb); + if (err == ESPCONN_OK) { + // dns cache hit, got the IP address, fake the callback (sigh) + os_printf("TCP DNS hit\n"); + tcpClientHostnameCb(hostname, &ip, tci->conn); + } else if (err != ESPCONN_INPROGRESS) { + tcpConnFree(tci); + break; + } + + return true; + + //== TCP Close/disconnect command + case 'C': + os_printf("TCP closing chan %d\n", chan); + tci = tcpConn+chan; + if (tci->state > TCP_idle) { + tci->state = TCP_idle; // hackish... + espconn_disconnect(tci->conn); + } + break; + + } + return false; } From ddd2af1a65b02d2a9352a664635f6f2825f0f7d4 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 24 Aug 2015 10:25:24 -0700 Subject: [PATCH 19/20] incorporated tuanpmt's slip protocol and rest library --- Makefile | 2 +- cmd/cmd.c | 179 ++++++++++ cmd/cmd.h | 90 +++++ cmd/handlers.c | 79 +++++ cmd/rest.c | 394 +++++++++++++++++++++ cmd/rest.h | 39 +++ {serial => cmd}/tcpclient.c | 0 {serial => cmd}/tcpclient.h | 2 +- serial/crc16.c | 78 +++++ serial/crc16.h | 100 ++++++ serial/serbridge.c | 670 +++++++++++++++--------------------- serial/serbridge.h | 2 +- serial/slip.c | 136 ++++++++ serial/uart.h | 2 +- user/cgiwifi.c | 5 +- user/cgiwifi.h | 2 + 16 files changed, 1382 insertions(+), 398 deletions(-) create mode 100644 cmd/cmd.c create mode 100644 cmd/cmd.h create mode 100644 cmd/handlers.c create mode 100644 cmd/rest.c create mode 100644 cmd/rest.h rename {serial => cmd}/tcpclient.c (100%) rename {serial => cmd}/tcpclient.h (86%) create mode 100644 serial/crc16.c create mode 100644 serial/crc16.h create mode 100644 serial/slip.c diff --git a/Makefile b/Makefile index 5b694ae..647ba20 100644 --- a/Makefile +++ b/Makefile @@ -146,7 +146,7 @@ TARGET = httpd APPGEN_TOOL ?= gen_appbin.py # which modules (subdirectories) of the project to include in compiling -MODULES = espfs httpd user serial +MODULES = espfs httpd user serial cmd EXTRA_INCDIR = include . # lib/heatshrink/ # libraries used in this project, mainly provided by the SDK diff --git a/cmd/cmd.c b/cmd/cmd.c new file mode 100644 index 0000000..06430ca --- /dev/null +++ b/cmd/cmd.c @@ -0,0 +1,179 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// +// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh + +#include "esp8266.h" +#include "cmd.h" +#include "crc16.h" +#include "serbridge.h" +#include "uart.h" + +extern const CmdList commands[]; + +//===== ESP -> Serial responses + +static void ICACHE_FLASH_ATTR +CMD_ProtoWrite(uint8_t data) { + switch(data){ + case SLIP_START: + case SLIP_END: + case SLIP_REPL: + uart0_write_char(SLIP_REPL); + uart0_write_char(SLIP_ESC(data)); + break; + default: + uart0_write_char(data); + } +} + +static void ICACHE_FLASH_ATTR +CMD_ProtoWriteBuf(uint8_t *data, short len) { + while (len--) CMD_ProtoWrite(*data++); +} + +// Start a response, returns the partial CRC +uint16_t ICACHE_FLASH_ATTR +CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc) { + uint16_t crc = 0; + + uart0_write_char(SLIP_START); + CMD_ProtoWriteBuf((uint8_t*)&cmd, 2); + crc = crc16_data((uint8_t*)&cmd, 2, crc); + CMD_ProtoWriteBuf((uint8_t*)&callback, 4); + crc = crc16_data((uint8_t*)&callback, 4, crc); + CMD_ProtoWriteBuf((uint8_t*)&_return, 4); + crc = crc16_data((uint8_t*)&_return, 4, crc); + CMD_ProtoWriteBuf((uint8_t*)&argc, 2); + crc = crc16_data((uint8_t*)&argc, 2, crc); + return crc; +} + +// Adds data to a response, returns the partial CRC +uint16_t ICACHE_FLASH_ATTR +CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len) { + short pad_len = len+3 - (len+3)%4; // round up to multiple of 4 + CMD_ProtoWriteBuf((uint8_t*)&pad_len, 2); + crc_in = crc16_data((uint8_t*)&pad_len, 2, crc_in); + + CMD_ProtoWriteBuf(data, len); + crc_in = crc16_data(data, len, crc_in); + + if (pad_len > len) { + uint32_t temp = 0; + CMD_ProtoWriteBuf((uint8_t*)&temp, pad_len-len); + crc_in = crc16_data((uint8_t*)&temp, pad_len-len, crc_in); + } + + return crc_in; +} + +// Ends a response +void ICACHE_FLASH_ATTR +CMD_ResponseEnd(uint16_t crc) { + CMD_ProtoWriteBuf((uint8_t*)&crc, 2); + uart0_write_char(SLIP_END); +} + +//===== serial -> ESP commands + +// Execute a parsed command +static uint32_t ICACHE_FLASH_ATTR +CMD_Exec(const CmdList *scp, CmdPacket *packet) { + uint16_t crc = 0; + // Iterate through the command table and call the appropriate function + while (scp->sc_function != NULL) { + if(scp->sc_name == packet->cmd) { + //os_printf("CMD: Dispatching cmd=%d\n", packet->cmd); + // call command function + uint32_t ret = scp->sc_function(packet); + // if requestor asked for a response, send it + if (packet->_return){ + os_printf("CMD: Response: %lu, cmd: %d\r\n", ret, packet->cmd); + crc = CMD_ResponseStart(packet->cmd, 0, ret, 0); + CMD_ResponseEnd(crc); + } else { + //os_printf("CMD: no response (%lu)\n", packet->_return); + } + return ret; + } + scp++; + } + os_printf("CMD: cmd=%d not found\n", packet->cmd); + return 0; +} + +// Parse a packet and print info about it +void ICACHE_FLASH_ATTR +CMD_parse_packet(uint8_t *buf, short len) { + // minimum command length + if (len < 12) return; + + // init pointers into buffer + CmdPacket *packet = (CmdPacket*)buf; + uint8_t *data_ptr = (uint8_t*)&packet->args; + uint8_t *data_limit = data_ptr+len; + uint16_t argc = packet->argc; + uint16_t argn = 0; + + os_printf("CMD: cmd=%d argc=%d cb=%p ret=%lu\n", + packet->cmd, packet->argc, (void *)packet->callback, packet->_return); + + // print out arguments + while (data_ptr+2 < data_limit && argc--) { + short l = *(uint16_t*)data_ptr; + os_printf("CMD: arg[%d] len=%d:", argn++, l); + data_ptr += 2; + while (data_ptr < data_limit && l--) { + os_printf(" %02X", *data_ptr++); + } + os_printf("\n"); + } + + if (data_ptr <= data_limit) { + CMD_Exec(commands, packet); + } else { + os_printf("CMD: packet length overrun, parsing arg %d\n", argn-1); + } +} + +//===== Helpers to parse a command packet + +// Fill out a CmdRequest struct given a CmdPacket +void ICACHE_FLASH_ATTR +CMD_Request(CmdRequest *req, CmdPacket* cmd) { + req->cmd = cmd; + req->arg_num = 0; + req->arg_ptr = (uint8_t*)&cmd->args; +} + +// Return the number of arguments given a command struct +uint32_t ICACHE_FLASH_ATTR +CMD_GetArgc(CmdRequest *req) { + return req->cmd->argc; +} + +// Copy the next argument from a command structure into the data pointer, returns 0 on success +// -1 on error +int32_t ICACHE_FLASH_ATTR +CMD_PopArg(CmdRequest *req, void *data, uint16_t len) { + uint16_t length; + + if (req->arg_num >= req->cmd->argc) + return -1; + + length = *(uint16_t*)req->arg_ptr; + if (length != len) return -1; // safety check + + req->arg_ptr += 2; + os_memcpy(data, req->arg_ptr, length); + req->arg_ptr += length; + + req->arg_num ++; + return 0; +} + +// Return the length of the next argument +uint16_t ICACHE_FLASH_ATTR +CMD_ArgLen(CmdRequest *req) { + return *(uint16_t*)req->arg_ptr; +} diff --git a/cmd/cmd.h b/cmd/cmd.h new file mode 100644 index 0000000..5a66bc7 --- /dev/null +++ b/cmd/cmd.h @@ -0,0 +1,90 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// +// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh + +#ifndef CMD_H +#define CMD_H + +// Escape chars used by tuanpmt, dunno why he didn't use std ones... +#define SLIP_START 0x7E +#define SLIP_END 0x7F +#define SLIP_REPL 0x7D +#define SLIP_ESC(x) (x ^ 0x20) + +#if 0 +// Proper SLIP escape chars from RFC +#define SLIP_END 0300 // indicates end of packet +#define SLIP_ESC 0333 // indicates byte stuffing +#define SLIP_ESC_END 0334 // ESC ESC_END means END data byte +#define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte +#endif + +typedef struct __attribute((__packed__)) { + uint16_t len; // length of data + uint8_t data[0]; // really data[len] +} CmdArg; + +typedef struct __attribute((__packed__)) { + uint16_t cmd; // command to perform, from CmdName enum + uint32_t callback; // callback pointer to embed in response + uint32_t _return; // return value to embed in response (?) + uint16_t argc; // number of arguments to command + CmdArg args[0]; // really args[argc] +} CmdPacket; + +typedef struct { + CmdPacket *cmd; // command packet header + uint32_t arg_num; // number of args parsed + uint8_t *arg_ptr; // pointer to ?? +} CmdRequest; + +typedef enum { + CMD_NULL = 0, + CMD_RESET, // reset esp (not honored in this implementation) + CMD_IS_READY, // health-check + CMD_WIFI_CONNECT, // (3) connect to AP (not honored in this implementation) + CMD_MQTT_SETUP, + CMD_MQTT_CONNECT, + CMD_MQTT_DISCONNECT, + CMD_MQTT_PUBLISH, + CMD_MQTT_SUBSCRIBE, + CMD_MQTT_LWT, + CMD_MQTT_EVENTS, + CMD_REST_SETUP, // (11) + CMD_REST_REQUEST, + CMD_REST_SETHEADER, + CMD_REST_EVENTS +} CmdName; + +typedef uint32_t (*cmdfunc_t)(CmdPacket *cmd); + +typedef struct { + CmdName sc_name; + cmdfunc_t sc_function; +} CmdList; + +void CMD_parse_packet(uint8_t *buf, short len); + +// Responses + +// Start a response, returns the partial CRC +uint16_t CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc); +// Adds data to a response, returns the partial CRC +uint16_t CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len); +// Ends a response +void CMD_ResponseEnd(uint16_t crc); + +void CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]); + +// Requests + +// Fill out a CmdRequest struct given a CmdPacket +void CMD_Request(CmdRequest *req, CmdPacket* cmd); +// Return the number of arguments given a request +uint32_t CMD_GetArgc(CmdRequest *req); +// Return the length of the next argument +uint16_t CMD_ArgLen(CmdRequest *req); +// Copy next arg from request into the data pointer, returns 0 on success, -1 on error +int32_t CMD_PopArg(CmdRequest *req, void *data, uint16_t len); + +#endif diff --git a/cmd/handlers.c b/cmd/handlers.c new file mode 100644 index 0000000..73370dd --- /dev/null +++ b/cmd/handlers.c @@ -0,0 +1,79 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// +// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh + +#include "esp8266.h" +#include "cmd.h" +#include "rest.h" +#include "crc16.h" +#include "serbridge.h" +#include "uart.h" +#include "cgiwifi.h" + +static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd); +static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd); +static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd); + +// Command dispatch table for serial -> ESP commands +const CmdList commands[] = { + {CMD_NULL, CMD_Null}, + {CMD_RESET, CMD_Null}, + {CMD_IS_READY, CMD_IsReady}, + {CMD_WIFI_CONNECT, CMD_WifiConnect}, + +/* + {CMD_MQTT_SETUP, MQTTAPP_Setup}, + {CMD_MQTT_CONNECT, MQTTAPP_Connect}, + {CMD_MQTT_DISCONNECT, MQTTAPP_Disconnect}, + {CMD_MQTT_PUBLISH, MQTTAPP_Publish}, + {CMD_MQTT_SUBSCRIBE , MQTTAPP_Subscribe}, + {CMD_MQTT_LWT, MQTTAPP_Lwt}, + */ + + {CMD_REST_SETUP, REST_Setup}, + {CMD_REST_REQUEST, REST_Request}, + {CMD_REST_SETHEADER, REST_SetHeader}, + {CMD_NULL, NULL} +}; + +// Command handler for IsReady (healthcheck) command +static uint32_t ICACHE_FLASH_ATTR +CMD_IsReady(CmdPacket *cmd) { + os_printf("CMD: Check ready\n"); + return 1; +} + +// Command handler for Null command +static uint32_t ICACHE_FLASH_ATTR +CMD_Null(CmdPacket *cmd) { + os_printf("CMD: NULL/unsupported command\n"); + return 1; +} + +static uint8_t lastWifiStatus; +static uint32_t wifiCallback; + +// Callback from wifi subsystem to notify us of status changes +static void ICACHE_FLASH_ATTR +CMD_WifiCb(uint8_t wifiStatus) { + if (wifiStatus != lastWifiStatus){ + lastWifiStatus = wifiStatus; + if (wifiCallback) { + uint8_t status = wifiStatus == wifiGotIP ? 5 : 1; + uint16_t crc = CMD_ResponseStart(CMD_WIFI_CONNECT, wifiCallback, 0, 1); + crc = CMD_ResponseBody(crc, (uint8_t*)&status, 1); + CMD_ResponseEnd(crc); + } + } +} + +// Command handler for Wifi connect command +static uint32_t ICACHE_FLASH_ATTR +CMD_WifiConnect(CmdPacket *cmd) { + if(cmd->argc != 2 || cmd->callback == 0) + return 0xFFFFFFFF; + + wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem + wifiCallback = cmd->callback; // save the MCU's callback + return 1; +} diff --git a/cmd/rest.c b/cmd/rest.c new file mode 100644 index 0000000..133bf2f --- /dev/null +++ b/cmd/rest.c @@ -0,0 +1,394 @@ +/* + * api.c + * + * Created on: Mar 4, 2015 + * Author: Minh + */ +#include "esp8266.h" +#include "rest.h" +#include "cmd.h" + +extern uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const char* str, void *ip); + +static void ICACHE_FLASH_ATTR +tcpclient_discon_cb(void *arg) { + //struct espconn *pespconn = (struct espconn *)arg; + //RestClient* client = (RestClient *)pespconn->reverse; +} + +static void ICACHE_FLASH_ATTR +tcpclient_recv(void *arg, char *pdata, unsigned short len) { + uint8_t currentLineIsBlank = 0; + uint8_t httpBody = 0; + uint8_t inStatus = 0; + char statusCode[4]; + int i = 0, j; + uint32_t code = 0; + uint16_t crc; + + struct espconn *pCon = (struct espconn*)arg; + RestClient *client = (RestClient *)pCon->reverse; + + for(j=0 ;jresp_cb, code, 0); + } else { + crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1); + crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+j), body_len); + } + CMD_ResponseEnd(crc); + break; + } else { + if (c == '\n' && currentLineIsBlank) { + httpBody = true; + } + if (c == '\n') { + // you're starting a new line + currentLineIsBlank = true; + } else if (c != '\r') { + // you've gotten a character on the current line + currentLineIsBlank = false; + } + } + } + //if(client->security) + // espconn_secure_disconnect(client->pCon); + //else + espconn_disconnect(client->pCon); + +} + +static void ICACHE_FLASH_ATTR +tcpclient_sent_cb(void *arg) { + //struct espconn *pCon = (struct espconn *)arg; + //RestClient* client = (RestClient *)pCon->reverse; + os_printf("REST: Sent\n"); +} + +static void ICACHE_FLASH_ATTR +tcpclient_connect_cb(void *arg) { + struct espconn *pCon = (struct espconn *)arg; + RestClient* client = (RestClient *)pCon->reverse; + + espconn_regist_disconcb(client->pCon, tcpclient_discon_cb); + espconn_regist_recvcb(client->pCon, tcpclient_recv);//////// + espconn_regist_sentcb(client->pCon, tcpclient_sent_cb);/////// + + //if(client->security){ + // espconn_secure_sent(client->pCon, client->data, client->data_len); + //} + //else{ + espconn_sent(client->pCon, client->data, client->data_len); + //} +} + +static void ICACHE_FLASH_ATTR +tcpclient_recon_cb(void *arg, sint8 errType) { + //struct espconn *pCon = (struct espconn *)arg; + //RestClient* client = (RestClient *)pCon->reverse; +} + +static void ICACHE_FLASH_ATTR +rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { + struct espconn *pConn = (struct espconn *)arg; + RestClient* client = (RestClient *)pConn->reverse; + + if(ipaddr == NULL) { + os_printf("REST DNS: Got no ip, try to reconnect\n"); + return; + } + + os_printf("REST DNS: found ip %d.%d.%d.%d\n", + *((uint8 *) &ipaddr->addr), + *((uint8 *) &ipaddr->addr + 1), + *((uint8 *) &ipaddr->addr + 2), + *((uint8 *) &ipaddr->addr + 3)); + + if(client->ip.addr == 0 && ipaddr->addr != 0) { + os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); + //if(client->security){ + // espconn_secure_connect(client->pCon); + //} + //else { + espconn_connect(client->pCon); + //} + os_printf("REST: connecting...\n"); + } +} + +uint32_t ICACHE_FLASH_ATTR +REST_Setup(CmdPacket *cmd) { + CmdRequest req; + RestClient *client; + uint8_t *rest_host; + uint16_t len; + uint32_t port, security; + + + // start parsing the command + CMD_Request(&req, cmd); + os_printf("REST: setup argc=%ld\n", CMD_GetArgc(&req)); + if(CMD_GetArgc(&req) != 3) + return 0; + + // get the hostname + len = CMD_ArgLen(&req); + os_printf("REST: len=%d\n", len); + if (len > 128) return 0; // safety check + rest_host = (uint8_t*)os_zalloc(len + 1); + if (CMD_PopArg(&req, rest_host, len)) return 0; + rest_host[len] = 0; + os_printf("REST: setup host=%s", rest_host); + + // get the port + if (CMD_PopArg(&req, (uint8_t*)&port, 4)) { + os_free(rest_host); + return 0; + } + os_printf(" port=%ld", port); + + // get the security mode + if (CMD_PopArg(&req, (uint8_t*)&security, 4)) { + os_free(rest_host); + return 0; + } + os_printf(" security=%ld\n", security); + + // allocate a connection structure + client = (RestClient*)os_zalloc(sizeof(RestClient)); + os_memset(client, 0, sizeof(RestClient)); + if(client == NULL) + return 0; + + os_printf("REST: setup host=%s port=%ld security=%ld\n", rest_host, port, security); + + client->resp_cb = cmd->callback; + + client->host = rest_host; + client->port = port; + client->security = security; + client->ip.addr = 0; + + client->data = (uint8_t*)os_zalloc(1024); + + client->header = (uint8_t*)os_zalloc(4); + client->header[0] = 0; + + client->content_type = (uint8_t*)os_zalloc(22); + os_sprintf((char *)client->content_type, "x-www-form-urlencoded"); + client->content_type[21] = 0; + + client->user_agent = (uint8_t*)os_zalloc(9); + os_sprintf((char *)client->user_agent, "esp-link"); + + client->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); + client->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + + client->pCon->type = ESPCONN_TCP; + client->pCon->state = ESPCONN_NONE; + client->pCon->proto.tcp->local_port = espconn_port(); + client->pCon->proto.tcp->remote_port = client->port; + + client->pCon->reverse = client; + + return (uint32_t)client; +} + +uint32_t ICACHE_FLASH_ATTR +REST_SetHeader(CmdPacket *cmd) { + CmdRequest req; + RestClient *client; + uint16_t len; + uint32_t header_index, client_ptr = 0; + + CMD_Request(&req, cmd); + + if(CMD_GetArgc(&req) != 3) + return 0; + + // Get client -- TODO: Whoa, this is totally unsafe! + if (CMD_PopArg(&req, (uint8_t*)&client_ptr, 4)) return 0; + client = (RestClient*)client_ptr; + + // Get header selector + if (CMD_PopArg(&req, (uint8_t*)&header_index, 4)) return 0; + + // Get header value + len = CMD_ArgLen(&req); + if (len > 256) return 0; //safety check + switch(header_index) { + case HEADER_GENERIC: + if(client->header) + os_free(client->header); + client->header = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, (uint8_t*)client->header, len); + client->header[len] = 0; + os_printf("REST: Set header: %s\r\n", client->header); + break; + case HEADER_CONTENT_TYPE: + if(client->content_type) + os_free(client->content_type); + client->content_type = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, (uint8_t*)client->content_type, len); + client->content_type[len] = 0; + os_printf("REST: Set content_type: %s\r\n", client->content_type); + break; + case HEADER_USER_AGENT: + if(client->user_agent) + os_free(client->user_agent); + client->user_agent = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, (uint8_t*)client->user_agent, len); + client->user_agent[len] = 0; + os_printf("REST: Set user_agent: %s\r\n", client->user_agent); + break; + } + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +REST_Request(CmdPacket *cmd) { + CmdRequest req; + RestClient *client; + uint16_t len, realLen = 0; + uint32_t client_ptr; + uint8_t *body = NULL; + uint8_t method[16]; + uint8_t path[1024]; + + CMD_Request(&req, cmd); + + if(CMD_GetArgc(&req) <3) + return 0; + + // Get client -- TODO: Whoa, this is totally unsafe! + if(CMD_PopArg(&req, (uint8_t*)&client_ptr, 4)) return 0; + client = (RestClient*)client_ptr; + + // Get HTTP method + len = CMD_ArgLen(&req); + if (len > 15) return 0; + CMD_PopArg(&req, method, len); + method[len] = 0; + + // Get HTTP path + len = CMD_ArgLen(&req); + if (len > 1023) return 0; + CMD_PopArg(&req, path, len); + path[len] = 0; + + // Get HTTP body + if (CMD_GetArgc(&req) == 3){ + realLen = 0; + len = 0; + } else { + CMD_PopArg(&req, (uint8_t*)&realLen, 4); + + len = CMD_ArgLen(&req); + if (len > 2048 || realLen > len) return 0; + body = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, body, len); + body[len] = 0; + } + + client->pCon->state = ESPCONN_NONE; + + os_printf("REST: method: %s, path: %s\n", method, path); + + client->data_len = os_sprintf((char*)client->data, "%s %s HTTP/1.1\r\n" + "Host: %s\r\n" + "%s" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "Content-Type: %s\r\n" + "User-Agent: %s\r\n\r\n", + method, path, + client->host, + client->header, + realLen, + client->content_type, + client->user_agent); + + if (realLen > 0){ + os_memcpy(client->data + client->data_len, body, realLen); + client->data_len += realLen; + //os_sprintf(client->data + client->data_len, "\r\n\r\n"); + //client->data_len += 4; + } + + client->pCon->state = ESPCONN_NONE; + espconn_regist_connectcb(client->pCon, tcpclient_connect_cb); + espconn_regist_reconcb(client->pCon, tcpclient_recon_cb); + + if(UTILS_StrToIP((char *)client->host, &client->pCon->proto.tcp->remote_ip)) { + os_printf("REST: Connect to ip %s:%ld\n",client->host, client->port); + //if(client->security){ + // espconn_secure_connect(client->pCon); + //} + //else { + espconn_connect(client->pCon); + //} + } else { + os_printf("REST: Connect to host %s:%ld\n", client->host, client->port); + espconn_gethostbyname(client->pCon, (char *)client->host, &client->ip, rest_dns_found); + } + + if(body) os_free(body); + return 1; +} + +uint8_t ICACHE_FLASH_ATTR +UTILS_StrToIP(const char* str, void *ip) +{ + /* The count of the number of bytes processed. */ + int i; + /* A pointer to the next digit to process. */ + const char * start; + + start = str; + for (i = 0; i < 4; i++) { + /* The digit being processed. */ + char c; + /* The value of this byte. */ + int n = 0; + while (1) { + c = * start; + start++; + if (c >= '0' && c <= '9') { + n *= 10; + n += c - '0'; + } + /* We insist on stopping at "." if we are still parsing + the first, second, or third numbers. If we have reached + the end of the numbers, we will allow any character. */ + else if ((i < 3 && c == '.') || i == 3) { + break; + } + else { + return 0; + } + } + if (n >= 256) { + return 0; + } + ((uint8_t*)ip)[i] = n; + } + return 1; +} diff --git a/cmd/rest.h b/cmd/rest.h new file mode 100644 index 0000000..13159ea --- /dev/null +++ b/cmd/rest.h @@ -0,0 +1,39 @@ +/* + * api.h + * + * Created on: Mar 4, 2015 + * Author: Minh + */ + +#ifndef MODULES_API_H_ +#define MODULES_API_H_ + +#include "c_types.h" +#include "ip_addr.h" +#include "cmd.h" + +typedef enum { + HEADER_GENERIC = 0, + HEADER_CONTENT_TYPE, + HEADER_USER_AGENT +} HEADER_TYPE; + +typedef struct { + uint8_t *host; + uint32_t port; + uint32_t security; + ip_addr_t ip; + struct espconn *pCon; + uint8_t *header; + uint8_t *data; + uint32_t data_len; + uint8_t *content_type; + uint8_t *user_agent; + uint32_t resp_cb; +} RestClient; + +uint32_t REST_Setup(CmdPacket *cmd); +uint32_t REST_Request(CmdPacket *cmd); +uint32_t REST_SetHeader(CmdPacket *cmd); + +#endif /* MODULES_INCLUDE_API_H_ */ diff --git a/serial/tcpclient.c b/cmd/tcpclient.c similarity index 100% rename from serial/tcpclient.c rename to cmd/tcpclient.c diff --git a/serial/tcpclient.h b/cmd/tcpclient.h similarity index 86% rename from serial/tcpclient.h rename to cmd/tcpclient.h index 9bfeb01..ff0ff9d 100644 --- a/serial/tcpclient.h +++ b/cmd/tcpclient.h @@ -4,7 +4,7 @@ // max number of channels the client can open #define MAX_TCP_CHAN 8 -// Parse and perform the commandm cmdBuf must be null-terminated +// Parse and perform the command, cmdBuf must be null-terminated bool tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf); // Append a character to the specified channel diff --git a/serial/crc16.c b/serial/crc16.c new file mode 100644 index 0000000..22a6651 --- /dev/null +++ b/serial/crc16.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + +/** \addtogroup crc16 + * @{ */ + +/** + * \file + * Implementation of the CRC16 calculcation + * \author + * Adam Dunkels + * + */ + +/* CITT CRC16 polynomial ^16 + ^12 + ^5 + 1 */ +/*---------------------------------------------------------------------------*/ +unsigned short +crc16_add(unsigned char b, unsigned short acc) +{ + /* + acc = (unsigned char)(acc >> 8) | (acc << 8); + acc ^= b; + acc ^= (unsigned char)(acc & 0xff) >> 4; + acc ^= (acc << 8) << 4; + acc ^= ((acc & 0xff) << 4) << 1; + */ + + acc ^= b; + acc = (acc >> 8) | (acc << 8); + acc ^= (acc & 0xff00) << 4; + acc ^= (acc >> 8) >> 4; + acc ^= (acc & 0xff00) >> 5; + return acc; +} +/*---------------------------------------------------------------------------*/ +unsigned short +crc16_data(const unsigned char *data, int len, unsigned short acc) +{ + int i; + + for(i = 0; i < len; ++i) { + acc = crc16_add(*data, acc); + ++data; + } + return acc; +} +/*---------------------------------------------------------------------------*/ + +/** @} */ diff --git a/serial/crc16.h b/serial/crc16.h new file mode 100644 index 0000000..bd4c52e --- /dev/null +++ b/serial/crc16.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2005, Swedish Institute of Computer Science + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * \file + * Header file for the CRC16 calculcation + * \author + * Adam Dunkels + * + */ + +/** \addtogroup lib + * @{ */ + +/** + * \defgroup crc16 Cyclic Redundancy Check 16 (CRC16) calculation + * + * The Cyclic Redundancy Check 16 is a hash function that produces a + * checksum that is used to detect errors in transmissions. The CRC16 + * calculation module is an iterative CRC calculator that can be used + * to cumulatively update a CRC checksum for every incoming byte. + * + * @{ + */ + +#ifndef CRC16_H_ +#define CRC16_H_ +#ifdef __cplusplus +extern "C" { +#endif +/** + * \brief Update an accumulated CRC16 checksum with one byte. + * \param b The byte to be added to the checksum + * \param crc The accumulated CRC that is to be updated. + * \return The updated CRC checksum. + * + * This function updates an accumulated CRC16 checksum + * with one byte. It can be used as a running checksum, or + * to checksum an entire data block. + * + * \note The algorithm used in this implementation is + * tailored for a running checksum and does not perform as + * well as a table-driven algorithm when checksumming an + * entire data block. + * + */ +unsigned short crc16_add(unsigned char b, unsigned short crc); + +/** + * \brief Calculate the CRC16 over a data area + * \param data Pointer to the data + * \param datalen The length of the data + * \param acc The accumulated CRC that is to be updated (or zero). + * \return The CRC16 checksum. + * + * This function calculates the CRC16 checksum of a data area. + * + * \note The algorithm used in this implementation is + * tailored for a running checksum and does not perform as + * well as a table-driven algorithm when checksumming an + * entire data block. + */ +unsigned short crc16_data(const unsigned char *data, int datalen, + unsigned short acc); +#ifdef __cplusplus +} +#endif +#endif /* CRC16_H_ */ + +/** @} */ +/** @} */ diff --git a/serial/serbridge.c b/serial/serbridge.c index 5c00175..415772b 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -9,24 +9,27 @@ #include "gpio.h" #include "uart.h" +#include "crc16.h" #include "serbridge.h" #include "serled.h" #include "config.h" #include "console.h" -#include "tcpclient.h" +#include "cmd.h" static struct espconn serbridgeConn; static esp_tcp serbridgeTcp; static int8_t mcu_reset_pin, mcu_isp_pin; +extern uint8_t slip_disabled; // disable slip to allow flashing of attached MCU + static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len); // Connection pool serbridgeConnData connData[MAX_CONN]; // Given a pointer to an espconn struct find the connection that correcponds to it static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) { - struct espconn *conn = arg; - return arg == NULL ? NULL : (serbridgeConnData *)conn->reverse; + struct espconn *conn = arg; + return arg == NULL ? NULL : (serbridgeConnData *)conn->reverse; } //===== TCP -> UART @@ -50,149 +53,152 @@ enum { TN_normal, TN_iac, TN_will, TN_start, TN_end, TN_comPort, TN_setControl } static uint8_t ICACHE_FLASH_ATTR telnetUnwrap(uint8_t *inBuf, int len, uint8_t state) { - for (int i=0; i write one to outbuf and go normal again - state = TN_normal; - uart0_write_char(c); - break; - case WILL: // negotiation - state = TN_will; - break; - case SB: // command sequence begin - state = TN_start; - break; - case SE: // command sequence end - state = TN_normal; - break; - default: // not sure... let's ignore - uart0_write_char(IAC); - uart0_write_char(c); - } - break; - case TN_will: - state = TN_normal; // yes, we do COM port options, let's go back to normal - break; - case TN_start: // in command seq, now comes the type of cmd - if (c == ComPortOpt) state = TN_comPort; - else state = TN_end; // an option we don't know, skip 'til the end seq - break; - case TN_end: // wait for end seq - if (c == IAC) state = TN_iac; // simple wait to accept end or next escape seq - break; - case TN_comPort: - if (c == SetControl) state = TN_setControl; - else state = TN_end; - break; - case TN_setControl: // switch control line and delay a tad - switch (c) { - case DTR_ON: - if (mcu_reset_pin >= 0) { - os_printf("MCU reset gpio%d\n", mcu_reset_pin); - GPIO_OUTPUT_SET(mcu_reset_pin, 0); - os_delay_us(100L); - } else os_printf("MCU reset: no pin\n"); - break; - case DTR_OFF: - if (mcu_reset_pin >= 0) { - GPIO_OUTPUT_SET(mcu_reset_pin, 1); - os_delay_us(100L); - } - break; - case RTS_ON: - if (mcu_isp_pin >= 0) { - os_printf("MCU ISP gpio%d\n", mcu_isp_pin); - GPIO_OUTPUT_SET(mcu_isp_pin, 0); - os_delay_us(100L); - } else os_printf("MCU isp: no pin\n"); - break; - case RTS_OFF: - if (mcu_isp_pin >= 0) { - GPIO_OUTPUT_SET(mcu_isp_pin, 1); - os_delay_us(100L); - } - break; - } - state = TN_end; - break; - } - } - return state; + for (int i=0; i write one to outbuf and go normal again + state = TN_normal; + uart0_write_char(c); + break; + case WILL: // negotiation + state = TN_will; + break; + case SB: // command sequence begin + state = TN_start; + break; + case SE: // command sequence end + state = TN_normal; + break; + default: // not sure... let's ignore + uart0_write_char(IAC); + uart0_write_char(c); + } + break; + case TN_will: + state = TN_normal; // yes, we do COM port options, let's go back to normal + break; + case TN_start: // in command seq, now comes the type of cmd + if (c == ComPortOpt) state = TN_comPort; + else state = TN_end; // an option we don't know, skip 'til the end seq + break; + case TN_end: // wait for end seq + if (c == IAC) state = TN_iac; // simple wait to accept end or next escape seq + break; + case TN_comPort: + if (c == SetControl) state = TN_setControl; + else state = TN_end; + break; + case TN_setControl: // switch control line and delay a tad + switch (c) { + case DTR_ON: + if (mcu_reset_pin >= 0) { + os_printf("MCU reset gpio%d\n", mcu_reset_pin); + GPIO_OUTPUT_SET(mcu_reset_pin, 0); + os_delay_us(100L); + } else os_printf("MCU reset: no pin\n"); + break; + case DTR_OFF: + if (mcu_reset_pin >= 0) { + GPIO_OUTPUT_SET(mcu_reset_pin, 1); + os_delay_us(100L); + } + break; + case RTS_ON: + if (mcu_isp_pin >= 0) { + os_printf("MCU ISP gpio%d\n", mcu_isp_pin); + GPIO_OUTPUT_SET(mcu_isp_pin, 0); + os_delay_us(100L); + } else os_printf("MCU isp: no pin\n"); + slip_disabled++; + break; + case RTS_OFF: + if (mcu_isp_pin >= 0) { + GPIO_OUTPUT_SET(mcu_isp_pin, 1); + os_delay_us(100L); + } + if (slip_disabled > 0) slip_disabled--; + break; + } + state = TN_end; + break; + } + } + return state; } +// Generate a reset pulse for the attached microcontroller void ICACHE_FLASH_ATTR serbridgeReset() { - if (mcu_reset_pin >= 0) { - os_printf("MCU reset gpio%d\n", mcu_reset_pin); - GPIO_OUTPUT_SET(mcu_reset_pin, 0); - os_delay_us(100L); - GPIO_OUTPUT_SET(mcu_reset_pin, 1); - } else os_printf("MCU reset: no pin\n"); + if (mcu_reset_pin >= 0) { + os_printf("MCU reset gpio%d\n", mcu_reset_pin); + GPIO_OUTPUT_SET(mcu_reset_pin, 0); + os_delay_us(100L); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); + } else os_printf("MCU reset: no pin\n"); } // Receive callback static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) { - serbridgeConnData *conn = serbridgeFindConnData(arg); - //os_printf("Receive callback on conn %p\n", conn); - if (conn == NULL) return; - - // at the start of a connection we're in cmInit mode and we wait for the first few characters - // to arrive in order to decide what type of connection this is.. The following if statements - // do this dispatch. An issue here is that we assume that the first few characters all arrive - // in the same TCP packet, which is true if the sender is a program, but not necessarily - // if the sender is a person typing (although in that case the line-oriented TTY input seems - // to make it work too). If this becomes a problem we need to buffer the first few chars... - if (conn->conn_mode == cmInit) { - - // If the connection starts with the Arduino or ARM reset sequence we perform a RESET - if ((len == 2 && strncmp(data, "0 ", 2) == 0) || - (len == 2 && strncmp(data, "?\n", 2) == 0) || - (len == 3 && strncmp(data, "?\r\n", 3) == 0)) { - os_printf("MCU Reset=%d ISP=%d\n", mcu_reset_pin, mcu_isp_pin); - os_delay_us(2*1000L); // time for os_printf to happen - // send reset to arduino/ARM - if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 0); - os_delay_us(100L); - if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 0); - os_delay_us(100L); - if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1); - os_delay_us(100L); - if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); - os_delay_us(1000L); - conn->conn_mode = cmAVR; - - - // If the connection starts with a telnet negotiation we will do telnet - } else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, ComPortOpt}, 3) == 0) { - conn->conn_mode = cmTelnet; - conn->telnet_state = TN_normal; - // note that the three negotiation chars will be gobbled-up by telnetUnwrap - os_printf("telnet mode\n"); - - // looks like a plain-vanilla connection! - } else { - conn->conn_mode = cmTransparent; - } - - // Process return data on TCP client connections - } else if (conn->conn_mode == cmTcpClient) { - } - - // write the buffer to the uart - if (conn->conn_mode == cmTelnet) { - conn->telnet_state = telnetUnwrap((uint8_t *)data, len, conn->telnet_state); - } else { - uart0_tx_buffer(data, len); - } - - serledFlash(50); // short blink on serial LED + serbridgeConnData *conn = serbridgeFindConnData(arg); + //os_printf("Receive callback on conn %p\n", conn); + if (conn == NULL) return; + + // at the start of a connection we're in cmInit mode and we wait for the first few characters + // to arrive in order to decide what type of connection this is.. The following if statements + // do this dispatch. An issue here is that we assume that the first few characters all arrive + // in the same TCP packet, which is true if the sender is a program, but not necessarily + // if the sender is a person typing (although in that case the line-oriented TTY input seems + // to make it work too). If this becomes a problem we need to buffer the first few chars... + if (conn->conn_mode == cmInit) { + + // If the connection starts with the Arduino or ARM reset sequence we perform a RESET + if ((len == 2 && strncmp(data, "0 ", 2) == 0) || + (len == 2 && strncmp(data, "?\n", 2) == 0) || + (len == 3 && strncmp(data, "?\r\n", 3) == 0)) { + os_printf("MCU Reset=gpio%d ISP=gpio%d\n", mcu_reset_pin, mcu_isp_pin); + os_delay_us(2*1000L); // time for os_printf to happen + // send reset to arduino/ARM + if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 0); + os_delay_us(100L); + if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 0); + os_delay_us(100L); + if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1); + os_delay_us(100L); + if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); + os_delay_us(1000L); + conn->conn_mode = cmAVR; + slip_disabled++; // disable SLIP so it doesn't interfere with flashing + + // If the connection starts with a telnet negotiation we will do telnet + } else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, ComPortOpt}, 3) == 0) { + conn->conn_mode = cmTelnet; + conn->telnet_state = TN_normal; + // note that the three negotiation chars will be gobbled-up by telnetUnwrap + os_printf("telnet mode\n"); + + // looks like a plain-vanilla connection! + } else { + conn->conn_mode = cmTransparent; + } + + // Process return data on TCP client connections + } else if (conn->conn_mode == cmTcpClient) { + } + + // write the buffer to the uart + if (conn->conn_mode == cmTelnet) { + conn->telnet_state = telnetUnwrap((uint8_t *)data, len, conn->telnet_state); + } else { + uart0_tx_buffer(data, len); + } + + serledFlash(50); // short blink on serial LED } //===== UART -> TCP @@ -203,18 +209,19 @@ static char txbuffer[MAX_CONN][MAX_TXBUFFER]; // Send all data in conn->txbuffer // returns result from espconn_sent if data in buffer or ESPCONN_OK (0) // Use only internally from espbuffsend and serbridgeSentCb -static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) { - sint8 result = ESPCONN_OK; - if (conn->txbufferlen != 0) { - //os_printf("%d TX %d\n", system_get_time(), conn->txbufferlen); - conn->readytosend = false; - result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); - conn->txbufferlen = 0; - if (result != ESPCONN_OK) { - os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); - } - } - return result; +static sint8 ICACHE_FLASH_ATTR +sendtxbuffer(serbridgeConnData *conn) { + sint8 result = ESPCONN_OK; + if (conn->txbufferlen != 0) { + //os_printf("%d TX %d\n", system_get_time(), conn->txbufferlen); + conn->readytosend = false; + result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); + conn->txbufferlen = 0; + if (result != ESPCONN_OK) { + os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); + } + } + return result; } // espbuffsend adds data to the send buffer. If the previous send was completed it calls @@ -222,173 +229,44 @@ static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) { // Returns ESPCONN_OK (0) for success, -128 if buffer is full or error from espconn_sent // Use espbuffsend instead of espconn_sent as it solves the problem that espconn_sent must // only be called *after* receiving an espconn_sent_callback for the previous packet. -static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) { - if (conn->txbufferlen + len > MAX_TXBUFFER) { - os_printf("espbuffsend: txbuffer full on conn %p\n", conn); - return -128; - } - os_memcpy(conn->txbuffer + conn->txbufferlen, data, len); - conn->txbufferlen += len; - if (conn->readytosend) { - return sendtxbuffer(conn); - } else { - //os_printf("%d QU %d\n", system_get_time(), conn->txbufferlen); - } - return ESPCONN_OK; +static sint8 ICACHE_FLASH_ATTR +espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) { + if (conn->txbufferlen + len > MAX_TXBUFFER) { + os_printf("espbuffsend: txbuffer full on conn %p\n", conn); + return -128; + } + os_memcpy(conn->txbuffer + conn->txbufferlen, data, len); + conn->txbufferlen += len; + if (conn->readytosend) { + return sendtxbuffer(conn); + } else { + //os_printf("%d QU %d\n", system_get_time(), conn->txbufferlen); + } + return ESPCONN_OK; +} + +void ICACHE_FLASH_ATTR +console_process(char *buf, short len) { + // push buffer into web-console + for (short i=0; ireadytosend = true; - sendtxbuffer(conn); // send possible new data in txbuffer -} - -// TCP client connection state machine -// This processes commands from the attached uC to open outboud TCP connections -enum { - TC_idle, // in-between commands - TC_newline, // newline seen - TC_start, // start character (~) seen - TC_cmd, // command start (@) seen - TC_cmdChar, // command character seen - TC_cmdLine, // accumulating command - TC_tdchan, // saw data channel character - TC_tdlen1, // saw first data length character - TC_tdata0, // accumulate data, zero-terminated - TC_tdataN, // accumulate data, length-terminated -}; -static uint8_t tcState = TC_newline; -static uint8_t tcChan; // channel for current command (index into tcConn) - -#define CMD_MAX 256 -static char tcCmdBuf[CMD_MAX]; -static short tcCmdBufLen = 0; -static char tcCmdChar; -static short tcLen; - -// scan a buffer for tcp client commands -static int ICACHE_FLASH_ATTR -tcpClientProcess(char *buf, int len) -{ - char *in=buf, *out=buf; - for (short i=0; i= '0' && c <= '9') { - tcChan = c-'0'; - tcState = TC_tdchan; - continue; - } - *out++ = '~'; // make up for '~' we skipped - break; - case TC_cmd: // saw control char (@), expect channel char - if (c >= '0' && c <= '9') { - tcChan = c-'0'; - tcState = TC_cmdChar; - continue; - } else { - *out++ = '~'; // make up for '~' we skipped - *out++ = '@'; // make up for '@' we skipped - break; - } - case TC_cmdChar: // saw channel number, expect command char - tcCmdChar = c; // save command character - tcCmdBufLen = 0; // empty the command buffer - tcState = TC_cmdLine; - continue; - case TC_cmdLine: // accumulating command in buffer - if (c != '\n') { - if (tcCmdBufLen < CMD_MAX) tcCmdBuf[tcCmdBufLen++] = c; - } else { - tcpClientCommand(tcChan, tcCmdChar, tcCmdBuf); - tcState = TC_newline; - } - continue; - case TC_tdchan: // saw channel number, getting first length char - if (c >= '0' && c <= '9') { - tcLen = c-'0'; - } else if (c >= 'A' && c <= 'F') { - tcLen = c-'A'+10; - } else { - *out++ = '~'; // make up for '~' we skipped - *out++ = '0'+tcChan; - break; - } - tcState = TC_tdlen1; - continue; - case TC_tdlen1: // saw first length char, get second - tcLen *= 16; - if (c >= '0' && c <= '9') { - tcLen += c-'0'; - } else if (c >= 'A' && c <= 'F') { - tcLen += c-'A'+10; - } else { - *out++ = '~'; // make up for '~' we skipped - *out++ = '0'+tcChan; - break; - } - tcState = tcLen == 0 ? TC_tdata0 : TC_tdataN; - continue; - case TC_tdata0: // saw data length, getting data characters zero-terminated - if (c != 0) { - tcpClientSendChar(tcChan, c); - } else { - tcpClientSendPush(tcChan); - tcState = TC_idle; - } - continue; - case TC_tdataN: // saw data length, getting data characters length-terminated - tcpClientSendChar(tcChan, c); - tcLen--; - if (tcLen == 0) { - tcpClientSendPush(tcChan); - tcState = TC_idle; - } - continue; - } - *out++ = c; - } - //if (tcState != TC_idle) os_printf("tcState=%d\n", tcState); - return out-buf; -} - -// callback with a buffer of characters that have arrived on the uart -void ICACHE_FLASH_ATTR -serbridgeUartCb(char *buf, int length) { - // push the buffer into the microcontroller console - for (int i=0; i 0) { - for (int i=0; ireadytosend = true; + sendtxbuffer(conn); // send possible new data in txbuffer } //===== Connect / disconnect @@ -396,101 +274,107 @@ serbridgeUartCb(char *buf, int length) { // Error callback (it's really poorly named, it's not a "connection reconnected" callback, // it's really a "connection broken, please reconnect" callback) static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) { - serbridgeConnData *sbConn = serbridgeFindConnData(arg); - if (sbConn == NULL) return; - // Close the connection - espconn_disconnect(sbConn->conn); - // free connection slot - sbConn->conn = NULL; + serbridgeConnData *sbConn = serbridgeFindConnData(arg); + if (sbConn == NULL) return; + if (sbConn->conn_mode == cmAVR) { + if (slip_disabled > 0) slip_disabled--; + } + // Close the connection + espconn_disconnect(sbConn->conn); + // free connection slot + sbConn->conn = NULL; } // Disconnection callback static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) { - serbridgeConnData *sbConn = serbridgeFindConnData(arg); - if (sbConn == NULL) return; - // send reset to arduino/ARM - if (sbConn->conn_mode == cmAVR && mcu_reset_pin >= 0) { - GPIO_OUTPUT_SET(mcu_reset_pin, 0); - os_delay_us(100L); - GPIO_OUTPUT_SET(mcu_reset_pin, 1); - } - // free connection slot - sbConn->conn = NULL; + serbridgeConnData *sbConn = serbridgeFindConnData(arg); + if (sbConn == NULL) return; + // send reset to arduino/ARM + if (sbConn->conn_mode == cmAVR) { + if (slip_disabled > 0) slip_disabled--; + if (mcu_reset_pin >= 0) { + GPIO_OUTPUT_SET(mcu_reset_pin, 0); + os_delay_us(100L); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); + } + } + // free connection slot + sbConn->conn = NULL; } // New connection callback, use one of the connection descriptors, if we have one left. static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { - struct espconn *conn = arg; - // Find empty conndata in pool - int i; - for (i=0; ireverse = connData+i; - connData[i].conn = conn; - connData[i].txbufferlen = 0; - connData[i].readytosend = true; - connData[i].telnet_state = 0; - connData[i].conn_mode = cmInit; - - espconn_regist_recvcb(conn, serbridgeRecvCb); - espconn_regist_reconcb(conn, serbridgeReconCb); - espconn_regist_disconcb(conn, serbridgeDisconCb); - espconn_regist_sentcb(conn, serbridgeSentCb); - - espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); + struct espconn *conn = arg; + // Find empty conndata in pool + int i; + for (i=0; ireverse = connData+i; + connData[i].conn = conn; + connData[i].txbufferlen = 0; + connData[i].readytosend = true; + connData[i].telnet_state = 0; + connData[i].conn_mode = cmInit; + + espconn_regist_recvcb(conn, serbridgeRecvCb); + espconn_regist_reconcb(conn, serbridgeReconCb); + espconn_regist_disconcb(conn, serbridgeDisconCb); + espconn_regist_sentcb(conn, serbridgeSentCb); + + espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); } //===== Initialization void ICACHE_FLASH_ATTR serbridgeInitPins() { - mcu_reset_pin = flashConfig.reset_pin; - mcu_isp_pin = flashConfig.isp_pin; - os_printf("Serbridge pins: reset=%d isp=%d swap=%d\n", - mcu_reset_pin, mcu_isp_pin, flashConfig.swap_uart); - - if (flashConfig.swap_uart) { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4); - PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U); - PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U); - system_uart_swap(); - } else { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, 0); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, 0); - system_uart_de_swap(); - } - - // set both pins to 1 before turning them on so we don't cause a reset - if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); - if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1); - // switch pin mux to make these pins GPIO pins - if (mcu_reset_pin >= 0) makeGpio(mcu_reset_pin); - if (mcu_isp_pin >= 0) makeGpio(mcu_isp_pin); + mcu_reset_pin = flashConfig.reset_pin; + mcu_isp_pin = flashConfig.isp_pin; + os_printf("Serbridge pins: reset=%d isp=%d swap=%d\n", + mcu_reset_pin, mcu_isp_pin, flashConfig.swap_uart); + + if (flashConfig.swap_uart) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4); + PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U); + PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U); + system_uart_swap(); + } else { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, 0); + system_uart_de_swap(); + } + + // set both pins to 1 before turning them on so we don't cause a reset + if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1); + if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1); + // switch pin mux to make these pins GPIO pins + if (mcu_reset_pin >= 0) makeGpio(mcu_reset_pin); + if (mcu_isp_pin >= 0) makeGpio(mcu_isp_pin); } // Start transparent serial bridge TCP server on specified port (typ. 23) void ICACHE_FLASH_ATTR serbridgeInit(int port) { - serbridgeInitPins(); - - int i; - for (i = 0; i < MAX_CONN; i++) { - connData[i].conn = NULL; - connData[i].txbuffer = txbuffer[i]; - } - serbridgeConn.type = ESPCONN_TCP; - serbridgeConn.state = ESPCONN_NONE; - serbridgeTcp.local_port = port; - serbridgeConn.proto.tcp = &serbridgeTcp; - - espconn_regist_connectcb(&serbridgeConn, serbridgeConnectCb); - espconn_accept(&serbridgeConn); - espconn_tcp_set_max_con_allow(&serbridgeConn, MAX_CONN); - espconn_regist_time(&serbridgeConn, SER_BRIDGE_TIMEOUT, 0); + serbridgeInitPins(); + + int i; + for (i = 0; i < MAX_CONN; i++) { + connData[i].conn = NULL; + connData[i].txbuffer = txbuffer[i]; + } + serbridgeConn.type = ESPCONN_TCP; + serbridgeConn.state = ESPCONN_NONE; + serbridgeTcp.local_port = port; + serbridgeConn.proto.tcp = &serbridgeTcp; + + espconn_regist_connectcb(&serbridgeConn, serbridgeConnectCb); + espconn_accept(&serbridgeConn); + espconn_tcp_set_max_con_allow(&serbridgeConn, MAX_CONN); + espconn_regist_time(&serbridgeConn, SER_BRIDGE_TIMEOUT, 0); } diff --git a/serial/serbridge.h b/serial/serbridge.h index 33c96a2..ce11906 100644 --- a/serial/serbridge.h +++ b/serial/serbridge.h @@ -32,7 +32,7 @@ typedef struct serbridgeConnData { void ICACHE_FLASH_ATTR serbridgeInit(int port); void ICACHE_FLASH_ATTR serbridgeInitPins(void); -void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, int len); +void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len); void ICACHE_FLASH_ATTR serbridgeReset(); #endif /* __SER_BRIDGE_H__ */ diff --git a/serial/slip.c b/serial/slip.c new file mode 100644 index 0000000..0c76103 --- /dev/null +++ b/serial/slip.c @@ -0,0 +1,136 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt + +#include "esp8266.h" +#include "uart.h" +#include "crc16.h" +#include "serbridge.h" +#include "serled.h" +#include "console.h" +#include "cmd.h" + +uint8_t slip_disabled; // disable slip to allow flashing of attached MCU + +extern void ICACHE_FLASH_ATTR console_process(char *buf, short len); + +// This SLIP parser does not conform to RFC 1055 https://tools.ietf.org/html/rfc1055, +// instead, it implements the framing implemented in https://github.com/tuanpmt/esp_bridge +// It accumulates each packet into a static buffer and calls cmd_parse() when the end +// of a packet is reached. It expects cmd_parse() to copy anything it needs from the +// buffer elsewhere as the buffer is immediately reused. +// One special feature is that if the first two characters of a packet are both printable or +// \n or \r then the parser assumes it's dealing with console debug output and calls +// slip_console(c) for each character and does not accumulate chars in the buffer until the +// next SLIP_END marker is seen. This allows random console debug output to come in between +// packets as long as each packet starts *and* ends with SLIP_END (which is an official +// variation on the SLIP protocol). + +static bool slip_escaped; // true when prev char received is escape +static bool slip_inpkt; // true when we're after SLIP_START and before SLIP_END +#define SLIP_MAX 1024 // max length of SLIP packet +static char slip_buf[SLIP_MAX]; // buffer for current SLIP packet +static short slip_len; // accumulated length in slip_buf + +// SLIP process a packet or a bunch of debug console chars +static void ICACHE_FLASH_ATTR +slip_process() { + if (slip_len < 1) return; + + if (!slip_inpkt) { + // debug console stuff + console_process(slip_buf, slip_len); + } else { + // proper SLIP packet, invoke command processor after checking CRC + //os_printf("SLIP: rcv %d\n", slip_len); + if (slip_len > 2) { + uint16_t crc = crc16_data((uint8_t*)slip_buf, slip_len-2, 0); + uint16_t rcv = ((uint16_t)slip_buf[slip_len-2]) | ((uint16_t)slip_buf[slip_len-1] << 8); + if (crc == rcv) { + CMD_parse_packet((uint8_t*)slip_buf, slip_len-2); + } else { + os_printf("SLIP: bad CRC, crc=%x rcv=%x\n", crc, rcv); + for (short i=0; i= ' ' && slip_buf[i] <= '~') + os_printf("%c", slip_buf[i]); + else + os_printf("\\%02X", slip_buf[i]); + } + os_printf("\n"); + } + } + } +} + +#if 0 +// determine whether a character is printable or not (or \r \n) +static bool ICACHE_FLASH_ATTR +slip_printable(char c) { + return (c >= ' ' && c <= '~') || c == '\n' || c == '\r'; +} +#endif + +static void ICACHE_FLASH_ATTR +slip_reset() { + slip_inpkt = false; + slip_escaped = false; + slip_len = 0; +} + +// SLIP parse a single character +static void ICACHE_FLASH_ATTR +slip_parse_char(char c) { + if (!slip_inpkt) { + if (c == SLIP_START) { + if (slip_len > 0) console_process(slip_buf, slip_len); + slip_reset(); + slip_inpkt = true; + return; + } + } else if (slip_escaped) { + // prev char was SLIP_REPL + c = SLIP_ESC(c); + slip_escaped = false; + } else { + switch (c) { + case SLIP_REPL: + slip_escaped = true; + return; + case SLIP_END: + // end of packet, process it and get ready for next one + if (slip_len > 0) slip_process(); + slip_reset(); + return; + case SLIP_START: + os_printf("SLIP: got SLIP_START while in packet?\n"); + slip_reset(); + return; + } + } + if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c; +} + +// callback with a buffer of characters that have arrived on the uart +void ICACHE_FLASH_ATTR +serbridgeUartCb(char *buf, short length) { + if (slip_disabled > 0) { + //os_printf("SLIP: disabled got %d\n", length); + console_process(buf, length); + for (short i=0; i 0) { + slip_process(); + slip_reset(); + } + + serledFlash(50); // short blink on serial LED +} diff --git a/serial/uart.h b/serial/uart.h index 2d1d432..3553155 100644 --- a/serial/uart.h +++ b/serial/uart.h @@ -4,7 +4,7 @@ #include "uart_hw.h" // Receive callback function signature -typedef void (*UartRecv_cb)(char *buf, int len); +typedef void (*UartRecv_cb)(char *buf, short len); // Initialize UARTs to the provided baud rates (115200 recommended). This also makes the os_printf // calls use uart1 for output (for debugging purposes) diff --git a/user/cgiwifi.c b/user/cgiwifi.c index 0d96e96..42ddf24 100644 --- a/user/cgiwifi.c +++ b/user/cgiwifi.c @@ -40,6 +40,8 @@ static char *wifiReasons[] = { static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; +void (*wifiStatusCb)(uint8_t); // callback when wifi status changes + static char* ICACHE_FLASH_ATTR wifiGetReason(void) { if (wifiReason <= 24) return wifiReasons[wifiReason]; if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24]; @@ -86,6 +88,7 @@ static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) { default: break; } + if (wifiStatusCb) (*wifiStatusCb)(wifiState); } // ===== wifi scanning @@ -478,7 +481,7 @@ int ICACHE_FLASH_ATTR printWifiInfo(char *buff) { char *mode = wifiMode[op]; char *status = "unknown"; int st = wifi_station_get_connect_status(); - if (st > 0 && st < sizeof(connStatuses)) status = connStatuses[st]; + if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; int p = wifi_get_phy_mode(); char *phy = wifiPhy[p&3]; char *warn = wifiWarn[op]; diff --git a/user/cgiwifi.h b/user/cgiwifi.h index 557c31a..c78a793 100644 --- a/user/cgiwifi.h +++ b/user/cgiwifi.h @@ -14,4 +14,6 @@ int cgiWiFiConnStatus(HttpdConnData *connData); int cgiWiFiSpecial(HttpdConnData *connData); void wifiInit(void); +extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes + #endif From 1261344e2ec3c2306413f8ddb80fb2ba41383ab6 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Mon, 24 Aug 2015 13:20:13 -0500 Subject: [PATCH 20/20] Added gitignores --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 7b7ee90..8a769b0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ html_compressed/ esp-link.tgz tve-patch/ yui +.localhistory/ +esp-link.sdf +espfs/mkespfsimage/mman-win32/libmman.a +espfs/mkespfsimage/mman-win32/mman.o