From 9ee1182d04570c23a620a0751fbcf6c48a3087f5 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 13 Jul 2015 10:25:29 -0700 Subject: [PATCH 001/104] 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 002/104] 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 003/104] 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 004/104] 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 005/104] 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 006/104] 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 007/104] 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 008/104] 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 009/104] 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 010/104] 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 011/104] 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 c7e8d19deeba7c3c99494df97cd12b7f39f7da7f Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 13 Jul 2015 10:25:29 -0700 Subject: [PATCH 012/104] 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 f16a0e0..308b4df 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 013/104] 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 014/104] 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 2c5dbd4..85aaf97 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 b31c0a557c23001af2e8003845d09194a010a350 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 1 Aug 2015 23:43:01 -0700 Subject: [PATCH 015/104] add support for wifi-link-12 with swapped uart pins Conflicts: serial/serbridge.c --- serial/serbridge.c | 13 ++++++++++++- user/cgipins.c | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/serial/serbridge.c b/serial/serbridge.c index 6d45610..e18ac82 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -322,7 +322,18 @@ serbridgeUartCb(char *buf, int length) { 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) { + 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 ae6b26c..a5940a3 100644 --- a/user/cgipins.c +++ b/user/cgipins.c @@ -23,15 +23,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, "\" }"); } @@ -87,6 +90,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 1ed85714da7d13cd2c2406e6044dc7d09a41cd68 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 3 Aug 2015 20:56:39 -0700 Subject: [PATCH 016/104] 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 08ab8b58db919254985024dad33f85a1f723eebc Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 14:13:25 -0700 Subject: [PATCH 017/104] fix swap-uart --- serial/serbridge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/serial/serbridge.c b/serial/serbridge.c index e18ac82..0d6dc57 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -328,6 +328,8 @@ void ICACHE_FLASH_ATTR serbridgeInitPins() { 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); From 08ac33dd56cac5c16f5ee3aec578897ecd760252 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 13:39:01 -0700 Subject: [PATCH 018/104] 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 a41f8916956b0feae66acca8f42aea625fca49de Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 14:43:06 -0700 Subject: [PATCH 019/104] add missing pieces for swap-uart; switch to SDK 1.3 --- Makefile | 4 ++-- user/cgipins.c | 20 +++++++++++--------- user/config.h | 1 + 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 99d5617..ec7203d 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" 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/user/cgipins.c b/user/cgipins.c index a5940a3..ab6fdc0 100644 --- a/user/cgipins.c +++ b/user/cgipins.c @@ -8,15 +8,16 @@ #include "serbridge.h" static char *map_names[] = { - "esp-bridge", "jn-esp-v2", "esp-01(AVR)", "esp-01(ARM)", "esp-br-rev", + "esp-bridge", "jn-esp-v2", "esp-01(AVR)", "esp-01(ARM)", "esp-br-rev", "wifi-link-12", }; -static char* map_func[] = { "reset", "isp", "conn_led", "ser_led" }; -static int8_t map_asn[][4] = { - { 12, 13, 0, 14 }, // esp-bridge - { 12, 13, 0, 2 }, // jn-esp-v2 - { 0, -1, 2, -1 }, // esp-01(AVR) - { 0, 2, -1, -1 }, // esp-01(ARM) - { 13, 12, 14, 0 }, // esp-br-rev -- for test purposes +static char* map_func[] = { "reset", "isp", "conn_led", "ser_led", "swap_uart" }; +static int8_t map_asn[][5] = { + { 12, 13, 0, 14, 0 }, // esp-bridge + { 12, 13, 0, 2, 0 }, // jn-esp-v2 + { 0, -1, 2, -1, 0 }, // esp-01(AVR) + { 0, 2, -1, -1, 0 }, // esp-01(ARM) + { 13, 12, 14, 0, 0 }, // esp-br-rev -- for test purposes + { 3, 1, 0, 2, 1 }, // esp-link-12 }; static const int num_map_names = sizeof(map_names)/sizeof(char*); static const int num_map_func = sizeof(map_func)/sizeof(char*); @@ -33,7 +34,8 @@ int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) { for (int i=0; i Date: Wed, 19 Aug 2015 19:13:30 -0700 Subject: [PATCH 020/104] 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 ec7203d..5d222a5 100644 --- a/Makefile +++ b/Makefile @@ -34,21 +34,37 @@ FLASH_SIZE ?= 512KB 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 b840c8f..0200919 100644 --- a/user/config.c +++ b/user/config.c @@ -24,10 +24,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 0d2eed195727251f5fbfe8b0822751fbb3c3edc3 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:13:55 -0700 Subject: [PATCH 021/104] 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 85aaf97..57c45b4 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 0200919..6c6cf91 100644 --- a/user/config.c +++ b/user/config.c @@ -90,7 +90,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; @@ -112,7 +112,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 dcadb70df870bdc793fa8911462210d875e9d756 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:14:10 -0700 Subject: [PATCH 022/104] 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 645d215..f454c17 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -111,7 +111,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 cd0754071b869aa3ce4fe95028ca51fdc93cfa9d Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:30:59 -0700 Subject: [PATCH 023/104] small flash config improvement plus more comments --- user/config.c | 11 +++++++---- user/config.h | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/user/config.c b/user/config.c index 6c6cf91..b8122be 100644 --- a/user/config.c +++ b/user/config.c @@ -17,6 +17,7 @@ FlashConfig flashDefault = { "esp-link\0 ", // hostname 0, 0x00ffffff, 0, // static ip, netmask, gateway 0, // log mode + 0, // swap uart (don't by default) }; typedef union { @@ -46,7 +47,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)); @@ -54,7 +54,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; @@ -66,11 +66,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; @@ -83,6 +83,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 f451f26..d92e7f2 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 b48614c37328e4607c0aa9ad49dade17d70d7c34 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 21:43:08 -0700 Subject: [PATCH 024/104] makefile tweak --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5d222a5..709a0a1 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ ESPBAUD ?= 460800 # --------------- chipset configuration --------------- -# Pick your flash size: "512KB" or "4MB" +# Pick your flash size: "512KB", "1MB", or "4MB" FLASH_SIZE ?= 512KB ifeq ("$(FLASH_SIZE)","512KB") From dd7f9a16a906e0beea00608b919b2969ae327a87 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Wed, 19 Aug 2015 19:13:30 -0700 Subject: [PATCH 025/104] 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 026/104] 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 027/104] 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 028/104] 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 029/104] 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 5cec16c3fdebed65480954e42426d5e64018f9fb Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 21 Aug 2015 17:52:40 -0500 Subject: [PATCH 030/104] Added windows build support --- .gitignore | 5 + Makefile | 5 +- esp-link.sln | 22 + esp-link.vcxproj | 175 +++ espfs/espfs.c | 21 +- espfs/heatshrink/LICENSE | 14 + espfs/heatshrink/Makefile | 49 + espfs/heatshrink/README.md | 49 + espfs/heatshrink/dec_sm.dot | 52 + espfs/heatshrink/enc_sm.dot | 51 + espfs/heatshrink/greatest.h | 591 +++++++++++ espfs/heatshrink/heatshrink.c | 446 ++++++++ espfs/heatshrink/heatshrink_common.h | 20 + espfs/heatshrink/heatshrink_config.h | 24 + espfs/heatshrink/heatshrink_decoder.c | 382 +++++++ espfs/heatshrink/heatshrink_decoder.h | 101 ++ espfs/heatshrink/heatshrink_encoder.c | 650 ++++++++++++ espfs/heatshrink/heatshrink_encoder.h | 109 ++ espfs/heatshrink/test_heatshrink_dynamic.c | 999 ++++++++++++++++++ .../test_heatshrink_dynamic_theft.c | 521 +++++++++ espfs/heatshrink/test_heatshrink_static.c | 167 +++ espfs/heatshrink_decoder.c | 2 +- espfs/mkespfsimage/Makefile | 36 +- espfs/mkespfsimage/Makefile.linux | 24 + espfs/mkespfsimage/Makefile.windows | 33 + espfs/mkespfsimage/build-linux.sh | 4 + espfs/mkespfsimage/build-win32.sh | 7 + espfs/mkespfsimage/espfsformat.h | 33 + espfs/mkespfsimage/heatshrink_encoder.c | 2 +- espfs/mkespfsimage/main.c | 35 +- espfs/mkespfsimage/mman-win32/Makefile | 48 + espfs/mkespfsimage/mman-win32/config.mak | 11 + espfs/mkespfsimage/mman-win32/configure | 157 +++ espfs/mkespfsimage/mman-win32/mman.c | 180 ++++ espfs/mkespfsimage/mman-win32/mman.h | 55 + espfs/mkespfsimage/mman-win32/test.c | 235 ++++ espmake.cmd | 7 + 37 files changed, 5285 insertions(+), 37 deletions(-) create mode 100644 esp-link.sln create mode 100644 esp-link.vcxproj create mode 100644 espfs/heatshrink/LICENSE create mode 100644 espfs/heatshrink/Makefile create mode 100644 espfs/heatshrink/README.md create mode 100644 espfs/heatshrink/dec_sm.dot create mode 100644 espfs/heatshrink/enc_sm.dot create mode 100644 espfs/heatshrink/greatest.h create mode 100644 espfs/heatshrink/heatshrink.c create mode 100644 espfs/heatshrink/heatshrink_common.h create mode 100644 espfs/heatshrink/heatshrink_config.h create mode 100644 espfs/heatshrink/heatshrink_decoder.c create mode 100644 espfs/heatshrink/heatshrink_decoder.h create mode 100644 espfs/heatshrink/heatshrink_encoder.c create mode 100644 espfs/heatshrink/heatshrink_encoder.h create mode 100644 espfs/heatshrink/test_heatshrink_dynamic.c create mode 100644 espfs/heatshrink/test_heatshrink_dynamic_theft.c create mode 100644 espfs/heatshrink/test_heatshrink_static.c create mode 100644 espfs/mkespfsimage/Makefile.linux create mode 100644 espfs/mkespfsimage/Makefile.windows create mode 100644 espfs/mkespfsimage/build-linux.sh create mode 100644 espfs/mkespfsimage/build-win32.sh create mode 100644 espfs/mkespfsimage/espfsformat.h create mode 100644 espfs/mkespfsimage/mman-win32/Makefile create mode 100644 espfs/mkespfsimage/mman-win32/config.mak create mode 100644 espfs/mkespfsimage/mman-win32/configure create mode 100644 espfs/mkespfsimage/mman-win32/mman.c create mode 100644 espfs/mkespfsimage/mman-win32/mman.h create mode 100644 espfs/mkespfsimage/mman-win32/test.c create mode 100644 espmake.cmd diff --git a/.gitignore b/.gitignore index 7b7ee90..899b7c3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ html_compressed/ esp-link.tgz tve-patch/ yui +espfs/mkespfsimage/mman-win32/mman.o +esp-link.opensdf +esp-link.sdf +espfs/mkespfsimage/mman-win32/libmman.a +.localhistory/ diff --git a/Makefile b/Makefile index 709a0a1..297e4fe 100644 --- a/Makefile +++ b/Makefile @@ -289,6 +289,9 @@ $(BUILD_DIR): wiflash: all ./wiflash $(ESP_HOSTNAME) $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin +baseflash: all + $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin + flash: all $(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 \ @@ -296,7 +299,7 @@ flash: all yui/$(YUI-COMPRESSOR): $(Q) mkdir -p yui - cd yui; wget https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) + cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) ifeq ("$(COMPRESS_W_YUI)","yes") $(BUILD_BASE)/espfs_img.o: yui/$(YUI-COMPRESSOR) diff --git a/esp-link.sln b/esp-link.sln new file mode 100644 index 0000000..fb7b468 --- /dev/null +++ b/esp-link.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "esp-link", "esp-link.vcxproj", "{A92F0CAA-F89B-4F78-AD2A-A042429BD87F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Release|ARM = Release|ARM + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Debug|ARM.ActiveCfg = Debug|ARM + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Debug|ARM.Build.0 = Debug|ARM + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Release|ARM.ActiveCfg = Release|ARM + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Release|ARM.Build.0 = Release|ARM + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/esp-link.vcxproj b/esp-link.vcxproj new file mode 100644 index 0000000..45dd2ae --- /dev/null +++ b/esp-link.vcxproj @@ -0,0 +1,175 @@ + + + + + Debug + ARM + + + Release + ARM + + + + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F} + MakeFileProj + + + + Makefile + + + + + + + + + + + __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ + .\mqtt\include;.\serial;.\user;.\espfs;.\httpd;.\include;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\x86_64-w64-mingw32;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\backward;c:\tools\mingw64\x86_64-w64-mingw32\include\c++;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include-fixed;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include;c:\Espressif\sdk\include + + + + + + + bin + build + + + espmake flash + espmake clean all + espmake clean + + + espmake baseflash + espmake clean all + espmake clean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/espfs/espfs.c b/espfs/espfs.c index 3da6692..1f503d5 100644 --- a/espfs/espfs.c +++ b/espfs/espfs.c @@ -7,9 +7,9 @@ It's written for use with httpd, but doesn't need to be used as such. /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): - * Jeroen Domburg wrote this file. As long as you retain - * this notice you can do whatever you want with this stuff. If we meet some day, - * and you think this stuff is worth it, you can buy me a beer in return. + * Jeroen Domburg wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. * ---------------------------------------------------------------------------- */ @@ -42,11 +42,12 @@ It's written for use with httpd, but doesn't need to be used as such. #ifdef ESPFS_HEATSHRINK #include "heatshrink_config_custom.h" -#include "heatshrink_decoder.h" +#include "heatshrink/heatshrink_decoder.h" #endif static char* espFsData = NULL; + struct EspFsFile { EspFsHeader *header; char decompressor; @@ -146,19 +147,19 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { return NULL; } if (h.flags&FLAG_LASTFILE) { - //os_printf("End of image.\n"); + os_printf("End of image.\n"); return NULL; } //Grab the name of the file. - p+=sizeof(EspFsHeader); + p+=sizeof(EspFsHeader); os_memcpy(namebuf, p, sizeof(namebuf)); -// os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", +// os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", // namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags); if (os_strcmp(namebuf, fileName)==0) { //Yay, this is the file we need! p+=h.nameLen; //Skip to content. r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem - //os_printf("Alloc %p[%d]\n", r, sizeof(EspFsFile)); +// os_printf("Alloc %p\n", r); if (r==NULL) return NULL; r->header=(EspFsHeader *)hpos; r->decompressor=h.compression; @@ -175,7 +176,7 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { //Decoder params are stored in 1st byte. memcpyAligned(&parm, r->posComp, 1); r->posComp++; - //os_printf("Heatshrink compressed file; decode parms = %x\n", parm); + os_printf("Heatshrink compressed file; decode parms = %x\n", parm); dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf); r->decompData=dec; #endif @@ -265,7 +266,7 @@ void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) { // os_printf("Freed %p\n", dec); } #endif - //os_printf("Freed %p\n", fh); +// os_printf("Freed %p\n", fh); os_free(fh); } diff --git a/espfs/heatshrink/LICENSE b/espfs/heatshrink/LICENSE new file mode 100644 index 0000000..31ec3df --- /dev/null +++ b/espfs/heatshrink/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2013, Scott Vokes +All rights reserved. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/espfs/heatshrink/Makefile b/espfs/heatshrink/Makefile new file mode 100644 index 0000000..d8e6875 --- /dev/null +++ b/espfs/heatshrink/Makefile @@ -0,0 +1,49 @@ +PROJECT = heatshrink +#OPTIMIZE = -O0 +#OPTIMIZE = -Os +OPTIMIZE = -O3 +WARN = -Wall -Wextra -pedantic #-Werror +CFLAGS += -std=c99 -g ${WARN} ${OPTIMIZE} +CFLAGS += -Wmissing-prototypes +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wmissing-declarations + +# If libtheft is available, build additional property-based tests. +# Uncomment these to use it in test_heatshrink_dynamic. +#CFLAGS += -DHEATSHRINK_HAS_THEFT +#LDFLAGS += -ltheft + +all: + @echo "For tests, make test_heatshrink_dynamic (default) or change the" + @echo "config.h to disable static memory and build test_heatshrink_static." + @echo "For the standalone command-line tool, make heatshrink." + +${PROJECT}: heatshrink.c + +OBJS= heatshrink_encoder.o \ + heatshrink_decoder.o \ + +heatshrink: ${OBJS} +test_heatshrink_dynamic: ${OBJS} test_heatshrink_dynamic_theft.o +test_heatshrink_static: ${OBJS} + +*.o: Makefile heatshrink_config.h + +heatshrink_decoder.o: heatshrink_decoder.h heatshrink_common.h +heatshrink_encoder.o: heatshrink_encoder.h heatshrink_common.h + +tags: TAGS + +TAGS: + etags *.[ch] + +diagrams: dec_sm.png enc_sm.png + +dec_sm.png: dec_sm.dot + dot -o $@ -Tpng $< + +enc_sm.png: enc_sm.dot + dot -o $@ -Tpng $< + +clean: + rm -f ${PROJECT} test_heatshrink_{dynamic,static} *.o *.core {dec,enc}_sm.png TAGS diff --git a/espfs/heatshrink/README.md b/espfs/heatshrink/README.md new file mode 100644 index 0000000..ab150ee --- /dev/null +++ b/espfs/heatshrink/README.md @@ -0,0 +1,49 @@ +# heatshrink + +A data compression/decompression library for embedded/real-time systems. + +## Key Features: + +- **Low memory usage (as low as 50 bytes)** + It is useful for some cases with less than 50 bytes, and useful + for many general cases with < 300 bytes. +- **Incremental, bounded CPU use** + You can chew on input data in arbitrarily tiny bites. + This is a useful property in hard real-time environments. +- **Can use either static or dynamic memory allocation** + The library doesn't impose any constraints on memory management. +- **ISC license** + You can use it freely, even for commercial purposes. + +## Getting Started: + +There is a standalone command-line program, `heatshrink`, but the +encoder and decoder can also be used as libraries, independent of each +other. To do so, copy `heatshrink_common.h`, `heatshrink_config.h`, and +either `heatshrink_encoder.c` or `heatshrink_decoder.c` (and their +respective header) into your project. + +Dynamic allocation is used by default, but in an embedded context, you +probably want to statically allocate the encoder/decoder. Set +`HEATSHRINK_DYNAMIC_ALLOC` to 0 in `heatshrink_config.h`. + +## More Information and Benchmarks: + +heatshrink is based on [LZSS], since it's particularly suitable for +compression in small amounts of memory. It can use an optional, small +[index] to make compression significantly faster, but otherwise can run +in under 100 bytes of memory. The index currently adds 2^(window size+1) +bytes to memory usage for compression, and temporarily allocates 512 +bytes on the stack during index construction. + +For more information, see the [blog post] for an overview, and the +`heatshrink_encoder.h` / `heatshrink_decoder.h` header files for API +documentation. + +[blog post]: http://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ +[index]: http://spin.atomicobject.com/2014/01/13/lightweight-indexing-for-embedded-systems/ +[LZSS]: http://en.wikipedia.org/wiki/Lempel-Ziv-Storer-Szymanski + +## Build Status + + [![Build Status](https://travis-ci.org/atomicobject/heatshrink.png)](http://travis-ci.org/atomicobject/heatshrink) diff --git a/espfs/heatshrink/dec_sm.dot b/espfs/heatshrink/dec_sm.dot new file mode 100644 index 0000000..470012f --- /dev/null +++ b/espfs/heatshrink/dec_sm.dot @@ -0,0 +1,52 @@ +digraph { + graph [label="Decoder state machine", labelloc="t"] + Start [style="invis", shape="point"] + empty + input_available + yield_literal + backref_index_msb + backref_index_lsb + backref_count_msb + backref_count_lsb + yield_backref + check_for_more_input + done [peripheries=2] + + empty->input_available [label="sink()", color="blue", weight=10] + Start->empty + + input_available->yield_literal [label="pop 1-bit"] + input_available->backref_index_msb [label="pop 0-bit", weight=10] + input_available->backref_index_lsb [label="pop 0-bit, index <8 bits", weight=10] + + yield_literal->yield_literal [label="sink()", color="blue"] + yield_literal->yield_literal [label="poll()", color="red"] + yield_literal->check_for_more_input [label="poll(), done", color="red"] + + backref_index_msb->backref_index_msb [label="sink()", color="blue"] + backref_index_msb->backref_index_lsb [label="pop index, upper bits", weight=10] + backref_index_msb->done [label="finish()", color="blue"] + + backref_index_lsb->backref_index_lsb [label="sink()", color="blue"] + backref_index_lsb->backref_count_msb [label="pop index, lower bits", weight=10] + backref_index_lsb->backref_count_lsb [label="pop index, count <=8 bits", weight=10] + backref_index_lsb->done [label="finish()", color="blue"] + + backref_count_msb->backref_count_msb [label="sink()", color="blue"] + backref_count_msb->backref_count_lsb [label="pop count, upper bits", weight=10] + backref_count_msb->done [label="finish()", color="blue"] + + backref_count_lsb->backref_count_lsb [label="sink()", color="blue"] + backref_count_lsb->yield_backref [label="pop count, lower bits", weight=10] + backref_count_lsb->done [label="finish()", color="blue"] + + yield_backref->yield_backref [label="sink()", color="blue"] + yield_backref->yield_backref [label="poll()", color="red"] + yield_backref->check_for_more_input [label="poll(), done", + color="red", weight=10] + + check_for_more_input->empty [label="no"] + check_for_more_input->input_available [label="yes"] + + empty->done [label="finish()", color="blue"] +} diff --git a/espfs/heatshrink/enc_sm.dot b/espfs/heatshrink/enc_sm.dot new file mode 100644 index 0000000..6d3030f --- /dev/null +++ b/espfs/heatshrink/enc_sm.dot @@ -0,0 +1,51 @@ +digraph { + graph [label="Encoder state machine", labelloc="t"] + start [style="invis", shape="point"] + not_full + filled + search + yield_tag_bit + yield_literal + yield_br_length + yield_br_index + save_backlog + flush_bits + done [peripheries=2] + + start->not_full [label="start"] + + not_full->not_full [label="sink(), not full", color="blue"] + not_full->filled [label="sink(), buffer is full", color="blue"] + not_full->filled [label="finish(), set is_finished", color="blue"] + + filled->search [label="indexing (if any)"] + + search->search [label="step"] + search->yield_tag_bit [label="literal"] + search->yield_tag_bit [label="match found"] + search->save_backlog [label="input exhausted"] + + yield_tag_bit->yield_tag_bit [label="poll(), full buf", color="red"] + yield_tag_bit->yield_literal [label="poll(), literal", color="red"] + yield_tag_bit->yield_br_index [label="poll(), no literal", color="red"] + yield_tag_bit->flush_bits [label="finishing, no literal"] + + yield_literal->yield_literal [label="poll(), full buf", color="red"] + yield_literal->search [label="poll(), no match", color="red"] + yield_literal->yield_tag_bit [label="poll(), match", color="red"] + yield_literal->flush_bits [label="poll(), final literal", color="red"] + + yield_br_index->yield_br_index [label="poll(), full buf", color="red"] + yield_br_index->yield_br_length [label="poll()", color="red"] + + yield_br_length->yield_br_length [label="poll(), full buf", color="red"] + yield_br_length->search [label="done"] + + save_backlog->flush_bits [label="finishing, no literal"] + save_backlog->yield_tag_bit [label="finishing, literal"] + save_backlog->not_full [label="expect more input"] + + flush_bits->flush_bits [label="poll(), full buf", color="red"] + flush_bits->done [label="poll(), flushed", color="red"] + flush_bits->done [label="no more output"] +} diff --git a/espfs/heatshrink/greatest.h b/espfs/heatshrink/greatest.h new file mode 100644 index 0000000..a92c642 --- /dev/null +++ b/espfs/heatshrink/greatest.h @@ -0,0 +1,591 @@ +/* + * Copyright (c) 2011 Scott Vokes + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef GREATEST_H +#define GREATEST_H + +#define GREATEST_VERSION_MAJOR 0 +#define GREATEST_VERSION_MINOR 9 +#define GREATEST_VERSION_PATCH 3 + +/* A unit testing system for C, contained in 1 file. + * It doesn't use dynamic allocation or depend on anything + * beyond ANSI C89. */ + + +/********************************************************************* + * Minimal test runner template + *********************************************************************/ +#if 0 + +#include "greatest.h" + +TEST foo_should_foo() { + PASS(); +} + +static void setup_cb(void *data) { + printf("setup callback for each test case\n"); +} + +static void teardown_cb(void *data) { + printf("teardown callback for each test case\n"); +} + +SUITE(suite) { + /* Optional setup/teardown callbacks which will be run before/after + * every test case in the suite. + * Cleared when the suite finishes. */ + SET_SETUP(setup_cb, voidp_to_callback_data); + SET_TEARDOWN(teardown_cb, voidp_to_callback_data); + + RUN_TEST(foo_should_foo); +} + +/* Add all the definitions that need to be in the test runner's main file. */ +GREATEST_MAIN_DEFS(); + +int main(int argc, char **argv) { + GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ + RUN_SUITE(suite); + GREATEST_MAIN_END(); /* display results */ +} + +#endif +/*********************************************************************/ + + +#include +#include +#include +#include + + +/*********** + * Options * + ***********/ + +/* Default column width for non-verbose output. */ +#ifndef GREATEST_DEFAULT_WIDTH +#define GREATEST_DEFAULT_WIDTH 72 +#endif + +/* FILE *, for test logging. */ +#ifndef GREATEST_STDOUT +#define GREATEST_STDOUT stdout +#endif + +/* Remove GREATEST_ prefix from most commonly used symbols? */ +#ifndef GREATEST_USE_ABBREVS +#define GREATEST_USE_ABBREVS 1 +#endif + + +/********* + * Types * + *********/ + +/* Info for the current running suite. */ +typedef struct greatest_suite_info { + unsigned int tests_run; + unsigned int passed; + unsigned int failed; + unsigned int skipped; + + /* timers, pre/post running suite and individual tests */ + clock_t pre_suite; + clock_t post_suite; + clock_t pre_test; + clock_t post_test; +} greatest_suite_info; + +/* Type for a suite function. */ +typedef void (greatest_suite_cb)(void); + +/* Types for setup/teardown callbacks. If non-NULL, these will be run + * and passed the pointer to their additional data. */ +typedef void (greatest_setup_cb)(void *udata); +typedef void (greatest_teardown_cb)(void *udata); + +typedef enum { + GREATEST_FLAG_VERBOSE = 0x01, + GREATEST_FLAG_FIRST_FAIL = 0x02, + GREATEST_FLAG_LIST_ONLY = 0x04 +} GREATEST_FLAG; + +typedef struct greatest_run_info { + unsigned int flags; + unsigned int tests_run; /* total test count */ + + /* Overall pass/fail/skip counts. */ + unsigned int passed; + unsigned int failed; + unsigned int skipped; + + /* currently running test suite */ + greatest_suite_info suite; + + /* info to print about the most recent failure */ + const char *fail_file; + unsigned int fail_line; + const char *msg; + + /* current setup/teardown hooks and userdata */ + greatest_setup_cb *setup; + void *setup_udata; + greatest_teardown_cb *teardown; + void *teardown_udata; + + /* formatting info for ".....s...F"-style output */ + unsigned int col; + unsigned int width; + + /* only run a specific suite or test */ + char *suite_filter; + char *test_filter; + + /* overall timers */ + clock_t begin; + clock_t end; +} greatest_run_info; + +/* Global var for the current testing context. + * Initialized by GREATEST_MAIN_DEFS(). */ +extern greatest_run_info greatest_info; + + +/********************** + * Exported functions * + **********************/ + +void greatest_do_pass(const char *name); +void greatest_do_fail(const char *name); +void greatest_do_skip(const char *name); +int greatest_pre_test(const char *name); +void greatest_post_test(const char *name, int res); +void greatest_usage(const char *name); +void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); +void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); + + +/********** + * Macros * + **********/ + +/* Define a suite. */ +#define GREATEST_SUITE(NAME) void NAME(void) + +/* Start defining a test function. + * The arguments are not included, to allow parametric testing. */ +#define GREATEST_TEST static int + +/* Run a suite. */ +#define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) + +/* Run a test in the current suite. */ +#define GREATEST_RUN_TEST(TEST) \ + do { \ + if (greatest_pre_test(#TEST) == 1) { \ + int res = TEST(); \ + greatest_post_test(#TEST, res); \ + } else if (GREATEST_LIST_ONLY()) { \ + fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ + } \ + } while (0) + +/* Run a test in the current suite with one void* argument, + * which can be a pointer to a struct with multiple arguments. */ +#define GREATEST_RUN_TEST1(TEST, ENV) \ + do { \ + if (greatest_pre_test(#TEST) == 1) { \ + int res = TEST(ENV); \ + greatest_post_test(#TEST, res); \ + } else if (GREATEST_LIST_ONLY()) { \ + fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ + } \ + } while (0) + +/* If __VA_ARGS__ (C99) is supported, allow parametric testing + * without needing to manually manage the argument struct. */ +#if __STDC_VERSION__ >= 19901L +#define GREATEST_RUN_TESTp(TEST, ...) \ + do { \ + if (greatest_pre_test(#TEST) == 1) { \ + int res = TEST(__VA_ARGS__); \ + greatest_post_test(#TEST, res); \ + } else if (GREATEST_LIST_ONLY()) { \ + fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ + } \ + } while (0) +#endif + + +/* Check if the test runner is in verbose mode. */ +#define GREATEST_IS_VERBOSE() (greatest_info.flags & GREATEST_FLAG_VERBOSE) +#define GREATEST_LIST_ONLY() (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) +#define GREATEST_FIRST_FAIL() (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) +#define GREATEST_FAILURE_ABORT() (greatest_info.suite.failed > 0 && GREATEST_FIRST_FAIL()) + +/* Message-less forms. */ +#define GREATEST_PASS() GREATEST_PASSm(NULL) +#define GREATEST_FAIL() GREATEST_FAILm(NULL) +#define GREATEST_SKIP() GREATEST_SKIPm(NULL) +#define GREATEST_ASSERT(COND) GREATEST_ASSERTm(#COND, COND) +#define GREATEST_ASSERT_FALSE(COND) GREATEST_ASSERT_FALSEm(#COND, COND) +#define GREATEST_ASSERT_EQ(EXP, GOT) GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) +#define GREATEST_ASSERT_STR_EQ(EXP, GOT) GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) + +/* The following forms take an additional message argument first, + * to be displayed by the test runner. */ + +/* Fail if a condition is not true, with message. */ +#define GREATEST_ASSERTm(MSG, COND) \ + do { \ + greatest_info.msg = MSG; \ + greatest_info.fail_file = __FILE__; \ + greatest_info.fail_line = __LINE__; \ + if (!(COND)) return -1; \ + greatest_info.msg = NULL; \ + } while (0) + +#define GREATEST_ASSERT_FALSEm(MSG, COND) \ + do { \ + greatest_info.msg = MSG; \ + greatest_info.fail_file = __FILE__; \ + greatest_info.fail_line = __LINE__; \ + if ((COND)) return -1; \ + greatest_info.msg = NULL; \ + } while (0) + +#define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \ + do { \ + greatest_info.msg = MSG; \ + greatest_info.fail_file = __FILE__; \ + greatest_info.fail_line = __LINE__; \ + if ((EXP) != (GOT)) return -1; \ + greatest_info.msg = NULL; \ + } while (0) + +#define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ + do { \ + const char *exp_s = (EXP); \ + const char *got_s = (GOT); \ + greatest_info.msg = MSG; \ + greatest_info.fail_file = __FILE__; \ + greatest_info.fail_line = __LINE__; \ + if (0 != strcmp(exp_s, got_s)) { \ + fprintf(GREATEST_STDOUT, \ + "Expected:\n####\n%s\n####\n", exp_s); \ + fprintf(GREATEST_STDOUT, \ + "Got:\n####\n%s\n####\n", got_s); \ + return -1; \ + } \ + greatest_info.msg = NULL; \ + } while (0) + +#define GREATEST_PASSm(MSG) \ + do { \ + greatest_info.msg = MSG; \ + return 0; \ + } while (0) + +#define GREATEST_FAILm(MSG) \ + do { \ + greatest_info.fail_file = __FILE__; \ + greatest_info.fail_line = __LINE__; \ + greatest_info.msg = MSG; \ + return -1; \ + } while (0) + +#define GREATEST_SKIPm(MSG) \ + do { \ + greatest_info.msg = MSG; \ + return 1; \ + } while (0) + +#define GREATEST_SET_TIME(NAME) \ + NAME = clock(); \ + if (NAME == (clock_t) -1) { \ + fprintf(GREATEST_STDOUT, \ + "clock error: %s\n", #NAME); \ + exit(EXIT_FAILURE); \ + } + +#define GREATEST_CLOCK_DIFF(C1, C2) \ + fprintf(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ + (long unsigned int) (C2) - (C1), \ + (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) \ + +/* Include several function definitions in the main test file. */ +#define GREATEST_MAIN_DEFS() \ + \ +/* Is FILTER a subset of NAME? */ \ +static int greatest_name_match(const char *name, \ + const char *filter) { \ + size_t offset = 0; \ + size_t filter_len = strlen(filter); \ + while (name[offset] != '\0') { \ + if (name[offset] == filter[0]) { \ + if (0 == strncmp(&name[offset], filter, filter_len)) { \ + return 1; \ + } \ + } \ + offset++; \ + } \ + \ + return 0; \ +} \ + \ +int greatest_pre_test(const char *name) { \ + if (!GREATEST_LIST_ONLY() \ + && (!GREATEST_FIRST_FAIL() || greatest_info.suite.failed == 0) \ + && (greatest_info.test_filter == NULL || \ + greatest_name_match(name, greatest_info.test_filter))) { \ + GREATEST_SET_TIME(greatest_info.suite.pre_test); \ + if (greatest_info.setup) { \ + greatest_info.setup(greatest_info.setup_udata); \ + } \ + return 1; /* test should be run */ \ + } else { \ + return 0; /* skipped */ \ + } \ +} \ + \ +void greatest_post_test(const char *name, int res) { \ + GREATEST_SET_TIME(greatest_info.suite.post_test); \ + if (greatest_info.teardown) { \ + void *udata = greatest_info.teardown_udata; \ + greatest_info.teardown(udata); \ + } \ + \ + if (res < 0) { \ + greatest_do_fail(name); \ + } else if (res > 0) { \ + greatest_do_skip(name); \ + } else if (res == 0) { \ + greatest_do_pass(name); \ + } \ + greatest_info.suite.tests_run++; \ + greatest_info.col++; \ + if (GREATEST_IS_VERBOSE()) { \ + GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ + greatest_info.suite.post_test); \ + fprintf(GREATEST_STDOUT, "\n"); \ + } else if (greatest_info.col % greatest_info.width == 0) { \ + fprintf(GREATEST_STDOUT, "\n"); \ + greatest_info.col = 0; \ + } \ + if (GREATEST_STDOUT == stdout) fflush(stdout); \ +} \ + \ +static void greatest_run_suite(greatest_suite_cb *suite_cb, \ + const char *suite_name) { \ + if (greatest_info.suite_filter && \ + !greatest_name_match(suite_name, greatest_info.suite_filter)) \ + return; \ + if (GREATEST_FIRST_FAIL() && greatest_info.failed > 0) return; \ + greatest_info.suite.tests_run = 0; \ + greatest_info.suite.failed = 0; \ + greatest_info.suite.passed = 0; \ + greatest_info.suite.skipped = 0; \ + greatest_info.suite.pre_suite = 0; \ + greatest_info.suite.post_suite = 0; \ + greatest_info.suite.pre_test = 0; \ + greatest_info.suite.post_test = 0; \ + greatest_info.col = 0; \ + fprintf(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ + GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ + suite_cb(); \ + GREATEST_SET_TIME(greatest_info.suite.post_suite); \ + if (greatest_info.suite.tests_run > 0) { \ + fprintf(GREATEST_STDOUT, \ + "\n%u tests - %u pass, %u fail, %u skipped", \ + greatest_info.suite.tests_run, \ + greatest_info.suite.passed, \ + greatest_info.suite.failed, \ + greatest_info.suite.skipped); \ + GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ + greatest_info.suite.post_suite); \ + fprintf(GREATEST_STDOUT, "\n"); \ + } \ + greatest_info.setup = NULL; \ + greatest_info.setup_udata = NULL; \ + greatest_info.teardown = NULL; \ + greatest_info.teardown_udata = NULL; \ + greatest_info.passed += greatest_info.suite.passed; \ + greatest_info.failed += greatest_info.suite.failed; \ + greatest_info.skipped += greatest_info.suite.skipped; \ + greatest_info.tests_run += greatest_info.suite.tests_run; \ +} \ + \ +void greatest_do_pass(const char *name) { \ + if (GREATEST_IS_VERBOSE()) { \ + fprintf(GREATEST_STDOUT, "PASS %s: %s", \ + name, greatest_info.msg ? greatest_info.msg : ""); \ + } else { \ + fprintf(GREATEST_STDOUT, "."); \ + } \ + greatest_info.suite.passed++; \ +} \ + \ +void greatest_do_fail(const char *name) { \ + if (GREATEST_IS_VERBOSE()) { \ + fprintf(GREATEST_STDOUT, \ + "FAIL %s: %s (%s:%u)", \ + name, greatest_info.msg ? greatest_info.msg : "", \ + greatest_info.fail_file, greatest_info.fail_line); \ + } else { \ + fprintf(GREATEST_STDOUT, "F"); \ + /* add linebreak if in line of '.'s */ \ + if (greatest_info.col % greatest_info.width != 0) \ + fprintf(GREATEST_STDOUT, "\n"); \ + greatest_info.col = 0; \ + fprintf(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ + name, \ + greatest_info.msg ? greatest_info.msg : "", \ + greatest_info.fail_file, greatest_info.fail_line); \ + } \ + greatest_info.suite.failed++; \ +} \ + \ +void greatest_do_skip(const char *name) { \ + if (GREATEST_IS_VERBOSE()) { \ + fprintf(GREATEST_STDOUT, "SKIP %s: %s", \ + name, \ + greatest_info.msg ? \ + greatest_info.msg : "" ); \ + } else { \ + fprintf(GREATEST_STDOUT, "s"); \ + } \ + greatest_info.suite.skipped++; \ +} \ + \ +void greatest_usage(const char *name) { \ + fprintf(GREATEST_STDOUT, \ + "Usage: %s [-hlfv] [-s SUITE] [-t TEST]\n" \ + " -h print this Help\n" \ + " -l List suites and their tests, then exit\n" \ + " -f Stop runner after first failure\n" \ + " -v Verbose output\n" \ + " -s SUITE only run suite named SUITE\n" \ + " -t TEST only run test named TEST\n", \ + name); \ +} \ + \ +void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ + greatest_info.setup = cb; \ + greatest_info.setup_udata = udata; \ +} \ + \ +void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \ + void *udata) { \ + greatest_info.teardown = cb; \ + greatest_info.teardown_udata = udata; \ +} \ + \ +greatest_run_info greatest_info + +/* Handle command-line arguments, etc. */ +#define GREATEST_MAIN_BEGIN() \ + do { \ + int i = 0; \ + memset(&greatest_info, 0, sizeof(greatest_info)); \ + if (greatest_info.width == 0) { \ + greatest_info.width = GREATEST_DEFAULT_WIDTH; \ + } \ + for (i = 1; i < argc; i++) { \ + if (0 == strcmp("-t", argv[i])) { \ + if (argc <= i + 1) { \ + greatest_usage(argv[0]); \ + exit(EXIT_FAILURE); \ + } \ + greatest_info.test_filter = argv[i+1]; \ + i++; \ + } else if (0 == strcmp("-s", argv[i])) { \ + if (argc <= i + 1) { \ + greatest_usage(argv[0]); \ + exit(EXIT_FAILURE); \ + } \ + greatest_info.suite_filter = argv[i+1]; \ + i++; \ + } else if (0 == strcmp("-f", argv[i])) { \ + greatest_info.flags |= GREATEST_FLAG_FIRST_FAIL; \ + } else if (0 == strcmp("-v", argv[i])) { \ + greatest_info.flags |= GREATEST_FLAG_VERBOSE; \ + } else if (0 == strcmp("-l", argv[i])) { \ + greatest_info.flags |= GREATEST_FLAG_LIST_ONLY; \ + } else if (0 == strcmp("-h", argv[i])) { \ + greatest_usage(argv[0]); \ + exit(EXIT_SUCCESS); \ + } else { \ + fprintf(GREATEST_STDOUT, \ + "Unknown argument '%s'\n", argv[i]); \ + greatest_usage(argv[0]); \ + exit(EXIT_FAILURE); \ + } \ + } \ + } while (0); \ + GREATEST_SET_TIME(greatest_info.begin) + +#define GREATEST_MAIN_END() \ + do { \ + if (!GREATEST_LIST_ONLY()) { \ + GREATEST_SET_TIME(greatest_info.end); \ + fprintf(GREATEST_STDOUT, \ + "\nTotal: %u tests", greatest_info.tests_run); \ + GREATEST_CLOCK_DIFF(greatest_info.begin, \ + greatest_info.end); \ + fprintf(GREATEST_STDOUT, "\n"); \ + fprintf(GREATEST_STDOUT, \ + "Pass: %u, fail: %u, skip: %u.\n", \ + greatest_info.passed, \ + greatest_info.failed, greatest_info.skipped); \ + } \ + return (greatest_info.failed > 0 \ + ? EXIT_FAILURE : EXIT_SUCCESS); \ + } while (0) + +/* Make abbreviations without the GREATEST_ prefix for the + * most commonly used symbols. */ +#if GREATEST_USE_ABBREVS +#define TEST GREATEST_TEST +#define SUITE GREATEST_SUITE +#define RUN_TEST GREATEST_RUN_TEST +#define RUN_TEST1 GREATEST_RUN_TEST1 +#define RUN_SUITE GREATEST_RUN_SUITE +#define ASSERT GREATEST_ASSERT +#define ASSERTm GREATEST_ASSERTm +#define ASSERT_FALSE GREATEST_ASSERT_FALSE +#define ASSERT_EQ GREATEST_ASSERT_EQ +#define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ +#define ASSERT_FALSEm GREATEST_ASSERT_FALSEm +#define ASSERT_EQm GREATEST_ASSERT_EQm +#define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm +#define PASS GREATEST_PASS +#define FAIL GREATEST_FAIL +#define SKIP GREATEST_SKIP +#define PASSm GREATEST_PASSm +#define FAILm GREATEST_FAILm +#define SKIPm GREATEST_SKIPm +#define SET_SETUP GREATEST_SET_SETUP_CB +#define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB + +#if __STDC_VERSION__ >= 19901L +#endif /* C99 */ +#define RUN_TESTp GREATEST_RUN_TESTp +#endif /* USE_ABBREVS */ + +#endif diff --git a/espfs/heatshrink/heatshrink.c b/espfs/heatshrink/heatshrink.c new file mode 100644 index 0000000..9de553f --- /dev/null +++ b/espfs/heatshrink/heatshrink.c @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "heatshrink_encoder.h" +#include "heatshrink_decoder.h" + +#define DEF_WINDOW_SZ2 11 +#define DEF_LOOKAHEAD_SZ2 4 +#define DEF_DECODER_INPUT_BUFFER_SIZE 256 +#define DEF_BUFFER_SIZE (64 * 1024) + +#if 0 +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG(...) /* NO-OP */ +#endif + +static const int version_major = HEATSHRINK_VERSION_MAJOR; +static const int version_minor = HEATSHRINK_VERSION_MINOR; +static const int version_patch = HEATSHRINK_VERSION_PATCH; +static const char author[] = HEATSHRINK_AUTHOR; +static const char url[] = HEATSHRINK_URL; + +static void usage(void) { + fprintf(stderr, "heatshrink version %u.%u.%u by %s\n", + version_major, version_minor, version_patch, author); + fprintf(stderr, "Home page: %s\n\n", url); + fprintf(stderr, + "Usage:\n" + " heatshrink [-h] [-e|-d] [-v] [-w SIZE] [-l BITS] [IN_FILE] [OUT_FILE]\n" + "\n" + "heatshrink compresses or uncompresses byte streams using LZSS, and is\n" + "designed especially for embedded, low-memory, and/or hard real-time\n" + "systems.\n" + "\n" + " -h print help\n" + " -e encode (compress, default)\n" + " -d decode (uncompress)\n" + " -v verbose (print input & output sizes, compression ratio, etc.)\n" + "\n" + " -w SIZE Base-2 log of LZSS sliding window size\n" + "\n" + " A larger value allows searches a larger history of the data for repeated\n" + " patterns, potentially compressing more effectively, but will use\n" + " more memory and processing time.\n" + " Recommended default: -w 8 (embedded systems), -w 10 (elsewhere)\n" + " \n" + " -l BITS Number of bits used for back-reference lengths\n" + "\n" + " A larger value allows longer substitutions, but since all\n" + " back-references must use -w + -l bits, larger -w or -l can be\n" + " counterproductive if most patterns are small and/or local.\n" + " Recommended default: -l 4\n" + "\n" + " If IN_FILE or OUT_FILE are unspecified, they will default to\n" + " \"-\" for standard input and standard output, respectively.\n"); + exit(1); +} + +typedef enum { IO_READ, IO_WRITE, } IO_mode; +typedef enum { OP_ENC, OP_DEC, } Operation; + +typedef struct { + int fd; /* file descriptor */ + IO_mode mode; + size_t fill; /* fill index */ + size_t read; /* read index */ + size_t size; + size_t total; + uint8_t buf[]; +} io_handle; + +typedef struct { + uint8_t window_sz2; + uint8_t lookahead_sz2; + size_t decoder_input_buffer_size; + size_t buffer_size; + uint8_t verbose; + Operation cmd; + char *in_fname; + char *out_fname; + io_handle *in; + io_handle *out; +} config; + +static void die(char *msg) { + fprintf(stderr, "%s\n", msg); + exit(EXIT_FAILURE); +} + +static void report(config *cfg); + +/* Open an IO handle. Returns NULL on error. */ +static io_handle *handle_open(char *fname, IO_mode m, size_t buf_sz) { + io_handle *io = NULL; + io = malloc(sizeof(*io) + buf_sz); + if (io == NULL) { return NULL; } + memset(io, 0, sizeof(*io) + buf_sz); + io->fd = -1; + io->size = buf_sz; + io->mode = m; + + if (m == IO_READ) { + if (0 == strcmp("-", fname)) { + io->fd = STDIN_FILENO; + } else { + io->fd = open(fname, O_RDONLY); + } + } else if (m == IO_WRITE) { + if (0 == strcmp("-", fname)) { + io->fd = STDOUT_FILENO; + } else { + io->fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC /*| O_EXCL*/, 0644); + } + } + + if (io->fd == -1) { /* failed to open */ + free(io); + err(1, "open"); + return NULL; + } + + return io; +} + +/* Read SIZE bytes from an IO handle and return a pointer to the content. + * BUF contains at least size_t bytes. Returns 0 on EOF, -1 on error. */ +static ssize_t handle_read(io_handle *io, size_t size, uint8_t **buf) { + LOG("@ read %zd\n", size); + if (buf == NULL) { return -1; } + if (size > io->size) { + printf("size %zd, io->size %zd\n", size, io->size); + return -1; + } + if (io->mode != IO_READ) { return -1; } + + size_t rem = io->fill - io->read; + if (rem >= size) { + *buf = &io->buf[io->read]; + return size; + } else { /* read and replenish */ + if (io->fd == -1) { /* already closed, return what we've got */ + *buf = &io->buf[io->read]; + return rem; + } + + memmove(io->buf, &io->buf[io->read], rem); + io->fill -= io->read; + io->read = 0; + ssize_t read_sz = read(io->fd, &io->buf[io->fill], io->size - io->fill); + if (read_sz < 0) { err(1, "read"); } + io->total += read_sz; + if (read_sz == 0) { /* EOF */ + if (close(io->fd) < 0) { err(1, "close"); } + io->fd = -1; + } + io->fill += read_sz; + *buf = io->buf; + return io->fill > size ? size : io->fill; + } +} + +/* Drop the oldest SIZE bytes from the buffer. Returns <0 on error. */ +static int handle_drop(io_handle *io, size_t size) { + LOG("@ drop %zd\n", size); + if (io->read + size <= io->fill) { + io->read += size; + } else { + return -1; + } + if (io->read == io->fill) { + io->read = 0; + io->fill = 0; + } + return 0; +} + +/* Sink SIZE bytes from INPUT into the io handle. Returns the number of + * bytes written, or -1 on error. */ +static ssize_t handle_sink(io_handle *io, size_t size, uint8_t *input) { + LOG("@ sink %zd\n", size); + if (size > io->size) { return -1; } + if (io->mode != IO_WRITE) { return -1; } + + if (io->fill + size > io->size) { + ssize_t written = write(io->fd, io->buf, io->fill); + LOG("@ flushing %zd, wrote %zd\n", io->fill, written); + io->total += written; + if (written == -1) { err(1, "write"); } + memmove(io->buf, &io->buf[written], io->fill - written); + io->fill -= written; + } + memcpy(&io->buf[io->fill], input, size); + io->fill += size; + return size; +} + +static void handle_close(io_handle *io) { + if (io->fd != -1) { + if (io->mode == IO_WRITE) { + ssize_t written = write(io->fd, io->buf, io->fill); + io->total += written; + LOG("@ close: flushing %zd, wrote %zd\n", io->fill, written); + if (written == -1) { err(1, "write"); } + } + close(io->fd); + io->fd = -1; + } +} + +static void close_and_report(config *cfg) { + handle_close(cfg->in); + handle_close(cfg->out); + if (cfg->verbose) { report(cfg); } + free(cfg->in); + free(cfg->out); +} + +static int encoder_sink_read(config *cfg, heatshrink_encoder *hse, + uint8_t *data, size_t data_sz) { + size_t out_sz = 4096; + uint8_t out_buf[out_sz]; + memset(out_buf, 0, out_sz); + size_t sink_sz = 0; + size_t poll_sz = 0; + HSE_sink_res sres; + HSE_poll_res pres; + HSE_finish_res fres; + io_handle *out = cfg->out; + + size_t sunk = 0; + do { + if (data_sz > 0) { + sres = heatshrink_encoder_sink(hse, &data[sunk], data_sz - sunk, &sink_sz); + if (sres < 0) { die("sink"); } + sunk += sink_sz; + } + + do { + pres = heatshrink_encoder_poll(hse, out_buf, out_sz, &poll_sz); + if (pres < 0) { die("poll"); } + if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink"); + } while (pres == HSER_POLL_MORE); + + if (poll_sz == 0 && data_sz == 0) { + fres = heatshrink_encoder_finish(hse); + if (fres < 0) { die("finish"); } + if (fres == HSER_FINISH_DONE) { return 1; } + } + } while (sunk < data_sz); + return 0; +} + +static int encode(config *cfg) { + uint8_t window_sz2 = cfg->window_sz2; + size_t window_sz = 1 << window_sz2; + heatshrink_encoder *hse = heatshrink_encoder_alloc(window_sz2, cfg->lookahead_sz2); + if (hse == NULL) { die("failed to init encoder: bad settings"); } + ssize_t read_sz = 0; + io_handle *in = cfg->in; + + /* Process input until end of stream */ + while (1) { + uint8_t *input = NULL; + read_sz = handle_read(in, window_sz, &input); + if (input == NULL) { + printf("handle read failure\n"); + die("read"); + } + if (read_sz < 0) { die("read"); } + + /* Pass read to encoder and check if input is fully processed. */ + if (encoder_sink_read(cfg, hse, input, read_sz)) break; + + if (handle_drop(in, read_sz) < 0) { die("drop"); } + }; + + if (read_sz == -1) { err(1, "read"); } + + heatshrink_encoder_free(hse); + close_and_report(cfg); + return 0; +} + +static int decoder_sink_read(config *cfg, heatshrink_decoder *hsd, + uint8_t *data, size_t data_sz) { + io_handle *out = cfg->out; + size_t sink_sz = 0; + size_t poll_sz = 0; + size_t out_sz = 4096; + uint8_t out_buf[out_sz]; + memset(out_buf, 0, out_sz); + + HSD_sink_res sres; + HSD_poll_res pres; + HSD_finish_res fres; + + size_t sunk = 0; + do { + if (data_sz > 0) { + sres = heatshrink_decoder_sink(hsd, &data[sunk], data_sz - sunk, &sink_sz); + if (sres < 0) { die("sink"); } + sunk += sink_sz; + } + + do { + pres = heatshrink_decoder_poll(hsd, out_buf, out_sz, &poll_sz); + if (pres < 0) { die("poll"); } + if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink"); + } while (pres == HSDR_POLL_MORE); + + if (data_sz == 0 && poll_sz == 0) { + fres = heatshrink_decoder_finish(hsd); + if (fres < 0) { die("finish"); } + if (fres == HSDR_FINISH_DONE) { return 1; } + } + } while (sunk < data_sz); + + return 0; +} + +static int decode(config *cfg) { + uint8_t window_sz2 = cfg->window_sz2; + size_t window_sz = 1 << window_sz2; + size_t ibs = cfg->decoder_input_buffer_size; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(ibs, + window_sz2, cfg->lookahead_sz2); + if (hsd == NULL) { die("failed to init decoder"); } + + ssize_t read_sz = 0; + + io_handle *in = cfg->in; + + HSD_finish_res fres; + + /* Process input until end of stream */ + while (1) { + uint8_t *input = NULL; + read_sz = handle_read(in, window_sz, &input); + if (input == NULL) { + printf("handle read failure\n"); + die("read"); + } + if (read_sz == 0) { + fres = heatshrink_decoder_finish(hsd); + if (fres < 0) { die("finish"); } + if (fres == HSDR_FINISH_DONE) break; + } else if (read_sz < 0) { + die("read"); + } else { + if (decoder_sink_read(cfg, hsd, input, read_sz)) { break; } + if (handle_drop(in, read_sz) < 0) { die("drop"); } + } + } + if (read_sz == -1) { err(1, "read"); } + + heatshrink_decoder_free(hsd); + close_and_report(cfg); + return 0; +} + +static void report(config *cfg) { + size_t inb = cfg->in->total; + size_t outb = cfg->out->total; + fprintf(cfg->out->fd == STDOUT_FILENO ? stderr : stdout, + "%s %0.2f %%\t %zd -> %zd (-w %u -l %u)\n", + cfg->in_fname, 100.0 - (100.0 * outb) / inb, inb, outb, + cfg->window_sz2, cfg->lookahead_sz2); +} + +static void proc_args(config *cfg, int argc, char **argv) { + cfg->window_sz2 = DEF_WINDOW_SZ2; + cfg->lookahead_sz2 = DEF_LOOKAHEAD_SZ2; + cfg->buffer_size = DEF_BUFFER_SIZE; + cfg->decoder_input_buffer_size = DEF_DECODER_INPUT_BUFFER_SIZE; + cfg->cmd = OP_ENC; + cfg->verbose = 0; + cfg->in_fname = "-"; + cfg->out_fname = "-"; + + int a = 0; + while ((a = getopt(argc, argv, "hedi:w:l:v")) != -1) { + switch (a) { + case 'h': /* help */ + usage(); + case 'e': /* encode */ + cfg->cmd = OP_ENC; break; + case 'd': /* decode */ + cfg->cmd = OP_DEC; break; + case 'i': /* input buffer size */ + cfg->decoder_input_buffer_size = atoi(optarg); + break; + case 'w': /* window bits */ + cfg->window_sz2 = atoi(optarg); + break; + case 'l': /* lookahead bits */ + cfg->lookahead_sz2 = atoi(optarg); + break; + case 'v': /* verbosity++ */ + cfg->verbose++; + break; + case '?': /* unknown argument */ + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc > 0) { + cfg->in_fname = argv[0]; + argc--; + argv++; + } + if (argc > 0) { cfg->out_fname = argv[0]; } +} + +int main(int argc, char **argv) { + config cfg; + memset(&cfg, 0, sizeof(cfg)); + proc_args(&cfg, argc, argv); + + if (0 == strcmp(cfg.in_fname, cfg.out_fname) + && (0 != strcmp("-", cfg.in_fname))) { + printf("Refusing to overwrite file '%s' with itself.\n", cfg.in_fname); + exit(1); + } + + cfg.in = handle_open(cfg.in_fname, IO_READ, cfg.buffer_size); + if (cfg.in == NULL) { die("Failed to open input file for read"); } + cfg.out = handle_open(cfg.out_fname, IO_WRITE, cfg.buffer_size); + if (cfg.out == NULL) { die("Failed to open output file for write"); } + + if (cfg.cmd == OP_ENC) { + return encode(&cfg); + } else if (cfg.cmd == OP_DEC) { + return decode(&cfg); + } else { + usage(); + } +} diff --git a/espfs/heatshrink/heatshrink_common.h b/espfs/heatshrink/heatshrink_common.h new file mode 100644 index 0000000..0d396b9 --- /dev/null +++ b/espfs/heatshrink/heatshrink_common.h @@ -0,0 +1,20 @@ +#ifndef HEATSHRINK_H +#define HEATSHRINK_H + +#define HEATSHRINK_AUTHOR "Scott Vokes " +#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" + +/* Version 0.3.1 */ +#define HEATSHRINK_VERSION_MAJOR 0 +#define HEATSHRINK_VERSION_MINOR 3 +#define HEATSHRINK_VERSION_PATCH 1 + +#define HEATSHRINK_MIN_WINDOW_BITS 4 +#define HEATSHRINK_MAX_WINDOW_BITS 15 + +#define HEATSHRINK_MIN_LOOKAHEAD_BITS 2 + +#define HEATSHRINK_LITERAL_MARKER 0x01 +#define HEATSHRINK_BACKREF_MARKER 0x00 + +#endif diff --git a/espfs/heatshrink/heatshrink_config.h b/espfs/heatshrink/heatshrink_config.h new file mode 100644 index 0000000..51d4772 --- /dev/null +++ b/espfs/heatshrink/heatshrink_config.h @@ -0,0 +1,24 @@ +#ifndef HEATSHRINK_CONFIG_H +#define HEATSHRINK_CONFIG_H + +/* Should functionality assuming dynamic allocation be used? */ +#define HEATSHRINK_DYNAMIC_ALLOC 1 + +#if HEATSHRINK_DYNAMIC_ALLOC + /* Optional replacement of malloc/free */ + #define HEATSHRINK_MALLOC(SZ) malloc(SZ) + #define HEATSHRINK_FREE(P, SZ) free(P) +#else + /* Required parameters for static configuration */ + #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 + #define HEATSHRINK_STATIC_WINDOW_BITS 8 + #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 +#endif + +/* Turn on logging for debugging. */ +#define HEATSHRINK_DEBUGGING_LOGS 0 + +/* Use indexing for faster compression. (This requires additional space.) */ +#define HEATSHRINK_USE_INDEX 1 + +#endif diff --git a/espfs/heatshrink/heatshrink_decoder.c b/espfs/heatshrink/heatshrink_decoder.c new file mode 100644 index 0000000..b92be13 --- /dev/null +++ b/espfs/heatshrink/heatshrink_decoder.c @@ -0,0 +1,382 @@ +#include +#include +#include "heatshrink_decoder.h" + +/* States for the polling state machine. */ +typedef enum { + HSDS_EMPTY, /* no input to process */ + HSDS_INPUT_AVAILABLE, /* new input, completely unprocessed */ + HSDS_YIELD_LITERAL, /* ready to yield literal byte */ + HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ + HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ + HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ + HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ + HSDS_YIELD_BACKREF, /* ready to yield back-reference */ + HSDS_CHECK_FOR_MORE_INPUT, /* check if input is exhausted */ +} HSD_state; + +#if HEATSHRINK_DEBUGGING_LOGS +#include +#include +#include +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#define ASSERT(X) assert(X) +static const char *state_names[] = { + "empty", + "input_available", + "yield_literal", + "backref_index", + "backref_count", + "yield_backref", + "check_for_more_input", +}; +#else +#define LOG(...) /* no-op */ +#define ASSERT(X) /* no-op */ +#endif + +typedef struct { + uint8_t *buf; /* output buffer */ + size_t buf_size; /* buffer size */ + size_t *output_size; /* bytes pushed to buffer, so far */ +} output_info; + +#define NO_BITS ((uint32_t)-1) + +/* Forward references. */ +static uint32_t get_bits(heatshrink_decoder *hsd, uint8_t count); +static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); + +#if HEATSHRINK_DYNAMIC_ALLOC +heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, + uint8_t window_sz2, + uint8_t lookahead_sz2) { + if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || + (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || + (input_buffer_size == 0) || + (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || + (lookahead_sz2 > window_sz2)) { + return NULL; + } + size_t buffers_sz = (1 << window_sz2) + input_buffer_size; + size_t sz = sizeof(heatshrink_decoder) + buffers_sz; + heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); + if (hsd == NULL) { return NULL; } + hsd->input_buffer_size = input_buffer_size; + hsd->window_sz2 = window_sz2; + hsd->lookahead_sz2 = lookahead_sz2; + heatshrink_decoder_reset(hsd); + LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", + sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); + return hsd; +} + +void heatshrink_decoder_free(heatshrink_decoder *hsd) { + size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; + size_t sz = sizeof(heatshrink_decoder) + buffers_sz; + HEATSHRINK_FREE(hsd, sz); + (void)sz; /* may not be used by free */ +} +#endif + +void heatshrink_decoder_reset(heatshrink_decoder *hsd) { + size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); + size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); + memset(hsd->buffers, 0, buf_sz + input_sz); + hsd->state = HSDS_EMPTY; + hsd->input_size = 0; + hsd->input_index = 0; + hsd->bit_index = 0x00; + hsd->current_byte = 0x00; + hsd->output_count = 0; + hsd->output_index = 0; + hsd->head_index = 0; + hsd->bit_accumulator = 0x00000000; +} + +/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ +HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, + uint8_t *in_buf, size_t size, size_t *input_size) { + if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { + return HSDR_SINK_ERROR_NULL; + } + + size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; + if (rem == 0) { + *input_size = 0; + return HSDR_SINK_FULL; + } + + size = rem < size ? rem : size; + LOG("-- sinking %zd bytes\n", size); + /* copy into input buffer (at head of buffers) */ + memcpy(&hsd->buffers[hsd->input_size], in_buf, size); + hsd->input_size += size; + if (hsd->state == HSDS_EMPTY) { + hsd->state = HSDS_INPUT_AVAILABLE; + hsd->input_index = 0; + } + *input_size = size; + return HSDR_SINK_OK; +} + + +/***************** + * Decompression * + *****************/ + +#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) +#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) + +// States +static HSD_state st_input_available(heatshrink_decoder *hsd); +static HSD_state st_yield_literal(heatshrink_decoder *hsd, + output_info *oi); +static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); +static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); +static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); +static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); +static HSD_state st_yield_backref(heatshrink_decoder *hsd, + output_info *oi); +static HSD_state st_check_for_input(heatshrink_decoder *hsd); + +HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { + if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { + return HSDR_POLL_ERROR_NULL; + } + *output_size = 0; + + output_info oi; + oi.buf = out_buf; + oi.buf_size = out_buf_size; + oi.output_size = output_size; + + while (1) { + LOG("-- poll, state is %d (%s), input_size %d\n", + hsd->state, state_names[hsd->state], hsd->input_size); + uint8_t in_state = hsd->state; + switch (in_state) { + case HSDS_EMPTY: + return HSDR_POLL_EMPTY; + case HSDS_INPUT_AVAILABLE: + hsd->state = st_input_available(hsd); + break; + case HSDS_YIELD_LITERAL: + hsd->state = st_yield_literal(hsd, &oi); + break; + case HSDS_BACKREF_INDEX_MSB: + hsd->state = st_backref_index_msb(hsd); + break; + case HSDS_BACKREF_INDEX_LSB: + hsd->state = st_backref_index_lsb(hsd); + break; + case HSDS_BACKREF_COUNT_MSB: + hsd->state = st_backref_count_msb(hsd); + break; + case HSDS_BACKREF_COUNT_LSB: + hsd->state = st_backref_count_lsb(hsd); + break; + case HSDS_YIELD_BACKREF: + hsd->state = st_yield_backref(hsd, &oi); + break; + case HSDS_CHECK_FOR_MORE_INPUT: + hsd->state = st_check_for_input(hsd); + break; + default: + return HSDR_POLL_ERROR_UNKNOWN; + } + + /* If the current state cannot advance, check if input or output + * buffer are exhausted. */ + if (hsd->state == in_state) { + if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } + return HSDR_POLL_EMPTY; + } + } +} + +static HSD_state st_input_available(heatshrink_decoder *hsd) { + uint32_t bits = get_bits(hsd, 1); // get tag bit + if (bits) { + return HSDS_YIELD_LITERAL; + } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { + return HSDS_BACKREF_INDEX_MSB; + } else { + hsd->output_index = 0; + return HSDS_BACKREF_INDEX_LSB; + } +} + +static HSD_state st_yield_literal(heatshrink_decoder *hsd, + output_info *oi) { + /* Emit a repeated section from the window buffer, and add it (again) + * to the window buffer. (Note that the repetition can include + * itself.)*/ + if (*oi->output_size < oi->buf_size) { + uint32_t byte = get_bits(hsd, 8); + if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ + uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; + uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; + uint8_t c = byte & 0xFF; + LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); + buf[hsd->head_index++ & mask] = c; + push_byte(hsd, oi, c); + return HSDS_CHECK_FOR_MORE_INPUT; + } else { + return HSDS_YIELD_LITERAL; + } +} + +static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { + uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); + ASSERT(bit_ct > 8); + uint32_t bits = get_bits(hsd, bit_ct - 8); + LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } + hsd->output_index = bits << 8; + return HSDS_BACKREF_INDEX_LSB; +} + +static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { + uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); + uint32_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); + LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } + hsd->output_index |= bits; + hsd->output_index++; + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + hsd->output_count = 0; + return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; +} + +static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + ASSERT(br_bit_ct > 8); + uint32_t bits = get_bits(hsd, br_bit_ct - 8); + LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } + hsd->output_count = bits << 8; + return HSDS_BACKREF_COUNT_LSB; +} + +static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + uint32_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); + LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } + hsd->output_count |= bits; + hsd->output_count++; + return HSDS_YIELD_BACKREF; +} + +static HSD_state st_yield_backref(heatshrink_decoder *hsd, + output_info *oi) { + size_t count = oi->buf_size - *oi->output_size; + if (count > 0) { + if (hsd->output_count < count) count = hsd->output_count; + uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; + uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; + uint16_t neg_offset = hsd->output_index; + LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); + ASSERT(neg_offset < mask + 1); + ASSERT(count <= 1 << BACKREF_COUNT_BITS(hsd)); + + for (size_t i=0; ihead_index - neg_offset) & mask]; + push_byte(hsd, oi, c); + buf[hsd->head_index & mask] = c; + hsd->head_index++; + LOG(" -- ++ 0x%02x\n", c); + } + hsd->output_count -= count; + if (hsd->output_count == 0) { return HSDS_CHECK_FOR_MORE_INPUT; } + } + return HSDS_YIELD_BACKREF; +} + +static HSD_state st_check_for_input(heatshrink_decoder *hsd) { + return (hsd->input_size == 0) ? HSDS_EMPTY : HSDS_INPUT_AVAILABLE; +} + +/* Get the next COUNT bits from the input buffer, saving incremental progress. + * Returns NO_BITS on end of input, or if more than 31 bits are requested. */ +static uint32_t get_bits(heatshrink_decoder *hsd, uint8_t count) { + if (count > 31) { return NO_BITS; } + LOG("-- popping %u bit(s)\n", count); + + /* If we aren't able to get COUNT bits, suspend immediately, because we + * don't track how many bits of COUNT we've accumulated before suspend. */ + if (hsd->input_size == 0) { + if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } + } + + for (int i = 0; i < count; i++) { + if (hsd->bit_index == 0x00) { + if (hsd->input_size == 0) { + LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", + hsd->bit_accumulator, hsd->bit_accumulator); + return NO_BITS; + } + hsd->current_byte = hsd->buffers[hsd->input_index++]; + LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); + if (hsd->input_index == hsd->input_size) { + hsd->input_index = 0; /* input is exhausted */ + hsd->input_size = 0; + } + hsd->bit_index = 0x80; + } + hsd->bit_accumulator <<= 1; + if (hsd->current_byte & hsd->bit_index) { + hsd->bit_accumulator |= 0x01; + if (0) { + LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", + hsd->bit_accumulator, hsd->bit_index); + } + } else { + if (0) { + LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", + hsd->bit_accumulator, hsd->bit_index); + } + } + hsd->bit_index >>= 1; + } + + uint32_t res = 0; + res = hsd->bit_accumulator; + hsd->bit_accumulator = 0x00000000; + if (count > 1) { LOG(" -- accumulated %08x\n", res); } + return res; +} + +HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { + if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } + switch (hsd->state) { + case HSDS_EMPTY: + return HSDR_FINISH_DONE; + + /* If we want to finish with no input, but are in these states, it's + * because the 0-bit padding to the last byte looks like a backref + * marker bit followed by all 0s for index and count bits. */ + case HSDS_BACKREF_INDEX_LSB: + case HSDS_BACKREF_INDEX_MSB: + case HSDS_BACKREF_COUNT_LSB: + case HSDS_BACKREF_COUNT_MSB: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + /* If the output stream is padded with 0xFFs (possibly due to being in + * flash memory), also explicitly check the input size rather than + * uselessly returning MORE but yielding 0 bytes when polling. */ + case HSDS_YIELD_LITERAL: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + default: + return HSDR_FINISH_MORE; + } +} + +static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { + LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); + oi->buf[(*oi->output_size)++] = byte; + (void)hsd; +} diff --git a/espfs/heatshrink/heatshrink_decoder.h b/espfs/heatshrink/heatshrink_decoder.h new file mode 100644 index 0000000..c1eb144 --- /dev/null +++ b/espfs/heatshrink/heatshrink_decoder.h @@ -0,0 +1,101 @@ +#ifndef HEATSHRINK_DECODER_H +#define HEATSHRINK_DECODER_H + +#include +#include +#include "heatshrink_common.h" +#include "heatshrink_config.h" + +typedef enum { + HSDR_SINK_OK, /* data sunk, ready to poll */ + HSDR_SINK_FULL, /* out of space in internal buffer */ + HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ +} HSD_sink_res; + +typedef enum { + HSDR_POLL_EMPTY, /* input exhausted */ + HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ + HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ + HSDR_POLL_ERROR_UNKNOWN=-2, +} HSD_poll_res; + +typedef enum { + HSDR_FINISH_DONE, /* output is done */ + HSDR_FINISH_MORE, /* more output remains */ + HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ +} HSD_finish_res; + +#if HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ + ((BUF)->input_buffer_size) +#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ + ((BUF)->window_sz2) +#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ + ((BUF)->lookahead_sz2) +#else +#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ + HEATSHRINK_STATIC_INPUT_BUFFER_SIZE +#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ + (HEATSHRINK_STATIC_WINDOW_BITS) +#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ + (HEATSHRINK_STATIC_LOOKAHEAD_BITS) +#endif + +typedef struct { + uint16_t input_size; /* bytes in input buffer */ + uint16_t input_index; /* offset to next unprocessed input byte */ + uint16_t output_count; /* how many bytes to output */ + uint16_t output_index; /* index for bytes to output */ + uint16_t head_index; /* head of window buffer */ + uint16_t bit_accumulator; + uint8_t state; /* current state machine node */ + uint8_t current_byte; /* current byte of input */ + uint8_t bit_index; /* current bit index */ + +#if HEATSHRINK_DYNAMIC_ALLOC + /* Fields that are only used if dynamically allocated. */ + uint8_t window_sz2; /* window buffer bits */ + uint8_t lookahead_sz2; /* lookahead bits */ + uint16_t input_buffer_size; /* input buffer size */ + + /* Input buffer, then expansion window buffer */ + uint8_t buffers[]; +#else + /* Input buffer, then expansion window buffer */ + uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) + + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; +#endif +} heatshrink_decoder; + +#if HEATSHRINK_DYNAMIC_ALLOC +/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, + * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead + * size of 2^lookahead_sz2. (The window buffer and lookahead sizes + * must match the settings used when the data was compressed.) + * Returns NULL on error. */ +heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, + uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); + +/* Free a decoder. */ +void heatshrink_decoder_free(heatshrink_decoder *hsd); +#endif + +/* Reset a decoder. */ +void heatshrink_decoder_reset(heatshrink_decoder *hsd); + +/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to + * indicate how many bytes were actually sunk (in case a buffer was filled). */ +HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, + uint8_t *in_buf, size_t size, size_t *input_size); + +/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into + * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ +HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size); + +/* Notify the dencoder that the input stream is finished. + * If the return value is HSDR_FINISH_MORE, there is still more output, so + * call heatshrink_decoder_poll and repeat. */ +HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); + +#endif diff --git a/espfs/heatshrink/heatshrink_encoder.c b/espfs/heatshrink/heatshrink_encoder.c new file mode 100644 index 0000000..ede5f60 --- /dev/null +++ b/espfs/heatshrink/heatshrink_encoder.c @@ -0,0 +1,650 @@ +#include +#include +#include +#include "heatshrink_encoder.h" + +typedef enum { + HSES_NOT_FULL, /* input buffer not full enough */ + HSES_FILLED, /* buffer is full */ + HSES_SEARCH, /* searching for patterns */ + HSES_YIELD_TAG_BIT, /* yield tag bit */ + HSES_YIELD_LITERAL, /* emit literal byte */ + HSES_YIELD_BR_INDEX, /* yielding backref index */ + HSES_YIELD_BR_LENGTH, /* yielding backref length */ + HSES_SAVE_BACKLOG, /* copying buffer to backlog */ + HSES_FLUSH_BITS, /* flush bit buffer */ + HSES_DONE, /* done */ +} HSE_state; + +#if HEATSHRINK_DEBUGGING_LOGS +#include +#include +#include +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#define ASSERT(X) assert(X) +static const char *state_names[] = { + "not_full", + "filled", + "search", + "yield_tag_bit", + "yield_literal", + "yield_br_index", + "yield_br_length", + "save_backlog", + "flush_bits", + "done", +}; +#else +#define LOG(...) /* no-op */ +#define ASSERT(X) /* no-op */ +#endif + +// Encoder flags +enum { + FLAG_IS_FINISHING = 0x01, + FLAG_HAS_LITERAL = 0x02, + FLAG_ON_FINAL_LITERAL = 0x04, + FLAG_BACKLOG_IS_PARTIAL = 0x08, + FLAG_BACKLOG_IS_FILLED = 0x10, +}; + +typedef struct { + uint8_t *buf; /* output buffer */ + size_t buf_size; /* buffer size */ + size_t *output_size; /* bytes pushed to buffer, so far */ +} output_info; + +#define MATCH_NOT_FOUND ((uint16_t)-1) + +static uint16_t get_input_offset(heatshrink_encoder *hse); +static uint16_t get_input_buffer_size(heatshrink_encoder *hse); +static uint16_t get_lookahead_size(heatshrink_encoder *hse); +static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); +static int can_take_byte(output_info *oi); +static int is_finishing(heatshrink_encoder *hse); +static int backlog_is_partial(heatshrink_encoder *hse); +static int backlog_is_filled(heatshrink_encoder *hse); +static int on_final_literal(heatshrink_encoder *hse); +static void save_backlog(heatshrink_encoder *hse); +static int has_literal(heatshrink_encoder *hse); + +/* Push COUNT (max 8) bits to the output buffer, which has room. */ +static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, + output_info *oi); +static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); +static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); + +#if HEATSHRINK_DYNAMIC_ALLOC +heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, + uint8_t lookahead_sz2) { + if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || + (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || + (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || + (lookahead_sz2 > window_sz2)) { + return NULL; + } + + /* Note: 2 * the window size is used because the buffer needs to fit + * (1 << window_sz2) bytes for the current input, and an additional + * (1 << window_sz2) bytes for the previous buffer of input, which + * will be scanned for useful backreferences. */ + size_t buf_sz = (2 << window_sz2); + + heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse) + buf_sz); + if (hse == NULL) { return NULL; } + hse->window_sz2 = window_sz2; + hse->lookahead_sz2 = lookahead_sz2; + heatshrink_encoder_reset(hse); + +#if HEATSHRINK_USE_INDEX + size_t index_sz = buf_sz*sizeof(uint16_t); + hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); + if (hse->search_index == NULL) { + HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); + return NULL; + } + hse->search_index->size = index_sz; +#endif + + LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", + buf_sz, get_input_buffer_size(hse)); + return hse; +} + +void heatshrink_encoder_free(heatshrink_encoder *hse) { + size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); +#if HEATSHRINK_USE_INDEX + size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; + HEATSHRINK_FREE(hse->search_index, index_sz); + (void)index_sz; +#endif + HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder) + buf_sz); + (void)buf_sz; +} +#endif + +void heatshrink_encoder_reset(heatshrink_encoder *hse) { + size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); + memset(hse->buffer, 0, buf_sz); + hse->input_size = 0; + hse->state = HSES_NOT_FULL; + hse->match_scan_index = 0; + hse->flags = 0; + hse->bit_index = 0x80; + hse->current_byte = 0x00; + hse->match_length = 0; + + hse->outgoing_bits = 0x0000; + hse->outgoing_bits_count = 0; + + #ifdef LOOP_DETECT + hse->loop_detect = (uint32_t)-1; + #endif +} + +HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, + uint8_t *in_buf, size_t size, size_t *input_size) { + if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { + return HSER_SINK_ERROR_NULL; + } + + /* Sinking more content after saying the content is done, tsk tsk */ + if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } + + /* Sinking more content before processing is done */ + if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } + + uint16_t write_offset = get_input_offset(hse) + hse->input_size; + uint16_t ibs = get_input_buffer_size(hse); + uint16_t rem = ibs - hse->input_size; + uint16_t cp_sz = rem < size ? rem : size; + + memcpy(&hse->buffer[write_offset], in_buf, cp_sz); + *input_size = cp_sz; + hse->input_size += cp_sz; + + LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", + cp_sz, size, write_offset, hse->input_size); + if (cp_sz == rem) { + LOG("-- internal buffer is now full\n"); + hse->state = HSES_FILLED; + } + + return HSER_SINK_OK; +} + + +/*************** + * Compression * + ***************/ + +static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, + uint16_t end, const uint16_t maxlen, uint16_t *match_length); +static void do_indexing(heatshrink_encoder *hse); + +static HSE_state st_step_search(heatshrink_encoder *hse); +static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_literal(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_br_index(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_br_length(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_save_backlog(heatshrink_encoder *hse); +static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, + output_info *oi); + +HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { + if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { + return HSER_POLL_ERROR_NULL; + } + if (out_buf_size == 0) { + LOG("-- MISUSE: output buffer size is 0\n"); + return HSER_POLL_ERROR_MISUSE; + } + *output_size = 0; + + output_info oi; + oi.buf = out_buf; + oi.buf_size = out_buf_size; + oi.output_size = output_size; + + while (1) { + LOG("-- polling, state %u (%s), flags 0x%02x\n", + hse->state, state_names[hse->state], hse->flags); + + uint8_t in_state = hse->state; + switch (in_state) { + case HSES_NOT_FULL: + return HSER_POLL_EMPTY; + case HSES_FILLED: + do_indexing(hse); + hse->state = HSES_SEARCH; + break; + case HSES_SEARCH: + hse->state = st_step_search(hse); + break; + case HSES_YIELD_TAG_BIT: + hse->state = st_yield_tag_bit(hse, &oi); + break; + case HSES_YIELD_LITERAL: + hse->state = st_yield_literal(hse, &oi); + break; + case HSES_YIELD_BR_INDEX: + hse->state = st_yield_br_index(hse, &oi); + break; + case HSES_YIELD_BR_LENGTH: + hse->state = st_yield_br_length(hse, &oi); + break; + case HSES_SAVE_BACKLOG: + hse->state = st_save_backlog(hse); + break; + case HSES_FLUSH_BITS: + hse->state = st_flush_bit_buffer(hse, &oi); + case HSES_DONE: + return HSER_POLL_EMPTY; + default: + LOG("-- bad state %s\n", state_names[hse->state]); + return HSER_POLL_ERROR_MISUSE; + } + + if (hse->state == in_state) { + /* Check if output buffer is exhausted. */ + if (*output_size == out_buf_size) return HSER_POLL_MORE; + } + } +} + +HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { + if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } + LOG("-- setting is_finishing flag\n"); + hse->flags |= FLAG_IS_FINISHING; + if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } + return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; +} + +static HSE_state st_step_search(heatshrink_encoder *hse) { + uint16_t window_length = get_input_buffer_size(hse); + uint16_t lookahead_sz = get_lookahead_size(hse); + uint16_t msi = hse->match_scan_index; + LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", + msi, hse->input_size + msi, 2*window_length, hse->input_size); + + bool fin = is_finishing(hse); + if (msi >= hse->input_size - (fin ? 0 : lookahead_sz)) { + /* Current search buffer is exhausted, copy it into the + * backlog and await more input. */ + LOG("-- end of search @ %d, saving backlog\n", msi); + return HSES_SAVE_BACKLOG; + } + + uint16_t input_offset = get_input_offset(hse); + uint16_t end = input_offset + msi; + + uint16_t start = 0; + if (backlog_is_filled(hse)) { /* last WINDOW_LENGTH bytes */ + start = end - window_length + 1; + } else if (backlog_is_partial(hse)) { /* clamp to available data */ + start = end - window_length + 1; + if (start < lookahead_sz) { start = lookahead_sz; } + } else { /* only scan available input */ + start = input_offset; + } + + uint16_t max_possible = lookahead_sz; + if (hse->input_size - msi < lookahead_sz) { + max_possible = hse->input_size - msi; + } + + uint16_t match_length = 0; + uint16_t match_pos = find_longest_match(hse, + start, end, max_possible, &match_length); + + if (match_pos == MATCH_NOT_FOUND) { + LOG("ss Match not found\n"); + hse->match_scan_index++; + hse->flags |= FLAG_HAS_LITERAL; + hse->match_length = 0; + return HSES_YIELD_TAG_BIT; + } else { + LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); + hse->match_pos = match_pos; + hse->match_length = match_length; + ASSERT(match_pos < 1 << hse->window_sz2 /*window_length*/); + + return HSES_YIELD_TAG_BIT; + } +} + +static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + if (hse->match_length == 0) { + add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); + return HSES_YIELD_LITERAL; + } else { + add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); + hse->outgoing_bits = hse->match_pos - 1; + hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); + return HSES_YIELD_BR_INDEX; + } + } else { + return HSES_YIELD_TAG_BIT; /* output is full, continue */ + } +} + +static HSE_state st_yield_literal(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + push_literal_byte(hse, oi); + hse->flags &= ~FLAG_HAS_LITERAL; + if (on_final_literal(hse)) { return HSES_FLUSH_BITS; } + return hse->match_length > 0 ? HSES_YIELD_TAG_BIT : HSES_SEARCH; + } else { + return HSES_YIELD_LITERAL; + } +} + +static HSE_state st_yield_br_index(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + LOG("-- yielding backref index %u\n", hse->match_pos); + if (push_outgoing_bits(hse, oi) > 0) { + return HSES_YIELD_BR_INDEX; /* continue */ + } else { + hse->outgoing_bits = hse->match_length - 1; + hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); + return HSES_YIELD_BR_LENGTH; /* done */ + } + } else { + return HSES_YIELD_BR_INDEX; /* continue */ + } +} + +static HSE_state st_yield_br_length(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + LOG("-- yielding backref length %u\n", hse->match_length); + if (push_outgoing_bits(hse, oi) > 0) { + return HSES_YIELD_BR_LENGTH; + } else { + hse->match_scan_index += hse->match_length; + hse->match_length = 0; + return HSES_SEARCH; + } + } else { + return HSES_YIELD_BR_LENGTH; + } +} + +static HSE_state st_save_backlog(heatshrink_encoder *hse) { + if (is_finishing(hse)) { + /* copy remaining literal (if necessary) */ + if (has_literal(hse)) { + hse->flags |= FLAG_ON_FINAL_LITERAL; + return HSES_YIELD_TAG_BIT; + } else { + return HSES_FLUSH_BITS; + } + } else { + LOG("-- saving backlog\n"); + save_backlog(hse); + return HSES_NOT_FULL; + } +} + +static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, + output_info *oi) { + if (hse->bit_index == 0x80) { + LOG("-- done!\n"); + return HSES_DONE; + } else if (can_take_byte(oi)) { + LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); + oi->buf[(*oi->output_size)++] = hse->current_byte; + LOG("-- done!\n"); + return HSES_DONE; + } else { + return HSES_FLUSH_BITS; + } +} + +static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { + LOG("-- adding tag bit: %d\n", tag); + push_bits(hse, 1, tag, oi); +} + +static uint16_t get_input_offset(heatshrink_encoder *hse) { + return get_input_buffer_size(hse); +} + +static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { + return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); + (void)hse; +} + +static uint16_t get_lookahead_size(heatshrink_encoder *hse) { + return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); + (void)hse; +} + +static void do_indexing(heatshrink_encoder *hse) { +#if HEATSHRINK_USE_INDEX + /* Build an index array I that contains flattened linked lists + * for the previous instances of every byte in the buffer. + * + * For example, if buf[200] == 'x', then index[200] will either + * be an offset i such that buf[i] == 'x', or a negative offset + * to indicate end-of-list. This significantly speeds up matching, + * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. + * + * Future optimization options: + * 1. Since any negative value represents end-of-list, the other + * 15 bits could be used to improve the index dynamically. + * + * 2. Likewise, the last lookahead_sz bytes of the index will + * not be usable, so temporary data could be stored there to + * dynamically improve the index. + * */ + struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); + uint16_t last[256]; + memset(last, 0xFF, sizeof(last)); + + uint8_t * const data = hse->buffer; + int16_t * const index = hsi->index; + + const uint16_t input_offset = get_input_offset(hse); + const uint16_t end = input_offset + hse->input_size; + + for (uint16_t i=0; iflags & FLAG_IS_FINISHING; +} + +static int backlog_is_partial(heatshrink_encoder *hse) { + return hse->flags & FLAG_BACKLOG_IS_PARTIAL; +} + +static int backlog_is_filled(heatshrink_encoder *hse) { + return hse->flags & FLAG_BACKLOG_IS_FILLED; +} + +static int on_final_literal(heatshrink_encoder *hse) { + return hse->flags & FLAG_ON_FINAL_LITERAL; +} + +static int has_literal(heatshrink_encoder *hse) { + return (hse->flags & FLAG_HAS_LITERAL); +} + +static int can_take_byte(output_info *oi) { + return *oi->output_size < oi->buf_size; +} + +/* Return the longest match for the bytes at buf[end:end+maxlen] between + * buf[start] and buf[end-1]. If no match is found, return -1. */ +static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, + uint16_t end, const uint16_t maxlen, uint16_t *match_length) { + LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", + end, end + maxlen, start, end + maxlen - 1, maxlen); + uint8_t *buf = hse->buffer; + + uint16_t match_maxlen = 0; + uint16_t match_index = MATCH_NOT_FOUND; + const uint16_t break_even_point = 3; + uint16_t len = 0; + uint8_t * const needlepoint = &buf[end]; +#if HEATSHRINK_USE_INDEX + struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); + int16_t pos = hsi->index[end]; + + while (pos >= start) { + uint8_t * const pospoint = &buf[pos]; + len = 0; + + /* Only check matches that will potentially beat the current maxlen. + * This is redundant with the index if match_maxlen is 0, but the + * added branch overhead to check if it == 0 seems to be worse. */ + if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { + pos = hsi->index[pos]; + continue; + } + + for (len = 1; len < maxlen; len++) { + if (pospoint[len] != needlepoint[len]) break; + } + + if (len > match_maxlen) { + match_maxlen = len; + match_index = pos; + if (len == maxlen) { break; } /* won't find better */ + } + pos = hsi->index[pos]; + } +#else + for (int16_t pos=end - 1; pos >= start; pos--) { + uint8_t * const pospoint = &buf[pos]; + if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) + && (*pospoint == *needlepoint)) { + for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", + pos + len, pospoint[len], needlepoint[len], start); + } + if (pospoint[len] != needlepoint[len]) { break; } + } + if (len > match_maxlen) { + match_maxlen = len; + match_index = pos; + if (len == maxlen) { break; } /* don't keep searching */ + } + } + } +#endif + + if (match_maxlen >= break_even_point) { + LOG("-- best match: %u bytes at -%u\n", + match_maxlen, end - match_index); + *match_length = match_maxlen; + return end - match_index; + } + LOG("-- none found\n"); + return MATCH_NOT_FOUND; +} + +static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { + uint8_t count = 0; + uint8_t bits = 0; + if (hse->outgoing_bits_count > 8) { + count = 8; + bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); + } else { + count = hse->outgoing_bits_count; + bits = hse->outgoing_bits; + } + + if (count > 0) { + LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); + push_bits(hse, count, bits, oi); + hse->outgoing_bits_count -= count; + } + return count; +} + +/* Push COUNT (max 8) bits to the output buffer, which has room. + * Bytes are set from the lowest bits, up. */ +static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, + output_info *oi) { + ASSERT(count <= 8); + LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); + + /* If adding a whole byte and at the start of a new output byte, + * just push it through whole and skip the bit IO loop. */ + if (count == 8 && hse->bit_index == 0x80) { + oi->buf[(*oi->output_size)++] = bits; + } else { + for (int i=count - 1; i>=0; i--) { + bool bit = bits & (1 << i); + if (bit) { hse->current_byte |= hse->bit_index; } + if (0) { + LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", + bit ? 1 : 0, hse->bit_index, hse->current_byte); + } + hse->bit_index >>= 1; + if (hse->bit_index == 0x00) { + hse->bit_index = 0x80; + LOG(" > pushing byte 0x%02x\n", hse->current_byte); + oi->buf[(*oi->output_size)++] = hse->current_byte; + hse->current_byte = 0x00; + } + } + } +} + +static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { + uint16_t processed_offset = hse->match_scan_index - 1; + uint16_t input_offset = get_input_offset(hse) + processed_offset; + uint8_t c = hse->buffer[input_offset]; + LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", + c, isprint(c) ? c : '.', input_offset); + push_bits(hse, 8, c, oi); +} + +static void save_backlog(heatshrink_encoder *hse) { + size_t input_buf_sz = get_input_buffer_size(hse); + + uint16_t msi = hse->match_scan_index; + + /* Copy processed data to beginning of buffer, so it can be + * used for future matches. Don't bother checking whether the + * input is less than the maximum size, because if it isn't, + * we're done anyway. */ + uint16_t rem = input_buf_sz - msi; // unprocessed bytes + uint16_t shift_sz = input_buf_sz + rem; + + memmove(&hse->buffer[0], + &hse->buffer[input_buf_sz - rem], + shift_sz); + + if (backlog_is_partial(hse)) { + /* The whole backlog is filled in now, so include it in scans. */ + hse->flags |= FLAG_BACKLOG_IS_FILLED; + } else { + /* Include backlog, except for the first lookahead_sz bytes, which + * are still undefined. */ + hse->flags |= FLAG_BACKLOG_IS_PARTIAL; + } + hse->match_scan_index = 0; + hse->input_size -= input_buf_sz - rem; +} diff --git a/espfs/heatshrink/heatshrink_encoder.h b/espfs/heatshrink/heatshrink_encoder.h new file mode 100644 index 0000000..18c1773 --- /dev/null +++ b/espfs/heatshrink/heatshrink_encoder.h @@ -0,0 +1,109 @@ +#ifndef HEATSHRINK_ENCODER_H +#define HEATSHRINK_ENCODER_H + +#include +#include +#include "heatshrink_common.h" +#include "heatshrink_config.h" + +typedef enum { + HSER_SINK_OK, /* data sunk into input buffer */ + HSER_SINK_ERROR_NULL=-1, /* NULL argument */ + HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ +} HSE_sink_res; + +typedef enum { + HSER_POLL_EMPTY, /* input exhausted */ + HSER_POLL_MORE, /* poll again for more output */ + HSER_POLL_ERROR_NULL=-1, /* NULL argument */ + HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ +} HSE_poll_res; + +typedef enum { + HSER_FINISH_DONE, /* encoding is complete */ + HSER_FINISH_MORE, /* more output remaining; use poll */ + HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ +} HSE_finish_res; + +#if HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ + ((HSE)->window_sz2) +#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ + ((HSE)->lookahead_sz2) +#define HEATSHRINK_ENCODER_INDEX(HSE) \ + ((HSE)->search_index) +struct hs_index { + uint16_t size; + int16_t index[]; +}; +#else +#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ + (HEATSHRINK_STATIC_WINDOW_BITS) +#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ + (HEATSHRINK_STATIC_LOOKAHEAD_BITS) +#define HEATSHRINK_ENCODER_INDEX(HSE) \ + (&(HSE)->search_index) +struct hs_index { + uint16_t size; + int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; +}; +#endif + +typedef struct { + uint16_t input_size; /* bytes in input buffer */ + uint16_t match_scan_index; + uint16_t match_length; + uint16_t match_pos; + uint16_t outgoing_bits; /* enqueued outgoing bits */ + uint8_t outgoing_bits_count; + uint8_t flags; + uint8_t state; /* current state machine node */ + uint8_t current_byte; /* current byte of output */ + uint8_t bit_index; /* current bit index */ +#if HEATSHRINK_DYNAMIC_ALLOC + uint8_t window_sz2; /* 2^n size of window */ + uint8_t lookahead_sz2; /* 2^n size of lookahead */ +#if HEATSHRINK_USE_INDEX + struct hs_index *search_index; +#endif + /* input buffer and / sliding window for expansion */ + uint8_t buffer[]; +#else + #if HEATSHRINK_USE_INDEX + struct hs_index search_index; + #endif + /* input buffer and / sliding window for expansion */ + uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; +#endif +} heatshrink_encoder; + +#if HEATSHRINK_DYNAMIC_ALLOC +/* Allocate a new encoder struct and its buffers. + * Returns NULL on error. */ +heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, + uint8_t lookahead_sz2); + +/* Free an encoder. */ +void heatshrink_encoder_free(heatshrink_encoder *hse); +#endif + +/* Reset an encoder. */ +void heatshrink_encoder_reset(heatshrink_encoder *hse); + +/* Sink up to SIZE bytes from IN_BUF into the encoder. + * INPUT_SIZE is set to the number of bytes actually sunk (in case a + * buffer was filled.). */ +HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, + uint8_t *in_buf, size_t size, size_t *input_size); + +/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into + * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ +HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size); + +/* Notify the encoder that the input stream is finished. + * If the return value is HSER_FINISH_MORE, there is still more output, so + * call heatshrink_encoder_poll and repeat. */ +HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); + +#endif diff --git a/espfs/heatshrink/test_heatshrink_dynamic.c b/espfs/heatshrink/test_heatshrink_dynamic.c new file mode 100644 index 0000000..1e18a69 --- /dev/null +++ b/espfs/heatshrink/test_heatshrink_dynamic.c @@ -0,0 +1,999 @@ +#include +#include +#include + +#include "heatshrink_encoder.h" +#include "heatshrink_decoder.h" +#include "greatest.h" + +#if !HEATSHRINK_DYNAMIC_ALLOC +#error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for dynamic allocation test suite. +#endif + +SUITE(encoding); +SUITE(decoding); +SUITE(integration); + +#ifdef HEATSHRINK_HAS_THEFT +SUITE(properties); +#endif + +static void dump_buf(char *name, uint8_t *buf, uint16_t count) { + for (int i=0; iinput_size, 6); + ASSERT_EQ(hsd->input_index, 0); + heatshrink_decoder_free(hsd); + PASS(); +} + +TEST decoder_poll_should_return_empty_if_empty(void) { + uint8_t output[256]; + size_t out_sz = 0; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, + HEATSHRINK_MIN_WINDOW_BITS, 4); + HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, &out_sz); + ASSERT_EQ(HSDR_POLL_EMPTY, res); + heatshrink_decoder_free(hsd); + PASS(); +} + +TEST decoder_poll_should_reject_null_hsd(void) { + uint8_t output[256]; + size_t out_sz = 0; + HSD_poll_res res = heatshrink_decoder_poll(NULL, output, 256, &out_sz); + ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); + PASS(); +} + +TEST decoder_poll_should_reject_null_output_buffer(void) { + size_t out_sz = 0; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, + HEATSHRINK_MIN_WINDOW_BITS, 4); + HSD_poll_res res = heatshrink_decoder_poll(hsd, NULL, 256, &out_sz); + ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); + heatshrink_decoder_free(hsd); + PASS(); +} + +TEST decoder_poll_should_reject_null_output_size_pointer(void) { + uint8_t output[256]; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, + HEATSHRINK_MIN_WINDOW_BITS, 4); + HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, NULL); + ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); + heatshrink_decoder_free(hsd); + PASS(); +} + +TEST decoder_poll_should_expand_short_literal(void) { + uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0 }; //"foo" + uint8_t output[4]; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 3); + size_t count = 0; + + HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); + ASSERT_EQ(HSDR_SINK_OK, sres); + + size_t out_sz = 0; + HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 4, &out_sz); + ASSERT_EQ(HSDR_POLL_EMPTY, pres); + ASSERT_EQ(3, out_sz); + ASSERT_EQ('f', output[0]); + ASSERT_EQ('o', output[1]); + ASSERT_EQ('o', output[2]); + + heatshrink_decoder_free(hsd); + PASS(); +} + +TEST decoder_poll_should_expand_short_literal_and_backref(void) { + uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo" + uint8_t output[6]; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7); + memset(output, 0, sizeof(*output)); + size_t count = 0; + + HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); + ASSERT_EQ(HSDR_SINK_OK, sres); + + size_t out_sz = 0; + (void)heatshrink_decoder_poll(hsd, output, 6, &out_sz); + + if (0) dump_buf("output", output, out_sz); + ASSERT_EQ(6, out_sz); + ASSERT_EQ('f', output[0]); + ASSERT_EQ('o', output[1]); + ASSERT_EQ('o', output[2]); + ASSERT_EQ('f', output[3]); + ASSERT_EQ('o', output[4]); + ASSERT_EQ('o', output[5]); + + heatshrink_decoder_free(hsd); + PASS(); +} + +TEST decoder_poll_should_expand_short_self_overlapping_backref(void) { + /* "aaaaa" == (literal, 1), ('a'), (backref, 1 back, 4 bytes) */ + uint8_t input[] = {0xb0, 0x80, 0x01, 0x80}; + uint8_t output[6]; + uint8_t expected[] = {'a', 'a', 'a', 'a', 'a'}; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 7); + size_t count = 0; + + HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); + ASSERT_EQ(HSDR_SINK_OK, sres); + + size_t out_sz = 0; + (void)heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz); + + if (0) dump_buf("output", output, out_sz); + ASSERT_EQ(sizeof(expected), out_sz); + for (size_t i=0; iwindow_sz2, + cfg->lookahead_sz2); + heatshrink_decoder *hsd = heatshrink_decoder_alloc(cfg->decoder_input_buffer_size, + cfg->window_sz2, cfg->lookahead_sz2); + size_t comp_sz = input_size + (input_size/2) + 4; + size_t decomp_sz = input_size + (input_size/2) + 4; + uint8_t *comp = malloc(comp_sz); + uint8_t *decomp = malloc(decomp_sz); + if (comp == NULL) FAILm("malloc fail"); + if (decomp == NULL) FAILm("malloc fail"); + memset(comp, 0, comp_sz); + memset(decomp, 0, decomp_sz); + + size_t count = 0; + + if (cfg->log_lvl > 1) { + printf("\n^^ COMPRESSING\n"); + dump_buf("input", input, input_size); + } + + size_t sunk = 0; + size_t polled = 0; + while (sunk < input_size) { + ASSERT(heatshrink_encoder_sink(hse, &input[sunk], input_size - sunk, &count) >= 0); + sunk += count; + if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); + if (sunk == input_size) { + ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); + } + + HSE_poll_res pres; + do { /* "turn the crank" */ + pres = heatshrink_encoder_poll(hse, &comp[polled], comp_sz - polled, &count); + ASSERT(pres >= 0); + polled += count; + if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); + } while (pres == HSER_POLL_MORE); + ASSERT_EQ(HSER_POLL_EMPTY, pres); + if (polled >= comp_sz) FAILm("compression should never expand that much"); + if (sunk == input_size) { + ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(hse)); + } + } + if (cfg->log_lvl > 0) printf("in: %u compressed: %zu ", input_size, polled); + size_t compressed_size = polled; + sunk = 0; + polled = 0; + + if (cfg->log_lvl > 1) { + printf("\n^^ DECOMPRESSING\n"); + dump_buf("comp", comp, compressed_size); + } + while (sunk < compressed_size) { + ASSERT(heatshrink_decoder_sink(hsd, &comp[sunk], compressed_size - sunk, &count) >= 0); + sunk += count; + if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); + if (sunk == compressed_size) { + ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(hsd)); + } + + HSD_poll_res pres; + do { + pres = heatshrink_decoder_poll(hsd, &decomp[polled], + decomp_sz - polled, &count); + ASSERT(pres >= 0); + ASSERT(count > 0); + polled += count; + if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); + } while (pres == HSDR_POLL_MORE); + ASSERT_EQ(HSDR_POLL_EMPTY, pres); + if (sunk == compressed_size) { + HSD_finish_res fres = heatshrink_decoder_finish(hsd); + ASSERT_EQ(HSDR_FINISH_DONE, fres); + } + + if (polled > input_size) { + printf("\nExpected %zd, got %zu\n", (size_t)input_size, polled); + FAILm("Decompressed data is larger than original input"); + } + } + if (cfg->log_lvl > 0) printf("decompressed: %zu\n", polled); + if (polled != input_size) { + FAILm("Decompressed length does not match original input length"); + } + + if (cfg->log_lvl > 1) dump_buf("decomp", decomp, polled); + for (uint32_t i=0; i out[%d] == 0x%02x ('%c') %c\n", + j, input[j], isprint(input[j]) ? input[j] : '.', + j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.', + input[j] == decomp[j] ? ' ' : 'X'); + } + } + } + ASSERT_EQ(input[i], decomp[i]); + } + free(comp); + free(decomp); + heatshrink_encoder_free(hse); + heatshrink_decoder_free(hsd); + PASS(); +} + +TEST data_without_duplication_should_match(void) { + uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + cfg_info cfg; + cfg.log_lvl = 0; + cfg.window_sz2 = 8; + cfg.lookahead_sz2 = 3; + cfg.decoder_input_buffer_size = 256; + return compress_and_expand_and_check(input, sizeof(input), &cfg); +} + +TEST data_with_simple_repetition_should_compress_and_decompress_properly(void) { + uint8_t input[] = {'a', 'b', 'c', 'a', 'b', 'c', 'd', 'a', 'b', + 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e', 'f', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h'}; + cfg_info cfg; + cfg.log_lvl = 0; + cfg.window_sz2 = 8; + cfg.lookahead_sz2 = 3; + cfg.decoder_input_buffer_size = 256; + return compress_and_expand_and_check(input, sizeof(input), &cfg); +} + +TEST data_without_duplication_should_match_with_absurdly_tiny_buffers(void) { + heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 3); + heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 3); + uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + uint8_t comp[60]; + uint8_t decomp[60]; + size_t count = 0; + int log = 0; + + if (log) dump_buf("input", input, sizeof(input)); + for (uint32_t i=0; i= 0); + } + ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); + + size_t packed_count = 0; + do { + ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); + packed_count += count; + } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); + + if (log) dump_buf("comp", comp, packed_count); + for (uint32_t i=0; i= 0); + } + + for (uint32_t i=0; i= 0); + } + + if (log) dump_buf("decomp", decomp, sizeof(input)); + for (uint32_t i=0; i= 0); + } + ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); + + size_t packed_count = 0; + do { + ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); + packed_count += count; + } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); + + if (log) dump_buf("comp", comp, packed_count); + for (uint32_t i=0; i= 0); + } + + for (uint32_t i=0; i= 0); + } + + if (log) dump_buf("decomp", decomp, sizeof(input)); + for (uint32_t i=0; ilog_lvl > 0) { + printf("\n-- size %u, seed %u, input buf %zu\n", + size, seed, cfg->decoder_input_buffer_size); + } + fill_with_pseudorandom_letters(input, size, seed); + return compress_and_expand_and_check(input, size, cfg); +} + +TEST small_input_buffer_should_not_impact_decoder_correctness(void) { + int size = 5; + uint8_t input[size]; + cfg_info cfg; + cfg.log_lvl = 0; + cfg.window_sz2 = 8; + cfg.lookahead_sz2 = 3; + cfg.decoder_input_buffer_size = 5; + for (uint16_t i=0; i= 19901L + printf("\n\nFuzzing (single-byte sizes):\n"); + for (uint8_t lsize=3; lsize < 8; lsize++) { + for (uint32_t size=1; size < 128*1024L; size <<= 1) { + if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); + for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ + if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); + for (uint32_t seed=1; seed<=10; seed++) { + if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); + cfg_info cfg; + cfg.log_lvl = 0; + cfg.window_sz2 = 8; + cfg.lookahead_sz2 = lsize; + cfg.decoder_input_buffer_size = ibs; + RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); + } + } + } + } + + printf("\nFuzzing (multi-byte sizes):\n"); + for (uint8_t lsize=6; lsize < 9; lsize++) { + for (uint32_t size=1; size < 128*1024L; size <<= 1) { + if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); + for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ + if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); + for (uint32_t seed=1; seed<=10; seed++) { + if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); + cfg_info cfg; + cfg.log_lvl = 0; + cfg.window_sz2 = 11; + cfg.lookahead_sz2 = lsize; + cfg.decoder_input_buffer_size = ibs; + RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); + } + } + } + } + +#endif +} + +/* Add all the definitions that need to be in the test runner's main file. */ +GREATEST_MAIN_DEFS(); + +int main(int argc, char **argv) { + GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ + RUN_SUITE(encoding); + RUN_SUITE(decoding); + RUN_SUITE(integration); + #ifdef HEATSHRINK_HAS_THEFT + RUN_SUITE(properties); + #endif + GREATEST_MAIN_END(); /* display results */ +} diff --git a/espfs/heatshrink/test_heatshrink_dynamic_theft.c b/espfs/heatshrink/test_heatshrink_dynamic_theft.c new file mode 100644 index 0000000..2752886 --- /dev/null +++ b/espfs/heatshrink/test_heatshrink_dynamic_theft.c @@ -0,0 +1,521 @@ +#include "heatshrink_config.h" +#ifdef HEATSHRINK_HAS_THEFT + +#include +#include +#include +#include +#include + +#include "heatshrink_encoder.h" +#include "heatshrink_decoder.h" +#include "greatest.h" +#include "theft.h" +#include "greatest_theft.h" + +#if !HEATSHRINK_DYNAMIC_ALLOC +#error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for this test suite. +#endif + +SUITE(properties); + +typedef struct { + int limit; + int fails; + int dots; +} test_env; + +typedef struct { + size_t size; + uint8_t buf[]; +} rbuf; + +static void *rbuf_alloc_cb(struct theft *t, theft_hash seed, void *env) { + test_env *te = (test_env *)env; + //printf("seed is 0x%016llx\n", seed); + + size_t sz = (size_t)(seed % te->limit) + 1; + rbuf *r = malloc(sizeof(rbuf) + sz); + if (r == NULL) { return THEFT_ERROR; } + r->size = sz; + + for (size_t i = 0; i < sz; i += sizeof(theft_hash)) { + theft_hash s = theft_random(t); + for (uint8_t b = 0; b < sizeof(theft_hash); b++) { + if (i + b >= sz) { break; } + r->buf[i + b] = (uint8_t) (s >> (8*b)) & 0xff; + } + } + + return r; +} + +static void rbuf_free_cb(void *instance, void *env) { + free(instance); + (void)env; +} + +static uint64_t rbuf_hash_cb(void *instance, void *env) { + rbuf *r = (rbuf *)instance; + (void)env; + return theft_hash_onepass(r->buf, r->size); +} + +/* Make a copy of a buffer, keeping NEW_SZ bytes starting at OFFSET. */ +static void *copy_rbuf_subset(rbuf *cur, size_t new_sz, size_t byte_offset) { + if (new_sz == 0) { return THEFT_DEAD_END; } + rbuf *nr = malloc(sizeof(rbuf) + new_sz); + if (nr == NULL) { return THEFT_ERROR; } + nr->size = new_sz; + memcpy(nr->buf, &cur->buf[byte_offset], new_sz); + /* printf("%zu -> %zu\n", cur->size, new_sz); */ + return nr; +} + +/* Make a copy of a buffer, but only PORTION, starting OFFSET in + * (e.g. the third quarter is (0.25 at +0.75). Rounds to ints. */ +static void *copy_rbuf_percent(rbuf *cur, float portion, float offset) { + size_t new_sz = cur->size * portion; + size_t byte_offset = (size_t)(cur->size * offset); + return copy_rbuf_subset(cur, new_sz, byte_offset); +} + +/* How to shrink a random buffer to a simpler one. */ +static void *rbuf_shrink_cb(void *instance, uint32_t tactic, void *env) { + rbuf *cur = (rbuf *)instance; + + if (tactic == 0) { /* first half */ + return copy_rbuf_percent(cur, 0.5, 0); + } else if (tactic == 1) { /* second half */ + return copy_rbuf_percent(cur, 0.5, 0.5); + } else if (tactic <= 18) { /* drop 1-16 bytes at start */ + const int last_tactic = 1; + const size_t drop = tactic - last_tactic; + if (cur->size < drop) { return THEFT_DEAD_END; } + return copy_rbuf_subset(cur, cur->size - drop, drop); + } else if (tactic <= 34) { /* drop 1-16 bytes at end */ + const int last_tactic = 18; + const size_t drop = tactic - last_tactic; + if (cur->size < drop) { return THEFT_DEAD_END; } + return copy_rbuf_subset(cur, cur->size - drop, 0); + } else if (tactic == 35) { + /* Divide every byte by 2, saturating at 0 */ + rbuf *cp = copy_rbuf_percent(cur, 1, 0); + if (cp == NULL) { return THEFT_ERROR; } + for (size_t i = 0; i < cp->size; i++) { cp->buf[i] /= 2; } + return cp; + } else if (tactic == 36) { + /* subtract 1 from every byte, saturating at 0 */ + rbuf *cp = copy_rbuf_percent(cur, 1, 0); + if (cp == NULL) { return THEFT_ERROR; } + for (size_t i = 0; i < cp->size; i++) { + if (cp->buf[i] > 0) { cp->buf[i]--; } + } + return cp; + } else { + (void)env; + return THEFT_NO_MORE_TACTICS; + } + + return THEFT_NO_MORE_TACTICS; +} + +static void rbuf_print_cb(FILE *f, void *instance, void *env) { + rbuf *r = (rbuf *)instance; + (void)env; + fprintf(f, "buf[%zd]:\n ", r->size); + uint8_t bytes = 0; + for (size_t i = 0; i < r->size; i++) { + fprintf(f, "%02x", r->buf[i]); + bytes++; + if (bytes == 16) { + fprintf(f, "\n "); + bytes = 0; + } + } + fprintf(f, "\n"); +} + +static struct theft_type_info rbuf_info = { + .alloc = rbuf_alloc_cb, + .free = rbuf_free_cb, + .hash = rbuf_hash_cb, + .shrink = rbuf_shrink_cb, + .print = rbuf_print_cb, +}; + +static theft_progress_callback_res +progress_cb(struct theft_trial_info *info, void *env) { + test_env *te = (test_env *)env; + if ((info->trial & 0xff) == 0) { + printf("."); + fflush(stdout); + te->dots++; + if (te->dots == 64) { + printf("\n"); + te->dots = 0; + } + } + + if (info->status == THEFT_TRIAL_FAIL) { + te->fails++; + rbuf *cur = info->args[0]; + if (cur->size < 5) { return THEFT_PROGRESS_HALT; } + } + + if (te->fails > 10) { + return THEFT_PROGRESS_HALT; + } + return THEFT_PROGRESS_CONTINUE; +} + +/* For an arbitrary input buffer, it should never get stuck in a + * state where the data has been sunk but no data can be polled. */ +static theft_trial_res prop_should_not_get_stuck(void *input) { + /* Make a buffer large enough for the output: 4 KB of input with + * each 16 bits becoming up to 16 bytes will fit in a 64 KB buffer. + * (4 KB of input comes from `env.limit = 1 << 12;` below.) */ + uint8_t output[64 * 1024]; + heatshrink_decoder *hsd = heatshrink_decoder_alloc((64 * 1024L) - 1, 12, 4); + if (hsd == NULL) { return THEFT_TRIAL_ERROR; } + + rbuf *r = (rbuf *)input; + + size_t count = 0; + HSD_sink_res sres = heatshrink_decoder_sink(hsd, r->buf, r->size, &count); + if (sres != HSDR_SINK_OK) { return THEFT_TRIAL_ERROR; } + + size_t out_sz = 0; + HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz); + if (pres != HSDR_POLL_EMPTY) { return THEFT_TRIAL_FAIL; } + + HSD_finish_res fres = heatshrink_decoder_finish(hsd); + heatshrink_decoder_free(hsd); + if (fres != HSDR_FINISH_DONE) { return THEFT_TRIAL_FAIL; } + + return THEFT_TRIAL_PASS; +} + +static bool get_time_seed(theft_seed *seed) +{ + struct timeval tv; + if (-1 == gettimeofday(&tv, NULL)) { return false; } + *seed = (theft_seed)((tv.tv_sec << 32) | tv.tv_usec); + /* printf("seed is 0x%016llx\n", *seed); */ + return true; +} + +TEST decoder_fuzzing_should_not_detect_stuck_state(void) { + // Get a random number seed based on the time + theft_seed seed; + if (!get_time_seed(&seed)) { FAIL(); } + + /* Pass the max buffer size for this property (4 KB) in a closure */ + test_env env = { .limit = 1 << 12 }; + + theft_seed always_seeds = { 0xe87bb1f61032a061 }; + + struct theft *t = theft_init(0); + struct theft_cfg cfg = { + .name = __func__, + .fun = prop_should_not_get_stuck, + .type_info = { &rbuf_info }, + .seed = seed, + .trials = 100000, + .progress_cb = progress_cb, + .env = &env, + + .always_seeds = &always_seeds, + .always_seed_count = 1, + }; + + theft_run_res res = theft_run(t, &cfg); + theft_free(t); + printf("\n"); + GREATEST_ASSERT_EQm("should_not_get_stuck", THEFT_RUN_PASS, res); + PASS(); +} + +static theft_trial_res prop_encoded_and_decoded_data_should_match(void *input) { + uint8_t e_output[64 * 1024]; + uint8_t d_output[64 * 1024]; + heatshrink_encoder *hse = heatshrink_encoder_alloc(12, 4); + if (hse == NULL) { return THEFT_TRIAL_ERROR; } + heatshrink_decoder *hsd = heatshrink_decoder_alloc(4096, 12, 4); + if (hsd == NULL) { return THEFT_TRIAL_ERROR; } + + rbuf *r = (rbuf *)input; + + size_t e_input_size = 0; + HSE_sink_res esres = heatshrink_encoder_sink(hse, + r->buf, r->size, &e_input_size); + if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } + if (e_input_size != r->size) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + + HSE_finish_res efres = heatshrink_encoder_finish(hse); + if (efres != HSER_FINISH_MORE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + + size_t e_output_size = 0; + HSE_poll_res epres = heatshrink_encoder_poll(hse, + e_output, sizeof(e_output), &e_output_size); + if (epres != HSER_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + + size_t count = 0; + HSD_sink_res sres = heatshrink_decoder_sink(hsd, e_output, e_output_size, &count); + if (sres != HSDR_SINK_OK) { return THEFT_TRIAL_ERROR; } + + size_t d_output_size = 0; + HSD_poll_res pres = heatshrink_decoder_poll(hsd, d_output, + sizeof(d_output), &d_output_size); + if (pres != HSDR_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + if (d_output_size != r->size) { + printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; + } + + if (0 != memcmp(d_output, r->buf, d_output_size)) { + return THEFT_TRIAL_FAIL; + } + + HSD_finish_res fres = heatshrink_decoder_finish(hsd); + if (fres != HSDR_FINISH_DONE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + + heatshrink_encoder_free(hse); + heatshrink_decoder_free(hsd); + return THEFT_TRIAL_PASS; +} + + +TEST encoded_and_decoded_data_should_match(void) { + test_env env = { .limit = 1 << 11 }; + + theft_seed seed; + if (!get_time_seed(&seed)) { FAIL(); } + + struct theft *t = theft_init(0); + struct theft_cfg cfg = { + .name = __func__, + .fun = prop_encoded_and_decoded_data_should_match, + .type_info = { &rbuf_info }, + .seed = seed, + .trials = 1000000, + .env = &env, + .progress_cb = progress_cb, + }; + + theft_run_res res = theft_run(t, &cfg); + theft_free(t); + printf("\n"); + ASSERT_EQ(THEFT_RUN_PASS, res); + PASS(); +} + +static size_t ceil_nine_eighths(size_t sz) { + return sz + sz/8 + (sz & 0x07 ? 1 : 0); +} + +static theft_trial_res +prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst(void *input) { + uint8_t output[32 * 1024]; + heatshrink_encoder *hse = heatshrink_encoder_alloc(12, 4); + if (hse == NULL) { return THEFT_TRIAL_ERROR; } + + rbuf *r = (rbuf *)input; + + size_t input_size = 0; + HSE_sink_res esres = heatshrink_encoder_sink(hse, + r->buf, r->size, &input_size); + if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } + /* Assumes data fits in one sink, failure here means buffer must be larger. */ + if (input_size != r->size) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + + HSE_finish_res efres = heatshrink_encoder_finish(hse); + if (efres != HSER_FINISH_MORE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + + size_t output_size = 0; + HSE_poll_res epres = heatshrink_encoder_poll(hse, + output, sizeof(output), &output_size); + if (epres != HSER_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } + + size_t ceil_9_8s = ceil_nine_eighths(r->size); + if (output_size > ceil_9_8s) { + return THEFT_TRIAL_FAIL; + } + + heatshrink_encoder_free(hse); + return THEFT_TRIAL_PASS; +} + +TEST encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst(void) { + test_env env = { .limit = 1 << 11 }; + + theft_seed seed; + if (!get_time_seed(&seed)) { FAIL(); } + + struct theft *t = theft_init(0); + struct theft_cfg cfg = { + .name = __func__, + .fun = prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst, + .type_info = { &rbuf_info }, + .seed = seed, + .trials = 10000, + .env = &env, + .progress_cb = progress_cb, + }; + + theft_run_res res = theft_run(t, &cfg); + theft_free(t); + printf("\n"); + ASSERT_EQ(THEFT_RUN_PASS, res); + PASS(); +} + +static theft_trial_res +prop_encoder_should_always_make_progress(void *instance) { + uint8_t output[64 * 1024]; + heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 4); + if (hse == NULL) { return THEFT_TRIAL_ERROR; } + + rbuf *r = (rbuf *)instance; + + size_t sunk = 0; + int no_progress = 0; + + while (1) { + if (sunk < r->size) { + size_t input_size = 0; + HSE_sink_res esres = heatshrink_encoder_sink(hse, + &r->buf[sunk], r->size - sunk, &input_size); + if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } + sunk += input_size; + } else { + HSE_finish_res efres = heatshrink_encoder_finish(hse); + if (efres == HSER_FINISH_DONE) { + break; + } else if (efres != HSER_FINISH_MORE) { + printf("FAIL %d\n", __LINE__); + return THEFT_TRIAL_FAIL; + } + } + + size_t output_size = 0; + HSE_poll_res epres = heatshrink_encoder_poll(hse, + output, sizeof(output), &output_size); + if (epres < 0) { return THEFT_TRIAL_ERROR; } + if (output_size == 0 && sunk == r->size) { + no_progress++; + if (no_progress > 2) { + return THEFT_TRIAL_FAIL; + } + } else { + no_progress = 0; + } + } + + heatshrink_encoder_free(hse); + return THEFT_TRIAL_PASS; +} + +TEST encoder_should_always_make_progress(void) { + test_env env = { .limit = 1 << 15 }; + + theft_seed seed; + if (!get_time_seed(&seed)) { FAIL(); } + + struct theft *t = theft_init(0); + struct theft_cfg cfg = { + .name = __func__, + .fun = prop_encoder_should_always_make_progress, + .type_info = { &rbuf_info }, + .seed = seed, + .trials = 10000, + .env = &env, + .progress_cb = progress_cb, + }; + + theft_run_res res = theft_run(t, &cfg); + theft_free(t); + printf("\n"); + ASSERT_EQ(THEFT_RUN_PASS, res); + PASS(); +} + +static theft_trial_res +prop_decoder_should_always_make_progress(void *instance) { + uint8_t output[64 * 1024]; + heatshrink_decoder *hsd = heatshrink_decoder_alloc(512, 8, 4); + if (hsd == NULL) { return THEFT_TRIAL_ERROR; } + + rbuf *r = (rbuf *)instance; + + size_t sunk = 0; + int no_progress = 0; + + while (1) { + if (sunk < r->size) { + size_t input_size = 0; + HSD_sink_res sres = heatshrink_decoder_sink(hsd, + &r->buf[sunk], r->size - sunk, &input_size); + if (sres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } + sunk += input_size; + } else { + HSD_finish_res fres = heatshrink_decoder_finish(hsd); + if (fres == HSDR_FINISH_DONE) { + break; + } else if (fres != HSDR_FINISH_MORE) { + printf("FAIL %d\n", __LINE__); + return THEFT_TRIAL_FAIL; + } + } + + size_t output_size = 0; + HSD_poll_res pres = heatshrink_decoder_poll(hsd, + output, sizeof(output), &output_size); + if (pres < 0) { return THEFT_TRIAL_ERROR; } + if (output_size == 0 && sunk == r->size) { + no_progress++; + if (no_progress > 2) { + return THEFT_TRIAL_FAIL; + } + } else { + no_progress = 0; + } + } + + heatshrink_decoder_free(hsd); + return THEFT_TRIAL_PASS; +} + +TEST decoder_should_always_make_progress(void) { + test_env env = { .limit = 1 << 15 }; + + theft_seed seed; + if (!get_time_seed(&seed)) { FAIL(); } + + struct theft *t = theft_init(0); + struct theft_cfg cfg = { + .name = __func__, + .fun = prop_decoder_should_always_make_progress, + .type_info = { &rbuf_info }, + .seed = seed, + .trials = 10000, + .env = &env, + .progress_cb = progress_cb, + }; + + theft_run_res res = theft_run(t, &cfg); + theft_free(t); + printf("\n"); + ASSERT_EQ(THEFT_RUN_PASS, res); + PASS(); +} + +SUITE(properties) { + RUN_TEST(decoder_fuzzing_should_not_detect_stuck_state); + RUN_TEST(encoded_and_decoded_data_should_match); + RUN_TEST(encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst); + RUN_TEST(encoder_should_always_make_progress); + RUN_TEST(decoder_should_always_make_progress); +} +#else +struct because_iso_c_requires_at_least_one_declaration; +#endif diff --git a/espfs/heatshrink/test_heatshrink_static.c b/espfs/heatshrink/test_heatshrink_static.c new file mode 100644 index 0000000..e9c9754 --- /dev/null +++ b/espfs/heatshrink/test_heatshrink_static.c @@ -0,0 +1,167 @@ +#include +#include + +#include "heatshrink_encoder.h" +#include "heatshrink_decoder.h" +#include "greatest.h" + +#if HEATSHRINK_DYNAMIC_ALLOC +#error HEATSHRINK_DYNAMIC_ALLOC must be false for static allocation test suite. +#endif + +SUITE(integration); + +/* The majority of the tests are in test_heatshrink_dynamic, because that allows + * instantiating encoders/decoders with different settings at run-time. */ + +static heatshrink_encoder hse; +static heatshrink_decoder hsd; + +static void fill_with_pseudorandom_letters(uint8_t *buf, uint16_t size, uint32_t seed) { + uint64_t rn = 9223372036854775783; /* prime under 2^64 */ + for (int i=0; i 1) { + printf("\n^^ COMPRESSING\n"); + dump_buf("input", input, input_size); + } + + uint32_t sunk = 0; + uint32_t polled = 0; + while (sunk < input_size) { + ASSERT(heatshrink_encoder_sink(&hse, &input[sunk], input_size - sunk, &count) >= 0); + sunk += count; + if (log_lvl > 1) printf("^^ sunk %zd\n", count); + if (sunk == input_size) { + ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(&hse)); + } + + HSE_poll_res pres; + do { /* "turn the crank" */ + pres = heatshrink_encoder_poll(&hse, &comp[polled], comp_sz - polled, &count); + ASSERT(pres >= 0); + polled += count; + if (log_lvl > 1) printf("^^ polled %zd\n", count); + } while (pres == HSER_POLL_MORE); + ASSERT_EQ(HSER_POLL_EMPTY, pres); + if (polled >= comp_sz) FAILm("compression should never expand that much"); + if (sunk == input_size) { + ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(&hse)); + } + } + if (log_lvl > 0) printf("in: %u compressed: %u ", input_size, polled); + uint32_t compressed_size = polled; + sunk = 0; + polled = 0; + + if (log_lvl > 1) { + printf("\n^^ DECOMPRESSING\n"); + dump_buf("comp", comp, compressed_size); + } + while (sunk < compressed_size) { + ASSERT(heatshrink_decoder_sink(&hsd, &comp[sunk], compressed_size - sunk, &count) >= 0); + sunk += count; + if (log_lvl > 1) printf("^^ sunk %zd\n", count); + if (sunk == compressed_size) { + ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(&hsd)); + } + + HSD_poll_res pres; + do { + pres = heatshrink_decoder_poll(&hsd, &decomp[polled], + decomp_sz - polled, &count); + ASSERT(pres >= 0); + polled += count; + if (log_lvl > 1) printf("^^ polled %zd\n", count); + } while (pres == HSDR_POLL_MORE); + ASSERT_EQ(HSDR_POLL_EMPTY, pres); + if (sunk == compressed_size) { + HSD_finish_res fres = heatshrink_decoder_finish(&hsd); + ASSERT_EQ(HSDR_FINISH_DONE, fres); + } + + if (polled > input_size) { + FAILm("Decompressed data is larger than original input"); + } + } + if (log_lvl > 0) printf("decompressed: %u\n", polled); + if (polled != input_size) { + FAILm("Decompressed length does not match original input length"); + } + + if (log_lvl > 1) dump_buf("decomp", decomp, polled); + for (size_t i=0; i out[%zd] == 0x%02x ('%c')\n", + j, input[j], isprint(input[j]) ? input[j] : '.', + j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.'); + } + } + } + ASSERT_EQ(input[i], decomp[i]); + } + free(comp); + free(decomp); + PASS(); +} + +TEST pseudorandom_data_should_match(uint32_t size, uint32_t seed) { + uint8_t input[size]; + fill_with_pseudorandom_letters(input, size, seed); + return compress_and_expand_and_check(input, size, 0); +} + +SUITE(integration) { +#if __STDC_VERSION__ >= 19901L + for (uint32_t size=1; size < 64*1024; size <<= 1) { + if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); + for (uint32_t seed=1; seed<=100; seed++) { + if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); + RUN_TESTp(pseudorandom_data_should_match, size, seed); + } + } +#endif +} + +/* Add all the definitions that need to be in the test runner's main file. */ +GREATEST_MAIN_DEFS(); + +int main(int argc, char **argv) { + GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ + printf("INPUT_BUFFER_SIZE: %u\n", HEATSHRINK_STATIC_INPUT_BUFFER_SIZE); + printf("WINDOW_BITS: %u\n", HEATSHRINK_STATIC_WINDOW_BITS); + printf("LOOKAHEAD_BITS: %u\n", HEATSHRINK_STATIC_LOOKAHEAD_BITS); + + printf("sizeof(heatshrink_encoder): %zd\n", sizeof(heatshrink_encoder)); + printf("sizeof(heatshrink_decoder): %zd\n", sizeof(heatshrink_decoder)); + RUN_SUITE(integration); + GREATEST_MAIN_END(); /* display results */ +} diff --git a/espfs/heatshrink_decoder.c b/espfs/heatshrink_decoder.c index 522b560..9a4a9f7 100644 --- a/espfs/heatshrink_decoder.c +++ b/espfs/heatshrink_decoder.c @@ -13,7 +13,7 @@ #endif #include "heatshrink_config_custom.h" -#include "../lib/heatshrink/heatshrink_decoder.c" +#include "heatshrink/heatshrink_decoder.c" #endif diff --git a/espfs/mkespfsimage/Makefile b/espfs/mkespfsimage/Makefile index 8a39d46..26484d4 100644 --- a/espfs/mkespfsimage/Makefile +++ b/espfs/mkespfsimage/Makefile @@ -1,24 +1,36 @@ GZIP_COMPRESSION ?= no USE_HEATSHRINK ?= yes -CFLAGS=-I../../lib/heatshrink -I.. -std=gnu99 +TARGET = mkespfsimage.exe + +CC = gcc +LD = $(CC) +CFLAGS=-c -I../../heatshrink -I.. -Imman-win32 -std=gnu99 +LDFLAGS=-Lmman-win32 -lmman + ifeq ("$(GZIP_COMPRESSION)","yes") -CFLAGS += -DESPFS_GZIP +CFLAGS += -DESPFS_GZIP +LDFLAGS += -lz endif ifeq ("$(USE_HEATSHRINK)","yes") -CFLAGS += -DESPFS_HEATSHRINK +CFLAGS += -DESPFS_HEATSHRINK endif -OBJS=main.o heatshrink_encoder.o -TARGET=mkespfsimage +OBJECTS = main.o heatshrink_encoder.o -$(TARGET): $(OBJS) -ifeq ("$(GZIP_COMPRESSION)","yes") - $(CC) -o $@ $^ -lz -else - $(CC) -o $@ $^ -endif +all: libmman $(TARGET) + +libmman: + $(Q) make -C mman-win32 + +$(TARGET): $(OBJECTS) + $(LD) -o $@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ $^ clean: - rm -f $(TARGET) $(OBJS) \ No newline at end of file + rm -f $(OBJECTS) $(TARGET) + +.PHONY: all clean diff --git a/espfs/mkespfsimage/Makefile.linux b/espfs/mkespfsimage/Makefile.linux new file mode 100644 index 0000000..f1ae1aa --- /dev/null +++ b/espfs/mkespfsimage/Makefile.linux @@ -0,0 +1,24 @@ +GZIP_COMPRESSION ?= no +USE_HEATSHRINK ?= yes + +CFLAGS=-I../../heatshrink -I../../include -I.. -std=gnu99 +ifeq ("$(GZIP_COMPRESSION)","yes") +CFLAGS += -DESPFS_GZIP +endif + +ifeq ("$(USE_HEATSHRINK)","yes") +CFLAGS += -DESPFS_HEATSHRINK +endif + +OBJS=main.o heatshrink_encoder.o +TARGET=mkespfsimage + +$(TARGET): $(OBJS) +ifeq ("$(GZIP_COMPRESSION)","yes") + $(CC) -o $@ $^ -lz +else + $(CC) -o $@ $^ +endif + +clean: + rm -f $(TARGET) $(OBJS) \ No newline at end of file diff --git a/espfs/mkespfsimage/Makefile.windows b/espfs/mkespfsimage/Makefile.windows new file mode 100644 index 0000000..15aa8e3 --- /dev/null +++ b/espfs/mkespfsimage/Makefile.windows @@ -0,0 +1,33 @@ +GZIP_COMPRESSION ?= no +USE_HEATSHRINK ?= yes + +TARGET = mkespfsimage.exe + +CC = gcc +LD = $(CC) +CFLAGS=-c -I../../heatshrink -I.. -Imman-win32 -std=gnu99 +LDFLAGS=-Lmman-win32 -lmman + +ifeq ("$(GZIP_COMPRESSION)","yes") +CFLAGS += -DESPFS_GZIP +LDFLAGS += -lz +endif + +ifeq ("$(USE_HEATSHRINK)","yes") +CFLAGS += -DESPFS_HEATSHRINK +endif + +OBJECTS = main.o heatshrink_encoder.o + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(LD) -o $@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm -f $(OBJECTS) $(TARGET) + +.PHONY: all clean diff --git a/espfs/mkespfsimage/build-linux.sh b/espfs/mkespfsimage/build-linux.sh new file mode 100644 index 0000000..69e3166 --- /dev/null +++ b/espfs/mkespfsimage/build-linux.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +make -f Makefile.linux clean +make -f Makefile.linux USE_HEATSHRINK="yes" GZIP_COMPRESSION="no" diff --git a/espfs/mkespfsimage/build-win32.sh b/espfs/mkespfsimage/build-win32.sh new file mode 100644 index 0000000..f552ea9 --- /dev/null +++ b/espfs/mkespfsimage/build-win32.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +cd mman-win32 +./configure && make +cd .. +make -f Makefile.windows clean +make -f Makefile.windows USE_HEATSHRINK="yes" GZIP_COMPRESSION="no" diff --git a/espfs/mkespfsimage/espfsformat.h b/espfs/mkespfsimage/espfsformat.h new file mode 100644 index 0000000..8ce5549 --- /dev/null +++ b/espfs/mkespfsimage/espfsformat.h @@ -0,0 +1,33 @@ +#ifndef ESPROFSFORMAT_H +#define ESPROFSFORMAT_H + +/* +Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module. +Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files, +headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself +when trying to do a <4byte or unaligned read. +*/ + +/* +The idea 'borrows' from cpio: it's basically a concatenation of {header, filename, file} data. +Header, filename and file data is 32-bit aligned. The last file is indicated by data-less header +with the FLAG_LASTFILE flag set. +*/ + + +#define FLAG_LASTFILE (1<<0) +#define FLAG_GZIP (1<<1) +#define COMPRESS_NONE 0 +#define COMPRESS_HEATSHRINK 1 +#define ESPFS_MAGIC 0x73665345 + +typedef struct { + int32_t magic; + int8_t flags; + int8_t compression; + int16_t nameLen; + int32_t fileLenComp; + int32_t fileLenDecomp; +} __attribute__((packed)) EspFsHeader; + +#endif \ No newline at end of file diff --git a/espfs/mkespfsimage/heatshrink_encoder.c b/espfs/mkespfsimage/heatshrink_encoder.c index b563970..fd82c9a 100644 --- a/espfs/mkespfsimage/heatshrink_encoder.c +++ b/espfs/mkespfsimage/heatshrink_encoder.c @@ -1,4 +1,4 @@ //Stupid wraparound include to make sure object file doesn't end up in heatshrink dir #ifdef ESPFS_HEATSHRINK -#include "../lib/heatshrink/heatshrink_encoder.c" +#include "../heatshrink/heatshrink_encoder.c" #endif \ No newline at end of file diff --git a/espfs/mkespfsimage/main.c b/espfs/mkespfsimage/main.c index 4d5da0d..20a2b00 100644 --- a/espfs/mkespfsimage/main.c +++ b/espfs/mkespfsimage/main.c @@ -5,22 +5,31 @@ #include #include #include +#include +#include "espfs.h" +#ifdef __MINGW32__ +#include +#else #include +#endif +#ifdef __WIN32__ +#include +#else #include +#endif #include -#include "espfs.h" #include "espfsformat.h" //Heatshrink #ifdef ESPFS_HEATSHRINK -#include "heatshrink_common.h" -#include "heatshrink_config.h" -#include "heatshrink_encoder.h" +#include "../heatshrink/heatshrink_common.h" +#include "../heatshrink/heatshrink_config.h" +#include "../heatshrink/heatshrink_encoder.h" #endif //Gzip #ifdef ESPFS_GZIP -// If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev" +// If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev" // to install missing package. #include #endif @@ -175,7 +184,7 @@ int parseGzipExtensions(char *input) { } #endif -int handleFile(int f, char *name, int compression, int level, char **compName, off_t *csizePtr) { +int handleFile(int f, char *name, int compression, int level, char **compName) { char *fdat, *cdat; off_t size, csize; EspFsHeader h; @@ -229,7 +238,7 @@ int handleFile(int f, char *name, int compression, int level, char **compName, o h.nameLen=htoxs(h.nameLen); h.fileLenComp=htoxl(csize); h.fileLenDecomp=htoxl(size); - + write(1, &h, sizeof(EspFsHeader)); write(1, name, nameLen); while (nameLen&3) { @@ -257,7 +266,6 @@ int handleFile(int f, char *name, int compression, int level, char **compName, o *compName = "unknown"; } } - *csizePtr = csize; return (csize*100)/size; } @@ -310,7 +318,7 @@ int main(int argc, char **argv) { #ifdef ESPFS_GZIP if (gzipExtensions == NULL) { - parseGzipExtensions(strdup("html,css,js,ico")); + parseGzipExtensions(strdup("html,css,js")); } #endif @@ -334,6 +342,10 @@ int main(int argc, char **argv) { exit(0); } +#ifdef __WIN32__ + setmode(fileno(stdout), _O_BINARY); +#endif + while(fgets(fileName, sizeof(fileName), stdin)) { //Kill off '\n' at the end fileName[strlen(fileName)-1]=0; @@ -347,9 +359,8 @@ int main(int argc, char **argv) { f=open(fileName, O_RDONLY); if (f>0) { char *compName = "unknown"; - off_t csize; - rate=handleFile(f, realName, compType, compLvl, &compName, &csize); - fprintf(stderr, "%-16s (%3d%%, %s, %4u bytes)\n", realName, rate, compName, (uint32_t)csize); + rate=handleFile(f, realName, compType, compLvl, &compName); + fprintf(stderr, "%s (%d%%, %s)\n", realName, rate, compName); close(f); } else { perror(fileName); diff --git a/espfs/mkespfsimage/mman-win32/Makefile b/espfs/mkespfsimage/mman-win32/Makefile new file mode 100644 index 0000000..5624c1b --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/Makefile @@ -0,0 +1,48 @@ +# +# mman-win32 (mingw32) Makefile +# +include config.mak + +ifeq ($(BUILD_STATIC),yes) + TARGETS+=libmman.a + INSTALL+=static-install +endif +ifeq ($(BUILD_MSVC),yes) + SHFLAGS+=-Wl,--output-def,libmman.def + INSTALL+=lib-install +endif + +all: $(TARGETS) + +mman.o: mman.c mman.h + $(CC) -o mman.o -c mman.c -Wall -O3 -fomit-frame-pointer + +libmman.a: mman.o + $(AR) cru libmman.a mman.o + $(RANLIB) libmman.a + +static-install: + mkdir -p $(DESTDIR)$(libdir) + cp libmman.a $(DESTDIR)$(libdir) + mkdir -p $(DESTDIR)$(incdir) + cp mman.h $(DESTDIR)$(incdir) + +lib-install: + mkdir -p $(DESTDIR)$(libdir) + cp libmman.lib $(DESTDIR)$(libdir) + +install: $(INSTALL) + +test.exe: test.c mman.c mman.h + $(CC) -o test.exe test.c -L. -lmman + +test: $(TARGETS) test.exe + test.exe + +clean:: + rm -f mman.o libmman.a libmman.def libmman.lib test.exe *.dat + +distclean: clean + rm -f config.mak + +.PHONY: clean distclean install test diff --git a/espfs/mkespfsimage/mman-win32/config.mak b/espfs/mkespfsimage/mman-win32/config.mak new file mode 100644 index 0000000..2d2ae1b --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/config.mak @@ -0,0 +1,11 @@ +# Automatically generated by configure +PREFIX=/mingw +libdir=/mingw/lib +incdir=/mingw/include/sys +AR=ar +CC=gcc +RANLIB=ranlib +STRIP=strip +BUILD_STATIC=yes +BUILD_MSVC= +LIBCMD=echo ignoring lib diff --git a/espfs/mkespfsimage/mman-win32/configure b/espfs/mkespfsimage/mman-win32/configure new file mode 100644 index 0000000..c928f11 --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/configure @@ -0,0 +1,157 @@ +#!/bin/sh +# mmap-win32 configure script +# +# Parts copied from FFmpeg's configure +# + +set_all(){ + value=$1 + shift + for var in $*; do + eval $var=$value + done +} + +enable(){ + set_all yes $* +} + +disable(){ + set_all no $* +} + +enabled(){ + eval test "x\$$1" = "xyes" +} + +disabled(){ + eval test "x\$$1" = "xno" +} + +show_help(){ + echo "Usage: configure [options]" + echo "Options: [defaults in brackets after descriptions]" + echo "All \"enable\" options have \"disable\" counterparts" + echo + echo " --help print this message" + echo " --prefix=PREFIX install in PREFIX [$PREFIX]" + echo " --libdir=DIR install libs in DIR [$PREFIX/lib]" + echo " --incdir=DIR install includes in DIR [$PREFIX/include]" + echo " --enable-static build static libraries [yes]" + echo " --enable-msvc create msvc-compatible import lib [auto]" + echo + echo " --cc=CC use C compiler CC [$cc_default]" + echo " --cross-prefix=PREFIX use PREFIX for compilation tools [$cross_prefix]" + exit 1 +} + +die_unknown(){ + echo "Unknown option \"$1\"." + echo "See $0 --help for available options." + exit 1 +} + +PREFIX="/mingw" +libdir="${PREFIX}/lib" +incdir="${PREFIX}/include/sys" +ar="ar" +cc_default="gcc" +ranlib="ranlib" +strip="strip" + +DEFAULT="msvc +" + +DEFAULT_YES="static + stripping +" + +CMDLINE_SELECT="$DEFAULT + $DEFAULT_NO + $DEFAULT_YES +" + +enable $DEFAULT_YES +disable $DEFAULT_NO + +for opt do + optval="${opt#*=}" + case "$opt" in + --help) + show_help + ;; + --prefix=*) + PREFIX="$optval" + ;; + --libdir=*) + libdir="$optval" + ;; + --incdir=*) + incdir="$optval" + ;; + --cc=*) + cc="$optval" + ;; + --cross-prefix=*) + cross_prefix="$optval" + ;; + --enable-?*|--disable-?*) + eval `echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g'` + echo "$CMDLINE_SELECT" | grep -q "^ *$option\$" || die_unknown $opt + $action $option + ;; + *) + die_unknown $opt + ;; + esac +done + +ar="${cross_prefix}${ar}" +cc_default="${cross_prefix}${cc_default}" +ranlib="${cross_prefix}${ranlib}" +strip="${cross_prefix}${strip}" + +if ! test -z $cc; then + cc_default="${cc}" +fi +cc="${cc_default}" + +disabled static && { + echo "At least one library type must be set."; + exit 1; +} + +if enabled msvc; then + lib /? > /dev/null 2>&1 /dev/null || { + echo "MSVC's lib command not found." + echo "Make sure MSVC is installed and its bin folder is in your \$PATH." + exit 1 + } +fi + +if ! enabled stripping; then + strip="echo ignoring strip" +fi + +enabled msvc && libcmd="lib" || libcmd="echo ignoring lib" + +echo "# Automatically generated by configure" > config.mak +echo "PREFIX=$PREFIX" >> config.mak +echo "libdir=$libdir" >> config.mak +echo "incdir=$incdir" >> config.mak +echo "AR=$ar" >> config.mak +echo "CC=$cc" >> config.mak +echo "RANLIB=$ranlib" >> config.mak +echo "STRIP=$strip" >> config.mak +echo "BUILD_STATIC=$static" >> config.mak +echo "BUILD_MSVC=$msvc" >> config.mak +echo "LIBCMD=$libcmd" >> config.mak + +echo "prefix: $PREFIX" +echo "libdir: $libdir" +echo "incdir: $incdir" +echo "ar: $ar" +echo "cc: $cc" +echo "ranlib: $ranlib" +echo "strip: $strip" +echo "static: $static" diff --git a/espfs/mkespfsimage/mman-win32/mman.c b/espfs/mkespfsimage/mman-win32/mman.c new file mode 100644 index 0000000..486ed94 --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/mman.c @@ -0,0 +1,180 @@ + +#include +#include +#include + +#include "mman.h" + +#ifndef FILE_MAP_EXECUTE +#define FILE_MAP_EXECUTE 0x0020 +#endif /* FILE_MAP_EXECUTE */ + +static int __map_mman_error(const DWORD err, const int deferr) +{ + if (err == 0) + return 0; + //TODO: implement + return err; +} + +static DWORD __map_mmap_prot_page(const int prot) +{ + DWORD protect = 0; + + if (prot == PROT_NONE) + return protect; + + if ((prot & PROT_EXEC) != 0) + { + protect = ((prot & PROT_WRITE) != 0) ? + PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; + } + else + { + protect = ((prot & PROT_WRITE) != 0) ? + PAGE_READWRITE : PAGE_READONLY; + } + + return protect; +} + +static DWORD __map_mmap_prot_file(const int prot) +{ + DWORD desiredAccess = 0; + + if (prot == PROT_NONE) + return desiredAccess; + + if ((prot & PROT_READ) != 0) + desiredAccess |= FILE_MAP_READ; + if ((prot & PROT_WRITE) != 0) + desiredAccess |= FILE_MAP_WRITE; + if ((prot & PROT_EXEC) != 0) + desiredAccess |= FILE_MAP_EXECUTE; + + return desiredAccess; +} + +void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) +{ + HANDLE fm, h; + + void * map = MAP_FAILED; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4293) +#endif + + const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)off : (DWORD)(off & 0xFFFFFFFFL); + const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL); + const DWORD protect = __map_mmap_prot_page(prot); + const DWORD desiredAccess = __map_mmap_prot_file(prot); + + const off_t maxSize = off + (off_t)len; + + const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL); + const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + errno = 0; + + if (len == 0 + /* Unsupported flag combinations */ + || (flags & MAP_FIXED) != 0 + /* Usupported protection combinations */ + || prot == PROT_EXEC) + { + errno = EINVAL; + return MAP_FAILED; + } + + h = ((flags & MAP_ANONYMOUS) == 0) ? + (HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE; + + if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return MAP_FAILED; + } + + fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL); + + if (fm == NULL) + { + errno = __map_mman_error(GetLastError(), EPERM); + return MAP_FAILED; + } + + map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len); + + CloseHandle(fm); + + if (map == NULL) + { + errno = __map_mman_error(GetLastError(), EPERM); + return MAP_FAILED; + } + + return map; +} + +int munmap(void *addr, size_t len) +{ + if (UnmapViewOfFile(addr)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int mprotect(void *addr, size_t len, int prot) +{ + DWORD newProtect = __map_mmap_prot_page(prot); + DWORD oldProtect = 0; + + if (VirtualProtect(addr, len, newProtect, &oldProtect)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int msync(void *addr, size_t len, int flags) +{ + if (FlushViewOfFile(addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int mlock(const void *addr, size_t len) +{ + if (VirtualLock((LPVOID)addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int munlock(const void *addr, size_t len) +{ + if (VirtualUnlock((LPVOID)addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} diff --git a/espfs/mkespfsimage/mman-win32/mman.h b/espfs/mkespfsimage/mman-win32/mman.h new file mode 100644 index 0000000..ffa3748 --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/mman.h @@ -0,0 +1,55 @@ +/* + * sys/mman.h + * mman-win32 + */ + +#ifndef _SYS_MMAN_H_ +#define _SYS_MMAN_H_ + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +/* All the headers include this file. */ +#ifndef _MSC_VER +#include <_mingw.h> +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_TYPE 0xf +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS + +#define MAP_FAILED ((void *)-1) + +/* Flags for msync. */ +#define MS_ASYNC 1 +#define MS_SYNC 2 +#define MS_INVALIDATE 4 + +void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); +int munmap(void *addr, size_t len); +int mprotect(void *addr, size_t len, int prot); +int msync(void *addr, size_t len, int flags); +int mlock(const void *addr, size_t len); +int munlock(const void *addr, size_t len); + +#ifdef __cplusplus +}; +#endif + +#endif /* _SYS_MMAN_H_ */ diff --git a/espfs/mkespfsimage/mman-win32/test.c b/espfs/mkespfsimage/mman-win32/test.c new file mode 100644 index 0000000..9374b9f --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/test.c @@ -0,0 +1,235 @@ + +#include "mman.h" + +#include +#include +#include + +#ifndef NULL +#define NULL (void*)0 +#endif + +const char* map_file_name = "map_file.dat"; + +int test_anon_map_readwrite() +{ + void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\n", errno); + + return result; +} + +int test_anon_map_readonly() +{ + void* map = mmap(NULL, 1024, PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + + return result; +} + +int test_anon_map_writeonly() +{ + void* map = mmap(NULL, 1024, PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\n", errno); + + return result; +} + +int test_anon_map_readonly_nowrite() +{ + void* map = mmap(NULL, 1024, PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + return -1; + } + + if (*((unsigned char*)map) != 0) + printf("test_anon_map_readonly_nowrite (MAP_ANONYMOUS, PROT_READ) returned unexpected value: %d\n", + (int)*((unsigned char*)map)); + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + + return result; +} + +int test_file_map_readwrite() +{ + mode_t mode = S_IRUSR | S_IWUSR; + int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); + + void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); + if (map == MAP_FAILED) + { + printf("mmap returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap returned unexpected error: %d\n", errno); + + close(o); + + /*TODO: get file info and content and compare it with the sources conditions */ + unlink(map_file_name); + + return result; +} + +int test_file_map_mlock_munlock() +{ + const size_t map_size = 1024; + + int result = 0; + mode_t mode = S_IRUSR | S_IWUSR; + int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); + if (o == -1) + { + printf("unable to create file %s: %d\n", map_file_name, errno); + return -1; + } + + void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); + if (map == MAP_FAILED) + { + printf("mmap returned unexpected error: %d\n", errno); + result = -1; + goto done_close; + } + + if (mlock(map, map_size) != 0) + { + printf("mlock returned unexpected error: %d\n", errno); + result = -1; + goto done_munmap; + } + + *((unsigned char*)map) = 1; + + if (munlock(map, map_size) != 0) + { + printf("munlock returned unexpected error: %d\n", errno); + result = -1; + } + +done_munmap: + result = munmap(map, map_size); + + if (result != 0) + printf("munmap returned unexpected error: %d\n", errno); + +done_close: + close(o); + + unlink(map_file_name); +done: + return result; +} + +int test_file_map_msync() +{ + const size_t map_size = 1024; + + int result = 0; + mode_t mode = S_IRUSR | S_IWUSR; + int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); + if (o == -1) + { + printf("unable to create file %s: %d\n", map_file_name, errno); + return -1; + } + + void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); + if (map == MAP_FAILED) + { + printf("mmap returned unexpected error: %d\n", errno); + result = -1; + goto done_close; + } + + *((unsigned char*)map) = 1; + + if (msync(map, map_size, MS_SYNC) != 0) + { + printf("msync returned unexpected error: %d\n", errno); + result = -1; + } + + result = munmap(map, map_size); + + if (result != 0) + printf("munmap returned unexpected error: %d\n", errno); + +done_close: + close(o); + + unlink(map_file_name); +done: + return result; +} + +#define EXEC_TEST(name) \ + if (name() != 0) { result = -1; printf( #name ": fail\n"); } \ + else { printf(#name ": pass\n"); } + +int main() +{ + int result = 0; + + EXEC_TEST(test_anon_map_readwrite); + //NOTE: this test must cause an access violation exception + //EXEC_TEST(test_anon_map_readonly); + EXEC_TEST(test_anon_map_readonly_nowrite); + EXEC_TEST(test_anon_map_writeonly); + + EXEC_TEST(test_file_map_readwrite); + EXEC_TEST(test_file_map_mlock_munlock); + EXEC_TEST(test_file_map_msync); + //TODO: EXEC_TEST(test_file_map_mprotect); + + return result; +} diff --git a/espmake.cmd b/espmake.cmd new file mode 100644 index 0000000..42e25a6 --- /dev/null +++ b/espmake.cmd @@ -0,0 +1,7 @@ +@echo off + +REM remove automatic created obj folder +rd obj /S /Q >nul 2>&1 + +PATH=%PATH%;C:\Espressif\xtensa-lx106-elf\bin;C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\espressif\git-bin;C:\espressif\java-bin;C:\Python27 +make -f Makefile %1 %2 %3 %4 %5 \ No newline at end of file From 0eabc5d468c2c00064982bc450d15add9b16f382 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 21 Aug 2015 22:40:49 -0700 Subject: [PATCH 031/104] removed heatshrink submodule --- .gitmodules | 3 --- 1 file changed, 3 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 From 6867989b477da5fc5fdd652e9aea131b95f5d3b8 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 21 Aug 2015 23:10:41 -0700 Subject: [PATCH 032/104] 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 3d2ac525e3b594a27dc256077f6b1d748694b081 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 22 Aug 2015 05:51:46 -0500 Subject: [PATCH 033/104] Removed heatshrink and modified Makefiles --- Makefile | 17 +- esp-link.vcxproj | 62 +- espfs/espfs.c | 83 +- espfs/espfs.h | 4 - espfs/heatshrink/LICENSE | 14 - espfs/heatshrink/Makefile | 49 - espfs/heatshrink/README.md | 49 - espfs/heatshrink/dec_sm.dot | 52 - espfs/heatshrink/enc_sm.dot | 51 - espfs/heatshrink/greatest.h | 591 ----------- espfs/heatshrink/heatshrink.c | 446 -------- espfs/heatshrink/heatshrink_common.h | 20 - espfs/heatshrink/heatshrink_config.h | 24 - espfs/heatshrink/heatshrink_decoder.c | 382 ------- espfs/heatshrink/heatshrink_decoder.h | 101 -- espfs/heatshrink/heatshrink_encoder.c | 650 ------------ espfs/heatshrink/heatshrink_encoder.h | 109 -- espfs/heatshrink/test_heatshrink_dynamic.c | 999 ------------------ .../test_heatshrink_dynamic_theft.c | 521 --------- espfs/heatshrink/test_heatshrink_static.c | 167 --- espfs/heatshrink_config_custom.h | 30 - espfs/heatshrink_decoder.c | 19 - espfs/mkespfsimage/Makefile | 40 +- espfs/mkespfsimage/Makefile.linux | 24 - espfs/mkespfsimage/Makefile.windows | 33 - espfs/mkespfsimage/build-linux.sh | 4 - espfs/mkespfsimage/build-win32.sh | 7 - espfs/mkespfsimage/espfsformat.h | 33 - espfs/mkespfsimage/heatshrink_encoder.c | 4 - espfs/mkespfsimage/main.c | 87 +- include/esp8266.h | 5 +- include/espmissingincludes.h | 10 +- 32 files changed, 50 insertions(+), 4637 deletions(-) delete mode 100644 espfs/heatshrink/LICENSE delete mode 100644 espfs/heatshrink/Makefile delete mode 100644 espfs/heatshrink/README.md delete mode 100644 espfs/heatshrink/dec_sm.dot delete mode 100644 espfs/heatshrink/enc_sm.dot delete mode 100644 espfs/heatshrink/greatest.h delete mode 100644 espfs/heatshrink/heatshrink.c delete mode 100644 espfs/heatshrink/heatshrink_common.h delete mode 100644 espfs/heatshrink/heatshrink_config.h delete mode 100644 espfs/heatshrink/heatshrink_decoder.c delete mode 100644 espfs/heatshrink/heatshrink_decoder.h delete mode 100644 espfs/heatshrink/heatshrink_encoder.c delete mode 100644 espfs/heatshrink/heatshrink_encoder.h delete mode 100644 espfs/heatshrink/test_heatshrink_dynamic.c delete mode 100644 espfs/heatshrink/test_heatshrink_dynamic_theft.c delete mode 100644 espfs/heatshrink/test_heatshrink_static.c delete mode 100644 espfs/heatshrink_config_custom.h delete mode 100644 espfs/heatshrink_decoder.c delete mode 100644 espfs/mkespfsimage/Makefile.linux delete mode 100644 espfs/mkespfsimage/Makefile.windows delete mode 100644 espfs/mkespfsimage/build-linux.sh delete mode 100644 espfs/mkespfsimage/build-win32.sh delete mode 100644 espfs/mkespfsimage/espfsformat.h delete mode 100644 espfs/mkespfsimage/heatshrink_encoder.c diff --git a/Makefile b/Makefile index 297e4fe..af5fcbf 100644 --- a/Makefile +++ b/Makefile @@ -108,7 +108,6 @@ CHANGE_TO_STA ?= yes # This could speed up the downloading of these files, but might break compatibility with older # web browsers not supporting gzip encoding because Accept-Encoding is simply ignored. # Enable this option if you have large static files to serve (for e.g. JQuery, Twitter bootstrap) -# By default only js, css and html files are compressed using heatshrink. # If you have text based static files with different extensions what you want to serve compressed # then you will need to add the extension to the following places: # - Add the extension to this Makefile at the webpages.espfs target to the find command @@ -127,10 +126,6 @@ GZIP_COMPRESSION ?= yes COMPRESS_W_YUI ?= yes YUI-COMPRESSOR ?= yuicompressor-2.4.8.jar -# If USE_HEATSHRINK is set to "yes" then the espfs files will be compressed with Heatshrink and -# decompressed on the fly while reading the file. -# Because the decompression is done in the esp8266, it does not require any support in the browser. -USE_HEATSHRINK ?= no # -------------- End of config options ------------- @@ -147,7 +142,7 @@ APPGEN_TOOL ?= gen_appbin.py # which modules (subdirectories) of the project to include in compiling MODULES = espfs httpd user serial -EXTRA_INCDIR = include . # lib/heatshrink/ +EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK LIBS = c gcc hal phy pp net80211 wpa main lwip @@ -216,10 +211,6 @@ ifeq ("$(GZIP_COMPRESSION)","yes") CFLAGS += -DGZIP_COMPRESSION endif -ifeq ("$(USE_HEATSHRINK)","yes") -CFLAGS += -DESPFS_HEATSHRINK -endif - ifeq ("$(CHANGE_TO_STA)","yes") CFLAGS += -DCHANGE_TO_STA endif @@ -299,7 +290,11 @@ flash: all yui/$(YUI-COMPRESSOR): $(Q) mkdir -p yui + ifeq ($(OS),Windows_NT) cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) + else + cd yui; wget https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) + endif ifeq ("$(COMPRESS_W_YUI)","yes") $(BUILD_BASE)/espfs_img.o: yui/$(YUI-COMPRESSOR) @@ -351,7 +346,7 @@ build/eagle.esphttpd2.v6.ld: $(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)" + $(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)" release: all $(Q) rm -rf release; mkdir -p release/esp-link diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 45dd2ae..d462b62 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\mqtt\include;.\serial;.\user;.\espfs;.\httpd;.\include;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\x86_64-w64-mingw32;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\backward;c:\tools\mingw64\x86_64-w64-mingw32\include\c++;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include-fixed;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include;c:\Espressif\sdk\include + .\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\x86_64-w64-mingw32;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\backward;c:\tools\mingw64\x86_64-w64-mingw32\include\c++;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include-fixed;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include @@ -54,17 +54,10 @@ - - - - - - - @@ -72,8 +65,6 @@ - - @@ -81,35 +72,13 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -119,8 +88,6 @@ - - @@ -130,40 +97,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/espfs/espfs.c b/espfs/espfs.c index 1f503d5..128ca29 100644 --- a/espfs/espfs.c +++ b/espfs/espfs.c @@ -7,9 +7,9 @@ It's written for use with httpd, but doesn't need to be used as such. /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): - * Jeroen Domburg wrote this file. As long as you retain - * this notice you can do whatever you want with this stuff. If we meet some day, - * and you think this stuff is worth it, you can buy me a beer in return. + * Jeroen Domburg wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. * ---------------------------------------------------------------------------- */ @@ -40,14 +40,8 @@ It's written for use with httpd, but doesn't need to be used as such. #include "espfsformat.h" #include "espfs.h" -#ifdef ESPFS_HEATSHRINK -#include "heatshrink_config_custom.h" -#include "heatshrink/heatshrink_decoder.h" -#endif - static char* espFsData = NULL; - struct EspFsFile { EspFsHeader *header; char decompressor; @@ -147,19 +141,19 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { return NULL; } if (h.flags&FLAG_LASTFILE) { - os_printf("End of image.\n"); + //os_printf("End of image.\n"); return NULL; } //Grab the name of the file. - p+=sizeof(EspFsHeader); + p+=sizeof(EspFsHeader); os_memcpy(namebuf, p, sizeof(namebuf)); -// os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", +// os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", // namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags); if (os_strcmp(namebuf, fileName)==0) { //Yay, this is the file we need! p+=h.nameLen; //Skip to content. r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem -// os_printf("Alloc %p\n", r); + //os_printf("Alloc %p[%d]\n", r, sizeof(EspFsFile)); if (r==NULL) return NULL; r->header=(EspFsHeader *)hpos; r->decompressor=h.compression; @@ -168,18 +162,6 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { r->posDecomp=0; if (h.compression==COMPRESS_NONE) { r->decompData=NULL; -#ifdef ESPFS_HEATSHRINK - } else if (h.compression==COMPRESS_HEATSHRINK) { - //File is compressed with Heatshrink. - char parm; - heatshrink_decoder *dec; - //Decoder params are stored in 1st byte. - memcpyAligned(&parm, r->posComp, 1); - r->posComp++; - os_printf("Heatshrink compressed file; decode parms = %x\n", parm); - dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf); - r->decompData=dec; -#endif } else { os_printf("Invalid compression: %d\n", h.compression); return NULL; @@ -210,48 +192,6 @@ int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { fh->posComp+=len; // os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp); return len; -#ifdef ESPFS_HEATSHRINK - } else if (fh->decompressor==COMPRESS_HEATSHRINK) { - int decoded=0; - size_t elen, rlen; - char ebuff[16]; - heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData; -// os_printf("Alloc %p\n", dec); - if (fh->posDecomp == fdlen) { - return 0; - } - - // We must ensure that whole file is decompressed and written to output buffer. - // This means even when there is no input data (elen==0) try to poll decoder until - // posDecomp equals decompressed file length - - while(decodedposComp - fh->posStart); - if (elen>0) { - memcpyAligned(ebuff, fh->posComp, 16); - heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen>16)?16:elen, &rlen); - fh->posComp+=rlen; - } - //Grab decompressed data and put into buff - heatshrink_decoder_poll(dec, (uint8_t *)buff, len-decoded, &rlen); - fh->posDecomp+=rlen; - buff+=rlen; - decoded+=rlen; - -// os_printf("Elen %d rlen %d d %d pd %ld fdl %d\n",elen,rlen,decoded, fh->posDecomp, fdlen); - - if (elen == 0) { - if (fh->posDecomp == fdlen) { -// os_printf("Decoder finish\n"); - heatshrink_decoder_finish(dec); - } - return decoded; - } - } - return len; -#endif } return 0; } @@ -259,14 +199,7 @@ int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { //Close the file. void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) { if (fh==NULL) return; -#ifdef ESPFS_HEATSHRINK - if (fh->decompressor==COMPRESS_HEATSHRINK) { - heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData; - heatshrink_decoder_free(dec); -// os_printf("Freed %p\n", dec); - } -#endif -// os_printf("Freed %p\n", fh); + //os_printf("Freed %p\n", fh); os_free(fh); } diff --git a/espfs/espfs.h b/espfs/espfs.h index c41df0a..c8e13e7 100644 --- a/espfs/espfs.h +++ b/espfs/espfs.h @@ -1,10 +1,6 @@ #ifndef ESPFS_H #define ESPFS_H -// This define is done in Makefile. If you do not use default Makefile, uncomment -// to be able to use Heatshrink-compressed espfs images. -//#define ESPFS_HEATSHRINK - typedef enum { ESPFS_INIT_RESULT_OK, ESPFS_INIT_RESULT_NO_IMAGE, diff --git a/espfs/heatshrink/LICENSE b/espfs/heatshrink/LICENSE deleted file mode 100644 index 31ec3df..0000000 --- a/espfs/heatshrink/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2013, Scott Vokes -All rights reserved. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/espfs/heatshrink/Makefile b/espfs/heatshrink/Makefile deleted file mode 100644 index d8e6875..0000000 --- a/espfs/heatshrink/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -PROJECT = heatshrink -#OPTIMIZE = -O0 -#OPTIMIZE = -Os -OPTIMIZE = -O3 -WARN = -Wall -Wextra -pedantic #-Werror -CFLAGS += -std=c99 -g ${WARN} ${OPTIMIZE} -CFLAGS += -Wmissing-prototypes -CFLAGS += -Wstrict-prototypes -CFLAGS += -Wmissing-declarations - -# If libtheft is available, build additional property-based tests. -# Uncomment these to use it in test_heatshrink_dynamic. -#CFLAGS += -DHEATSHRINK_HAS_THEFT -#LDFLAGS += -ltheft - -all: - @echo "For tests, make test_heatshrink_dynamic (default) or change the" - @echo "config.h to disable static memory and build test_heatshrink_static." - @echo "For the standalone command-line tool, make heatshrink." - -${PROJECT}: heatshrink.c - -OBJS= heatshrink_encoder.o \ - heatshrink_decoder.o \ - -heatshrink: ${OBJS} -test_heatshrink_dynamic: ${OBJS} test_heatshrink_dynamic_theft.o -test_heatshrink_static: ${OBJS} - -*.o: Makefile heatshrink_config.h - -heatshrink_decoder.o: heatshrink_decoder.h heatshrink_common.h -heatshrink_encoder.o: heatshrink_encoder.h heatshrink_common.h - -tags: TAGS - -TAGS: - etags *.[ch] - -diagrams: dec_sm.png enc_sm.png - -dec_sm.png: dec_sm.dot - dot -o $@ -Tpng $< - -enc_sm.png: enc_sm.dot - dot -o $@ -Tpng $< - -clean: - rm -f ${PROJECT} test_heatshrink_{dynamic,static} *.o *.core {dec,enc}_sm.png TAGS diff --git a/espfs/heatshrink/README.md b/espfs/heatshrink/README.md deleted file mode 100644 index ab150ee..0000000 --- a/espfs/heatshrink/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# heatshrink - -A data compression/decompression library for embedded/real-time systems. - -## Key Features: - -- **Low memory usage (as low as 50 bytes)** - It is useful for some cases with less than 50 bytes, and useful - for many general cases with < 300 bytes. -- **Incremental, bounded CPU use** - You can chew on input data in arbitrarily tiny bites. - This is a useful property in hard real-time environments. -- **Can use either static or dynamic memory allocation** - The library doesn't impose any constraints on memory management. -- **ISC license** - You can use it freely, even for commercial purposes. - -## Getting Started: - -There is a standalone command-line program, `heatshrink`, but the -encoder and decoder can also be used as libraries, independent of each -other. To do so, copy `heatshrink_common.h`, `heatshrink_config.h`, and -either `heatshrink_encoder.c` or `heatshrink_decoder.c` (and their -respective header) into your project. - -Dynamic allocation is used by default, but in an embedded context, you -probably want to statically allocate the encoder/decoder. Set -`HEATSHRINK_DYNAMIC_ALLOC` to 0 in `heatshrink_config.h`. - -## More Information and Benchmarks: - -heatshrink is based on [LZSS], since it's particularly suitable for -compression in small amounts of memory. It can use an optional, small -[index] to make compression significantly faster, but otherwise can run -in under 100 bytes of memory. The index currently adds 2^(window size+1) -bytes to memory usage for compression, and temporarily allocates 512 -bytes on the stack during index construction. - -For more information, see the [blog post] for an overview, and the -`heatshrink_encoder.h` / `heatshrink_decoder.h` header files for API -documentation. - -[blog post]: http://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ -[index]: http://spin.atomicobject.com/2014/01/13/lightweight-indexing-for-embedded-systems/ -[LZSS]: http://en.wikipedia.org/wiki/Lempel-Ziv-Storer-Szymanski - -## Build Status - - [![Build Status](https://travis-ci.org/atomicobject/heatshrink.png)](http://travis-ci.org/atomicobject/heatshrink) diff --git a/espfs/heatshrink/dec_sm.dot b/espfs/heatshrink/dec_sm.dot deleted file mode 100644 index 470012f..0000000 --- a/espfs/heatshrink/dec_sm.dot +++ /dev/null @@ -1,52 +0,0 @@ -digraph { - graph [label="Decoder state machine", labelloc="t"] - Start [style="invis", shape="point"] - empty - input_available - yield_literal - backref_index_msb - backref_index_lsb - backref_count_msb - backref_count_lsb - yield_backref - check_for_more_input - done [peripheries=2] - - empty->input_available [label="sink()", color="blue", weight=10] - Start->empty - - input_available->yield_literal [label="pop 1-bit"] - input_available->backref_index_msb [label="pop 0-bit", weight=10] - input_available->backref_index_lsb [label="pop 0-bit, index <8 bits", weight=10] - - yield_literal->yield_literal [label="sink()", color="blue"] - yield_literal->yield_literal [label="poll()", color="red"] - yield_literal->check_for_more_input [label="poll(), done", color="red"] - - backref_index_msb->backref_index_msb [label="sink()", color="blue"] - backref_index_msb->backref_index_lsb [label="pop index, upper bits", weight=10] - backref_index_msb->done [label="finish()", color="blue"] - - backref_index_lsb->backref_index_lsb [label="sink()", color="blue"] - backref_index_lsb->backref_count_msb [label="pop index, lower bits", weight=10] - backref_index_lsb->backref_count_lsb [label="pop index, count <=8 bits", weight=10] - backref_index_lsb->done [label="finish()", color="blue"] - - backref_count_msb->backref_count_msb [label="sink()", color="blue"] - backref_count_msb->backref_count_lsb [label="pop count, upper bits", weight=10] - backref_count_msb->done [label="finish()", color="blue"] - - backref_count_lsb->backref_count_lsb [label="sink()", color="blue"] - backref_count_lsb->yield_backref [label="pop count, lower bits", weight=10] - backref_count_lsb->done [label="finish()", color="blue"] - - yield_backref->yield_backref [label="sink()", color="blue"] - yield_backref->yield_backref [label="poll()", color="red"] - yield_backref->check_for_more_input [label="poll(), done", - color="red", weight=10] - - check_for_more_input->empty [label="no"] - check_for_more_input->input_available [label="yes"] - - empty->done [label="finish()", color="blue"] -} diff --git a/espfs/heatshrink/enc_sm.dot b/espfs/heatshrink/enc_sm.dot deleted file mode 100644 index 6d3030f..0000000 --- a/espfs/heatshrink/enc_sm.dot +++ /dev/null @@ -1,51 +0,0 @@ -digraph { - graph [label="Encoder state machine", labelloc="t"] - start [style="invis", shape="point"] - not_full - filled - search - yield_tag_bit - yield_literal - yield_br_length - yield_br_index - save_backlog - flush_bits - done [peripheries=2] - - start->not_full [label="start"] - - not_full->not_full [label="sink(), not full", color="blue"] - not_full->filled [label="sink(), buffer is full", color="blue"] - not_full->filled [label="finish(), set is_finished", color="blue"] - - filled->search [label="indexing (if any)"] - - search->search [label="step"] - search->yield_tag_bit [label="literal"] - search->yield_tag_bit [label="match found"] - search->save_backlog [label="input exhausted"] - - yield_tag_bit->yield_tag_bit [label="poll(), full buf", color="red"] - yield_tag_bit->yield_literal [label="poll(), literal", color="red"] - yield_tag_bit->yield_br_index [label="poll(), no literal", color="red"] - yield_tag_bit->flush_bits [label="finishing, no literal"] - - yield_literal->yield_literal [label="poll(), full buf", color="red"] - yield_literal->search [label="poll(), no match", color="red"] - yield_literal->yield_tag_bit [label="poll(), match", color="red"] - yield_literal->flush_bits [label="poll(), final literal", color="red"] - - yield_br_index->yield_br_index [label="poll(), full buf", color="red"] - yield_br_index->yield_br_length [label="poll()", color="red"] - - yield_br_length->yield_br_length [label="poll(), full buf", color="red"] - yield_br_length->search [label="done"] - - save_backlog->flush_bits [label="finishing, no literal"] - save_backlog->yield_tag_bit [label="finishing, literal"] - save_backlog->not_full [label="expect more input"] - - flush_bits->flush_bits [label="poll(), full buf", color="red"] - flush_bits->done [label="poll(), flushed", color="red"] - flush_bits->done [label="no more output"] -} diff --git a/espfs/heatshrink/greatest.h b/espfs/heatshrink/greatest.h deleted file mode 100644 index a92c642..0000000 --- a/espfs/heatshrink/greatest.h +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright (c) 2011 Scott Vokes - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef GREATEST_H -#define GREATEST_H - -#define GREATEST_VERSION_MAJOR 0 -#define GREATEST_VERSION_MINOR 9 -#define GREATEST_VERSION_PATCH 3 - -/* A unit testing system for C, contained in 1 file. - * It doesn't use dynamic allocation or depend on anything - * beyond ANSI C89. */ - - -/********************************************************************* - * Minimal test runner template - *********************************************************************/ -#if 0 - -#include "greatest.h" - -TEST foo_should_foo() { - PASS(); -} - -static void setup_cb(void *data) { - printf("setup callback for each test case\n"); -} - -static void teardown_cb(void *data) { - printf("teardown callback for each test case\n"); -} - -SUITE(suite) { - /* Optional setup/teardown callbacks which will be run before/after - * every test case in the suite. - * Cleared when the suite finishes. */ - SET_SETUP(setup_cb, voidp_to_callback_data); - SET_TEARDOWN(teardown_cb, voidp_to_callback_data); - - RUN_TEST(foo_should_foo); -} - -/* Add all the definitions that need to be in the test runner's main file. */ -GREATEST_MAIN_DEFS(); - -int main(int argc, char **argv) { - GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ - RUN_SUITE(suite); - GREATEST_MAIN_END(); /* display results */ -} - -#endif -/*********************************************************************/ - - -#include -#include -#include -#include - - -/*********** - * Options * - ***********/ - -/* Default column width for non-verbose output. */ -#ifndef GREATEST_DEFAULT_WIDTH -#define GREATEST_DEFAULT_WIDTH 72 -#endif - -/* FILE *, for test logging. */ -#ifndef GREATEST_STDOUT -#define GREATEST_STDOUT stdout -#endif - -/* Remove GREATEST_ prefix from most commonly used symbols? */ -#ifndef GREATEST_USE_ABBREVS -#define GREATEST_USE_ABBREVS 1 -#endif - - -/********* - * Types * - *********/ - -/* Info for the current running suite. */ -typedef struct greatest_suite_info { - unsigned int tests_run; - unsigned int passed; - unsigned int failed; - unsigned int skipped; - - /* timers, pre/post running suite and individual tests */ - clock_t pre_suite; - clock_t post_suite; - clock_t pre_test; - clock_t post_test; -} greatest_suite_info; - -/* Type for a suite function. */ -typedef void (greatest_suite_cb)(void); - -/* Types for setup/teardown callbacks. If non-NULL, these will be run - * and passed the pointer to their additional data. */ -typedef void (greatest_setup_cb)(void *udata); -typedef void (greatest_teardown_cb)(void *udata); - -typedef enum { - GREATEST_FLAG_VERBOSE = 0x01, - GREATEST_FLAG_FIRST_FAIL = 0x02, - GREATEST_FLAG_LIST_ONLY = 0x04 -} GREATEST_FLAG; - -typedef struct greatest_run_info { - unsigned int flags; - unsigned int tests_run; /* total test count */ - - /* Overall pass/fail/skip counts. */ - unsigned int passed; - unsigned int failed; - unsigned int skipped; - - /* currently running test suite */ - greatest_suite_info suite; - - /* info to print about the most recent failure */ - const char *fail_file; - unsigned int fail_line; - const char *msg; - - /* current setup/teardown hooks and userdata */ - greatest_setup_cb *setup; - void *setup_udata; - greatest_teardown_cb *teardown; - void *teardown_udata; - - /* formatting info for ".....s...F"-style output */ - unsigned int col; - unsigned int width; - - /* only run a specific suite or test */ - char *suite_filter; - char *test_filter; - - /* overall timers */ - clock_t begin; - clock_t end; -} greatest_run_info; - -/* Global var for the current testing context. - * Initialized by GREATEST_MAIN_DEFS(). */ -extern greatest_run_info greatest_info; - - -/********************** - * Exported functions * - **********************/ - -void greatest_do_pass(const char *name); -void greatest_do_fail(const char *name); -void greatest_do_skip(const char *name); -int greatest_pre_test(const char *name); -void greatest_post_test(const char *name, int res); -void greatest_usage(const char *name); -void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); -void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); - - -/********** - * Macros * - **********/ - -/* Define a suite. */ -#define GREATEST_SUITE(NAME) void NAME(void) - -/* Start defining a test function. - * The arguments are not included, to allow parametric testing. */ -#define GREATEST_TEST static int - -/* Run a suite. */ -#define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) - -/* Run a test in the current suite. */ -#define GREATEST_RUN_TEST(TEST) \ - do { \ - if (greatest_pre_test(#TEST) == 1) { \ - int res = TEST(); \ - greatest_post_test(#TEST, res); \ - } else if (GREATEST_LIST_ONLY()) { \ - fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ - } \ - } while (0) - -/* Run a test in the current suite with one void* argument, - * which can be a pointer to a struct with multiple arguments. */ -#define GREATEST_RUN_TEST1(TEST, ENV) \ - do { \ - if (greatest_pre_test(#TEST) == 1) { \ - int res = TEST(ENV); \ - greatest_post_test(#TEST, res); \ - } else if (GREATEST_LIST_ONLY()) { \ - fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ - } \ - } while (0) - -/* If __VA_ARGS__ (C99) is supported, allow parametric testing - * without needing to manually manage the argument struct. */ -#if __STDC_VERSION__ >= 19901L -#define GREATEST_RUN_TESTp(TEST, ...) \ - do { \ - if (greatest_pre_test(#TEST) == 1) { \ - int res = TEST(__VA_ARGS__); \ - greatest_post_test(#TEST, res); \ - } else if (GREATEST_LIST_ONLY()) { \ - fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ - } \ - } while (0) -#endif - - -/* Check if the test runner is in verbose mode. */ -#define GREATEST_IS_VERBOSE() (greatest_info.flags & GREATEST_FLAG_VERBOSE) -#define GREATEST_LIST_ONLY() (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) -#define GREATEST_FIRST_FAIL() (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) -#define GREATEST_FAILURE_ABORT() (greatest_info.suite.failed > 0 && GREATEST_FIRST_FAIL()) - -/* Message-less forms. */ -#define GREATEST_PASS() GREATEST_PASSm(NULL) -#define GREATEST_FAIL() GREATEST_FAILm(NULL) -#define GREATEST_SKIP() GREATEST_SKIPm(NULL) -#define GREATEST_ASSERT(COND) GREATEST_ASSERTm(#COND, COND) -#define GREATEST_ASSERT_FALSE(COND) GREATEST_ASSERT_FALSEm(#COND, COND) -#define GREATEST_ASSERT_EQ(EXP, GOT) GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) -#define GREATEST_ASSERT_STR_EQ(EXP, GOT) GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) - -/* The following forms take an additional message argument first, - * to be displayed by the test runner. */ - -/* Fail if a condition is not true, with message. */ -#define GREATEST_ASSERTm(MSG, COND) \ - do { \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if (!(COND)) return -1; \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_ASSERT_FALSEm(MSG, COND) \ - do { \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if ((COND)) return -1; \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \ - do { \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if ((EXP) != (GOT)) return -1; \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ - do { \ - const char *exp_s = (EXP); \ - const char *got_s = (GOT); \ - greatest_info.msg = MSG; \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - if (0 != strcmp(exp_s, got_s)) { \ - fprintf(GREATEST_STDOUT, \ - "Expected:\n####\n%s\n####\n", exp_s); \ - fprintf(GREATEST_STDOUT, \ - "Got:\n####\n%s\n####\n", got_s); \ - return -1; \ - } \ - greatest_info.msg = NULL; \ - } while (0) - -#define GREATEST_PASSm(MSG) \ - do { \ - greatest_info.msg = MSG; \ - return 0; \ - } while (0) - -#define GREATEST_FAILm(MSG) \ - do { \ - greatest_info.fail_file = __FILE__; \ - greatest_info.fail_line = __LINE__; \ - greatest_info.msg = MSG; \ - return -1; \ - } while (0) - -#define GREATEST_SKIPm(MSG) \ - do { \ - greatest_info.msg = MSG; \ - return 1; \ - } while (0) - -#define GREATEST_SET_TIME(NAME) \ - NAME = clock(); \ - if (NAME == (clock_t) -1) { \ - fprintf(GREATEST_STDOUT, \ - "clock error: %s\n", #NAME); \ - exit(EXIT_FAILURE); \ - } - -#define GREATEST_CLOCK_DIFF(C1, C2) \ - fprintf(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ - (long unsigned int) (C2) - (C1), \ - (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) \ - -/* Include several function definitions in the main test file. */ -#define GREATEST_MAIN_DEFS() \ - \ -/* Is FILTER a subset of NAME? */ \ -static int greatest_name_match(const char *name, \ - const char *filter) { \ - size_t offset = 0; \ - size_t filter_len = strlen(filter); \ - while (name[offset] != '\0') { \ - if (name[offset] == filter[0]) { \ - if (0 == strncmp(&name[offset], filter, filter_len)) { \ - return 1; \ - } \ - } \ - offset++; \ - } \ - \ - return 0; \ -} \ - \ -int greatest_pre_test(const char *name) { \ - if (!GREATEST_LIST_ONLY() \ - && (!GREATEST_FIRST_FAIL() || greatest_info.suite.failed == 0) \ - && (greatest_info.test_filter == NULL || \ - greatest_name_match(name, greatest_info.test_filter))) { \ - GREATEST_SET_TIME(greatest_info.suite.pre_test); \ - if (greatest_info.setup) { \ - greatest_info.setup(greatest_info.setup_udata); \ - } \ - return 1; /* test should be run */ \ - } else { \ - return 0; /* skipped */ \ - } \ -} \ - \ -void greatest_post_test(const char *name, int res) { \ - GREATEST_SET_TIME(greatest_info.suite.post_test); \ - if (greatest_info.teardown) { \ - void *udata = greatest_info.teardown_udata; \ - greatest_info.teardown(udata); \ - } \ - \ - if (res < 0) { \ - greatest_do_fail(name); \ - } else if (res > 0) { \ - greatest_do_skip(name); \ - } else if (res == 0) { \ - greatest_do_pass(name); \ - } \ - greatest_info.suite.tests_run++; \ - greatest_info.col++; \ - if (GREATEST_IS_VERBOSE()) { \ - GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ - greatest_info.suite.post_test); \ - fprintf(GREATEST_STDOUT, "\n"); \ - } else if (greatest_info.col % greatest_info.width == 0) { \ - fprintf(GREATEST_STDOUT, "\n"); \ - greatest_info.col = 0; \ - } \ - if (GREATEST_STDOUT == stdout) fflush(stdout); \ -} \ - \ -static void greatest_run_suite(greatest_suite_cb *suite_cb, \ - const char *suite_name) { \ - if (greatest_info.suite_filter && \ - !greatest_name_match(suite_name, greatest_info.suite_filter)) \ - return; \ - if (GREATEST_FIRST_FAIL() && greatest_info.failed > 0) return; \ - greatest_info.suite.tests_run = 0; \ - greatest_info.suite.failed = 0; \ - greatest_info.suite.passed = 0; \ - greatest_info.suite.skipped = 0; \ - greatest_info.suite.pre_suite = 0; \ - greatest_info.suite.post_suite = 0; \ - greatest_info.suite.pre_test = 0; \ - greatest_info.suite.post_test = 0; \ - greatest_info.col = 0; \ - fprintf(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ - GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ - suite_cb(); \ - GREATEST_SET_TIME(greatest_info.suite.post_suite); \ - if (greatest_info.suite.tests_run > 0) { \ - fprintf(GREATEST_STDOUT, \ - "\n%u tests - %u pass, %u fail, %u skipped", \ - greatest_info.suite.tests_run, \ - greatest_info.suite.passed, \ - greatest_info.suite.failed, \ - greatest_info.suite.skipped); \ - GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ - greatest_info.suite.post_suite); \ - fprintf(GREATEST_STDOUT, "\n"); \ - } \ - greatest_info.setup = NULL; \ - greatest_info.setup_udata = NULL; \ - greatest_info.teardown = NULL; \ - greatest_info.teardown_udata = NULL; \ - greatest_info.passed += greatest_info.suite.passed; \ - greatest_info.failed += greatest_info.suite.failed; \ - greatest_info.skipped += greatest_info.suite.skipped; \ - greatest_info.tests_run += greatest_info.suite.tests_run; \ -} \ - \ -void greatest_do_pass(const char *name) { \ - if (GREATEST_IS_VERBOSE()) { \ - fprintf(GREATEST_STDOUT, "PASS %s: %s", \ - name, greatest_info.msg ? greatest_info.msg : ""); \ - } else { \ - fprintf(GREATEST_STDOUT, "."); \ - } \ - greatest_info.suite.passed++; \ -} \ - \ -void greatest_do_fail(const char *name) { \ - if (GREATEST_IS_VERBOSE()) { \ - fprintf(GREATEST_STDOUT, \ - "FAIL %s: %s (%s:%u)", \ - name, greatest_info.msg ? greatest_info.msg : "", \ - greatest_info.fail_file, greatest_info.fail_line); \ - } else { \ - fprintf(GREATEST_STDOUT, "F"); \ - /* add linebreak if in line of '.'s */ \ - if (greatest_info.col % greatest_info.width != 0) \ - fprintf(GREATEST_STDOUT, "\n"); \ - greatest_info.col = 0; \ - fprintf(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ - name, \ - greatest_info.msg ? greatest_info.msg : "", \ - greatest_info.fail_file, greatest_info.fail_line); \ - } \ - greatest_info.suite.failed++; \ -} \ - \ -void greatest_do_skip(const char *name) { \ - if (GREATEST_IS_VERBOSE()) { \ - fprintf(GREATEST_STDOUT, "SKIP %s: %s", \ - name, \ - greatest_info.msg ? \ - greatest_info.msg : "" ); \ - } else { \ - fprintf(GREATEST_STDOUT, "s"); \ - } \ - greatest_info.suite.skipped++; \ -} \ - \ -void greatest_usage(const char *name) { \ - fprintf(GREATEST_STDOUT, \ - "Usage: %s [-hlfv] [-s SUITE] [-t TEST]\n" \ - " -h print this Help\n" \ - " -l List suites and their tests, then exit\n" \ - " -f Stop runner after first failure\n" \ - " -v Verbose output\n" \ - " -s SUITE only run suite named SUITE\n" \ - " -t TEST only run test named TEST\n", \ - name); \ -} \ - \ -void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ - greatest_info.setup = cb; \ - greatest_info.setup_udata = udata; \ -} \ - \ -void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \ - void *udata) { \ - greatest_info.teardown = cb; \ - greatest_info.teardown_udata = udata; \ -} \ - \ -greatest_run_info greatest_info - -/* Handle command-line arguments, etc. */ -#define GREATEST_MAIN_BEGIN() \ - do { \ - int i = 0; \ - memset(&greatest_info, 0, sizeof(greatest_info)); \ - if (greatest_info.width == 0) { \ - greatest_info.width = GREATEST_DEFAULT_WIDTH; \ - } \ - for (i = 1; i < argc; i++) { \ - if (0 == strcmp("-t", argv[i])) { \ - if (argc <= i + 1) { \ - greatest_usage(argv[0]); \ - exit(EXIT_FAILURE); \ - } \ - greatest_info.test_filter = argv[i+1]; \ - i++; \ - } else if (0 == strcmp("-s", argv[i])) { \ - if (argc <= i + 1) { \ - greatest_usage(argv[0]); \ - exit(EXIT_FAILURE); \ - } \ - greatest_info.suite_filter = argv[i+1]; \ - i++; \ - } else if (0 == strcmp("-f", argv[i])) { \ - greatest_info.flags |= GREATEST_FLAG_FIRST_FAIL; \ - } else if (0 == strcmp("-v", argv[i])) { \ - greatest_info.flags |= GREATEST_FLAG_VERBOSE; \ - } else if (0 == strcmp("-l", argv[i])) { \ - greatest_info.flags |= GREATEST_FLAG_LIST_ONLY; \ - } else if (0 == strcmp("-h", argv[i])) { \ - greatest_usage(argv[0]); \ - exit(EXIT_SUCCESS); \ - } else { \ - fprintf(GREATEST_STDOUT, \ - "Unknown argument '%s'\n", argv[i]); \ - greatest_usage(argv[0]); \ - exit(EXIT_FAILURE); \ - } \ - } \ - } while (0); \ - GREATEST_SET_TIME(greatest_info.begin) - -#define GREATEST_MAIN_END() \ - do { \ - if (!GREATEST_LIST_ONLY()) { \ - GREATEST_SET_TIME(greatest_info.end); \ - fprintf(GREATEST_STDOUT, \ - "\nTotal: %u tests", greatest_info.tests_run); \ - GREATEST_CLOCK_DIFF(greatest_info.begin, \ - greatest_info.end); \ - fprintf(GREATEST_STDOUT, "\n"); \ - fprintf(GREATEST_STDOUT, \ - "Pass: %u, fail: %u, skip: %u.\n", \ - greatest_info.passed, \ - greatest_info.failed, greatest_info.skipped); \ - } \ - return (greatest_info.failed > 0 \ - ? EXIT_FAILURE : EXIT_SUCCESS); \ - } while (0) - -/* Make abbreviations without the GREATEST_ prefix for the - * most commonly used symbols. */ -#if GREATEST_USE_ABBREVS -#define TEST GREATEST_TEST -#define SUITE GREATEST_SUITE -#define RUN_TEST GREATEST_RUN_TEST -#define RUN_TEST1 GREATEST_RUN_TEST1 -#define RUN_SUITE GREATEST_RUN_SUITE -#define ASSERT GREATEST_ASSERT -#define ASSERTm GREATEST_ASSERTm -#define ASSERT_FALSE GREATEST_ASSERT_FALSE -#define ASSERT_EQ GREATEST_ASSERT_EQ -#define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ -#define ASSERT_FALSEm GREATEST_ASSERT_FALSEm -#define ASSERT_EQm GREATEST_ASSERT_EQm -#define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm -#define PASS GREATEST_PASS -#define FAIL GREATEST_FAIL -#define SKIP GREATEST_SKIP -#define PASSm GREATEST_PASSm -#define FAILm GREATEST_FAILm -#define SKIPm GREATEST_SKIPm -#define SET_SETUP GREATEST_SET_SETUP_CB -#define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB - -#if __STDC_VERSION__ >= 19901L -#endif /* C99 */ -#define RUN_TESTp GREATEST_RUN_TESTp -#endif /* USE_ABBREVS */ - -#endif diff --git a/espfs/heatshrink/heatshrink.c b/espfs/heatshrink/heatshrink.c deleted file mode 100644 index 9de553f..0000000 --- a/espfs/heatshrink/heatshrink.c +++ /dev/null @@ -1,446 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" - -#define DEF_WINDOW_SZ2 11 -#define DEF_LOOKAHEAD_SZ2 4 -#define DEF_DECODER_INPUT_BUFFER_SIZE 256 -#define DEF_BUFFER_SIZE (64 * 1024) - -#if 0 -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#else -#define LOG(...) /* NO-OP */ -#endif - -static const int version_major = HEATSHRINK_VERSION_MAJOR; -static const int version_minor = HEATSHRINK_VERSION_MINOR; -static const int version_patch = HEATSHRINK_VERSION_PATCH; -static const char author[] = HEATSHRINK_AUTHOR; -static const char url[] = HEATSHRINK_URL; - -static void usage(void) { - fprintf(stderr, "heatshrink version %u.%u.%u by %s\n", - version_major, version_minor, version_patch, author); - fprintf(stderr, "Home page: %s\n\n", url); - fprintf(stderr, - "Usage:\n" - " heatshrink [-h] [-e|-d] [-v] [-w SIZE] [-l BITS] [IN_FILE] [OUT_FILE]\n" - "\n" - "heatshrink compresses or uncompresses byte streams using LZSS, and is\n" - "designed especially for embedded, low-memory, and/or hard real-time\n" - "systems.\n" - "\n" - " -h print help\n" - " -e encode (compress, default)\n" - " -d decode (uncompress)\n" - " -v verbose (print input & output sizes, compression ratio, etc.)\n" - "\n" - " -w SIZE Base-2 log of LZSS sliding window size\n" - "\n" - " A larger value allows searches a larger history of the data for repeated\n" - " patterns, potentially compressing more effectively, but will use\n" - " more memory and processing time.\n" - " Recommended default: -w 8 (embedded systems), -w 10 (elsewhere)\n" - " \n" - " -l BITS Number of bits used for back-reference lengths\n" - "\n" - " A larger value allows longer substitutions, but since all\n" - " back-references must use -w + -l bits, larger -w or -l can be\n" - " counterproductive if most patterns are small and/or local.\n" - " Recommended default: -l 4\n" - "\n" - " If IN_FILE or OUT_FILE are unspecified, they will default to\n" - " \"-\" for standard input and standard output, respectively.\n"); - exit(1); -} - -typedef enum { IO_READ, IO_WRITE, } IO_mode; -typedef enum { OP_ENC, OP_DEC, } Operation; - -typedef struct { - int fd; /* file descriptor */ - IO_mode mode; - size_t fill; /* fill index */ - size_t read; /* read index */ - size_t size; - size_t total; - uint8_t buf[]; -} io_handle; - -typedef struct { - uint8_t window_sz2; - uint8_t lookahead_sz2; - size_t decoder_input_buffer_size; - size_t buffer_size; - uint8_t verbose; - Operation cmd; - char *in_fname; - char *out_fname; - io_handle *in; - io_handle *out; -} config; - -static void die(char *msg) { - fprintf(stderr, "%s\n", msg); - exit(EXIT_FAILURE); -} - -static void report(config *cfg); - -/* Open an IO handle. Returns NULL on error. */ -static io_handle *handle_open(char *fname, IO_mode m, size_t buf_sz) { - io_handle *io = NULL; - io = malloc(sizeof(*io) + buf_sz); - if (io == NULL) { return NULL; } - memset(io, 0, sizeof(*io) + buf_sz); - io->fd = -1; - io->size = buf_sz; - io->mode = m; - - if (m == IO_READ) { - if (0 == strcmp("-", fname)) { - io->fd = STDIN_FILENO; - } else { - io->fd = open(fname, O_RDONLY); - } - } else if (m == IO_WRITE) { - if (0 == strcmp("-", fname)) { - io->fd = STDOUT_FILENO; - } else { - io->fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC /*| O_EXCL*/, 0644); - } - } - - if (io->fd == -1) { /* failed to open */ - free(io); - err(1, "open"); - return NULL; - } - - return io; -} - -/* Read SIZE bytes from an IO handle and return a pointer to the content. - * BUF contains at least size_t bytes. Returns 0 on EOF, -1 on error. */ -static ssize_t handle_read(io_handle *io, size_t size, uint8_t **buf) { - LOG("@ read %zd\n", size); - if (buf == NULL) { return -1; } - if (size > io->size) { - printf("size %zd, io->size %zd\n", size, io->size); - return -1; - } - if (io->mode != IO_READ) { return -1; } - - size_t rem = io->fill - io->read; - if (rem >= size) { - *buf = &io->buf[io->read]; - return size; - } else { /* read and replenish */ - if (io->fd == -1) { /* already closed, return what we've got */ - *buf = &io->buf[io->read]; - return rem; - } - - memmove(io->buf, &io->buf[io->read], rem); - io->fill -= io->read; - io->read = 0; - ssize_t read_sz = read(io->fd, &io->buf[io->fill], io->size - io->fill); - if (read_sz < 0) { err(1, "read"); } - io->total += read_sz; - if (read_sz == 0) { /* EOF */ - if (close(io->fd) < 0) { err(1, "close"); } - io->fd = -1; - } - io->fill += read_sz; - *buf = io->buf; - return io->fill > size ? size : io->fill; - } -} - -/* Drop the oldest SIZE bytes from the buffer. Returns <0 on error. */ -static int handle_drop(io_handle *io, size_t size) { - LOG("@ drop %zd\n", size); - if (io->read + size <= io->fill) { - io->read += size; - } else { - return -1; - } - if (io->read == io->fill) { - io->read = 0; - io->fill = 0; - } - return 0; -} - -/* Sink SIZE bytes from INPUT into the io handle. Returns the number of - * bytes written, or -1 on error. */ -static ssize_t handle_sink(io_handle *io, size_t size, uint8_t *input) { - LOG("@ sink %zd\n", size); - if (size > io->size) { return -1; } - if (io->mode != IO_WRITE) { return -1; } - - if (io->fill + size > io->size) { - ssize_t written = write(io->fd, io->buf, io->fill); - LOG("@ flushing %zd, wrote %zd\n", io->fill, written); - io->total += written; - if (written == -1) { err(1, "write"); } - memmove(io->buf, &io->buf[written], io->fill - written); - io->fill -= written; - } - memcpy(&io->buf[io->fill], input, size); - io->fill += size; - return size; -} - -static void handle_close(io_handle *io) { - if (io->fd != -1) { - if (io->mode == IO_WRITE) { - ssize_t written = write(io->fd, io->buf, io->fill); - io->total += written; - LOG("@ close: flushing %zd, wrote %zd\n", io->fill, written); - if (written == -1) { err(1, "write"); } - } - close(io->fd); - io->fd = -1; - } -} - -static void close_and_report(config *cfg) { - handle_close(cfg->in); - handle_close(cfg->out); - if (cfg->verbose) { report(cfg); } - free(cfg->in); - free(cfg->out); -} - -static int encoder_sink_read(config *cfg, heatshrink_encoder *hse, - uint8_t *data, size_t data_sz) { - size_t out_sz = 4096; - uint8_t out_buf[out_sz]; - memset(out_buf, 0, out_sz); - size_t sink_sz = 0; - size_t poll_sz = 0; - HSE_sink_res sres; - HSE_poll_res pres; - HSE_finish_res fres; - io_handle *out = cfg->out; - - size_t sunk = 0; - do { - if (data_sz > 0) { - sres = heatshrink_encoder_sink(hse, &data[sunk], data_sz - sunk, &sink_sz); - if (sres < 0) { die("sink"); } - sunk += sink_sz; - } - - do { - pres = heatshrink_encoder_poll(hse, out_buf, out_sz, &poll_sz); - if (pres < 0) { die("poll"); } - if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink"); - } while (pres == HSER_POLL_MORE); - - if (poll_sz == 0 && data_sz == 0) { - fres = heatshrink_encoder_finish(hse); - if (fres < 0) { die("finish"); } - if (fres == HSER_FINISH_DONE) { return 1; } - } - } while (sunk < data_sz); - return 0; -} - -static int encode(config *cfg) { - uint8_t window_sz2 = cfg->window_sz2; - size_t window_sz = 1 << window_sz2; - heatshrink_encoder *hse = heatshrink_encoder_alloc(window_sz2, cfg->lookahead_sz2); - if (hse == NULL) { die("failed to init encoder: bad settings"); } - ssize_t read_sz = 0; - io_handle *in = cfg->in; - - /* Process input until end of stream */ - while (1) { - uint8_t *input = NULL; - read_sz = handle_read(in, window_sz, &input); - if (input == NULL) { - printf("handle read failure\n"); - die("read"); - } - if (read_sz < 0) { die("read"); } - - /* Pass read to encoder and check if input is fully processed. */ - if (encoder_sink_read(cfg, hse, input, read_sz)) break; - - if (handle_drop(in, read_sz) < 0) { die("drop"); } - }; - - if (read_sz == -1) { err(1, "read"); } - - heatshrink_encoder_free(hse); - close_and_report(cfg); - return 0; -} - -static int decoder_sink_read(config *cfg, heatshrink_decoder *hsd, - uint8_t *data, size_t data_sz) { - io_handle *out = cfg->out; - size_t sink_sz = 0; - size_t poll_sz = 0; - size_t out_sz = 4096; - uint8_t out_buf[out_sz]; - memset(out_buf, 0, out_sz); - - HSD_sink_res sres; - HSD_poll_res pres; - HSD_finish_res fres; - - size_t sunk = 0; - do { - if (data_sz > 0) { - sres = heatshrink_decoder_sink(hsd, &data[sunk], data_sz - sunk, &sink_sz); - if (sres < 0) { die("sink"); } - sunk += sink_sz; - } - - do { - pres = heatshrink_decoder_poll(hsd, out_buf, out_sz, &poll_sz); - if (pres < 0) { die("poll"); } - if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink"); - } while (pres == HSDR_POLL_MORE); - - if (data_sz == 0 && poll_sz == 0) { - fres = heatshrink_decoder_finish(hsd); - if (fres < 0) { die("finish"); } - if (fres == HSDR_FINISH_DONE) { return 1; } - } - } while (sunk < data_sz); - - return 0; -} - -static int decode(config *cfg) { - uint8_t window_sz2 = cfg->window_sz2; - size_t window_sz = 1 << window_sz2; - size_t ibs = cfg->decoder_input_buffer_size; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(ibs, - window_sz2, cfg->lookahead_sz2); - if (hsd == NULL) { die("failed to init decoder"); } - - ssize_t read_sz = 0; - - io_handle *in = cfg->in; - - HSD_finish_res fres; - - /* Process input until end of stream */ - while (1) { - uint8_t *input = NULL; - read_sz = handle_read(in, window_sz, &input); - if (input == NULL) { - printf("handle read failure\n"); - die("read"); - } - if (read_sz == 0) { - fres = heatshrink_decoder_finish(hsd); - if (fres < 0) { die("finish"); } - if (fres == HSDR_FINISH_DONE) break; - } else if (read_sz < 0) { - die("read"); - } else { - if (decoder_sink_read(cfg, hsd, input, read_sz)) { break; } - if (handle_drop(in, read_sz) < 0) { die("drop"); } - } - } - if (read_sz == -1) { err(1, "read"); } - - heatshrink_decoder_free(hsd); - close_and_report(cfg); - return 0; -} - -static void report(config *cfg) { - size_t inb = cfg->in->total; - size_t outb = cfg->out->total; - fprintf(cfg->out->fd == STDOUT_FILENO ? stderr : stdout, - "%s %0.2f %%\t %zd -> %zd (-w %u -l %u)\n", - cfg->in_fname, 100.0 - (100.0 * outb) / inb, inb, outb, - cfg->window_sz2, cfg->lookahead_sz2); -} - -static void proc_args(config *cfg, int argc, char **argv) { - cfg->window_sz2 = DEF_WINDOW_SZ2; - cfg->lookahead_sz2 = DEF_LOOKAHEAD_SZ2; - cfg->buffer_size = DEF_BUFFER_SIZE; - cfg->decoder_input_buffer_size = DEF_DECODER_INPUT_BUFFER_SIZE; - cfg->cmd = OP_ENC; - cfg->verbose = 0; - cfg->in_fname = "-"; - cfg->out_fname = "-"; - - int a = 0; - while ((a = getopt(argc, argv, "hedi:w:l:v")) != -1) { - switch (a) { - case 'h': /* help */ - usage(); - case 'e': /* encode */ - cfg->cmd = OP_ENC; break; - case 'd': /* decode */ - cfg->cmd = OP_DEC; break; - case 'i': /* input buffer size */ - cfg->decoder_input_buffer_size = atoi(optarg); - break; - case 'w': /* window bits */ - cfg->window_sz2 = atoi(optarg); - break; - case 'l': /* lookahead bits */ - cfg->lookahead_sz2 = atoi(optarg); - break; - case 'v': /* verbosity++ */ - cfg->verbose++; - break; - case '?': /* unknown argument */ - default: - usage(); - } - } - argc -= optind; - argv += optind; - if (argc > 0) { - cfg->in_fname = argv[0]; - argc--; - argv++; - } - if (argc > 0) { cfg->out_fname = argv[0]; } -} - -int main(int argc, char **argv) { - config cfg; - memset(&cfg, 0, sizeof(cfg)); - proc_args(&cfg, argc, argv); - - if (0 == strcmp(cfg.in_fname, cfg.out_fname) - && (0 != strcmp("-", cfg.in_fname))) { - printf("Refusing to overwrite file '%s' with itself.\n", cfg.in_fname); - exit(1); - } - - cfg.in = handle_open(cfg.in_fname, IO_READ, cfg.buffer_size); - if (cfg.in == NULL) { die("Failed to open input file for read"); } - cfg.out = handle_open(cfg.out_fname, IO_WRITE, cfg.buffer_size); - if (cfg.out == NULL) { die("Failed to open output file for write"); } - - if (cfg.cmd == OP_ENC) { - return encode(&cfg); - } else if (cfg.cmd == OP_DEC) { - return decode(&cfg); - } else { - usage(); - } -} diff --git a/espfs/heatshrink/heatshrink_common.h b/espfs/heatshrink/heatshrink_common.h deleted file mode 100644 index 0d396b9..0000000 --- a/espfs/heatshrink/heatshrink_common.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HEATSHRINK_H -#define HEATSHRINK_H - -#define HEATSHRINK_AUTHOR "Scott Vokes " -#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" - -/* Version 0.3.1 */ -#define HEATSHRINK_VERSION_MAJOR 0 -#define HEATSHRINK_VERSION_MINOR 3 -#define HEATSHRINK_VERSION_PATCH 1 - -#define HEATSHRINK_MIN_WINDOW_BITS 4 -#define HEATSHRINK_MAX_WINDOW_BITS 15 - -#define HEATSHRINK_MIN_LOOKAHEAD_BITS 2 - -#define HEATSHRINK_LITERAL_MARKER 0x01 -#define HEATSHRINK_BACKREF_MARKER 0x00 - -#endif diff --git a/espfs/heatshrink/heatshrink_config.h b/espfs/heatshrink/heatshrink_config.h deleted file mode 100644 index 51d4772..0000000 --- a/espfs/heatshrink/heatshrink_config.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -/* Should functionality assuming dynamic allocation be used? */ -#define HEATSHRINK_DYNAMIC_ALLOC 1 - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Optional replacement of malloc/free */ - #define HEATSHRINK_MALLOC(SZ) malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) free(P) -#else - /* Required parameters for static configuration */ - #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 -#endif - -/* Turn on logging for debugging. */ -#define HEATSHRINK_DEBUGGING_LOGS 0 - -/* Use indexing for faster compression. (This requires additional space.) */ -#define HEATSHRINK_USE_INDEX 1 - -#endif diff --git a/espfs/heatshrink/heatshrink_decoder.c b/espfs/heatshrink/heatshrink_decoder.c deleted file mode 100644 index b92be13..0000000 --- a/espfs/heatshrink/heatshrink_decoder.c +++ /dev/null @@ -1,382 +0,0 @@ -#include -#include -#include "heatshrink_decoder.h" - -/* States for the polling state machine. */ -typedef enum { - HSDS_EMPTY, /* no input to process */ - HSDS_INPUT_AVAILABLE, /* new input, completely unprocessed */ - HSDS_YIELD_LITERAL, /* ready to yield literal byte */ - HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ - HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ - HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ - HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ - HSDS_YIELD_BACKREF, /* ready to yield back-reference */ - HSDS_CHECK_FOR_MORE_INPUT, /* check if input is exhausted */ -} HSD_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "empty", - "input_available", - "yield_literal", - "backref_index", - "backref_count", - "yield_backref", - "check_for_more_input", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define NO_BITS ((uint32_t)-1) - -/* Forward references. */ -static uint32_t get_bits(heatshrink_decoder *hsd, uint8_t count); -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (input_buffer_size == 0) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 > window_sz2)) { - return NULL; - } - size_t buffers_sz = (1 << window_sz2) + input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); - if (hsd == NULL) { return NULL; } - hsd->input_buffer_size = input_buffer_size; - hsd->window_sz2 = window_sz2; - hsd->lookahead_sz2 = lookahead_sz2; - heatshrink_decoder_reset(hsd); - LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", - sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); - return hsd; -} - -void heatshrink_decoder_free(heatshrink_decoder *hsd) { - size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - HEATSHRINK_FREE(hsd, sz); - (void)sz; /* may not be used by free */ -} -#endif - -void heatshrink_decoder_reset(heatshrink_decoder *hsd) { - size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); - size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); - memset(hsd->buffers, 0, buf_sz + input_sz); - hsd->state = HSDS_EMPTY; - hsd->input_size = 0; - hsd->input_index = 0; - hsd->bit_index = 0x00; - hsd->current_byte = 0x00; - hsd->output_count = 0; - hsd->output_index = 0; - hsd->head_index = 0; - hsd->bit_accumulator = 0x00000000; -} - -/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSDR_SINK_ERROR_NULL; - } - - size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; - if (rem == 0) { - *input_size = 0; - return HSDR_SINK_FULL; - } - - size = rem < size ? rem : size; - LOG("-- sinking %zd bytes\n", size); - /* copy into input buffer (at head of buffers) */ - memcpy(&hsd->buffers[hsd->input_size], in_buf, size); - hsd->input_size += size; - if (hsd->state == HSDS_EMPTY) { - hsd->state = HSDS_INPUT_AVAILABLE; - hsd->input_index = 0; - } - *input_size = size; - return HSDR_SINK_OK; -} - - -/***************** - * Decompression * - *****************/ - -#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) -#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) - -// States -static HSD_state st_input_available(heatshrink_decoder *hsd); -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi); -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi); -static HSD_state st_check_for_input(heatshrink_decoder *hsd); - -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSDR_POLL_ERROR_NULL; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- poll, state is %d (%s), input_size %d\n", - hsd->state, state_names[hsd->state], hsd->input_size); - uint8_t in_state = hsd->state; - switch (in_state) { - case HSDS_EMPTY: - return HSDR_POLL_EMPTY; - case HSDS_INPUT_AVAILABLE: - hsd->state = st_input_available(hsd); - break; - case HSDS_YIELD_LITERAL: - hsd->state = st_yield_literal(hsd, &oi); - break; - case HSDS_BACKREF_INDEX_MSB: - hsd->state = st_backref_index_msb(hsd); - break; - case HSDS_BACKREF_INDEX_LSB: - hsd->state = st_backref_index_lsb(hsd); - break; - case HSDS_BACKREF_COUNT_MSB: - hsd->state = st_backref_count_msb(hsd); - break; - case HSDS_BACKREF_COUNT_LSB: - hsd->state = st_backref_count_lsb(hsd); - break; - case HSDS_YIELD_BACKREF: - hsd->state = st_yield_backref(hsd, &oi); - break; - case HSDS_CHECK_FOR_MORE_INPUT: - hsd->state = st_check_for_input(hsd); - break; - default: - return HSDR_POLL_ERROR_UNKNOWN; - } - - /* If the current state cannot advance, check if input or output - * buffer are exhausted. */ - if (hsd->state == in_state) { - if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } - return HSDR_POLL_EMPTY; - } - } -} - -static HSD_state st_input_available(heatshrink_decoder *hsd) { - uint32_t bits = get_bits(hsd, 1); // get tag bit - if (bits) { - return HSDS_YIELD_LITERAL; - } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { - return HSDS_BACKREF_INDEX_MSB; - } else { - hsd->output_index = 0; - return HSDS_BACKREF_INDEX_LSB; - } -} - -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi) { - /* Emit a repeated section from the window buffer, and add it (again) - * to the window buffer. (Note that the repetition can include - * itself.)*/ - if (*oi->output_size < oi->buf_size) { - uint32_t byte = get_bits(hsd, 8); - if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint8_t c = byte & 0xFF; - LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); - buf[hsd->head_index++ & mask] = c; - push_byte(hsd, oi, c); - return HSDS_CHECK_FOR_MORE_INPUT; - } else { - return HSDS_YIELD_LITERAL; - } -} - -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - ASSERT(bit_ct > 8); - uint32_t bits = get_bits(hsd, bit_ct - 8); - LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } - hsd->output_index = bits << 8; - return HSDS_BACKREF_INDEX_LSB; -} - -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - uint32_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); - LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } - hsd->output_index |= bits; - hsd->output_index++; - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - hsd->output_count = 0; - return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - ASSERT(br_bit_ct > 8); - uint32_t bits = get_bits(hsd, br_bit_ct - 8); - LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } - hsd->output_count = bits << 8; - return HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - uint32_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); - LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } - hsd->output_count |= bits; - hsd->output_count++; - return HSDS_YIELD_BACKREF; -} - -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi) { - size_t count = oi->buf_size - *oi->output_size; - if (count > 0) { - if (hsd->output_count < count) count = hsd->output_count; - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint16_t neg_offset = hsd->output_index; - LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); - ASSERT(neg_offset < mask + 1); - ASSERT(count <= 1 << BACKREF_COUNT_BITS(hsd)); - - for (size_t i=0; ihead_index - neg_offset) & mask]; - push_byte(hsd, oi, c); - buf[hsd->head_index & mask] = c; - hsd->head_index++; - LOG(" -- ++ 0x%02x\n", c); - } - hsd->output_count -= count; - if (hsd->output_count == 0) { return HSDS_CHECK_FOR_MORE_INPUT; } - } - return HSDS_YIELD_BACKREF; -} - -static HSD_state st_check_for_input(heatshrink_decoder *hsd) { - return (hsd->input_size == 0) ? HSDS_EMPTY : HSDS_INPUT_AVAILABLE; -} - -/* Get the next COUNT bits from the input buffer, saving incremental progress. - * Returns NO_BITS on end of input, or if more than 31 bits are requested. */ -static uint32_t get_bits(heatshrink_decoder *hsd, uint8_t count) { - if (count > 31) { return NO_BITS; } - LOG("-- popping %u bit(s)\n", count); - - /* If we aren't able to get COUNT bits, suspend immediately, because we - * don't track how many bits of COUNT we've accumulated before suspend. */ - if (hsd->input_size == 0) { - if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } - } - - for (int i = 0; i < count; i++) { - if (hsd->bit_index == 0x00) { - if (hsd->input_size == 0) { - LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", - hsd->bit_accumulator, hsd->bit_accumulator); - return NO_BITS; - } - hsd->current_byte = hsd->buffers[hsd->input_index++]; - LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); - if (hsd->input_index == hsd->input_size) { - hsd->input_index = 0; /* input is exhausted */ - hsd->input_size = 0; - } - hsd->bit_index = 0x80; - } - hsd->bit_accumulator <<= 1; - if (hsd->current_byte & hsd->bit_index) { - hsd->bit_accumulator |= 0x01; - if (0) { - LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", - hsd->bit_accumulator, hsd->bit_index); - } - } else { - if (0) { - LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", - hsd->bit_accumulator, hsd->bit_index); - } - } - hsd->bit_index >>= 1; - } - - uint32_t res = 0; - res = hsd->bit_accumulator; - hsd->bit_accumulator = 0x00000000; - if (count > 1) { LOG(" -- accumulated %08x\n", res); } - return res; -} - -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { - if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } - switch (hsd->state) { - case HSDS_EMPTY: - return HSDR_FINISH_DONE; - - /* If we want to finish with no input, but are in these states, it's - * because the 0-bit padding to the last byte looks like a backref - * marker bit followed by all 0s for index and count bits. */ - case HSDS_BACKREF_INDEX_LSB: - case HSDS_BACKREF_INDEX_MSB: - case HSDS_BACKREF_COUNT_LSB: - case HSDS_BACKREF_COUNT_MSB: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If the output stream is padded with 0xFFs (possibly due to being in - * flash memory), also explicitly check the input size rather than - * uselessly returning MORE but yielding 0 bytes when polling. */ - case HSDS_YIELD_LITERAL: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - default: - return HSDR_FINISH_MORE; - } -} - -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { - LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); - oi->buf[(*oi->output_size)++] = byte; - (void)hsd; -} diff --git a/espfs/heatshrink/heatshrink_decoder.h b/espfs/heatshrink/heatshrink_decoder.h deleted file mode 100644 index c1eb144..0000000 --- a/espfs/heatshrink/heatshrink_decoder.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef HEATSHRINK_DECODER_H -#define HEATSHRINK_DECODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSDR_SINK_OK, /* data sunk, ready to poll */ - HSDR_SINK_FULL, /* out of space in internal buffer */ - HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ -} HSD_sink_res; - -typedef enum { - HSDR_POLL_EMPTY, /* input exhausted */ - HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ - HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ - HSDR_POLL_ERROR_UNKNOWN=-2, -} HSD_poll_res; - -typedef enum { - HSDR_FINISH_DONE, /* output is done */ - HSDR_FINISH_MORE, /* more output remains */ - HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ -} HSD_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ - ((BUF)->input_buffer_size) -#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ - ((BUF)->window_sz2) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - ((BUF)->lookahead_sz2) -#else -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ - HEATSHRINK_STATIC_INPUT_BUFFER_SIZE -#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t input_index; /* offset to next unprocessed input byte */ - uint16_t output_count; /* how many bytes to output */ - uint16_t output_index; /* index for bytes to output */ - uint16_t head_index; /* head of window buffer */ - uint16_t bit_accumulator; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of input */ - uint8_t bit_index; /* current bit index */ - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Fields that are only used if dynamically allocated. */ - uint8_t window_sz2; /* window buffer bits */ - uint8_t lookahead_sz2; /* lookahead bits */ - uint16_t input_buffer_size; /* input buffer size */ - - /* Input buffer, then expansion window buffer */ - uint8_t buffers[]; -#else - /* Input buffer, then expansion window buffer */ - uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) - + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; -#endif -} heatshrink_decoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, - * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead - * size of 2^lookahead_sz2. (The window buffer and lookahead sizes - * must match the settings used when the data was compressed.) - * Returns NULL on error. */ -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); - -/* Free a decoder. */ -void heatshrink_decoder_free(heatshrink_decoder *hsd); -#endif - -/* Reset a decoder. */ -void heatshrink_decoder_reset(heatshrink_decoder *hsd); - -/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to - * indicate how many bytes were actually sunk (in case a buffer was filled). */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the dencoder that the input stream is finished. - * If the return value is HSDR_FINISH_MORE, there is still more output, so - * call heatshrink_decoder_poll and repeat. */ -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); - -#endif diff --git a/espfs/heatshrink/heatshrink_encoder.c b/espfs/heatshrink/heatshrink_encoder.c deleted file mode 100644 index ede5f60..0000000 --- a/espfs/heatshrink/heatshrink_encoder.c +++ /dev/null @@ -1,650 +0,0 @@ -#include -#include -#include -#include "heatshrink_encoder.h" - -typedef enum { - HSES_NOT_FULL, /* input buffer not full enough */ - HSES_FILLED, /* buffer is full */ - HSES_SEARCH, /* searching for patterns */ - HSES_YIELD_TAG_BIT, /* yield tag bit */ - HSES_YIELD_LITERAL, /* emit literal byte */ - HSES_YIELD_BR_INDEX, /* yielding backref index */ - HSES_YIELD_BR_LENGTH, /* yielding backref length */ - HSES_SAVE_BACKLOG, /* copying buffer to backlog */ - HSES_FLUSH_BITS, /* flush bit buffer */ - HSES_DONE, /* done */ -} HSE_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "not_full", - "filled", - "search", - "yield_tag_bit", - "yield_literal", - "yield_br_index", - "yield_br_length", - "save_backlog", - "flush_bits", - "done", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -// Encoder flags -enum { - FLAG_IS_FINISHING = 0x01, - FLAG_HAS_LITERAL = 0x02, - FLAG_ON_FINAL_LITERAL = 0x04, - FLAG_BACKLOG_IS_PARTIAL = 0x08, - FLAG_BACKLOG_IS_FILLED = 0x10, -}; - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define MATCH_NOT_FOUND ((uint16_t)-1) - -static uint16_t get_input_offset(heatshrink_encoder *hse); -static uint16_t get_input_buffer_size(heatshrink_encoder *hse); -static uint16_t get_lookahead_size(heatshrink_encoder *hse); -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); -static int can_take_byte(output_info *oi); -static int is_finishing(heatshrink_encoder *hse); -static int backlog_is_partial(heatshrink_encoder *hse); -static int backlog_is_filled(heatshrink_encoder *hse); -static int on_final_literal(heatshrink_encoder *hse); -static void save_backlog(heatshrink_encoder *hse); -static int has_literal(heatshrink_encoder *hse); - -/* Push COUNT (max 8) bits to the output buffer, which has room. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi); -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 > window_sz2)) { - return NULL; - } - - /* Note: 2 * the window size is used because the buffer needs to fit - * (1 << window_sz2) bytes for the current input, and an additional - * (1 << window_sz2) bytes for the previous buffer of input, which - * will be scanned for useful backreferences. */ - size_t buf_sz = (2 << window_sz2); - - heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse) + buf_sz); - if (hse == NULL) { return NULL; } - hse->window_sz2 = window_sz2; - hse->lookahead_sz2 = lookahead_sz2; - heatshrink_encoder_reset(hse); - -#if HEATSHRINK_USE_INDEX - size_t index_sz = buf_sz*sizeof(uint16_t); - hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); - if (hse->search_index == NULL) { - HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); - return NULL; - } - hse->search_index->size = index_sz; -#endif - - LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", - buf_sz, get_input_buffer_size(hse)); - return hse; -} - -void heatshrink_encoder_free(heatshrink_encoder *hse) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); -#if HEATSHRINK_USE_INDEX - size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; - HEATSHRINK_FREE(hse->search_index, index_sz); - (void)index_sz; -#endif - HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder) + buf_sz); - (void)buf_sz; -} -#endif - -void heatshrink_encoder_reset(heatshrink_encoder *hse) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - memset(hse->buffer, 0, buf_sz); - hse->input_size = 0; - hse->state = HSES_NOT_FULL; - hse->match_scan_index = 0; - hse->flags = 0; - hse->bit_index = 0x80; - hse->current_byte = 0x00; - hse->match_length = 0; - - hse->outgoing_bits = 0x0000; - hse->outgoing_bits_count = 0; - - #ifdef LOOP_DETECT - hse->loop_detect = (uint32_t)-1; - #endif -} - -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSER_SINK_ERROR_NULL; - } - - /* Sinking more content after saying the content is done, tsk tsk */ - if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } - - /* Sinking more content before processing is done */ - if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } - - uint16_t write_offset = get_input_offset(hse) + hse->input_size; - uint16_t ibs = get_input_buffer_size(hse); - uint16_t rem = ibs - hse->input_size; - uint16_t cp_sz = rem < size ? rem : size; - - memcpy(&hse->buffer[write_offset], in_buf, cp_sz); - *input_size = cp_sz; - hse->input_size += cp_sz; - - LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", - cp_sz, size, write_offset, hse->input_size); - if (cp_sz == rem) { - LOG("-- internal buffer is now full\n"); - hse->state = HSES_FILLED; - } - - return HSER_SINK_OK; -} - - -/*************** - * Compression * - ***************/ - -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length); -static void do_indexing(heatshrink_encoder *hse); - -static HSE_state st_step_search(heatshrink_encoder *hse); -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_save_backlog(heatshrink_encoder *hse); -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi); - -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSER_POLL_ERROR_NULL; - } - if (out_buf_size == 0) { - LOG("-- MISUSE: output buffer size is 0\n"); - return HSER_POLL_ERROR_MISUSE; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- polling, state %u (%s), flags 0x%02x\n", - hse->state, state_names[hse->state], hse->flags); - - uint8_t in_state = hse->state; - switch (in_state) { - case HSES_NOT_FULL: - return HSER_POLL_EMPTY; - case HSES_FILLED: - do_indexing(hse); - hse->state = HSES_SEARCH; - break; - case HSES_SEARCH: - hse->state = st_step_search(hse); - break; - case HSES_YIELD_TAG_BIT: - hse->state = st_yield_tag_bit(hse, &oi); - break; - case HSES_YIELD_LITERAL: - hse->state = st_yield_literal(hse, &oi); - break; - case HSES_YIELD_BR_INDEX: - hse->state = st_yield_br_index(hse, &oi); - break; - case HSES_YIELD_BR_LENGTH: - hse->state = st_yield_br_length(hse, &oi); - break; - case HSES_SAVE_BACKLOG: - hse->state = st_save_backlog(hse); - break; - case HSES_FLUSH_BITS: - hse->state = st_flush_bit_buffer(hse, &oi); - case HSES_DONE: - return HSER_POLL_EMPTY; - default: - LOG("-- bad state %s\n", state_names[hse->state]); - return HSER_POLL_ERROR_MISUSE; - } - - if (hse->state == in_state) { - /* Check if output buffer is exhausted. */ - if (*output_size == out_buf_size) return HSER_POLL_MORE; - } - } -} - -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { - if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } - LOG("-- setting is_finishing flag\n"); - hse->flags |= FLAG_IS_FINISHING; - if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } - return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; -} - -static HSE_state st_step_search(heatshrink_encoder *hse) { - uint16_t window_length = get_input_buffer_size(hse); - uint16_t lookahead_sz = get_lookahead_size(hse); - uint16_t msi = hse->match_scan_index; - LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", - msi, hse->input_size + msi, 2*window_length, hse->input_size); - - bool fin = is_finishing(hse); - if (msi >= hse->input_size - (fin ? 0 : lookahead_sz)) { - /* Current search buffer is exhausted, copy it into the - * backlog and await more input. */ - LOG("-- end of search @ %d, saving backlog\n", msi); - return HSES_SAVE_BACKLOG; - } - - uint16_t input_offset = get_input_offset(hse); - uint16_t end = input_offset + msi; - - uint16_t start = 0; - if (backlog_is_filled(hse)) { /* last WINDOW_LENGTH bytes */ - start = end - window_length + 1; - } else if (backlog_is_partial(hse)) { /* clamp to available data */ - start = end - window_length + 1; - if (start < lookahead_sz) { start = lookahead_sz; } - } else { /* only scan available input */ - start = input_offset; - } - - uint16_t max_possible = lookahead_sz; - if (hse->input_size - msi < lookahead_sz) { - max_possible = hse->input_size - msi; - } - - uint16_t match_length = 0; - uint16_t match_pos = find_longest_match(hse, - start, end, max_possible, &match_length); - - if (match_pos == MATCH_NOT_FOUND) { - LOG("ss Match not found\n"); - hse->match_scan_index++; - hse->flags |= FLAG_HAS_LITERAL; - hse->match_length = 0; - return HSES_YIELD_TAG_BIT; - } else { - LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); - hse->match_pos = match_pos; - hse->match_length = match_length; - ASSERT(match_pos < 1 << hse->window_sz2 /*window_length*/); - - return HSES_YIELD_TAG_BIT; - } -} - -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - if (hse->match_length == 0) { - add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); - return HSES_YIELD_LITERAL; - } else { - add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); - hse->outgoing_bits = hse->match_pos - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); - return HSES_YIELD_BR_INDEX; - } - } else { - return HSES_YIELD_TAG_BIT; /* output is full, continue */ - } -} - -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - push_literal_byte(hse, oi); - hse->flags &= ~FLAG_HAS_LITERAL; - if (on_final_literal(hse)) { return HSES_FLUSH_BITS; } - return hse->match_length > 0 ? HSES_YIELD_TAG_BIT : HSES_SEARCH; - } else { - return HSES_YIELD_LITERAL; - } -} - -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref index %u\n", hse->match_pos); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_INDEX; /* continue */ - } else { - hse->outgoing_bits = hse->match_length - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); - return HSES_YIELD_BR_LENGTH; /* done */ - } - } else { - return HSES_YIELD_BR_INDEX; /* continue */ - } -} - -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref length %u\n", hse->match_length); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_LENGTH; - } else { - hse->match_scan_index += hse->match_length; - hse->match_length = 0; - return HSES_SEARCH; - } - } else { - return HSES_YIELD_BR_LENGTH; - } -} - -static HSE_state st_save_backlog(heatshrink_encoder *hse) { - if (is_finishing(hse)) { - /* copy remaining literal (if necessary) */ - if (has_literal(hse)) { - hse->flags |= FLAG_ON_FINAL_LITERAL; - return HSES_YIELD_TAG_BIT; - } else { - return HSES_FLUSH_BITS; - } - } else { - LOG("-- saving backlog\n"); - save_backlog(hse); - return HSES_NOT_FULL; - } -} - -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi) { - if (hse->bit_index == 0x80) { - LOG("-- done!\n"); - return HSES_DONE; - } else if (can_take_byte(oi)) { - LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); - oi->buf[(*oi->output_size)++] = hse->current_byte; - LOG("-- done!\n"); - return HSES_DONE; - } else { - return HSES_FLUSH_BITS; - } -} - -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { - LOG("-- adding tag bit: %d\n", tag); - push_bits(hse, 1, tag, oi); -} - -static uint16_t get_input_offset(heatshrink_encoder *hse) { - return get_input_buffer_size(hse); -} - -static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - (void)hse; -} - -static uint16_t get_lookahead_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - (void)hse; -} - -static void do_indexing(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - /* Build an index array I that contains flattened linked lists - * for the previous instances of every byte in the buffer. - * - * For example, if buf[200] == 'x', then index[200] will either - * be an offset i such that buf[i] == 'x', or a negative offset - * to indicate end-of-list. This significantly speeds up matching, - * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. - * - * Future optimization options: - * 1. Since any negative value represents end-of-list, the other - * 15 bits could be used to improve the index dynamically. - * - * 2. Likewise, the last lookahead_sz bytes of the index will - * not be usable, so temporary data could be stored there to - * dynamically improve the index. - * */ - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - uint16_t last[256]; - memset(last, 0xFF, sizeof(last)); - - uint8_t * const data = hse->buffer; - int16_t * const index = hsi->index; - - const uint16_t input_offset = get_input_offset(hse); - const uint16_t end = input_offset + hse->input_size; - - for (uint16_t i=0; iflags & FLAG_IS_FINISHING; -} - -static int backlog_is_partial(heatshrink_encoder *hse) { - return hse->flags & FLAG_BACKLOG_IS_PARTIAL; -} - -static int backlog_is_filled(heatshrink_encoder *hse) { - return hse->flags & FLAG_BACKLOG_IS_FILLED; -} - -static int on_final_literal(heatshrink_encoder *hse) { - return hse->flags & FLAG_ON_FINAL_LITERAL; -} - -static int has_literal(heatshrink_encoder *hse) { - return (hse->flags & FLAG_HAS_LITERAL); -} - -static int can_take_byte(output_info *oi) { - return *oi->output_size < oi->buf_size; -} - -/* Return the longest match for the bytes at buf[end:end+maxlen] between - * buf[start] and buf[end-1]. If no match is found, return -1. */ -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length) { - LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", - end, end + maxlen, start, end + maxlen - 1, maxlen); - uint8_t *buf = hse->buffer; - - uint16_t match_maxlen = 0; - uint16_t match_index = MATCH_NOT_FOUND; - const uint16_t break_even_point = 3; - uint16_t len = 0; - uint8_t * const needlepoint = &buf[end]; -#if HEATSHRINK_USE_INDEX - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t pos = hsi->index[end]; - - while (pos >= start) { - uint8_t * const pospoint = &buf[pos]; - len = 0; - - /* Only check matches that will potentially beat the current maxlen. - * This is redundant with the index if match_maxlen is 0, but the - * added branch overhead to check if it == 0 seems to be worse. */ - if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { - pos = hsi->index[pos]; - continue; - } - - for (len = 1; len < maxlen; len++) { - if (pospoint[len] != needlepoint[len]) break; - } - - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* won't find better */ - } - pos = hsi->index[pos]; - } -#else - for (int16_t pos=end - 1; pos >= start; pos--) { - uint8_t * const pospoint = &buf[pos]; - if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) - && (*pospoint == *needlepoint)) { - for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", - pos + len, pospoint[len], needlepoint[len], start); - } - if (pospoint[len] != needlepoint[len]) { break; } - } - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* don't keep searching */ - } - } - } -#endif - - if (match_maxlen >= break_even_point) { - LOG("-- best match: %u bytes at -%u\n", - match_maxlen, end - match_index); - *match_length = match_maxlen; - return end - match_index; - } - LOG("-- none found\n"); - return MATCH_NOT_FOUND; -} - -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { - uint8_t count = 0; - uint8_t bits = 0; - if (hse->outgoing_bits_count > 8) { - count = 8; - bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); - } else { - count = hse->outgoing_bits_count; - bits = hse->outgoing_bits; - } - - if (count > 0) { - LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); - push_bits(hse, count, bits, oi); - hse->outgoing_bits_count -= count; - } - return count; -} - -/* Push COUNT (max 8) bits to the output buffer, which has room. - * Bytes are set from the lowest bits, up. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi) { - ASSERT(count <= 8); - LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); - - /* If adding a whole byte and at the start of a new output byte, - * just push it through whole and skip the bit IO loop. */ - if (count == 8 && hse->bit_index == 0x80) { - oi->buf[(*oi->output_size)++] = bits; - } else { - for (int i=count - 1; i>=0; i--) { - bool bit = bits & (1 << i); - if (bit) { hse->current_byte |= hse->bit_index; } - if (0) { - LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", - bit ? 1 : 0, hse->bit_index, hse->current_byte); - } - hse->bit_index >>= 1; - if (hse->bit_index == 0x00) { - hse->bit_index = 0x80; - LOG(" > pushing byte 0x%02x\n", hse->current_byte); - oi->buf[(*oi->output_size)++] = hse->current_byte; - hse->current_byte = 0x00; - } - } - } -} - -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { - uint16_t processed_offset = hse->match_scan_index - 1; - uint16_t input_offset = get_input_offset(hse) + processed_offset; - uint8_t c = hse->buffer[input_offset]; - LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", - c, isprint(c) ? c : '.', input_offset); - push_bits(hse, 8, c, oi); -} - -static void save_backlog(heatshrink_encoder *hse) { - size_t input_buf_sz = get_input_buffer_size(hse); - - uint16_t msi = hse->match_scan_index; - - /* Copy processed data to beginning of buffer, so it can be - * used for future matches. Don't bother checking whether the - * input is less than the maximum size, because if it isn't, - * we're done anyway. */ - uint16_t rem = input_buf_sz - msi; // unprocessed bytes - uint16_t shift_sz = input_buf_sz + rem; - - memmove(&hse->buffer[0], - &hse->buffer[input_buf_sz - rem], - shift_sz); - - if (backlog_is_partial(hse)) { - /* The whole backlog is filled in now, so include it in scans. */ - hse->flags |= FLAG_BACKLOG_IS_FILLED; - } else { - /* Include backlog, except for the first lookahead_sz bytes, which - * are still undefined. */ - hse->flags |= FLAG_BACKLOG_IS_PARTIAL; - } - hse->match_scan_index = 0; - hse->input_size -= input_buf_sz - rem; -} diff --git a/espfs/heatshrink/heatshrink_encoder.h b/espfs/heatshrink/heatshrink_encoder.h deleted file mode 100644 index 18c1773..0000000 --- a/espfs/heatshrink/heatshrink_encoder.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef HEATSHRINK_ENCODER_H -#define HEATSHRINK_ENCODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSER_SINK_OK, /* data sunk into input buffer */ - HSER_SINK_ERROR_NULL=-1, /* NULL argument */ - HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ -} HSE_sink_res; - -typedef enum { - HSER_POLL_EMPTY, /* input exhausted */ - HSER_POLL_MORE, /* poll again for more output */ - HSER_POLL_ERROR_NULL=-1, /* NULL argument */ - HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ -} HSE_poll_res; - -typedef enum { - HSER_FINISH_DONE, /* encoding is complete */ - HSER_FINISH_MORE, /* more output remaining; use poll */ - HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ -} HSE_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ - ((HSE)->window_sz2) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ - ((HSE)->lookahead_sz2) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - ((HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[]; -}; -#else -#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - (&(HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; -}; -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t match_scan_index; - uint16_t match_length; - uint16_t match_pos; - uint16_t outgoing_bits; /* enqueued outgoing bits */ - uint8_t outgoing_bits_count; - uint8_t flags; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of output */ - uint8_t bit_index; /* current bit index */ -#if HEATSHRINK_DYNAMIC_ALLOC - uint8_t window_sz2; /* 2^n size of window */ - uint8_t lookahead_sz2; /* 2^n size of lookahead */ -#if HEATSHRINK_USE_INDEX - struct hs_index *search_index; -#endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[]; -#else - #if HEATSHRINK_USE_INDEX - struct hs_index search_index; - #endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; -#endif -} heatshrink_encoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a new encoder struct and its buffers. - * Returns NULL on error. */ -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, - uint8_t lookahead_sz2); - -/* Free an encoder. */ -void heatshrink_encoder_free(heatshrink_encoder *hse); -#endif - -/* Reset an encoder. */ -void heatshrink_encoder_reset(heatshrink_encoder *hse); - -/* Sink up to SIZE bytes from IN_BUF into the encoder. - * INPUT_SIZE is set to the number of bytes actually sunk (in case a - * buffer was filled.). */ -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the encoder that the input stream is finished. - * If the return value is HSER_FINISH_MORE, there is still more output, so - * call heatshrink_encoder_poll and repeat. */ -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); - -#endif diff --git a/espfs/heatshrink/test_heatshrink_dynamic.c b/espfs/heatshrink/test_heatshrink_dynamic.c deleted file mode 100644 index 1e18a69..0000000 --- a/espfs/heatshrink/test_heatshrink_dynamic.c +++ /dev/null @@ -1,999 +0,0 @@ -#include -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" -#include "greatest.h" - -#if !HEATSHRINK_DYNAMIC_ALLOC -#error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for dynamic allocation test suite. -#endif - -SUITE(encoding); -SUITE(decoding); -SUITE(integration); - -#ifdef HEATSHRINK_HAS_THEFT -SUITE(properties); -#endif - -static void dump_buf(char *name, uint8_t *buf, uint16_t count) { - for (int i=0; iinput_size, 6); - ASSERT_EQ(hsd->input_index, 0); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_return_empty_if_empty(void) { - uint8_t output[256]; - size_t out_sz = 0; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, - HEATSHRINK_MIN_WINDOW_BITS, 4); - HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, &out_sz); - ASSERT_EQ(HSDR_POLL_EMPTY, res); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_reject_null_hsd(void) { - uint8_t output[256]; - size_t out_sz = 0; - HSD_poll_res res = heatshrink_decoder_poll(NULL, output, 256, &out_sz); - ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); - PASS(); -} - -TEST decoder_poll_should_reject_null_output_buffer(void) { - size_t out_sz = 0; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, - HEATSHRINK_MIN_WINDOW_BITS, 4); - HSD_poll_res res = heatshrink_decoder_poll(hsd, NULL, 256, &out_sz); - ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_reject_null_output_size_pointer(void) { - uint8_t output[256]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, - HEATSHRINK_MIN_WINDOW_BITS, 4); - HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, NULL); - ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_expand_short_literal(void) { - uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0 }; //"foo" - uint8_t output[4]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 3); - size_t count = 0; - - HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); - ASSERT_EQ(HSDR_SINK_OK, sres); - - size_t out_sz = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 4, &out_sz); - ASSERT_EQ(HSDR_POLL_EMPTY, pres); - ASSERT_EQ(3, out_sz); - ASSERT_EQ('f', output[0]); - ASSERT_EQ('o', output[1]); - ASSERT_EQ('o', output[2]); - - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_expand_short_literal_and_backref(void) { - uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo" - uint8_t output[6]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7); - memset(output, 0, sizeof(*output)); - size_t count = 0; - - HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); - ASSERT_EQ(HSDR_SINK_OK, sres); - - size_t out_sz = 0; - (void)heatshrink_decoder_poll(hsd, output, 6, &out_sz); - - if (0) dump_buf("output", output, out_sz); - ASSERT_EQ(6, out_sz); - ASSERT_EQ('f', output[0]); - ASSERT_EQ('o', output[1]); - ASSERT_EQ('o', output[2]); - ASSERT_EQ('f', output[3]); - ASSERT_EQ('o', output[4]); - ASSERT_EQ('o', output[5]); - - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST decoder_poll_should_expand_short_self_overlapping_backref(void) { - /* "aaaaa" == (literal, 1), ('a'), (backref, 1 back, 4 bytes) */ - uint8_t input[] = {0xb0, 0x80, 0x01, 0x80}; - uint8_t output[6]; - uint8_t expected[] = {'a', 'a', 'a', 'a', 'a'}; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 7); - size_t count = 0; - - HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); - ASSERT_EQ(HSDR_SINK_OK, sres); - - size_t out_sz = 0; - (void)heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz); - - if (0) dump_buf("output", output, out_sz); - ASSERT_EQ(sizeof(expected), out_sz); - for (size_t i=0; iwindow_sz2, - cfg->lookahead_sz2); - heatshrink_decoder *hsd = heatshrink_decoder_alloc(cfg->decoder_input_buffer_size, - cfg->window_sz2, cfg->lookahead_sz2); - size_t comp_sz = input_size + (input_size/2) + 4; - size_t decomp_sz = input_size + (input_size/2) + 4; - uint8_t *comp = malloc(comp_sz); - uint8_t *decomp = malloc(decomp_sz); - if (comp == NULL) FAILm("malloc fail"); - if (decomp == NULL) FAILm("malloc fail"); - memset(comp, 0, comp_sz); - memset(decomp, 0, decomp_sz); - - size_t count = 0; - - if (cfg->log_lvl > 1) { - printf("\n^^ COMPRESSING\n"); - dump_buf("input", input, input_size); - } - - size_t sunk = 0; - size_t polled = 0; - while (sunk < input_size) { - ASSERT(heatshrink_encoder_sink(hse, &input[sunk], input_size - sunk, &count) >= 0); - sunk += count; - if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); - } - - HSE_poll_res pres; - do { /* "turn the crank" */ - pres = heatshrink_encoder_poll(hse, &comp[polled], comp_sz - polled, &count); - ASSERT(pres >= 0); - polled += count; - if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSER_POLL_MORE); - ASSERT_EQ(HSER_POLL_EMPTY, pres); - if (polled >= comp_sz) FAILm("compression should never expand that much"); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(hse)); - } - } - if (cfg->log_lvl > 0) printf("in: %u compressed: %zu ", input_size, polled); - size_t compressed_size = polled; - sunk = 0; - polled = 0; - - if (cfg->log_lvl > 1) { - printf("\n^^ DECOMPRESSING\n"); - dump_buf("comp", comp, compressed_size); - } - while (sunk < compressed_size) { - ASSERT(heatshrink_decoder_sink(hsd, &comp[sunk], compressed_size - sunk, &count) >= 0); - sunk += count; - if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == compressed_size) { - ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(hsd)); - } - - HSD_poll_res pres; - do { - pres = heatshrink_decoder_poll(hsd, &decomp[polled], - decomp_sz - polled, &count); - ASSERT(pres >= 0); - ASSERT(count > 0); - polled += count; - if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSDR_POLL_MORE); - ASSERT_EQ(HSDR_POLL_EMPTY, pres); - if (sunk == compressed_size) { - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - ASSERT_EQ(HSDR_FINISH_DONE, fres); - } - - if (polled > input_size) { - printf("\nExpected %zd, got %zu\n", (size_t)input_size, polled); - FAILm("Decompressed data is larger than original input"); - } - } - if (cfg->log_lvl > 0) printf("decompressed: %zu\n", polled); - if (polled != input_size) { - FAILm("Decompressed length does not match original input length"); - } - - if (cfg->log_lvl > 1) dump_buf("decomp", decomp, polled); - for (uint32_t i=0; i out[%d] == 0x%02x ('%c') %c\n", - j, input[j], isprint(input[j]) ? input[j] : '.', - j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.', - input[j] == decomp[j] ? ' ' : 'X'); - } - } - } - ASSERT_EQ(input[i], decomp[i]); - } - free(comp); - free(decomp); - heatshrink_encoder_free(hse); - heatshrink_decoder_free(hsd); - PASS(); -} - -TEST data_without_duplication_should_match(void) { - uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', - 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', - 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = 3; - cfg.decoder_input_buffer_size = 256; - return compress_and_expand_and_check(input, sizeof(input), &cfg); -} - -TEST data_with_simple_repetition_should_compress_and_decompress_properly(void) { - uint8_t input[] = {'a', 'b', 'c', 'a', 'b', 'c', 'd', 'a', 'b', - 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e', 'f', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'a', 'b', - 'c', 'd', 'e', 'f', 'g', 'h'}; - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = 3; - cfg.decoder_input_buffer_size = 256; - return compress_and_expand_and_check(input, sizeof(input), &cfg); -} - -TEST data_without_duplication_should_match_with_absurdly_tiny_buffers(void) { - heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 3); - heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 3); - uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', - 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', - 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - uint8_t comp[60]; - uint8_t decomp[60]; - size_t count = 0; - int log = 0; - - if (log) dump_buf("input", input, sizeof(input)); - for (uint32_t i=0; i= 0); - } - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); - - size_t packed_count = 0; - do { - ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); - packed_count += count; - } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); - - if (log) dump_buf("comp", comp, packed_count); - for (uint32_t i=0; i= 0); - } - - for (uint32_t i=0; i= 0); - } - - if (log) dump_buf("decomp", decomp, sizeof(input)); - for (uint32_t i=0; i= 0); - } - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); - - size_t packed_count = 0; - do { - ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); - packed_count += count; - } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); - - if (log) dump_buf("comp", comp, packed_count); - for (uint32_t i=0; i= 0); - } - - for (uint32_t i=0; i= 0); - } - - if (log) dump_buf("decomp", decomp, sizeof(input)); - for (uint32_t i=0; ilog_lvl > 0) { - printf("\n-- size %u, seed %u, input buf %zu\n", - size, seed, cfg->decoder_input_buffer_size); - } - fill_with_pseudorandom_letters(input, size, seed); - return compress_and_expand_and_check(input, size, cfg); -} - -TEST small_input_buffer_should_not_impact_decoder_correctness(void) { - int size = 5; - uint8_t input[size]; - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = 3; - cfg.decoder_input_buffer_size = 5; - for (uint16_t i=0; i= 19901L - printf("\n\nFuzzing (single-byte sizes):\n"); - for (uint8_t lsize=3; lsize < 8; lsize++) { - for (uint32_t size=1; size < 128*1024L; size <<= 1) { - if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); - for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ - if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); - for (uint32_t seed=1; seed<=10; seed++) { - if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 8; - cfg.lookahead_sz2 = lsize; - cfg.decoder_input_buffer_size = ibs; - RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); - } - } - } - } - - printf("\nFuzzing (multi-byte sizes):\n"); - for (uint8_t lsize=6; lsize < 9; lsize++) { - for (uint32_t size=1; size < 128*1024L; size <<= 1) { - if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); - for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ - if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); - for (uint32_t seed=1; seed<=10; seed++) { - if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); - cfg_info cfg; - cfg.log_lvl = 0; - cfg.window_sz2 = 11; - cfg.lookahead_sz2 = lsize; - cfg.decoder_input_buffer_size = ibs; - RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); - } - } - } - } - -#endif -} - -/* Add all the definitions that need to be in the test runner's main file. */ -GREATEST_MAIN_DEFS(); - -int main(int argc, char **argv) { - GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ - RUN_SUITE(encoding); - RUN_SUITE(decoding); - RUN_SUITE(integration); - #ifdef HEATSHRINK_HAS_THEFT - RUN_SUITE(properties); - #endif - GREATEST_MAIN_END(); /* display results */ -} diff --git a/espfs/heatshrink/test_heatshrink_dynamic_theft.c b/espfs/heatshrink/test_heatshrink_dynamic_theft.c deleted file mode 100644 index 2752886..0000000 --- a/espfs/heatshrink/test_heatshrink_dynamic_theft.c +++ /dev/null @@ -1,521 +0,0 @@ -#include "heatshrink_config.h" -#ifdef HEATSHRINK_HAS_THEFT - -#include -#include -#include -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" -#include "greatest.h" -#include "theft.h" -#include "greatest_theft.h" - -#if !HEATSHRINK_DYNAMIC_ALLOC -#error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for this test suite. -#endif - -SUITE(properties); - -typedef struct { - int limit; - int fails; - int dots; -} test_env; - -typedef struct { - size_t size; - uint8_t buf[]; -} rbuf; - -static void *rbuf_alloc_cb(struct theft *t, theft_hash seed, void *env) { - test_env *te = (test_env *)env; - //printf("seed is 0x%016llx\n", seed); - - size_t sz = (size_t)(seed % te->limit) + 1; - rbuf *r = malloc(sizeof(rbuf) + sz); - if (r == NULL) { return THEFT_ERROR; } - r->size = sz; - - for (size_t i = 0; i < sz; i += sizeof(theft_hash)) { - theft_hash s = theft_random(t); - for (uint8_t b = 0; b < sizeof(theft_hash); b++) { - if (i + b >= sz) { break; } - r->buf[i + b] = (uint8_t) (s >> (8*b)) & 0xff; - } - } - - return r; -} - -static void rbuf_free_cb(void *instance, void *env) { - free(instance); - (void)env; -} - -static uint64_t rbuf_hash_cb(void *instance, void *env) { - rbuf *r = (rbuf *)instance; - (void)env; - return theft_hash_onepass(r->buf, r->size); -} - -/* Make a copy of a buffer, keeping NEW_SZ bytes starting at OFFSET. */ -static void *copy_rbuf_subset(rbuf *cur, size_t new_sz, size_t byte_offset) { - if (new_sz == 0) { return THEFT_DEAD_END; } - rbuf *nr = malloc(sizeof(rbuf) + new_sz); - if (nr == NULL) { return THEFT_ERROR; } - nr->size = new_sz; - memcpy(nr->buf, &cur->buf[byte_offset], new_sz); - /* printf("%zu -> %zu\n", cur->size, new_sz); */ - return nr; -} - -/* Make a copy of a buffer, but only PORTION, starting OFFSET in - * (e.g. the third quarter is (0.25 at +0.75). Rounds to ints. */ -static void *copy_rbuf_percent(rbuf *cur, float portion, float offset) { - size_t new_sz = cur->size * portion; - size_t byte_offset = (size_t)(cur->size * offset); - return copy_rbuf_subset(cur, new_sz, byte_offset); -} - -/* How to shrink a random buffer to a simpler one. */ -static void *rbuf_shrink_cb(void *instance, uint32_t tactic, void *env) { - rbuf *cur = (rbuf *)instance; - - if (tactic == 0) { /* first half */ - return copy_rbuf_percent(cur, 0.5, 0); - } else if (tactic == 1) { /* second half */ - return copy_rbuf_percent(cur, 0.5, 0.5); - } else if (tactic <= 18) { /* drop 1-16 bytes at start */ - const int last_tactic = 1; - const size_t drop = tactic - last_tactic; - if (cur->size < drop) { return THEFT_DEAD_END; } - return copy_rbuf_subset(cur, cur->size - drop, drop); - } else if (tactic <= 34) { /* drop 1-16 bytes at end */ - const int last_tactic = 18; - const size_t drop = tactic - last_tactic; - if (cur->size < drop) { return THEFT_DEAD_END; } - return copy_rbuf_subset(cur, cur->size - drop, 0); - } else if (tactic == 35) { - /* Divide every byte by 2, saturating at 0 */ - rbuf *cp = copy_rbuf_percent(cur, 1, 0); - if (cp == NULL) { return THEFT_ERROR; } - for (size_t i = 0; i < cp->size; i++) { cp->buf[i] /= 2; } - return cp; - } else if (tactic == 36) { - /* subtract 1 from every byte, saturating at 0 */ - rbuf *cp = copy_rbuf_percent(cur, 1, 0); - if (cp == NULL) { return THEFT_ERROR; } - for (size_t i = 0; i < cp->size; i++) { - if (cp->buf[i] > 0) { cp->buf[i]--; } - } - return cp; - } else { - (void)env; - return THEFT_NO_MORE_TACTICS; - } - - return THEFT_NO_MORE_TACTICS; -} - -static void rbuf_print_cb(FILE *f, void *instance, void *env) { - rbuf *r = (rbuf *)instance; - (void)env; - fprintf(f, "buf[%zd]:\n ", r->size); - uint8_t bytes = 0; - for (size_t i = 0; i < r->size; i++) { - fprintf(f, "%02x", r->buf[i]); - bytes++; - if (bytes == 16) { - fprintf(f, "\n "); - bytes = 0; - } - } - fprintf(f, "\n"); -} - -static struct theft_type_info rbuf_info = { - .alloc = rbuf_alloc_cb, - .free = rbuf_free_cb, - .hash = rbuf_hash_cb, - .shrink = rbuf_shrink_cb, - .print = rbuf_print_cb, -}; - -static theft_progress_callback_res -progress_cb(struct theft_trial_info *info, void *env) { - test_env *te = (test_env *)env; - if ((info->trial & 0xff) == 0) { - printf("."); - fflush(stdout); - te->dots++; - if (te->dots == 64) { - printf("\n"); - te->dots = 0; - } - } - - if (info->status == THEFT_TRIAL_FAIL) { - te->fails++; - rbuf *cur = info->args[0]; - if (cur->size < 5) { return THEFT_PROGRESS_HALT; } - } - - if (te->fails > 10) { - return THEFT_PROGRESS_HALT; - } - return THEFT_PROGRESS_CONTINUE; -} - -/* For an arbitrary input buffer, it should never get stuck in a - * state where the data has been sunk but no data can be polled. */ -static theft_trial_res prop_should_not_get_stuck(void *input) { - /* Make a buffer large enough for the output: 4 KB of input with - * each 16 bits becoming up to 16 bytes will fit in a 64 KB buffer. - * (4 KB of input comes from `env.limit = 1 << 12;` below.) */ - uint8_t output[64 * 1024]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc((64 * 1024L) - 1, 12, 4); - if (hsd == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)input; - - size_t count = 0; - HSD_sink_res sres = heatshrink_decoder_sink(hsd, r->buf, r->size, &count); - if (sres != HSDR_SINK_OK) { return THEFT_TRIAL_ERROR; } - - size_t out_sz = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz); - if (pres != HSDR_POLL_EMPTY) { return THEFT_TRIAL_FAIL; } - - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - heatshrink_decoder_free(hsd); - if (fres != HSDR_FINISH_DONE) { return THEFT_TRIAL_FAIL; } - - return THEFT_TRIAL_PASS; -} - -static bool get_time_seed(theft_seed *seed) -{ - struct timeval tv; - if (-1 == gettimeofday(&tv, NULL)) { return false; } - *seed = (theft_seed)((tv.tv_sec << 32) | tv.tv_usec); - /* printf("seed is 0x%016llx\n", *seed); */ - return true; -} - -TEST decoder_fuzzing_should_not_detect_stuck_state(void) { - // Get a random number seed based on the time - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - /* Pass the max buffer size for this property (4 KB) in a closure */ - test_env env = { .limit = 1 << 12 }; - - theft_seed always_seeds = { 0xe87bb1f61032a061 }; - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_should_not_get_stuck, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 100000, - .progress_cb = progress_cb, - .env = &env, - - .always_seeds = &always_seeds, - .always_seed_count = 1, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - GREATEST_ASSERT_EQm("should_not_get_stuck", THEFT_RUN_PASS, res); - PASS(); -} - -static theft_trial_res prop_encoded_and_decoded_data_should_match(void *input) { - uint8_t e_output[64 * 1024]; - uint8_t d_output[64 * 1024]; - heatshrink_encoder *hse = heatshrink_encoder_alloc(12, 4); - if (hse == NULL) { return THEFT_TRIAL_ERROR; } - heatshrink_decoder *hsd = heatshrink_decoder_alloc(4096, 12, 4); - if (hsd == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)input; - - size_t e_input_size = 0; - HSE_sink_res esres = heatshrink_encoder_sink(hse, - r->buf, r->size, &e_input_size); - if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - if (e_input_size != r->size) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - HSE_finish_res efres = heatshrink_encoder_finish(hse); - if (efres != HSER_FINISH_MORE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t e_output_size = 0; - HSE_poll_res epres = heatshrink_encoder_poll(hse, - e_output, sizeof(e_output), &e_output_size); - if (epres != HSER_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t count = 0; - HSD_sink_res sres = heatshrink_decoder_sink(hsd, e_output, e_output_size, &count); - if (sres != HSDR_SINK_OK) { return THEFT_TRIAL_ERROR; } - - size_t d_output_size = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, d_output, - sizeof(d_output), &d_output_size); - if (pres != HSDR_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - if (d_output_size != r->size) { - printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; - } - - if (0 != memcmp(d_output, r->buf, d_output_size)) { - return THEFT_TRIAL_FAIL; - } - - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - if (fres != HSDR_FINISH_DONE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - heatshrink_encoder_free(hse); - heatshrink_decoder_free(hsd); - return THEFT_TRIAL_PASS; -} - - -TEST encoded_and_decoded_data_should_match(void) { - test_env env = { .limit = 1 << 11 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_encoded_and_decoded_data_should_match, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 1000000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -static size_t ceil_nine_eighths(size_t sz) { - return sz + sz/8 + (sz & 0x07 ? 1 : 0); -} - -static theft_trial_res -prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst(void *input) { - uint8_t output[32 * 1024]; - heatshrink_encoder *hse = heatshrink_encoder_alloc(12, 4); - if (hse == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)input; - - size_t input_size = 0; - HSE_sink_res esres = heatshrink_encoder_sink(hse, - r->buf, r->size, &input_size); - if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - /* Assumes data fits in one sink, failure here means buffer must be larger. */ - if (input_size != r->size) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - HSE_finish_res efres = heatshrink_encoder_finish(hse); - if (efres != HSER_FINISH_MORE) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t output_size = 0; - HSE_poll_res epres = heatshrink_encoder_poll(hse, - output, sizeof(output), &output_size); - if (epres != HSER_POLL_EMPTY) { printf("FAIL %d\n", __LINE__); return THEFT_TRIAL_FAIL; } - - size_t ceil_9_8s = ceil_nine_eighths(r->size); - if (output_size > ceil_9_8s) { - return THEFT_TRIAL_FAIL; - } - - heatshrink_encoder_free(hse); - return THEFT_TRIAL_PASS; -} - -TEST encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst(void) { - test_env env = { .limit = 1 << 11 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 10000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -static theft_trial_res -prop_encoder_should_always_make_progress(void *instance) { - uint8_t output[64 * 1024]; - heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 4); - if (hse == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)instance; - - size_t sunk = 0; - int no_progress = 0; - - while (1) { - if (sunk < r->size) { - size_t input_size = 0; - HSE_sink_res esres = heatshrink_encoder_sink(hse, - &r->buf[sunk], r->size - sunk, &input_size); - if (esres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - sunk += input_size; - } else { - HSE_finish_res efres = heatshrink_encoder_finish(hse); - if (efres == HSER_FINISH_DONE) { - break; - } else if (efres != HSER_FINISH_MORE) { - printf("FAIL %d\n", __LINE__); - return THEFT_TRIAL_FAIL; - } - } - - size_t output_size = 0; - HSE_poll_res epres = heatshrink_encoder_poll(hse, - output, sizeof(output), &output_size); - if (epres < 0) { return THEFT_TRIAL_ERROR; } - if (output_size == 0 && sunk == r->size) { - no_progress++; - if (no_progress > 2) { - return THEFT_TRIAL_FAIL; - } - } else { - no_progress = 0; - } - } - - heatshrink_encoder_free(hse); - return THEFT_TRIAL_PASS; -} - -TEST encoder_should_always_make_progress(void) { - test_env env = { .limit = 1 << 15 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_encoder_should_always_make_progress, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 10000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -static theft_trial_res -prop_decoder_should_always_make_progress(void *instance) { - uint8_t output[64 * 1024]; - heatshrink_decoder *hsd = heatshrink_decoder_alloc(512, 8, 4); - if (hsd == NULL) { return THEFT_TRIAL_ERROR; } - - rbuf *r = (rbuf *)instance; - - size_t sunk = 0; - int no_progress = 0; - - while (1) { - if (sunk < r->size) { - size_t input_size = 0; - HSD_sink_res sres = heatshrink_decoder_sink(hsd, - &r->buf[sunk], r->size - sunk, &input_size); - if (sres != HSER_SINK_OK) { return THEFT_TRIAL_ERROR; } - sunk += input_size; - } else { - HSD_finish_res fres = heatshrink_decoder_finish(hsd); - if (fres == HSDR_FINISH_DONE) { - break; - } else if (fres != HSDR_FINISH_MORE) { - printf("FAIL %d\n", __LINE__); - return THEFT_TRIAL_FAIL; - } - } - - size_t output_size = 0; - HSD_poll_res pres = heatshrink_decoder_poll(hsd, - output, sizeof(output), &output_size); - if (pres < 0) { return THEFT_TRIAL_ERROR; } - if (output_size == 0 && sunk == r->size) { - no_progress++; - if (no_progress > 2) { - return THEFT_TRIAL_FAIL; - } - } else { - no_progress = 0; - } - } - - heatshrink_decoder_free(hsd); - return THEFT_TRIAL_PASS; -} - -TEST decoder_should_always_make_progress(void) { - test_env env = { .limit = 1 << 15 }; - - theft_seed seed; - if (!get_time_seed(&seed)) { FAIL(); } - - struct theft *t = theft_init(0); - struct theft_cfg cfg = { - .name = __func__, - .fun = prop_decoder_should_always_make_progress, - .type_info = { &rbuf_info }, - .seed = seed, - .trials = 10000, - .env = &env, - .progress_cb = progress_cb, - }; - - theft_run_res res = theft_run(t, &cfg); - theft_free(t); - printf("\n"); - ASSERT_EQ(THEFT_RUN_PASS, res); - PASS(); -} - -SUITE(properties) { - RUN_TEST(decoder_fuzzing_should_not_detect_stuck_state); - RUN_TEST(encoded_and_decoded_data_should_match); - RUN_TEST(encoding_data_should_never_increase_it_by_more_than_an_eighth_at_worst); - RUN_TEST(encoder_should_always_make_progress); - RUN_TEST(decoder_should_always_make_progress); -} -#else -struct because_iso_c_requires_at_least_one_declaration; -#endif diff --git a/espfs/heatshrink/test_heatshrink_static.c b/espfs/heatshrink/test_heatshrink_static.c deleted file mode 100644 index e9c9754..0000000 --- a/espfs/heatshrink/test_heatshrink_static.c +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include - -#include "heatshrink_encoder.h" -#include "heatshrink_decoder.h" -#include "greatest.h" - -#if HEATSHRINK_DYNAMIC_ALLOC -#error HEATSHRINK_DYNAMIC_ALLOC must be false for static allocation test suite. -#endif - -SUITE(integration); - -/* The majority of the tests are in test_heatshrink_dynamic, because that allows - * instantiating encoders/decoders with different settings at run-time. */ - -static heatshrink_encoder hse; -static heatshrink_decoder hsd; - -static void fill_with_pseudorandom_letters(uint8_t *buf, uint16_t size, uint32_t seed) { - uint64_t rn = 9223372036854775783; /* prime under 2^64 */ - for (int i=0; i 1) { - printf("\n^^ COMPRESSING\n"); - dump_buf("input", input, input_size); - } - - uint32_t sunk = 0; - uint32_t polled = 0; - while (sunk < input_size) { - ASSERT(heatshrink_encoder_sink(&hse, &input[sunk], input_size - sunk, &count) >= 0); - sunk += count; - if (log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(&hse)); - } - - HSE_poll_res pres; - do { /* "turn the crank" */ - pres = heatshrink_encoder_poll(&hse, &comp[polled], comp_sz - polled, &count); - ASSERT(pres >= 0); - polled += count; - if (log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSER_POLL_MORE); - ASSERT_EQ(HSER_POLL_EMPTY, pres); - if (polled >= comp_sz) FAILm("compression should never expand that much"); - if (sunk == input_size) { - ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(&hse)); - } - } - if (log_lvl > 0) printf("in: %u compressed: %u ", input_size, polled); - uint32_t compressed_size = polled; - sunk = 0; - polled = 0; - - if (log_lvl > 1) { - printf("\n^^ DECOMPRESSING\n"); - dump_buf("comp", comp, compressed_size); - } - while (sunk < compressed_size) { - ASSERT(heatshrink_decoder_sink(&hsd, &comp[sunk], compressed_size - sunk, &count) >= 0); - sunk += count; - if (log_lvl > 1) printf("^^ sunk %zd\n", count); - if (sunk == compressed_size) { - ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(&hsd)); - } - - HSD_poll_res pres; - do { - pres = heatshrink_decoder_poll(&hsd, &decomp[polled], - decomp_sz - polled, &count); - ASSERT(pres >= 0); - polled += count; - if (log_lvl > 1) printf("^^ polled %zd\n", count); - } while (pres == HSDR_POLL_MORE); - ASSERT_EQ(HSDR_POLL_EMPTY, pres); - if (sunk == compressed_size) { - HSD_finish_res fres = heatshrink_decoder_finish(&hsd); - ASSERT_EQ(HSDR_FINISH_DONE, fres); - } - - if (polled > input_size) { - FAILm("Decompressed data is larger than original input"); - } - } - if (log_lvl > 0) printf("decompressed: %u\n", polled); - if (polled != input_size) { - FAILm("Decompressed length does not match original input length"); - } - - if (log_lvl > 1) dump_buf("decomp", decomp, polled); - for (size_t i=0; i out[%zd] == 0x%02x ('%c')\n", - j, input[j], isprint(input[j]) ? input[j] : '.', - j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.'); - } - } - } - ASSERT_EQ(input[i], decomp[i]); - } - free(comp); - free(decomp); - PASS(); -} - -TEST pseudorandom_data_should_match(uint32_t size, uint32_t seed) { - uint8_t input[size]; - fill_with_pseudorandom_letters(input, size, seed); - return compress_and_expand_and_check(input, size, 0); -} - -SUITE(integration) { -#if __STDC_VERSION__ >= 19901L - for (uint32_t size=1; size < 64*1024; size <<= 1) { - if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); - for (uint32_t seed=1; seed<=100; seed++) { - if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); - RUN_TESTp(pseudorandom_data_should_match, size, seed); - } - } -#endif -} - -/* Add all the definitions that need to be in the test runner's main file. */ -GREATEST_MAIN_DEFS(); - -int main(int argc, char **argv) { - GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ - printf("INPUT_BUFFER_SIZE: %u\n", HEATSHRINK_STATIC_INPUT_BUFFER_SIZE); - printf("WINDOW_BITS: %u\n", HEATSHRINK_STATIC_WINDOW_BITS); - printf("LOOKAHEAD_BITS: %u\n", HEATSHRINK_STATIC_LOOKAHEAD_BITS); - - printf("sizeof(heatshrink_encoder): %zd\n", sizeof(heatshrink_encoder)); - printf("sizeof(heatshrink_decoder): %zd\n", sizeof(heatshrink_decoder)); - RUN_SUITE(integration); - GREATEST_MAIN_END(); /* display results */ -} diff --git a/espfs/heatshrink_config_custom.h b/espfs/heatshrink_config_custom.h deleted file mode 100644 index f885f87..0000000 --- a/espfs/heatshrink_config_custom.h +++ /dev/null @@ -1,30 +0,0 @@ -//Heatshrink config for the decompressor. -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -/* Should functionality assuming dynamic allocation be used? */ -#define HEATSHRINK_DYNAMIC_ALLOC 1 - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Optional replacement of malloc/free */ - #ifdef __ets__ - #define HEATSHRINK_MALLOC(SZ) os_malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) os_free(P) - #else - #define HEATSHRINK_MALLOC(SZ) malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) free(P) - #endif -#else - /* Required parameters for static configuration */ - #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 -#endif - -/* Turn on logging for debugging. */ -#define HEATSHRINK_DEBUGGING_LOGS 0 - -/* Use indexing for faster compression. (This requires additional space.) */ -#define HEATSHRINK_USE_INDEX 1 - -#endif diff --git a/espfs/heatshrink_decoder.c b/espfs/heatshrink_decoder.c deleted file mode 100644 index 9a4a9f7..0000000 --- a/espfs/heatshrink_decoder.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "espfs.h" -#ifdef ESPFS_HEATSHRINK -//Stupid wrapper so we don't have to move c-files around -//Also loads httpd-specific config. - -#ifdef __ets__ -//esp build - -#include - -#define memset(x,y,z) os_memset(x,y,z) -#define memcpy(x,y,z) os_memcpy(x,y,z) -#endif - -#include "heatshrink_config_custom.h" -#include "heatshrink/heatshrink_decoder.c" - - -#endif diff --git a/espfs/mkespfsimage/Makefile b/espfs/mkespfsimage/Makefile index 26484d4..b05d5b0 100644 --- a/espfs/mkespfsimage/Makefile +++ b/espfs/mkespfsimage/Makefile @@ -1,36 +1,24 @@ -GZIP_COMPRESSION ?= no -USE_HEATSHRINK ?= yes +GZIP_COMPRESSION?=no -TARGET = mkespfsimage.exe - -CC = gcc -LD = $(CC) -CFLAGS=-c -I../../heatshrink -I.. -Imman-win32 -std=gnu99 -LDFLAGS=-Lmman-win32 -lmman +CFLAGS=-I.. -std=gnu99 ifeq ("$(GZIP_COMPRESSION)","yes") -CFLAGS += -DESPFS_GZIP -LDFLAGS += -lz +LDFLAGS+=-lz +CFLAGS+=-DESPFS_GZIP endif -ifeq ("$(USE_HEATSHRINK)","yes") -CFLAGS += -DESPFS_HEATSHRINK +ifeq ($(OS),Windows_NT) +CFLAGS+=-Imman-win32 +LDFLAGS+=-Lmman-win32 -lmman +TARGET =mkespfsimage.exe +else +TARGET =mkespfsimage endif -OBJECTS = main.o heatshrink_encoder.o - -all: libmman $(TARGET) - -libmman: - $(Q) make -C mman-win32 +OBJS=main.o -$(TARGET): $(OBJECTS) - $(LD) -o $@ $^ $(LDFLAGS) - -%.o: %.c - $(CC) $(CFLAGS) -o $@ $^ +$(TARGET): $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) clean: - rm -f $(OBJECTS) $(TARGET) - -.PHONY: all clean + rm -f $(TARGET) $(OBJS) \ No newline at end of file diff --git a/espfs/mkespfsimage/Makefile.linux b/espfs/mkespfsimage/Makefile.linux deleted file mode 100644 index f1ae1aa..0000000 --- a/espfs/mkespfsimage/Makefile.linux +++ /dev/null @@ -1,24 +0,0 @@ -GZIP_COMPRESSION ?= no -USE_HEATSHRINK ?= yes - -CFLAGS=-I../../heatshrink -I../../include -I.. -std=gnu99 -ifeq ("$(GZIP_COMPRESSION)","yes") -CFLAGS += -DESPFS_GZIP -endif - -ifeq ("$(USE_HEATSHRINK)","yes") -CFLAGS += -DESPFS_HEATSHRINK -endif - -OBJS=main.o heatshrink_encoder.o -TARGET=mkespfsimage - -$(TARGET): $(OBJS) -ifeq ("$(GZIP_COMPRESSION)","yes") - $(CC) -o $@ $^ -lz -else - $(CC) -o $@ $^ -endif - -clean: - rm -f $(TARGET) $(OBJS) \ No newline at end of file diff --git a/espfs/mkespfsimage/Makefile.windows b/espfs/mkespfsimage/Makefile.windows deleted file mode 100644 index 15aa8e3..0000000 --- a/espfs/mkespfsimage/Makefile.windows +++ /dev/null @@ -1,33 +0,0 @@ -GZIP_COMPRESSION ?= no -USE_HEATSHRINK ?= yes - -TARGET = mkespfsimage.exe - -CC = gcc -LD = $(CC) -CFLAGS=-c -I../../heatshrink -I.. -Imman-win32 -std=gnu99 -LDFLAGS=-Lmman-win32 -lmman - -ifeq ("$(GZIP_COMPRESSION)","yes") -CFLAGS += -DESPFS_GZIP -LDFLAGS += -lz -endif - -ifeq ("$(USE_HEATSHRINK)","yes") -CFLAGS += -DESPFS_HEATSHRINK -endif - -OBJECTS = main.o heatshrink_encoder.o - -all: $(TARGET) - -$(TARGET): $(OBJECTS) - $(LD) -o $@ $^ $(LDFLAGS) - -%.o: %.c - $(CC) $(CFLAGS) -o $@ $^ - -clean: - rm -f $(OBJECTS) $(TARGET) - -.PHONY: all clean diff --git a/espfs/mkespfsimage/build-linux.sh b/espfs/mkespfsimage/build-linux.sh deleted file mode 100644 index 69e3166..0000000 --- a/espfs/mkespfsimage/build-linux.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -make -f Makefile.linux clean -make -f Makefile.linux USE_HEATSHRINK="yes" GZIP_COMPRESSION="no" diff --git a/espfs/mkespfsimage/build-win32.sh b/espfs/mkespfsimage/build-win32.sh deleted file mode 100644 index f552ea9..0000000 --- a/espfs/mkespfsimage/build-win32.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -cd mman-win32 -./configure && make -cd .. -make -f Makefile.windows clean -make -f Makefile.windows USE_HEATSHRINK="yes" GZIP_COMPRESSION="no" diff --git a/espfs/mkespfsimage/espfsformat.h b/espfs/mkespfsimage/espfsformat.h deleted file mode 100644 index 8ce5549..0000000 --- a/espfs/mkespfsimage/espfsformat.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ESPROFSFORMAT_H -#define ESPROFSFORMAT_H - -/* -Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module. -Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files, -headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself -when trying to do a <4byte or unaligned read. -*/ - -/* -The idea 'borrows' from cpio: it's basically a concatenation of {header, filename, file} data. -Header, filename and file data is 32-bit aligned. The last file is indicated by data-less header -with the FLAG_LASTFILE flag set. -*/ - - -#define FLAG_LASTFILE (1<<0) -#define FLAG_GZIP (1<<1) -#define COMPRESS_NONE 0 -#define COMPRESS_HEATSHRINK 1 -#define ESPFS_MAGIC 0x73665345 - -typedef struct { - int32_t magic; - int8_t flags; - int8_t compression; - int16_t nameLen; - int32_t fileLenComp; - int32_t fileLenDecomp; -} __attribute__((packed)) EspFsHeader; - -#endif \ No newline at end of file diff --git a/espfs/mkespfsimage/heatshrink_encoder.c b/espfs/mkespfsimage/heatshrink_encoder.c deleted file mode 100644 index fd82c9a..0000000 --- a/espfs/mkespfsimage/heatshrink_encoder.c +++ /dev/null @@ -1,4 +0,0 @@ -//Stupid wraparound include to make sure object file doesn't end up in heatshrink dir -#ifdef ESPFS_HEATSHRINK -#include "../heatshrink/heatshrink_encoder.c" -#endif \ No newline at end of file diff --git a/espfs/mkespfsimage/main.c b/espfs/mkespfsimage/main.c index 20a2b00..be3aeb7 100644 --- a/espfs/mkespfsimage/main.c +++ b/espfs/mkespfsimage/main.c @@ -8,7 +8,7 @@ #include #include "espfs.h" #ifdef __MINGW32__ -#include +#include "mman-win32/mman.h" #else #include #endif @@ -17,16 +17,8 @@ #else #include #endif -#include #include "espfsformat.h" -//Heatshrink -#ifdef ESPFS_HEATSHRINK -#include "../heatshrink/heatshrink_common.h" -#include "../heatshrink/heatshrink_config.h" -#include "../heatshrink/heatshrink_encoder.h" -#endif - //Gzip #ifdef ESPFS_GZIP // If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev" @@ -35,6 +27,7 @@ #endif + //Routines to convert host format to the endianness used in the xtensa short htoxs(short in) { char r[2]; @@ -52,53 +45,6 @@ int htoxl(int in) { return *((int *)r); } -#ifdef ESPFS_HEATSHRINK -size_t compressHeatshrink(char *in, int insize, char *out, int outsize, int level) { - char *inp=in; - char *outp=out; - size_t len; - int ws[]={5, 6, 8, 11, 13}; - int ls[]={3, 3, 4, 4, 4}; - HSE_poll_res pres; - HSE_sink_res sres; - size_t r; - if (level==-1) level=8; - level=(level-1)/2; //level is now 0, 1, 2, 3, 4 - heatshrink_encoder *enc=heatshrink_encoder_alloc(ws[level], ls[level]); - if (enc==NULL) { - perror("allocating mem for heatshrink"); - exit(1); - } - //Save encoder parms as first byte - *outp=(ws[level]<<4)|ls[level]; - outp++; outsize--; - - r=1; - do { - if (insize>0) { - sres=heatshrink_encoder_sink(enc, inp, insize, &len); - if (sres!=HSER_SINK_OK) break; - inp+=len; insize-=len; - if (insize==0) heatshrink_encoder_finish(enc); - } - do { - pres=heatshrink_encoder_poll(enc, outp, outsize, &len); - if (pres!=HSER_POLL_MORE && pres!=HSER_POLL_EMPTY) break; - outp+=len; outsize-=len; - r+=len; - } while (pres==HSER_POLL_MORE); - } while (insize!=0); - - if (insize!=0) { - fprintf(stderr, "Heatshrink: Bug? insize is still %d. sres=%d pres=%d\n", insize, sres, pres); - exit(1); - } - - heatshrink_encoder_free(enc); - return r; -} -#endif - #ifdef ESPFS_GZIP size_t compressGzip(char *in, int insize, char *out, int outsize, int level) { z_stream stream; @@ -184,7 +130,7 @@ int parseGzipExtensions(char *input) { } #endif -int handleFile(int f, char *name, int compression, int level, char **compName) { +int handleFile(int f, char *name, int compression, int level, char **compName, off_t *csizePtr) { char *fdat, *cdat; off_t size, csize; EspFsHeader h; @@ -211,11 +157,6 @@ int handleFile(int f, char *name, int compression, int level, char **compName) { if (compression==COMPRESS_NONE) { csize=size; cdat=fdat; -#ifdef ESPFS_HEATSHRINK - } else if (compression==COMPRESS_HEATSHRINK) { - cdat=malloc(size*2); - csize=compressHeatshrink(fdat, size, cdat, size*2, level); -#endif } else { fprintf(stderr, "Unknown compression - %d\n", compression); exit(1); @@ -238,7 +179,7 @@ int handleFile(int f, char *name, int compression, int level, char **compName) { h.nameLen=htoxs(h.nameLen); h.fileLenComp=htoxl(csize); h.fileLenDecomp=htoxl(size); - + write(1, &h, sizeof(EspFsHeader)); write(1, name, nameLen); while (nameLen&3) { @@ -254,9 +195,7 @@ int handleFile(int f, char *name, int compression, int level, char **compName) { munmap(fdat, size); if (compName != NULL) { - if (h.compression==COMPRESS_HEATSHRINK) { - *compName = "heatshrink"; - } else if (h.compression==COMPRESS_NONE) { + if (h.compression==COMPRESS_NONE) { if (h.flags & FLAG_GZIP) { *compName = "gzip"; } else { @@ -266,6 +205,7 @@ int handleFile(int f, char *name, int compression, int level, char **compName) { *compName = "unknown"; } } + *csizePtr = csize; return (csize*100)/size; } @@ -292,11 +232,7 @@ int main(int argc, char **argv) { int compType; //default compression type - heatshrink int compLvl=-1; -#ifdef ESPFS_HEATSHRINK - compType = COMPRESS_HEATSHRINK; -#else compType = COMPRESS_NONE; -#endif for (x=1; x=x-2) { @@ -318,7 +254,7 @@ int main(int argc, char **argv) { #ifdef ESPFS_GZIP if (gzipExtensions == NULL) { - parseGzipExtensions(strdup("html,css,js")); + parseGzipExtensions(strdup("html,css,js,ico")); } #endif @@ -330,11 +266,7 @@ int main(int argc, char **argv) { #endif fprintf(stderr, "> out.espfs\n"); fprintf(stderr, "Compressors:\n"); -#ifdef ESPFS_HEATSHRINK - fprintf(stderr, "0 - None\n1 - Heatshrink(default)\n"); -#else fprintf(stderr, "0 - None(default)\n"); -#endif fprintf(stderr, "\nCompression level: 1 is worst but low RAM usage, higher is better compression \nbut uses more ram on decompression. -1 = compressors default.\n"); #ifdef ESPFS_GZIP fprintf(stderr, "\nGzipped extensions: list of comma separated, case sensitive file extensions \nthat will be gzipped. Defaults to 'html,css,js'\n"); @@ -359,8 +291,9 @@ int main(int argc, char **argv) { f=open(fileName, O_RDONLY); if (f>0) { char *compName = "unknown"; - rate=handleFile(f, realName, compType, compLvl, &compName); - fprintf(stderr, "%s (%d%%, %s)\n", realName, rate, compName); + off_t csize; + rate=handleFile(f, realName, compType, compLvl, &compName, &csize); + fprintf(stderr, "%-16s (%3d%%, %s, %4u bytes)\n", realName, rate, compName, (uint32_t)csize); close(f); } else { perror(fileName); diff --git a/include/esp8266.h b/include/esp8266.h index ce92455..96a7364 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -13,7 +13,10 @@ #include #include #include -#include #include "espmissingincludes.h" #include "uart_hw.h" + +#ifdef __WIN32__ +#include <_mingw.h> +#endif diff --git a/include/espmissingincludes.h b/include/espmissingincludes.h index c69b9fc..e5a20a0 100644 --- a/include/espmissingincludes.h +++ b/include/espmissingincludes.h @@ -1,9 +1,8 @@ #ifndef ESPMISSINGINCLUDES_H #define ESPMISSINGINCLUDES_H -#include -#include -#include +#include +#include //Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. //MOST OF THESE ARE GUESSED! but they seem to work and shut up the compiler. @@ -54,6 +53,11 @@ int rand(void); void ets_bzero(void *s, size_t n); void ets_delay_us(int ms); +// Shortcuts for memory functions +#define os_malloc pvPortMalloc +#define os_free vPortFree +#define os_zalloc pvPortZalloc + // disappeared in SDK 1.1.0: #define os_timer_done ets_timer_done #define os_timer_handler_isr ets_timer_handler_isr From 80f3788564165858042fd7c2fb70677fb83123f6 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 22 Aug 2015 06:08:09 -0500 Subject: [PATCH 034/104] Added Makefile comment --- Makefile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index af5fcbf..9047faa 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ 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 +# Windows users use the com port i.e: ESPPORT ?= com3 ESPTOOL ?= $(abspath ../esp-open-sdk/esptool/esptool.py) ESPPORT ?= /dev/ttyUSB0 ESPBAUD ?= 460800 @@ -132,10 +133,10 @@ YUI-COMPRESSOR ?= yuicompressor-2.4.8.jar # Output directors to store intermediate compiled files # relative to the project directory BUILD_BASE = build -FW_BASE = firmware +FW_BASE = firmware # name for the target project -TARGET = httpd +TARGET = httpd # espressif tool to concatenate sections for OTA upload using bootloader v1.2+ APPGEN_TOOL ?= gen_appbin.py @@ -145,10 +146,10 @@ MODULES = espfs httpd user serial EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal phy pp net80211 wpa main lwip +LIBS = c gcc hal phy pp net80211 wpa main lwip # compiler flags using during compilation of source files -CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ +CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ -nostdlib -mlongcalls -mtext-section-literals -ffunction-sections -fdata-sections \ -D__ets__ -DICACHE_FLASH -D_STDINT_H -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) \ -DMCU_RESET_PIN=$(MCU_RESET_PIN) -DMCU_ISP_PIN=$(MCU_ISP_PIN) \ @@ -208,11 +209,11 @@ vecho := @echo endif ifeq ("$(GZIP_COMPRESSION)","yes") -CFLAGS += -DGZIP_COMPRESSION +CFLAGS += -DGZIP_COMPRESSION endif ifeq ("$(CHANGE_TO_STA)","yes") -CFLAGS += -DCHANGE_TO_STA +CFLAGS += -DCHANGE_TO_STA endif vpath %.c $(SRC_DIR) @@ -289,9 +290,9 @@ flash: all $(ET_BLANK) $(SDK_BASE)/bin/blank.bin yui/$(YUI-COMPRESSOR): - $(Q) mkdir -p yui + $(Q) mkdir -p yui ifeq ($(OS),Windows_NT) - cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) + cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) else cd yui; wget https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) endif From 706ae4844e80f269910054711cdd20471df6e839 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 22 Aug 2015 06:42:57 -0500 Subject: [PATCH 035/104] Weird line endings or something throwing make error, fixed now. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9047faa..7a74e19 100644 --- a/Makefile +++ b/Makefile @@ -290,9 +290,9 @@ flash: all $(ET_BLANK) $(SDK_BASE)/bin/blank.bin yui/$(YUI-COMPRESSOR): - $(Q) mkdir -p yui + $(Q) mkdir -p yui ifeq ($(OS),Windows_NT) - cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) + cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) else cd yui; wget https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) endif From 7490e45aab45854e675830b1da22635343e773e4 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 22 Aug 2015 10:29:51 -0700 Subject: [PATCH 036/104] 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 037/104] 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 038/104] 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 From 6cbad0d16000e5b8bf70c5baacfcf8e431a5566d Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Mon, 24 Aug 2015 13:47:36 -0500 Subject: [PATCH 039/104] Fixed merge misses --- serial/console.c | 51 ---------------------------------------------- serial/serbridge.c | 26 ----------------------- 2 files changed, 77 deletions(-) diff --git a/serial/console.c b/serial/console.c index 00a59d3..d0d0dd7 100644 --- a/serial/console.c +++ b/serial/console.c @@ -45,13 +45,8 @@ console_prev(void) { void ICACHE_FLASH_ATTR console_write_char(char c) { -<<<<<<< HEAD - //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); ->>>>>>> master } int ICACHE_FLASH_ATTR @@ -88,51 +83,6 @@ ajaxConsoleBaud(HttpdConnData *connData) { int ICACHE_FLASH_ATTR ajaxConsole(HttpdConnData *connData) { -<<<<<<< HEAD - 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 @@ -176,7 +126,6 @@ ajaxConsole(HttpdConnData *connData) { os_strcpy(buff+len, "\"}"); len+=2; httpdSend(connData, buff, len); return HTTPD_CGI_DONE; ->>>>>>> master } void ICACHE_FLASH_ATTR consoleInit() { diff --git a/serial/serbridge.c b/serial/serbridge.c index f583ffe..415772b 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -334,31 +334,6 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { //===== Initialization void ICACHE_FLASH_ATTR serbridgeInitPins() { -<<<<<<< HEAD - 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", @@ -382,7 +357,6 @@ void ICACHE_FLASH_ATTR serbridgeInitPins() { // 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); ->>>>>>> master } // Start transparent serial bridge TCP server on specified port (typ. 23) From f3d18243aeae0ba38fc6f25fde51a3d5225346e9 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Mon, 24 Aug 2015 14:02:06 -0500 Subject: [PATCH 040/104] Reverting some things that didn't merge right --- Makefile | 12 ++++++------ esp-link.vcxproj | 12 ++++++++++++ user/config.c | 2 ++ user/config.h | 2 ++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index a60880d..61cd321 100644 --- a/Makefile +++ b/Makefile @@ -31,14 +31,14 @@ 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 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 +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 @@ -330,11 +330,11 @@ 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 '}' \ diff --git a/esp-link.vcxproj b/esp-link.vcxproj index d462b62..558f09a 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -63,6 +63,10 @@ + + + + @@ -73,12 +77,15 @@ + + + @@ -86,6 +93,9 @@ + + + @@ -98,12 +108,14 @@ + + diff --git a/user/config.c b/user/config.c index b8122be..b44b329 100644 --- a/user/config.c +++ b/user/config.c @@ -18,6 +18,8 @@ FlashConfig flashDefault = { 0, 0x00ffffff, 0, // static ip, netmask, gateway 0, // log mode 0, // swap uart (don't by default) + 1, 0, // tcp_enable, rssi_enable + "\0", // api_key }; typedef union { diff --git a/user/config.h b/user/config.h index d92e7f2..18284a5 100644 --- a/user/config.h +++ b/user/config.h @@ -14,6 +14,8 @@ typedef struct { 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; From c68a281580972a31c3c65058b3349c183aacf5c3 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Mon, 24 Aug 2015 14:30:24 -0500 Subject: [PATCH 041/104] Fixed VS includes and replaced missing cmd module --- Makefile | 2 +- esp-link.vcxproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 61cd321..08f0dcd 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,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 . # libraries used in this project, mainly provided by the SDK diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 558f09a..1fc9e5c 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\x86_64-w64-mingw32;c:\tools\mingw64\x86_64-w64-mingw32\include\c++\backward;c:\tools\mingw64\x86_64-w64-mingw32\include\c++;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include-fixed;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include + .\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;C:\tools\mingw64\include From c586f38765e04fd5caa2e9936a0c7dbfd7ee8c2f Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Mon, 24 Aug 2015 15:48:12 -0500 Subject: [PATCH 042/104] swapped out for offsite web files --- Makefile | 6 +- cmd/cmd.h | 1 + esp-link.vcxproj | 2 +- html-old/console.html | 46 +++++++++++++++ {html => html-old}/console.js | 0 {html => html-old}/favicon.ico | Bin html-old/head- | 10 ++++ html-old/home.html | 87 ++++++++++++++++++++++++++++ {html => html-old}/jl-400x110.png- | Bin html-old/log.html | 48 ++++++++++++++++ {html => html-old}/pure.css | 0 {html => html-old}/style.css | 0 {html => html-old}/ui.js | 0 {html => html-old}/wifi/icons.png | Bin html-old/wifi/wifi.html | 84 +++++++++++++++++++++++++++ {html => html-old}/wifi/wifi.js | 0 html/console.html | 47 +-------------- html/head- | 10 ---- html/home.html | 88 +---------------------------- html/log.html | 49 +--------------- html/wifi/wifi.html | 85 +--------------------------- user/user_main.c | 59 ++++++++++--------- 22 files changed, 317 insertions(+), 305 deletions(-) create mode 100644 html-old/console.html rename {html => html-old}/console.js (100%) rename {html => html-old}/favicon.ico (100%) mode change 100755 => 100644 create mode 100644 html-old/head- create mode 100644 html-old/home.html rename {html => html-old}/jl-400x110.png- (100%) mode change 100755 => 100644 create mode 100644 html-old/log.html rename {html => html-old}/pure.css (100%) rename {html => html-old}/style.css (100%) rename {html => html-old}/ui.js (100%) rename {html => html-old}/wifi/icons.png (100%) create mode 100644 html-old/wifi/wifi.html rename {html => html-old}/wifi/wifi.js (100%) diff --git a/Makefile b/Makefile index 08f0dcd..a3df665 100644 --- a/Makefile +++ b/Makefile @@ -25,13 +25,13 @@ SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.3.0) # Typically you'll use https://github.com/themadinventor/esptool # Windows users use the com port i.e: ESPPORT ?= com3 ESPTOOL ?= $(abspath ../esp-open-sdk/esptool/esptool.py) -ESPPORT ?= /dev/ttyUSB0 -ESPBAUD ?= 460800 +ESPPORT ?= com10 +ESPBAUD ?= 230400 # --------------- chipset configuration --------------- # Pick your flash size: "512KB", "1MB", 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/cmd/cmd.h b/cmd/cmd.h index 5a66bc7..23bd584 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -4,6 +4,7 @@ #ifndef CMD_H #define CMD_H +#include // Escape chars used by tuanpmt, dunno why he didn't use std ones... #define SLIP_START 0x7E diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 1fc9e5c..2a774e9 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;C:\tools\mingw64\include + .\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include diff --git a/html-old/console.html b/html-old/console.html new file mode 100644 index 0000000..db4c0a6 --- /dev/null +++ b/html-old/console.html @@ -0,0 +1,46 @@ +
+
+

Microcontroller Console

+
+ +
+

The Microcontroller console shows the last 1024 characters + received from UART0, to which a microcontroller is typically attached. + The UART is configured for 8 bits, no parity, 1 stop bit (8N1).

+

+ Reset µC +  Baud: + +

+

+    
+
+
+ + + + + diff --git a/html/console.js b/html-old/console.js similarity index 100% rename from html/console.js rename to html-old/console.js diff --git a/html/favicon.ico b/html-old/favicon.ico old mode 100755 new mode 100644 similarity index 100% rename from html/favicon.ico rename to html-old/favicon.ico diff --git a/html-old/head- b/html-old/head- new file mode 100644 index 0000000..514ff53 --- /dev/null +++ b/html-old/head- @@ -0,0 +1,10 @@ + + + esp-link + + + + + + +
diff --git a/html-old/home.html b/html-old/home.html new file mode 100644 index 0000000..46a984d --- /dev/null +++ b/html-old/home.html @@ -0,0 +1,87 @@ +
+
+
JEELABS
+

esp-link

+

+
+ +
+
+
+

The JeeLabs esp-link firmware bridges the ESP8266 serial port to Wifi and can + program microcontrollers over the serial port, in particular Arduinos, AVRs, and + NXP's LPC800 and other ARM processors.

+

Program an Arduino/AVR using avrdude using a command + line similar to:

+
/home/arduino-1.0.5/hardware/tools/avrdude \
+   -DV -patmega328p -Pnet:esp-link.local:23 -carduino -b115200 -U \
+   -C /home/arduino-1.0.5/hardware/tools/avrdude.conf flash:w:my_sketch.hex:i +
+

where -Pnet:esp-link.local:23 tells avrdude to connect to port 23 of esp-link. + You can substitute the IP address of your esp-link for esp-link.local if necessary.

+

Please refer to + the online README + for up-to-date help and to the forthcoming + JeeLabs blog for an intro to the codebase.

+
+
+
+
+
+

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 +
+
+
+
+
+
+
+
+
+
+ + + diff --git a/html/jl-400x110.png- b/html-old/jl-400x110.png- old mode 100755 new mode 100644 similarity index 100% rename from html/jl-400x110.png- rename to html-old/jl-400x110.png- diff --git a/html-old/log.html b/html-old/log.html new file mode 100644 index 0000000..46ec785 --- /dev/null +++ b/html-old/log.html @@ -0,0 +1,48 @@ +
+
+

Debug Log

+
+ +
+

The debug log shows the most recent characters printed by the esp-link software itself to + its own debug log.

+
+

+ Refresh +

+

+ UART debug log: + auto + off + on +

+
+

+    
+
+ + + + + + diff --git a/html/pure.css b/html-old/pure.css similarity index 100% rename from html/pure.css rename to html-old/pure.css diff --git a/html/style.css b/html-old/style.css similarity index 100% rename from html/style.css rename to html-old/style.css diff --git a/html/ui.js b/html-old/ui.js similarity index 100% rename from html/ui.js rename to html-old/ui.js diff --git a/html/wifi/icons.png b/html-old/wifi/icons.png similarity index 100% rename from html/wifi/icons.png rename to html-old/wifi/icons.png diff --git a/html-old/wifi/wifi.html b/html-old/wifi/wifi.html new file mode 100644 index 0000000..b6c142b --- /dev/null +++ b/html-old/wifi/wifi.html @@ -0,0 +1,84 @@ +
+
+

Wifi Configuration

+
+ +
+
+
+

Wifi State

+
+ + + + + + + + + + + +
+
+

Wifi Association

+ +
+ To connect to a WiFi network, please select one of the detected networks, + enter the password, and hit the connect button... + +
Scanning...
+ + + +
+
+
+
+
+

Special Settings

+
+ Special settings, use with care! +
+ + +
+
+ + +
+
+ + + + + + +
+ +
+
+
+
+
+ + + + + + diff --git a/html/wifi/wifi.js b/html-old/wifi/wifi.js similarity index 100% rename from html/wifi/wifi.js rename to html-old/wifi/wifi.js diff --git a/html/console.html b/html/console.html index db4c0a6..549ae82 100644 --- a/html/console.html +++ b/html/console.html @@ -1,46 +1 @@ -
-
-

Microcontroller Console

-
- -
-

The Microcontroller console shows the last 1024 characters - received from UART0, to which a microcontroller is typically attached. - The UART is configured for 8 bits, no parity, 1 stop bit (8N1).

-

- Reset µC -  Baud: - -

-

-    
-
- - - - - - + diff --git a/html/head- b/html/head- index 514ff53..e69de29 100644 --- a/html/head- +++ b/html/head- @@ -1,10 +0,0 @@ - - - esp-link - - - - - - -
diff --git a/html/home.html b/html/home.html index 46a984d..2677264 100644 --- a/html/home.html +++ b/html/home.html @@ -1,87 +1 @@ -
-
-
JEELABS
-

esp-link

-

-
- -
-
-
-

The JeeLabs esp-link firmware bridges the ESP8266 serial port to Wifi and can - program microcontrollers over the serial port, in particular Arduinos, AVRs, and - NXP's LPC800 and other ARM processors.

-

Program an Arduino/AVR using avrdude using a command - line similar to:

-
/home/arduino-1.0.5/hardware/tools/avrdude \
-   -DV -patmega328p -Pnet:esp-link.local:23 -carduino -b115200 -U \
-   -C /home/arduino-1.0.5/hardware/tools/avrdude.conf flash:w:my_sketch.hex:i -
-

where -Pnet:esp-link.local:23 tells avrdude to connect to port 23 of esp-link. - You can substitute the IP address of your esp-link for esp-link.local if necessary.

-

Please refer to - the online README - for up-to-date help and to the forthcoming - JeeLabs blog for an intro to the codebase.

-
-
-
-
-
-

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 -
-
-
-
-
-
-
-
-
-
- - - + diff --git a/html/log.html b/html/log.html index 46ec785..7bec267 100644 --- a/html/log.html +++ b/html/log.html @@ -1,48 +1 @@ -
-
-

Debug Log

-
- -
-

The debug log shows the most recent characters printed by the esp-link software itself to - its own debug log.

-
-

- Refresh -

-

- UART debug log: - auto - off - on -

-
-

-    
-
- - - - - - + diff --git a/html/wifi/wifi.html b/html/wifi/wifi.html index b6c142b..a6dd616 100644 --- a/html/wifi/wifi.html +++ b/html/wifi/wifi.html @@ -1,84 +1 @@ -
-
-

Wifi Configuration

-
- -
-
-
-

Wifi State

-
- - - - - - - - - - - -
-
-

Wifi Association

- -
- To connect to a WiFi network, please select one of the detected networks, - enter the password, and hit the connect button... - -
Scanning...
- - - -
-
-
-
-
-

Special Settings

-
- Special settings, use with care! -
- - -
-
- - -
-
- - - - - - -
- -
-
-
-
-
- - - - - - + diff --git a/user/user_main.c b/user/user_main.c index 54ca59e..cf6d266 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -48,7 +48,6 @@ int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pas return 0; } - /* This is the main url->function dispatching data struct. In short, it's a struct with various URLs plus their handlers. The handlers can @@ -95,7 +94,6 @@ HttpdBuiltInUrl builtInUrls[]={ {NULL, NULL, NULL} }; - //#define SHOW_HEAP_USE #ifdef SHOW_HEAP_USE @@ -106,7 +104,36 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { } #endif +# define VERS_STR_STR(V) #V +# define VERS_STR(V) VERS_STR_STR(V) +char *esp_link_version = VERS_STR(VERSION); + void user_rf_pre_init(void) { + // get the flash config so we know how to init things + //configWipe(); // uncomment to reset the config for testing purposes + bool restoreOk = configRestore(); + + // init gpio pin registers + gpio_init(); + + // init UART + uart_init(flashConfig.baud_rate, 115200); + logInit(); // must come after init of uart + + // say hello (leave some time to cause break in TX after boot loader's msg + os_delay_us(10000L); + os_printf("\n\n** %s\n", esp_link_version); + os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); + + // Status LEDs + statusInit(); + serledInit(); + +#ifdef SHOW_HEAP_USE + os_timer_disarm(&prHeapTimer); + os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); + os_timer_arm(&prHeapTimer, 10000, 1); +#endif } // address of espfs binary blob @@ -116,43 +143,23 @@ static char *rst_codes[] = { "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", }; -# define VERS_STR_STR(V) #V -# define VERS_STR(V) VERS_STR_STR(V) -char *esp_link_version = VERS_STR(VERSION); - //Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. void user_init(void) { - // get the flash config so we know how to init things - //configWipe(); // uncomment to reset the config for testing purposes - bool restoreOk = configRestore(); - // init gpio pin registers - gpio_init(); - // init UART - uart_init(flashConfig.baud_rate, 115200); - logInit(); // must come after init of uart - // say hello (leave some time to cause break in TX after boot loader's msg - os_delay_us(10000L); - os_printf("\n\n** %s\n", esp_link_version); - os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); - // Status LEDs - statusInit(); - serledInit(); // Wifi wifiInit(); + // init the flash filesystem with the html stuff espFsInit(&_binary_espfs_img_start); + //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); //os_printf("espFsInit %s\n", res?"ERR":"ok"); + // mount the http handlers httpdInit(builtInUrls, 80); + // init the wifi-serial transparent bridge (port 23) serbridgeInit(23); uart_add_recv_cb(&serbridgeUartCb); -#ifdef SHOW_HEAP_USE - os_timer_disarm(&prHeapTimer); - os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); - os_timer_arm(&prHeapTimer, 10000, 1); -#endif struct rst_info *rst_info = system_get_rst_info(); os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); From a2b5f94e1422d69fe6896ec87dec396316efc96d Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 28 Aug 2015 12:33:23 -0700 Subject: [PATCH 043/104] send initial wifi state to MCU --- cmd/cmd.c | 2 +- cmd/handlers.c | 4 ++++ user/cgiwifi.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/cmd.c b/cmd/cmd.c index 06430ca..64aeef7 100644 --- a/cmd/cmd.c +++ b/cmd/cmd.c @@ -88,7 +88,7 @@ CMD_Exec(const CmdList *scp, CmdPacket *packet) { 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); + os_printf("CMD: Response: 0x%lx, cmd: %d\r\n", ret, packet->cmd); crc = CMD_ResponseStart(packet->cmd, 0, ret, 0); CMD_ResponseEnd(crc); } else { diff --git a/cmd/handlers.c b/cmd/handlers.c index 73370dd..396a43e 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -70,10 +70,14 @@ CMD_WifiCb(uint8_t wifiStatus) { // Command handler for Wifi connect command static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd) { + os_printf("CMD: Wifi connect\n"); 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 + // trigger an immediate callback with the current status + lastWifiStatus = 0xff; + CMD_WifiCb(wifiState); return 1; } diff --git a/user/cgiwifi.h b/user/cgiwifi.h index c78a793..d73628e 100644 --- a/user/cgiwifi.h +++ b/user/cgiwifi.h @@ -14,6 +14,7 @@ int cgiWiFiConnStatus(HttpdConnData *connData); int cgiWiFiSpecial(HttpdConnData *connData); void wifiInit(void); +extern uint8_t wifiState; extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes #endif From fb1d7f6298dd5699bbf874ca104ceab50905806e Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 28 Aug 2015 12:33:36 -0700 Subject: [PATCH 044/104] REST library fixes --- cmd/rest.c | 246 ++++++++++++++++++++++++++++++----------------------- cmd/rest.h | 13 +-- 2 files changed, 146 insertions(+), 113 deletions(-) diff --git a/cmd/rest.c b/cmd/rest.c index 133bf2f..08afd09 100644 --- a/cmd/rest.c +++ b/cmd/rest.c @@ -1,19 +1,30 @@ -/* - * api.c - * - * Created on: Mar 4, 2015 - * Author: Minh - */ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// +// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Mar 4, 2015, Author: Minh + #include "esp8266.h" #include "rest.h" #include "cmd.h" +// Connection pool for REST clients. Attached MCU's just call REST_setup and this allocates +// a connection, They never call any 'free' and given that the attached MCU could restart at +// any time, we cannot really rely on the attached MCU to call 'free' ever, so better do without. +// Instead, we allocate a fixed pool of connections an round-robin. What this means is that the +// attached MCU should really use at most as many REST connections as there are slots in the pool. +#define MAX_REST 4 +static RestClient restClient[MAX_REST]; +static uint8_t restNum = 0xff; // index into restClient for next slot to allocate +#define REST_CB 0xdead0000 // fudge added to callback for arduino so we can detect problems + 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; + struct espconn *pespconn = (struct espconn *)arg; + RestClient* client = (RestClient *)pespconn->reverse; + // free the data buffer, if we have one + if (client->data) os_free(client->data); + client->data = 0; } static void ICACHE_FLASH_ATTR @@ -78,32 +89,46 @@ tcpclient_recv(void *arg, char *pdata, unsigned short len) { static void ICACHE_FLASH_ATTR tcpclient_sent_cb(void *arg) { - //struct espconn *pCon = (struct espconn *)arg; - //RestClient* client = (RestClient *)pCon->reverse; + struct espconn *pCon = (struct espconn *)arg; + RestClient* client = (RestClient *)pCon->reverse; os_printf("REST: Sent\n"); + if (client->data_sent != client->data_len) { + // we only sent part of the buffer, send the rest + espconn_sent(client->pCon, (uint8_t*)(client->data+client->data_sent), + client->data_len-client->data_sent); + client->data_sent = client->data_len; + } else { + // we're done sending, free the memory + if (client->data) os_free(client->data); + client->data = 0; + } } static void ICACHE_FLASH_ATTR tcpclient_connect_cb(void *arg) { struct espconn *pCon = (struct espconn *)arg; RestClient* client = (RestClient *)pCon->reverse; + os_printf("REST #%d: connected\n", client-restClient); espconn_regist_disconcb(client->pCon, tcpclient_discon_cb); - espconn_regist_recvcb(client->pCon, tcpclient_recv);//////// - espconn_regist_sentcb(client->pCon, tcpclient_sent_cb);/////// + espconn_regist_recvcb(client->pCon, tcpclient_recv); + espconn_regist_sentcb(client->pCon, tcpclient_sent_cb); + client->data_sent = client->data_len <= 1400 ? client->data_len : 1400; + os_printf("REST #%d: sending %d\n", client-restClient, client->data_sent); //if(client->security){ - // espconn_secure_sent(client->pCon, client->data, client->data_len); + // espconn_secure_sent(client->pCon, client->data, client->data_sent); //} //else{ - espconn_sent(client->pCon, client->data, client->data_len); + espconn_sent(client->pCon, (uint8_t*)client->data, client->data_sent); //} } static void ICACHE_FLASH_ATTR tcpclient_recon_cb(void *arg, sint8 errType) { - //struct espconn *pCon = (struct espconn *)arg; - //RestClient* client = (RestClient *)pCon->reverse; + struct espconn *pCon = (struct espconn *)arg; + RestClient* client = (RestClient *)pCon->reverse; + os_printf("REST $%d: conn reset\n", client-restClient); } static void ICACHE_FLASH_ATTR @@ -137,66 +162,68 @@ rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { 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; + if(CMD_GetArgc(&req) != 3) return 0; // get the hostname - len = CMD_ArgLen(&req); - os_printf("REST: len=%d\n", len); + uint16_t len = CMD_ArgLen(&req); if (len > 128) return 0; // safety check - rest_host = (uint8_t*)os_zalloc(len + 1); + uint8_t *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); + + // clear connection structures the first time + if (restNum == 0xff) { + os_memset(restClient, 0, MAX_REST * sizeof(RestClient)); + restNum = 0; + } // allocate a connection structure - client = (RestClient*)os_zalloc(sizeof(RestClient)); + RestClient *client = restClient + restNum; + uint8_t clientNum = restNum; + restNum = (restNum+1)%MAX_REST; + + // free any data structure that may be left from a previous connection + if (client->header) os_free(client->header); + if (client->content_type) os_free(client->content_type); + if (client->user_agent) os_free(client->user_agent); + if (client->data) os_free(client->data); + if (client->pCon) { + if (client->pCon->proto.tcp) os_free(client->pCon->proto.tcp); + os_free(client->pCon); + } 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); + os_printf("REST: setup #%d host=%s port=%ld security=%ld\n", clientNum, rest_host, port, security); client->resp_cb = cmd->callback; - client->host = rest_host; + client->host = (char *)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 = (char*)os_zalloc(4); client->header[0] = 0; - client->content_type = (uint8_t*)os_zalloc(22); + client->content_type = (char*)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); + client->user_agent = (char*)os_zalloc(9); os_sprintf((char *)client->user_agent, "esp-link"); client->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); @@ -209,54 +236,56 @@ REST_Setup(CmdPacket *cmd) { client->pCon->reverse = client; - return (uint32_t)client; + return REST_CB | (uint32_t)clientNum; } 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 client + uint32_t clientNum; + if (CMD_PopArg(&req, (uint8_t*)&clientNum, 4)) return 0; + if ((clientNum & 0xffff0000) != REST_CB) return 0; + RestClient *client = restClient + ((clientNum & 0xffff) % MAX_REST); // Get header selector + uint32_t header_index; if (CMD_PopArg(&req, (uint8_t*)&header_index, 4)) return 0; // Get header value - len = CMD_ArgLen(&req); + uint16_t 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); + if(client->header) os_free(client->header); + client->header = (char*)os_zalloc(len + 3); CMD_PopArg(&req, (uint8_t*)client->header, len); - client->header[len] = 0; + client->header[len] = '\r'; + client->header[len+1] = '\n'; + client->header[len+2] = 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); + if(client->content_type) os_free(client->content_type); + client->content_type = (char*)os_zalloc(len + 3); CMD_PopArg(&req, (uint8_t*)client->content_type, len); - client->content_type[len] = 0; + client->content_type[len] = '\r'; + client->content_type[len+1] = '\n'; + client->content_type[len+2] = 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); + if(client->user_agent) os_free(client->user_agent); + client->user_agent = (char*)os_zalloc(len + 3); CMD_PopArg(&req, (uint8_t*)client->user_agent, len); - client->user_agent[len] = 0; + client->user_agent[len] = '\r'; + client->user_agent[len+1] = '\n'; + client->user_agent[len+2] = 0; os_printf("REST: Set user_agent: %s\r\n", client->user_agent); break; } @@ -266,76 +295,76 @@ REST_SetHeader(CmdPacket *cmd) { 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); + os_printf("REST: request"); - 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 client + uint32_t clientNum; + if (CMD_PopArg(&req, (uint8_t*)&clientNum, 4)) goto fail; + if ((clientNum & 0xffff0000) != REST_CB) goto fail; + clientNum &= 0xffff; + RestClient *client = restClient + clientNum % MAX_REST; + os_printf(" #%ld", clientNum); // Get HTTP method - len = CMD_ArgLen(&req); - if (len > 15) return 0; + uint16_t len = CMD_ArgLen(&req); + if (len > 15) goto fail; + char method[16]; CMD_PopArg(&req, method, len); method[len] = 0; + os_printf(" method=%s", method); // Get HTTP path len = CMD_ArgLen(&req); - if (len > 1023) return 0; + if (len > 1023) goto fail; + char path[1024]; CMD_PopArg(&req, path, len); path[len] = 0; + os_printf(" path=%s", path); // Get HTTP body - if (CMD_GetArgc(&req) == 3){ + uint32_t realLen = 0; + 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; + if (len > 2048 || realLen > len) goto fail; } - - 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); + os_printf(" bodyLen=%ld", realLen); + + // we need to allocate memory for the header plus the body. First we count the length of the + // header (including some extra counted "%s" and then we add the body length. We allocate the + // whole shebang and copy everything into it. + char *headerFmt = "%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"; + uint16_t headerLen = strlen(headerFmt) + strlen(method) + strlen(path) + strlen(client->host) + + strlen(client->header) + strlen(client->content_type) + strlen(client->user_agent); + os_printf(" hdrLen=%d", headerLen); + if (client->data) os_free(client->data); + client->data = (char*)os_zalloc(headerLen + realLen); + if (client->data == NULL) goto fail; + os_printf(" totLen=%ld data=%p", headerLen + realLen, client->data); + client->data_len = os_sprintf((char*)client->data, headerFmt, method, path, client->host, + client->header, realLen, client->content_type, client->user_agent); + os_printf(" hdrLen=%d", client->data_len); + + if (realLen > 0) { + CMD_PopArg(&req, client->data + client->data_len, 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); + os_printf("\n"); 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); @@ -350,8 +379,11 @@ REST_Request(CmdPacket *cmd) { espconn_gethostbyname(client->pCon, (char *)client->host, &client->ip, rest_dns_found); } - if(body) os_free(body); return 1; + +fail: + os_printf("\n"); + return 0; } uint8_t ICACHE_FLASH_ATTR diff --git a/cmd/rest.h b/cmd/rest.h index 13159ea..7161e1f 100644 --- a/cmd/rest.h +++ b/cmd/rest.h @@ -19,16 +19,17 @@ typedef enum { } HEADER_TYPE; typedef struct { - uint8_t *host; + char *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; + char *header; + char *data; + uint16_t data_len; + uint16_t data_sent; + char *content_type; + char *user_agent; uint32_t resp_cb; } RestClient; From 38922383883cd06a6b2cfd408de476dffbf8095c Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 15:00:59 -0500 Subject: [PATCH 045/104] dynamic arduino callback support --- cmd/cmd.h | 24 +++-- cmd/handlers.c | 102 +++++++++++++++++-- {html-old => httpd/html-old}/console.html | 0 {html-old => httpd/html-old}/console.js | 0 {html-old => httpd/html-old}/favicon.ico | Bin {html-old => httpd/html-old}/head- | 0 {html-old => httpd/html-old}/home.html | 0 {html-old => httpd/html-old}/jl-400x110.png- | Bin {html-old => httpd/html-old}/log.html | 0 {html-old => httpd/html-old}/pure.css | 0 {html-old => httpd/html-old}/style.css | 0 {html-old => httpd/html-old}/ui.js | 0 {html-old => httpd/html-old}/wifi/icons.png | Bin {html-old => httpd/html-old}/wifi/wifi.html | 0 {html-old => httpd/html-old}/wifi/wifi.js | 0 15 files changed, 110 insertions(+), 16 deletions(-) rename {html-old => httpd/html-old}/console.html (100%) rename {html-old => httpd/html-old}/console.js (100%) rename {html-old => httpd/html-old}/favicon.ico (100%) rename {html-old => httpd/html-old}/head- (100%) rename {html-old => httpd/html-old}/home.html (100%) rename {html-old => httpd/html-old}/jl-400x110.png- (100%) rename {html-old => httpd/html-old}/log.html (100%) rename {html-old => httpd/html-old}/pure.css (100%) rename {html-old => httpd/html-old}/style.css (100%) rename {html-old => httpd/html-old}/ui.js (100%) rename {html-old => httpd/html-old}/wifi/icons.png (100%) rename {html-old => httpd/html-old}/wifi/wifi.html (100%) rename {html-old => httpd/html-old}/wifi/wifi.js (100%) diff --git a/cmd/cmd.h b/cmd/cmd.h index 23bd584..b489108 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -20,12 +20,12 @@ #define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte #endif -typedef struct __attribute((__packed__)) { +typedef struct __attribute__((__packed__)) { uint16_t len; // length of data uint8_t data[0]; // really data[len] } CmdArg; -typedef struct __attribute((__packed__)) { +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 (?) @@ -54,7 +54,9 @@ typedef enum { CMD_REST_SETUP, // (11) CMD_REST_REQUEST, CMD_REST_SETHEADER, - CMD_REST_EVENTS + CMD_REST_EVENTS, + CMD_ADD_SENSOR, // 15 + CMD_SENSOR_EVENTS } CmdName; typedef uint32_t (*cmdfunc_t)(CmdPacket *cmd); @@ -64,18 +66,24 @@ typedef struct { cmdfunc_t sc_function; } CmdList; -void CMD_parse_packet(uint8_t *buf, short len); +typedef struct { + char name[16]; + uint32_t callback; +} cmdCallback; + +void ICACHE_FLASH_ATTR CMD_parse_packet(uint8_t *buf, short len); +cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name); // Responses // Start a response, returns the partial CRC -uint16_t CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc); +uint16_t ICACHE_FLASH_ATTR 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); +uint16_t ICACHE_FLASH_ATTR CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len); // Ends a response -void CMD_ResponseEnd(uint16_t crc); +void ICACHE_FLASH_ATTR CMD_ResponseEnd(uint16_t crc); -void CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]); +//void ICACHE_FLASH_ATTR CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]); // Requests diff --git a/cmd/handlers.c b/cmd/handlers.c index 73370dd..8e6deb0 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -9,10 +9,14 @@ #include "serbridge.h" #include "uart.h" #include "cgiwifi.h" +#include 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); +static uint32_t ICACHE_FLASH_ATTR CMD_AddSensor(CmdPacket *cmd); + +static uint8_t lastWifiStatus = wifiIsDisconnected; // Command dispatch table for serial -> ESP commands const CmdList commands[] = { @@ -33,34 +37,83 @@ const CmdList commands[] = { {CMD_REST_SETUP, REST_Setup}, {CMD_REST_REQUEST, REST_Request}, {CMD_REST_SETHEADER, REST_SetHeader}, + + {CMD_ADD_SENSOR, CMD_AddSensor }, + {CMD_NULL, NULL} }; +// WifiCb plus 10 for sensors +cmdCallback callbacks[12] = { + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 }, + { { '\0' }, -1 } +}; + // Command handler for IsReady (healthcheck) command static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd) { - os_printf("CMD: Check ready\n"); + os_printf("CMD_IsReady: 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"); + os_printf("CMD_Null: NULL/unsupported command\n"); return 1; } -static uint8_t lastWifiStatus; -static uint32_t wifiCallback; +static void ICACHE_FLASH_ATTR +CMD_AddCb(char* name, uint32_t cb) { + char checkname[16]; + os_strncpy(checkname, name, sizeof(checkname)); + for (uint8_t i = 0; i < sizeof(commands); i++) { + os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); + // find existing callback or add to the end + if (os_strcmp(callbacks[i].name, checkname) == 0 || callbacks[i].name[0] == '\0') { + os_strncpy(callbacks[i].name, checkname, sizeof(checkname)); + callbacks[i].callback = cb; + os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i); + break; + } + } +} + +cmdCallback* ICACHE_FLASH_ATTR +CMD_GetCbByName(char* name) { + char checkname[16]; + os_strncpy(checkname, name, sizeof(checkname)); + for (uint8_t i = 0; i < sizeof(commands); i++) { + os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); + // if callback doesn't exist or it's null + if (os_strcmp(callbacks[i].name, checkname) == 0) { + os_printf("CMD_GetCbByName: cb %s found at index %d\n", callbacks[i].name, i); + return &callbacks[i]; + } + } + os_printf("CMD_GetCbByName: cb %s not found\n", name); + return 0; +} // Callback from wifi subsystem to notify us of status changes static void ICACHE_FLASH_ATTR CMD_WifiCb(uint8_t wifiStatus) { if (wifiStatus != lastWifiStatus){ + os_printf("CMD_WifiCb: wifiStatus=%d\n", wifiStatus); lastWifiStatus = wifiStatus; - if (wifiCallback) { + cmdCallback *wifiCb = CMD_GetCbByName("wifiCb"); + if ((uint32_t)wifiCb->callback != -1) { uint8_t status = wifiStatus == wifiGotIP ? 5 : 1; - uint16_t crc = CMD_ResponseStart(CMD_WIFI_CONNECT, wifiCallback, 0, 1); + uint16_t crc = CMD_ResponseStart(CMD_WIFI_CONNECT, (uint32_t)wifiCb->callback, 0, 1); crc = CMD_ResponseBody(crc, (uint8_t*)&status, 1); CMD_ResponseEnd(crc); } @@ -70,10 +123,43 @@ CMD_WifiCb(uint8_t wifiStatus) { // Command handler for Wifi connect command static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + os_printf("CMD_WifiConnect: setup argc=%ld\n", CMD_GetArgc(&req)); if(cmd->argc != 2 || cmd->callback == 0) - return 0xFFFFFFFF; + return 0; wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem - wifiCallback = cmd->callback; // save the MCU's callback + CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback + lastWifiStatus = wifiIsDisconnected; + if (statusWifiState() != wifiIsDisconnected) { + CMD_WifiCb(statusWifiState()); + } + + return 1; +} + +// Command handler for Wifi connect command +static uint32_t ICACHE_FLASH_ATTR +CMD_AddSensor(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + os_printf("CMD_AddSensor: setup argc=%ld\n", CMD_GetArgc(&req)); + if (cmd->argc != 1 || cmd->callback == 0) + return 0; + + uint8_t* name; + uint16_t len; + + // get the sensor name + len = CMD_ArgLen(&req); + os_printf("CMD_AddSensor: name len=%d\n", len); + if (len > 15) return 0; // max size of name is 15 characters + name = (uint8_t*)os_zalloc(len + 1); + if (CMD_PopArg(&req, name, len)) return 0; + name[len] = 0; + os_printf("CMD_AddSensor: name=%s\n", name); + + CMD_AddCb((char*)name, (uint32_t)cmd->callback); // save the sensor callback return 1; } diff --git a/html-old/console.html b/httpd/html-old/console.html similarity index 100% rename from html-old/console.html rename to httpd/html-old/console.html diff --git a/html-old/console.js b/httpd/html-old/console.js similarity index 100% rename from html-old/console.js rename to httpd/html-old/console.js diff --git a/html-old/favicon.ico b/httpd/html-old/favicon.ico similarity index 100% rename from html-old/favicon.ico rename to httpd/html-old/favicon.ico diff --git a/html-old/head- b/httpd/html-old/head- similarity index 100% rename from html-old/head- rename to httpd/html-old/head- diff --git a/html-old/home.html b/httpd/html-old/home.html similarity index 100% rename from html-old/home.html rename to httpd/html-old/home.html diff --git a/html-old/jl-400x110.png- b/httpd/html-old/jl-400x110.png- similarity index 100% rename from html-old/jl-400x110.png- rename to httpd/html-old/jl-400x110.png- diff --git a/html-old/log.html b/httpd/html-old/log.html similarity index 100% rename from html-old/log.html rename to httpd/html-old/log.html diff --git a/html-old/pure.css b/httpd/html-old/pure.css similarity index 100% rename from html-old/pure.css rename to httpd/html-old/pure.css diff --git a/html-old/style.css b/httpd/html-old/style.css similarity index 100% rename from html-old/style.css rename to httpd/html-old/style.css diff --git a/html-old/ui.js b/httpd/html-old/ui.js similarity index 100% rename from html-old/ui.js rename to httpd/html-old/ui.js diff --git a/html-old/wifi/icons.png b/httpd/html-old/wifi/icons.png similarity index 100% rename from html-old/wifi/icons.png rename to httpd/html-old/wifi/icons.png diff --git a/html-old/wifi/wifi.html b/httpd/html-old/wifi/wifi.html similarity index 100% rename from html-old/wifi/wifi.html rename to httpd/html-old/wifi/wifi.html diff --git a/html-old/wifi/wifi.js b/httpd/html-old/wifi/wifi.js similarity index 100% rename from html-old/wifi/wifi.js rename to httpd/html-old/wifi/wifi.js From 8e1516aacb3752f86a0bde967cc52cb387f22f07 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 15:23:34 -0500 Subject: [PATCH 046/104] merge regressions --- cmd/handlers.c | 4 +--- esp-link.vcxproj | 2 +- user/status.c | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/handlers.c b/cmd/handlers.c index 3033e5c..28382dd 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -131,9 +131,7 @@ CMD_WifiConnect(CmdPacket *cmd) { wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback lastWifiStatus = wifiIsDisconnected; - if (statusWifiState() != wifiIsDisconnected) { - CMD_WifiCb(statusWifiState()); - } + CMD_WifiCb(wifiState); return 1; } diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 2a774e9..f8db9b3 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -44,7 +44,7 @@ espmake clean - espmake baseflash + espmake all wiflash espmake clean all espmake clean diff --git a/user/status.c b/user/status.c index 50afb85..a6080fe 100644 --- a/user/status.c +++ b/user/status.c @@ -23,7 +23,6 @@ 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) { From c286473be91d3cf04dd67237d81d93b8d35337e8 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 15:37:50 -0500 Subject: [PATCH 047/104] no message --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8a769b0..6b40320 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,3 @@ esp-link.tgz tve-patch/ yui .localhistory/ -esp-link.sdf -espfs/mkespfsimage/mman-win32/libmman.a -espfs/mkespfsimage/mman-win32/mman.o From b8890e5aaf0349410b04cf1eb510cbec96458487 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 17:37:43 -0500 Subject: [PATCH 048/104] fixed dupe variable --- user/status.c | 1 - 1 file changed, 1 deletion(-) diff --git a/user/status.c b/user/status.c index 50afb85..a6080fe 100644 --- a/user/status.c +++ b/user/status.c @@ -23,7 +23,6 @@ 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) { From 1f411e16fa0f20510da8d28bdb40f7af300d7076 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 18:36:00 -0500 Subject: [PATCH 049/104] Fixed makefile and removed espfstest --- esp-link.vcxproj | 1 - espfs/espfstest/Makefile | 13 ------- espfs/espfstest/main.c | 67 ------------------------------------- espfs/mkespfsimage/Makefile | 53 +++++++++++++++++++++++------ 4 files changed, 42 insertions(+), 92 deletions(-) delete mode 100644 espfs/espfstest/Makefile delete mode 100644 espfs/espfstest/main.c diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 1fc9e5c..bd259cf 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -68,7 +68,6 @@ - diff --git a/espfs/espfstest/Makefile b/espfs/espfstest/Makefile deleted file mode 100644 index f8296f4..0000000 --- a/espfs/espfstest/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -CFLAGS=-I../../lib/heatshrink -I.. -std=gnu99 -DESPFS_HEATSHRINK - -espfstest: main.o espfs.o heatshrink_decoder.o - $(CC) -o $@ $^ - -espfs.o: ../espfs.c - $(CC) $(CFLAGS) -c $^ -o $@ - -heatshrink_decoder.o: ../heatshrink_decoder.c - $(CC) $(CFLAGS) -c $^ -o $@ - -clean: - rm -f *.o espfstest diff --git a/espfs/espfstest/main.c b/espfs/espfstest/main.c deleted file mode 100644 index 16a6287..0000000 --- a/espfs/espfstest/main.c +++ /dev/null @@ -1,67 +0,0 @@ -/* -Simple and stupid file decompressor for an espfs image. Mostly used as a testbed for espfs.c and -the decompressors: code compiled natively is way easier to debug using gdb et all :) -*/ -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "espfs.h" - -char *espFsData; - -int main(int argc, char **argv) { - int f, out; - int len; - char buff[128]; - EspFsFile *ef; - off_t size; - EspFsInitResult ir; - - if (argc!=3) { - printf("Usage: %s espfs-image file\nExpands file from the espfs-image archive.\n", argv[0]); - exit(0); - } - - f=open(argv[1], O_RDONLY); - if (f<=0) { - perror(argv[1]); - exit(1); - } - size=lseek(f, 0, SEEK_END); - espFsData=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0); - if (espFsData==MAP_FAILED) { - perror("mmap"); - exit(1); - } - - ir=espFsInit(espFsData); - if (ir != ESPFS_INIT_RESULT_OK) { - printf("Couldn't init espfs filesystem (code %d)\n", ir); - exit(1); - } - - ef=espFsOpen(argv[2]); - if (ef==NULL) { - printf("Couldn't find %s in image.\n", argv[2]); - exit(1); - } - - out=open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (out<=0) { - perror(argv[2]); - exit(1); - } - - while ((len=espFsRead(ef, buff, 128))!=0) { - write(out, buff, len); - } - espFsClose(ef); - //munmap, close, ... I can't be bothered. -} diff --git a/espfs/mkespfsimage/Makefile b/espfs/mkespfsimage/Makefile index b05d5b0..2980331 100644 --- a/espfs/mkespfsimage/Makefile +++ b/espfs/mkespfsimage/Makefile @@ -1,24 +1,55 @@ -GZIP_COMPRESSION?=no +GZIP_COMPRESSION ?= no -CFLAGS=-I.. -std=gnu99 +ifeq ($(OS),Windows_NT) + +TARGET = mkespfsimage.exe + +CC = gcc +LD = $(CC) +CFLAGS=-c -I.. -Imman-win32 -std=gnu99 +LDFLAGS=-Lmman-win32 -lmman ifeq ("$(GZIP_COMPRESSION)","yes") -LDFLAGS+=-lz -CFLAGS+=-DESPFS_GZIP +CFLAGS += -DESPFS_GZIP +LDFLAGS += -lz endif -ifeq ($(OS),Windows_NT) -CFLAGS+=-Imman-win32 -LDFLAGS+=-Lmman-win32 -lmman -TARGET =mkespfsimage.exe +OBJECTS = main.o + +all: libmman $(TARGET) + +libmman: + $(Q) make -C mman-win32 + +$(TARGET): $(OBJECTS) + $(LD) -o $@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm -f $(OBJECTS) $(TARGET) ./mman-win32/libmman.a ./mman-win32/mman.o + +.PHONY: all clean + else -TARGET =mkespfsimage + +CFLAGS=-I.. -std=gnu99 +ifeq ("$(GZIP_COMPRESSION)","yes") +CFLAGS += -DESPFS_GZIP endif OBJS=main.o +TARGET=mkespfsimage $(TARGET): $(OBJS) - $(CC) -o $@ $^ $(LDFLAGS) +ifeq ("$(GZIP_COMPRESSION)","yes") + $(CC) -o $@ $^ -lz +else + $(CC) -o $@ $^ +endif clean: - rm -f $(TARGET) $(OBJS) \ No newline at end of file + rm -f $(TARGET) $(OBJS) + +endif \ No newline at end of file From 059692dfad91e5ca6968ec7ea62b2b549a72c4e5 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 19:23:16 -0500 Subject: [PATCH 050/104] Fixed mkespfsimage makefile and removed espfstest --- esp-link.vcxproj | 1 - espfs/espfstest/Makefile | 13 ------- espfs/espfstest/main.c | 67 ------------------------------------- espfs/mkespfsimage/Makefile | 53 +++++++++++++++++++++++------ 4 files changed, 42 insertions(+), 92 deletions(-) delete mode 100644 espfs/espfstest/Makefile delete mode 100644 espfs/espfstest/main.c diff --git a/esp-link.vcxproj b/esp-link.vcxproj index f8db9b3..c98faa5 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -68,7 +68,6 @@ - diff --git a/espfs/espfstest/Makefile b/espfs/espfstest/Makefile deleted file mode 100644 index f8296f4..0000000 --- a/espfs/espfstest/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -CFLAGS=-I../../lib/heatshrink -I.. -std=gnu99 -DESPFS_HEATSHRINK - -espfstest: main.o espfs.o heatshrink_decoder.o - $(CC) -o $@ $^ - -espfs.o: ../espfs.c - $(CC) $(CFLAGS) -c $^ -o $@ - -heatshrink_decoder.o: ../heatshrink_decoder.c - $(CC) $(CFLAGS) -c $^ -o $@ - -clean: - rm -f *.o espfstest diff --git a/espfs/espfstest/main.c b/espfs/espfstest/main.c deleted file mode 100644 index 16a6287..0000000 --- a/espfs/espfstest/main.c +++ /dev/null @@ -1,67 +0,0 @@ -/* -Simple and stupid file decompressor for an espfs image. Mostly used as a testbed for espfs.c and -the decompressors: code compiled natively is way easier to debug using gdb et all :) -*/ -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "espfs.h" - -char *espFsData; - -int main(int argc, char **argv) { - int f, out; - int len; - char buff[128]; - EspFsFile *ef; - off_t size; - EspFsInitResult ir; - - if (argc!=3) { - printf("Usage: %s espfs-image file\nExpands file from the espfs-image archive.\n", argv[0]); - exit(0); - } - - f=open(argv[1], O_RDONLY); - if (f<=0) { - perror(argv[1]); - exit(1); - } - size=lseek(f, 0, SEEK_END); - espFsData=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0); - if (espFsData==MAP_FAILED) { - perror("mmap"); - exit(1); - } - - ir=espFsInit(espFsData); - if (ir != ESPFS_INIT_RESULT_OK) { - printf("Couldn't init espfs filesystem (code %d)\n", ir); - exit(1); - } - - ef=espFsOpen(argv[2]); - if (ef==NULL) { - printf("Couldn't find %s in image.\n", argv[2]); - exit(1); - } - - out=open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (out<=0) { - perror(argv[2]); - exit(1); - } - - while ((len=espFsRead(ef, buff, 128))!=0) { - write(out, buff, len); - } - espFsClose(ef); - //munmap, close, ... I can't be bothered. -} diff --git a/espfs/mkespfsimage/Makefile b/espfs/mkespfsimage/Makefile index b05d5b0..2980331 100644 --- a/espfs/mkespfsimage/Makefile +++ b/espfs/mkespfsimage/Makefile @@ -1,24 +1,55 @@ -GZIP_COMPRESSION?=no +GZIP_COMPRESSION ?= no -CFLAGS=-I.. -std=gnu99 +ifeq ($(OS),Windows_NT) + +TARGET = mkespfsimage.exe + +CC = gcc +LD = $(CC) +CFLAGS=-c -I.. -Imman-win32 -std=gnu99 +LDFLAGS=-Lmman-win32 -lmman ifeq ("$(GZIP_COMPRESSION)","yes") -LDFLAGS+=-lz -CFLAGS+=-DESPFS_GZIP +CFLAGS += -DESPFS_GZIP +LDFLAGS += -lz endif -ifeq ($(OS),Windows_NT) -CFLAGS+=-Imman-win32 -LDFLAGS+=-Lmman-win32 -lmman -TARGET =mkespfsimage.exe +OBJECTS = main.o + +all: libmman $(TARGET) + +libmman: + $(Q) make -C mman-win32 + +$(TARGET): $(OBJECTS) + $(LD) -o $@ $^ $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm -f $(OBJECTS) $(TARGET) ./mman-win32/libmman.a ./mman-win32/mman.o + +.PHONY: all clean + else -TARGET =mkespfsimage + +CFLAGS=-I.. -std=gnu99 +ifeq ("$(GZIP_COMPRESSION)","yes") +CFLAGS += -DESPFS_GZIP endif OBJS=main.o +TARGET=mkespfsimage $(TARGET): $(OBJS) - $(CC) -o $@ $^ $(LDFLAGS) +ifeq ("$(GZIP_COMPRESSION)","yes") + $(CC) -o $@ $^ -lz +else + $(CC) -o $@ $^ +endif clean: - rm -f $(TARGET) $(OBJS) \ No newline at end of file + rm -f $(TARGET) $(OBJS) + +endif \ No newline at end of file From f90bf5bb30d51e8f85c8e5a541c790c796d992d9 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 20:23:07 -0500 Subject: [PATCH 051/104] Started adding in mqtt module from tuan --- Makefile | 2 +- esp-link.vcxproj | 15 +- include/user_config.h | 38 +++ mqtt/include/mqtt.h | 140 +++++++++ mqtt/include/mqtt_msg.h | 129 ++++++++ mqtt/include/proto.h | 31 ++ mqtt/include/queue.h | 45 +++ mqtt/include/ringbuf.h | 18 ++ mqtt/include/typedef.h | 17 ++ mqtt/include/utils.h | 9 + mqtt/mqtt.c | 637 ++++++++++++++++++++++++++++++++++++++++ mqtt/mqtt_msg.c | 471 +++++++++++++++++++++++++++++ mqtt/proto.c | 128 ++++++++ mqtt/queue.c | 50 ++++ mqtt/ringbuf.c | 67 +++++ mqtt/utils.c | 144 +++++++++ 16 files changed, 1939 insertions(+), 2 deletions(-) create mode 100644 mqtt/include/mqtt.h create mode 100644 mqtt/include/mqtt_msg.h create mode 100644 mqtt/include/proto.h create mode 100644 mqtt/include/queue.h create mode 100644 mqtt/include/ringbuf.h create mode 100644 mqtt/include/typedef.h create mode 100644 mqtt/include/utils.h create mode 100644 mqtt/mqtt.c create mode 100644 mqtt/mqtt_msg.c create mode 100644 mqtt/proto.c create mode 100644 mqtt/queue.c create mode 100644 mqtt/ringbuf.c create mode 100644 mqtt/utils.c diff --git a/Makefile b/Makefile index a3df665..7969417 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ TARGET = httpd APPGEN_TOOL ?= gen_appbin.py # which modules (subdirectories) of the project to include in compiling -MODULES = espfs httpd user serial cmd +MODULES = espfs httpd user serial cmd mqtt EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK diff --git a/esp-link.vcxproj b/esp-link.vcxproj index c98faa5..eac1d5b 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include + .\mqtt\include;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include @@ -75,6 +75,12 @@ + + + + + + @@ -106,6 +112,13 @@ + + + + + + + diff --git a/include/user_config.h b/include/user_config.h index 8b13789..d38c637 100644 --- a/include/user_config.h +++ b/include/user_config.h @@ -1 +1,39 @@ +#ifndef _USER_CONFIG_H_ +#define _USER_CONFIG_H_ +/*DEFAULT CONFIGURATIONS*/ + +#define MQTT_HOST "mqtt.yourdomain.com" //or "mqtt.yourdomain.com" +#define MQTT_PORT 1883 +#define MQTT_BUF_SIZE 1024 +#define MQTT_KEEPALIVE 120 /*second*/ + +#define MQTT_CLIENT_ID "H_%08X" //Cuidar para não colocar valores execendentes da ESTRUTURA SYSCFG +#define MQTT_USER "DVES_USER" +#define MQTT_PASS "DVES_PASS" + +#define STA_SSID "TESTE" +#define STA_PASS "54545" +#define STA_TYPE AUTH_WPA2_PSK + +#define MQTT_RECONNECT_TIMEOUT 5 /*second*/ + +#define DEFAULT_SECURITY 0 +#define QUEUE_BUFFER_SIZE 2048 + +//#undef MCU_RESET_PIN +//#undef MCU_ISP_PIN +//#undef LED_CONN_PIN +//#undef LED_SERIAL_PIN +// +//#define MCU_RESET_PIN 2 +//#define MCU_ISP_PIN -1 +//#define LED_CONN_PIN -1 +//#define LED_SERIAL_PIN -1 + +//#define BAUD_RATE 9600 +//#define HOSTNAME "nodemcu\0 " + +#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ +//PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ +#endif \ No newline at end of file diff --git a/mqtt/include/mqtt.h b/mqtt/include/mqtt.h new file mode 100644 index 0000000..52d21fe --- /dev/null +++ b/mqtt/include/mqtt.h @@ -0,0 +1,140 @@ +/* mqtt.h +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ +#ifndef USER_AT_MQTT_H_ +#define USER_AT_MQTT_H_ + +#include +#include "mqtt_msg.h" +#include "queue.h" +#include "utils.h" + +typedef struct mqtt_event_data_t +{ + uint8_t type; + const char* topic; + const char* data; + uint16_t topic_length; + uint16_t data_length; + uint16_t data_offset; +} mqtt_event_data_t; + +typedef struct mqtt_state_t +{ + uint16_t port; + int auto_reconnect; + mqtt_connect_info_t* connect_info; + uint8_t* in_buffer; + uint8_t* out_buffer; + int in_buffer_length; + int out_buffer_length; + uint16_t message_length; + uint16_t message_length_read; + mqtt_message_t* outbound_message; + mqtt_connection_t mqtt_connection; + uint16_t pending_msg_id; + int pending_msg_type; + int pending_publish_qos; +} mqtt_state_t; + +typedef enum { + WIFI_INIT, + WIFI_CONNECTING, + WIFI_CONNECTING_ERROR, + WIFI_CONNECTED, + DNS_RESOLVE, + TCP_DISCONNECTED, + TCP_RECONNECT_REQ, + TCP_RECONNECT, + TCP_CONNECTING, + TCP_CONNECTING_ERROR, + TCP_CONNECTED, + MQTT_CONNECT_SEND, + MQTT_CONNECT_SENDING, + MQTT_SUBSCIBE_SEND, + MQTT_SUBSCIBE_SENDING, + MQTT_DATA, + MQTT_PUBLISH_RECV, + MQTT_PUBLISHING +} tConnState; + +typedef void (*MqttCallback)(uint32_t *args); +typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); + +typedef struct { + struct espconn *pCon; + uint8_t security; + uint8_t* host; + uint32_t port; + ip_addr_t ip; + mqtt_state_t mqtt_state; + mqtt_connect_info_t connect_info; + MqttCallback connectedCb; + MqttCallback disconnectedCb; + MqttCallback publishedCb; + MqttDataCallback dataCb; + ETSTimer mqttTimer; + uint32_t keepAliveTick; + uint32_t reconnectTick; + uint32_t sendTimeout; + tConnState connState; + QUEUE msgQueue; + void* user_data; +} MQTT_Client; + +#define SEC_NONSSL 0 +#define SEC_SSL 1 + +#define MQTT_FLAG_CONNECTED 1 +#define MQTT_FLAG_READY 2 +#define MQTT_FLAG_EXIT 4 + +#define MQTT_EVENT_TYPE_NONE 0 +#define MQTT_EVENT_TYPE_CONNECTED 1 +#define MQTT_EVENT_TYPE_DISCONNECTED 2 +#define MQTT_EVENT_TYPE_SUBSCRIBED 3 +#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 +#define MQTT_EVENT_TYPE_PUBLISH 5 +#define MQTT_EVENT_TYPE_PUBLISHED 6 +#define MQTT_EVENT_TYPE_EXITED 7 +#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 + +void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security); +void ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession); +void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); +void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); +void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); +void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); +void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); +bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); +void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); +void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); +bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int qos, int retain); + +#endif /* USER_AT_MQTT_H_ */ diff --git a/mqtt/include/mqtt_msg.h b/mqtt/include/mqtt_msg.h new file mode 100644 index 0000000..f16bb80 --- /dev/null +++ b/mqtt/include/mqtt_msg.h @@ -0,0 +1,129 @@ +/* + * File: mqtt_msg.h + * Author: Minh Tuan + * + * Created on July 12, 2014, 1:05 PM + */ + +#ifndef MQTT_MSG_H +#define MQTT_MSG_H +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* +* Copyright (c) 2014, Stephen Robinson +* 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +* +*/ +/* 7 6 5 4 3 2 1 0*/ +/* | --- Message Type ---- | DUP Flag | QoS Level | Retain | Remaining Length | */ + + +enum mqtt_message_type +{ + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 +}; + +typedef struct mqtt_message +{ + uint8_t* data; + uint16_t length; + +} mqtt_message_t; + +typedef struct mqtt_connection +{ + mqtt_message_t message; + + uint16_t message_id; + uint8_t* buffer; + uint16_t buffer_length; + +} mqtt_connection_t; + +typedef struct mqtt_connect_info +{ + char* client_id; + char* username; + char* password; + char* will_topic; + char* will_message; + int keepalive; + int will_qos; + int will_retain; + int clean_session; + +} mqtt_connect_info_t; + + +static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } +static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } +static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } +static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } + +void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); +int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); +const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); +const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); +uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length); + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); + + +#ifdef __cplusplus +} +#endif + +#endif /* MQTT_MSG_H */ + diff --git a/mqtt/include/proto.h b/mqtt/include/proto.h new file mode 100644 index 0000000..73a4e8b --- /dev/null +++ b/mqtt/include/proto.h @@ -0,0 +1,31 @@ +/* + * File: proto.h + * Author: ThuHien + * + * Created on November 23, 2012, 8:57 AM + */ + +#ifndef _PROTO_H_ +#define _PROTO_H_ +#include +#include "ringbuf.h" + +typedef void(PROTO_PARSE_CALLBACK)(); + +typedef struct{ + uint8_t *buf; + uint16_t bufSize; + uint16_t dataLen; + uint8_t isEsc; + uint8_t isBegin; + PROTO_PARSE_CALLBACK* callback; +}PROTO_PARSER; + +int8_t ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, uint8_t *buf, uint16_t bufSize); +int8_t ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, uint8_t *buf, uint16_t len); +int16_t ICACHE_FLASH_ATTR PROTO_Add(uint8_t *buf, const uint8_t *packet, int16_t bufSize); +int16_t ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const uint8_t *packet, int16_t len); +int8_t ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, uint8_t value); +int16_t ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, uint8_t *bufOut, uint16_t* len, uint16_t maxBufLen); +#endif + diff --git a/mqtt/include/queue.h b/mqtt/include/queue.h new file mode 100644 index 0000000..7a84480 --- /dev/null +++ b/mqtt/include/queue.h @@ -0,0 +1,45 @@ +/* str_queue.h -- +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ + +#ifndef USER_QUEUE_H_ +#define USER_QUEUE_H_ +#include +#include "proto.h" +#include "ringbuf.h" +typedef struct { + uint8_t *buf; + RINGBUF rb; +} QUEUE; + +void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize); +int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); +int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); +bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue); +#endif /* USER_QUEUE_H_ */ diff --git a/mqtt/include/ringbuf.h b/mqtt/include/ringbuf.h new file mode 100644 index 0000000..03d780d --- /dev/null +++ b/mqtt/include/ringbuf.h @@ -0,0 +1,18 @@ +#ifndef _RING_BUF_H_ +#define _RING_BUF_H_ + +#include +#include "typedef.h" + +typedef struct{ + U8* p_o; /**< Original pointer */ + U8* volatile p_r; /**< Read pointer */ + U8* volatile p_w; /**< Write pointer */ + volatile I32 fill_cnt; /**< Number of filled slots */ + I32 size; /**< Buffer size */ +}RINGBUF; + +I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); +I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c); +I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c); +#endif diff --git a/mqtt/include/typedef.h b/mqtt/include/typedef.h new file mode 100644 index 0000000..a4c69d6 --- /dev/null +++ b/mqtt/include/typedef.h @@ -0,0 +1,17 @@ +/** +* \file +* Standard Types definition +*/ + +#ifndef _TYPE_DEF_H_ +#define _TYPE_DEF_H_ + +typedef char I8; +typedef unsigned char U8; +typedef short I16; +typedef unsigned short U16; +typedef long I32; +typedef unsigned long U32; +typedef unsigned long long U64; + +#endif diff --git a/mqtt/include/utils.h b/mqtt/include/utils.h new file mode 100644 index 0000000..676501e --- /dev/null +++ b/mqtt/include/utils.h @@ -0,0 +1,9 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include + +uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s); +uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip); +uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str); +#endif diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c new file mode 100644 index 0000000..49ed507 --- /dev/null +++ b/mqtt/mqtt.c @@ -0,0 +1,637 @@ +/* mqtt.c +* Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ + +#include "mqtt.h" + +#define MQTT_TASK_PRIO 0 +#define MQTT_TASK_QUEUE_SIZE 1 +#define MQTT_SEND_TIMOUT 5 + +#ifndef QUEUE_BUFFER_SIZE +#define QUEUE_BUFFER_SIZE 2048 +#endif + +unsigned char *default_certificate; +unsigned int default_certificate_len = 0; +unsigned char *default_private_key; +unsigned int default_private_key_len = 0; + +os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; + +LOCAL void ICACHE_FLASH_ATTR +mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct espconn *pConn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pConn->reverse; + + + if (ipaddr == NULL) + { + os_printf("DNS: Found, but got no ip, try to reconnect\n"); + client->connState = TCP_RECONNECT_REQ; + return; + } + + os_printf("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); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_connect(client->pCon); + } + else +#endif + espconn_connect(client->pCon); + + client->connState = TCP_CONNECTING; + os_printf("MQTT-TCP: connecting...\n"); + } + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + + + +LOCAL void ICACHE_FLASH_ATTR +deliver_publish(MQTT_Client* client, uint8_t* message, int length) +{ + mqtt_event_data_t event_data; + + event_data.topic_length = length; + event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); + event_data.data_length = length; + event_data.data = mqtt_get_publish_data(message, &event_data.data_length); + + if (client->dataCb) + client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); + +} + + +/** +* @brief Client received callback function. +* @param arg: contain the ip link information +* @param pdata: received data +* @param len: the lenght of received data +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) +{ + uint8_t msg_type; + uint8_t msg_qos; + uint16_t msg_id; + + struct espconn *pCon = (struct espconn*)arg; + MQTT_Client *client = (MQTT_Client *)pCon->reverse; + +READPACKET: + os_printf("MQTT-TCP: Data received %d bytes\n", len); + if (len < MQTT_BUF_SIZE && len > 0){ + os_memcpy(client->mqtt_state.in_buffer, pdata, len); + + msg_type = mqtt_get_type(client->mqtt_state.in_buffer); + msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); + msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); + if (client->connState == MQTT_CONNECT_SENDING) { + if (msg_type == MQTT_MSG_TYPE_CONNACK){ + if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT){ + os_printf("MQTT: Invalid packet\n"); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_disconnect(client->pCon); + } + else +#endif + espconn_disconnect(client->pCon); + } + else { + os_printf("MQTT: Connected to %s:%ld\n", client->host, client->port); + client->connState = MQTT_DATA; + if (client->connectedCb) + client->connectedCb((uint32_t*)client); + } + } + } + else if (client->connState == MQTT_DATA) { + client->mqtt_state.message_length_read = len; + client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + + + if (msg_type == MQTT_MSG_TYPE_SUBACK) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + os_printf("MQTT: Subscribe successful\n"); + } + else if (msg_type == MQTT_MSG_TYPE_UNSUBACK){ + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + os_printf("MQTT: UnSubscribe successful\n"); + } + else if (msg_type == MQTT_MSG_TYPE_PUBLISH) { + if (msg_qos == 1) + client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); + else if (msg_qos == 2) + client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); + if (msg_qos == 1 || msg_qos == 2){ + os_printf("MQTT: Queue response QoS: %d\n", msg_qos); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + os_printf("MQTT: Queue full\n"); + } + } + deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + } + else if (msg_type == MQTT_MSG_TYPE_PUBACK) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ + os_printf("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PUBREC) { + client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + os_printf("MQTT: Queue full\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PUBREL) { + client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + os_printf("MQTT: Queue full\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PUBCOMP) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ + os_printf("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PINGREQ) { + client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + os_printf("MQTT: Queue full\n"); + } + } + + // NOTE: this is done down here and not in the switch case above + // because the PSOCK_READBUF_LEN() won't work inside a switch + // statement due to the way protothreads resume. + if (msg_type == MQTT_MSG_TYPE_PUBLISH) + { + len = client->mqtt_state.message_length_read; + + if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) + { + //client->connState = MQTT_PUBLISH_RECV; + //Not Implement yet + len -= client->mqtt_state.message_length; + pdata += client->mqtt_state.message_length; + + os_printf("Get another published message\n"); + goto READPACKET; + } + } + } + } + else { + os_printf("ERROR: Message too long\n"); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief Client send over callback function. +* @param arg: contain the ip link information +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_sent_cb(void *arg) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + os_printf("MQTT-TCP: Sent\n"); + client->sendTimeout = 0; + if (client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH){ + if (client->publishedCb) + client->publishedCb((uint32_t*)client); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +void ICACHE_FLASH_ATTR mqtt_timer(void *arg) +{ + MQTT_Client* client = (MQTT_Client*)arg; + + if (client->connState == MQTT_DATA){ + client->keepAliveTick++; + if (client->keepAliveTick > client->mqtt_state.connect_info->keepalive){ + + os_printf("\nMQTT: Send keepalive packet to %s:%ld!\n", client->host, client->port); + client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); + client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + else +#endif + espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + client->mqtt_state.outbound_message = NULL; + + client->keepAliveTick = 0; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + + } + else if (client->connState == TCP_RECONNECT_REQ){ + client->reconnectTick++; + if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { + client->reconnectTick = 0; + client->connState = TCP_RECONNECT; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + } + if (client->sendTimeout > 0) + client->sendTimeout--; +} + +void ICACHE_FLASH_ATTR +mqtt_tcpclient_discon_cb(void *arg) +{ + + struct espconn *pespconn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pespconn->reverse; + os_printf("MQTT-TCP: Disconnected callback\n"); + client->connState = TCP_RECONNECT_REQ; + if (client->disconnectedCb) + client->disconnectedCb((uint32_t*)client); + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief Tcp client connect success callback function. +* @param arg: contain the ip link information +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_connect_cb(void *arg) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); + espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// + espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// + os_printf("MQTT: Connected to broker %s:%ld\n", client->host, client->port); + + mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); + client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + else +#endif + espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + client->mqtt_state.outbound_message = NULL; + client->connState = MQTT_CONNECT_SENDING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief Tcp client connect repeat callback function. +* @param arg: contain the ip link information +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recon_cb(void *arg, sint8 errType) +{ + struct espconn *pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + os_printf("MQTT-TCP: Reconnect to %s:%ld\n", client->host, client->port); + + client->connState = TCP_RECONNECT_REQ; + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief MQTT publish function. +* @param client: MQTT_Client reference +* @param topic: string topic will publish to +* @param data: buffer data send point to +* @param data_length: length of data +* @param qos: qos +* @param retain: retain +* @retval TRUE if success queue +*/ +bool ICACHE_FLASH_ATTR +MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int qos, int retain) +{ + int data_length = os_strlen(data); + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, + topic, data, data_length, + qos, retain, + &client->mqtt_state.pending_msg_id); + if (client->mqtt_state.outbound_message->length == 0){ + os_printf("MQTT: Queuing Publish failed\n"); + return FALSE; + } + os_printf("MQTT: Queuing Publish, length: %d, queue size(%ld/%ld)\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + os_printf("MQTT: Queue full\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + os_printf("MQTT: Serious buffer error\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +/** +* @brief MQTT subscibe function. +* @param client: MQTT_Client reference +* @param topic: string topic will subscribe +* @param qos: qos +* @retval TRUE if success queue +*/ +bool ICACHE_FLASH_ATTR +MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) +{ + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + + client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, + topic, 0, + &client->mqtt_state.pending_msg_id); + os_printf("MQTT: Queue Subscribe, topic: \"%s\", id: %d\n", topic, client->mqtt_state.pending_msg_id); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + os_printf("MQTT: Queue full\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + os_printf("MQTT: Serious buffer error\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +void ICACHE_FLASH_ATTR MQTT_Task(os_event_t *e) { + MQTT_Client* client = (MQTT_Client*)e->par; + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + if (e->par == 0) + return; + + if (client->connState == TCP_RECONNECT_REQ) { + return; + } + else if (client->connState == TCP_RECONNECT){ + MQTT_Connect(client); + os_printf("MQTT-TCP: Reconnect to: %s:%ld\n", client->host, client->port); + client->connState = TCP_CONNECTING; + } + else if (client->connState == MQTT_DATA) { + if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) + return; + + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0){ + client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); + client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); + client->sendTimeout = MQTT_SEND_TIMOUT; + os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_sent(client->pCon, dataBuffer, dataLen); + } + else +#endif + espconn_sent(client->pCon, dataBuffer, dataLen); + + client->mqtt_state.outbound_message = NULL; + return; + } + return; + } +} + +/** +* @brief MQTT initialization connection function +* @param client: MQTT_Client reference +* @param host: Domain or IP string +* @param port: Port to connect +* @param security: 1 for ssl, 0 for none +* @retval None +*/ +void ICACHE_FLASH_ATTR +MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security) +{ + uint32_t temp; + os_printf("MQTT_InitConnection\n"); + os_memset(mqttClient, 0, sizeof(MQTT_Client)); + temp = sizeof((char*)host); + mqttClient->host = (uint8_t*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->host, (char*)host); + mqttClient->host[temp] = 0; + mqttClient->port = port; + mqttClient->security = security; +} + +/** +* @brief MQTT initialization mqtt client function +* @param client: MQTT_Client reference +* @param clientid: MQTT client id +* @param client_user:MQTT client user +* @param client_pass:MQTT client password +* @param client_pass:MQTT keep alive timer, in second +* @retval None +*/ +void ICACHE_FLASH_ATTR +MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession) +{ + uint32_t temp; + os_printf("MQTT_InitClient\n"); + + os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); + + temp = os_strlen((char*)client_id); + mqttClient->connect_info.client_id = (char*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->connect_info.client_id, (char*)client_id); + mqttClient->connect_info.client_id[temp] = 0; + + temp = os_strlen((char*)client_user); + mqttClient->connect_info.username = (char*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->connect_info.username, (char*)client_user); + mqttClient->connect_info.username[temp] = 0; + + temp = os_strlen((char*)client_pass); + mqttClient->connect_info.password = (char*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->connect_info.password, (char*)client_pass); + mqttClient->connect_info.password[temp] = 0; + + + mqttClient->connect_info.keepalive = keepAliveTime; + mqttClient->connect_info.clean_session = cleanSession; + + mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; + + mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); + + QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); + + system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); +} + +void ICACHE_FLASH_ATTR +MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) +{ + uint32_t temp; + temp = os_strlen((char*)will_topic); + mqttClient->connect_info.will_topic = (char*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->connect_info.will_topic, (char*)will_topic); + mqttClient->connect_info.will_topic[temp] = 0; + + temp = os_strlen((char*)will_msg); + mqttClient->connect_info.will_message = (char*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->connect_info.will_message, (char*)will_msg); + mqttClient->connect_info.will_message[temp] = 0; + + + mqttClient->connect_info.will_qos = will_qos; + mqttClient->connect_info.will_retain = will_retain; +} + +/** +* @brief Begin connect to MQTT broker +* @param client: MQTT_Client reference +* @retval None +*/ +void ICACHE_FLASH_ATTR +MQTT_Connect(MQTT_Client *mqttClient) +{ + MQTT_Disconnect(mqttClient); + mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); + mqttClient->pCon->type = ESPCONN_TCP; + mqttClient->pCon->state = ESPCONN_NONE; + mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + mqttClient->pCon->proto.tcp->local_port = espconn_port(); + mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; + mqttClient->pCon->reverse = mqttClient; + espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); + espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); + + mqttClient->keepAliveTick = 0; + mqttClient->reconnectTick = 0; + + + os_timer_disarm(&mqttClient->mqttTimer); + os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); + os_timer_arm(&mqttClient->mqttTimer, 1000, 1); + + if (UTILS_StrToIP((const int8_t *)mqttClient->host, (void*)&mqttClient->pCon->proto.tcp->remote_ip)) { + os_printf("MQTT-TCP: Connect to ip %s:%ld\n", mqttClient->host, mqttClient->port); +#ifdef CLIENT_SSL_ENABLE + if (mqttClient->security){ + espconn_secure_connect(mqttClient->pCon); + } + else +#endif + espconn_connect(mqttClient->pCon); + } + else { + os_printf("MQTT-TCP: Connect to domain %s:%ld\n", mqttClient->host, mqttClient->port); + espconn_gethostbyname(mqttClient->pCon, (const char *)mqttClient->host, &mqttClient->ip, mqtt_dns_found); + } + mqttClient->connState = TCP_CONNECTING; +} + +void ICACHE_FLASH_ATTR +MQTT_Disconnect(MQTT_Client *mqttClient) +{ + if (mqttClient->pCon){ + os_printf("Free memory\n"); + if (mqttClient->pCon->proto.tcp) + os_free(mqttClient->pCon->proto.tcp); + os_free(mqttClient->pCon); + mqttClient->pCon = NULL; + } + + os_timer_disarm(&mqttClient->mqttTimer); +} + +void ICACHE_FLASH_ATTR +MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) +{ + mqttClient->connectedCb = connectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) +{ + mqttClient->disconnectedCb = disconnectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) +{ + mqttClient->dataCb = dataCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) +{ + mqttClient->publishedCb = publishedCb; +} diff --git a/mqtt/mqtt_msg.c b/mqtt/mqtt_msg.c new file mode 100644 index 0000000..7e159c6 --- /dev/null +++ b/mqtt/mqtt_msg.c @@ -0,0 +1,471 @@ +/* +* Copyright (c) 2014, Stephen Robinson +* 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +* +*/ + +#include "mqtt_msg.h" +#define MQTT_MAX_FIXED_HEADER_SIZE 3 + +enum mqtt_connect_flag +{ + MQTT_CONNECT_FLAG_USERNAME = 1 << 7, + MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, + MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, + MQTT_CONNECT_FLAG_WILL = 1 << 2, + MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 +}; + +struct __attribute__((__packed__)) mqtt_connect_variable_header +{ + uint8_t lengthMsb; + uint8_t lengthLsb; +#if defined(PROTOCOL_NAMEv31) + uint8_t magic[6]; +#elif defined(PROTOCOL_NAMEv311) + uint8_t magic[4]; +#else +#error "Please define protocol name" +#endif + uint8_t version; + uint8_t flags; + uint8_t keepaliveMsb; + uint8_t keepaliveLsb; +}; + +static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) +{ + if (connection->message.length + len + 2 > connection->buffer_length) + return -1; + + connection->buffer[connection->message.length++] = len >> 8; + connection->buffer[connection->message.length++] = len & 0xff; + memcpy(connection->buffer + connection->message.length, string, len); + connection->message.length += len; + + return len + 2; +} + +static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) +{ + // If message_id is zero then we should assign one, otherwise + // we'll use the one supplied by the caller + while (message_id == 0) + message_id = ++connection->message_id; + + if (connection->message.length + 2 > connection->buffer_length) + return 0; + + connection->buffer[connection->message.length++] = message_id >> 8; + connection->buffer[connection->message.length++] = message_id & 0xff; + + return message_id; +} + +static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) +{ + connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; + return MQTT_MAX_FIXED_HEADER_SIZE; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) +{ + connection->message.data = connection->buffer; + connection->message.length = 0; + return &connection->message; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) +{ + int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; + + if (remaining_length > 127) + { + connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[1] = 0x80 | (remaining_length % 128); + connection->buffer[2] = remaining_length / 128; + connection->message.length = remaining_length + 3; + connection->message.data = connection->buffer; + } + else + { + connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[2] = remaining_length; + connection->message.length = remaining_length + 2; + connection->message.data = connection->buffer + 1; + } + + return &connection->message; +} + +void ICACHE_FLASH_ATTR +mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) { + memset(connection, 0, sizeof(connection)); + connection->buffer = buffer; + connection->buffer_length = buffer_length; +} + +int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) +{ + int i; + int totlen = 0; + + for (i = 1; i < length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + return totlen; +} + +const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + + for (i = 1; i < *length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen > *length) + return NULL; + + *length = topiclen; + return (const char*)(buffer + i); +} + +const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + + for (i = 1; i < *length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= *length){ + *length = 0; + return NULL; + } + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) + { + if (i + 2 >= *length) + return NULL; + i += 2; + } + + if (totlen < i) + return NULL; + + if (totlen <= *length) + *length = totlen - i; + else + *length = *length - i; + return (const char*)(buffer + i); +} + +uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length) +{ + if (length < 1) + return 0; + + switch (mqtt_get_type(buffer)) + { + case MQTT_MSG_TYPE_PUBLISH: + { + int i; + int topiclen; + + for (i = 1; i < length; ++i) + { + if ((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + + if (i + 2 >= length) + return 0; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= length) + return 0; + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) + { + if (i + 2 >= length) + return 0; + //i += 2; + } + else { + return 0; + } + + return (buffer[i] << 8) | buffer[i + 1]; + } + case MQTT_MSG_TYPE_PUBACK: + case MQTT_MSG_TYPE_PUBREC: + case MQTT_MSG_TYPE_PUBREL: + case MQTT_MSG_TYPE_PUBCOMP: + case MQTT_MSG_TYPE_SUBACK: + case MQTT_MSG_TYPE_UNSUBACK: + case MQTT_MSG_TYPE_SUBSCRIBE: + { + // This requires the remaining length to be encoded in 1 byte, + // which it should be. + if (length >= 4 && (buffer[1] & 0x80) == 0) + return (buffer[2] << 8) | buffer[3]; + else + return 0; + } + + default: + return 0; + } +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) +{ + struct mqtt_connect_variable_header* variable_header; + + init_message(connection); + + if (connection->message.length + sizeof(*variable_header) > connection->buffer_length) + return fail_message(connection); + variable_header = (void*)(connection->buffer + connection->message.length); + connection->message.length += sizeof(*variable_header); + + variable_header->lengthMsb = 0; +#if defined(PROTOCOL_NAMEv31) + variable_header->lengthLsb = 6; + memcpy(variable_header->magic, "MQIsdp", 6); + variable_header->version = 3; +#elif defined(PROTOCOL_NAMEv311) + variable_header->lengthLsb = 4; + memcpy(variable_header->magic, "MQTT", 4); + variable_header->version = 4; +#else +#error "Please define protocol name" +#endif + + variable_header->flags = 0; + variable_header->keepaliveMsb = info->keepalive >> 8; + variable_header->keepaliveLsb = info->keepalive & 0xff; + + if (info->clean_session) + variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; + + if (info->client_id != NULL && info->client_id[0] != '\0') + { + if (append_string(connection, info->client_id, strlen(info->client_id)) < 0) + return fail_message(connection); + } + else + return fail_message(connection); + + if (info->will_topic != NULL && info->will_topic[0] != '\0') + { + if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) + return fail_message(connection); + + if (append_string(connection, info->will_message, strlen(info->will_message)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_WILL; + if (info->will_retain) + variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; + variable_header->flags |= (info->will_qos & 3) << 3; + } + + if (info->username != NULL && info->username[0] != '\0') + { + if (append_string(connection, info->username, strlen(info->username)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; + } + + if (info->password != NULL && info->password[0] != '\0') + { + if (append_string(connection, info->password, strlen(info->password)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; + } + + return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (qos > 0) + { + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + } + else + *message_id = 0; + + if (connection->message.length + data_length > connection->buffer_length) + return fail_message(connection); + memcpy(connection->buffer + connection->message.length, data, data_length); + connection->message.length += data_length; + + return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (connection->message.length + 1 > connection->buffer_length) + return fail_message(connection); + connection->buffer[connection->message.length++] = qos; + + return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) +{ + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); +} diff --git a/mqtt/proto.c b/mqtt/proto.c new file mode 100644 index 0000000..3909b15 --- /dev/null +++ b/mqtt/proto.c @@ -0,0 +1,128 @@ +#include "proto.h" + +int8_t ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, uint8_t *buf, uint16_t bufSize) +{ + parser->buf = buf; + parser->bufSize = bufSize; + parser->dataLen = 0; + parser->callback = completeCallback; + parser->isEsc = 0; + return 0; +} + +int8_t ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, uint8_t value) +{ + switch(value){ + case 0x7D: + parser->isEsc = 1; + break; + + case 0x7E: + parser->dataLen = 0; + parser->isEsc = 0; + parser->isBegin = 1; + break; + + case 0x7F: + if (parser->callback != NULL) + parser->callback(); + parser->isBegin = 0; + return 0; + break; + + default: + if(parser->isBegin == 0) break; + + if(parser->isEsc){ + value ^= 0x20; + parser->isEsc = 0; + } + + if(parser->dataLen < parser->bufSize) + parser->buf[parser->dataLen++] = value; + + break; + } + return -1; +} + +int8_t ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, uint8_t *buf, uint16_t len) +{ + while(len--) + PROTO_ParseByte(parser, *buf++); + return 0; +} +int16_t ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, uint8_t *bufOut, uint16_t* len, uint16_t maxBufLen) +{ + uint8_t c; + + PROTO_PARSER proto; + PROTO_Init(&proto, NULL, bufOut, maxBufLen); + while(RINGBUF_Get(rb, &c) == 0){ + if(PROTO_ParseByte(&proto, c) == 0){ + *len = proto.dataLen; + return 0; + } + } + return -1; +} +int16_t ICACHE_FLASH_ATTR PROTO_Add(uint8_t *buf, const uint8_t *packet, int16_t bufSize) +{ + uint16_t i = 2; + uint16_t len = *(uint16_t*) packet; + + if (bufSize < 1) return -1; + + *buf++ = 0x7E; + bufSize--; + + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if (bufSize < 2) return -1; + *buf++ = 0x7D; + *buf++ = *packet++ ^ 0x20; + i += 2; + bufSize -= 2; + break; + default: + if (bufSize < 1) return -1; + *buf++ = *packet++; + i++; + bufSize--; + break; + } + } + + if (bufSize < 1) return -1; + *buf++ = 0x7F; + + return i; +} + +int16_t ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const uint8_t *packet, int16_t len) +{ + uint16_t i = 2; + if(RINGBUF_Put(rb, 0x7E) == -1) return -1; + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if(RINGBUF_Put(rb, 0x7D) == -1) return -1; + if(RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; + i += 2; + break; + default: + if(RINGBUF_Put(rb, *packet++) == -1) return -1; + i++; + break; + } + } + if(RINGBUF_Put(rb, 0x7F) == -1) return -1; + + return i; +} + diff --git a/mqtt/queue.c b/mqtt/queue.c new file mode 100644 index 0000000..147015e --- /dev/null +++ b/mqtt/queue.c @@ -0,0 +1,50 @@ +/* str_queue.c +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ +#include "queue.h" + +void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize) { + queue->buf = (uint8_t*)os_zalloc(bufferSize); + RINGBUF_Init(&queue->rb, queue->buf, bufferSize); +} + +int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) { + return PROTO_AddRb(&queue->rb, buffer, len); +} + +int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) { + + return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); +} + +bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue) { + if (queue->rb.fill_cnt <= 0) + return TRUE; + return FALSE; +} \ No newline at end of file diff --git a/mqtt/ringbuf.c b/mqtt/ringbuf.c new file mode 100644 index 0000000..5ac3d07 --- /dev/null +++ b/mqtt/ringbuf.c @@ -0,0 +1,67 @@ +/** +* \file +* Ring Buffer library +*/ + +#include "ringbuf.h" + + +/** +* \brief init a RINGBUF object +* \param r pointer to a RINGBUF object +* \param buf pointer to a byte array +* \param size size of buf +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size) +{ + if(r == NULL || buf == NULL || size < 2) return -1; + + r->p_o = r->p_r = r->p_w = buf; + r->fill_cnt = 0; + r->size = size; + + return 0; +} +/** +* \brief put a character into ring buffer +* \param r pointer to a ringbuf object +* \param c character to be put +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c) +{ + if(r->fill_cnt>=r->size)return -1; // ring buffer is full, this should be atomic operation + + + r->fill_cnt++; // increase filled slots count, this should be atomic operation + + + *r->p_w++ = c; // put character into buffer + + if(r->p_w >= r->p_o + r->size) // rollback if write pointer go pass + r->p_w = r->p_o; // the physical boundary + + return 0; +} +/** +* \brief get a character from ring buffer +* \param r pointer to a ringbuf object +* \param c read character +* \return 0 if successfull, otherwise failed +*/ +I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c) +{ + if(r->fill_cnt<=0)return -1; // ring buffer is empty, this should be atomic operation + + + r->fill_cnt--; // decrease filled slots count + + + *c = *r->p_r++; // get the character out + + if(r->p_r >= r->p_o + r->size) // rollback if write pointer go pass + r->p_r = r->p_o; // the physical boundary + + return 0; +} diff --git a/mqtt/utils.c b/mqtt/utils.c new file mode 100644 index 0000000..5ece013 --- /dev/null +++ b/mqtt/utils.c @@ -0,0 +1,144 @@ +/* +* Copyright (c) 2014, Tuan PM +* Email: tuanpm@live.com +* +* 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +* +*/ +#include "utils.h" + + +uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str) +{ + uint8_t segs = 0; /* Segment count. */ + uint8_t chcnt = 0; /* Character count within segment. */ + uint8_t accum = 0; /* Accumulator for segment. */ + /* Catch NULL pointer. */ + if (str == 0) + return 0; + /* Process every character in string. */ + + while (*str != '\0') { + /* Segment changeover. */ + + if (*str == '.') { + /* Must have some digits in segment. */ + if (chcnt == 0) + return 0; + /* Limit number of segments. */ + if (++segs == 4) + return 0; + /* Reset segment values and restart loop. */ + chcnt = accum = 0; + str++; + continue; + } + + /* Check numeric. */ + if ((*str < '0') || (*str > '9')) + return 0; + + /* Accumulate and check segment. */ + + if ((accum = accum * 10 + *str - '0') > 255) + return 0; + /* Advance other segment specific stuff and continue loop. */ + + chcnt++; + str++; + } + + /* Check enough segments and enough characters in last segment. */ + + if (segs != 3) + return 0; + if (chcnt == 0) + return 0; + /* Address okay. */ + + return 1; +} +uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* 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; + +} +uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s) +{ + uint32_t value = 0, digit; + int8_t c; + + while((c = *s++)){ + if('0' <= c && c <= '9') + digit = c - '0'; + else if('A' <= c && c <= 'F') + digit = c - 'A' + 10; + else if('a' <= c && c<= 'f') + digit = c - 'a' + 10; + else break; + + value = (value << 4) | digit; + } + + return value; +} + From b3408a70228b429ec8b6d27f09cc0cc78227f109 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 28 Aug 2015 20:33:59 -0500 Subject: [PATCH 052/104] all warnings fixed --- mqtt/mqtt_msg.c | 3 +- mqtt/utils.c | 194 ++++++++++++++++++++++++------------------------ 2 files changed, 99 insertions(+), 98 deletions(-) diff --git a/mqtt/mqtt_msg.c b/mqtt/mqtt_msg.c index 7e159c6..77c777e 100644 --- a/mqtt/mqtt_msg.c +++ b/mqtt/mqtt_msg.c @@ -125,7 +125,8 @@ static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connect void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) { - memset(connection, 0, sizeof(connection)); + uint8_t len = sizeof(connection); + memset(connection, '\0', len); connection->buffer = buffer; connection->buffer_length = buffer_length; } diff --git a/mqtt/utils.c b/mqtt/utils.c index 5ece013..173b573 100644 --- a/mqtt/utils.c +++ b/mqtt/utils.c @@ -32,113 +32,113 @@ */ #include "utils.h" - -uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str) +uint8_t ICACHE_FLASH_ATTR +UTILS_IsIPV4(int8_t *str) { - uint8_t segs = 0; /* Segment count. */ - uint8_t chcnt = 0; /* Character count within segment. */ - uint8_t accum = 0; /* Accumulator for segment. */ - /* Catch NULL pointer. */ - if (str == 0) - return 0; - /* Process every character in string. */ - - while (*str != '\0') { - /* Segment changeover. */ - - if (*str == '.') { - /* Must have some digits in segment. */ - if (chcnt == 0) - return 0; - /* Limit number of segments. */ - if (++segs == 4) - return 0; - /* Reset segment values and restart loop. */ - chcnt = accum = 0; - str++; - continue; - } - - /* Check numeric. */ - if ((*str < '0') || (*str > '9')) - return 0; - - /* Accumulate and check segment. */ - - if ((accum = accum * 10 + *str - '0') > 255) - return 0; - /* Advance other segment specific stuff and continue loop. */ - - chcnt++; - str++; - } + uint8_t segs = 0; /* Segment count. */ + uint8_t chcnt = 0; /* Character count within segment. */ + uint8_t accum = 0; /* Accumulator for segment. */ + /* Catch NULL pointer. */ + if (str == 0) + return 0; + /* Process every character in string. */ - /* Check enough segments and enough characters in last segment. */ + while (*str != '\0') { + /* Segment changeover. */ - if (segs != 3) + if (*str == '.') { + /* Must have some digits in segment. */ + if (chcnt == 0) return 0; - if (chcnt == 0) + /* Limit number of segments. */ + if (++segs == 4) return 0; - /* Address okay. */ + /* Reset segment values and restart loop. */ + chcnt = accum = 0; + str++; + continue; + } + + /* Check numeric. */ + if ((*str < '0') || (*str > '9')) + return 0; + + /* Accumulate and check segment. */ + + if ((accum = accum * 10 + *str - '0') > 255) + return 0; + /* Advance other segment specific stuff and continue loop. */ + + chcnt++; + str++; + } + + /* Check enough segments and enough characters in last segment. */ + + if (segs != 3) + return 0; + if (chcnt == 0) + return 0; + /* Address okay. */ - return 1; + return 1; } -uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* 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; +uint8_t ICACHE_FLASH_ATTR +UTILS_StrToIP(const int8_t* str, void *ip) +{ + /* The count of the number of bytes processed. */ + int i; + /* A pointer to the next digit to process. */ + for (i = 0; i < 4; i++) { + /* The digit being processed. */ + char c; + /* The value of this byte. */ + int n = 0; + while (1) { + c = *(const char *)str; + (const char *)str++; + 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; } -uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s) + +uint32_t ICACHE_FLASH_ATTR +UTILS_Atoh(const int8_t *s) { - uint32_t value = 0, digit; - int8_t c; - - while((c = *s++)){ - if('0' <= c && c <= '9') - digit = c - '0'; - else if('A' <= c && c <= 'F') - digit = c - 'A' + 10; - else if('a' <= c && c<= 'f') - digit = c - 'a' + 10; - else break; - - value = (value << 4) | digit; - } - - return value; + uint32_t value = 0, digit; + int8_t c; + + while ((c = *s++)){ + if ('0' <= c && c <= '9') + digit = c - '0'; + else if ('A' <= c && c <= 'F') + digit = c - 'A' + 10; + else if ('a' <= c && c <= 'f') + digit = c - 'a' + 10; + else break; + + value = (value << 4) | digit; + } + + return value; } From 74f61c07b945ae36b328f6a751eea0fbbdae1bdc Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 28 Aug 2015 22:27:48 -0700 Subject: [PATCH 053/104] rest lib tweak --- cmd/rest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/rest.c b/cmd/rest.c index 08afd09..75b5b0b 100644 --- a/cmd/rest.c +++ b/cmd/rest.c @@ -14,7 +14,7 @@ #define MAX_REST 4 static RestClient restClient[MAX_REST]; static uint8_t restNum = 0xff; // index into restClient for next slot to allocate -#define REST_CB 0xdead0000 // fudge added to callback for arduino so we can detect problems +#define REST_CB 0xbeef0000 // fudge added to callback for arduino so we can detect problems extern uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const char* str, void *ip); @@ -128,7 +128,7 @@ static void ICACHE_FLASH_ATTR tcpclient_recon_cb(void *arg, sint8 errType) { struct espconn *pCon = (struct espconn *)arg; RestClient* client = (RestClient *)pCon->reverse; - os_printf("REST $%d: conn reset\n", client-restClient); + os_printf("REST #%d: conn reset\n", client-restClient); } static void ICACHE_FLASH_ATTR From 31d308416b84e095e8399fb24fa91eb01cf0d1cb Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 29 Aug 2015 05:26:37 -0500 Subject: [PATCH 054/104] Merging --- .gitignore | 5 +- Makefile | 21 +- esp-link.sln | 22 +++ espfs/espfs.c | 66 ------- espfs/espfs.h | 4 - espfs/mkespfsimage/main.c | 90 ++------- espfs/mkespfsimage/mman-win32/Makefile | 48 +++++ espfs/mkespfsimage/mman-win32/config.mak | 11 ++ espfs/mkespfsimage/mman-win32/configure | 157 +++++++++++++++ espfs/mkespfsimage/mman-win32/mman.c | 180 +++++++++++++++++ espfs/mkespfsimage/mman-win32/mman.h | 55 ++++++ espfs/mkespfsimage/mman-win32/test.c | 235 +++++++++++++++++++++++ espmake.cmd | 7 + include/esp8266.h | 5 +- include/espmissingincludes.h | 10 +- user/config.c | 2 +- 16 files changed, 757 insertions(+), 161 deletions(-) create mode 100644 esp-link.sln create mode 100644 espfs/mkespfsimage/mman-win32/Makefile create mode 100644 espfs/mkespfsimage/mman-win32/config.mak create mode 100644 espfs/mkespfsimage/mman-win32/configure create mode 100644 espfs/mkespfsimage/mman-win32/mman.c create mode 100644 espfs/mkespfsimage/mman-win32/mman.h create mode 100644 espfs/mkespfsimage/mman-win32/test.c create mode 100644 espmake.cmd diff --git a/.gitignore b/.gitignore index 3364a8f..899b7c3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,8 @@ html_compressed/ esp-link.tgz tve-patch/ yui -.localhistory/ espfs/mkespfsimage/mman-win32/mman.o -espfs/mkespfsimage/mman-win32/libmman.a +esp-link.opensdf esp-link.sdf +espfs/mkespfsimage/mman-win32/libmman.a +.localhistory/ diff --git a/Makefile b/Makefile index 647ba20..08f0dcd 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ 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 +# Windows users use the com port i.e: ESPPORT ?= com3 ESPTOOL ?= $(abspath ../esp-open-sdk/esptool/esptool.py) ESPPORT ?= /dev/ttyUSB0 ESPBAUD ?= 460800 @@ -108,7 +109,6 @@ CHANGE_TO_STA ?= yes # This could speed up the downloading of these files, but might break compatibility with older # web browsers not supporting gzip encoding because Accept-Encoding is simply ignored. # Enable this option if you have large static files to serve (for e.g. JQuery, Twitter bootstrap) -# By default only js, css and html files are compressed using heatshrink. # If you have text based static files with different extensions what you want to serve compressed # then you will need to add the extension to the following places: # - Add the extension to this Makefile at the webpages.espfs target to the find command @@ -127,10 +127,6 @@ GZIP_COMPRESSION ?= yes COMPRESS_W_YUI ?= yes YUI-COMPRESSOR ?= yuicompressor-2.4.8.jar -# If USE_HEATSHRINK is set to "yes" then the espfs files will be compressed with Heatshrink and -# decompressed on the fly while reading the file. -# Because the decompression is done in the esp8266, it does not require any support in the browser. -USE_HEATSHRINK ?= no # -------------- End of config options ------------- @@ -147,7 +143,7 @@ APPGEN_TOOL ?= gen_appbin.py # which modules (subdirectories) of the project to include in compiling MODULES = espfs httpd user serial cmd -EXTRA_INCDIR = include . # lib/heatshrink/ +EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK LIBS = c gcc hal phy pp net80211 wpa main lwip @@ -216,10 +212,6 @@ ifeq ("$(GZIP_COMPRESSION)","yes") CFLAGS += -DGZIP_COMPRESSION endif -ifeq ("$(USE_HEATSHRINK)","yes") -CFLAGS += -DESPFS_HEATSHRINK -endif - ifeq ("$(CHANGE_TO_STA)","yes") CFLAGS += -DCHANGE_TO_STA endif @@ -289,6 +281,9 @@ $(BUILD_DIR): wiflash: all ./wiflash $(ESP_HOSTNAME) $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin +baseflash: all + $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin + flash: all $(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 \ @@ -296,7 +291,11 @@ flash: all yui/$(YUI-COMPRESSOR): $(Q) mkdir -p yui + ifeq ($(OS),Windows_NT) + cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) + else cd yui; wget https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) + endif ifeq ("$(COMPRESS_W_YUI)","yes") $(BUILD_BASE)/espfs_img.o: yui/$(YUI-COMPRESSOR) @@ -348,7 +347,7 @@ build/eagle.esphttpd2.v6.ld: $(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)" + $(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)" release: all $(Q) rm -rf release; mkdir -p release/esp-link diff --git a/esp-link.sln b/esp-link.sln new file mode 100644 index 0000000..fb7b468 --- /dev/null +++ b/esp-link.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "esp-link", "esp-link.vcxproj", "{A92F0CAA-F89B-4F78-AD2A-A042429BD87F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Release|ARM = Release|ARM + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Debug|ARM.ActiveCfg = Debug|ARM + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Debug|ARM.Build.0 = Debug|ARM + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Release|ARM.ActiveCfg = Release|ARM + {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Release|ARM.Build.0 = Release|ARM + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/espfs/espfs.c b/espfs/espfs.c index 3da6692..128ca29 100644 --- a/espfs/espfs.c +++ b/espfs/espfs.c @@ -40,11 +40,6 @@ It's written for use with httpd, but doesn't need to be used as such. #include "espfsformat.h" #include "espfs.h" -#ifdef ESPFS_HEATSHRINK -#include "heatshrink_config_custom.h" -#include "heatshrink_decoder.h" -#endif - static char* espFsData = NULL; struct EspFsFile { @@ -167,18 +162,6 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { r->posDecomp=0; if (h.compression==COMPRESS_NONE) { r->decompData=NULL; -#ifdef ESPFS_HEATSHRINK - } else if (h.compression==COMPRESS_HEATSHRINK) { - //File is compressed with Heatshrink. - char parm; - heatshrink_decoder *dec; - //Decoder params are stored in 1st byte. - memcpyAligned(&parm, r->posComp, 1); - r->posComp++; - //os_printf("Heatshrink compressed file; decode parms = %x\n", parm); - dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf); - r->decompData=dec; -#endif } else { os_printf("Invalid compression: %d\n", h.compression); return NULL; @@ -209,48 +192,6 @@ int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { fh->posComp+=len; // os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp); return len; -#ifdef ESPFS_HEATSHRINK - } else if (fh->decompressor==COMPRESS_HEATSHRINK) { - int decoded=0; - size_t elen, rlen; - char ebuff[16]; - heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData; -// os_printf("Alloc %p\n", dec); - if (fh->posDecomp == fdlen) { - return 0; - } - - // We must ensure that whole file is decompressed and written to output buffer. - // This means even when there is no input data (elen==0) try to poll decoder until - // posDecomp equals decompressed file length - - while(decodedposComp - fh->posStart); - if (elen>0) { - memcpyAligned(ebuff, fh->posComp, 16); - heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen>16)?16:elen, &rlen); - fh->posComp+=rlen; - } - //Grab decompressed data and put into buff - heatshrink_decoder_poll(dec, (uint8_t *)buff, len-decoded, &rlen); - fh->posDecomp+=rlen; - buff+=rlen; - decoded+=rlen; - -// os_printf("Elen %d rlen %d d %d pd %ld fdl %d\n",elen,rlen,decoded, fh->posDecomp, fdlen); - - if (elen == 0) { - if (fh->posDecomp == fdlen) { -// os_printf("Decoder finish\n"); - heatshrink_decoder_finish(dec); - } - return decoded; - } - } - return len; -#endif } return 0; } @@ -258,13 +199,6 @@ int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { //Close the file. void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) { if (fh==NULL) return; -#ifdef ESPFS_HEATSHRINK - if (fh->decompressor==COMPRESS_HEATSHRINK) { - heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData; - heatshrink_decoder_free(dec); -// os_printf("Freed %p\n", dec); - } -#endif //os_printf("Freed %p\n", fh); os_free(fh); } diff --git a/espfs/espfs.h b/espfs/espfs.h index c41df0a..c8e13e7 100644 --- a/espfs/espfs.h +++ b/espfs/espfs.h @@ -1,10 +1,6 @@ #ifndef ESPFS_H #define ESPFS_H -// This define is done in Makefile. If you do not use default Makefile, uncomment -// to be able to use Heatshrink-compressed espfs images. -//#define ESPFS_HEATSHRINK - typedef enum { ESPFS_INIT_RESULT_OK, ESPFS_INIT_RESULT_NO_IMAGE, diff --git a/espfs/mkespfsimage/main.c b/espfs/mkespfsimage/main.c index 4d5da0d..be3aeb7 100644 --- a/espfs/mkespfsimage/main.c +++ b/espfs/mkespfsimage/main.c @@ -5,27 +5,29 @@ #include #include #include -#include -#include #include #include "espfs.h" -#include "espfsformat.h" - -//Heatshrink -#ifdef ESPFS_HEATSHRINK -#include "heatshrink_common.h" -#include "heatshrink_config.h" -#include "heatshrink_encoder.h" +#ifdef __MINGW32__ +#include "mman-win32/mman.h" +#else +#include +#endif +#ifdef __WIN32__ +#include +#else +#include #endif +#include "espfsformat.h" //Gzip #ifdef ESPFS_GZIP -// If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev" +// If compiler complains about missing header, try running "sudo apt-get install zlib1g-dev" // to install missing package. #include #endif + //Routines to convert host format to the endianness used in the xtensa short htoxs(short in) { char r[2]; @@ -43,53 +45,6 @@ int htoxl(int in) { return *((int *)r); } -#ifdef ESPFS_HEATSHRINK -size_t compressHeatshrink(char *in, int insize, char *out, int outsize, int level) { - char *inp=in; - char *outp=out; - size_t len; - int ws[]={5, 6, 8, 11, 13}; - int ls[]={3, 3, 4, 4, 4}; - HSE_poll_res pres; - HSE_sink_res sres; - size_t r; - if (level==-1) level=8; - level=(level-1)/2; //level is now 0, 1, 2, 3, 4 - heatshrink_encoder *enc=heatshrink_encoder_alloc(ws[level], ls[level]); - if (enc==NULL) { - perror("allocating mem for heatshrink"); - exit(1); - } - //Save encoder parms as first byte - *outp=(ws[level]<<4)|ls[level]; - outp++; outsize--; - - r=1; - do { - if (insize>0) { - sres=heatshrink_encoder_sink(enc, inp, insize, &len); - if (sres!=HSER_SINK_OK) break; - inp+=len; insize-=len; - if (insize==0) heatshrink_encoder_finish(enc); - } - do { - pres=heatshrink_encoder_poll(enc, outp, outsize, &len); - if (pres!=HSER_POLL_MORE && pres!=HSER_POLL_EMPTY) break; - outp+=len; outsize-=len; - r+=len; - } while (pres==HSER_POLL_MORE); - } while (insize!=0); - - if (insize!=0) { - fprintf(stderr, "Heatshrink: Bug? insize is still %d. sres=%d pres=%d\n", insize, sres, pres); - exit(1); - } - - heatshrink_encoder_free(enc); - return r; -} -#endif - #ifdef ESPFS_GZIP size_t compressGzip(char *in, int insize, char *out, int outsize, int level) { z_stream stream; @@ -202,11 +157,6 @@ int handleFile(int f, char *name, int compression, int level, char **compName, o if (compression==COMPRESS_NONE) { csize=size; cdat=fdat; -#ifdef ESPFS_HEATSHRINK - } else if (compression==COMPRESS_HEATSHRINK) { - cdat=malloc(size*2); - csize=compressHeatshrink(fdat, size, cdat, size*2, level); -#endif } else { fprintf(stderr, "Unknown compression - %d\n", compression); exit(1); @@ -245,9 +195,7 @@ int handleFile(int f, char *name, int compression, int level, char **compName, o munmap(fdat, size); if (compName != NULL) { - if (h.compression==COMPRESS_HEATSHRINK) { - *compName = "heatshrink"; - } else if (h.compression==COMPRESS_NONE) { + if (h.compression==COMPRESS_NONE) { if (h.flags & FLAG_GZIP) { *compName = "gzip"; } else { @@ -284,11 +232,7 @@ int main(int argc, char **argv) { int compType; //default compression type - heatshrink int compLvl=-1; -#ifdef ESPFS_HEATSHRINK - compType = COMPRESS_HEATSHRINK; -#else compType = COMPRESS_NONE; -#endif for (x=1; x=x-2) { @@ -322,11 +266,7 @@ int main(int argc, char **argv) { #endif fprintf(stderr, "> out.espfs\n"); fprintf(stderr, "Compressors:\n"); -#ifdef ESPFS_HEATSHRINK - fprintf(stderr, "0 - None\n1 - Heatshrink(default)\n"); -#else fprintf(stderr, "0 - None(default)\n"); -#endif fprintf(stderr, "\nCompression level: 1 is worst but low RAM usage, higher is better compression \nbut uses more ram on decompression. -1 = compressors default.\n"); #ifdef ESPFS_GZIP fprintf(stderr, "\nGzipped extensions: list of comma separated, case sensitive file extensions \nthat will be gzipped. Defaults to 'html,css,js'\n"); @@ -334,6 +274,10 @@ int main(int argc, char **argv) { exit(0); } +#ifdef __WIN32__ + setmode(fileno(stdout), _O_BINARY); +#endif + while(fgets(fileName, sizeof(fileName), stdin)) { //Kill off '\n' at the end fileName[strlen(fileName)-1]=0; diff --git a/espfs/mkespfsimage/mman-win32/Makefile b/espfs/mkespfsimage/mman-win32/Makefile new file mode 100644 index 0000000..5624c1b --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/Makefile @@ -0,0 +1,48 @@ +# +# mman-win32 (mingw32) Makefile +# +include config.mak + +ifeq ($(BUILD_STATIC),yes) + TARGETS+=libmman.a + INSTALL+=static-install +endif +ifeq ($(BUILD_MSVC),yes) + SHFLAGS+=-Wl,--output-def,libmman.def + INSTALL+=lib-install +endif + +all: $(TARGETS) + +mman.o: mman.c mman.h + $(CC) -o mman.o -c mman.c -Wall -O3 -fomit-frame-pointer + +libmman.a: mman.o + $(AR) cru libmman.a mman.o + $(RANLIB) libmman.a + +static-install: + mkdir -p $(DESTDIR)$(libdir) + cp libmman.a $(DESTDIR)$(libdir) + mkdir -p $(DESTDIR)$(incdir) + cp mman.h $(DESTDIR)$(incdir) + +lib-install: + mkdir -p $(DESTDIR)$(libdir) + cp libmman.lib $(DESTDIR)$(libdir) + +install: $(INSTALL) + +test.exe: test.c mman.c mman.h + $(CC) -o test.exe test.c -L. -lmman + +test: $(TARGETS) test.exe + test.exe + +clean:: + rm -f mman.o libmman.a libmman.def libmman.lib test.exe *.dat + +distclean: clean + rm -f config.mak + +.PHONY: clean distclean install test diff --git a/espfs/mkespfsimage/mman-win32/config.mak b/espfs/mkespfsimage/mman-win32/config.mak new file mode 100644 index 0000000..2d2ae1b --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/config.mak @@ -0,0 +1,11 @@ +# Automatically generated by configure +PREFIX=/mingw +libdir=/mingw/lib +incdir=/mingw/include/sys +AR=ar +CC=gcc +RANLIB=ranlib +STRIP=strip +BUILD_STATIC=yes +BUILD_MSVC= +LIBCMD=echo ignoring lib diff --git a/espfs/mkespfsimage/mman-win32/configure b/espfs/mkespfsimage/mman-win32/configure new file mode 100644 index 0000000..c928f11 --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/configure @@ -0,0 +1,157 @@ +#!/bin/sh +# mmap-win32 configure script +# +# Parts copied from FFmpeg's configure +# + +set_all(){ + value=$1 + shift + for var in $*; do + eval $var=$value + done +} + +enable(){ + set_all yes $* +} + +disable(){ + set_all no $* +} + +enabled(){ + eval test "x\$$1" = "xyes" +} + +disabled(){ + eval test "x\$$1" = "xno" +} + +show_help(){ + echo "Usage: configure [options]" + echo "Options: [defaults in brackets after descriptions]" + echo "All \"enable\" options have \"disable\" counterparts" + echo + echo " --help print this message" + echo " --prefix=PREFIX install in PREFIX [$PREFIX]" + echo " --libdir=DIR install libs in DIR [$PREFIX/lib]" + echo " --incdir=DIR install includes in DIR [$PREFIX/include]" + echo " --enable-static build static libraries [yes]" + echo " --enable-msvc create msvc-compatible import lib [auto]" + echo + echo " --cc=CC use C compiler CC [$cc_default]" + echo " --cross-prefix=PREFIX use PREFIX for compilation tools [$cross_prefix]" + exit 1 +} + +die_unknown(){ + echo "Unknown option \"$1\"." + echo "See $0 --help for available options." + exit 1 +} + +PREFIX="/mingw" +libdir="${PREFIX}/lib" +incdir="${PREFIX}/include/sys" +ar="ar" +cc_default="gcc" +ranlib="ranlib" +strip="strip" + +DEFAULT="msvc +" + +DEFAULT_YES="static + stripping +" + +CMDLINE_SELECT="$DEFAULT + $DEFAULT_NO + $DEFAULT_YES +" + +enable $DEFAULT_YES +disable $DEFAULT_NO + +for opt do + optval="${opt#*=}" + case "$opt" in + --help) + show_help + ;; + --prefix=*) + PREFIX="$optval" + ;; + --libdir=*) + libdir="$optval" + ;; + --incdir=*) + incdir="$optval" + ;; + --cc=*) + cc="$optval" + ;; + --cross-prefix=*) + cross_prefix="$optval" + ;; + --enable-?*|--disable-?*) + eval `echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g'` + echo "$CMDLINE_SELECT" | grep -q "^ *$option\$" || die_unknown $opt + $action $option + ;; + *) + die_unknown $opt + ;; + esac +done + +ar="${cross_prefix}${ar}" +cc_default="${cross_prefix}${cc_default}" +ranlib="${cross_prefix}${ranlib}" +strip="${cross_prefix}${strip}" + +if ! test -z $cc; then + cc_default="${cc}" +fi +cc="${cc_default}" + +disabled static && { + echo "At least one library type must be set."; + exit 1; +} + +if enabled msvc; then + lib /? > /dev/null 2>&1 /dev/null || { + echo "MSVC's lib command not found." + echo "Make sure MSVC is installed and its bin folder is in your \$PATH." + exit 1 + } +fi + +if ! enabled stripping; then + strip="echo ignoring strip" +fi + +enabled msvc && libcmd="lib" || libcmd="echo ignoring lib" + +echo "# Automatically generated by configure" > config.mak +echo "PREFIX=$PREFIX" >> config.mak +echo "libdir=$libdir" >> config.mak +echo "incdir=$incdir" >> config.mak +echo "AR=$ar" >> config.mak +echo "CC=$cc" >> config.mak +echo "RANLIB=$ranlib" >> config.mak +echo "STRIP=$strip" >> config.mak +echo "BUILD_STATIC=$static" >> config.mak +echo "BUILD_MSVC=$msvc" >> config.mak +echo "LIBCMD=$libcmd" >> config.mak + +echo "prefix: $PREFIX" +echo "libdir: $libdir" +echo "incdir: $incdir" +echo "ar: $ar" +echo "cc: $cc" +echo "ranlib: $ranlib" +echo "strip: $strip" +echo "static: $static" diff --git a/espfs/mkespfsimage/mman-win32/mman.c b/espfs/mkespfsimage/mman-win32/mman.c new file mode 100644 index 0000000..486ed94 --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/mman.c @@ -0,0 +1,180 @@ + +#include +#include +#include + +#include "mman.h" + +#ifndef FILE_MAP_EXECUTE +#define FILE_MAP_EXECUTE 0x0020 +#endif /* FILE_MAP_EXECUTE */ + +static int __map_mman_error(const DWORD err, const int deferr) +{ + if (err == 0) + return 0; + //TODO: implement + return err; +} + +static DWORD __map_mmap_prot_page(const int prot) +{ + DWORD protect = 0; + + if (prot == PROT_NONE) + return protect; + + if ((prot & PROT_EXEC) != 0) + { + protect = ((prot & PROT_WRITE) != 0) ? + PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; + } + else + { + protect = ((prot & PROT_WRITE) != 0) ? + PAGE_READWRITE : PAGE_READONLY; + } + + return protect; +} + +static DWORD __map_mmap_prot_file(const int prot) +{ + DWORD desiredAccess = 0; + + if (prot == PROT_NONE) + return desiredAccess; + + if ((prot & PROT_READ) != 0) + desiredAccess |= FILE_MAP_READ; + if ((prot & PROT_WRITE) != 0) + desiredAccess |= FILE_MAP_WRITE; + if ((prot & PROT_EXEC) != 0) + desiredAccess |= FILE_MAP_EXECUTE; + + return desiredAccess; +} + +void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) +{ + HANDLE fm, h; + + void * map = MAP_FAILED; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4293) +#endif + + const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)off : (DWORD)(off & 0xFFFFFFFFL); + const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL); + const DWORD protect = __map_mmap_prot_page(prot); + const DWORD desiredAccess = __map_mmap_prot_file(prot); + + const off_t maxSize = off + (off_t)len; + + const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL); + const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + errno = 0; + + if (len == 0 + /* Unsupported flag combinations */ + || (flags & MAP_FIXED) != 0 + /* Usupported protection combinations */ + || prot == PROT_EXEC) + { + errno = EINVAL; + return MAP_FAILED; + } + + h = ((flags & MAP_ANONYMOUS) == 0) ? + (HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE; + + if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return MAP_FAILED; + } + + fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL); + + if (fm == NULL) + { + errno = __map_mman_error(GetLastError(), EPERM); + return MAP_FAILED; + } + + map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len); + + CloseHandle(fm); + + if (map == NULL) + { + errno = __map_mman_error(GetLastError(), EPERM); + return MAP_FAILED; + } + + return map; +} + +int munmap(void *addr, size_t len) +{ + if (UnmapViewOfFile(addr)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int mprotect(void *addr, size_t len, int prot) +{ + DWORD newProtect = __map_mmap_prot_page(prot); + DWORD oldProtect = 0; + + if (VirtualProtect(addr, len, newProtect, &oldProtect)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int msync(void *addr, size_t len, int flags) +{ + if (FlushViewOfFile(addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int mlock(const void *addr, size_t len) +{ + if (VirtualLock((LPVOID)addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int munlock(const void *addr, size_t len) +{ + if (VirtualUnlock((LPVOID)addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} diff --git a/espfs/mkespfsimage/mman-win32/mman.h b/espfs/mkespfsimage/mman-win32/mman.h new file mode 100644 index 0000000..ffa3748 --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/mman.h @@ -0,0 +1,55 @@ +/* + * sys/mman.h + * mman-win32 + */ + +#ifndef _SYS_MMAN_H_ +#define _SYS_MMAN_H_ + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +/* All the headers include this file. */ +#ifndef _MSC_VER +#include <_mingw.h> +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_TYPE 0xf +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS + +#define MAP_FAILED ((void *)-1) + +/* Flags for msync. */ +#define MS_ASYNC 1 +#define MS_SYNC 2 +#define MS_INVALIDATE 4 + +void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); +int munmap(void *addr, size_t len); +int mprotect(void *addr, size_t len, int prot); +int msync(void *addr, size_t len, int flags); +int mlock(const void *addr, size_t len); +int munlock(const void *addr, size_t len); + +#ifdef __cplusplus +}; +#endif + +#endif /* _SYS_MMAN_H_ */ diff --git a/espfs/mkespfsimage/mman-win32/test.c b/espfs/mkespfsimage/mman-win32/test.c new file mode 100644 index 0000000..9374b9f --- /dev/null +++ b/espfs/mkespfsimage/mman-win32/test.c @@ -0,0 +1,235 @@ + +#include "mman.h" + +#include +#include +#include + +#ifndef NULL +#define NULL (void*)0 +#endif + +const char* map_file_name = "map_file.dat"; + +int test_anon_map_readwrite() +{ + void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\n", errno); + + return result; +} + +int test_anon_map_readonly() +{ + void* map = mmap(NULL, 1024, PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + + return result; +} + +int test_anon_map_writeonly() +{ + void* map = mmap(NULL, 1024, PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\n", errno); + + return result; +} + +int test_anon_map_readonly_nowrite() +{ + void* map = mmap(NULL, 1024, PROT_READ, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map == MAP_FAILED) + { + printf("mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + return -1; + } + + if (*((unsigned char*)map) != 0) + printf("test_anon_map_readonly_nowrite (MAP_ANONYMOUS, PROT_READ) returned unexpected value: %d\n", + (int)*((unsigned char*)map)); + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); + + return result; +} + +int test_file_map_readwrite() +{ + mode_t mode = S_IRUSR | S_IWUSR; + int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); + + void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); + if (map == MAP_FAILED) + { + printf("mmap returned unexpected error: %d\n", errno); + return -1; + } + + *((unsigned char*)map) = 1; + + int result = munmap(map, 1024); + + if (result != 0) + printf("munmap returned unexpected error: %d\n", errno); + + close(o); + + /*TODO: get file info and content and compare it with the sources conditions */ + unlink(map_file_name); + + return result; +} + +int test_file_map_mlock_munlock() +{ + const size_t map_size = 1024; + + int result = 0; + mode_t mode = S_IRUSR | S_IWUSR; + int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); + if (o == -1) + { + printf("unable to create file %s: %d\n", map_file_name, errno); + return -1; + } + + void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); + if (map == MAP_FAILED) + { + printf("mmap returned unexpected error: %d\n", errno); + result = -1; + goto done_close; + } + + if (mlock(map, map_size) != 0) + { + printf("mlock returned unexpected error: %d\n", errno); + result = -1; + goto done_munmap; + } + + *((unsigned char*)map) = 1; + + if (munlock(map, map_size) != 0) + { + printf("munlock returned unexpected error: %d\n", errno); + result = -1; + } + +done_munmap: + result = munmap(map, map_size); + + if (result != 0) + printf("munmap returned unexpected error: %d\n", errno); + +done_close: + close(o); + + unlink(map_file_name); +done: + return result; +} + +int test_file_map_msync() +{ + const size_t map_size = 1024; + + int result = 0; + mode_t mode = S_IRUSR | S_IWUSR; + int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); + if (o == -1) + { + printf("unable to create file %s: %d\n", map_file_name, errno); + return -1; + } + + void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); + if (map == MAP_FAILED) + { + printf("mmap returned unexpected error: %d\n", errno); + result = -1; + goto done_close; + } + + *((unsigned char*)map) = 1; + + if (msync(map, map_size, MS_SYNC) != 0) + { + printf("msync returned unexpected error: %d\n", errno); + result = -1; + } + + result = munmap(map, map_size); + + if (result != 0) + printf("munmap returned unexpected error: %d\n", errno); + +done_close: + close(o); + + unlink(map_file_name); +done: + return result; +} + +#define EXEC_TEST(name) \ + if (name() != 0) { result = -1; printf( #name ": fail\n"); } \ + else { printf(#name ": pass\n"); } + +int main() +{ + int result = 0; + + EXEC_TEST(test_anon_map_readwrite); + //NOTE: this test must cause an access violation exception + //EXEC_TEST(test_anon_map_readonly); + EXEC_TEST(test_anon_map_readonly_nowrite); + EXEC_TEST(test_anon_map_writeonly); + + EXEC_TEST(test_file_map_readwrite); + EXEC_TEST(test_file_map_mlock_munlock); + EXEC_TEST(test_file_map_msync); + //TODO: EXEC_TEST(test_file_map_mprotect); + + return result; +} diff --git a/espmake.cmd b/espmake.cmd new file mode 100644 index 0000000..42e25a6 --- /dev/null +++ b/espmake.cmd @@ -0,0 +1,7 @@ +@echo off + +REM remove automatic created obj folder +rd obj /S /Q >nul 2>&1 + +PATH=%PATH%;C:\Espressif\xtensa-lx106-elf\bin;C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\espressif\git-bin;C:\espressif\java-bin;C:\Python27 +make -f Makefile %1 %2 %3 %4 %5 \ No newline at end of file diff --git a/include/esp8266.h b/include/esp8266.h index ce92455..96a7364 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -13,7 +13,10 @@ #include #include #include -#include #include "espmissingincludes.h" #include "uart_hw.h" + +#ifdef __WIN32__ +#include <_mingw.h> +#endif diff --git a/include/espmissingincludes.h b/include/espmissingincludes.h index c69b9fc..e5a20a0 100644 --- a/include/espmissingincludes.h +++ b/include/espmissingincludes.h @@ -1,9 +1,8 @@ #ifndef ESPMISSINGINCLUDES_H #define ESPMISSINGINCLUDES_H -#include -#include -#include +#include +#include //Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. //MOST OF THESE ARE GUESSED! but they seem to work and shut up the compiler. @@ -54,6 +53,11 @@ int rand(void); void ets_bzero(void *s, size_t n); void ets_delay_us(int ms); +// Shortcuts for memory functions +#define os_malloc pvPortMalloc +#define os_free vPortFree +#define os_zalloc pvPortZalloc + // disappeared in SDK 1.1.0: #define os_timer_done ets_timer_done #define os_timer_handler_isr ets_timer_handler_isr diff --git a/user/config.c b/user/config.c index 835e74f..b44b329 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 (don't by default) + 0, // swap uart (don't by default) 1, 0, // tcp_enable, rssi_enable "\0", // api_key }; From e74b9f5a0bc12aa9bc8d66aa96838269cfc62c79 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 29 Aug 2015 05:33:26 -0500 Subject: [PATCH 055/104] fixing merge problems --- esp-link.vcxproj | 2 +- espfs/heatshrink_config_custom.h | 30 ------------------------- espfs/heatshrink_decoder.c | 19 ---------------- espfs/mkespfsimage/heatshrink_encoder.c | 4 ---- 4 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 espfs/heatshrink_config_custom.h delete mode 100644 espfs/heatshrink_decoder.c delete mode 100644 espfs/mkespfsimage/heatshrink_encoder.c diff --git a/esp-link.vcxproj b/esp-link.vcxproj index c98faa5..918ed96 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include + .\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;C:\tools\mingw64\include diff --git a/espfs/heatshrink_config_custom.h b/espfs/heatshrink_config_custom.h deleted file mode 100644 index f885f87..0000000 --- a/espfs/heatshrink_config_custom.h +++ /dev/null @@ -1,30 +0,0 @@ -//Heatshrink config for the decompressor. -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -/* Should functionality assuming dynamic allocation be used? */ -#define HEATSHRINK_DYNAMIC_ALLOC 1 - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Optional replacement of malloc/free */ - #ifdef __ets__ - #define HEATSHRINK_MALLOC(SZ) os_malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) os_free(P) - #else - #define HEATSHRINK_MALLOC(SZ) malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) free(P) - #endif -#else - /* Required parameters for static configuration */ - #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 -#endif - -/* Turn on logging for debugging. */ -#define HEATSHRINK_DEBUGGING_LOGS 0 - -/* Use indexing for faster compression. (This requires additional space.) */ -#define HEATSHRINK_USE_INDEX 1 - -#endif diff --git a/espfs/heatshrink_decoder.c b/espfs/heatshrink_decoder.c deleted file mode 100644 index 522b560..0000000 --- a/espfs/heatshrink_decoder.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "espfs.h" -#ifdef ESPFS_HEATSHRINK -//Stupid wrapper so we don't have to move c-files around -//Also loads httpd-specific config. - -#ifdef __ets__ -//esp build - -#include - -#define memset(x,y,z) os_memset(x,y,z) -#define memcpy(x,y,z) os_memcpy(x,y,z) -#endif - -#include "heatshrink_config_custom.h" -#include "../lib/heatshrink/heatshrink_decoder.c" - - -#endif diff --git a/espfs/mkespfsimage/heatshrink_encoder.c b/espfs/mkespfsimage/heatshrink_encoder.c deleted file mode 100644 index b563970..0000000 --- a/espfs/mkespfsimage/heatshrink_encoder.c +++ /dev/null @@ -1,4 +0,0 @@ -//Stupid wraparound include to make sure object file doesn't end up in heatshrink dir -#ifdef ESPFS_HEATSHRINK -#include "../lib/heatshrink/heatshrink_encoder.c" -#endif \ No newline at end of file From c2e60809f6155aa260eb1be58141cac8c382e195 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 29 Aug 2015 11:29:40 -0500 Subject: [PATCH 056/104] more mqtt work --- cmd/handlers.c | 15 +- cmd/mqtt_cmd.c | 336 ++++++++++++++++++++++++++++++++++ cmd/mqtt_cmd.h | 22 +++ esp-link.vcxproj | 35 +++- include/esp8266.h | 4 +- mqtt/include/proto.h | 31 ---- mqtt/include/ringbuf.h | 18 -- mqtt/include/typedef.h | 17 -- mqtt/include/utils.h | 9 - mqtt/mqtt.c | 190 ++++++++++--------- mqtt/{include => }/mqtt.h | 112 ++++++------ mqtt/mqtt_msg.c | 217 ++++++++++------------ mqtt/{include => }/mqtt_msg.h | 66 +++---- mqtt/proto.c | 182 +++++++----------- mqtt/proto.h | 28 +++ mqtt/queue.c | 15 +- mqtt/{include => }/queue.h | 13 +- mqtt/ringbuf.c | 82 ++++----- mqtt/ringbuf.h | 17 ++ mqtt/utils.c | 18 +- mqtt/utils.h | 9 + user/user_main.c | 186 +++++++++---------- 22 files changed, 947 insertions(+), 675 deletions(-) create mode 100644 cmd/mqtt_cmd.c create mode 100644 cmd/mqtt_cmd.h delete mode 100644 mqtt/include/proto.h delete mode 100644 mqtt/include/ringbuf.h delete mode 100644 mqtt/include/typedef.h delete mode 100644 mqtt/include/utils.h rename mqtt/{include => }/mqtt.h (60%) rename mqtt/{include => }/mqtt_msg.h (86%) create mode 100644 mqtt/proto.h rename mqtt/{include => }/queue.h (86%) create mode 100644 mqtt/ringbuf.h create mode 100644 mqtt/utils.h diff --git a/cmd/handlers.c b/cmd/handlers.c index 28382dd..ae21bec 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -9,6 +9,7 @@ #include "serbridge.h" #include "uart.h" #include "cgiwifi.h" +#include "mqtt_cmd.h" static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd); static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd); @@ -24,14 +25,12 @@ const CmdList commands[] = { {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_MQTT_SETUP, MQTTCMD_Setup}, + {CMD_MQTT_CONNECT, MQTTCMD_Connect}, + {CMD_MQTT_DISCONNECT, MQTTCMD_Disconnect}, + {CMD_MQTT_PUBLISH, MQTTCMD_Publish}, + {CMD_MQTT_SUBSCRIBE , MQTTCMD_Subscribe}, + {CMD_MQTT_LWT, MQTTCMD_Lwt}, {CMD_REST_SETUP, REST_Setup}, {CMD_REST_REQUEST, REST_Request}, diff --git a/cmd/mqtt_cmd.c b/cmd/mqtt_cmd.c new file mode 100644 index 0000000..d12ba21 --- /dev/null +++ b/cmd/mqtt_cmd.c @@ -0,0 +1,336 @@ +#include "mqtt_cmd.h" + +uint32_t connectedCb = 0, disconnectCb = 0, publishedCb = 0, dataCb = 0; + +void ICACHE_FLASH_ATTR +mqttConnectedCb(uint32_t* args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: Connected connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n", + (void*)cb->connectedCb, + (void*)cb->disconnectedCb, + (void*)cb->publishedCb, + (void*)cb->dataCb); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +mqttTcpDisconnectedCb(uint32_t *args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb *cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: TCP Disconnected\n"); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->tcpDisconnectedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +mqttDisconnectedCb(uint32_t* args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: Disconnected\n"); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +mqttPublishedCb(uint32_t* args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: Published\n"); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +mqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) { + uint16_t crc = 0; + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + + crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->dataCb, 0, 2); + crc = CMD_ResponseBody(crc, (uint8_t*)topic, topic_len); + crc = CMD_ResponseBody(crc, (uint8_t*)data, data_len); + CMD_ResponseEnd(crc); +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Setup(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 9) + return 0; + + // create mqtt client + uint8_t clientLen = sizeof(MQTT_Client); + MQTT_Client* client = (MQTT_Client*)os_zalloc(clientLen); + if (client == NULL) + return 0; + os_memset(client, 0, clientLen); + + uint16_t len; + uint8_t *client_id, *user_data, *pass_data; + uint32_t keepalive, clean_session, cb_data; + + // get client id + len = CMD_ArgLen(&req); + if (len > 32) return 0; // safety check + client_id = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, client_id, len); + client_id[len] = 0; + + // get username + len = CMD_ArgLen(&req); + if (len > 32) return 0; // safety check + user_data = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, user_data, len); + user_data[len] = 0; + + // get password + len = CMD_ArgLen(&req); + if (len > 32) return 0; // safety check + pass_data = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, pass_data, len); + pass_data[len] = 0; + + // get keepalive + CMD_PopArg(&req, (uint8_t*)&keepalive, 4); + + // get clean session + CMD_PopArg(&req, (uint8_t*)&clean_session, 4); + + os_printf("MQTT: MQTTCMD_Setup clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session); + + // init client + // TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again? + MQTT_InitClient(client, client_id, user_data, pass_data, keepalive, clean_session); + + // create callback + MqttCmdCb* callback = (MqttCmdCb*)os_zalloc(sizeof(MqttCmdCb)); + + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->connectedCb = cb_data; + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->disconnectedCb = cb_data; + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->publishedCb = cb_data; + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->dataCb = cb_data; + + client->user_data = callback; + + client->cmdConnectedCb = mqttConnectedCb; + client->cmdDisconnectedCb = mqttDisconnectedCb; + client->cmdPublishedCb = mqttPublishedCb; + client->cmdDataCb = mqttDataCb; + + if (CMD_GetArgc(&req) == 10) { + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->tcpDisconnectedCb = cb_data; + client->cmdTcpDisconnectedCb = mqttTcpDisconnectedCb; + } + + os_free(client_id); + os_free(user_data); + os_free(pass_data); + + return (uint32_t)client; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Lwt(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 5) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + + // get topic + if (client->connect_info.will_topic) + os_free(client->connect_info.will_topic); + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + client->connect_info.will_topic = (char*)os_zalloc(len + 1); + CMD_PopArg(&req, client->connect_info.will_topic, len); + client->connect_info.will_topic[len] = 0; + + // get message + if (client->connect_info.will_message) + os_free(client->connect_info.will_message); + len = CMD_ArgLen(&req); + // TODO: safety check + client->connect_info.will_message = (char*)os_zalloc(len + 1); + CMD_PopArg(&req, client->connect_info.will_message, len); + client->connect_info.will_message[len] = 0; + + // get qos + CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_qos, 4); + + // get retain + CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); + + os_printf("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%ld, retain=%ld\n", + client->connect_info.will_topic, + client->connect_info.will_message, + client->connect_info.will_qos, + client->connect_info.will_retain); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Connect(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 4) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Connect client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + + // get host + if (client->host) + os_free(client->host); + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + client->host = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, client->host, len); + client->host[len] = 0; + + // get port + CMD_PopArg(&req, (uint8_t*)&client->port, 4); + + // get security + CMD_PopArg(&req, (uint8_t*)&client->security, 4); + + os_printf("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%ld\n", + client->host, + client->port, + client->security); + + MQTT_Connect(client); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Disconnect(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 1) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Disconnect client ptr=%p\n", (void*)client_ptr); + + // disconnect + MQTT_Disconnect(client); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Publish(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 6) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Publish client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + uint8_t *topic, *data; + uint32_t qos = 0, retain = 0, data_len; + + // get topic + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + topic = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, topic, len); + topic[len] = 0; + + // get data + len = CMD_ArgLen(&req); + // TODO: Safety check + // TODO: this was orignially zalloc len not len+1 + data = (uint8_t*)os_zalloc(len+1); + CMD_PopArg(&req, data, len); + // TODO: next line not originally present + data[len] = 0; + + // get data length + // TODO: this isn't used but we have to pull it off the stack + CMD_PopArg(&req, (uint8_t*)&data_len, 4); + + // get qos + CMD_PopArg(&req, (uint8_t*)&qos, 4); + + // get retain + CMD_PopArg(&req, (uint8_t*)&retain, 4); + + os_printf("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n", + topic, + os_strlen((char*)data), + qos, + retain); + + MQTT_Publish(client, (char*)topic, (char*)data, (uint8_t)qos, (uint8_t)retain); + os_free(topic); + os_free(data); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Subscribe(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 3) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Subscribe client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + uint8_t* topic; + uint32_t qos = 0; + + // get topic + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + topic = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, topic, len); + topic[len] = 0; + + // get qos + CMD_PopArg(&req, (uint8_t*)&qos, 4); + + os_printf("MQTT: MQTTCMD_Subscribe topic=%s, qos=%ld\n", topic, qos); + MQTT_Subscribe(client, (char*)topic, (uint8_t)qos); + os_free(topic); + return 1; +} diff --git a/cmd/mqtt_cmd.h b/cmd/mqtt_cmd.h new file mode 100644 index 0000000..66fd8f6 --- /dev/null +++ b/cmd/mqtt_cmd.h @@ -0,0 +1,22 @@ +#ifndef MODULES_MQTT_CMD_H_ +#define MODULES_MQTT_CMD_H_ + +#include "cmd.h" +#include "mqtt.h" + +typedef struct { + uint32_t connectedCb; + uint32_t disconnectedCb; + uint32_t publishedCb; + uint32_t dataCb; + uint32_t tcpDisconnectedCb; +} MqttCmdCb; + +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Connect(CmdPacket *cmd); +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Disconnect(CmdPacket *cmd); +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Setup(CmdPacket *cmd); +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Publish(CmdPacket *cmd); +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Subscribe(CmdPacket *cmd); +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Lwt(CmdPacket *cmd); + +#endif /* MODULES_MQTT_CMD_H_ */ diff --git a/esp-link.vcxproj b/esp-link.vcxproj index eac1d5b..ce508d0 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\mqtt\include;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include + .\mqtt;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include @@ -65,6 +65,7 @@ + @@ -87,6 +88,15 @@ + + + + + + + + + @@ -95,10 +105,10 @@ - + @@ -112,18 +122,25 @@ - - - - - - - + + + + + + + + + + + + + + diff --git a/include/esp8266.h b/include/esp8266.h index 96a7364..535dfeb 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -1,5 +1,5 @@ // Combined include file for esp8266 - +#include #include #include #include @@ -17,6 +17,8 @@ #include "espmissingincludes.h" #include "uart_hw.h" +//void init(void); + #ifdef __WIN32__ #include <_mingw.h> #endif diff --git a/mqtt/include/proto.h b/mqtt/include/proto.h deleted file mode 100644 index 73a4e8b..0000000 --- a/mqtt/include/proto.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * File: proto.h - * Author: ThuHien - * - * Created on November 23, 2012, 8:57 AM - */ - -#ifndef _PROTO_H_ -#define _PROTO_H_ -#include -#include "ringbuf.h" - -typedef void(PROTO_PARSE_CALLBACK)(); - -typedef struct{ - uint8_t *buf; - uint16_t bufSize; - uint16_t dataLen; - uint8_t isEsc; - uint8_t isBegin; - PROTO_PARSE_CALLBACK* callback; -}PROTO_PARSER; - -int8_t ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, uint8_t *buf, uint16_t bufSize); -int8_t ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, uint8_t *buf, uint16_t len); -int16_t ICACHE_FLASH_ATTR PROTO_Add(uint8_t *buf, const uint8_t *packet, int16_t bufSize); -int16_t ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const uint8_t *packet, int16_t len); -int8_t ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, uint8_t value); -int16_t ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, uint8_t *bufOut, uint16_t* len, uint16_t maxBufLen); -#endif - diff --git a/mqtt/include/ringbuf.h b/mqtt/include/ringbuf.h deleted file mode 100644 index 03d780d..0000000 --- a/mqtt/include/ringbuf.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _RING_BUF_H_ -#define _RING_BUF_H_ - -#include -#include "typedef.h" - -typedef struct{ - U8* p_o; /**< Original pointer */ - U8* volatile p_r; /**< Read pointer */ - U8* volatile p_w; /**< Write pointer */ - volatile I32 fill_cnt; /**< Number of filled slots */ - I32 size; /**< Buffer size */ -}RINGBUF; - -I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); -I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c); -I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c); -#endif diff --git a/mqtt/include/typedef.h b/mqtt/include/typedef.h deleted file mode 100644 index a4c69d6..0000000 --- a/mqtt/include/typedef.h +++ /dev/null @@ -1,17 +0,0 @@ -/** -* \file -* Standard Types definition -*/ - -#ifndef _TYPE_DEF_H_ -#define _TYPE_DEF_H_ - -typedef char I8; -typedef unsigned char U8; -typedef short I16; -typedef unsigned short U16; -typedef long I32; -typedef unsigned long U32; -typedef unsigned long long U64; - -#endif diff --git a/mqtt/include/utils.h b/mqtt/include/utils.h deleted file mode 100644 index 676501e..0000000 --- a/mqtt/include/utils.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _UTILS_H_ -#define _UTILS_H_ - -#include - -uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s); -uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip); -uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str); -#endif diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c index 49ed507..9d435c8 100644 --- a/mqtt/mqtt.c +++ b/mqtt/mqtt.c @@ -33,41 +33,38 @@ #define MQTT_TASK_PRIO 0 #define MQTT_TASK_QUEUE_SIZE 1 -#define MQTT_SEND_TIMOUT 5 +#define MQTT_SEND_TIMOUT 5 #ifndef QUEUE_BUFFER_SIZE #define QUEUE_BUFFER_SIZE 2048 #endif -unsigned char *default_certificate; +unsigned char* default_certificate; unsigned int default_certificate_len = 0; -unsigned char *default_private_key; +unsigned char* default_private_key; unsigned int default_private_key_len = 0; os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; LOCAL void ICACHE_FLASH_ATTR -mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) -{ - struct espconn *pConn = (struct espconn *)arg; +mqtt_dns_found(const char* name, ip_addr_t* ipaddr, void* arg) { + struct espconn* pConn = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pConn->reverse; - if (ipaddr == NULL) - { + if (ipaddr == NULL) { os_printf("DNS: Found, but got no ip, try to reconnect\n"); client->connState = TCP_RECONNECT_REQ; return; } os_printf("DNS: found ip %d.%d.%d.%d\n", - *((uint8 *)&ipaddr->addr), - *((uint8 *)&ipaddr->addr + 1), - *((uint8 *)&ipaddr->addr + 2), - *((uint8 *)&ipaddr->addr + 3)); + *((uint8 *)&ipaddr->addr), + *((uint8 *)&ipaddr->addr + 1), + *((uint8 *)&ipaddr->addr + 2), + *((uint8 *)&ipaddr->addr + 3)); - if (client->ip.addr == 0 && ipaddr->addr != 0) - { + if (client->ip.addr == 0 && ipaddr->addr != 0) { os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); #ifdef CLIENT_SSL_ENABLE if (client->security){ @@ -75,7 +72,7 @@ mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) } else #endif - espconn_connect(client->pCon); + espconn_connect(client->pCon); client->connState = TCP_CONNECTING; os_printf("MQTT-TCP: connecting...\n"); @@ -85,10 +82,8 @@ mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) } - LOCAL void ICACHE_FLASH_ATTR -deliver_publish(MQTT_Client* client, uint8_t* message, int length) -{ +deliver_publish(MQTT_Client* client, uint8_t* message, uint16_t length) { mqtt_event_data_t event_data; event_data.topic_length = length; @@ -99,8 +94,10 @@ deliver_publish(MQTT_Client* client, uint8_t* message, int length) if (client->dataCb) client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); -} + if (client->cmdDataCb) + client->cmdDataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); +} /** * @brief Client received callback function. @@ -110,26 +107,25 @@ deliver_publish(MQTT_Client* client, uint8_t* message, int length) * @retval None */ void ICACHE_FLASH_ATTR -mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) -{ +mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { uint8_t msg_type; uint8_t msg_qos; uint16_t msg_id; - struct espconn *pCon = (struct espconn*)arg; - MQTT_Client *client = (MQTT_Client *)pCon->reverse; + struct espconn* pCon = (struct espconn*)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; READPACKET: os_printf("MQTT-TCP: Data received %d bytes\n", len); - if (len < MQTT_BUF_SIZE && len > 0){ + if (len < MQTT_BUF_SIZE && len > 0) { os_memcpy(client->mqtt_state.in_buffer, pdata, len); msg_type = mqtt_get_type(client->mqtt_state.in_buffer); msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); if (client->connState == MQTT_CONNECT_SENDING) { - if (msg_type == MQTT_MSG_TYPE_CONNACK){ - if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT){ + if (msg_type == MQTT_MSG_TYPE_CONNACK) { + if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) { os_printf("MQTT: Invalid packet\n"); #ifdef CLIENT_SSL_ENABLE if (client->security){ @@ -137,13 +133,15 @@ READPACKET: } else #endif - espconn_disconnect(client->pCon); + espconn_disconnect(client->pCon); } else { os_printf("MQTT: Connected to %s:%ld\n", client->host, client->port); client->connState = MQTT_DATA; if (client->connectedCb) client->connectedCb((uint32_t*)client); + if (client->cmdConnectedCb) + client->cmdConnectedCb((uint32_t*)client); } } } @@ -154,50 +152,50 @@ READPACKET: if (msg_type == MQTT_MSG_TYPE_SUBACK) { if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) - os_printf("MQTT: Subscribe successful\n"); + os_printf("MQTT: Subscribe successful\n"); } - else if (msg_type == MQTT_MSG_TYPE_UNSUBACK){ + else if (msg_type == MQTT_MSG_TYPE_UNSUBACK) { if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) - os_printf("MQTT: UnSubscribe successful\n"); + os_printf("MQTT: UnSubscribe successful\n"); } else if (msg_type == MQTT_MSG_TYPE_PUBLISH) { if (msg_qos == 1) client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); else if (msg_qos == 2) client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); - if (msg_qos == 1 || msg_qos == 2){ + if (msg_qos == 1 || msg_qos == 2) { os_printf("MQTT: Queue response QoS: %d\n", msg_qos); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { os_printf("MQTT: Queue full\n"); } } deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); } else if (msg_type == MQTT_MSG_TYPE_PUBACK) { - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { os_printf("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\n"); } } else if (msg_type == MQTT_MSG_TYPE_PUBREC) { client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { os_printf("MQTT: Queue full\n"); } } else if (msg_type == MQTT_MSG_TYPE_PUBREL) { client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { os_printf("MQTT: Queue full\n"); } } else if (msg_type == MQTT_MSG_TYPE_PUBCOMP) { - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){ + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { os_printf("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\n"); } } else if (msg_type == MQTT_MSG_TYPE_PINGREQ) { client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { os_printf("MQTT: Queue full\n"); } } @@ -205,12 +203,10 @@ READPACKET: // NOTE: this is done down here and not in the switch case above // because the PSOCK_READBUF_LEN() won't work inside a switch // statement due to the way protothreads resume. - if (msg_type == MQTT_MSG_TYPE_PUBLISH) - { + if (msg_type == MQTT_MSG_TYPE_PUBLISH) { len = client->mqtt_state.message_length_read; - if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) - { + if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) { //client->connState = MQTT_PUBLISH_RECV; //Not Implement yet len -= client->mqtt_state.message_length; @@ -234,26 +230,27 @@ READPACKET: * @retval None */ void ICACHE_FLASH_ATTR -mqtt_tcpclient_sent_cb(void *arg) -{ - struct espconn *pCon = (struct espconn *)arg; +mqtt_tcpclient_sent_cb(void* arg) { + struct espconn* pCon = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pCon->reverse; os_printf("MQTT-TCP: Sent\n"); client->sendTimeout = 0; - if (client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH){ + if (client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) { if (client->publishedCb) client->publishedCb((uint32_t*)client); + if (client->cmdPublishedCb) + client->cmdPublishedCb((uint32_t*)client); } system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); } -void ICACHE_FLASH_ATTR mqtt_timer(void *arg) -{ +void ICACHE_FLASH_ATTR +mqtt_timer(void* arg) { MQTT_Client* client = (MQTT_Client*)arg; - if (client->connState == MQTT_DATA){ + if (client->connState == MQTT_DATA) { client->keepAliveTick++; - if (client->keepAliveTick > client->mqtt_state.connect_info->keepalive){ + if (client->keepAliveTick > client->mqtt_state.connect_info->keepalive) { os_printf("\nMQTT: Send keepalive packet to %s:%ld!\n", client->host, client->port); client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); @@ -270,7 +267,7 @@ void ICACHE_FLASH_ATTR mqtt_timer(void *arg) } else #endif - espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); client->mqtt_state.outbound_message = NULL; @@ -279,11 +276,15 @@ void ICACHE_FLASH_ATTR mqtt_timer(void *arg) } } - else if (client->connState == TCP_RECONNECT_REQ){ + else if (client->connState == TCP_RECONNECT_REQ) { client->reconnectTick++; if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { client->reconnectTick = 0; client->connState = TCP_RECONNECT; + if (client->tcpDisconnectedCb) + client->tcpDisconnectedCb((uint32_t*)client); + if (client->cmdTcpDisconnectedCb) + client->cmdTcpDisconnectedCb((uint32_t*)client); system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); } } @@ -292,15 +293,16 @@ void ICACHE_FLASH_ATTR mqtt_timer(void *arg) } void ICACHE_FLASH_ATTR -mqtt_tcpclient_discon_cb(void *arg) -{ +mqtt_tcpclient_discon_cb(void* arg) { - struct espconn *pespconn = (struct espconn *)arg; + struct espconn* pespconn = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pespconn->reverse; os_printf("MQTT-TCP: Disconnected callback\n"); client->connState = TCP_RECONNECT_REQ; if (client->disconnectedCb) client->disconnectedCb((uint32_t*)client); + if (client->cmdDisconnectedCb) + client->cmdDisconnectedCb((uint32_t*)client); system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); } @@ -311,9 +313,8 @@ mqtt_tcpclient_discon_cb(void *arg) * @retval None */ void ICACHE_FLASH_ATTR -mqtt_tcpclient_connect_cb(void *arg) -{ - struct espconn *pCon = (struct espconn *)arg; +mqtt_tcpclient_connect_cb(void* arg) { + struct espconn* pCon = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pCon->reverse; espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); @@ -335,7 +336,7 @@ mqtt_tcpclient_connect_cb(void *arg) } else #endif - espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); client->mqtt_state.outbound_message = NULL; client->connState = MQTT_CONNECT_SENDING; @@ -348,9 +349,8 @@ mqtt_tcpclient_connect_cb(void *arg) * @retval None */ void ICACHE_FLASH_ATTR -mqtt_tcpclient_recon_cb(void *arg, sint8 errType) -{ - struct espconn *pCon = (struct espconn *)arg; +mqtt_tcpclient_recon_cb(void* arg, int8_t errType) { + struct espconn* pCon = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pCon->reverse; os_printf("MQTT-TCP: Reconnect to %s:%ld\n", client->host, client->port); @@ -371,21 +371,20 @@ mqtt_tcpclient_recon_cb(void *arg, sint8 errType) * @retval TRUE if success queue */ bool ICACHE_FLASH_ATTR -MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int qos, int retain) -{ +MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain) { int data_length = os_strlen(data); uint8_t dataBuffer[MQTT_BUF_SIZE]; uint16_t dataLen; client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, - topic, data, data_length, - qos, retain, - &client->mqtt_state.pending_msg_id); - if (client->mqtt_state.outbound_message->length == 0){ + topic, data, data_length, + qos, retain, + &client->mqtt_state.pending_msg_id); + if (client->mqtt_state.outbound_message->length == 0) { os_printf("MQTT: Queuing Publish failed\n"); return FALSE; } os_printf("MQTT: Queuing Publish, length: %d, queue size(%ld/%ld)\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { os_printf("MQTT: Queue full\n"); if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { os_printf("MQTT: Serious buffer error\n"); @@ -404,16 +403,15 @@ MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int qos, * @retval TRUE if success queue */ bool ICACHE_FLASH_ATTR -MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) -{ +MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos) { uint8_t dataBuffer[MQTT_BUF_SIZE]; uint16_t dataLen; client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, - topic, 0, - &client->mqtt_state.pending_msg_id); + topic, 0, + &client->mqtt_state.pending_msg_id); os_printf("MQTT: Queue Subscribe, topic: \"%s\", id: %d\n", topic, client->mqtt_state.pending_msg_id); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){ + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { os_printf("MQTT: Queue full\n"); if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { os_printf("MQTT: Serious buffer error\n"); @@ -424,7 +422,8 @@ MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) return TRUE; } -void ICACHE_FLASH_ATTR MQTT_Task(os_event_t *e) { +void ICACHE_FLASH_ATTR +MQTT_Task(os_event_t* e) { MQTT_Client* client = (MQTT_Client*)e->par; uint8_t dataBuffer[MQTT_BUF_SIZE]; uint16_t dataLen; @@ -434,7 +433,7 @@ void ICACHE_FLASH_ATTR MQTT_Task(os_event_t *e) { if (client->connState == TCP_RECONNECT_REQ) { return; } - else if (client->connState == TCP_RECONNECT){ + else if (client->connState == TCP_RECONNECT) { MQTT_Connect(client); os_printf("MQTT-TCP: Reconnect to: %s:%ld\n", client->host, client->port); client->connState = TCP_CONNECTING; @@ -443,7 +442,7 @@ void ICACHE_FLASH_ATTR MQTT_Task(os_event_t *e) { if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) return; - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0){ + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) { client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); client->sendTimeout = MQTT_SEND_TIMOUT; @@ -454,7 +453,7 @@ void ICACHE_FLASH_ATTR MQTT_Task(os_event_t *e) { } else #endif - espconn_sent(client->pCon, dataBuffer, dataLen); + espconn_sent(client->pCon, dataBuffer, dataLen); client->mqtt_state.outbound_message = NULL; return; @@ -472,8 +471,7 @@ void ICACHE_FLASH_ATTR MQTT_Task(os_event_t *e) { * @retval None */ void ICACHE_FLASH_ATTR -MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security) -{ +MQTT_InitConnection(MQTT_Client* mqttClient, uint8_t* host, uint32 port, uint8_t security) { uint32_t temp; os_printf("MQTT_InitConnection\n"); os_memset(mqttClient, 0, sizeof(MQTT_Client)); @@ -495,8 +493,7 @@ MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t * @retval None */ void ICACHE_FLASH_ATTR -MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession) -{ +MQTT_InitClient(MQTT_Client* mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) { uint32_t temp; os_printf("MQTT_InitClient\n"); @@ -536,8 +533,7 @@ MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_use } void ICACHE_FLASH_ATTR -MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) -{ +MQTT_InitLWT(MQTT_Client* mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) { uint32_t temp; temp = os_strlen((char*)will_topic); mqttClient->connect_info.will_topic = (char*)os_zalloc(temp + 1); @@ -560,8 +556,7 @@ MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, ui * @retval None */ void ICACHE_FLASH_ATTR -MQTT_Connect(MQTT_Client *mqttClient) -{ +MQTT_Connect(MQTT_Client* mqttClient) { MQTT_Disconnect(mqttClient); mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); mqttClient->pCon->type = ESPCONN_TCP; @@ -589,7 +584,7 @@ MQTT_Connect(MQTT_Client *mqttClient) } else #endif - espconn_connect(mqttClient->pCon); + espconn_connect(mqttClient->pCon); } else { os_printf("MQTT-TCP: Connect to domain %s:%ld\n", mqttClient->host, mqttClient->port); @@ -599,12 +594,11 @@ MQTT_Connect(MQTT_Client *mqttClient) } void ICACHE_FLASH_ATTR -MQTT_Disconnect(MQTT_Client *mqttClient) -{ - if (mqttClient->pCon){ +MQTT_Disconnect(MQTT_Client* mqttClient) { + if (mqttClient->pCon) { os_printf("Free memory\n"); if (mqttClient->pCon->proto.tcp) - os_free(mqttClient->pCon->proto.tcp); + os_free(mqttClient->pCon->proto.tcp); os_free(mqttClient->pCon); mqttClient->pCon = NULL; } @@ -613,25 +607,27 @@ MQTT_Disconnect(MQTT_Client *mqttClient) } void ICACHE_FLASH_ATTR -MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) -{ +MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb) { mqttClient->connectedCb = connectedCb; } void ICACHE_FLASH_ATTR -MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) -{ +MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb) { mqttClient->disconnectedCb = disconnectedCb; } void ICACHE_FLASH_ATTR -MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) +MQTT_OnTcpDisconnected(MQTT_Client *mqttClient, MqttCallback tcpDisconnectedCb) { + mqttClient->tcpDisconnectedCb = tcpDisconnectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb) { mqttClient->dataCb = dataCb; } void ICACHE_FLASH_ATTR -MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) -{ +MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb) { mqttClient->publishedCb = publishedCb; } diff --git a/mqtt/include/mqtt.h b/mqtt/mqtt.h similarity index 60% rename from mqtt/include/mqtt.h rename to mqtt/mqtt.h index 52d21fe..d0fdfb5 100644 --- a/mqtt/include/mqtt.h +++ b/mqtt/mqtt.h @@ -35,8 +35,7 @@ #include "queue.h" #include "utils.h" -typedef struct mqtt_event_data_t -{ +typedef struct mqtt_event_data_t { uint8_t type; const char* topic; const char* data; @@ -45,8 +44,7 @@ typedef struct mqtt_event_data_t uint16_t data_offset; } mqtt_event_data_t; -typedef struct mqtt_state_t -{ +typedef struct mqtt_state_t { uint16_t port; int auto_reconnect; mqtt_connect_info_t* connect_info; @@ -64,48 +62,54 @@ typedef struct mqtt_state_t } mqtt_state_t; typedef enum { - WIFI_INIT, - WIFI_CONNECTING, - WIFI_CONNECTING_ERROR, - WIFI_CONNECTED, - DNS_RESOLVE, - TCP_DISCONNECTED, - TCP_RECONNECT_REQ, - TCP_RECONNECT, - TCP_CONNECTING, - TCP_CONNECTING_ERROR, - TCP_CONNECTED, - MQTT_CONNECT_SEND, - MQTT_CONNECT_SENDING, - MQTT_SUBSCIBE_SEND, - MQTT_SUBSCIBE_SENDING, - MQTT_DATA, - MQTT_PUBLISH_RECV, - MQTT_PUBLISHING + WIFI_INIT, + WIFI_CONNECTING, + WIFI_CONNECTING_ERROR, + WIFI_CONNECTED, + DNS_RESOLVE, + TCP_DISCONNECTED, + TCP_RECONNECT_REQ, + TCP_RECONNECT, + TCP_CONNECTING, + TCP_CONNECTING_ERROR, + TCP_CONNECTED, + MQTT_CONNECT_SEND, + MQTT_CONNECT_SENDING, + MQTT_SUBSCIBE_SEND, + MQTT_SUBSCIBE_SENDING, + MQTT_DATA, + MQTT_PUBLISH_RECV, + MQTT_PUBLISHING } tConnState; -typedef void (*MqttCallback)(uint32_t *args); -typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); +typedef void (*MqttCallback)(uint32_t* args); +typedef void (*MqttDataCallback)(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t lengh); -typedef struct { - struct espconn *pCon; - uint8_t security; - uint8_t* host; - uint32_t port; - ip_addr_t ip; - mqtt_state_t mqtt_state; - mqtt_connect_info_t connect_info; - MqttCallback connectedCb; - MqttCallback disconnectedCb; - MqttCallback publishedCb; - MqttDataCallback dataCb; - ETSTimer mqttTimer; - uint32_t keepAliveTick; - uint32_t reconnectTick; - uint32_t sendTimeout; - tConnState connState; - QUEUE msgQueue; - void* user_data; +typedef struct { + struct espconn* pCon; + uint32_t security; + uint8_t* host; + uint32_t port; + ip_addr_t ip; + mqtt_state_t mqtt_state; + mqtt_connect_info_t connect_info; + MqttCallback connectedCb; + MqttCallback cmdConnectedCb; + MqttCallback disconnectedCb; + MqttCallback cmdDisconnectedCb; + MqttCallback tcpDisconnectedCb; + MqttCallback cmdTcpDisconnectedCb; + MqttCallback publishedCb; + MqttCallback cmdPublishedCb; + MqttDataCallback dataCb; + MqttDataCallback cmdDataCb; + ETSTimer mqttTimer; + uint32_t keepAliveTick; + uint32_t reconnectTick; + uint32_t sendTimeout; + tConnState connState; + QUEUE msgQueue; + void* user_data; } MQTT_Client; #define SEC_NONSSL 0 @@ -125,16 +129,16 @@ typedef struct { #define MQTT_EVENT_TYPE_EXITED 7 #define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 -void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32 port, uint8_t security); -void ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession); -void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); -void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); -void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); -void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); -void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); -bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); -void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); -void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); -bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int qos, int retain); +void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client* mqttClient, uint8_t* host, uint32 port, uint8_t security); +void ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client* mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint8_t keepAliveTime, uint8_t cleanSession); +void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client* mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); +void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb); +void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb); +void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb); +void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb); +bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); +void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client* mqttClient); +void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client* mqttClient); +bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain); #endif /* USER_AT_MQTT_H_ */ diff --git a/mqtt/mqtt_msg.c b/mqtt/mqtt_msg.c index 77c777e..03da28e 100644 --- a/mqtt/mqtt_msg.c +++ b/mqtt/mqtt_msg.c @@ -32,8 +32,7 @@ #include "mqtt_msg.h" #define MQTT_MAX_FIXED_HEADER_SIZE 3 -enum mqtt_connect_flag -{ +enum mqtt_connect_flag { MQTT_CONNECT_FLAG_USERNAME = 1 << 7, MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, @@ -41,8 +40,8 @@ enum mqtt_connect_flag MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 }; -struct __attribute__((__packed__)) mqtt_connect_variable_header -{ +struct + __attribute__((__packed__)) mqtt_connect_variable_header { uint8_t lengthMsb; uint8_t lengthLsb; #if defined(PROTOCOL_NAMEv31) @@ -58,8 +57,8 @@ struct __attribute__((__packed__)) mqtt_connect_variable_header uint8_t keepaliveLsb; }; -static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) -{ +static int ICACHE_FLASH_ATTR +append_string(mqtt_connection_t* connection, const char* string, int len) { if (connection->message.length + len + 2 > connection->buffer_length) return -1; @@ -71,8 +70,8 @@ static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const return len + 2; } -static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) -{ +static uint16_t ICACHE_FLASH_ATTR +append_message_id(mqtt_connection_t* connection, uint16_t message_id) { // If message_id is zero then we should assign one, otherwise // we'll use the one supplied by the caller while (message_id == 0) @@ -87,33 +86,31 @@ static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connectio return message_id; } -static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) -{ +static int ICACHE_FLASH_ATTR +init_message(mqtt_connection_t* connection) { connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; return MQTT_MAX_FIXED_HEADER_SIZE; } -static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) -{ +static mqtt_message_t* ICACHE_FLASH_ATTR +fail_message(mqtt_connection_t* connection) { connection->message.data = connection->buffer; connection->message.length = 0; return &connection->message; } -static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) -{ +static mqtt_message_t* ICACHE_FLASH_ATTR +fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) { int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; - if (remaining_length > 127) - { + if (remaining_length > 127) { connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); connection->buffer[1] = 0x80 | (remaining_length % 128); connection->buffer[2] = remaining_length / 128; connection->message.length = remaining_length + 3; connection->message.data = connection->buffer; } - else - { + else { connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); connection->buffer[2] = remaining_length; connection->message.length = remaining_length + 2; @@ -131,16 +128,14 @@ mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_le connection->buffer_length = buffer_length; } -int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) -{ +int ICACHE_FLASH_ATTR +mqtt_get_total_length(uint8_t* buffer, uint16_t length) { int i; int totlen = 0; - for (i = 1; i < length; ++i) - { + for (i = 1; i < length; ++i) { totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); - if ((buffer[i] & 0x80) == 0) - { + if ((buffer[i] & 0x80) == 0) { ++i; break; } @@ -150,17 +145,15 @@ int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) return totlen; } -const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) -{ +const char* ICACHE_FLASH_ATTR +mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) { int i; int totlen = 0; int topiclen; - for (i = 1; i < *length; ++i) - { + for (i = 1; i < *length; ++i) { totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); - if ((buffer[i] & 0x80) == 0) - { + if ((buffer[i] & 0x80) == 0) { ++i; break; } @@ -179,17 +172,15 @@ const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* return (const char*)(buffer + i); } -const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) -{ +const char* ICACHE_FLASH_ATTR +mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) { int i; int totlen = 0; int topiclen; - for (i = 1; i < *length; ++i) - { + for (i = 1; i < *length; ++i) { totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); - if ((buffer[i] & 0x80) == 0) - { + if ((buffer[i] & 0x80) == 0) { ++i; break; } @@ -201,14 +192,13 @@ const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* l topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; - if (i + topiclen >= *length){ + if (i + topiclen >= *length) { *length = 0; return NULL; } i += topiclen; - if (mqtt_get_qos(buffer) > 0) - { + if (mqtt_get_qos(buffer) > 0) { if (i + 2 >= *length) return NULL; i += 2; @@ -224,71 +214,65 @@ const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* l return (const char*)(buffer + i); } -uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length) -{ +uint16_t ICACHE_FLASH_ATTR +mqtt_get_id(uint8_t* buffer, uint16_t length) { if (length < 1) return 0; - switch (mqtt_get_type(buffer)) - { - case MQTT_MSG_TYPE_PUBLISH: - { - int i; - int topiclen; - - for (i = 1; i < length; ++i) - { - if ((buffer[i] & 0x80) == 0) - { - ++i; - break; + switch (mqtt_get_type(buffer)) { + case MQTT_MSG_TYPE_PUBLISH: { + int i; + int topiclen; + + for (i = 1; i < length; ++i) { + if ((buffer[i] & 0x80) == 0) { + ++i; + break; + } } - } - if (i + 2 >= length) - return 0; - topiclen = buffer[i++] << 8; - topiclen |= buffer[i++]; + if (i + 2 >= length) + return 0; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; - if (i + topiclen >= length) - return 0; - i += topiclen; + if (i + topiclen >= length) + return 0; + i += topiclen; - if (mqtt_get_qos(buffer) > 0) - { - if (i + 2 >= length) + if (mqtt_get_qos(buffer) > 0) { + if (i + 2 >= length) + return 0; + //i += 2; + } + else { return 0; - //i += 2; + } + + return (buffer[i] << 8) | buffer[i + 1]; } - else { - return 0; + case MQTT_MSG_TYPE_PUBACK: + case MQTT_MSG_TYPE_PUBREC: + case MQTT_MSG_TYPE_PUBREL: + case MQTT_MSG_TYPE_PUBCOMP: + case MQTT_MSG_TYPE_SUBACK: + case MQTT_MSG_TYPE_UNSUBACK: + case MQTT_MSG_TYPE_SUBSCRIBE: { + // This requires the remaining length to be encoded in 1 byte, + // which it should be. + if (length >= 4 && (buffer[1] & 0x80) == 0) + return (buffer[2] << 8) | buffer[3]; + else + return 0; } - return (buffer[i] << 8) | buffer[i + 1]; - } - case MQTT_MSG_TYPE_PUBACK: - case MQTT_MSG_TYPE_PUBREC: - case MQTT_MSG_TYPE_PUBREL: - case MQTT_MSG_TYPE_PUBCOMP: - case MQTT_MSG_TYPE_SUBACK: - case MQTT_MSG_TYPE_UNSUBACK: - case MQTT_MSG_TYPE_SUBSCRIBE: - { - // This requires the remaining length to be encoded in 1 byte, - // which it should be. - if (length >= 4 && (buffer[1] & 0x80) == 0) - return (buffer[2] << 8) | buffer[3]; - else + default: return 0; } - - default: - return 0; - } } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) { struct mqtt_connect_variable_header* variable_header; init_message(connection); @@ -318,16 +302,14 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection if (info->clean_session) variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; - if (info->client_id != NULL && info->client_id[0] != '\0') - { + if (info->client_id != NULL && info->client_id[0] != '\0') { if (append_string(connection, info->client_id, strlen(info->client_id)) < 0) return fail_message(connection); } else return fail_message(connection); - if (info->will_topic != NULL && info->will_topic[0] != '\0') - { + if (info->will_topic != NULL && info->will_topic[0] != '\0') { if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) return fail_message(connection); @@ -340,16 +322,14 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection variable_header->flags |= (info->will_qos & 3) << 3; } - if (info->username != NULL && info->username[0] != '\0') - { + if (info->username != NULL && info->username[0] != '\0') { if (append_string(connection, info->username, strlen(info->username)) < 0) return fail_message(connection); variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; } - if (info->password != NULL && info->password[0] != '\0') - { + if (info->password != NULL && info->password[0] != '\0') { if (append_string(connection, info->password, strlen(info->password)) < 0) return fail_message(connection); @@ -359,8 +339,8 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) { init_message(connection); if (topic == NULL || topic[0] == '\0') @@ -369,8 +349,7 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection if (append_string(connection, topic, strlen(topic)) < 0) return fail_message(connection); - if (qos > 0) - { + if (qos > 0) { if ((*message_id = append_message_id(connection, 0)) == 0) return fail_message(connection); } @@ -385,40 +364,40 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) return fail_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) return fail_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) return fail_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) return fail_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) { init_message(connection); if (topic == NULL || topic[0] == '\0') @@ -437,8 +416,8 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connecti return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) { init_message(connection); if (topic == NULL || topic[0] == '\0') @@ -453,20 +432,20 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connec return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pingreq(mqtt_connection_t* connection) { init_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pingresp(mqtt_connection_t* connection) { init_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) -{ +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_disconnect(mqtt_connection_t* connection) { init_message(connection); return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); } diff --git a/mqtt/include/mqtt_msg.h b/mqtt/mqtt_msg.h similarity index 86% rename from mqtt/include/mqtt_msg.h rename to mqtt/mqtt_msg.h index f16bb80..ce840b7 100644 --- a/mqtt/include/mqtt_msg.h +++ b/mqtt/mqtt_msg.h @@ -1,18 +1,3 @@ -/* - * File: mqtt_msg.h - * Author: Minh Tuan - * - * Created on July 12, 2014, 1:05 PM - */ - -#ifndef MQTT_MSG_H -#define MQTT_MSG_H -#include - -#ifdef __cplusplus -extern "C" { -#endif - /* * Copyright (c) 2014, Stephen Robinson * All rights reserved. @@ -43,12 +28,12 @@ extern "C" { * POSSIBILITY OF SUCH DAMAGE. * */ -/* 7 6 5 4 3 2 1 0*/ -/* | --- Message Type ---- | DUP Flag | QoS Level | Retain | Remaining Length | */ +#ifndef MQTT_MSG_H +#define MQTT_MSG_H +#include -enum mqtt_message_type -{ +enum mqtt_message_type { MQTT_MSG_TYPE_CONNECT = 1, MQTT_MSG_TYPE_CONNACK = 2, MQTT_MSG_TYPE_PUBLISH = 3, @@ -65,15 +50,13 @@ enum mqtt_message_type MQTT_MSG_TYPE_DISCONNECT = 14 }; -typedef struct mqtt_message -{ +typedef struct mqtt_message { uint8_t* data; uint16_t length; } mqtt_message_t; -typedef struct mqtt_connection -{ +typedef struct mqtt_connection { mqtt_message_t message; uint16_t message_id; @@ -82,25 +65,35 @@ typedef struct mqtt_connection } mqtt_connection_t; -typedef struct mqtt_connect_info -{ +typedef struct mqtt_connect_info { char* client_id; char* username; char* password; char* will_topic; char* will_message; - int keepalive; - int will_qos; - int will_retain; - int clean_session; + uint32_t keepalive; + uint32_t will_qos; + uint32_t will_retain; + uint32_t clean_session; } mqtt_connect_info_t; -static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } -static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } -static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } -static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } +static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { + return (buffer[0] & 0xf0) >> 4; +} + +static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { + return (buffer[0] & 0x08) >> 3; +} + +static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { + return (buffer[0] & 0x06) >> 1; +} + +static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { + return (buffer[0] & 0x01); +} void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); @@ -120,10 +113,5 @@ mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); - -#ifdef __cplusplus -} -#endif - -#endif /* MQTT_MSG_H */ +#endif // MQTT_MSG_H diff --git a/mqtt/proto.c b/mqtt/proto.c index 3909b15..69b5367 100644 --- a/mqtt/proto.c +++ b/mqtt/proto.c @@ -1,128 +1,86 @@ #include "proto.h" -int8_t ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, uint8_t *buf, uint16_t bufSize) -{ - parser->buf = buf; - parser->bufSize = bufSize; - parser->dataLen = 0; - parser->callback = completeCallback; - parser->isEsc = 0; - return 0; +int8_t ICACHE_FLASH_ATTR +PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize) { + parser->buf = buf; + parser->bufSize = bufSize; + parser->dataLen = 0; + parser->callback = completeCallback; + parser->isEsc = 0; + return 0; } -int8_t ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, uint8_t value) -{ - switch(value){ - case 0x7D: - parser->isEsc = 1; - break; - - case 0x7E: - parser->dataLen = 0; - parser->isEsc = 0; - parser->isBegin = 1; - break; - - case 0x7F: - if (parser->callback != NULL) - parser->callback(); - parser->isBegin = 0; - return 0; - break; - - default: - if(parser->isBegin == 0) break; - - if(parser->isEsc){ - value ^= 0x20; - parser->isEsc = 0; - } - - if(parser->dataLen < parser->bufSize) - parser->buf[parser->dataLen++] = value; - - break; - } - return -1; -} +int8_t ICACHE_FLASH_ATTR +PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value) { + switch (value) { + case 0x7D: + parser->isEsc = 1; + break; -int8_t ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, uint8_t *buf, uint16_t len) -{ - while(len--) - PROTO_ParseByte(parser, *buf++); - return 0; -} -int16_t ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, uint8_t *bufOut, uint16_t* len, uint16_t maxBufLen) -{ - uint8_t c; + case 0x7E: + parser->dataLen = 0; + parser->isEsc = 0; + parser->isBegin = 1; + break; - PROTO_PARSER proto; - PROTO_Init(&proto, NULL, bufOut, maxBufLen); - while(RINGBUF_Get(rb, &c) == 0){ - if(PROTO_ParseByte(&proto, c) == 0){ - *len = proto.dataLen; - return 0; - } - } - return -1; -} -int16_t ICACHE_FLASH_ATTR PROTO_Add(uint8_t *buf, const uint8_t *packet, int16_t bufSize) -{ - uint16_t i = 2; - uint16_t len = *(uint16_t*) packet; + case 0x7F: + if (parser->callback != NULL) + parser->callback(); + parser->isBegin = 0; + return 0; + break; - if (bufSize < 1) return -1; + default: + if (parser->isBegin == 0) break; - *buf++ = 0x7E; - bufSize--; + if (parser->isEsc) { + value ^= 0x20; + parser->isEsc = 0; + } - while (len--) { - switch (*packet) { - case 0x7D: - case 0x7E: - case 0x7F: - if (bufSize < 2) return -1; - *buf++ = 0x7D; - *buf++ = *packet++ ^ 0x20; - i += 2; - bufSize -= 2; - break; - default: - if (bufSize < 1) return -1; - *buf++ = *packet++; - i++; - bufSize--; - break; - } - } + if (parser->dataLen < parser->bufSize) + parser->buf[parser->dataLen++] = value; - if (bufSize < 1) return -1; - *buf++ = 0x7F; + break; + } + return -1; +} + +int16_t ICACHE_FLASH_ATTR +PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen) { + uint8_t c; - return i; + PROTO_PARSER proto; + PROTO_Init(&proto, NULL, bufOut, maxBufLen); + while (RINGBUF_Get(rb, &c) == 0) { + if (PROTO_ParseByte(&proto, c) == 0) { + *len = proto.dataLen; + return 0; + } + } + return -1; } -int16_t ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const uint8_t *packet, int16_t len) -{ - uint16_t i = 2; - if(RINGBUF_Put(rb, 0x7E) == -1) return -1; - while (len--) { - switch (*packet) { - case 0x7D: - case 0x7E: - case 0x7F: - if(RINGBUF_Put(rb, 0x7D) == -1) return -1; - if(RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; - i += 2; - break; - default: - if(RINGBUF_Put(rb, *packet++) == -1) return -1; - i++; - break; - } +int16_t ICACHE_FLASH_ATTR +PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len) { + uint16_t i = 2; + if (RINGBUF_Put(rb, 0x7E) == -1) return -1; + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if (RINGBUF_Put(rb, 0x7D) == -1) return -1; + if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; + i += 2; + break; + default: + if (RINGBUF_Put(rb, *packet++) == -1) return -1; + i++; + break; } - if(RINGBUF_Put(rb, 0x7F) == -1) return -1; + } + if (RINGBUF_Put(rb, 0x7F) == -1) return -1; - return i; + return i; } - diff --git a/mqtt/proto.h b/mqtt/proto.h new file mode 100644 index 0000000..8d20832 --- /dev/null +++ b/mqtt/proto.h @@ -0,0 +1,28 @@ +/* + * File: proto.h + * Author: ThuHien + * + * Created on November 23, 2012, 8:57 AM + */ + +#ifndef _PROTO_H_ +#define _PROTO_H_ +#include +#include "ringbuf.h" + +typedef void (PROTO_PARSE_CALLBACK)(); + +typedef struct { + uint8_t* buf; + uint16_t bufSize; + uint16_t dataLen; + uint8_t isEsc; + uint8_t isBegin; + PROTO_PARSE_CALLBACK* callback; +} PROTO_PARSER; + +int8_t ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize); +int16_t ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len); +int8_t ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value); +int16_t ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen); +#endif diff --git a/mqtt/queue.c b/mqtt/queue.c index 147015e..39c4790 100644 --- a/mqtt/queue.c +++ b/mqtt/queue.c @@ -29,22 +29,25 @@ */ #include "queue.h" -void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize) { +void ICACHE_FLASH_ATTR +QUEUE_Init(QUEUE* queue, int bufferSize) { queue->buf = (uint8_t*)os_zalloc(bufferSize); RINGBUF_Init(&queue->rb, queue->buf, bufferSize); } -int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) { +int32_t ICACHE_FLASH_ATTR +QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len) { return PROTO_AddRb(&queue->rb, buffer, len); } -int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) { - +int32_t ICACHE_FLASH_ATTR +QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) { return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); } -bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue) { +bool ICACHE_FLASH_ATTR +QUEUE_IsEmpty(QUEUE* queue) { if (queue->rb.fill_cnt <= 0) return TRUE; return FALSE; -} \ No newline at end of file +} diff --git a/mqtt/include/queue.h b/mqtt/queue.h similarity index 86% rename from mqtt/include/queue.h rename to mqtt/queue.h index 7a84480..78b7882 100644 --- a/mqtt/include/queue.h +++ b/mqtt/queue.h @@ -33,13 +33,14 @@ #include #include "proto.h" #include "ringbuf.h" + typedef struct { - uint8_t *buf; - RINGBUF rb; + uint8_t* buf; + RINGBUF rb; } QUEUE; -void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize); -int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); -int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); -bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue); +void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE* queue, int bufferSize); +int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len); +int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); +bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE* queue); #endif /* USER_QUEUE_H_ */ diff --git a/mqtt/ringbuf.c b/mqtt/ringbuf.c index 5ac3d07..a0cc782 100644 --- a/mqtt/ringbuf.c +++ b/mqtt/ringbuf.c @@ -1,11 +1,5 @@ -/** -* \file -* Ring Buffer library -*/ - #include "ringbuf.h" - /** * \brief init a RINGBUF object * \param r pointer to a RINGBUF object @@ -13,55 +7,57 @@ * \param size size of buf * \return 0 if successfull, otherwise failed */ -I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size) -{ - if(r == NULL || buf == NULL || size < 2) return -1; - - r->p_o = r->p_r = r->p_w = buf; - r->fill_cnt = 0; - r->size = size; - - return 0; +int16_t ICACHE_FLASH_ATTR +RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size) { + if (r == NULL || buf == NULL || size < 2) return -1; + + r->p_o = r->p_r = r->p_w = buf; + r->fill_cnt = 0; + r->size = size; + + return 0; } + /** * \brief put a character into ring buffer * \param r pointer to a ringbuf object * \param c character to be put * \return 0 if successfull, otherwise failed */ -I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c) -{ - if(r->fill_cnt>=r->size)return -1; // ring buffer is full, this should be atomic operation - - - r->fill_cnt++; // increase filled slots count, this should be atomic operation - - - *r->p_w++ = c; // put character into buffer - - if(r->p_w >= r->p_o + r->size) // rollback if write pointer go pass - r->p_w = r->p_o; // the physical boundary - - return 0; +int16_t ICACHE_FLASH_ATTR +RINGBUF_Put(RINGBUF* r, uint8_t c) { + if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation + + + r->fill_cnt++; // increase filled slots count, this should be atomic operation + + + *r->p_w++ = c; // put character into buffer + + if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass + r->p_w = r->p_o; // the physical boundary + + return 0; } + /** * \brief get a character from ring buffer * \param r pointer to a ringbuf object * \param c read character * \return 0 if successfull, otherwise failed */ -I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c) -{ - if(r->fill_cnt<=0)return -1; // ring buffer is empty, this should be atomic operation - - - r->fill_cnt--; // decrease filled slots count - - - *c = *r->p_r++; // get the character out - - if(r->p_r >= r->p_o + r->size) // rollback if write pointer go pass - r->p_r = r->p_o; // the physical boundary - - return 0; +int16_t ICACHE_FLASH_ATTR +RINGBUF_Get(RINGBUF* r, uint8_t* c) { + if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation + + + r->fill_cnt--; // decrease filled slots count + + + *c = *r->p_r++; // get the character out + + if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass + r->p_r = r->p_o; // the physical boundary + + return 0; } diff --git a/mqtt/ringbuf.h b/mqtt/ringbuf.h new file mode 100644 index 0000000..7d9f8e9 --- /dev/null +++ b/mqtt/ringbuf.h @@ -0,0 +1,17 @@ +#ifndef _RING_BUF_H_ +#define _RING_BUF_H_ + +#include + +typedef struct { + uint8_t* p_o; /**< Original pointer */ + uint8_t* volatile p_r; /**< Read pointer */ + uint8_t* volatile p_w; /**< Write pointer */ + volatile int32_t fill_cnt; /**< Number of filled slots */ + int32_t size; /**< Buffer size */ +} RINGBUF; + +int16_t ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size); +int16_t ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF* r, uint8_t c); +int16_t ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF* r, uint8_t* c); +#endif diff --git a/mqtt/utils.c b/mqtt/utils.c index 173b573..86aff20 100644 --- a/mqtt/utils.c +++ b/mqtt/utils.c @@ -33,11 +33,10 @@ #include "utils.h" uint8_t ICACHE_FLASH_ATTR -UTILS_IsIPV4(int8_t *str) -{ - uint8_t segs = 0; /* Segment count. */ - uint8_t chcnt = 0; /* Character count within segment. */ - uint8_t accum = 0; /* Accumulator for segment. */ +UTILS_IsIPV4(int8_t* str) { + uint8_t segs = 0; /* Segment count. */ + uint8_t chcnt = 0; /* Character count within segment. */ + uint8_t accum = 0; /* Accumulator for segment. */ /* Catch NULL pointer. */ if (str == 0) return 0; @@ -85,8 +84,7 @@ UTILS_IsIPV4(int8_t *str) } uint8_t ICACHE_FLASH_ATTR -UTILS_StrToIP(const int8_t* str, void *ip) -{ +UTILS_StrToIP(const int8_t* str, void* ip) { /* The count of the number of bytes processed. */ int i; @@ -122,12 +120,11 @@ UTILS_StrToIP(const int8_t* str, void *ip) } uint32_t ICACHE_FLASH_ATTR -UTILS_Atoh(const int8_t *s) -{ +UTILS_Atoh(const int8_t* s) { uint32_t value = 0, digit; int8_t c; - while ((c = *s++)){ + while ((c = *s++)) { if ('0' <= c && c <= '9') digit = c - '0'; else if ('A' <= c && c <= 'F') @@ -141,4 +138,3 @@ UTILS_Atoh(const int8_t *s) return value; } - diff --git a/mqtt/utils.h b/mqtt/utils.h new file mode 100644 index 0000000..bc4d2af --- /dev/null +++ b/mqtt/utils.h @@ -0,0 +1,9 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include + +uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t* s); +uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void* ip); +uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4(int8_t* str); +#endif diff --git a/user/user_main.c b/user/user_main.c index cf6d266..db5db0f 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -1,13 +1,13 @@ /* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * Jeroen Domburg wrote this file. As long as you retain - * this notice you can do whatever you want with this stuff. If we meet some day, - * and you think this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - * Heavily modified and enhanced by Thorsten von Eicken in 2015 - * ---------------------------------------------------------------------------- - */ +* ---------------------------------------------------------------------------- +* "THE BEER-WARE LICENSE" (Revision 42): +* Jeroen Domburg wrote this file. As long as you retain +* this notice you can do whatever you want with this stuff. If we meet some day, +* and you think this stuff is worth it, you can buy me a beer in return. +* ---------------------------------------------------------------------------- +* Heavily modified and enhanced by Thorsten von Eicken in 2015 +* ---------------------------------------------------------------------------- +*/ #include @@ -35,19 +35,20 @@ //This is disabled in the default build; if you want to try it, enable the authBasic line in //the builtInUrls below. int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { - if (no==0) { - os_strcpy(user, "admin"); - os_strcpy(pass, "s3cr3t"); - return 1; -//Add more users this way. Check against incrementing no for each user added. -// } else if (no==1) { -// os_strcpy(user, "user1"); -// os_strcpy(pass, "something"); -// return 1; - } - return 0; + if (no == 0) { + os_strcpy(user, "admin"); + os_strcpy(pass, "s3cr3t"); + return 1; + //Add more users this way. Check against incrementing no for each user added. + // } else if (no==1) { + // os_strcpy(user, "user1"); + // os_strcpy(pass, "something"); + // return 1; + } + return 0; } + /* This is the main url->function dispatching data struct. In short, it's a struct with various URLs plus their handlers. The handlers can @@ -58,115 +59,110 @@ handled top-down, so make sure to put more specific rules above the more general ones. Authorization things (like authBasic) act as a 'barrier' and should be placed above the URLs they protect. */ -HttpdBuiltInUrl builtInUrls[]={ - {"/", cgiRedirect, "/home.html"}, - {"/menu", cgiMenu, NULL}, - {"/flash/next", cgiGetFirmwareNext, NULL}, - {"/flash/upload", cgiUploadFirmware, NULL}, - {"/flash/reboot", cgiRebootFirmware, NULL}, - //{"/home.html", cgiEspFsHtml, NULL}, - //{"/log.html", cgiEspFsHtml, NULL}, - {"/log/text", ajaxLog, NULL}, - {"/log/dbg", ajaxLogDbg, NULL}, - //{"/console.html", cgiEspFsHtml, NULL}, - {"/console/reset", ajaxConsoleReset, NULL}, - {"/console/baud", ajaxConsoleBaud, NULL}, - {"/console/text", ajaxConsole, NULL}, - - //Routines to make the /wifi URL and everything beneath it work. - -//Enable the line below to protect the WiFi configuration with an username/password combo. -// {"/wifi/*", authBasic, myPassFn}, - - {"/wifi", cgiRedirect, "/wifi/wifi.html"}, - {"/wifi/", cgiRedirect, "/wifi/wifi.html"}, - //{"/wifi/wifi.html", cgiEspFsHtml, NULL}, - {"/wifi/info", cgiWifiInfo, NULL}, - {"/wifi/scan", cgiWiFiScan, NULL}, - {"/wifi/connect", cgiWiFiConnect, NULL}, - {"/wifi/connstatus", cgiWiFiConnStatus, NULL}, - {"/wifi/setmode", cgiWiFiSetMode, NULL}, - {"/wifi/special", cgiWiFiSpecial, NULL}, - {"/pins", cgiPins, NULL}, - {"/tcpclient", cgiTcp, NULL}, - - {"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem - {NULL, NULL, NULL} +HttpdBuiltInUrl builtInUrls[] = { + { "/", cgiRedirect, "/home.html" }, + { "/menu", cgiMenu, NULL }, + { "/flash/next", cgiGetFirmwareNext, NULL }, + { "/flash/upload", cgiUploadFirmware, NULL }, + { "/flash/reboot", cgiRebootFirmware, NULL }, + //{"/home.html", cgiEspFsHtml, NULL}, + //{"/log.html", cgiEspFsHtml, NULL}, + { "/log/text", ajaxLog, NULL }, + { "/log/dbg", ajaxLogDbg, NULL }, + //{"/console.html", cgiEspFsHtml, NULL}, + { "/console/reset", ajaxConsoleReset, NULL }, + { "/console/baud", ajaxConsoleBaud, NULL }, + { "/console/text", ajaxConsole, NULL }, + + //Routines to make the /wifi URL and everything beneath it work. + + //Enable the line below to protect the WiFi configuration with an username/password combo. + // {"/wifi/*", authBasic, myPassFn}, + + { "/wifi", cgiRedirect, "/wifi/wifi.html" }, + { "/wifi/", cgiRedirect, "/wifi/wifi.html" }, + //{"/wifi/wifi.html", cgiEspFsHtml, NULL}, + { "/wifi/info", cgiWifiInfo, NULL }, + { "/wifi/scan", cgiWiFiScan, NULL }, + { "/wifi/connect", cgiWiFiConnect, NULL }, + { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, + { "/wifi/setmode", cgiWiFiSetMode, NULL }, + { "/wifi/special", cgiWiFiSpecial, NULL }, + { "/pins", cgiPins, NULL }, + { "/tcpclient", cgiTcp, NULL }, + + { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem + { NULL, NULL, NULL } }; + //#define SHOW_HEAP_USE #ifdef SHOW_HEAP_USE static ETSTimer prHeapTimer; static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { - os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); + os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); } #endif +void user_rf_pre_init(void) { +} + +// address of espfs binary blob +extern uint32_t _binary_espfs_img_start; + +static char *rst_codes[] = { + "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", +}; + # define VERS_STR_STR(V) #V # define VERS_STR(V) VERS_STR_STR(V) char *esp_link_version = VERS_STR(VERSION); -void user_rf_pre_init(void) { +//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. +void user_init(void) { // get the flash config so we know how to init things - //configWipe(); // uncomment to reset the config for testing purposes + //configWipe(); // uncomment to reset the config for testing purposes bool restoreOk = configRestore(); - // init gpio pin registers gpio_init(); - // init UART uart_init(flashConfig.baud_rate, 115200); logInit(); // must come after init of uart - // say hello (leave some time to cause break in TX after boot loader's msg os_delay_us(10000L); os_printf("\n\n** %s\n", esp_link_version); os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); - // Status LEDs statusInit(); serledInit(); - + // Wifi + wifiInit(); + // init the flash filesystem with the html stuff + espFsInit(&_binary_espfs_img_start); + //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); + //os_printf("espFsInit %s\n", res?"ERR":"ok"); + // mount the http handlers + httpdInit(builtInUrls, 80); + // init the wifi-serial transparent bridge (port 23) + serbridgeInit(23); + uart_add_recv_cb(&serbridgeUartCb); #ifdef SHOW_HEAP_USE os_timer_disarm(&prHeapTimer); os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); os_timer_arm(&prHeapTimer, 10000, 1); #endif -} -// address of espfs binary blob -extern uint32_t _binary_espfs_img_start; + struct rst_info *rst_info = system_get_rst_info(); + os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); + 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()); -static char *rst_codes[] = { - "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", -}; + os_printf("** esp-link ready\n"); -//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. -void user_init(void) { - // Wifi - wifiInit(); - - // init the flash filesystem with the html stuff - espFsInit(&_binary_espfs_img_start); - - //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); - //os_printf("espFsInit %s\n", res?"ERR":"ok"); - - // mount the http handlers - httpdInit(builtInUrls, 80); - - // init the wifi-serial transparent bridge (port 23) - serbridgeInit(23); - uart_add_recv_cb(&serbridgeUartCb); - - struct rst_info *rst_info = system_get_rst_info(); - os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); - 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"); -} + // call user_main init +// init(); +} \ No newline at end of file From a2bb08186fd2095788fbc64a5aac6ccd65c352d9 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 29 Aug 2015 11:40:08 -0500 Subject: [PATCH 057/104] Setup user code area and moved esp-link to it's own module --- Makefile | 2 +- esp-link.vcxproj | 1 + {user => esp-link}/cgi.c | 0 {user => esp-link}/cgi.h | 0 {user => esp-link}/cgiflash.c | 0 {user => esp-link}/cgiflash.h | 0 {user => esp-link}/cgipins.c | 0 {user => esp-link}/cgipins.h | 0 {user => esp-link}/cgitcp.c | 0 {user => esp-link}/cgitcp.h | 0 {user => esp-link}/cgiwifi.c | 0 {user => esp-link}/cgiwifi.h | 0 {user => esp-link}/config.c | 0 {user => esp-link}/config.h | 0 {user => esp-link}/log.c | 0 {user => esp-link}/log.h | 0 esp-link/main.c | 168 ++++++++++++++++++++++++++++++++++ {user => esp-link}/status.c | 0 {user => esp-link}/status.h | 0 include/esp8266.h | 2 +- include/user_config.h | 1 + user/user_main.c | 167 +-------------------------------- 22 files changed, 174 insertions(+), 167 deletions(-) rename {user => esp-link}/cgi.c (100%) rename {user => esp-link}/cgi.h (100%) rename {user => esp-link}/cgiflash.c (100%) rename {user => esp-link}/cgiflash.h (100%) rename {user => esp-link}/cgipins.c (100%) rename {user => esp-link}/cgipins.h (100%) rename {user => esp-link}/cgitcp.c (100%) rename {user => esp-link}/cgitcp.h (100%) rename {user => esp-link}/cgiwifi.c (100%) rename {user => esp-link}/cgiwifi.h (100%) rename {user => esp-link}/config.c (100%) rename {user => esp-link}/config.h (100%) rename {user => esp-link}/log.c (100%) rename {user => esp-link}/log.h (100%) create mode 100644 esp-link/main.c rename {user => esp-link}/status.c (100%) rename {user => esp-link}/status.h (100%) diff --git a/Makefile b/Makefile index 7969417..070a1f5 100644 --- a/Makefile +++ b/Makefile @@ -142,7 +142,7 @@ TARGET = httpd APPGEN_TOOL ?= gen_appbin.py # which modules (subdirectories) of the project to include in compiling -MODULES = espfs httpd user serial cmd mqtt +MODULES = espfs httpd user serial cmd mqtt esp-link EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK diff --git a/esp-link.vcxproj b/esp-link.vcxproj index ce508d0..760a8ef 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -105,6 +105,7 @@ + diff --git a/user/cgi.c b/esp-link/cgi.c similarity index 100% rename from user/cgi.c rename to esp-link/cgi.c diff --git a/user/cgi.h b/esp-link/cgi.h similarity index 100% rename from user/cgi.h rename to esp-link/cgi.h diff --git a/user/cgiflash.c b/esp-link/cgiflash.c similarity index 100% rename from user/cgiflash.c rename to esp-link/cgiflash.c diff --git a/user/cgiflash.h b/esp-link/cgiflash.h similarity index 100% rename from user/cgiflash.h rename to esp-link/cgiflash.h diff --git a/user/cgipins.c b/esp-link/cgipins.c similarity index 100% rename from user/cgipins.c rename to esp-link/cgipins.c diff --git a/user/cgipins.h b/esp-link/cgipins.h similarity index 100% rename from user/cgipins.h rename to esp-link/cgipins.h diff --git a/user/cgitcp.c b/esp-link/cgitcp.c similarity index 100% rename from user/cgitcp.c rename to esp-link/cgitcp.c diff --git a/user/cgitcp.h b/esp-link/cgitcp.h similarity index 100% rename from user/cgitcp.h rename to esp-link/cgitcp.h diff --git a/user/cgiwifi.c b/esp-link/cgiwifi.c similarity index 100% rename from user/cgiwifi.c rename to esp-link/cgiwifi.c diff --git a/user/cgiwifi.h b/esp-link/cgiwifi.h similarity index 100% rename from user/cgiwifi.h rename to esp-link/cgiwifi.h diff --git a/user/config.c b/esp-link/config.c similarity index 100% rename from user/config.c rename to esp-link/config.c diff --git a/user/config.h b/esp-link/config.h similarity index 100% rename from user/config.h rename to esp-link/config.h diff --git a/user/log.c b/esp-link/log.c similarity index 100% rename from user/log.c rename to esp-link/log.c diff --git a/user/log.h b/esp-link/log.h similarity index 100% rename from user/log.h rename to esp-link/log.h diff --git a/esp-link/main.c b/esp-link/main.c new file mode 100644 index 0000000..417a1b2 --- /dev/null +++ b/esp-link/main.c @@ -0,0 +1,168 @@ +/* +* ---------------------------------------------------------------------------- +* "THE BEER-WARE LICENSE" (Revision 42): +* Jeroen Domburg wrote this file. As long as you retain +* this notice you can do whatever you want with this stuff. If we meet some day, +* and you think this stuff is worth it, you can buy me a beer in return. +* ---------------------------------------------------------------------------- +* Heavily modified and enhanced by Thorsten von Eicken in 2015 +* ---------------------------------------------------------------------------- +*/ + + +#include +#include "httpd.h" +#include "httpdespfs.h" +#include "cgi.h" +#include "cgiwifi.h" +#include "cgipins.h" +#include "cgitcp.h" +#include "cgiflash.h" +#include "auth.h" +#include "espfs.h" +#include "uart.h" +#include "serbridge.h" +#include "status.h" +#include "serled.h" +#include "console.h" +#include "config.h" +#include "log.h" +#include + +//#define SHOW_HEAP_USE + +//Function that tells the authentication system what users/passwords live on the system. +//This is disabled in the default build; if you want to try it, enable the authBasic line in +//the builtInUrls below. +int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { + if (no == 0) { + os_strcpy(user, "admin"); + os_strcpy(pass, "s3cr3t"); + return 1; + //Add more users this way. Check against incrementing no for each user added. + // } else if (no==1) { + // os_strcpy(user, "user1"); + // os_strcpy(pass, "something"); + // return 1; + } + return 0; +} + + +/* +This is the main url->function dispatching data struct. +In short, it's a struct with various URLs plus their handlers. The handlers can +be 'standard' CGI functions you wrote, or 'special' CGIs requiring an argument. +They can also be auth-functions. An asterisk will match any url starting with +everything before the asterisks; "*" matches everything. The list will be +handled top-down, so make sure to put more specific rules above the more +general ones. Authorization things (like authBasic) act as a 'barrier' and +should be placed above the URLs they protect. +*/ +HttpdBuiltInUrl builtInUrls[] = { + { "/", cgiRedirect, "/home.html" }, + { "/menu", cgiMenu, NULL }, + { "/flash/next", cgiGetFirmwareNext, NULL }, + { "/flash/upload", cgiUploadFirmware, NULL }, + { "/flash/reboot", cgiRebootFirmware, NULL }, + //{"/home.html", cgiEspFsHtml, NULL}, + //{"/log.html", cgiEspFsHtml, NULL}, + { "/log/text", ajaxLog, NULL }, + { "/log/dbg", ajaxLogDbg, NULL }, + //{"/console.html", cgiEspFsHtml, NULL}, + { "/console/reset", ajaxConsoleReset, NULL }, + { "/console/baud", ajaxConsoleBaud, NULL }, + { "/console/text", ajaxConsole, NULL }, + + //Routines to make the /wifi URL and everything beneath it work. + + //Enable the line below to protect the WiFi configuration with an username/password combo. + // {"/wifi/*", authBasic, myPassFn}, + + { "/wifi", cgiRedirect, "/wifi/wifi.html" }, + { "/wifi/", cgiRedirect, "/wifi/wifi.html" }, + //{"/wifi/wifi.html", cgiEspFsHtml, NULL}, + { "/wifi/info", cgiWifiInfo, NULL }, + { "/wifi/scan", cgiWiFiScan, NULL }, + { "/wifi/connect", cgiWiFiConnect, NULL }, + { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, + { "/wifi/setmode", cgiWiFiSetMode, NULL }, + { "/wifi/special", cgiWiFiSpecial, NULL }, + { "/pins", cgiPins, NULL }, + { "/tcpclient", cgiTcp, NULL }, + + { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem + { NULL, NULL, NULL } +}; + + +//#define SHOW_HEAP_USE + +#ifdef SHOW_HEAP_USE +static ETSTimer prHeapTimer; + +static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { + os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); +} +#endif + +void user_rf_pre_init(void) { +} + +// address of espfs binary blob +extern uint32_t _binary_espfs_img_start; + +static char *rst_codes[] = { + "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", +}; + +# define VERS_STR_STR(V) #V +# define VERS_STR(V) VERS_STR_STR(V) +char *esp_link_version = VERS_STR(VERSION); + +//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. +void user_init(void) { + // get the flash config so we know how to init things + //configWipe(); // uncomment to reset the config for testing purposes + bool restoreOk = configRestore(); + // init gpio pin registers + gpio_init(); + // init UART + uart_init(flashConfig.baud_rate, 115200); + logInit(); // must come after init of uart + // say hello (leave some time to cause break in TX after boot loader's msg + os_delay_us(10000L); + os_printf("\n\n** %s\n", esp_link_version); + os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); + // Status LEDs + statusInit(); + serledInit(); + // Wifi + wifiInit(); + // init the flash filesystem with the html stuff + espFsInit(&_binary_espfs_img_start); + //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); + //os_printf("espFsInit %s\n", res?"ERR":"ok"); + // mount the http handlers + httpdInit(builtInUrls, 80); + // init the wifi-serial transparent bridge (port 23) + serbridgeInit(23); + uart_add_recv_cb(&serbridgeUartCb); +#ifdef SHOW_HEAP_USE + os_timer_disarm(&prHeapTimer); + os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); + os_timer_arm(&prHeapTimer, 10000, 1); +#endif + + struct rst_info *rst_info = system_get_rst_info(); + os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); + 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"); + + // call user_main init + init(); +} \ No newline at end of file diff --git a/user/status.c b/esp-link/status.c similarity index 100% rename from user/status.c rename to esp-link/status.c diff --git a/user/status.h b/esp-link/status.h similarity index 100% rename from user/status.h rename to esp-link/status.h diff --git a/include/esp8266.h b/include/esp8266.h index 535dfeb..d1f5cc4 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -17,7 +17,7 @@ #include "espmissingincludes.h" #include "uart_hw.h" -//void init(void); +void ICACHE_FLASH_ATTR init(void); #ifdef __WIN32__ #include <_mingw.h> diff --git a/include/user_config.h b/include/user_config.h index d38c637..16f7f9c 100644 --- a/include/user_config.h +++ b/include/user_config.h @@ -36,4 +36,5 @@ #define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ //PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ + #endif \ No newline at end of file diff --git a/user/user_main.c b/user/user_main.c index db5db0f..31e62a1 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -1,168 +1,5 @@ -/* -* ---------------------------------------------------------------------------- -* "THE BEER-WARE LICENSE" (Revision 42): -* Jeroen Domburg wrote this file. As long as you retain -* this notice you can do whatever you want with this stuff. If we meet some day, -* and you think this stuff is worth it, you can buy me a beer in return. -* ---------------------------------------------------------------------------- -* Heavily modified and enhanced by Thorsten von Eicken in 2015 -* ---------------------------------------------------------------------------- -*/ - - #include -#include "httpd.h" -#include "httpdespfs.h" -#include "cgi.h" -#include "cgiwifi.h" -#include "cgipins.h" -#include "cgitcp.h" -#include "cgiflash.h" -#include "auth.h" -#include "espfs.h" -#include "uart.h" -#include "serbridge.h" -#include "status.h" -#include "serled.h" -#include "console.h" -#include "config.h" -#include "log.h" -#include - -//#define SHOW_HEAP_USE - -//Function that tells the authentication system what users/passwords live on the system. -//This is disabled in the default build; if you want to try it, enable the authBasic line in -//the builtInUrls below. -int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { - if (no == 0) { - os_strcpy(user, "admin"); - os_strcpy(pass, "s3cr3t"); - return 1; - //Add more users this way. Check against incrementing no for each user added. - // } else if (no==1) { - // os_strcpy(user, "user1"); - // os_strcpy(pass, "something"); - // return 1; - } - return 0; -} - - -/* -This is the main url->function dispatching data struct. -In short, it's a struct with various URLs plus their handlers. The handlers can -be 'standard' CGI functions you wrote, or 'special' CGIs requiring an argument. -They can also be auth-functions. An asterisk will match any url starting with -everything before the asterisks; "*" matches everything. The list will be -handled top-down, so make sure to put more specific rules above the more -general ones. Authorization things (like authBasic) act as a 'barrier' and -should be placed above the URLs they protect. -*/ -HttpdBuiltInUrl builtInUrls[] = { - { "/", cgiRedirect, "/home.html" }, - { "/menu", cgiMenu, NULL }, - { "/flash/next", cgiGetFirmwareNext, NULL }, - { "/flash/upload", cgiUploadFirmware, NULL }, - { "/flash/reboot", cgiRebootFirmware, NULL }, - //{"/home.html", cgiEspFsHtml, NULL}, - //{"/log.html", cgiEspFsHtml, NULL}, - { "/log/text", ajaxLog, NULL }, - { "/log/dbg", ajaxLogDbg, NULL }, - //{"/console.html", cgiEspFsHtml, NULL}, - { "/console/reset", ajaxConsoleReset, NULL }, - { "/console/baud", ajaxConsoleBaud, NULL }, - { "/console/text", ajaxConsole, NULL }, - - //Routines to make the /wifi URL and everything beneath it work. - - //Enable the line below to protect the WiFi configuration with an username/password combo. - // {"/wifi/*", authBasic, myPassFn}, - - { "/wifi", cgiRedirect, "/wifi/wifi.html" }, - { "/wifi/", cgiRedirect, "/wifi/wifi.html" }, - //{"/wifi/wifi.html", cgiEspFsHtml, NULL}, - { "/wifi/info", cgiWifiInfo, NULL }, - { "/wifi/scan", cgiWiFiScan, NULL }, - { "/wifi/connect", cgiWiFiConnect, NULL }, - { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, - { "/wifi/setmode", cgiWiFiSetMode, NULL }, - { "/wifi/special", cgiWiFiSpecial, NULL }, - { "/pins", cgiPins, NULL }, - { "/tcpclient", cgiTcp, NULL }, - - { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem - { NULL, NULL, NULL } -}; - - -//#define SHOW_HEAP_USE - -#ifdef SHOW_HEAP_USE -static ETSTimer prHeapTimer; - -static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { - os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); -} -#endif - -void user_rf_pre_init(void) { -} - -// address of espfs binary blob -extern uint32_t _binary_espfs_img_start; - -static char *rst_codes[] = { - "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", -}; - -# define VERS_STR_STR(V) #V -# define VERS_STR(V) VERS_STR_STR(V) -char *esp_link_version = VERS_STR(VERSION); - -//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. -void user_init(void) { - // get the flash config so we know how to init things - //configWipe(); // uncomment to reset the config for testing purposes - bool restoreOk = configRestore(); - // init gpio pin registers - gpio_init(); - // init UART - uart_init(flashConfig.baud_rate, 115200); - logInit(); // must come after init of uart - // say hello (leave some time to cause break in TX after boot loader's msg - os_delay_us(10000L); - os_printf("\n\n** %s\n", esp_link_version); - os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); - // Status LEDs - statusInit(); - serledInit(); - // Wifi - wifiInit(); - // init the flash filesystem with the html stuff - espFsInit(&_binary_espfs_img_start); - //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); - //os_printf("espFsInit %s\n", res?"ERR":"ok"); - // mount the http handlers - httpdInit(builtInUrls, 80); - // init the wifi-serial transparent bridge (port 23) - serbridgeInit(23); - uart_add_recv_cb(&serbridgeUartCb); -#ifdef SHOW_HEAP_USE - os_timer_disarm(&prHeapTimer); - os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); - os_timer_arm(&prHeapTimer, 10000, 1); -#endif - - struct rst_info *rst_info = system_get_rst_info(); - os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); - 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"); - // call user_main init -// init(); +void init() { + } \ No newline at end of file From 4a77c56b308a92471f44ac06363199d7ecf3d456 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 29 Aug 2015 09:40:37 -0700 Subject: [PATCH 058/104] improve make release --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 08f0dcd..a949184 100644 --- a/Makefile +++ b/Makefile @@ -90,9 +90,10 @@ LED_SERIAL_PIN ?= 14 # on the release tag, make release, upload esp-link.tgz into the release files #VERSION ?= "esp-link custom version" DATE := $(shell date '+%F %T') -BRANCH := $(shell git describe --tags) +BRANCH := $(shell if git diff --quiet HEAD; then git describe --tags; \ + else git symbolic-ref --short HEAD; fi) SHA := $(shell if git diff --quiet HEAD; then git rev-parse --short HEAD | cut -d"/" -f 3; \ - else echo "development"; fi) + else echo "development"; fi) VERSION ?=esp-link $(BRANCH) - $(DATE) - $(SHA) # --------------- esp-link config options --------------- @@ -351,9 +352,11 @@ espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/ release: all $(Q) rm -rf release; mkdir -p release/esp-link + $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user1.bin | cut -b 1-80 + $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80 $(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) tar zcf esp-link-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link $(Q) rm -rf release clean: From f05bb652bb445d5d87f851ae7cfaa97fa97a7ed9 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 29 Aug 2015 10:08:44 -0700 Subject: [PATCH 059/104] improve make release #2 --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a949184..407ff05 100644 --- a/Makefile +++ b/Makefile @@ -351,12 +351,13 @@ espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/ $(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)" release: all - $(Q) rm -rf release; mkdir -p release/esp-link + $(Q) rm -rf release; mkdir -p release/esp-link-$(BRANCH) $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user1.bin | cut -b 1-80 $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80 $(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-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link + "$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link-$(BRANCH) + $(Q) tar zcf esp-link-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link-$(BRANCH) + $(Q) echo "Release file: esp-link-$(BRANCH)-$(FLASH_SIZE).tgz" $(Q) rm -rf release clean: From e440289628ffff5857f64c1f80bde8260f656d0f Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 29 Aug 2015 12:09:53 -0700 Subject: [PATCH 060/104] fix flash target in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 407ff05..66bcbe9 100644 --- a/Makefile +++ b/Makefile @@ -286,7 +286,7 @@ baseflash: all $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin flash: all - $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) -fs $(ET_FS) -ff $(ET_FF) write_flash \ + $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \ 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \ $(ET_BLANK) $(SDK_BASE)/bin/blank.bin From 1c5679f9c89eab3a19193014fb94bcd3ad933a38 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 29 Aug 2015 13:04:24 -0700 Subject: [PATCH 061/104] fix REST repsonse issues --- cmd/cmd.h | 18 +++++---- cmd/handlers.c | 79 ++++++++++++++++++++-------------------- cmd/rest.c | 99 +++++++++++++++++++++++++++----------------------- serial/slip.c | 5 +++ 4 files changed, 110 insertions(+), 91 deletions(-) diff --git a/cmd/cmd.h b/cmd/cmd.h index b489108..97c8236 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -55,7 +55,7 @@ typedef enum { CMD_REST_REQUEST, CMD_REST_SETHEADER, CMD_REST_EVENTS, - CMD_ADD_SENSOR, // 15 + CMD_ADD_CALLBACK, // 15 CMD_SENSOR_EVENTS } CmdName; @@ -71,19 +71,23 @@ typedef struct { uint32_t callback; } cmdCallback; -void ICACHE_FLASH_ATTR CMD_parse_packet(uint8_t *buf, short len); -cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name); +// Used by slip protocol to cause parsing of a received packet +void CMD_parse_packet(uint8_t *buf, short len); + +// Return the info about a callback to the attached uC by name, these are callbacks that the +// attached uC registers using the ADD_SENSOR command +cmdCallback* CMD_GetCbByName(char* name); // Responses // 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 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 ICACHE_FLASH_ATTR CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len); +uint16_t CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len); // Ends a response -void ICACHE_FLASH_ATTR CMD_ResponseEnd(uint16_t crc); +void CMD_ResponseEnd(uint16_t crc); -//void ICACHE_FLASH_ATTR CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]); +//void CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]); // Requests diff --git a/cmd/handlers.c b/cmd/handlers.c index 28382dd..8b37a18 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -10,17 +10,19 @@ #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); -static uint32_t ICACHE_FLASH_ATTR CMD_AddSensor(CmdPacket *cmd); +static uint32_t CMD_Null(CmdPacket *cmd); +static uint32_t CMD_IsReady(CmdPacket *cmd); +static uint32_t CMD_Reset(CmdPacket *cmd); +static uint32_t CMD_WifiConnect(CmdPacket *cmd); +static uint32_t CMD_AddCallback(CmdPacket *cmd); +// keep track of last status sent to uC so we can notify it when it changes static uint8_t lastWifiStatus = wifiIsDisconnected; // Command dispatch table for serial -> ESP commands const CmdList commands[] = { {CMD_NULL, CMD_Null}, - {CMD_RESET, CMD_Null}, + {CMD_RESET, CMD_Reset}, {CMD_IS_READY, CMD_IsReady}, {CMD_WIFI_CONNECT, CMD_WifiConnect}, @@ -37,25 +39,14 @@ const CmdList commands[] = { {CMD_REST_REQUEST, REST_Request}, {CMD_REST_SETHEADER, REST_SetHeader}, - {CMD_ADD_SENSOR, CMD_AddSensor }, + {CMD_ADD_CALLBACK, CMD_AddCallback }, {CMD_NULL, NULL} }; // WifiCb plus 10 for sensors -cmdCallback callbacks[12] = { - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 }, - { { '\0' }, -1 } -}; +#define MAX_CALLBACKS 12 +cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset // Command handler for IsReady (healthcheck) command static uint32_t ICACHE_FLASH_ATTR @@ -71,20 +62,31 @@ CMD_Null(CmdPacket *cmd) { return 1; } -static void ICACHE_FLASH_ATTR +// Command handler for Reset command, this was originally to reset the ESP but we don't want to +// do that is esp-link. It is still good to clear any information the ESP has about the attached +// uC. +static uint32_t ICACHE_FLASH_ATTR +CMD_Reset(CmdPacket *cmd) { + os_printf("CMD_Reset\n"); + // clear callbacks table + os_memset(callbacks, 0, sizeof(callbacks)); + return 1; +} + +static uint32_t ICACHE_FLASH_ATTR CMD_AddCb(char* name, uint32_t cb) { - char checkname[16]; - os_strncpy(checkname, name, sizeof(checkname)); - for (uint8_t i = 0; i < sizeof(commands); i++) { - os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); + for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { + //os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, + // (void *)callbacks[i].callback); // find existing callback or add to the end - if (os_strcmp(callbacks[i].name, checkname) == 0 || callbacks[i].name[0] == '\0') { - os_strncpy(callbacks[i].name, checkname, sizeof(checkname)); + if (os_strcmp(callbacks[i].name, name) == 0 || callbacks[i].name[0] == '\0') { + os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name)); callbacks[i].callback = cb; os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i); - break; + return 1; } } + return 0; } cmdCallback* ICACHE_FLASH_ATTR @@ -92,7 +94,8 @@ CMD_GetCbByName(char* name) { char checkname[16]; os_strncpy(checkname, name, sizeof(checkname)); for (uint8_t i = 0; i < sizeof(commands); i++) { - os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); + //os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, + // (void *)callbacks[i].callback); // if callback doesn't exist or it's null if (os_strcmp(callbacks[i].name, checkname) == 0) { os_printf("CMD_GetCbByName: cb %s found at index %d\n", callbacks[i].name, i); @@ -130,33 +133,31 @@ CMD_WifiConnect(CmdPacket *cmd) { wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback - lastWifiStatus = wifiIsDisconnected; + lastWifiStatus = 0xff; // set to invalid value so we immediately send status cb in all cases CMD_WifiCb(wifiState); return 1; } -// Command handler for Wifi connect command +// Command handler to add a callback to the named-callbacks list, this is for a callback to the uC static uint32_t ICACHE_FLASH_ATTR -CMD_AddSensor(CmdPacket *cmd) { +CMD_AddCallback(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); - os_printf("CMD_AddSensor: setup argc=%ld\n", CMD_GetArgc(&req)); + os_printf("CMD_AddCallback: setup argc=%ld\n", CMD_GetArgc(&req)); if (cmd->argc != 1 || cmd->callback == 0) return 0; - uint8_t* name; + char name[16]; uint16_t len; // get the sensor name len = CMD_ArgLen(&req); - os_printf("CMD_AddSensor: name len=%d\n", len); + os_printf("CMD_AddCallback: name len=%d\n", len); if (len > 15) return 0; // max size of name is 15 characters - name = (uint8_t*)os_zalloc(len + 1); - if (CMD_PopArg(&req, name, len)) return 0; + if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0; name[len] = 0; - os_printf("CMD_AddSensor: name=%s\n", name); + os_printf("CMD_AddCallback: name=%s\n", name); - CMD_AddCb((char*)name, (uint32_t)cmd->callback); // save the sensor callback - return 1; + return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback } diff --git a/cmd/rest.c b/cmd/rest.c index 75b5b0b..5bc3da7 100644 --- a/cmd/rest.c +++ b/cmd/rest.c @@ -27,59 +27,66 @@ tcpclient_discon_cb(void *arg) { client->data = 0; } +// Receive HTTP response - this hacky function assumes that the full response is received in +// one go. Sigh... 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; + // parse header, all this does is look for the end of the header + bool currentLineIsBlank = false; + while (pi < len) { + if (pdata[pi] == '\n') { + if (currentLineIsBlank) { + // body is starting + pi++; + break; } + currentLineIsBlank = true; + } else if (pdata[pi] != '\r') { + currentLineIsBlank = false; } + pi++; } + //if (pi < len && pdata[pi] == '\r') pi++; // hacky! + + // collect body and send it + uint16_t crc; + int body_len = len-pi; + os_printf("REST: status=%ld, body=%d\n", code, body_len); + if (pi == len) { + crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0); + } else { + crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1); + crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+pi), body_len); + CMD_ResponseEnd(crc); + os_printf("REST: body="); + for (int j=pi; jsecurity) // espconn_secure_disconnect(client->pCon); //else @@ -338,7 +345,8 @@ REST_Request(CmdPacket *cmd) { // we need to allocate memory for the header plus the body. First we count the length of the // header (including some extra counted "%s" and then we add the body length. We allocate the // whole shebang and copy everything into it. - char *headerFmt = "%s %s HTTP/1.1\r\n" + // BTW, use http/1.0 to avoid responses with transfer-encoding: chunked + char *headerFmt = "%s %s HTTP/1.0\r\n" "Host: %s\r\n" "%s" "Content-Length: %d\r\n" @@ -360,11 +368,12 @@ REST_Request(CmdPacket *cmd) { CMD_PopArg(&req, client->data + client->data_len, realLen); client->data_len += realLen; } + os_printf("\n"); + os_printf("REST: pCon state=%d\n", client->pCon->state); client->pCon->state = ESPCONN_NONE; espconn_regist_connectcb(client->pCon, tcpclient_connect_cb); espconn_regist_reconcb(client->pCon, tcpclient_recon_cb); - os_printf("\n"); 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); diff --git a/serial/slip.c b/serial/slip.c index 0c76103..0e067a1 100644 --- a/serial/slip.c +++ b/serial/slip.c @@ -70,6 +70,7 @@ slip_printable(char c) { static void ICACHE_FLASH_ATTR slip_reset() { + //os_printf("SLIP: reset\n"); slip_inpkt = false; slip_escaped = false; slip_len = 0; @@ -83,6 +84,7 @@ slip_parse_char(char c) { if (slip_len > 0) console_process(slip_buf, slip_len); slip_reset(); slip_inpkt = true; + os_printf("SLIP: start\n"); return; } } else if (slip_escaped) { @@ -101,6 +103,9 @@ slip_parse_char(char c) { return; case SLIP_START: os_printf("SLIP: got SLIP_START while in packet?\n"); + //os_printf("SLIP: rcv %d:", slip_len); + //for (int i=0; i Date: Sat, 29 Aug 2015 13:38:07 -0700 Subject: [PATCH 062/104] update readme for v2.0.beta2 --- README.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e7898ae..34d3090 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,24 @@ It implements a number of features: - 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 +- outbound REST HTTP requests from the attached micro-controller to the internet, protocol + based on espduino and compatible with [tuanpmt/espduino](https://github.com/tuanpmt/espduino) The firmware includes a tiny HTTP server based on [esphttpd](http://www.esp8266.com/viewforum.php?f=34) with a simple web interface, many thanks to Jeroen Domburg for making it available! +Many thanks to https://github.com/brunnels for contributions around the espduino functionality. -[![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) +###[Releases](https://github.com/jeelabs/esp-link/releases) + +- [V2.0.beta2](https://github.com/jeelabs/esp-link/releases/tag/v2.0.beta2) has REST support but + requires a 1MByte or 4MByte ESP8266 flash, e.g. esp-12 or wroom-02 +- [V1.0.1](https://github.com/jeelabs/esp-link/releases/tag/v1.0.1) is _stable_ + and has the web server, transparent bridge, flash-programming support, but lacks + the REST and upcoming MQTT support. V1 works with 512KB flash, e.g. esp-1, esp-3, ... -###[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. +For quick support and questions: +[![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) Eye Candy --------- @@ -31,7 +39,7 @@ attached microcontroller, and the pin assignments card: Hardware info ------------- This firmware is designed for esp8266 modules which have most ESP I/O pins available and -512KB flash. +at least 1MB flash. (The V1 firmware supports modules with 512KB flash). The default connections are: - URXD: connect to TX of microcontroller - UTXD: connect to RX of microcontroller @@ -42,6 +50,9 @@ The default connections are: If you are using an FTDI connector, GPIO12 goes to DTR and GPIO13 goes to CTS. +If you are using an esp-12 module, you can avoid the initial boot message from the esp8266 +bootloader by using the swap-pins option. This swaps the esp8266 TX/RX to gpio15/gpio13 respectively. + The GPIO pin assignments can be changed dynamically in the web UI and are saved in flash. Initial flashing From 36303a2b67dd6fe78519b54719c8f015717593c6 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 29 Aug 2015 15:48:51 -0500 Subject: [PATCH 063/104] finished mqtt --- cmd/handlers.c | 6 +- cmd/mqtt_cmd.c | 30 ++++----- cmd/mqtt_cmd.h | 12 ++-- cmd/rest.c | 2 - cmd/rest.h | 1 + esp-link.vcxproj | 4 +- esp-link/cgiwifi.h | 4 +- esp-link/main.c | 2 +- httpd/httpdespfs.c | 11 ++-- httpd/httpdespfs.h | 6 +- include/esp8266.h | 1 + include/user_config.h | 42 ++++--------- mqtt/mqtt.c | 32 +++++----- mqtt/mqtt.h | 28 ++++----- mqtt/mqtt_msg.h | 40 ++++++------ mqtt/proto.h | 15 ++--- mqtt/queue.h | 8 +-- mqtt/ringbuf.h | 6 +- mqtt/utils.c | 140 ------------------------------------------ mqtt/utils.h | 9 --- user/user_main.c | 67 +++++++++++++++++++- 21 files changed, 181 insertions(+), 285 deletions(-) delete mode 100644 mqtt/utils.c delete mode 100644 mqtt/utils.h diff --git a/cmd/handlers.c b/cmd/handlers.c index ae21bec..d5359cb 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -17,6 +17,7 @@ static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd); static uint32_t ICACHE_FLASH_ATTR CMD_AddSensor(CmdPacket *cmd); static uint8_t lastWifiStatus = wifiIsDisconnected; +static bool wifiCbAdded = false; // Command dispatch table for serial -> ESP commands const CmdList commands[] = { @@ -127,7 +128,10 @@ CMD_WifiConnect(CmdPacket *cmd) { if(cmd->argc != 2 || cmd->callback == 0) return 0; - wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem + if (!wifiCbAdded) { + wifiAddStateChangeCb(CMD_WifiCb); // register our callback with wifi subsystem + wifiCbAdded = true; + } CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback lastWifiStatus = wifiIsDisconnected; CMD_WifiCb(wifiState); diff --git a/cmd/mqtt_cmd.c b/cmd/mqtt_cmd.c index d12ba21..358ad48 100644 --- a/cmd/mqtt_cmd.c +++ b/cmd/mqtt_cmd.c @@ -1,9 +1,9 @@ #include "mqtt_cmd.h" -uint32_t connectedCb = 0, disconnectCb = 0, publishedCb = 0, dataCb = 0; +uint32_t connectedCb = 0, disconnectCb = 0, tcpDisconnectedCb = 0, publishedCb = 0, dataCb = 0; void ICACHE_FLASH_ATTR -mqttConnectedCb(uint32_t* args) { +cmdMqttConnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; os_printf("MQTT: Connected connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n", @@ -16,7 +16,7 @@ mqttConnectedCb(uint32_t* args) { } void ICACHE_FLASH_ATTR -mqttTcpDisconnectedCb(uint32_t *args) { +cmdMqttTcpDisconnectedCb(uint32_t *args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb *cb = (MqttCmdCb*)client->user_data; os_printf("MQTT: TCP Disconnected\n"); @@ -25,7 +25,7 @@ mqttTcpDisconnectedCb(uint32_t *args) { } void ICACHE_FLASH_ATTR -mqttDisconnectedCb(uint32_t* args) { +cmdMqttDisconnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; os_printf("MQTT: Disconnected\n"); @@ -34,7 +34,7 @@ mqttDisconnectedCb(uint32_t* args) { } void ICACHE_FLASH_ATTR -mqttPublishedCb(uint32_t* args) { +cmdMqttPublishedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; os_printf("MQTT: Published\n"); @@ -43,7 +43,7 @@ mqttPublishedCb(uint32_t* args) { } void ICACHE_FLASH_ATTR -mqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) { +cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) { uint16_t crc = 0; MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; @@ -104,7 +104,7 @@ MQTTCMD_Setup(CmdPacket *cmd) { // init client // TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again? - MQTT_InitClient(client, client_id, user_data, pass_data, keepalive, clean_session); + MQTT_InitClient(client, (char*)client_id, (char*)user_data, (char*)pass_data, keepalive, clean_session); // create callback MqttCmdCb* callback = (MqttCmdCb*)os_zalloc(sizeof(MqttCmdCb)); @@ -120,15 +120,15 @@ MQTTCMD_Setup(CmdPacket *cmd) { client->user_data = callback; - client->cmdConnectedCb = mqttConnectedCb; - client->cmdDisconnectedCb = mqttDisconnectedCb; - client->cmdPublishedCb = mqttPublishedCb; - client->cmdDataCb = mqttDataCb; + client->cmdConnectedCb = cmdMqttConnectedCb; + client->cmdDisconnectedCb = cmdMqttDisconnectedCb; + client->cmdPublishedCb = cmdMqttPublishedCb; + client->cmdDataCb = cmdMqttDataCb; if (CMD_GetArgc(&req) == 10) { CMD_PopArg(&req, (uint8_t*)&cb_data, 4); callback->tcpDisconnectedCb = cb_data; - client->cmdTcpDisconnectedCb = mqttTcpDisconnectedCb; + client->cmdTcpDisconnectedCb = cmdMqttTcpDisconnectedCb; } os_free(client_id); @@ -178,7 +178,7 @@ MQTTCMD_Lwt(CmdPacket *cmd) { // get retain CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); - os_printf("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%ld, retain=%ld\n", + os_printf("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%d, retain=%d\n", client->connect_info.will_topic, client->connect_info.will_message, client->connect_info.will_qos, @@ -207,7 +207,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { os_free(client->host); len = CMD_ArgLen(&req); if (len > 128) return 0; // safety check - client->host = (uint8_t*)os_zalloc(len + 1); + client->host = (char*)os_zalloc(len + 1); CMD_PopArg(&req, client->host, len); client->host[len] = 0; @@ -217,7 +217,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { // get security CMD_PopArg(&req, (uint8_t*)&client->security, 4); - os_printf("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%ld\n", + os_printf("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%d\n", client->host, client->port, client->security); diff --git a/cmd/mqtt_cmd.h b/cmd/mqtt_cmd.h index 66fd8f6..9ccbd08 100644 --- a/cmd/mqtt_cmd.h +++ b/cmd/mqtt_cmd.h @@ -12,11 +12,11 @@ typedef struct { uint32_t tcpDisconnectedCb; } MqttCmdCb; -uint32_t ICACHE_FLASH_ATTR MQTTCMD_Connect(CmdPacket *cmd); -uint32_t ICACHE_FLASH_ATTR MQTTCMD_Disconnect(CmdPacket *cmd); -uint32_t ICACHE_FLASH_ATTR MQTTCMD_Setup(CmdPacket *cmd); -uint32_t ICACHE_FLASH_ATTR MQTTCMD_Publish(CmdPacket *cmd); -uint32_t ICACHE_FLASH_ATTR MQTTCMD_Subscribe(CmdPacket *cmd); -uint32_t ICACHE_FLASH_ATTR MQTTCMD_Lwt(CmdPacket *cmd); +uint32_t MQTTCMD_Connect(CmdPacket *cmd); +uint32_t MQTTCMD_Disconnect(CmdPacket *cmd); +uint32_t MQTTCMD_Setup(CmdPacket *cmd); +uint32_t MQTTCMD_Publish(CmdPacket *cmd); +uint32_t MQTTCMD_Subscribe(CmdPacket *cmd); +uint32_t MQTTCMD_Lwt(CmdPacket *cmd); #endif /* MODULES_MQTT_CMD_H_ */ diff --git a/cmd/rest.c b/cmd/rest.c index 75b5b0b..3ae6575 100644 --- a/cmd/rest.c +++ b/cmd/rest.c @@ -16,8 +16,6 @@ static RestClient restClient[MAX_REST]; static uint8_t restNum = 0xff; // index into restClient for next slot to allocate #define REST_CB 0xbeef0000 // fudge added to callback for arduino so we can detect problems -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; diff --git a/cmd/rest.h b/cmd/rest.h index 7161e1f..9c6cd5c 100644 --- a/cmd/rest.h +++ b/cmd/rest.h @@ -36,5 +36,6 @@ typedef struct { uint32_t REST_Setup(CmdPacket *cmd); uint32_t REST_Request(CmdPacket *cmd); uint32_t REST_SetHeader(CmdPacket *cmd); +uint8_t UTILS_StrToIP(const char* str, void *ip); #endif /* MODULES_INCLUDE_API_H_ */ diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 760a8ef..f371f1a 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\mqtt;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include + .\esp-link;.\mqtt;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include @@ -81,7 +81,6 @@ - @@ -128,7 +127,6 @@ - diff --git a/esp-link/cgiwifi.h b/esp-link/cgiwifi.h index d73628e..3f7fe09 100644 --- a/esp-link/cgiwifi.h +++ b/esp-link/cgiwifi.h @@ -4,6 +4,7 @@ #include "httpd.h" enum { wifiIsDisconnected, wifiIsConnected, wifiGotIP }; +typedef void(*WifiStateChangeCb)(uint8_t wifiStatus); int cgiWiFiScan(HttpdConnData *connData); int cgiWifiInfo(HttpdConnData *connData); @@ -13,8 +14,9 @@ int cgiWiFiSetMode(HttpdConnData *connData); int cgiWiFiConnStatus(HttpdConnData *connData); int cgiWiFiSpecial(HttpdConnData *connData); void wifiInit(void); +void wifiAddStateChangeCb(WifiStateChangeCb cb); extern uint8_t wifiState; -extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes +//extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes #endif diff --git a/esp-link/main.c b/esp-link/main.c index 417a1b2..5febf87 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -118,7 +118,7 @@ static char *rst_codes[] = { # define VERS_STR_STR(V) #V # define VERS_STR(V) VERS_STR_STR(V) -char *esp_link_version = VERS_STR(VERSION); +char* esp_link_version = VERS_STR(VERSION); //Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. void user_init(void) { diff --git a/httpd/httpdespfs.c b/httpd/httpdespfs.c index b6853aa..c6f2c0c 100644 --- a/httpd/httpdespfs.c +++ b/httpd/httpdespfs.c @@ -12,12 +12,7 @@ Connector to let httpd use the espfs filesystem to serve the files in it. * Modified and enhanced by Thorsten von Eicken in 2015 * ---------------------------------------------------------------------------- */ - -#include #include "httpdespfs.h" -#include "espfs.h" -#include "espfsformat.h" -#include "cgi.h" // The static files marked with FLAG_GZIP are compressed and will be served with GZIP compression. // If the client does not advertise that he accepts GZIP send following warning message (telnet users for e.g.) @@ -27,7 +22,8 @@ static const char *gzipNonSupportedMessage = "HTTP/1.0 501 Not implemented\r\nSe //This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding //path in the filesystem and if it exists, passes the file through. This simulates what a normal //webserver would do with static files. -int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) { +int ICACHE_FLASH_ATTR +cgiEspFsHook(HttpdConnData *connData) { EspFsFile *file=connData->cgiData; int len; char buff[1024]; @@ -93,7 +89,8 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) { #if 0 //cgiEspFsHtml is a simple HTML file that gets prefixed by head.tpl -int ICACHE_FLASH_ATTR cgiEspFsHtml(HttpdConnData *connData) { +int ICACHE_FLASH_ATTR +cgiEspFsHtml(HttpdConnData *connData) { EspFsFile *file = connData->cgiData; char buff[2048]; diff --git a/httpd/httpdespfs.h b/httpd/httpdespfs.h index fb07008..847a8b6 100644 --- a/httpd/httpdespfs.h +++ b/httpd/httpdespfs.h @@ -1,10 +1,14 @@ #ifndef HTTPDESPFS_H #define HTTPDESPFS_H +#include +#include "espfs.h" +#include "espfsformat.h" +#include "cgi.h" #include "httpd.h" int cgiEspFsHook(HttpdConnData *connData); -int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData); +//int cgiEspFsTemplate(HttpdConnData *connData); //int ICACHE_FLASH_ATTR cgiEspFsHtml(HttpdConnData *connData); #endif diff --git a/include/esp8266.h b/include/esp8266.h index d1f5cc4..c3d0c40 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -16,6 +16,7 @@ #include "espmissingincludes.h" #include "uart_hw.h" +extern char* esp_link_version; void ICACHE_FLASH_ATTR init(void); diff --git a/include/user_config.h b/include/user_config.h index 16f7f9c..7bdfd38 100644 --- a/include/user_config.h +++ b/include/user_config.h @@ -1,40 +1,20 @@ #ifndef _USER_CONFIG_H_ #define _USER_CONFIG_H_ -/*DEFAULT CONFIGURATIONS*/ - -#define MQTT_HOST "mqtt.yourdomain.com" //or "mqtt.yourdomain.com" -#define MQTT_PORT 1883 +#define MQTT_RECONNECT_TIMEOUT 5 // seconds #define MQTT_BUF_SIZE 1024 -#define MQTT_KEEPALIVE 120 /*second*/ - -#define MQTT_CLIENT_ID "H_%08X" //Cuidar para não colocar valores execendentes da ESTRUTURA SYSCFG -#define MQTT_USER "DVES_USER" -#define MQTT_PASS "DVES_PASS" - -#define STA_SSID "TESTE" -#define STA_PASS "54545" -#define STA_TYPE AUTH_WPA2_PSK -#define MQTT_RECONNECT_TIMEOUT 5 /*second*/ - -#define DEFAULT_SECURITY 0 -#define QUEUE_BUFFER_SIZE 2048 - -//#undef MCU_RESET_PIN -//#undef MCU_ISP_PIN -//#undef LED_CONN_PIN -//#undef LED_SERIAL_PIN -// -//#define MCU_RESET_PIN 2 -//#define MCU_ISP_PIN -1 -//#define LED_CONN_PIN -1 -//#define LED_SERIAL_PIN -1 +#define MQTT_HOST "10.0.0.220" // "mqtt.yourdomain.com" or ip "10.0.0.1" +#define MQTT_PORT 1883 +#define MQTT_SECURITY 0 -//#define BAUD_RATE 9600 -//#define HOSTNAME "nodemcu\0 " +#define MQTT_CLIENT_ID "esp-link" // "" +#define MQTT_USER "" +#define MQTT_PASS "" +#define MQTT_KEEPALIVE 120 // seconds +#define MQTT_CLSESSION true -#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ -//PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ +#define PROTOCOL_NAMEv31 // MQTT version 3.1 compatible with Mosquitto v0.15 +//PROTOCOL_NAMEv311 // MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/ #endif \ No newline at end of file diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c index 9d435c8..12b9514 100644 --- a/mqtt/mqtt.c +++ b/mqtt/mqtt.c @@ -471,14 +471,16 @@ MQTT_Task(os_event_t* e) { * @retval None */ void ICACHE_FLASH_ATTR -MQTT_InitConnection(MQTT_Client* mqttClient, uint8_t* host, uint32 port, uint8_t security) { - uint32_t temp; +MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security) { os_printf("MQTT_InitConnection\n"); - os_memset(mqttClient, 0, sizeof(MQTT_Client)); - temp = sizeof((char*)host); - mqttClient->host = (uint8_t*)os_zalloc(temp + 1); - os_strcpy((char*)mqttClient->host, (char*)host); + uint8_t len = sizeof(MQTT_Client); + os_memset(mqttClient, 0, len); + + uint32_t temp = os_strlen(host); + mqttClient->host = (char*)os_zalloc(temp + 1); + os_strcpy(mqttClient->host, host); mqttClient->host[temp] = 0; + mqttClient->port = port; mqttClient->security = security; } @@ -493,25 +495,25 @@ MQTT_InitConnection(MQTT_Client* mqttClient, uint8_t* host, uint32 port, uint8_t * @retval None */ void ICACHE_FLASH_ATTR -MQTT_InitClient(MQTT_Client* mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) { +MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) { uint32_t temp; os_printf("MQTT_InitClient\n"); os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); - temp = os_strlen((char*)client_id); + temp = os_strlen(client_id); mqttClient->connect_info.client_id = (char*)os_zalloc(temp + 1); - os_strcpy((char*)mqttClient->connect_info.client_id, (char*)client_id); + os_strcpy(mqttClient->connect_info.client_id, client_id); mqttClient->connect_info.client_id[temp] = 0; - temp = os_strlen((char*)client_user); + temp = os_strlen(client_user); mqttClient->connect_info.username = (char*)os_zalloc(temp + 1); - os_strcpy((char*)mqttClient->connect_info.username, (char*)client_user); + os_strcpy(mqttClient->connect_info.username, client_user); mqttClient->connect_info.username[temp] = 0; - temp = os_strlen((char*)client_pass); + temp = os_strlen(client_pass); mqttClient->connect_info.password = (char*)os_zalloc(temp + 1); - os_strcpy((char*)mqttClient->connect_info.password, (char*)client_pass); + os_strcpy(mqttClient->connect_info.password, client_pass); mqttClient->connect_info.password[temp] = 0; @@ -533,7 +535,7 @@ MQTT_InitClient(MQTT_Client* mqttClient, uint8_t* client_id, uint8_t* client_use } void ICACHE_FLASH_ATTR -MQTT_InitLWT(MQTT_Client* mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) { +MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, uint8_t will_qos, uint8_t will_retain) { uint32_t temp; temp = os_strlen((char*)will_topic); mqttClient->connect_info.will_topic = (char*)os_zalloc(temp + 1); @@ -576,7 +578,7 @@ MQTT_Connect(MQTT_Client* mqttClient) { os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); os_timer_arm(&mqttClient->mqttTimer, 1000, 1); - if (UTILS_StrToIP((const int8_t *)mqttClient->host, (void*)&mqttClient->pCon->proto.tcp->remote_ip)) { + if (UTILS_StrToIP((const char *)mqttClient->host, (void*)&mqttClient->pCon->proto.tcp->remote_ip)) { os_printf("MQTT-TCP: Connect to ip %s:%ld\n", mqttClient->host, mqttClient->port); #ifdef CLIENT_SSL_ENABLE if (mqttClient->security){ diff --git a/mqtt/mqtt.h b/mqtt/mqtt.h index d0fdfb5..9ad6825 100644 --- a/mqtt/mqtt.h +++ b/mqtt/mqtt.h @@ -33,7 +33,7 @@ #include #include "mqtt_msg.h" #include "queue.h" -#include "utils.h" +#include typedef struct mqtt_event_data_t { uint8_t type; @@ -87,8 +87,8 @@ typedef void (*MqttDataCallback)(uint32_t* args, const char* topic, uint32_t top typedef struct { struct espconn* pCon; - uint32_t security; - uint8_t* host; + uint8_t security; + char* host; uint32_t port; ip_addr_t ip; mqtt_state_t mqtt_state; @@ -129,16 +129,16 @@ typedef struct { #define MQTT_EVENT_TYPE_EXITED 7 #define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 -void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client* mqttClient, uint8_t* host, uint32 port, uint8_t security); -void ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client* mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint8_t keepAliveTime, uint8_t cleanSession); -void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client* mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); -void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb); -void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb); -void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb); -void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb); -bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); -void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client* mqttClient); -void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client* mqttClient); -bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain); +void MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security); +void MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession); +void MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, uint8_t will_qos, uint8_t will_retain); +void MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb); +void MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb); +void MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb); +void MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb); +bool MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); +void MQTT_Connect(MQTT_Client* mqttClient); +void MQTT_Disconnect(MQTT_Client* mqttClient); +bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain); #endif /* USER_AT_MQTT_H_ */ diff --git a/mqtt/mqtt_msg.h b/mqtt/mqtt_msg.h index ce840b7..bf305df 100644 --- a/mqtt/mqtt_msg.h +++ b/mqtt/mqtt_msg.h @@ -72,9 +72,9 @@ typedef struct mqtt_connect_info { char* will_topic; char* will_message; uint32_t keepalive; - uint32_t will_qos; - uint32_t will_retain; - uint32_t clean_session; + uint8_t will_qos; + uint8_t will_retain; + uint8_t clean_session; } mqtt_connect_info_t; @@ -95,23 +95,23 @@ static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } -void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); -int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); -const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); -const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); -uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length); - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); +void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); +int mqtt_get_total_length(uint8_t* buffer, uint16_t length); +const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); +const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); +uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length); + +mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); +mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); +mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); +mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); +mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection); +mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection); +mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection); #endif // MQTT_MSG_H diff --git a/mqtt/proto.h b/mqtt/proto.h index 8d20832..67b3e26 100644 --- a/mqtt/proto.h +++ b/mqtt/proto.h @@ -1,10 +1,3 @@ -/* - * File: proto.h - * Author: ThuHien - * - * Created on November 23, 2012, 8:57 AM - */ - #ifndef _PROTO_H_ #define _PROTO_H_ #include @@ -21,8 +14,8 @@ typedef struct { PROTO_PARSE_CALLBACK* callback; } PROTO_PARSER; -int8_t ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize); -int16_t ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len); -int8_t ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value); -int16_t ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen); +int8_t PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize); +int16_t PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len); +int8_t PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value); +int16_t PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen); #endif diff --git a/mqtt/queue.h b/mqtt/queue.h index 78b7882..bdbc503 100644 --- a/mqtt/queue.h +++ b/mqtt/queue.h @@ -39,8 +39,8 @@ typedef struct { RINGBUF rb; } QUEUE; -void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE* queue, int bufferSize); -int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len); -int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); -bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE* queue); +void QUEUE_Init(QUEUE* queue, int bufferSize); +int32_t QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len); +int32_t QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); +bool QUEUE_IsEmpty(QUEUE* queue); #endif /* USER_QUEUE_H_ */ diff --git a/mqtt/ringbuf.h b/mqtt/ringbuf.h index 7d9f8e9..d504d53 100644 --- a/mqtt/ringbuf.h +++ b/mqtt/ringbuf.h @@ -11,7 +11,7 @@ typedef struct { int32_t size; /**< Buffer size */ } RINGBUF; -int16_t ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size); -int16_t ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF* r, uint8_t c); -int16_t ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF* r, uint8_t* c); +int16_t RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size); +int16_t RINGBUF_Put(RINGBUF* r, uint8_t c); +int16_t RINGBUF_Get(RINGBUF* r, uint8_t* c); #endif diff --git a/mqtt/utils.c b/mqtt/utils.c deleted file mode 100644 index 86aff20..0000000 --- a/mqtt/utils.c +++ /dev/null @@ -1,140 +0,0 @@ -/* -* Copyright (c) 2014, Tuan PM -* Email: tuanpm@live.com -* -* 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. -* -*/ -#include "utils.h" - -uint8_t ICACHE_FLASH_ATTR -UTILS_IsIPV4(int8_t* str) { - uint8_t segs = 0; /* Segment count. */ - uint8_t chcnt = 0; /* Character count within segment. */ - uint8_t accum = 0; /* Accumulator for segment. */ - /* Catch NULL pointer. */ - if (str == 0) - return 0; - /* Process every character in string. */ - - while (*str != '\0') { - /* Segment changeover. */ - - if (*str == '.') { - /* Must have some digits in segment. */ - if (chcnt == 0) - return 0; - /* Limit number of segments. */ - if (++segs == 4) - return 0; - /* Reset segment values and restart loop. */ - chcnt = accum = 0; - str++; - continue; - } - - /* Check numeric. */ - if ((*str < '0') || (*str > '9')) - return 0; - - /* Accumulate and check segment. */ - - if ((accum = accum * 10 + *str - '0') > 255) - return 0; - /* Advance other segment specific stuff and continue loop. */ - - chcnt++; - str++; - } - - /* Check enough segments and enough characters in last segment. */ - - if (segs != 3) - return 0; - if (chcnt == 0) - return 0; - /* Address okay. */ - - return 1; -} - -uint8_t ICACHE_FLASH_ATTR -UTILS_StrToIP(const int8_t* str, void* ip) { - - /* The count of the number of bytes processed. */ - int i; - /* A pointer to the next digit to process. */ - for (i = 0; i < 4; i++) { - /* The digit being processed. */ - char c; - /* The value of this byte. */ - int n = 0; - while (1) { - c = *(const char *)str; - (const char *)str++; - 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; -} - -uint32_t ICACHE_FLASH_ATTR -UTILS_Atoh(const int8_t* s) { - uint32_t value = 0, digit; - int8_t c; - - while ((c = *s++)) { - if ('0' <= c && c <= '9') - digit = c - '0'; - else if ('A' <= c && c <= 'F') - digit = c - 'A' + 10; - else if ('a' <= c && c <= 'f') - digit = c - 'a' + 10; - else break; - - value = (value << 4) | digit; - } - - return value; -} diff --git a/mqtt/utils.h b/mqtt/utils.h deleted file mode 100644 index bc4d2af..0000000 --- a/mqtt/utils.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _UTILS_H_ -#define _UTILS_H_ - -#include - -uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t* s); -uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void* ip); -uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4(int8_t* str); -#endif diff --git a/user/user_main.c b/user/user_main.c index 31e62a1..5738807 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -1,5 +1,70 @@ #include +#include +#include + +MQTT_Client mqttClient; + +void ICACHE_FLASH_ATTR +mqttConnectedCb(uint32_t *args) { + MQTT_Client* client = (MQTT_Client*)args; + MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); +} + +void ICACHE_FLASH_ATTR +mqttDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Disconnected\n"); +} + +void ICACHE_FLASH_ATTR +mqttTcpDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT TCP Disconnected\n"); +} + +void ICACHE_FLASH_ATTR +mqttPublishedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Published\n"); +} + +void ICACHE_FLASH_ATTR +mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { + char *topicBuf = (char*)os_zalloc(topic_len + 1); + char *dataBuf = (char*)os_zalloc(data_len + 1); + +// MQTT_Client* client = (MQTT_Client*)args; + + os_memcpy(topicBuf, topic, topic_len); + topicBuf[topic_len] = 0; + + os_memcpy(dataBuf, data, data_len); + dataBuf[data_len] = 0; + + os_printf("Receive topic: %s, data: %s\n", topicBuf, dataBuf); + os_free(topicBuf); + os_free(dataBuf); +} + +void ICACHE_FLASH_ATTR +wifiStateChangeCb(uint8_t status) +{ + if (status == wifiGotIP && mqttClient.connState != TCP_CONNECTING){ + MQTT_Connect(&mqttClient); + } + else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING){ + MQTT_Disconnect(&mqttClient); + } +} void init() { - + wifiAddStateChangeCb(wifiStateChangeCb); + MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); + MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); + MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); + MQTT_OnConnected(&mqttClient, mqttConnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); + MQTT_OnPublished(&mqttClient, mqttPublishedCb); + MQTT_OnData(&mqttClient, mqttDataCb); } \ No newline at end of file From 248f2f3f47b6aa4ce46668f040805d868a49ca07 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 29 Aug 2015 16:13:52 -0500 Subject: [PATCH 064/104] mergeing mqtt into master --- Makefile | 2 +- cmd/handlers.c | 7 +- cmd/mqtt_cmd.c | 336 ++++++++++++++++++ cmd/mqtt_cmd.h | 22 ++ cmd/rest.c | 2 - cmd/rest.h | 1 + esp-link.vcxproj | 31 +- {user => esp-link}/cgi.c | 0 {user => esp-link}/cgi.h | 0 {user => esp-link}/cgiflash.c | 0 {user => esp-link}/cgiflash.h | 0 {user => esp-link}/cgipins.c | 0 {user => esp-link}/cgipins.h | 0 {user => esp-link}/cgitcp.c | 0 {user => esp-link}/cgitcp.h | 0 {user => esp-link}/cgiwifi.c | 0 {user => esp-link}/cgiwifi.h | 4 +- {user => esp-link}/config.c | 0 {user => esp-link}/config.h | 0 {user => esp-link}/log.c | 0 {user => esp-link}/log.h | 0 esp-link/main.c | 168 +++++++++ {user => esp-link}/status.c | 0 {user => esp-link}/status.h | 0 httpd/httpdespfs.c | 11 +- httpd/httpdespfs.h | 6 +- include/esp8266.h | 5 +- include/user_config.h | 19 + mqtt/mqtt.c | 635 ++++++++++++++++++++++++++++++++++ mqtt/mqtt.h | 144 ++++++++ mqtt/mqtt_msg.c | 451 ++++++++++++++++++++++++ mqtt/mqtt_msg.h | 117 +++++++ mqtt/proto.c | 86 +++++ mqtt/proto.h | 21 ++ mqtt/queue.c | 53 +++ mqtt/queue.h | 46 +++ mqtt/ringbuf.c | 63 ++++ mqtt/ringbuf.h | 17 + user/user_main.c | 203 +++-------- 39 files changed, 2286 insertions(+), 164 deletions(-) create mode 100644 cmd/mqtt_cmd.c create mode 100644 cmd/mqtt_cmd.h rename {user => esp-link}/cgi.c (100%) rename {user => esp-link}/cgi.h (100%) rename {user => esp-link}/cgiflash.c (100%) rename {user => esp-link}/cgiflash.h (100%) rename {user => esp-link}/cgipins.c (100%) rename {user => esp-link}/cgipins.h (100%) rename {user => esp-link}/cgitcp.c (100%) rename {user => esp-link}/cgitcp.h (100%) rename {user => esp-link}/cgiwifi.c (100%) rename {user => esp-link}/cgiwifi.h (72%) rename {user => esp-link}/config.c (100%) rename {user => esp-link}/config.h (100%) rename {user => esp-link}/log.c (100%) rename {user => esp-link}/log.h (100%) create mode 100644 esp-link/main.c rename {user => esp-link}/status.c (100%) rename {user => esp-link}/status.h (100%) create mode 100644 mqtt/mqtt.c create mode 100644 mqtt/mqtt.h create mode 100644 mqtt/mqtt_msg.c create mode 100644 mqtt/mqtt_msg.h create mode 100644 mqtt/proto.c create mode 100644 mqtt/proto.h create mode 100644 mqtt/queue.c create mode 100644 mqtt/queue.h create mode 100644 mqtt/ringbuf.c create mode 100644 mqtt/ringbuf.h diff --git a/Makefile b/Makefile index 66bcbe9..451d7f3 100644 --- a/Makefile +++ b/Makefile @@ -143,7 +143,7 @@ TARGET = httpd APPGEN_TOOL ?= gen_appbin.py # which modules (subdirectories) of the project to include in compiling -MODULES = espfs httpd user serial cmd +MODULES = espfs httpd user serial cmd mqtt esp-link EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK diff --git a/cmd/handlers.c b/cmd/handlers.c index 8b37a18..b54773f 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -9,6 +9,7 @@ #include "serbridge.h" #include "uart.h" #include "cgiwifi.h" +#include "mqtt_cmd.h" static uint32_t CMD_Null(CmdPacket *cmd); static uint32_t CMD_IsReady(CmdPacket *cmd); @@ -18,6 +19,7 @@ static uint32_t CMD_AddCallback(CmdPacket *cmd); // keep track of last status sent to uC so we can notify it when it changes static uint8_t lastWifiStatus = wifiIsDisconnected; +static bool wifiCbAdded = false; // Command dispatch table for serial -> ESP commands const CmdList commands[] = { @@ -131,7 +133,10 @@ CMD_WifiConnect(CmdPacket *cmd) { if(cmd->argc != 2 || cmd->callback == 0) return 0; - wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem + if (!wifiCbAdded) { + wifiAddStateChangeCb(CMD_WifiCb); // register our callback with wifi subsystem + wifiCbAdded = true; + } CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback lastWifiStatus = 0xff; // set to invalid value so we immediately send status cb in all cases CMD_WifiCb(wifiState); diff --git a/cmd/mqtt_cmd.c b/cmd/mqtt_cmd.c new file mode 100644 index 0000000..358ad48 --- /dev/null +++ b/cmd/mqtt_cmd.c @@ -0,0 +1,336 @@ +#include "mqtt_cmd.h" + +uint32_t connectedCb = 0, disconnectCb = 0, tcpDisconnectedCb = 0, publishedCb = 0, dataCb = 0; + +void ICACHE_FLASH_ATTR +cmdMqttConnectedCb(uint32_t* args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: Connected connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n", + (void*)cb->connectedCb, + (void*)cb->disconnectedCb, + (void*)cb->publishedCb, + (void*)cb->dataCb); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +cmdMqttTcpDisconnectedCb(uint32_t *args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb *cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: TCP Disconnected\n"); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->tcpDisconnectedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +cmdMqttDisconnectedCb(uint32_t* args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: Disconnected\n"); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +cmdMqttPublishedCb(uint32_t* args) { + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + os_printf("MQTT: Published\n"); + uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0); + CMD_ResponseEnd(crc); +} + +void ICACHE_FLASH_ATTR +cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) { + uint16_t crc = 0; + MQTT_Client* client = (MQTT_Client*)args; + MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + + crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->dataCb, 0, 2); + crc = CMD_ResponseBody(crc, (uint8_t*)topic, topic_len); + crc = CMD_ResponseBody(crc, (uint8_t*)data, data_len); + CMD_ResponseEnd(crc); +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Setup(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 9) + return 0; + + // create mqtt client + uint8_t clientLen = sizeof(MQTT_Client); + MQTT_Client* client = (MQTT_Client*)os_zalloc(clientLen); + if (client == NULL) + return 0; + os_memset(client, 0, clientLen); + + uint16_t len; + uint8_t *client_id, *user_data, *pass_data; + uint32_t keepalive, clean_session, cb_data; + + // get client id + len = CMD_ArgLen(&req); + if (len > 32) return 0; // safety check + client_id = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, client_id, len); + client_id[len] = 0; + + // get username + len = CMD_ArgLen(&req); + if (len > 32) return 0; // safety check + user_data = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, user_data, len); + user_data[len] = 0; + + // get password + len = CMD_ArgLen(&req); + if (len > 32) return 0; // safety check + pass_data = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, pass_data, len); + pass_data[len] = 0; + + // get keepalive + CMD_PopArg(&req, (uint8_t*)&keepalive, 4); + + // get clean session + CMD_PopArg(&req, (uint8_t*)&clean_session, 4); + + os_printf("MQTT: MQTTCMD_Setup clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session); + + // init client + // TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again? + MQTT_InitClient(client, (char*)client_id, (char*)user_data, (char*)pass_data, keepalive, clean_session); + + // create callback + MqttCmdCb* callback = (MqttCmdCb*)os_zalloc(sizeof(MqttCmdCb)); + + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->connectedCb = cb_data; + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->disconnectedCb = cb_data; + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->publishedCb = cb_data; + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->dataCb = cb_data; + + client->user_data = callback; + + client->cmdConnectedCb = cmdMqttConnectedCb; + client->cmdDisconnectedCb = cmdMqttDisconnectedCb; + client->cmdPublishedCb = cmdMqttPublishedCb; + client->cmdDataCb = cmdMqttDataCb; + + if (CMD_GetArgc(&req) == 10) { + CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + callback->tcpDisconnectedCb = cb_data; + client->cmdTcpDisconnectedCb = cmdMqttTcpDisconnectedCb; + } + + os_free(client_id); + os_free(user_data); + os_free(pass_data); + + return (uint32_t)client; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Lwt(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 5) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + + // get topic + if (client->connect_info.will_topic) + os_free(client->connect_info.will_topic); + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + client->connect_info.will_topic = (char*)os_zalloc(len + 1); + CMD_PopArg(&req, client->connect_info.will_topic, len); + client->connect_info.will_topic[len] = 0; + + // get message + if (client->connect_info.will_message) + os_free(client->connect_info.will_message); + len = CMD_ArgLen(&req); + // TODO: safety check + client->connect_info.will_message = (char*)os_zalloc(len + 1); + CMD_PopArg(&req, client->connect_info.will_message, len); + client->connect_info.will_message[len] = 0; + + // get qos + CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_qos, 4); + + // get retain + CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); + + os_printf("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%d, retain=%d\n", + client->connect_info.will_topic, + client->connect_info.will_message, + client->connect_info.will_qos, + client->connect_info.will_retain); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Connect(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 4) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Connect client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + + // get host + if (client->host) + os_free(client->host); + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + client->host = (char*)os_zalloc(len + 1); + CMD_PopArg(&req, client->host, len); + client->host[len] = 0; + + // get port + CMD_PopArg(&req, (uint8_t*)&client->port, 4); + + // get security + CMD_PopArg(&req, (uint8_t*)&client->security, 4); + + os_printf("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%d\n", + client->host, + client->port, + client->security); + + MQTT_Connect(client); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Disconnect(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 1) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Disconnect client ptr=%p\n", (void*)client_ptr); + + // disconnect + MQTT_Disconnect(client); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Publish(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 6) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Publish client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + uint8_t *topic, *data; + uint32_t qos = 0, retain = 0, data_len; + + // get topic + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + topic = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, topic, len); + topic[len] = 0; + + // get data + len = CMD_ArgLen(&req); + // TODO: Safety check + // TODO: this was orignially zalloc len not len+1 + data = (uint8_t*)os_zalloc(len+1); + CMD_PopArg(&req, data, len); + // TODO: next line not originally present + data[len] = 0; + + // get data length + // TODO: this isn't used but we have to pull it off the stack + CMD_PopArg(&req, (uint8_t*)&data_len, 4); + + // get qos + CMD_PopArg(&req, (uint8_t*)&qos, 4); + + // get retain + CMD_PopArg(&req, (uint8_t*)&retain, 4); + + os_printf("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n", + topic, + os_strlen((char*)data), + qos, + retain); + + MQTT_Publish(client, (char*)topic, (char*)data, (uint8_t)qos, (uint8_t)retain); + os_free(topic); + os_free(data); + return 1; +} + +uint32_t ICACHE_FLASH_ATTR +MQTTCMD_Subscribe(CmdPacket *cmd) { + CmdRequest req; + CMD_Request(&req, cmd); + + if (CMD_GetArgc(&req) != 3) + return 0; + + // get mqtt client + uint32_t client_ptr; + CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + MQTT_Client* client = (MQTT_Client*)client_ptr; + os_printf("MQTT: MQTTCMD_Subscribe client ptr=%p\n", (void*)client_ptr); + + uint16_t len; + uint8_t* topic; + uint32_t qos = 0; + + // get topic + len = CMD_ArgLen(&req); + if (len > 128) return 0; // safety check + topic = (uint8_t*)os_zalloc(len + 1); + CMD_PopArg(&req, topic, len); + topic[len] = 0; + + // get qos + CMD_PopArg(&req, (uint8_t*)&qos, 4); + + os_printf("MQTT: MQTTCMD_Subscribe topic=%s, qos=%ld\n", topic, qos); + MQTT_Subscribe(client, (char*)topic, (uint8_t)qos); + os_free(topic); + return 1; +} diff --git a/cmd/mqtt_cmd.h b/cmd/mqtt_cmd.h new file mode 100644 index 0000000..9ccbd08 --- /dev/null +++ b/cmd/mqtt_cmd.h @@ -0,0 +1,22 @@ +#ifndef MODULES_MQTT_CMD_H_ +#define MODULES_MQTT_CMD_H_ + +#include "cmd.h" +#include "mqtt.h" + +typedef struct { + uint32_t connectedCb; + uint32_t disconnectedCb; + uint32_t publishedCb; + uint32_t dataCb; + uint32_t tcpDisconnectedCb; +} MqttCmdCb; + +uint32_t MQTTCMD_Connect(CmdPacket *cmd); +uint32_t MQTTCMD_Disconnect(CmdPacket *cmd); +uint32_t MQTTCMD_Setup(CmdPacket *cmd); +uint32_t MQTTCMD_Publish(CmdPacket *cmd); +uint32_t MQTTCMD_Subscribe(CmdPacket *cmd); +uint32_t MQTTCMD_Lwt(CmdPacket *cmd); + +#endif /* MODULES_MQTT_CMD_H_ */ diff --git a/cmd/rest.c b/cmd/rest.c index 5bc3da7..3556681 100644 --- a/cmd/rest.c +++ b/cmd/rest.c @@ -16,8 +16,6 @@ static RestClient restClient[MAX_REST]; static uint8_t restNum = 0xff; // index into restClient for next slot to allocate #define REST_CB 0xbeef0000 // fudge added to callback for arduino so we can detect problems -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; diff --git a/cmd/rest.h b/cmd/rest.h index 7161e1f..9c6cd5c 100644 --- a/cmd/rest.h +++ b/cmd/rest.h @@ -36,5 +36,6 @@ typedef struct { uint32_t REST_Setup(CmdPacket *cmd); uint32_t REST_Request(CmdPacket *cmd); uint32_t REST_SetHeader(CmdPacket *cmd); +uint8_t UTILS_StrToIP(const char* str, void *ip); #endif /* MODULES_INCLUDE_API_H_ */ diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 918ed96..f371f1a 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -28,7 +28,7 @@ __ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__ - .\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;C:\tools\mingw64\include + .\esp-link;.\mqtt;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include @@ -65,6 +65,7 @@ + @@ -75,12 +76,26 @@ + + + + + + + + + + + + + + @@ -93,6 +108,7 @@ + @@ -106,11 +122,24 @@ + + + + + + + + + + + + + diff --git a/user/cgi.c b/esp-link/cgi.c similarity index 100% rename from user/cgi.c rename to esp-link/cgi.c diff --git a/user/cgi.h b/esp-link/cgi.h similarity index 100% rename from user/cgi.h rename to esp-link/cgi.h diff --git a/user/cgiflash.c b/esp-link/cgiflash.c similarity index 100% rename from user/cgiflash.c rename to esp-link/cgiflash.c diff --git a/user/cgiflash.h b/esp-link/cgiflash.h similarity index 100% rename from user/cgiflash.h rename to esp-link/cgiflash.h diff --git a/user/cgipins.c b/esp-link/cgipins.c similarity index 100% rename from user/cgipins.c rename to esp-link/cgipins.c diff --git a/user/cgipins.h b/esp-link/cgipins.h similarity index 100% rename from user/cgipins.h rename to esp-link/cgipins.h diff --git a/user/cgitcp.c b/esp-link/cgitcp.c similarity index 100% rename from user/cgitcp.c rename to esp-link/cgitcp.c diff --git a/user/cgitcp.h b/esp-link/cgitcp.h similarity index 100% rename from user/cgitcp.h rename to esp-link/cgitcp.h diff --git a/user/cgiwifi.c b/esp-link/cgiwifi.c similarity index 100% rename from user/cgiwifi.c rename to esp-link/cgiwifi.c diff --git a/user/cgiwifi.h b/esp-link/cgiwifi.h similarity index 72% rename from user/cgiwifi.h rename to esp-link/cgiwifi.h index d73628e..3f7fe09 100644 --- a/user/cgiwifi.h +++ b/esp-link/cgiwifi.h @@ -4,6 +4,7 @@ #include "httpd.h" enum { wifiIsDisconnected, wifiIsConnected, wifiGotIP }; +typedef void(*WifiStateChangeCb)(uint8_t wifiStatus); int cgiWiFiScan(HttpdConnData *connData); int cgiWifiInfo(HttpdConnData *connData); @@ -13,8 +14,9 @@ int cgiWiFiSetMode(HttpdConnData *connData); int cgiWiFiConnStatus(HttpdConnData *connData); int cgiWiFiSpecial(HttpdConnData *connData); void wifiInit(void); +void wifiAddStateChangeCb(WifiStateChangeCb cb); extern uint8_t wifiState; -extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes +//extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes #endif diff --git a/user/config.c b/esp-link/config.c similarity index 100% rename from user/config.c rename to esp-link/config.c diff --git a/user/config.h b/esp-link/config.h similarity index 100% rename from user/config.h rename to esp-link/config.h diff --git a/user/log.c b/esp-link/log.c similarity index 100% rename from user/log.c rename to esp-link/log.c diff --git a/user/log.h b/esp-link/log.h similarity index 100% rename from user/log.h rename to esp-link/log.h diff --git a/esp-link/main.c b/esp-link/main.c new file mode 100644 index 0000000..5febf87 --- /dev/null +++ b/esp-link/main.c @@ -0,0 +1,168 @@ +/* +* ---------------------------------------------------------------------------- +* "THE BEER-WARE LICENSE" (Revision 42): +* Jeroen Domburg wrote this file. As long as you retain +* this notice you can do whatever you want with this stuff. If we meet some day, +* and you think this stuff is worth it, you can buy me a beer in return. +* ---------------------------------------------------------------------------- +* Heavily modified and enhanced by Thorsten von Eicken in 2015 +* ---------------------------------------------------------------------------- +*/ + + +#include +#include "httpd.h" +#include "httpdespfs.h" +#include "cgi.h" +#include "cgiwifi.h" +#include "cgipins.h" +#include "cgitcp.h" +#include "cgiflash.h" +#include "auth.h" +#include "espfs.h" +#include "uart.h" +#include "serbridge.h" +#include "status.h" +#include "serled.h" +#include "console.h" +#include "config.h" +#include "log.h" +#include + +//#define SHOW_HEAP_USE + +//Function that tells the authentication system what users/passwords live on the system. +//This is disabled in the default build; if you want to try it, enable the authBasic line in +//the builtInUrls below. +int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { + if (no == 0) { + os_strcpy(user, "admin"); + os_strcpy(pass, "s3cr3t"); + return 1; + //Add more users this way. Check against incrementing no for each user added. + // } else if (no==1) { + // os_strcpy(user, "user1"); + // os_strcpy(pass, "something"); + // return 1; + } + return 0; +} + + +/* +This is the main url->function dispatching data struct. +In short, it's a struct with various URLs plus their handlers. The handlers can +be 'standard' CGI functions you wrote, or 'special' CGIs requiring an argument. +They can also be auth-functions. An asterisk will match any url starting with +everything before the asterisks; "*" matches everything. The list will be +handled top-down, so make sure to put more specific rules above the more +general ones. Authorization things (like authBasic) act as a 'barrier' and +should be placed above the URLs they protect. +*/ +HttpdBuiltInUrl builtInUrls[] = { + { "/", cgiRedirect, "/home.html" }, + { "/menu", cgiMenu, NULL }, + { "/flash/next", cgiGetFirmwareNext, NULL }, + { "/flash/upload", cgiUploadFirmware, NULL }, + { "/flash/reboot", cgiRebootFirmware, NULL }, + //{"/home.html", cgiEspFsHtml, NULL}, + //{"/log.html", cgiEspFsHtml, NULL}, + { "/log/text", ajaxLog, NULL }, + { "/log/dbg", ajaxLogDbg, NULL }, + //{"/console.html", cgiEspFsHtml, NULL}, + { "/console/reset", ajaxConsoleReset, NULL }, + { "/console/baud", ajaxConsoleBaud, NULL }, + { "/console/text", ajaxConsole, NULL }, + + //Routines to make the /wifi URL and everything beneath it work. + + //Enable the line below to protect the WiFi configuration with an username/password combo. + // {"/wifi/*", authBasic, myPassFn}, + + { "/wifi", cgiRedirect, "/wifi/wifi.html" }, + { "/wifi/", cgiRedirect, "/wifi/wifi.html" }, + //{"/wifi/wifi.html", cgiEspFsHtml, NULL}, + { "/wifi/info", cgiWifiInfo, NULL }, + { "/wifi/scan", cgiWiFiScan, NULL }, + { "/wifi/connect", cgiWiFiConnect, NULL }, + { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, + { "/wifi/setmode", cgiWiFiSetMode, NULL }, + { "/wifi/special", cgiWiFiSpecial, NULL }, + { "/pins", cgiPins, NULL }, + { "/tcpclient", cgiTcp, NULL }, + + { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem + { NULL, NULL, NULL } +}; + + +//#define SHOW_HEAP_USE + +#ifdef SHOW_HEAP_USE +static ETSTimer prHeapTimer; + +static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { + os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); +} +#endif + +void user_rf_pre_init(void) { +} + +// address of espfs binary blob +extern uint32_t _binary_espfs_img_start; + +static char *rst_codes[] = { + "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", +}; + +# define VERS_STR_STR(V) #V +# define VERS_STR(V) VERS_STR_STR(V) +char* esp_link_version = VERS_STR(VERSION); + +//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. +void user_init(void) { + // get the flash config so we know how to init things + //configWipe(); // uncomment to reset the config for testing purposes + bool restoreOk = configRestore(); + // init gpio pin registers + gpio_init(); + // init UART + uart_init(flashConfig.baud_rate, 115200); + logInit(); // must come after init of uart + // say hello (leave some time to cause break in TX after boot loader's msg + os_delay_us(10000L); + os_printf("\n\n** %s\n", esp_link_version); + os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); + // Status LEDs + statusInit(); + serledInit(); + // Wifi + wifiInit(); + // init the flash filesystem with the html stuff + espFsInit(&_binary_espfs_img_start); + //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); + //os_printf("espFsInit %s\n", res?"ERR":"ok"); + // mount the http handlers + httpdInit(builtInUrls, 80); + // init the wifi-serial transparent bridge (port 23) + serbridgeInit(23); + uart_add_recv_cb(&serbridgeUartCb); +#ifdef SHOW_HEAP_USE + os_timer_disarm(&prHeapTimer); + os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); + os_timer_arm(&prHeapTimer, 10000, 1); +#endif + + struct rst_info *rst_info = system_get_rst_info(); + os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); + 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"); + + // call user_main init + init(); +} \ No newline at end of file diff --git a/user/status.c b/esp-link/status.c similarity index 100% rename from user/status.c rename to esp-link/status.c diff --git a/user/status.h b/esp-link/status.h similarity index 100% rename from user/status.h rename to esp-link/status.h diff --git a/httpd/httpdespfs.c b/httpd/httpdespfs.c index b6853aa..c6f2c0c 100644 --- a/httpd/httpdespfs.c +++ b/httpd/httpdespfs.c @@ -12,12 +12,7 @@ Connector to let httpd use the espfs filesystem to serve the files in it. * Modified and enhanced by Thorsten von Eicken in 2015 * ---------------------------------------------------------------------------- */ - -#include #include "httpdespfs.h" -#include "espfs.h" -#include "espfsformat.h" -#include "cgi.h" // The static files marked with FLAG_GZIP are compressed and will be served with GZIP compression. // If the client does not advertise that he accepts GZIP send following warning message (telnet users for e.g.) @@ -27,7 +22,8 @@ static const char *gzipNonSupportedMessage = "HTTP/1.0 501 Not implemented\r\nSe //This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding //path in the filesystem and if it exists, passes the file through. This simulates what a normal //webserver would do with static files. -int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) { +int ICACHE_FLASH_ATTR +cgiEspFsHook(HttpdConnData *connData) { EspFsFile *file=connData->cgiData; int len; char buff[1024]; @@ -93,7 +89,8 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) { #if 0 //cgiEspFsHtml is a simple HTML file that gets prefixed by head.tpl -int ICACHE_FLASH_ATTR cgiEspFsHtml(HttpdConnData *connData) { +int ICACHE_FLASH_ATTR +cgiEspFsHtml(HttpdConnData *connData) { EspFsFile *file = connData->cgiData; char buff[2048]; diff --git a/httpd/httpdespfs.h b/httpd/httpdespfs.h index fb07008..847a8b6 100644 --- a/httpd/httpdespfs.h +++ b/httpd/httpdespfs.h @@ -1,10 +1,14 @@ #ifndef HTTPDESPFS_H #define HTTPDESPFS_H +#include +#include "espfs.h" +#include "espfsformat.h" +#include "cgi.h" #include "httpd.h" int cgiEspFsHook(HttpdConnData *connData); -int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData); +//int cgiEspFsTemplate(HttpdConnData *connData); //int ICACHE_FLASH_ATTR cgiEspFsHtml(HttpdConnData *connData); #endif diff --git a/include/esp8266.h b/include/esp8266.h index 96a7364..c3d0c40 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -1,5 +1,5 @@ // Combined include file for esp8266 - +#include #include #include #include @@ -16,6 +16,9 @@ #include "espmissingincludes.h" #include "uart_hw.h" +extern char* esp_link_version; + +void ICACHE_FLASH_ATTR init(void); #ifdef __WIN32__ #include <_mingw.h> diff --git a/include/user_config.h b/include/user_config.h index 8b13789..7bdfd38 100644 --- a/include/user_config.h +++ b/include/user_config.h @@ -1 +1,20 @@ +#ifndef _USER_CONFIG_H_ +#define _USER_CONFIG_H_ +#define MQTT_RECONNECT_TIMEOUT 5 // seconds +#define MQTT_BUF_SIZE 1024 + +#define MQTT_HOST "10.0.0.220" // "mqtt.yourdomain.com" or ip "10.0.0.1" +#define MQTT_PORT 1883 +#define MQTT_SECURITY 0 + +#define MQTT_CLIENT_ID "esp-link" // "" +#define MQTT_USER "" +#define MQTT_PASS "" +#define MQTT_KEEPALIVE 120 // seconds +#define MQTT_CLSESSION true + +#define PROTOCOL_NAMEv31 // MQTT version 3.1 compatible with Mosquitto v0.15 +//PROTOCOL_NAMEv311 // MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/ + +#endif \ No newline at end of file diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c new file mode 100644 index 0000000..12b9514 --- /dev/null +++ b/mqtt/mqtt.c @@ -0,0 +1,635 @@ +/* mqtt.c +* Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ + +#include "mqtt.h" + +#define MQTT_TASK_PRIO 0 +#define MQTT_TASK_QUEUE_SIZE 1 +#define MQTT_SEND_TIMOUT 5 + +#ifndef QUEUE_BUFFER_SIZE +#define QUEUE_BUFFER_SIZE 2048 +#endif + +unsigned char* default_certificate; +unsigned int default_certificate_len = 0; +unsigned char* default_private_key; +unsigned int default_private_key_len = 0; + +os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; + +LOCAL void ICACHE_FLASH_ATTR +mqtt_dns_found(const char* name, ip_addr_t* ipaddr, void* arg) { + struct espconn* pConn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pConn->reverse; + + + if (ipaddr == NULL) { + os_printf("DNS: Found, but got no ip, try to reconnect\n"); + client->connState = TCP_RECONNECT_REQ; + return; + } + + os_printf("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); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_connect(client->pCon); + } + else +#endif + espconn_connect(client->pCon); + + client->connState = TCP_CONNECTING; + os_printf("MQTT-TCP: connecting...\n"); + } + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + + +LOCAL void ICACHE_FLASH_ATTR +deliver_publish(MQTT_Client* client, uint8_t* message, uint16_t length) { + mqtt_event_data_t event_data; + + event_data.topic_length = length; + event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); + event_data.data_length = length; + event_data.data = mqtt_get_publish_data(message, &event_data.data_length); + + if (client->dataCb) + client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); + + if (client->cmdDataCb) + client->cmdDataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); + +} + +/** +* @brief Client received callback function. +* @param arg: contain the ip link information +* @param pdata: received data +* @param len: the lenght of received data +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { + uint8_t msg_type; + uint8_t msg_qos; + uint16_t msg_id; + + struct espconn* pCon = (struct espconn*)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + +READPACKET: + os_printf("MQTT-TCP: Data received %d bytes\n", len); + if (len < MQTT_BUF_SIZE && len > 0) { + os_memcpy(client->mqtt_state.in_buffer, pdata, len); + + msg_type = mqtt_get_type(client->mqtt_state.in_buffer); + msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); + msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); + if (client->connState == MQTT_CONNECT_SENDING) { + if (msg_type == MQTT_MSG_TYPE_CONNACK) { + if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) { + os_printf("MQTT: Invalid packet\n"); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_disconnect(client->pCon); + } + else +#endif + espconn_disconnect(client->pCon); + } + else { + os_printf("MQTT: Connected to %s:%ld\n", client->host, client->port); + client->connState = MQTT_DATA; + if (client->connectedCb) + client->connectedCb((uint32_t*)client); + if (client->cmdConnectedCb) + client->cmdConnectedCb((uint32_t*)client); + } + } + } + else if (client->connState == MQTT_DATA) { + client->mqtt_state.message_length_read = len; + client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + + + if (msg_type == MQTT_MSG_TYPE_SUBACK) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + os_printf("MQTT: Subscribe successful\n"); + } + else if (msg_type == MQTT_MSG_TYPE_UNSUBACK) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + os_printf("MQTT: UnSubscribe successful\n"); + } + else if (msg_type == MQTT_MSG_TYPE_PUBLISH) { + if (msg_qos == 1) + client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); + else if (msg_qos == 2) + client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); + if (msg_qos == 1 || msg_qos == 2) { + os_printf("MQTT: Queue response QoS: %d\n", msg_qos); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + os_printf("MQTT: Queue full\n"); + } + } + deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + } + else if (msg_type == MQTT_MSG_TYPE_PUBACK) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { + os_printf("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PUBREC) { + client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + os_printf("MQTT: Queue full\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PUBREL) { + client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + os_printf("MQTT: Queue full\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PUBCOMP) { + if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { + os_printf("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\n"); + } + } + else if (msg_type == MQTT_MSG_TYPE_PINGREQ) { + client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); + if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + os_printf("MQTT: Queue full\n"); + } + } + + // NOTE: this is done down here and not in the switch case above + // because the PSOCK_READBUF_LEN() won't work inside a switch + // statement due to the way protothreads resume. + if (msg_type == MQTT_MSG_TYPE_PUBLISH) { + len = client->mqtt_state.message_length_read; + + if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) { + //client->connState = MQTT_PUBLISH_RECV; + //Not Implement yet + len -= client->mqtt_state.message_length; + pdata += client->mqtt_state.message_length; + + os_printf("Get another published message\n"); + goto READPACKET; + } + } + } + } + else { + os_printf("ERROR: Message too long\n"); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief Client send over callback function. +* @param arg: contain the ip link information +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_sent_cb(void* arg) { + struct espconn* pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + os_printf("MQTT-TCP: Sent\n"); + client->sendTimeout = 0; + if (client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) { + if (client->publishedCb) + client->publishedCb((uint32_t*)client); + if (client->cmdPublishedCb) + client->cmdPublishedCb((uint32_t*)client); + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +void ICACHE_FLASH_ATTR +mqtt_timer(void* arg) { + MQTT_Client* client = (MQTT_Client*)arg; + + if (client->connState == MQTT_DATA) { + client->keepAliveTick++; + if (client->keepAliveTick > client->mqtt_state.connect_info->keepalive) { + + os_printf("\nMQTT: Send keepalive packet to %s:%ld!\n", client->host, client->port); + client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); + client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + else +#endif + espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + client->mqtt_state.outbound_message = NULL; + + client->keepAliveTick = 0; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + + } + else if (client->connState == TCP_RECONNECT_REQ) { + client->reconnectTick++; + if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { + client->reconnectTick = 0; + client->connState = TCP_RECONNECT; + if (client->tcpDisconnectedCb) + client->tcpDisconnectedCb((uint32_t*)client); + if (client->cmdTcpDisconnectedCb) + client->cmdTcpDisconnectedCb((uint32_t*)client); + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + } + } + if (client->sendTimeout > 0) + client->sendTimeout--; +} + +void ICACHE_FLASH_ATTR +mqtt_tcpclient_discon_cb(void* arg) { + + struct espconn* pespconn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pespconn->reverse; + os_printf("MQTT-TCP: Disconnected callback\n"); + client->connState = TCP_RECONNECT_REQ; + if (client->disconnectedCb) + client->disconnectedCb((uint32_t*)client); + if (client->cmdDisconnectedCb) + client->cmdDisconnectedCb((uint32_t*)client); + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief Tcp client connect success callback function. +* @param arg: contain the ip link information +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_connect_cb(void* arg) { + struct espconn* pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); + espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// + espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// + os_printf("MQTT: Connected to broker %s:%ld\n", client->host, client->port); + + mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); + client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); + client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); + client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + + client->sendTimeout = MQTT_SEND_TIMOUT; + os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + } + else +#endif + espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + + client->mqtt_state.outbound_message = NULL; + client->connState = MQTT_CONNECT_SENDING; + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief Tcp client connect repeat callback function. +* @param arg: contain the ip link information +* @retval None +*/ +void ICACHE_FLASH_ATTR +mqtt_tcpclient_recon_cb(void* arg, int8_t errType) { + struct espconn* pCon = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pCon->reverse; + + os_printf("MQTT-TCP: Reconnect to %s:%ld\n", client->host, client->port); + + client->connState = TCP_RECONNECT_REQ; + + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +} + +/** +* @brief MQTT publish function. +* @param client: MQTT_Client reference +* @param topic: string topic will publish to +* @param data: buffer data send point to +* @param data_length: length of data +* @param qos: qos +* @param retain: retain +* @retval TRUE if success queue +*/ +bool ICACHE_FLASH_ATTR +MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain) { + int data_length = os_strlen(data); + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, + topic, data, data_length, + qos, retain, + &client->mqtt_state.pending_msg_id); + if (client->mqtt_state.outbound_message->length == 0) { + os_printf("MQTT: Queuing Publish failed\n"); + return FALSE; + } + os_printf("MQTT: Queuing Publish, length: %d, queue size(%ld/%ld)\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + os_printf("MQTT: Queue full\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + os_printf("MQTT: Serious buffer error\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +/** +* @brief MQTT subscibe function. +* @param client: MQTT_Client reference +* @param topic: string topic will subscribe +* @param qos: qos +* @retval TRUE if success queue +*/ +bool ICACHE_FLASH_ATTR +MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos) { + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + + client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, + topic, 0, + &client->mqtt_state.pending_msg_id); + os_printf("MQTT: Queue Subscribe, topic: \"%s\", id: %d\n", topic, client->mqtt_state.pending_msg_id); + while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { + os_printf("MQTT: Queue full\n"); + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { + os_printf("MQTT: Serious buffer error\n"); + return FALSE; + } + } + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + return TRUE; +} + +void ICACHE_FLASH_ATTR +MQTT_Task(os_event_t* e) { + MQTT_Client* client = (MQTT_Client*)e->par; + uint8_t dataBuffer[MQTT_BUF_SIZE]; + uint16_t dataLen; + if (e->par == 0) + return; + + if (client->connState == TCP_RECONNECT_REQ) { + return; + } + else if (client->connState == TCP_RECONNECT) { + MQTT_Connect(client); + os_printf("MQTT-TCP: Reconnect to: %s:%ld\n", client->host, client->port); + client->connState = TCP_CONNECTING; + } + else if (client->connState == MQTT_DATA) { + if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) + return; + + if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) { + client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); + client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); + client->sendTimeout = MQTT_SEND_TIMOUT; + os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); +#ifdef CLIENT_SSL_ENABLE + if (client->security){ + espconn_secure_sent(client->pCon, dataBuffer, dataLen); + } + else +#endif + espconn_sent(client->pCon, dataBuffer, dataLen); + + client->mqtt_state.outbound_message = NULL; + return; + } + return; + } +} + +/** +* @brief MQTT initialization connection function +* @param client: MQTT_Client reference +* @param host: Domain or IP string +* @param port: Port to connect +* @param security: 1 for ssl, 0 for none +* @retval None +*/ +void ICACHE_FLASH_ATTR +MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security) { + os_printf("MQTT_InitConnection\n"); + uint8_t len = sizeof(MQTT_Client); + os_memset(mqttClient, 0, len); + + uint32_t temp = os_strlen(host); + mqttClient->host = (char*)os_zalloc(temp + 1); + os_strcpy(mqttClient->host, host); + mqttClient->host[temp] = 0; + + mqttClient->port = port; + mqttClient->security = security; +} + +/** +* @brief MQTT initialization mqtt client function +* @param client: MQTT_Client reference +* @param clientid: MQTT client id +* @param client_user:MQTT client user +* @param client_pass:MQTT client password +* @param client_pass:MQTT keep alive timer, in second +* @retval None +*/ +void ICACHE_FLASH_ATTR +MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) { + uint32_t temp; + os_printf("MQTT_InitClient\n"); + + os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); + + temp = os_strlen(client_id); + mqttClient->connect_info.client_id = (char*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.client_id, client_id); + mqttClient->connect_info.client_id[temp] = 0; + + temp = os_strlen(client_user); + mqttClient->connect_info.username = (char*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.username, client_user); + mqttClient->connect_info.username[temp] = 0; + + temp = os_strlen(client_pass); + mqttClient->connect_info.password = (char*)os_zalloc(temp + 1); + os_strcpy(mqttClient->connect_info.password, client_pass); + mqttClient->connect_info.password[temp] = 0; + + + mqttClient->connect_info.keepalive = keepAliveTime; + mqttClient->connect_info.clean_session = cleanSession; + + mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); + mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; + mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; + + mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); + + QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); + + system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); + system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); +} + +void ICACHE_FLASH_ATTR +MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, uint8_t will_qos, uint8_t will_retain) { + uint32_t temp; + temp = os_strlen((char*)will_topic); + mqttClient->connect_info.will_topic = (char*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->connect_info.will_topic, (char*)will_topic); + mqttClient->connect_info.will_topic[temp] = 0; + + temp = os_strlen((char*)will_msg); + mqttClient->connect_info.will_message = (char*)os_zalloc(temp + 1); + os_strcpy((char*)mqttClient->connect_info.will_message, (char*)will_msg); + mqttClient->connect_info.will_message[temp] = 0; + + + mqttClient->connect_info.will_qos = will_qos; + mqttClient->connect_info.will_retain = will_retain; +} + +/** +* @brief Begin connect to MQTT broker +* @param client: MQTT_Client reference +* @retval None +*/ +void ICACHE_FLASH_ATTR +MQTT_Connect(MQTT_Client* mqttClient) { + MQTT_Disconnect(mqttClient); + mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); + mqttClient->pCon->type = ESPCONN_TCP; + mqttClient->pCon->state = ESPCONN_NONE; + mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + mqttClient->pCon->proto.tcp->local_port = espconn_port(); + mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; + mqttClient->pCon->reverse = mqttClient; + espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); + espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); + + mqttClient->keepAliveTick = 0; + mqttClient->reconnectTick = 0; + + + os_timer_disarm(&mqttClient->mqttTimer); + os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); + os_timer_arm(&mqttClient->mqttTimer, 1000, 1); + + if (UTILS_StrToIP((const char *)mqttClient->host, (void*)&mqttClient->pCon->proto.tcp->remote_ip)) { + os_printf("MQTT-TCP: Connect to ip %s:%ld\n", mqttClient->host, mqttClient->port); +#ifdef CLIENT_SSL_ENABLE + if (mqttClient->security){ + espconn_secure_connect(mqttClient->pCon); + } + else +#endif + espconn_connect(mqttClient->pCon); + } + else { + os_printf("MQTT-TCP: Connect to domain %s:%ld\n", mqttClient->host, mqttClient->port); + espconn_gethostbyname(mqttClient->pCon, (const char *)mqttClient->host, &mqttClient->ip, mqtt_dns_found); + } + mqttClient->connState = TCP_CONNECTING; +} + +void ICACHE_FLASH_ATTR +MQTT_Disconnect(MQTT_Client* mqttClient) { + if (mqttClient->pCon) { + os_printf("Free memory\n"); + if (mqttClient->pCon->proto.tcp) + os_free(mqttClient->pCon->proto.tcp); + os_free(mqttClient->pCon); + mqttClient->pCon = NULL; + } + + os_timer_disarm(&mqttClient->mqttTimer); +} + +void ICACHE_FLASH_ATTR +MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb) { + mqttClient->connectedCb = connectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb) { + mqttClient->disconnectedCb = disconnectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnTcpDisconnected(MQTT_Client *mqttClient, MqttCallback tcpDisconnectedCb) +{ + mqttClient->tcpDisconnectedCb = tcpDisconnectedCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb) { + mqttClient->dataCb = dataCb; +} + +void ICACHE_FLASH_ATTR +MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb) { + mqttClient->publishedCb = publishedCb; +} diff --git a/mqtt/mqtt.h b/mqtt/mqtt.h new file mode 100644 index 0000000..9ad6825 --- /dev/null +++ b/mqtt/mqtt.h @@ -0,0 +1,144 @@ +/* mqtt.h +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ +#ifndef USER_AT_MQTT_H_ +#define USER_AT_MQTT_H_ + +#include +#include "mqtt_msg.h" +#include "queue.h" +#include + +typedef struct mqtt_event_data_t { + uint8_t type; + const char* topic; + const char* data; + uint16_t topic_length; + uint16_t data_length; + uint16_t data_offset; +} mqtt_event_data_t; + +typedef struct mqtt_state_t { + uint16_t port; + int auto_reconnect; + mqtt_connect_info_t* connect_info; + uint8_t* in_buffer; + uint8_t* out_buffer; + int in_buffer_length; + int out_buffer_length; + uint16_t message_length; + uint16_t message_length_read; + mqtt_message_t* outbound_message; + mqtt_connection_t mqtt_connection; + uint16_t pending_msg_id; + int pending_msg_type; + int pending_publish_qos; +} mqtt_state_t; + +typedef enum { + WIFI_INIT, + WIFI_CONNECTING, + WIFI_CONNECTING_ERROR, + WIFI_CONNECTED, + DNS_RESOLVE, + TCP_DISCONNECTED, + TCP_RECONNECT_REQ, + TCP_RECONNECT, + TCP_CONNECTING, + TCP_CONNECTING_ERROR, + TCP_CONNECTED, + MQTT_CONNECT_SEND, + MQTT_CONNECT_SENDING, + MQTT_SUBSCIBE_SEND, + MQTT_SUBSCIBE_SENDING, + MQTT_DATA, + MQTT_PUBLISH_RECV, + MQTT_PUBLISHING +} tConnState; + +typedef void (*MqttCallback)(uint32_t* args); +typedef void (*MqttDataCallback)(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t lengh); + +typedef struct { + struct espconn* pCon; + uint8_t security; + char* host; + uint32_t port; + ip_addr_t ip; + mqtt_state_t mqtt_state; + mqtt_connect_info_t connect_info; + MqttCallback connectedCb; + MqttCallback cmdConnectedCb; + MqttCallback disconnectedCb; + MqttCallback cmdDisconnectedCb; + MqttCallback tcpDisconnectedCb; + MqttCallback cmdTcpDisconnectedCb; + MqttCallback publishedCb; + MqttCallback cmdPublishedCb; + MqttDataCallback dataCb; + MqttDataCallback cmdDataCb; + ETSTimer mqttTimer; + uint32_t keepAliveTick; + uint32_t reconnectTick; + uint32_t sendTimeout; + tConnState connState; + QUEUE msgQueue; + void* user_data; +} MQTT_Client; + +#define SEC_NONSSL 0 +#define SEC_SSL 1 + +#define MQTT_FLAG_CONNECTED 1 +#define MQTT_FLAG_READY 2 +#define MQTT_FLAG_EXIT 4 + +#define MQTT_EVENT_TYPE_NONE 0 +#define MQTT_EVENT_TYPE_CONNECTED 1 +#define MQTT_EVENT_TYPE_DISCONNECTED 2 +#define MQTT_EVENT_TYPE_SUBSCRIBED 3 +#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 +#define MQTT_EVENT_TYPE_PUBLISH 5 +#define MQTT_EVENT_TYPE_PUBLISHED 6 +#define MQTT_EVENT_TYPE_EXITED 7 +#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 + +void MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security); +void MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession); +void MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, uint8_t will_qos, uint8_t will_retain); +void MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb); +void MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb); +void MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb); +void MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb); +bool MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); +void MQTT_Connect(MQTT_Client* mqttClient); +void MQTT_Disconnect(MQTT_Client* mqttClient); +bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain); + +#endif /* USER_AT_MQTT_H_ */ diff --git a/mqtt/mqtt_msg.c b/mqtt/mqtt_msg.c new file mode 100644 index 0000000..03da28e --- /dev/null +++ b/mqtt/mqtt_msg.c @@ -0,0 +1,451 @@ +/* +* Copyright (c) 2014, Stephen Robinson +* 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +* +*/ + +#include "mqtt_msg.h" +#define MQTT_MAX_FIXED_HEADER_SIZE 3 + +enum mqtt_connect_flag { + MQTT_CONNECT_FLAG_USERNAME = 1 << 7, + MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, + MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, + MQTT_CONNECT_FLAG_WILL = 1 << 2, + MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 +}; + +struct + __attribute__((__packed__)) mqtt_connect_variable_header { + uint8_t lengthMsb; + uint8_t lengthLsb; +#if defined(PROTOCOL_NAMEv31) + uint8_t magic[6]; +#elif defined(PROTOCOL_NAMEv311) + uint8_t magic[4]; +#else +#error "Please define protocol name" +#endif + uint8_t version; + uint8_t flags; + uint8_t keepaliveMsb; + uint8_t keepaliveLsb; +}; + +static int ICACHE_FLASH_ATTR +append_string(mqtt_connection_t* connection, const char* string, int len) { + if (connection->message.length + len + 2 > connection->buffer_length) + return -1; + + connection->buffer[connection->message.length++] = len >> 8; + connection->buffer[connection->message.length++] = len & 0xff; + memcpy(connection->buffer + connection->message.length, string, len); + connection->message.length += len; + + return len + 2; +} + +static uint16_t ICACHE_FLASH_ATTR +append_message_id(mqtt_connection_t* connection, uint16_t message_id) { + // If message_id is zero then we should assign one, otherwise + // we'll use the one supplied by the caller + while (message_id == 0) + message_id = ++connection->message_id; + + if (connection->message.length + 2 > connection->buffer_length) + return 0; + + connection->buffer[connection->message.length++] = message_id >> 8; + connection->buffer[connection->message.length++] = message_id & 0xff; + + return message_id; +} + +static int ICACHE_FLASH_ATTR +init_message(mqtt_connection_t* connection) { + connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; + return MQTT_MAX_FIXED_HEADER_SIZE; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR +fail_message(mqtt_connection_t* connection) { + connection->message.data = connection->buffer; + connection->message.length = 0; + return &connection->message; +} + +static mqtt_message_t* ICACHE_FLASH_ATTR +fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) { + int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; + + if (remaining_length > 127) { + connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[1] = 0x80 | (remaining_length % 128); + connection->buffer[2] = remaining_length / 128; + connection->message.length = remaining_length + 3; + connection->message.data = connection->buffer; + } + else { + connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[2] = remaining_length; + connection->message.length = remaining_length + 2; + connection->message.data = connection->buffer + 1; + } + + return &connection->message; +} + +void ICACHE_FLASH_ATTR +mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) { + uint8_t len = sizeof(connection); + memset(connection, '\0', len); + connection->buffer = buffer; + connection->buffer_length = buffer_length; +} + +int ICACHE_FLASH_ATTR +mqtt_get_total_length(uint8_t* buffer, uint16_t length) { + int i; + int totlen = 0; + + for (i = 1; i < length; ++i) { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) { + ++i; + break; + } + } + totlen += i; + + return totlen; +} + +const char* ICACHE_FLASH_ATTR +mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) { + int i; + int totlen = 0; + int topiclen; + + for (i = 1; i < *length; ++i) { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen > *length) + return NULL; + + *length = topiclen; + return (const char*)(buffer + i); +} + +const char* ICACHE_FLASH_ATTR +mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) { + int i; + int totlen = 0; + int topiclen; + + for (i = 1; i < *length; ++i) { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if ((buffer[i] & 0x80) == 0) { + ++i; + break; + } + } + totlen += i; + + if (i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= *length) { + *length = 0; + return NULL; + } + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) { + if (i + 2 >= *length) + return NULL; + i += 2; + } + + if (totlen < i) + return NULL; + + if (totlen <= *length) + *length = totlen - i; + else + *length = *length - i; + return (const char*)(buffer + i); +} + +uint16_t ICACHE_FLASH_ATTR +mqtt_get_id(uint8_t* buffer, uint16_t length) { + if (length < 1) + return 0; + + switch (mqtt_get_type(buffer)) { + case MQTT_MSG_TYPE_PUBLISH: { + int i; + int topiclen; + + for (i = 1; i < length; ++i) { + if ((buffer[i] & 0x80) == 0) { + ++i; + break; + } + } + + if (i + 2 >= length) + return 0; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if (i + topiclen >= length) + return 0; + i += topiclen; + + if (mqtt_get_qos(buffer) > 0) { + if (i + 2 >= length) + return 0; + //i += 2; + } + else { + return 0; + } + + return (buffer[i] << 8) | buffer[i + 1]; + } + case MQTT_MSG_TYPE_PUBACK: + case MQTT_MSG_TYPE_PUBREC: + case MQTT_MSG_TYPE_PUBREL: + case MQTT_MSG_TYPE_PUBCOMP: + case MQTT_MSG_TYPE_SUBACK: + case MQTT_MSG_TYPE_UNSUBACK: + case MQTT_MSG_TYPE_SUBSCRIBE: { + // This requires the remaining length to be encoded in 1 byte, + // which it should be. + if (length >= 4 && (buffer[1] & 0x80) == 0) + return (buffer[2] << 8) | buffer[3]; + else + return 0; + } + + default: + return 0; + } +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) { + struct mqtt_connect_variable_header* variable_header; + + init_message(connection); + + if (connection->message.length + sizeof(*variable_header) > connection->buffer_length) + return fail_message(connection); + variable_header = (void*)(connection->buffer + connection->message.length); + connection->message.length += sizeof(*variable_header); + + variable_header->lengthMsb = 0; +#if defined(PROTOCOL_NAMEv31) + variable_header->lengthLsb = 6; + memcpy(variable_header->magic, "MQIsdp", 6); + variable_header->version = 3; +#elif defined(PROTOCOL_NAMEv311) + variable_header->lengthLsb = 4; + memcpy(variable_header->magic, "MQTT", 4); + variable_header->version = 4; +#else +#error "Please define protocol name" +#endif + + variable_header->flags = 0; + variable_header->keepaliveMsb = info->keepalive >> 8; + variable_header->keepaliveLsb = info->keepalive & 0xff; + + if (info->clean_session) + variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; + + if (info->client_id != NULL && info->client_id[0] != '\0') { + if (append_string(connection, info->client_id, strlen(info->client_id)) < 0) + return fail_message(connection); + } + else + return fail_message(connection); + + if (info->will_topic != NULL && info->will_topic[0] != '\0') { + if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) + return fail_message(connection); + + if (append_string(connection, info->will_message, strlen(info->will_message)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_WILL; + if (info->will_retain) + variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; + variable_header->flags |= (info->will_qos & 3) << 3; + } + + if (info->username != NULL && info->username[0] != '\0') { + if (append_string(connection, info->username, strlen(info->username)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; + } + + if (info->password != NULL && info->password[0] != '\0') { + if (append_string(connection, info->password, strlen(info->password)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; + } + + return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) { + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (qos > 0) { + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + } + else + *message_id = 0; + + if (connection->message.length + data_length > connection->buffer_length) + return fail_message(connection); + memcpy(connection->buffer + connection->message.length, data, data_length); + connection->message.length += data_length; + + return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) { + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) { + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) { + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) { + init_message(connection); + if (append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) { + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if (connection->message.length + 1 > connection->buffer_length) + return fail_message(connection); + connection->buffer[connection->message.length++] = qos; + + return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) { + init_message(connection); + + if (topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if ((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if (append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pingreq(mqtt_connection_t* connection) { + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_pingresp(mqtt_connection_t* connection) { + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); +} + +mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_msg_disconnect(mqtt_connection_t* connection) { + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); +} diff --git a/mqtt/mqtt_msg.h b/mqtt/mqtt_msg.h new file mode 100644 index 0000000..bf305df --- /dev/null +++ b/mqtt/mqtt_msg.h @@ -0,0 +1,117 @@ +/* +* Copyright (c) 2014, Stephen Robinson +* 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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. +* +*/ + +#ifndef MQTT_MSG_H +#define MQTT_MSG_H +#include + +enum mqtt_message_type { + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 +}; + +typedef struct mqtt_message { + uint8_t* data; + uint16_t length; + +} mqtt_message_t; + +typedef struct mqtt_connection { + mqtt_message_t message; + + uint16_t message_id; + uint8_t* buffer; + uint16_t buffer_length; + +} mqtt_connection_t; + +typedef struct mqtt_connect_info { + char* client_id; + char* username; + char* password; + char* will_topic; + char* will_message; + uint32_t keepalive; + uint8_t will_qos; + uint8_t will_retain; + uint8_t clean_session; + +} mqtt_connect_info_t; + + +static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { + return (buffer[0] & 0xf0) >> 4; +} + +static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { + return (buffer[0] & 0x08) >> 3; +} + +static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { + return (buffer[0] & 0x06) >> 1; +} + +static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { + return (buffer[0] & 0x01); +} + +void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); +int mqtt_get_total_length(uint8_t* buffer, uint16_t length); +const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); +const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); +uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length); + +mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); +mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); +mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); +mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); +mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection); +mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection); +mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection); + +#endif // MQTT_MSG_H + diff --git a/mqtt/proto.c b/mqtt/proto.c new file mode 100644 index 0000000..69b5367 --- /dev/null +++ b/mqtt/proto.c @@ -0,0 +1,86 @@ +#include "proto.h" + +int8_t ICACHE_FLASH_ATTR +PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize) { + parser->buf = buf; + parser->bufSize = bufSize; + parser->dataLen = 0; + parser->callback = completeCallback; + parser->isEsc = 0; + return 0; +} + +int8_t ICACHE_FLASH_ATTR +PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value) { + switch (value) { + case 0x7D: + parser->isEsc = 1; + break; + + case 0x7E: + parser->dataLen = 0; + parser->isEsc = 0; + parser->isBegin = 1; + break; + + case 0x7F: + if (parser->callback != NULL) + parser->callback(); + parser->isBegin = 0; + return 0; + break; + + default: + if (parser->isBegin == 0) break; + + if (parser->isEsc) { + value ^= 0x20; + parser->isEsc = 0; + } + + if (parser->dataLen < parser->bufSize) + parser->buf[parser->dataLen++] = value; + + break; + } + return -1; +} + +int16_t ICACHE_FLASH_ATTR +PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen) { + uint8_t c; + + PROTO_PARSER proto; + PROTO_Init(&proto, NULL, bufOut, maxBufLen); + while (RINGBUF_Get(rb, &c) == 0) { + if (PROTO_ParseByte(&proto, c) == 0) { + *len = proto.dataLen; + return 0; + } + } + return -1; +} + +int16_t ICACHE_FLASH_ATTR +PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len) { + uint16_t i = 2; + if (RINGBUF_Put(rb, 0x7E) == -1) return -1; + while (len--) { + switch (*packet) { + case 0x7D: + case 0x7E: + case 0x7F: + if (RINGBUF_Put(rb, 0x7D) == -1) return -1; + if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; + i += 2; + break; + default: + if (RINGBUF_Put(rb, *packet++) == -1) return -1; + i++; + break; + } + } + if (RINGBUF_Put(rb, 0x7F) == -1) return -1; + + return i; +} diff --git a/mqtt/proto.h b/mqtt/proto.h new file mode 100644 index 0000000..67b3e26 --- /dev/null +++ b/mqtt/proto.h @@ -0,0 +1,21 @@ +#ifndef _PROTO_H_ +#define _PROTO_H_ +#include +#include "ringbuf.h" + +typedef void (PROTO_PARSE_CALLBACK)(); + +typedef struct { + uint8_t* buf; + uint16_t bufSize; + uint16_t dataLen; + uint8_t isEsc; + uint8_t isBegin; + PROTO_PARSE_CALLBACK* callback; +} PROTO_PARSER; + +int8_t PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize); +int16_t PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len); +int8_t PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value); +int16_t PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen); +#endif diff --git a/mqtt/queue.c b/mqtt/queue.c new file mode 100644 index 0000000..39c4790 --- /dev/null +++ b/mqtt/queue.c @@ -0,0 +1,53 @@ +/* str_queue.c +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ +#include "queue.h" + +void ICACHE_FLASH_ATTR +QUEUE_Init(QUEUE* queue, int bufferSize) { + queue->buf = (uint8_t*)os_zalloc(bufferSize); + RINGBUF_Init(&queue->rb, queue->buf, bufferSize); +} + +int32_t ICACHE_FLASH_ATTR +QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len) { + return PROTO_AddRb(&queue->rb, buffer, len); +} + +int32_t ICACHE_FLASH_ATTR +QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) { + return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); +} + +bool ICACHE_FLASH_ATTR +QUEUE_IsEmpty(QUEUE* queue) { + if (queue->rb.fill_cnt <= 0) + return TRUE; + return FALSE; +} diff --git a/mqtt/queue.h b/mqtt/queue.h new file mode 100644 index 0000000..bdbc503 --- /dev/null +++ b/mqtt/queue.h @@ -0,0 +1,46 @@ +/* str_queue.h -- +* +* Copyright (c) 2014-2015, Tuan PM +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* * 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. +* * Neither the name of Redis 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ + +#ifndef USER_QUEUE_H_ +#define USER_QUEUE_H_ +#include +#include "proto.h" +#include "ringbuf.h" + +typedef struct { + uint8_t* buf; + RINGBUF rb; +} QUEUE; + +void QUEUE_Init(QUEUE* queue, int bufferSize); +int32_t QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len); +int32_t QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); +bool QUEUE_IsEmpty(QUEUE* queue); +#endif /* USER_QUEUE_H_ */ diff --git a/mqtt/ringbuf.c b/mqtt/ringbuf.c new file mode 100644 index 0000000..a0cc782 --- /dev/null +++ b/mqtt/ringbuf.c @@ -0,0 +1,63 @@ +#include "ringbuf.h" + +/** +* \brief init a RINGBUF object +* \param r pointer to a RINGBUF object +* \param buf pointer to a byte array +* \param size size of buf +* \return 0 if successfull, otherwise failed +*/ +int16_t ICACHE_FLASH_ATTR +RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size) { + if (r == NULL || buf == NULL || size < 2) return -1; + + r->p_o = r->p_r = r->p_w = buf; + r->fill_cnt = 0; + r->size = size; + + return 0; +} + +/** +* \brief put a character into ring buffer +* \param r pointer to a ringbuf object +* \param c character to be put +* \return 0 if successfull, otherwise failed +*/ +int16_t ICACHE_FLASH_ATTR +RINGBUF_Put(RINGBUF* r, uint8_t c) { + if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation + + + r->fill_cnt++; // increase filled slots count, this should be atomic operation + + + *r->p_w++ = c; // put character into buffer + + if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass + r->p_w = r->p_o; // the physical boundary + + return 0; +} + +/** +* \brief get a character from ring buffer +* \param r pointer to a ringbuf object +* \param c read character +* \return 0 if successfull, otherwise failed +*/ +int16_t ICACHE_FLASH_ATTR +RINGBUF_Get(RINGBUF* r, uint8_t* c) { + if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation + + + r->fill_cnt--; // decrease filled slots count + + + *c = *r->p_r++; // get the character out + + if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass + r->p_r = r->p_o; // the physical boundary + + return 0; +} diff --git a/mqtt/ringbuf.h b/mqtt/ringbuf.h new file mode 100644 index 0000000..d504d53 --- /dev/null +++ b/mqtt/ringbuf.h @@ -0,0 +1,17 @@ +#ifndef _RING_BUF_H_ +#define _RING_BUF_H_ + +#include + +typedef struct { + uint8_t* p_o; /**< Original pointer */ + uint8_t* volatile p_r; /**< Read pointer */ + uint8_t* volatile p_w; /**< Write pointer */ + volatile int32_t fill_cnt; /**< Number of filled slots */ + int32_t size; /**< Buffer size */ +} RINGBUF; + +int16_t RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size); +int16_t RINGBUF_Put(RINGBUF* r, uint8_t c); +int16_t RINGBUF_Get(RINGBUF* r, uint8_t* c); +#endif diff --git a/user/user_main.c b/user/user_main.c index 54ca59e..5738807 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -1,165 +1,70 @@ -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * Jeroen Domburg wrote this file. As long as you retain - * this notice you can do whatever you want with this stuff. If we meet some day, - * and you think this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - * Heavily modified and enhanced by Thorsten von Eicken in 2015 - * ---------------------------------------------------------------------------- - */ - - #include -#include "httpd.h" -#include "httpdespfs.h" -#include "cgi.h" -#include "cgiwifi.h" -#include "cgipins.h" -#include "cgitcp.h" -#include "cgiflash.h" -#include "auth.h" -#include "espfs.h" -#include "uart.h" -#include "serbridge.h" -#include "status.h" -#include "serled.h" -#include "console.h" -#include "config.h" -#include "log.h" -#include +#include +#include -//#define SHOW_HEAP_USE +MQTT_Client mqttClient; -//Function that tells the authentication system what users/passwords live on the system. -//This is disabled in the default build; if you want to try it, enable the authBasic line in -//the builtInUrls below. -int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { - if (no==0) { - os_strcpy(user, "admin"); - os_strcpy(pass, "s3cr3t"); - return 1; -//Add more users this way. Check against incrementing no for each user added. -// } else if (no==1) { -// os_strcpy(user, "user1"); -// os_strcpy(pass, "something"); -// return 1; - } - return 0; +void ICACHE_FLASH_ATTR +mqttConnectedCb(uint32_t *args) { + MQTT_Client* client = (MQTT_Client*)args; + MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); } +void ICACHE_FLASH_ATTR +mqttDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Disconnected\n"); +} -/* -This is the main url->function dispatching data struct. -In short, it's a struct with various URLs plus their handlers. The handlers can -be 'standard' CGI functions you wrote, or 'special' CGIs requiring an argument. -They can also be auth-functions. An asterisk will match any url starting with -everything before the asterisks; "*" matches everything. The list will be -handled top-down, so make sure to put more specific rules above the more -general ones. Authorization things (like authBasic) act as a 'barrier' and -should be placed above the URLs they protect. -*/ -HttpdBuiltInUrl builtInUrls[]={ - {"/", cgiRedirect, "/home.html"}, - {"/menu", cgiMenu, NULL}, - {"/flash/next", cgiGetFirmwareNext, NULL}, - {"/flash/upload", cgiUploadFirmware, NULL}, - {"/flash/reboot", cgiRebootFirmware, NULL}, - //{"/home.html", cgiEspFsHtml, NULL}, - //{"/log.html", cgiEspFsHtml, NULL}, - {"/log/text", ajaxLog, NULL}, - {"/log/dbg", ajaxLogDbg, NULL}, - //{"/console.html", cgiEspFsHtml, NULL}, - {"/console/reset", ajaxConsoleReset, NULL}, - {"/console/baud", ajaxConsoleBaud, NULL}, - {"/console/text", ajaxConsole, NULL}, - - //Routines to make the /wifi URL and everything beneath it work. - -//Enable the line below to protect the WiFi configuration with an username/password combo. -// {"/wifi/*", authBasic, myPassFn}, - - {"/wifi", cgiRedirect, "/wifi/wifi.html"}, - {"/wifi/", cgiRedirect, "/wifi/wifi.html"}, - //{"/wifi/wifi.html", cgiEspFsHtml, NULL}, - {"/wifi/info", cgiWifiInfo, NULL}, - {"/wifi/scan", cgiWiFiScan, NULL}, - {"/wifi/connect", cgiWiFiConnect, NULL}, - {"/wifi/connstatus", cgiWiFiConnStatus, NULL}, - {"/wifi/setmode", cgiWiFiSetMode, NULL}, - {"/wifi/special", cgiWiFiSpecial, NULL}, - {"/pins", cgiPins, NULL}, - {"/tcpclient", cgiTcp, NULL}, - - {"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem - {NULL, NULL, NULL} -}; - - -//#define SHOW_HEAP_USE - -#ifdef SHOW_HEAP_USE -static ETSTimer prHeapTimer; - -static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { - os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); +void ICACHE_FLASH_ATTR +mqttTcpDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT TCP Disconnected\n"); } -#endif -void user_rf_pre_init(void) { +void ICACHE_FLASH_ATTR +mqttPublishedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Published\n"); } -// address of espfs binary blob -extern uint32_t _binary_espfs_img_start; +void ICACHE_FLASH_ATTR +mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { + char *topicBuf = (char*)os_zalloc(topic_len + 1); + char *dataBuf = (char*)os_zalloc(data_len + 1); -static char *rst_codes[] = { - "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", -}; +// MQTT_Client* client = (MQTT_Client*)args; -# define VERS_STR_STR(V) #V -# define VERS_STR(V) VERS_STR_STR(V) -char *esp_link_version = VERS_STR(VERSION); + os_memcpy(topicBuf, topic, topic_len); + topicBuf[topic_len] = 0; -//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. -void user_init(void) { - // get the flash config so we know how to init things - //configWipe(); // uncomment to reset the config for testing purposes - bool restoreOk = configRestore(); - // init gpio pin registers - gpio_init(); - // init UART - uart_init(flashConfig.baud_rate, 115200); - logInit(); // must come after init of uart - // say hello (leave some time to cause break in TX after boot loader's msg - os_delay_us(10000L); - os_printf("\n\n** %s\n", esp_link_version); - os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); - // Status LEDs - statusInit(); - serledInit(); - // Wifi - wifiInit(); - // init the flash filesystem with the html stuff - espFsInit(&_binary_espfs_img_start); - //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); - //os_printf("espFsInit %s\n", res?"ERR":"ok"); - // mount the http handlers - httpdInit(builtInUrls, 80); - // init the wifi-serial transparent bridge (port 23) - serbridgeInit(23); - uart_add_recv_cb(&serbridgeUartCb); -#ifdef SHOW_HEAP_USE - os_timer_disarm(&prHeapTimer); - os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); - os_timer_arm(&prHeapTimer, 10000, 1); -#endif + os_memcpy(dataBuf, data, data_len); + dataBuf[data_len] = 0; - struct rst_info *rst_info = system_get_rst_info(); - os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); - 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("Receive topic: %s, data: %s\n", topicBuf, dataBuf); + os_free(topicBuf); + os_free(dataBuf); +} - os_printf("** esp-link ready\n"); +void ICACHE_FLASH_ATTR +wifiStateChangeCb(uint8_t status) +{ + if (status == wifiGotIP && mqttClient.connState != TCP_CONNECTING){ + MQTT_Connect(&mqttClient); + } + else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING){ + MQTT_Disconnect(&mqttClient); + } } + +void init() { + wifiAddStateChangeCb(wifiStateChangeCb); + MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); + MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); + MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); + MQTT_OnConnected(&mqttClient, mqttConnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); + MQTT_OnPublished(&mqttClient, mqttPublishedCb); + MQTT_OnData(&mqttClient, mqttDataCb); +} \ No newline at end of file From 47b33f837fdf1193a1d4f533a9bb6da02ad347ec Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sat, 29 Aug 2015 16:24:44 -0500 Subject: [PATCH 065/104] merge error fix --- esp-link/cgiwifi.c | 19 +++++++++++++++++-- esp-link/cgiwifi.h | 1 - 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index 42ddf24..2354b13 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -24,7 +24,8 @@ Cgi/template routines for the /wifi url. //#define SLEEP_MODE LIGHT_SLEEP_T #define SLEEP_MODE MODEM_SLEEP_T -// ===== wifi status change callback +// ===== wifi status change callbacks +static WifiStateChangeCb wifi_state_change_cb[4]; uint8_t wifiState = wifiIsDisconnected; // reasons for which a connection failed @@ -88,7 +89,21 @@ static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) { default: break; } - if (wifiStatusCb) (*wifiStatusCb)(wifiState); + + for (int i = 0; i < 4; i++) { + if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState); + } +} + +void ICACHE_FLASH_ATTR +wifiAddStateChangeCb(WifiStateChangeCb cb) { + for (int i = 0; i < 4; i++) { + if (wifi_state_change_cb[i] == NULL) { + wifi_state_change_cb[i] = cb; + return; + } + } + os_printf("WIFI: max state change cb count exceeded\n"); } // ===== wifi scanning diff --git a/esp-link/cgiwifi.h b/esp-link/cgiwifi.h index 3f7fe09..a2782a5 100644 --- a/esp-link/cgiwifi.h +++ b/esp-link/cgiwifi.h @@ -17,6 +17,5 @@ void wifiInit(void); void wifiAddStateChangeCb(WifiStateChangeCb cb); extern uint8_t wifiState; -//extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes #endif From ef01fc76b7e59f0e760806b62746be97a3d02aa7 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 29 Aug 2015 13:51:08 -0700 Subject: [PATCH 066/104] makefile tweak --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 451d7f3..7f47c20 100644 --- a/Makefile +++ b/Makefile @@ -356,8 +356,8 @@ release: all $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80 $(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-$(BRANCH) - $(Q) tar zcf esp-link-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link-$(BRANCH) - $(Q) echo "Release file: esp-link-$(BRANCH)-$(FLASH_SIZE).tgz" + $(Q) tar zcf esp-link-$(BRANCH).tgz -C release esp-link-$(BRANCH) + $(Q) echo "Release file: esp-link-$(BRANCH).tgz" $(Q) rm -rf release clean: From 0bb7dba4988f9617409e0f6f662534c8a092086a Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 29 Aug 2015 19:01:35 -0700 Subject: [PATCH 067/104] readme update --- README.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 34d3090..d90fa9b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,28 @@ Many thanks to https://github.com/brunnels for contributions around the espduino For quick support and questions: [![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) +Esp-link uses +------------- +The simplest use of esp-link is as a transparent serial to wifi bridge. You can flash an attached +uC over wifi and you can watch the uC's serial debug output by connecting to port 23 or looking +at the uC Console web page. + +The next level is to use the outbound connectivity of esp-link in the uC code. For example, the +uC can use REST requests to services like thingspeak.com to send sensor values that then get +stored and plotted by the external service. +The uC can also use REST requests to retrieve simple configuration +information or push other forms of notifications. (MQTT functionality is forthcoming.) + +An additional option is to add code to esp-link to customize it and put all the communication +code into esp-link and only keep simple sensor/actuator control in the attached uC. In this +mode the attached uC sends custom commands to esp-link with sensor/acturator info and +registers a set of callbacks with esp-link that control sensors/actuators. This way, custom +commands in esp-link can receive MQTT messages, make simple callbacks into the uC to get sensor +values or change actuators, and then respond back with MQTT. The way this is architected is that +the attached uC registers callbacks at start-up such that the code in the esp doesn't need to +know which exact sensors/actuators the attached uC has, it learns thta through the initial +callback registration. + Eye Candy --------- These screen shots show the Home page, the Wifi configuration page, the console for the @@ -237,7 +259,22 @@ 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... +Outbound HTTP REST requests +--------------------------- +The V2 versions of esp-link support the espduino SLIP protocol that supports simple outbound +HTTP REST requests. The SLIP protocol consists of commands with binary arguments sent from the +attached microcontroller to the esp8266, which then performs the command and responds back. +The responses back use a callback address in the attached microcontroller code, i.e., the +command sent by the uC contains a callback address and the response from the esp8266 starts +with that callback address. This enables asynchronous communication where esp-link can notify the +uC when requests complete or when other actions happen, such as wifi connectivity status changes. +Support for MQTT is forthcoming. + +You can find a demo sketch in a fork of the espduino library at +https://github.com/tve/espduino in the +[examples/demo folder](https://github.com/tve/espduino/tree/master/espduino/examples/demo). + Contact ------- If you find problems with esp-link, please create a github issue. If you have a question, please -use the gitter link at the top of this page. +use the gitter chat link at the top of this page. From bc3856334d7f596d3aa433f69d8c6a8cc4b30c2e Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Sun, 30 Aug 2015 12:03:18 -0500 Subject: [PATCH 068/104] Made some modules optional --- Makefile | 29 +++++++-- cmd/cmd.h | 4 +- cmd/handlers.c | 37 ++++++----- esp-link.vcxproj | 8 +-- esp-link/cgi.c | 42 +++++++++++-- esp-link/cgi.h | 2 + include/esp8266.h | 8 ++- include/user_config.h | 10 +++ mqtt/mqtt.h | 1 - {cmd => mqtt}/mqtt_cmd.c | 0 {cmd => mqtt}/mqtt_cmd.h | 0 {cmd => rest}/rest.c | 39 ------------ {cmd => rest}/rest.h | 1 - user/user_main.c | 128 +++++++++++++++++++-------------------- 14 files changed, 166 insertions(+), 143 deletions(-) rename {cmd => mqtt}/mqtt_cmd.c (100%) rename {cmd => mqtt}/mqtt_cmd.h (100%) rename {cmd => rest}/rest.c (93%) rename {cmd => rest}/rest.h (93%) diff --git a/Makefile b/Makefile index 451d7f3..113944c 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ # # Makefile heavily adapted to esp-link and wireless flashing by Thorsten von Eicken # Original from esphttpd and others... +#VERBOSE=1 # --------------- toolchain configuration --------------- @@ -128,6 +129,8 @@ GZIP_COMPRESSION ?= yes COMPRESS_W_YUI ?= yes YUI-COMPRESSOR ?= yuicompressor-2.4.8.jar +# Optional Modules +MODULES ?= rest # -------------- End of config options ------------- @@ -142,15 +145,29 @@ TARGET = httpd # espressif tool to concatenate sections for OTA upload using bootloader v1.2+ APPGEN_TOOL ?= gen_appbin.py +CFLAGS= + +# set defines for optional modules +ifneq (,$(findstring mqtt,$(MODULES))) + CFLAGS += -DMQTT +endif + +ifneq (,$(findstring rest,$(MODULES))) + CFLAGS += -DREST +endif + # which modules (subdirectories) of the project to include in compiling -MODULES = espfs httpd user serial cmd mqtt esp-link +LIBRARIES_DIR = libraries +MODULES += espfs httpd user serial cmd esp-link +MODULES += $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*)) +EXTRA_INCDIR =include . EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK LIBS = c gcc hal phy pp net80211 wpa main lwip # compiler flags using during compilation of source files -CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ +CFLAGS += -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ -nostdlib -mlongcalls -mtext-section-literals -ffunction-sections -fdata-sections \ -D__ets__ -DICACHE_FLASH -D_STDINT_H -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) \ -DMCU_RESET_PIN=$(MCU_RESET_PIN) -DMCU_ISP_PIN=$(MCU_ISP_PIN) \ @@ -167,7 +184,7 @@ LD_SCRIPT2 := build/eagle.esphttpd2.v6.ld # various paths from the SDK used in this project SDK_LIBDIR = lib -SDK_LDDIR = ld +SDK_LDDIR = ld SDK_INCDIR = include include/json SDK_TOOLSDIR = tools @@ -175,8 +192,8 @@ SDK_TOOLSDIR = tools CC := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc AR := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-ar LD := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc -OBJCP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objcopy -OBJDP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objdump +OBJCP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objcopy +OBJDP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objdump #### @@ -214,7 +231,7 @@ CFLAGS += -DGZIP_COMPRESSION endif ifeq ("$(CHANGE_TO_STA)","yes") -CFLAGS += -DCHANGE_TO_STA +CFLAGS += -DCHANGE_TO_STA endif vpath %.c $(SRC_DIR) diff --git a/cmd/cmd.h b/cmd/cmd.h index 97c8236..2d07e1c 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -55,8 +55,8 @@ typedef enum { CMD_REST_REQUEST, CMD_REST_SETHEADER, CMD_REST_EVENTS, - CMD_ADD_CALLBACK, // 15 - CMD_SENSOR_EVENTS + CMD_CB_ADD, // 15 + CMD_CB_EVENTS } CmdName; typedef uint32_t (*cmdfunc_t)(CmdPacket *cmd); diff --git a/cmd/handlers.c b/cmd/handlers.c index b54773f..af6eab7 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -4,12 +4,13 @@ #include "esp8266.h" #include "cmd.h" -#include "rest.h" -#include "crc16.h" -#include "serbridge.h" -#include "uart.h" -#include "cgiwifi.h" -#include "mqtt_cmd.h" +#include +#ifdef MQTT +#include +#endif +#ifdef REST +#include +#endif static uint32_t CMD_Null(CmdPacket *cmd); static uint32_t CMD_IsReady(CmdPacket *cmd); @@ -27,22 +28,20 @@ const CmdList commands[] = { {CMD_RESET, CMD_Reset}, {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}, - */ - +#ifdef MQTT + {CMD_MQTT_SETUP, MQTTCMD_Setup}, + {CMD_MQTT_CONNECT, MQTTCMD_Connect}, + {CMD_MQTT_DISCONNECT, MQTTCMD_Disconnect}, + {CMD_MQTT_PUBLISH, MQTTCMD_Publish}, + {CMD_MQTT_SUBSCRIBE , MQTTCMD_Subscribe}, + {CMD_MQTT_LWT, MQTTCMD_Lwt}, +#endif +#ifdef REST {CMD_REST_SETUP, REST_Setup}, {CMD_REST_REQUEST, REST_Request}, {CMD_REST_SETHEADER, REST_SetHeader}, - - {CMD_ADD_CALLBACK, CMD_AddCallback }, - +#endif + { CMD_CB_ADD, CMD_AddCallback }, {CMD_NULL, NULL} }; diff --git a/esp-link.vcxproj b/esp-link.vcxproj index f371f1a..72b0ea6 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -65,8 +65,8 @@ - - + + @@ -108,8 +108,8 @@ - - + + diff --git a/esp-link/cgi.c b/esp-link/cgi.c index 8f87d38..65cab94 100644 --- a/esp-link/cgi.c +++ b/esp-link/cgi.c @@ -12,11 +12,7 @@ Some random cgi routines. * Heavily modified and enhanced by Thorsten von Eicken in 2015 * ---------------------------------------------------------------------------- */ - - -#include #include "cgi.h" -#include "espfs.h" void ICACHE_FLASH_ATTR jsonHeader(HttpdConnData *connData, int code) { @@ -28,6 +24,44 @@ jsonHeader(HttpdConnData *connData, int code) { httpdEndHeaders(connData); } +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; +} + #define TOKEN(x) (os_strcmp(token, x) == 0) #if 0 // Handle system information variables and print their value, returns the number of diff --git a/esp-link/cgi.h b/esp-link/cgi.h index b5690cf..200a176 100644 --- a/esp-link/cgi.h +++ b/esp-link/cgi.h @@ -1,9 +1,11 @@ #ifndef CGI_H #define CGI_H +#include #include "httpd.h" void jsonHeader(HttpdConnData *connData, int code); int cgiMenu(HttpdConnData *connData); +uint8_t UTILS_StrToIP(const char* str, void *ip); #endif diff --git a/include/esp8266.h b/include/esp8266.h index c3d0c40..3e7eeb9 100644 --- a/include/esp8266.h +++ b/include/esp8266.h @@ -1,4 +1,7 @@ // Combined include file for esp8266 +#ifndef _ESP8266_H_ +#define _ESP8266_H_ + #include #include #include @@ -16,10 +19,9 @@ #include "espmissingincludes.h" #include "uart_hw.h" -extern char* esp_link_version; - -void ICACHE_FLASH_ATTR init(void); #ifdef __WIN32__ #include <_mingw.h> #endif + +#endif // _ESP8266_H_ \ No newline at end of file diff --git a/include/user_config.h b/include/user_config.h index 7bdfd38..3be06f4 100644 --- a/include/user_config.h +++ b/include/user_config.h @@ -1,5 +1,9 @@ #ifndef _USER_CONFIG_H_ #define _USER_CONFIG_H_ +#include +#ifdef __WIN32__ +#include <_mingw.h> +#endif #define MQTT_RECONNECT_TIMEOUT 5 // seconds #define MQTT_BUF_SIZE 1024 @@ -17,4 +21,10 @@ #define PROTOCOL_NAMEv31 // MQTT version 3.1 compatible with Mosquitto v0.15 //PROTOCOL_NAMEv311 // MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/ +extern char* esp_link_version; + +extern uint8_t UTILS_StrToIP(const char* str, void *ip); + +extern void ICACHE_FLASH_ATTR init(void); + #endif \ No newline at end of file diff --git a/mqtt/mqtt.h b/mqtt/mqtt.h index 9ad6825..00c432a 100644 --- a/mqtt/mqtt.h +++ b/mqtt/mqtt.h @@ -33,7 +33,6 @@ #include #include "mqtt_msg.h" #include "queue.h" -#include typedef struct mqtt_event_data_t { uint8_t type; diff --git a/cmd/mqtt_cmd.c b/mqtt/mqtt_cmd.c similarity index 100% rename from cmd/mqtt_cmd.c rename to mqtt/mqtt_cmd.c diff --git a/cmd/mqtt_cmd.h b/mqtt/mqtt_cmd.h similarity index 100% rename from cmd/mqtt_cmd.h rename to mqtt/mqtt_cmd.h diff --git a/cmd/rest.c b/rest/rest.c similarity index 93% rename from cmd/rest.c rename to rest/rest.c index 3556681..a9cd298 100644 --- a/cmd/rest.c +++ b/rest/rest.c @@ -392,42 +392,3 @@ fail: os_printf("\n"); return 0; } - -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/rest/rest.h similarity index 93% rename from cmd/rest.h rename to rest/rest.h index 9c6cd5c..7161e1f 100644 --- a/cmd/rest.h +++ b/rest/rest.h @@ -36,6 +36,5 @@ typedef struct { uint32_t REST_Setup(CmdPacket *cmd); uint32_t REST_Request(CmdPacket *cmd); uint32_t REST_SetHeader(CmdPacket *cmd); -uint8_t UTILS_StrToIP(const char* str, void *ip); #endif /* MODULES_INCLUDE_API_H_ */ diff --git a/user/user_main.c b/user/user_main.c index 5738807..e75876b 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -1,70 +1,70 @@ #include -#include -#include +//#include +//#include -MQTT_Client mqttClient; - -void ICACHE_FLASH_ATTR -mqttConnectedCb(uint32_t *args) { - MQTT_Client* client = (MQTT_Client*)args; - MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); -} - -void ICACHE_FLASH_ATTR -mqttDisconnectedCb(uint32_t *args) { -// MQTT_Client* client = (MQTT_Client*)args; - os_printf("MQTT Disconnected\n"); -} - -void ICACHE_FLASH_ATTR -mqttTcpDisconnectedCb(uint32_t *args) { +//MQTT_Client mqttClient; +// +//void ICACHE_FLASH_ATTR +//mqttConnectedCb(uint32_t *args) { // MQTT_Client* client = (MQTT_Client*)args; - os_printf("MQTT TCP Disconnected\n"); -} - -void ICACHE_FLASH_ATTR -mqttPublishedCb(uint32_t *args) { -// MQTT_Client* client = (MQTT_Client*)args; - os_printf("MQTT Published\n"); -} - -void ICACHE_FLASH_ATTR -mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { - char *topicBuf = (char*)os_zalloc(topic_len + 1); - char *dataBuf = (char*)os_zalloc(data_len + 1); - -// MQTT_Client* client = (MQTT_Client*)args; - - os_memcpy(topicBuf, topic, topic_len); - topicBuf[topic_len] = 0; - - os_memcpy(dataBuf, data, data_len); - dataBuf[data_len] = 0; - - os_printf("Receive topic: %s, data: %s\n", topicBuf, dataBuf); - os_free(topicBuf); - os_free(dataBuf); -} - -void ICACHE_FLASH_ATTR -wifiStateChangeCb(uint8_t status) -{ - if (status == wifiGotIP && mqttClient.connState != TCP_CONNECTING){ - MQTT_Connect(&mqttClient); - } - else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING){ - MQTT_Disconnect(&mqttClient); - } -} +// MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); +//} +// +//void ICACHE_FLASH_ATTR +//mqttDisconnectedCb(uint32_t *args) { +//// MQTT_Client* client = (MQTT_Client*)args; +// os_printf("MQTT Disconnected\n"); +//} +// +//void ICACHE_FLASH_ATTR +//mqttTcpDisconnectedCb(uint32_t *args) { +//// MQTT_Client* client = (MQTT_Client*)args; +// os_printf("MQTT TCP Disconnected\n"); +//} +// +//void ICACHE_FLASH_ATTR +//mqttPublishedCb(uint32_t *args) { +//// MQTT_Client* client = (MQTT_Client*)args; +// os_printf("MQTT Published\n"); +//} +// +//void ICACHE_FLASH_ATTR +//mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { +// char *topicBuf = (char*)os_zalloc(topic_len + 1); +// char *dataBuf = (char*)os_zalloc(data_len + 1); +// +//// MQTT_Client* client = (MQTT_Client*)args; +// +// os_memcpy(topicBuf, topic, topic_len); +// topicBuf[topic_len] = 0; +// +// os_memcpy(dataBuf, data, data_len); +// dataBuf[data_len] = 0; +// +// os_printf("Receive topic: %s, data: %s\n", topicBuf, dataBuf); +// os_free(topicBuf); +// os_free(dataBuf); +//} +// +//void ICACHE_FLASH_ATTR +//wifiStateChangeCb(uint8_t status) +//{ +// if (status == wifiGotIP && mqttClient.connState != TCP_CONNECTING){ +// MQTT_Connect(&mqttClient); +// } +// else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING){ +// MQTT_Disconnect(&mqttClient); +// } +//} void init() { - wifiAddStateChangeCb(wifiStateChangeCb); - MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); - MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); - MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); - MQTT_OnConnected(&mqttClient, mqttConnectedCb); - MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); - MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); - MQTT_OnPublished(&mqttClient, mqttPublishedCb); - MQTT_OnData(&mqttClient, mqttDataCb); +// wifiAddStateChangeCb(wifiStateChangeCb); +// MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); +// MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); +// MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); +// MQTT_OnConnected(&mqttClient, mqttConnectedCb); +// MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); +// MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); +// MQTT_OnPublished(&mqttClient, mqttPublishedCb); +// MQTT_OnData(&mqttClient, mqttDataCb); } \ No newline at end of file From da7a1b913f5b2d35c8626bd19095ec1e47a7191a Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sun, 6 Sep 2015 10:12:59 -0700 Subject: [PATCH 069/104] indentation changes --- esp-link/cgiwifi.c | 824 ++++++++++++++++++++++----------------------- 1 file changed, 412 insertions(+), 412 deletions(-) diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index 2354b13..69d59b4 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -31,12 +31,12 @@ uint8_t wifiState = wifiIsDisconnected; // reasons for which a connection failed uint8_t wifiReason = 0; static char *wifiReasons[] = { - "", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed", - "not_assoced", "assoc_leave", "assoc_not_authed", "disassoc_pwrcap_bad", "disassoc_supchan_bad", - "ie_invalid", "mic_failure", "4way_handshake_timeout", "group_key_update_timeout", - "ie_in_4way_differs", "group_cipher_invalid", "pairwise_cipher_invalid", "akmp_invalid", - "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected", - "beacon_timeout", "no_ap_found" }; + "", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed", + "not_assoced", "assoc_leave", "assoc_not_authed", "disassoc_pwrcap_bad", "disassoc_supchan_bad", + "ie_invalid", "mic_failure", "4way_handshake_timeout", "group_key_update_timeout", + "ie_in_4way_differs", "group_cipher_invalid", "pairwise_cipher_invalid", "akmp_invalid", + "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected", + "beacon_timeout", "no_ap_found" }; static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; @@ -44,51 +44,51 @@ 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]; - return wifiReasons[1]; + if (wifiReason <= 24) return wifiReasons[wifiReason]; + if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24]; + return wifiReasons[1]; } // handler for wifi status change callback coming in from espressif library static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) { - switch (evt->event) { - case EVENT_STAMODE_CONNECTED: - wifiState = wifiIsConnected; - wifiReason = 0; - os_printf("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, - evt->event_info.connected.channel); - statusWifiUpdate(wifiState); - break; - case EVENT_STAMODE_DISCONNECTED: - wifiState = wifiIsDisconnected; - wifiReason = evt->event_info.disconnected.reason; - os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n", - evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); - statusWifiUpdate(wifiState); - break; - case EVENT_STAMODE_AUTHMODE_CHANGE: - os_printf("Wifi auth mode: %d -> %d\n", - evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); - break; - case EVENT_STAMODE_GOT_IP: - wifiState = wifiGotIP; - wifiReason = 0; - os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", - IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), - IP2STR(&evt->event_info.got_ip.gw)); - statusWifiUpdate(wifiState); - break; - case EVENT_SOFTAPMODE_STACONNECTED: - os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n", - MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); - break; - case EVENT_SOFTAPMODE_STADISCONNECTED: - os_printf("Wifi AP: station " MACSTR " left, AID = %d\n", - MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); - break; - default: - break; - } + switch (evt->event) { + case EVENT_STAMODE_CONNECTED: + wifiState = wifiIsConnected; + wifiReason = 0; + os_printf("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, + evt->event_info.connected.channel); + statusWifiUpdate(wifiState); + break; + case EVENT_STAMODE_DISCONNECTED: + wifiState = wifiIsDisconnected; + wifiReason = evt->event_info.disconnected.reason; + os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n", + evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); + statusWifiUpdate(wifiState); + break; + case EVENT_STAMODE_AUTHMODE_CHANGE: + os_printf("Wifi auth mode: %d -> %d\n", + evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); + break; + case EVENT_STAMODE_GOT_IP: + wifiState = wifiGotIP; + wifiReason = 0; + os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", + IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), + IP2STR(&evt->event_info.got_ip.gw)); + statusWifiUpdate(wifiState); + break; + case EVENT_SOFTAPMODE_STACONNECTED: + os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n", + MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); + break; + case EVENT_SOFTAPMODE_STADISCONNECTED: + os_printf("Wifi AP: station " MACSTR " left, AID = %d\n", + MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); + break; + default: + break; + } for (int i = 0; i < 4; i++) { if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState); @@ -110,16 +110,16 @@ wifiAddStateChangeCb(WifiStateChangeCb cb) { //WiFi access point data typedef struct { - char ssid[32]; - sint8 rssi; - char enc; + char ssid[32]; + sint8 rssi; + char enc; } ApData; //Scan result typedef struct { - char scanInProgress; //if 1, don't access the underlying stuff from the webpage. - ApData **apData; - int noAps; + char scanInProgress; //if 1, don't access the underlying stuff from the webpage. + ApData **apData; + int noAps; } ScanResultData; //Static scan status storage. @@ -128,109 +128,109 @@ static ScanResultData cgiWifiAps; //Callback the code calls when a wlan ap scan is done. Basically stores the result in //the cgiWifiAps struct. void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { - int n; - struct bss_info *bss_link = (struct bss_info *)arg; - - if (status!=OK) { - os_printf("wifiScanDoneCb status=%d\n", status); - cgiWifiAps.scanInProgress=0; - return; - } - - //Clear prev ap data if needed. - if (cgiWifiAps.apData!=NULL) { - for (n=0; nnext.stqe_next; - n++; - } - //Allocate memory for access point data - cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); - cgiWifiAps.noAps=n; - os_printf("Scan done: found %d APs\n", n); - - //Copy access point data to the static struct - n=0; - bss_link = (struct bss_info *)arg; - while (bss_link != NULL) { - if (n>=cgiWifiAps.noAps) { - //This means the bss_link changed under our nose. Shouldn't happen! - //Break because otherwise we will write in unallocated memory. - os_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); - break; - } - //Save the ap data. - cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData)); - cgiWifiAps.apData[n]->rssi=bss_link->rssi; - cgiWifiAps.apData[n]->enc=bss_link->authmode; - strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); - os_printf("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi); - - bss_link = bss_link->next.stqe_next; - n++; - } - //We're done. - cgiWifiAps.scanInProgress=0; + int n; + struct bss_info *bss_link = (struct bss_info *)arg; + + if (status!=OK) { + os_printf("wifiScanDoneCb status=%d\n", status); + cgiWifiAps.scanInProgress=0; + return; + } + + //Clear prev ap data if needed. + if (cgiWifiAps.apData!=NULL) { + for (n=0; nnext.stqe_next; + n++; + } + //Allocate memory for access point data + cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); + cgiWifiAps.noAps=n; + os_printf("Scan done: found %d APs\n", n); + + //Copy access point data to the static struct + n=0; + bss_link = (struct bss_info *)arg; + while (bss_link != NULL) { + if (n>=cgiWifiAps.noAps) { + //This means the bss_link changed under our nose. Shouldn't happen! + //Break because otherwise we will write in unallocated memory. + os_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); + break; + } + //Save the ap data. + cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData)); + cgiWifiAps.apData[n]->rssi=bss_link->rssi; + cgiWifiAps.apData[n]->enc=bss_link->authmode; + strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); + os_printf("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi); + + bss_link = bss_link->next.stqe_next; + n++; + } + //We're done. + cgiWifiAps.scanInProgress=0; } static ETSTimer scanTimer; static void ICACHE_FLASH_ATTR scanStartCb(void *arg) { - os_printf("Starting a scan\n"); - wifi_station_scan(NULL, wifiScanDoneCb); + os_printf("Starting a scan\n"); + wifi_station_scan(NULL, wifiScanDoneCb); } static int ICACHE_FLASH_ATTR cgiWiFiStartScan(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - if (!cgiWifiAps.scanInProgress) { - cgiWifiAps.scanInProgress = 1; - os_timer_disarm(&scanTimer); - os_timer_setfn(&scanTimer, scanStartCb, NULL); - os_timer_arm(&scanTimer, 1000, 0); - } - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + if (!cgiWifiAps.scanInProgress) { + cgiWifiAps.scanInProgress = 1; + os_timer_disarm(&scanTimer); + os_timer_setfn(&scanTimer, scanStartCb, NULL); + os_timer_arm(&scanTimer, 1000, 0); + } + return HTTPD_CGI_DONE; } static int ICACHE_FLASH_ATTR cgiWiFiGetScan(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[2048]; - int len; - - jsonHeader(connData, 200); - - if (cgiWifiAps.scanInProgress==1) { - //We're still scanning. Tell Javascript code that. - len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; - } - - len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); - for (int pos=0; posssid, cgiWifiAps.apData[pos]->rssi, - cgiWifiAps.apData[pos]->enc, (pos==cgiWifiAps.noAps-1)?"":","); - } - len += os_sprintf(buff+len, "]}}\n"); - //os_printf("Sending %d bytes: %s\n", len, buff); - 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; + + jsonHeader(connData, 200); + + if (cgiWifiAps.scanInProgress==1) { + //We're still scanning. Tell Javascript code that. + len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; + } + + len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); + for (int pos=0; posssid, cgiWifiAps.apData[pos]->rssi, + cgiWifiAps.apData[pos]->enc, (pos==cgiWifiAps.noAps-1)?"":","); + } + len += os_sprintf(buff+len, "]}}\n"); + //os_printf("Sending %d bytes: %s\n", len, buff); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { - if (connData->requestType == HTTPD_METHOD_GET) { - return cgiWiFiGetScan(connData); - } else if (connData->requestType == HTTPD_METHOD_POST) { - return cgiWiFiStartScan(connData); - } else { - jsonHeader(connData, 404); - return HTTPD_CGI_DONE; - } + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiWiFiGetScan(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiWiFiStartScan(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } } // ===== timers to change state and rescue from failed associations @@ -243,31 +243,31 @@ static ETSTimer resetTimer; // the connect succeeds, this gets the module in STA-only mode. If it fails, it ensures // that the module is in STA+AP mode so the user has a chance to recover. static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { - int x = wifi_station_get_connect_status(); - int m = wifi_get_opmode() & 0x3; - os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x); + int x = wifi_station_get_connect_status(); + int m = wifi_get_opmode() & 0x3; + os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x); - if (x == STATION_GOT_IP) { - if (m != 1) { + if (x == STATION_GOT_IP) { + if (m != 1) { #ifdef CHANGE_TO_STA - // We're happily connected, go to STA mode - os_printf("Wifi got IP. Going into STA mode..\n"); - wifi_set_opmode(1); - wifi_set_sleep_type(SLEEP_MODE); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + // We're happily connected, go to STA mode + os_printf("Wifi got IP. Going into STA mode..\n"); + wifi_set_opmode(1); + wifi_set_sleep_type(SLEEP_MODE); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); #endif - } - log_uart(false); - // no more resetTimer at this point, gotta use physical reset to recover if in trouble - } else { - if (m != 3) { - os_printf("Wifi connect failed. Going into STA+AP mode..\n"); - wifi_set_opmode(3); - } - log_uart(true); - os_printf("Enabling/continuing uart log\n"); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); - } + } + log_uart(false); + // no more resetTimer at this point, gotta use physical reset to recover if in trouble + } else { + if (m != 3) { + os_printf("Wifi connect failed. Going into STA+AP mode..\n"); + wifi_set_opmode(3); + } + log_uart(true); + os_printf("Enabling/continuing uart log\n"); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + } } // Temp store for new ap info. @@ -277,206 +277,206 @@ static ETSTimer reassTimer; // Callback actually doing reassociation 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 - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + os_printf("Wifi changing association\n"); + wifi_station_disconnect(); + stconf.bssid_set = 0; + wifi_station_set_config(&stconf); + wifi_station_connect(); + // Schedule check + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } // This cgi uses the routines above to connect to a specific access point with the // given ESSID using the given password. int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { - char essid[128]; - char passwd[128]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; - - int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid)); - int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd)); - - if (el > 0 && pl >= 0) { - //Set to 0 if you want to disable the actual reconnecting bit - os_strncpy((char*)stconf.ssid, essid, 32); - os_strncpy((char*)stconf.password, passwd, 64); - os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd); - - //Schedule disconnect/connect - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, reassTimerCb, NULL); - os_timer_arm(&reassTimer, 1000, 0); - jsonHeader(connData, 200); - } else { - jsonHeader(connData, 400); - httpdSend(connData, "Cannot parse ssid or password", -1); - } - return HTTPD_CGI_DONE; + char essid[128]; + char passwd[128]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid)); + int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd)); + + if (el > 0 && pl >= 0) { + //Set to 0 if you want to disable the actual reconnecting bit + os_strncpy((char*)stconf.ssid, essid, 32); + os_strncpy((char*)stconf.password, passwd, 64); + os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd); + + //Schedule disconnect/connect + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, reassTimerCb, NULL); + os_timer_arm(&reassTimer, 1000, 0); + jsonHeader(connData, 200); + } else { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse ssid or password", -1); + } + return HTTPD_CGI_DONE; } static bool parse_ip(char *buff, ip_addr_t *ip_ptr) { - char *next = buff; // where to start parsing next integer - int found = 0; // number of integers parsed - uint32_t ip = 0; // the ip addres parsed - for (int i=0; i<32; i++) { // 32 is just a safety limit - char c = buff[i]; - if (c == '.' || c == 0) { - // parse the preceding integer and accumulate into IP address - bool last = c == 0; - buff[i] = 0; - uint32_t v = atoi(next); - ip = ip | ((v&0xff)<<(found*8)); - next = buff+i+1; // next integer starts after the '.' - found++; - if (last) { // if at end of string we better got 4 integers - ip_ptr->addr = ip; - return found == 4; - } - continue; - } - if (c < '0' || c > '9') return false; - } - return false; + char *next = buff; // where to start parsing next integer + int found = 0; // number of integers parsed + uint32_t ip = 0; // the ip addres parsed + for (int i=0; i<32; i++) { // 32 is just a safety limit + char c = buff[i]; + if (c == '.' || c == 0) { + // parse the preceding integer and accumulate into IP address + bool last = c == 0; + buff[i] = 0; + uint32_t v = atoi(next); + ip = ip | ((v&0xff)<<(found*8)); + next = buff+i+1; // next integer starts after the '.' + found++; + if (last) { // if at end of string we better got 4 integers + ip_ptr->addr = ip; + return found == 4; + } + continue; + } + if (c < '0' || c > '9') return false; + } + return false; } #define DEBUGIP #ifdef DEBUGIP static void ICACHE_FLASH_ATTR debugIP() { - struct ip_info info; - if (wifi_get_ip_info(0, &info)) { - os_printf("\"ip\": \"%d.%d.%d.%d\"\n", IP2STR(&info.ip.addr)); - os_printf("\"netmask\": \"%d.%d.%d.%d\"\n", IP2STR(&info.netmask.addr)); - os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr)); - os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname()); - } else { - os_printf("\"ip\": \"-none-\"\n"); - } + struct ip_info info; + if (wifi_get_ip_info(0, &info)) { + os_printf("\"ip\": \"%d.%d.%d.%d\"\n", IP2STR(&info.ip.addr)); + os_printf("\"netmask\": \"%d.%d.%d.%d\"\n", IP2STR(&info.netmask.addr)); + os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr)); + os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname()); + } else { + os_printf("\"ip\": \"-none-\"\n"); + } } #endif // configure Wifi, specifically DHCP vs static IP address based on flash config static void ICACHE_FLASH_ATTR configWifiIP() { - if (flashConfig.staticip == 0) { - // let's DHCP! - wifi_station_set_hostname(flashConfig.hostname); - if (wifi_station_dhcpc_status() == DHCP_STARTED) - wifi_station_dhcpc_stop(); - wifi_station_dhcpc_start(); - os_printf("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); - } else { - // no DHCP, we got static network config! - wifi_station_dhcpc_stop(); - struct ip_info ipi; - ipi.ip.addr = flashConfig.staticip; - ipi.netmask.addr = flashConfig.netmask; - ipi.gw.addr = flashConfig.gateway; - wifi_set_ip_info(0, &ipi); - os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); - } + if (flashConfig.staticip == 0) { + // let's DHCP! + wifi_station_set_hostname(flashConfig.hostname); + if (wifi_station_dhcpc_status() == DHCP_STARTED) + wifi_station_dhcpc_stop(); + wifi_station_dhcpc_start(); + os_printf("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); + } else { + // no DHCP, we got static network config! + wifi_station_dhcpc_stop(); + struct ip_info ipi; + ipi.ip.addr = flashConfig.staticip; + ipi.netmask.addr = flashConfig.netmask; + ipi.gw.addr = flashConfig.gateway; + wifi_set_ip_info(0, &ipi); + os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); + } #ifdef DEBUGIP - debugIP(); + debugIP(); #endif } // Change special settings int ICACHE_FLASH_ATTR cgiWiFiSpecial(HttpdConnData *connData) { - char dhcp[8]; - char hostname[32]; - char staticip[20]; - char netmask[20]; - char gateway[20]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; - - // get args and their string lengths - int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); - int hl = httpdFindArg(connData->getArgs, "hostname", hostname, sizeof(hostname)); - int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); - int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); - int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); - - if (!(dl > 0 && hl >= 0 && sl >= 0 && nl >= 0 && gl >= 0)) { - jsonHeader(connData, 400); - httpdSend(connData, "Request is missing fields", -1); - return HTTPD_CGI_DONE; - } - - char url[64]; // redirect URL - if (os_strcmp(dhcp, "off") == 0) { - // parse static IP params - struct ip_info ipi; - bool ok = parse_ip(staticip, &ipi.ip); - if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); - else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); - if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); - else ipi.gw.addr = 0; - if (!ok) { - jsonHeader(connData, 400); - httpdSend(connData, "Cannot parse static IP config", -1); - return HTTPD_CGI_DONE; - } - // save the params in flash - flashConfig.staticip = ipi.ip.addr; - flashConfig.netmask = ipi.netmask.addr; - flashConfig.gateway = ipi.gw.addr; - // construct redirect URL - os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); - - } else { - // no static IP, set hostname - if (hl == 0) os_strcpy(hostname, "esp-link"); - flashConfig.staticip = 0; - os_strcpy(flashConfig.hostname, hostname); - os_sprintf(url, "{\"url\": \"http://%s\"}", hostname); - } - - configSave(); // ignore error... - // schedule change-over - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, configWifiIP, NULL); - os_timer_arm(&reassTimer, 1000, 0); - // return redirect info - jsonHeader(connData, 200); - httpdSend(connData, url, -1); - return HTTPD_CGI_DONE; + char dhcp[8]; + char hostname[32]; + char staticip[20]; + char netmask[20]; + char gateway[20]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + // get args and their string lengths + int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); + int hl = httpdFindArg(connData->getArgs, "hostname", hostname, sizeof(hostname)); + int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); + int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); + int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); + + if (!(dl > 0 && hl >= 0 && sl >= 0 && nl >= 0 && gl >= 0)) { + jsonHeader(connData, 400); + httpdSend(connData, "Request is missing fields", -1); + return HTTPD_CGI_DONE; + } + + char url[64]; // redirect URL + if (os_strcmp(dhcp, "off") == 0) { + // parse static IP params + struct ip_info ipi; + bool ok = parse_ip(staticip, &ipi.ip); + if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); + else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); + if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); + else ipi.gw.addr = 0; + if (!ok) { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse static IP config", -1); + return HTTPD_CGI_DONE; + } + // save the params in flash + flashConfig.staticip = ipi.ip.addr; + flashConfig.netmask = ipi.netmask.addr; + flashConfig.gateway = ipi.gw.addr; + // construct redirect URL + os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); + + } else { + // no static IP, set hostname + if (hl == 0) os_strcpy(hostname, "esp-link"); + flashConfig.staticip = 0; + os_strcpy(flashConfig.hostname, hostname); + os_sprintf(url, "{\"url\": \"http://%s\"}", hostname); + } + + configSave(); // ignore error... + // schedule change-over + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, configWifiIP, NULL); + os_timer_arm(&reassTimer, 1000, 0); + // return redirect info + jsonHeader(connData, 200); + httpdSend(connData, url, -1); + return HTTPD_CGI_DONE; } //This cgi changes the operating mode: STA / AP / STA+AP int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { - int len; - char buff[1024]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - - len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); - if (len!=0) { - int m = atoi(buff); - os_printf("Wifi switching to mode %d\n", m); - wifi_set_opmode(m&3); - if (m == 1) { - wifi_set_sleep_type(SLEEP_MODE); - // STA-only mode, reset into STA+AP after a timeout - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); - } - jsonHeader(connData, 200); - } else { - jsonHeader(connData, 400); - } - return HTTPD_CGI_DONE; + int len; + char buff[1024]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + + len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); + if (len!=0) { + int m = atoi(buff); + os_printf("Wifi switching to mode %d\n", m); + wifi_set_opmode(m&3); + if (m == 1) { + wifi_set_sleep_type(SLEEP_MODE); + // STA-only mode, reset into STA+AP after a timeout + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + } + jsonHeader(connData, 200); + } else { + jsonHeader(connData, 400); + } + return HTTPD_CGI_DONE; } static char *connStatuses[] = { "idle", "connecting", "wrong password", "AP not found", "failed", "got IP address" }; static char *wifiWarn[] = { 0, - "Switch to STA+AP mode", - "Can't scan in this mode! Switch to STA+AP mode", - "Switch to STA mode", + "Switch to STA+AP mode", + "Can't scan in this mode! Switch to STA+AP mode", + "Switch to STA mode", }; #ifdef CHANGE_TO_STA @@ -487,109 +487,109 @@ static char *wifiWarn[] = { 0, // print various Wifi information into json buffer int ICACHE_FLASH_ATTR printWifiInfo(char *buff) { - int len; - - struct station_config stconf; - wifi_station_get_config(&stconf); - - uint8_t op = wifi_get_opmode() & 0x3; - char *mode = wifiMode[op]; - char *status = "unknown"; - int st = wifi_station_get_connect_status(); - if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; - int p = wifi_get_phy_mode(); - char *phy = wifiPhy[p&3]; - char *warn = wifiWarn[op]; - sint8 rssi = wifi_station_get_rssi(); - if (rssi > 0) rssi = 0; - uint8 mac_addr[6]; - wifi_get_macaddr(0, mac_addr); - uint8_t chan = wifi_get_channel(); - - len = os_sprintf(buff, - "\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", " - "\"rssi\": \"%ddB\", \"warn\": \"%s\", \"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":%d", - mode, MODECHANGE, (char*)stconf.ssid, status, phy, rssi, warn, - mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], chan); - - struct ip_info info; - if (wifi_get_ip_info(0, &info)) { - len += os_sprintf(buff+len, ", \"ip\": \"%d.%d.%d.%d\"", IP2STR(&info.ip.addr)); - len += os_sprintf(buff+len, ", \"netmask\": \"%d.%d.%d.%d\"", IP2STR(&info.netmask.addr)); - len += os_sprintf(buff+len, ", \"gateway\": \"%d.%d.%d.%d\"", IP2STR(&info.gw.addr)); - len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname); - } else { - len += os_sprintf(buff+len, ", \"ip\": \"-none-\""); - } - len += os_sprintf(buff+len, ", \"staticip\": \"%d.%d.%d.%d\"", IP2STR(&flashConfig.staticip)); - len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on"); - - return len; + int len; + + struct station_config stconf; + wifi_station_get_config(&stconf); + + uint8_t op = wifi_get_opmode() & 0x3; + char *mode = wifiMode[op]; + char *status = "unknown"; + int st = wifi_station_get_connect_status(); + if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; + int p = wifi_get_phy_mode(); + char *phy = wifiPhy[p&3]; + char *warn = wifiWarn[op]; + sint8 rssi = wifi_station_get_rssi(); + if (rssi > 0) rssi = 0; + uint8 mac_addr[6]; + wifi_get_macaddr(0, mac_addr); + uint8_t chan = wifi_get_channel(); + + len = os_sprintf(buff, + "\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", " + "\"rssi\": \"%ddB\", \"warn\": \"%s\", \"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":%d", + mode, MODECHANGE, (char*)stconf.ssid, status, phy, rssi, warn, + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], chan); + + struct ip_info info; + if (wifi_get_ip_info(0, &info)) { + len += os_sprintf(buff+len, ", \"ip\": \"%d.%d.%d.%d\"", IP2STR(&info.ip.addr)); + len += os_sprintf(buff+len, ", \"netmask\": \"%d.%d.%d.%d\"", IP2STR(&info.netmask.addr)); + len += os_sprintf(buff+len, ", \"gateway\": \"%d.%d.%d.%d\"", IP2STR(&info.gw.addr)); + len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname); + } else { + len += os_sprintf(buff+len, ", \"ip\": \"-none-\""); + } + len += os_sprintf(buff+len, ", \"staticip\": \"%d.%d.%d.%d\"", IP2STR(&flashConfig.staticip)); + len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on"); + + return len; } int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) { - char buff[1024]; - int len; + char buff[1024]; + int len; - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); - len = os_sprintf(buff, "{"); - len += printWifiInfo(buff+len); - len += os_sprintf(buff+len, ", "); + len = os_sprintf(buff, "{"); + len += printWifiInfo(buff+len); + len += os_sprintf(buff+len, ", "); - if (wifiReason != 0) { - len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason()); - } + if (wifiReason != 0) { + len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason()); + } #if 0 - // commented out 'cause often the client that requested the change can't get a request in to - // find out that it succeeded. Better to just wait the std 15 seconds... - int st=wifi_station_get_connect_status(); - if (st == STATION_GOT_IP) { - if (wifi_get_opmode() != 1) { - // Reset into AP-only mode sooner. - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, 1000, 0); - } - } + // commented out 'cause often the client that requested the change can't get a request in to + // find out that it succeeded. Better to just wait the std 15 seconds... + int st=wifi_station_get_connect_status(); + if (st == STATION_GOT_IP) { + if (wifi_get_opmode() != 1) { + // Reset into AP-only mode sooner. + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, 1000, 0); + } + } #endif - len += os_sprintf(buff+len, "\"x\":0}\n"); + len += os_sprintf(buff+len, "\"x\":0}\n"); - os_printf(" -> %s\n", buff); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + os_printf(" -> %s\n", buff); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } // Cgi to return various Wifi information int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) { - char buff[1024]; + char buff[1024]; - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - os_strcpy(buff, "{"); - printWifiInfo(buff+1); - os_strcat(buff, "}"); + os_strcpy(buff, "{"); + printWifiInfo(buff+1); + os_strcat(buff, "}"); - jsonHeader(connData, 200); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + jsonHeader(connData, 200); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } // Init the wireless, which consists of setting a timer if we expect to connect to an AP // so we can revert to STA+AP mode if we can't connect. void ICACHE_FLASH_ATTR wifiInit() { - wifi_set_phy_mode(2); - int x = wifi_get_opmode() & 0x3; - os_printf("Wifi init, mode=%s\n", wifiMode[x]); - configWifiIP(); - - wifi_set_event_handler_cb(wifiHandleEventCb); - // check on the wifi in a few seconds to see whether we need to switch mode - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + wifi_set_phy_mode(2); + int x = wifi_get_opmode() & 0x3; + os_printf("Wifi init, mode=%s\n", wifiMode[x]); + configWifiIP(); + + wifi_set_event_handler_cb(wifiHandleEventCb); + // check on the wifi in a few seconds to see whether we need to switch mode + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } From 9e4cbb0dbd49ef7f692d19f34b86ef216625ee47 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 7 Sep 2015 19:53:23 -0700 Subject: [PATCH 070/104] massive rework of MQTT --- Makefile | 2 +- cmd/handlers.c | 2 +- cmd/mqtt_cmd.c | 47 ++- cmd/mqtt_cmd.h | 1 - cmd/rest.c | 12 +- esp-link/cgipins.c | 46 +-- esp-link/main.c | 12 +- esp-link/status.c | 176 ++++---- html/console.html | 2 +- httpd/httpd.c | 2 +- include/user_config.h | 18 +- mqtt/mqtt.c | 924 ++++++++++++++++++++++++------------------ mqtt/mqtt.h | 171 ++++---- mqtt/mqtt_msg.c | 51 +-- mqtt/mqtt_msg.h | 48 ++- mqtt/pktbuf.c | 64 +++ mqtt/pktbuf.h | 29 ++ mqtt/proto.c | 86 ---- mqtt/proto.h | 21 - mqtt/queue.c | 53 --- mqtt/queue.h | 46 --- mqtt/ringbuf.c | 63 --- mqtt/ringbuf.h | 17 - serial/serbridge.c | 1 + serial/serled.h | 1 - serial/uart.c | 2 +- user/user_main.c | 59 ++- 27 files changed, 946 insertions(+), 1010 deletions(-) create mode 100644 mqtt/pktbuf.c create mode 100644 mqtt/pktbuf.h delete mode 100644 mqtt/proto.c delete mode 100644 mqtt/proto.h delete mode 100644 mqtt/queue.c delete mode 100644 mqtt/queue.h delete mode 100644 mqtt/ringbuf.c delete mode 100644 mqtt/ringbuf.h diff --git a/Makefile b/Makefile index 7f47c20..98a8361 100644 --- a/Makefile +++ b/Makefile @@ -147,7 +147,7 @@ MODULES = espfs httpd user serial cmd mqtt esp-link EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal phy pp net80211 wpa main lwip +LIBS = c gcc hal phy pp net80211 wpa main lwip # crypto ssl # compiler flags using during compilation of source files CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ diff --git a/cmd/handlers.c b/cmd/handlers.c index b54773f..e20686c 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -95,7 +95,7 @@ cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name) { char checkname[16]; os_strncpy(checkname, name, sizeof(checkname)); - for (uint8_t i = 0; i < sizeof(commands); i++) { + for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { //os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, // (void *)callbacks[i].callback); // if callback doesn't exist or it's null diff --git a/cmd/mqtt_cmd.c b/cmd/mqtt_cmd.c index 358ad48..d2a01e2 100644 --- a/cmd/mqtt_cmd.c +++ b/cmd/mqtt_cmd.c @@ -1,8 +1,10 @@ +#include +#include "mqtt.h" #include "mqtt_cmd.h" uint32_t connectedCb = 0, disconnectCb = 0, tcpDisconnectedCb = 0, publishedCb = 0, dataCb = 0; -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR cmdMqttConnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; @@ -24,7 +26,7 @@ cmdMqttTcpDisconnectedCb(uint32_t *args) { CMD_ResponseEnd(crc); } -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR cmdMqttDisconnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; @@ -33,7 +35,7 @@ cmdMqttDisconnectedCb(uint32_t* args) { CMD_ResponseEnd(crc); } -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR cmdMqttPublishedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; @@ -42,7 +44,7 @@ cmdMqttPublishedCb(uint32_t* args) { CMD_ResponseEnd(crc); } -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) { uint16_t crc = 0; MQTT_Client* client = (MQTT_Client*)args; @@ -54,7 +56,7 @@ cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* CMD_ResponseEnd(crc); } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Setup(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -65,10 +67,12 @@ MQTTCMD_Setup(CmdPacket *cmd) { // create mqtt client uint8_t clientLen = sizeof(MQTT_Client); MQTT_Client* client = (MQTT_Client*)os_zalloc(clientLen); - if (client == NULL) - return 0; + if (client == NULL) return 0; os_memset(client, 0, clientLen); + return 0; +#if 0 + uint16_t len; uint8_t *client_id, *user_data, *pass_data; uint32_t keepalive, clean_session, cb_data; @@ -102,7 +106,7 @@ MQTTCMD_Setup(CmdPacket *cmd) { os_printf("MQTT: MQTTCMD_Setup clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session); - // init client + // init client // TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again? MQTT_InitClient(client, (char*)client_id, (char*)user_data, (char*)pass_data, keepalive, clean_session); @@ -112,11 +116,11 @@ MQTTCMD_Setup(CmdPacket *cmd) { CMD_PopArg(&req, (uint8_t*)&cb_data, 4); callback->connectedCb = cb_data; CMD_PopArg(&req, (uint8_t*)&cb_data, 4); - callback->disconnectedCb = cb_data; + callback->disconnectedCb = cb_data; CMD_PopArg(&req, (uint8_t*)&cb_data, 4); callback->publishedCb = cb_data; CMD_PopArg(&req, (uint8_t*)&cb_data, 4); - callback->dataCb = cb_data; + callback->dataCb = cb_data; client->user_data = callback; @@ -136,9 +140,10 @@ MQTTCMD_Setup(CmdPacket *cmd) { os_free(pass_data); return (uint32_t)client; +#endif } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Lwt(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -153,7 +158,7 @@ MQTTCMD_Lwt(CmdPacket *cmd) { os_printf("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr); uint16_t len; - + // get topic if (client->connect_info.will_topic) os_free(client->connect_info.will_topic); @@ -174,7 +179,7 @@ MQTTCMD_Lwt(CmdPacket *cmd) { // get qos CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_qos, 4); - + // get retain CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); @@ -186,13 +191,13 @@ MQTTCMD_Lwt(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Connect(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); if (CMD_GetArgc(&req) != 4) - return 0; + return 0; // get mqtt client uint32_t client_ptr; @@ -202,7 +207,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { uint16_t len; - // get host + // get host if (client->host) os_free(client->host); len = CMD_ArgLen(&req); @@ -217,7 +222,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { // get security CMD_PopArg(&req, (uint8_t*)&client->security, 4); - os_printf("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%d\n", + os_printf("MQTT: MQTTCMD_Connect host=%s, port=%d, security=%d\n", client->host, client->port, client->security); @@ -226,7 +231,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Disconnect(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -245,7 +250,7 @@ MQTTCMD_Disconnect(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Publish(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -279,7 +284,7 @@ MQTTCMD_Publish(CmdPacket *cmd) { // TODO: next line not originally present data[len] = 0; - // get data length + // get data length // TODO: this isn't used but we have to pull it off the stack CMD_PopArg(&req, (uint8_t*)&data_len, 4); @@ -301,7 +306,7 @@ MQTTCMD_Publish(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Subscribe(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); diff --git a/cmd/mqtt_cmd.h b/cmd/mqtt_cmd.h index 9ccbd08..72ffe98 100644 --- a/cmd/mqtt_cmd.h +++ b/cmd/mqtt_cmd.h @@ -2,7 +2,6 @@ #define MODULES_MQTT_CMD_H_ #include "cmd.h" -#include "mqtt.h" typedef struct { uint32_t connectedCb; diff --git a/cmd/rest.c b/cmd/rest.c index 3556681..2c8d606 100644 --- a/cmd/rest.c +++ b/cmd/rest.c @@ -154,12 +154,12 @@ rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { 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 { +#ifdef CLIENT_SSL_ENABLE + if(client->security) { + espconn_secure_connect(client->pCon); + } else +#endif espconn_connect(client->pCon); - //} os_printf("REST: connecting...\n"); } } @@ -368,6 +368,8 @@ REST_Request(CmdPacket *cmd) { } os_printf("\n"); + //os_printf("REST request: %s", (char*)client->data); + os_printf("REST: pCon state=%d\n", client->pCon->state); client->pCon->state = ESPCONN_NONE; espconn_regist_connectcb(client->pCon, tcpclient_connect_cb); diff --git a/esp-link/cgipins.c b/esp-link/cgipins.c index 2d71849..dabc1fd 100644 --- a/esp-link/cgipins.c +++ b/esp-link/cgipins.c @@ -24,9 +24,9 @@ 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) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted - char buff[2048]; + char buff[2048]; int len; // figure out current mapping @@ -62,27 +62,27 @@ int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) { } len += os_sprintf(buff+len, "\n] }"); - jsonHeader(connData, 200); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + jsonHeader(connData, 200); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } // Cgi to change choice of pin assignments int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { - if (connData->conn==NULL) { - return HTTPD_CGI_DONE; // Connection aborted - } + if (connData->conn==NULL) { + return HTTPD_CGI_DONE; // Connection aborted + } char buff[128]; - int len = httpdFindArg(connData->getArgs, "map", buff, sizeof(buff)); - if (len <= 0) { - jsonHeader(connData, 400); + int len = httpdFindArg(connData->getArgs, "map", buff, sizeof(buff)); + if (len <= 0) { + jsonHeader(connData, 400); return HTTPD_CGI_DONE; } int m = atoi(buff); - if (m < 0 || m >= num_map_names) { - jsonHeader(connData, 400); + if (m < 0 || m >= num_map_names) { + jsonHeader(connData, 400); return HTTPD_CGI_DONE; } @@ -106,17 +106,17 @@ int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { httpdEndHeaders(connData); httpdSend(connData, "Failed to save config", -1); } - return HTTPD_CGI_DONE; + return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR cgiPins(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - if (connData->requestType == HTTPD_METHOD_GET) { - return cgiPinsGet(connData); - } else if (connData->requestType == HTTPD_METHOD_POST) { - return cgiPinsSet(connData); - } else { - jsonHeader(connData, 404); - return HTTPD_CGI_DONE; - } + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiPinsGet(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiPinsSet(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } } diff --git a/esp-link/main.c b/esp-link/main.c index 5febf87..12d715e 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -29,7 +29,7 @@ #include "log.h" #include -//#define SHOW_HEAP_USE +#define SHOW_HEAP_USE //Function that tells the authentication system what users/passwords live on the system. //This is disabled in the default build; if you want to try it, enable the authBasic line in @@ -120,13 +120,16 @@ static char *rst_codes[] = { # define VERS_STR(V) VERS_STR_STR(V) char* esp_link_version = VERS_STR(VERSION); -//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. +extern void app_init(void); + +// Main routine to initialize esp-link. void user_init(void) { // get the flash config so we know how to init things //configWipe(); // uncomment to reset the config for testing purposes bool restoreOk = configRestore(); // init gpio pin registers gpio_init(); + gpio_output_set(0, 0, 0, (1<<15)); // some people tie it GND, gotta ensure it's disabled // init UART uart_init(flashConfig.baud_rate, 115200); logInit(); // must come after init of uart @@ -163,6 +166,5 @@ void user_init(void) { os_printf("** esp-link ready\n"); - // call user_main init - init(); -} \ No newline at end of file + app_init(); +} diff --git a/esp-link/status.c b/esp-link/status.c index a6080fe..4e0da55 100644 --- a/esp-link/status.c +++ b/esp-link/status.c @@ -14,56 +14,56 @@ static ETSTimer ledTimer; static void ICACHE_FLASH_ATTR setLed(int on) { int8_t pin = flashConfig.conn_led_pin; if (pin < 0) return; // disabled - // LED is active-low - if (on) { - gpio_output_set(0, (1<= 0) return; // not connected or other error - - // compose TCP command - uint8_t chan = MAX_TCP_CHAN-1; - tcpClientCommand(chan, 'T', "grovestreams.com:80"); - - // 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", - flashConfig.api_key); - - // http body - int dataLen = os_sprintf(buf+hdrLen, - "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", - flashConfig.hostname, GS_STREAM, rssi); - buf[hdrLen+dataLen++] = 0; - buf[hdrLen+dataLen++] = '\n'; - - // hackish way to fill in the content-length - os_sprintf(buf+hdrLen-9, "%5d", dataLen); - buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) - - // send the request off and forget about it... - for (short i=0; i= 0) return; // not connected or other error + + // compose TCP command + uint8_t chan = MAX_TCP_CHAN-1; + tcpClientCommand(chan, 'T', "grovestreams.com:80"); + + // 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", + flashConfig.api_key); + + // http body + int dataLen = os_sprintf(buf+hdrLen, + "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", + flashConfig.hostname, GS_STREAM, rssi); + buf[hdrLen+dataLen++] = 0; + buf[hdrLen+dataLen++] = '\n'; + + // hackish way to fill in the content-length + os_sprintf(buf+hdrLen-9, "%5d", dataLen); + buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) + + // send the request off and forget about it... + for (short i=0; i= 0) { - makeGpio(flashConfig.conn_led_pin); - setLed(1); - } - os_printf("CONN led=%d\n", flashConfig.conn_led_pin); - - 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 + if (flashConfig.conn_led_pin >= 0) { + makeGpio(flashConfig.conn_led_pin); + setLed(1); + } + os_printf("CONN led=%d\n", flashConfig.conn_led_pin); + + 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 } diff --git a/html/console.html b/html/console.html index db4c0a6..9dc359c 100644 --- a/html/console.html +++ b/html/console.html @@ -20,7 +20,7 @@ + diff --git a/html/style.css b/html/style.css index 29d15a3..ca072d8 100644 --- a/html/style.css +++ b/html/style.css @@ -3,6 +3,10 @@ html, button, input, select, textarea, .pure-g [class *= "pure-u"] { font-family: sans-serif; } +input[type="text"], input[type="password"] { + width: 100%; +} + body { color: #777; } diff --git a/html/ui.js b/html/ui.js index 56cc256..2a4e20e 100644 --- a/html/ui.js +++ b/html/ui.js @@ -374,11 +374,11 @@ function changeTcpClient(e) { addClass(cb, 'pure-button-disabled'); ajaxSpin("POST", url, function(resp) { removeClass(cb, 'pure-button-disabled'); - getWifiInfo(); + fetchTcpClient(); }, function(s, st) { showWarning("Error: "+st); removeClass(cb, 'pure-button-disabled'); - getWifiInfo(); + fetchTcpClient(); }); } @@ -394,4 +394,69 @@ function fetchTcpClient() { }); } +//===== MQTT cards + +function changeMqtt(e) { + e.preventDefault(); + var url = "mqtt?1=1"; + var i, inputs = $("input"); + for (i=0; i 0; + else el.value = data[v]; + } + }); + $("#mqtt-spinner").setAttribute("hidden", ""); + $("#mqtt-status-spinner").setAttribute("hidden", ""); + $("#mqtt-form").removeAttribute("hidden"); + $("#mqtt-status-form").removeAttribute("hidden"); + + var i, inputs = $("input"); + for (i=0; ipending_buffer->data, client->pending_buffer->filled); } - os_printf("MQTT: Recv type=%d id=%04X len=%d; Pend type=%d id=%02X\n", - msg_type, msg_id, msg_len, pending_msg_type, pending_msg_id); + os_printf("MQTT: Recv type=%s id=%04X len=%d; Pend type=%s id=%02X\n", + mqtt_msg_type[msg_type], msg_id, msg_len, mqtt_msg_type[pending_msg_type], pending_msg_id); switch (msg_type) { case MQTT_MSG_TYPE_CONNACK: @@ -177,7 +182,7 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_PUBREC: // rec for a publish we sent if (pending_msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg_id == msg_id) { - os_printf("MQTT: Recv PUBREC, cont QoS2 publish\n"); + os_printf("MQTT: QoS2 publish cont\n"); client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); // we need to send PUBREL mqtt_msg_pubrel(&client->mqtt_connection, msg_id); @@ -202,7 +207,6 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { if (msg_qos == 1) mqtt_msg_puback(&client->mqtt_connection, msg_id); if (msg_qos == 2) mqtt_msg_pubrec(&client->mqtt_connection, msg_id); if (msg_qos == 1 || msg_qos == 2) { - os_printf("MQTT: Queue response QoS: %d\n", msg_qos); mqtt_enq_message(client, client->mqtt_connection.message.data, client->mqtt_connection.message.length); } @@ -213,7 +217,7 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_PUBREL: // rel for a rec we sent (originally publish received) if (pending_msg_type == MQTT_MSG_TYPE_PUBREC && pending_msg_id == msg_id) { - os_printf("MQTT: Recv PUBREL, cont QoS2 recv\n"); + os_printf("MQTT: Cont QoS2 recv\n"); client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); // we need to send PUBCOMP mqtt_msg_pubcomp(&client->mqtt_connection, msg_id); @@ -223,7 +227,6 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { break; case MQTT_MSG_TYPE_PINGRESP: - os_printf("MQTT: Recv PINGRESP\n"); client->keepAliveAckTick = 0; break; } @@ -299,7 +302,7 @@ mqtt_timer(void* arg) { // check whether we need to send a keep-alive message if (client->keepAliveTick > 0 && --client->keepAliveTick == 0) { // timeout: we need to send a ping message - os_printf("MQTT: Send keepalive to %s:%d\n", client->host, client->port); + //os_printf("MQTT: Send keepalive\n"); mqtt_msg_pingreq(&client->mqtt_connection); PktBuf *buf = PktBuf_New(client->mqtt_connection.message.length); os_memcpy(buf->data, client->mqtt_connection.message.data, @@ -432,7 +435,7 @@ mqtt_send_message(MQTT_Client* client) { // get some details about the message uint16_t msg_type = mqtt_get_type(buf->data); uint8_t msg_id = mqtt_get_id(buf->data, buf->filled); - os_printf("MQTT: Send type=%d, id=%04X len=%d\n", msg_type, msg_id, buf->filled); + os_printf("MQTT: Send type=%s id=%04X len=%d\n", mqtt_msg_type[msg_type], msg_id, buf->filled); #if 0 for (int i=0; ifilled; i++) { if (buf->data[i] >= ' ' && buf->data[i] <= '~') os_printf("%c", buf->data[i]); From c1ed3695a9e7a33ef1a08a82d39f54af8de3dead Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Tue, 8 Sep 2015 23:24:49 -0700 Subject: [PATCH 072/104] server-side handling of MQTT web page --- esp-link/cgi.c | 52 +++++++++++++++++++++++++++-- esp-link/cgi.h | 3 ++ esp-link/cgimqtt.c | 66 ++++++++++++++++++++++++------------ html/mqtt.html | 10 +++--- html/ui.js | 83 ++++++++++++++++++++++++++-------------------- httpd/httpd.c | 2 +- serial/serbridge.c | 14 ++++++++ serial/slip.c | 19 ++--------- serial/slip.h | 6 ++++ 9 files changed, 173 insertions(+), 82 deletions(-) create mode 100644 serial/slip.h diff --git a/esp-link/cgi.c b/esp-link/cgi.c index 3a37347..bfb7028 100644 --- a/esp-link/cgi.c +++ b/esp-link/cgi.c @@ -18,16 +18,64 @@ Some random cgi routines. #include "cgi.h" #include "espfs.h" -void ICACHE_FLASH_ATTR -jsonHeader(HttpdConnData *connData, int code) { +void noCacheHeaders(HttpdConnData *connData, int code) { httpdStartResponse(connData, code); httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); httpdHeader(connData, "Pragma", "no-cache"); httpdHeader(connData, "Expires", "0"); +} + +void ICACHE_FLASH_ATTR +jsonHeader(HttpdConnData *connData, int code) { + noCacheHeaders(connData, code); httpdHeader(connData, "Content-Type", "application/json"); httpdEndHeaders(connData); } +void ICACHE_FLASH_ATTR +errorResponse(HttpdConnData *connData, int code, char *message) { + noCacheHeaders(connData, code); + httpdEndHeaders(connData); + httpdSend(connData, message, -1); + os_printf("HTTP %d error response: \"%s\"\n", code, message); +} + +// look for the HTTP arg 'name' and store it at 'config' with max length 'max_len' (incl +// terminating zero), returns -1 on error, 0 if not found, 1 if found and OK +int ICACHE_FLASH_ATTR +getStringArg(HttpdConnData *connData, char *name, char *config, int max_len) { + char buff[128]; + int len = httpdFindArg(connData->getArgs, name, buff, sizeof(buff)); + if (len < 0) return 0; // not found, skip + if (len >= max_len) { + os_sprintf(buff, "Value for %s too long (%d > %d allowed)", name, len, max_len-1); + errorResponse(connData, 400, buff); + return -1; + } + strcpy(config, buff); + return 1; +} + +int ICACHE_FLASH_ATTR +getBoolArg(HttpdConnData *connData, char *name, bool*config) { + char buff[64]; + int len = httpdFindArg(connData->getArgs, name, buff, sizeof(buff)); + if (len < 0) return 0; // not found, skip + + if (strcmp(buff, "1") == 0 || strcmp(buff, "true") == 0) { + *config = true; + return 1; + } + if (strcmp(buff, "0") == 0 || strcmp(buff, "false") == 0) { + *config = false; + return 1; + } + os_sprintf(buff, "Invalid value for %s", name); + errorResponse(connData, 400, buff); + return -1; +} + + #define TOKEN(x) (os_strcmp(token, x) == 0) #if 0 // Handle system information variables and print their value, returns the number of diff --git a/esp-link/cgi.h b/esp-link/cgi.h index b5690cf..8d61f36 100644 --- a/esp-link/cgi.h +++ b/esp-link/cgi.h @@ -4,6 +4,9 @@ #include "httpd.h" void jsonHeader(HttpdConnData *connData, int code); +void errorResponse(HttpdConnData *connData, int code, char *message); +int getStringArg(HttpdConnData *connData, char *name, char *config, int max_len); +int getBoolArg(HttpdConnData *connData, char *name, bool*config); int cgiMenu(HttpdConnData *connData); #endif diff --git a/esp-link/cgimqtt.c b/esp-link/cgimqtt.c index dbfbc14..cbf4c16 100644 --- a/esp-link/cgimqtt.c +++ b/esp-link/cgimqtt.c @@ -38,34 +38,56 @@ int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; -#if 0 - // 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; + // handle MQTT server settings + int mqtt_server = 0; // accumulator for changes/errors + mqtt_server |= getStringArg(connData, "mqtt-host", + flashConfig.mqtt_hostname, sizeof(flashConfig.mqtt_hostname)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getStringArg(connData, "mqtt-client-id", + flashConfig.mqtt_client, sizeof(flashConfig.mqtt_client)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getStringArg(connData, "mqtt-username", + flashConfig.mqtt_username, sizeof(flashConfig.mqtt_username)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getStringArg(connData, "mqtt-password", + flashConfig.mqtt_password, sizeof(flashConfig.mqtt_password)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getBoolArg(connData, "mqtt-enable", + &flashConfig.mqtt_enable); + + // handle mqtt port + char buff[16]; + if (httpdFindArg(connData->getArgs, "mqtt-port", buff, sizeof(buff)) > 0) { + int32_t port = atoi(buff); + if (port > 0 && port < 65536) { + flashConfig.mqtt_port = port; + mqtt_server |= 1; + } else { + errorResponse(connData, 400, "Invalid MQTT port"); + 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; + // if server setting changed, we need to "make it so" + if (mqtt_server) { + os_printf("MQTT server settings changed, enable=%d\n", flashConfig.mqtt_enable); + // TODO } - 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); + // no action required if mqtt status settings change, they just get picked up at the + // next status tick + if (getBoolArg(connData, "mqtt-status-enable", &flashConfig.mqtt_status_enable) < 0) return HTTPD_CGI_DONE; - } - buff[sizeof(flashConfig.api_key)-1] = 0; // ensure we don't get an overrun - os_strcpy(flashConfig.api_key, buff); -#endif + if (getStringArg(connData, "mqtt-status-topic", + flashConfig.mqtt_status_topic, sizeof(flashConfig.mqtt_status_topic)) < 0) + return HTTPD_CGI_DONE; + + // if SLIP-enable is toggled it gets picked-up immediately by the parser + int slip_update = getBoolArg(connData, "slip-enable", &flashConfig.slip_enable); + if (slip_update < 0) return HTTPD_CGI_DONE; + if (slip_update > 0) os_printf("SLIP-enable changed: %d\n", flashConfig.slip_enable); + os_printf("Saving config\n"); if (configSave()) { httpdStartResponse(connData, 200); httpdEndHeaders(connData); diff --git a/html/mqtt.html b/html/mqtt.html index dd9653c..257c137 100644 --- a/html/mqtt.html +++ b/html/mqtt.html @@ -71,18 +71,17 @@
+ Suffixes: rssi, heap-free, ...
+

REST

REST requests are enabled as soon as SLIP is enabled. There are no REST-specific settings.

-
@@ -94,6 +93,7 @@ onLoad(function() { fetchMqtt(); bnd($("#mqtt-form"), "submit", changeMqtt); + bnd($("#mqtt-status-form"), "submit", changeMqttStatus); }); diff --git a/html/ui.js b/html/ui.js index 2a4e20e..1d4c365 100644 --- a/html/ui.js +++ b/html/ui.js @@ -316,44 +316,44 @@ function createInputForPin(pin) { input.type = "radio"; input.name = "pins"; input.data = pin.name; - input.className = "pin-input"; + 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; + 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 0) { + //os_printf("SLIP: disabled got %d\n", length); + console_process(buf, length); + } else { + slip_parse_buf(buf, length); + } + + serledFlash(50); // short blink on serial LED +} + //callback after the data are sent static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) { diff --git a/serial/slip.c b/serial/slip.c index 0e067a1..76948d5 100644 --- a/serial/slip.c +++ b/serial/slip.c @@ -4,11 +4,10 @@ #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 +uint8_t slip_disabled; // temporarily disable slip to allow flashing of attached MCU extern void ICACHE_FLASH_ATTR console_process(char *buf, short len); @@ -115,18 +114,7 @@ slip_parse_char(char 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 Date: Thu, 10 Sep 2015 22:26:47 -0700 Subject: [PATCH 073/104] mqtt status client hook-up --- Makefile | 14 ++ cmd/tcpclient.c | 315 ----------------------------------------- cmd/tcpclient.h | 16 --- esp-link/log.c | 225 +++++++++++++++-------------- esp-link/log.h | 2 + esp-link/main.c | 8 +- esp-link/mqtt_client.c | 90 ++++++++++++ esp-link/status.c | 69 ++++----- mqtt/mqtt.c | 8 +- 9 files changed, 266 insertions(+), 481 deletions(-) delete mode 100644 cmd/tcpclient.c delete mode 100644 cmd/tcpclient.h create mode 100644 esp-link/mqtt_client.c diff --git a/Makefile b/Makefile index 98a8361..8534cfc 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,20 @@ 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 ifeq ("$(FLASH_SIZE)","2MB") +# Manuf 0xA1 Chip 0x4015 found on wroom-02 modules +# 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 (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 ?= 16m # 16Mbit flash size in esptool flash command +ET_FF ?= 80m # 80Mhz flash speed in esptool flash command +ET_BLANK ?= 0x1FE000 # 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 diff --git a/cmd/tcpclient.c b/cmd/tcpclient.c deleted file mode 100644 index fa1b458..0000000 --- a/cmd/tcpclient.c +++ /dev/null @@ -1,315 +0,0 @@ -// 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" -#include "uart.h" -#include "serled.h" -#include "tcpclient.h" - -// max number of channels the client can open -#define MAX_CHAN MAX_TCP_CHAN -// 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; - -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 -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 - -// 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); -} - -//===== 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 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); - } -} - -// 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) { - 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; - } -} - -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); -} - -//===== Command parsing - -// Perform a TCP command: parse the command and do the right thing. -// 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; -} - diff --git a/cmd/tcpclient.h b/cmd/tcpclient.h deleted file mode 100644 index ff0ff9d..0000000 --- a/cmd/tcpclient.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __TCP_CLIENT_H__ -#define __TCP_CLIENT_H__ - -// max number of channels the client can open -#define MAX_TCP_CHAN 8 - -// 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 -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/esp-link/log.c b/esp-link/log.c index 9103a41..93da9aa 100644 --- a/esp-link/log.c +++ b/esp-link/log.c @@ -22,142 +22,155 @@ static bool log_newline; // at start of a new line // when we connect to wifi AP. Here this is gated by the flash setting void ICACHE_FLASH_ATTR log_uart(bool enable) { - if (!enable && !log_no_uart && flashConfig.log_mode != LOG_MODE_ON) { - // we're asked to turn uart off, and uart is on, and the flash setting isn't always-on + if (!enable && !log_no_uart && flashConfig.log_mode != LOG_MODE_ON) { + // we're asked to turn uart off, and uart is on, and the flash setting isn't always-on #if 1 - os_printf("Turning OFF uart log\n"); - os_delay_us(4*1000L); // time for uart to flush - log_no_uart = !enable; + os_printf("Turning OFF uart log\n"); + os_delay_us(4*1000L); // time for uart to flush + log_no_uart = !enable; #endif - } else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) { - // we're asked to turn uart on, and uart is off, and the flash setting isn't always-off - log_no_uart = !enable; - os_printf("Turning ON uart log\n"); - } + } else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) { + // we're asked to turn uart on, and uart is off, and the flash setting isn't always-off + log_no_uart = !enable; + os_printf("Turning ON uart log\n"); + } } static void ICACHE_FLASH_ATTR log_write(char c) { - log_buf[log_wr] = c; - log_wr = (log_wr+1) % BUF_MAX; - if (log_wr == log_rd) { - log_rd = (log_rd+1) % BUF_MAX; // full, eat first char - log_pos++; - } + log_buf[log_wr] = c; + log_wr = (log_wr+1) % BUF_MAX; + if (log_wr == log_rd) { + log_rd = (log_rd+1) % BUF_MAX; // full, eat first char + log_pos++; + } } #if 0 static char ICACHE_FLASH_ATTR log_read(void) { - char c = 0; - if (log_rd != log_wr) { - c = log_buf[log_rd]; - log_rd = (log_rd+1) % BUF_MAX; - } - return c; + char c = 0; + if (log_rd != log_wr) { + c = log_buf[log_rd]; + log_rd = (log_rd+1) % BUF_MAX; + } + return c; } #endif static void ICACHE_FLASH_ATTR log_write_char(char c) { - // Uart output unless disabled - if (!log_no_uart) { - if (log_newline) { - char buff[16]; - int l = os_sprintf(buff, "%6d> ", (system_get_time()/1000)%1000000); - for (int i=0; i ", (system_get_time()/1000)%1000000); + for (int i=0; iconn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - - // figure out where to start in buffer based on URI param - len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); - if (len > 0) { - start = atoi(buff); - if (start < log_pos) { - start = 0; - } else if (start >= log_pos+log_len) { - start = log_len; - } else { - start = start - log_pos; - } - } - - // start outputting - len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", - log_len-start, log_pos+start); - - int rd = (log_rd+start) % BUF_MAX; - while (len < 2040 && rd != log_wr) { - uint8_t c = log_buf[rd]; - if (c == '\\' || c == '"') { - buff[len++] = '\\'; - buff[len++] = c; - } 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; + char buff[2048]; + int len; // length of text in buff + int log_len = (log_wr+BUF_MAX-log_rd) % BUF_MAX; // num chars in log_buf + int start = 0; // offset onto log_wr to start sending out chars + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + + // figure out where to start in buffer based on URI param + len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); + if (len > 0) { + start = atoi(buff); + if (start < log_pos) { + start = 0; + } else if (start >= log_pos+log_len) { + start = log_len; + } else { + start = start - log_pos; + } + } + + // start outputting + len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", + log_len-start, log_pos+start); + + int rd = (log_rd+start) % BUF_MAX; + while (len < 2040 && rd != log_wr) { + uint8_t c = log_buf[rd]; + if (c == '\\' || c == '"') { + buff[len++] = '\\'; + buff[len++] = c; + } 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; } static char *dbg_mode[] = { "auto", "off", "on" }; int ICACHE_FLASH_ATTR ajaxLogDbg(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, "mode", buff, sizeof(buff)); - if (len > 0) { - int8_t mode = -1; - if (os_strcmp(buff, "auto") == 0) mode = LOG_MODE_AUTO; - if (os_strcmp(buff, "off") == 0) mode = LOG_MODE_OFF; - if (os_strcmp(buff, "on") == 0) mode = LOG_MODE_ON; - if (mode >= 0) { - flashConfig.log_mode = mode; - if (mode != LOG_MODE_AUTO) log_uart(mode == LOG_MODE_ON); - status = configSave() ? 200 : 400; - } - } else if (connData->requestType == HTTPD_METHOD_GET) { - status = 200; - } - - jsonHeader(connData, status); - os_sprintf(buff, "{\"mode\": \"%s\"}", dbg_mode[flashConfig.log_mode]); - 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, "mode", buff, sizeof(buff)); + if (len > 0) { + int8_t mode = -1; + if (os_strcmp(buff, "auto") == 0) mode = LOG_MODE_AUTO; + if (os_strcmp(buff, "off") == 0) mode = LOG_MODE_OFF; + if (os_strcmp(buff, "on") == 0) mode = LOG_MODE_ON; + if (mode >= 0) { + flashConfig.log_mode = mode; + if (mode != LOG_MODE_AUTO) log_uart(mode == LOG_MODE_ON); + status = configSave() ? 200 : 400; + } + } else if (connData->requestType == HTTPD_METHOD_GET) { + status = 200; + } + + jsonHeader(connData, status); + os_sprintf(buff, "{\"mode\": \"%s\"}", dbg_mode[flashConfig.log_mode]); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } +void ICACHE_FLASH_ATTR dumpMem(void *addr, int len) { + uint8_t *a = addr; + int off = 0; + while (off < len) { + os_printf("%p ", a); + for (int i=0; i<16 && off+i 0x20 && *a < 0x3f ? *a : '.'); + os_printf("\n"); + } +} void ICACHE_FLASH_ATTR logInit() { - log_no_uart = flashConfig.log_mode == LOG_MODE_OFF; // ON unless set to always-off - log_wr = 0; - log_rd = 0; + log_no_uart = flashConfig.log_mode == LOG_MODE_OFF; // ON unless set to always-off + log_wr = 0; + log_rd = 0; os_install_putc1((void *)log_write_char); } diff --git a/esp-link/log.h b/esp-link/log.h index 94d52c0..bf2c763 100644 --- a/esp-link/log.h +++ b/esp-link/log.h @@ -12,4 +12,6 @@ void log_uart(bool enable); int ajaxLog(HttpdConnData *connData); int ajaxLogDbg(HttpdConnData *connData); +void dumpMem(void *addr, int len); + #endif diff --git a/esp-link/main.c b/esp-link/main.c index aea1846..1790d66 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -117,6 +117,10 @@ extern uint32_t _binary_espfs_img_start; static char *rst_codes[] = { "normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "external", }; +static char *flash_maps[] = { + "512KB:256/256", "256KB", "1MB:512/512", "2MB:512/512", "4MB:512/512", + "2MB:1024/1024", "4MB:1024/1024" +}; # define VERS_STR_STR(V) #V # define VERS_STR(V) VERS_STR_STR(V) @@ -167,7 +171,9 @@ 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()); + uint32_t fid = spi_flash_get_id(); + os_printf("Flash map %s, manuf 0x%02lX chip 0x%04lX\n", flash_maps[system_get_flash_size_map()], + fid & 0xff, (fid&0xff00)|((fid>>16)&0xff)); os_printf("** esp-link ready\n"); diff --git a/esp-link/mqtt_client.c b/esp-link/mqtt_client.c new file mode 100644 index 0000000..8e8c936 --- /dev/null +++ b/esp-link/mqtt_client.c @@ -0,0 +1,90 @@ +#include +#include "cgiwifi.h" +#include "config.h" +#include "mqtt.h" + +MQTT_Client mqttClient; + +static ETSTimer mqttTimer; + +static int once = 0; +static void ICACHE_FLASH_ATTR mqttTimerCb(void *arg) { + if (once++ > 0) return; + MQTT_Init(&mqttClient, flashConfig.mqtt_hostname, flashConfig.mqtt_port, 0, 2, + flashConfig.mqtt_client, flashConfig.mqtt_username, flashConfig.mqtt_password, + 60, 1); + MQTT_Connect(&mqttClient); + MQTT_Subscribe(&mqttClient, "system/time", 0); +} + +void ICACHE_FLASH_ATTR +wifiStateChangeCb(uint8_t status) +{ + if (status == wifiGotIP) { + os_timer_disarm(&mqttTimer); + os_timer_setfn(&mqttTimer, mqttTimerCb, NULL); + os_timer_arm(&mqttTimer, 200, 0); + } +} + + +// initialize the custom stuff that goes beyond esp-link +void mqtt_client_init() { + wifiAddStateChangeCb(wifiStateChangeCb); +} + + +#if 0 +MQTT_Client mqttClient; + +void ICACHE_FLASH_ATTR +mqttConnectedCb(uint32_t *args) { + MQTT_Client* client = (MQTT_Client*)args; + MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); +} + +void ICACHE_FLASH_ATTR +mqttDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Disconnected\n"); +} + +void ICACHE_FLASH_ATTR +mqttTcpDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT TCP Disconnected\n"); +} + +void ICACHE_FLASH_ATTR +mqttPublishedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Published\n"); +} + +void ICACHE_FLASH_ATTR +mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { + char *topicBuf = (char*)os_zalloc(topic_len + 1); + char *dataBuf = (char*)os_zalloc(data_len + 1); + +// MQTT_Client* client = (MQTT_Client*)args; + + os_memcpy(topicBuf, topic, topic_len); + topicBuf[topic_len] = 0; + + os_memcpy(dataBuf, data, data_len); + dataBuf[data_len] = 0; + + os_printf("Receive topic: %s, data: %s\n", topicBuf, dataBuf); + os_free(topicBuf); + os_free(dataBuf); +} + + MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); + MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); + MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); + MQTT_OnConnected(&mqttClient, mqttConnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); + MQTT_OnPublished(&mqttClient, mqttPublishedCb); + MQTT_OnData(&mqttClient, mqttDataCb); +#endif diff --git a/esp-link/status.c b/esp-link/status.c index 4e0da55..5d9562f 100644 --- a/esp-link/status.c +++ b/esp-link/status.c @@ -4,7 +4,9 @@ #include "config.h" #include "serled.h" #include "cgiwifi.h" -#include "tcpclient.h" +#include "mqtt.h" + +extern MQTT_Client mqttClient; //===== "CONN" LED status indication @@ -66,51 +68,34 @@ void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { os_timer_arm(&ledTimer, 500, 0); } -//===== RSSI Status update sent to GroveStreams +//===== MQTT Status update + +// Every minute... +#define MQTT_STATUS_INTERVAL (60*1000) -#define RSSI_INTERVAL (60*1000) +static ETSTimer mqttStatusTimer; -static ETSTimer rssiTimer; +static int ICACHE_FLASH_ATTR +mqttStatusMsg(char *buf) { + sint8 rssi = wifi_station_get_rssi(); + if (rssi > 0) rssi = 0; // not connected or other error + //os_printf("timer rssi=%d\n", rssi); -#define GS_STREAM "rssi" + // compose MQTT message + return os_sprintf(buf, + "{\"rssi\":%d, \"heap_free\":%ld}", + rssi, (unsigned long)system_get_free_heap_size()); +} // 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) +static void ICACHE_FLASH_ATTR mqttStatusCb(void *v) { + if (!flashConfig.mqtt_status_enable || os_strlen(flashConfig.mqtt_status_topic) == 0 || + mqttClient.connState != MQTT_CONNECTED) 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 - uint8_t chan = MAX_TCP_CHAN-1; - tcpClientCommand(chan, 'T', "grovestreams.com:80"); - - // 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", - flashConfig.api_key); - - // http body - int dataLen = os_sprintf(buf+hdrLen, - "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", - flashConfig.hostname, GS_STREAM, rssi); - buf[hdrLen+dataLen++] = 0; - buf[hdrLen+dataLen++] = '\n'; - - // hackish way to fill in the content-length - os_sprintf(buf+hdrLen-9, "%5d", dataLen); - buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) - - // send the request off and forget about it... - for (short i=0; ipending_buffer->data, client->pending_buffer->filled); } - os_printf("MQTT: Recv type=%s id=%04X len=%d; Pend type=%s id=%02X\n", + os_printf("MQTT: Recv type=%s id=%04X len=%d; Pend type=%s id=%04X\n", mqtt_msg_type[msg_type], msg_id, msg_len, mqtt_msg_type[pending_msg_type], pending_msg_id); switch (msg_type) { @@ -553,8 +555,12 @@ MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t q return FALSE; } client->mqtt_connection.message_id = msg.message_id; + if (msg.message.data != buf->data) + os_memcpy(buf->data, msg.message.data, msg.message.length); + buf->filled = msg.message.length; os_printf("MQTT: Publish, topic: \"%s\", length: %d\n", topic, msg.message.length); + dumpMem(buf, buf_len); client->msgQueue = PktBuf_Push(client->msgQueue, buf); if (!client->sending && client->pending_buffer == NULL) { From 19693a11eca42234597da9e80473c68fd772f4c3 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Thu, 10 Sep 2015 22:28:04 -0700 Subject: [PATCH 074/104] mqtt status client hook-up --- esp-link/main.c | 3 +++ user/user_main.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/esp-link/main.c b/esp-link/main.c index 1790d66..25a528a 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -127,6 +127,7 @@ static char *flash_maps[] = { char* esp_link_version = VERS_STR(VERSION); extern void app_init(void); +extern void mqtt_client_init(void); // Main routine to initialize esp-link. void user_init(void) { @@ -177,5 +178,7 @@ void user_init(void) { os_printf("** esp-link ready\n"); + mqtt_client_init(); + app_init(); } diff --git a/user/user_main.c b/user/user_main.c index cd0dbf0..e5dd622 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -2,6 +2,8 @@ #include "cgiwifi.h" #include "mqtt.h" +#if 0 + MQTT_Client mqttClient; static ETSTimer mqttTimer; @@ -31,7 +33,6 @@ void app_init() { } -#if 0 MQTT_Client mqttClient; void ICACHE_FLASH_ATTR From 215da43a44412cac202b3fc8d9098690b664ec81 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Thu, 10 Sep 2015 22:49:48 -0700 Subject: [PATCH 075/104] ensure reconnect rexmits pending messages --- esp-link/mqtt_client.c | 3 +-- esp-link/status.c | 4 +++- mqtt/mqtt.c | 8 +++++--- mqtt/mqtt.h | 2 +- user/user_main.c | 28 ++-------------------------- 5 files changed, 12 insertions(+), 33 deletions(-) diff --git a/esp-link/mqtt_client.c b/esp-link/mqtt_client.c index 8e8c936..229d996 100644 --- a/esp-link/mqtt_client.c +++ b/esp-link/mqtt_client.c @@ -11,8 +11,7 @@ static int once = 0; static void ICACHE_FLASH_ATTR mqttTimerCb(void *arg) { if (once++ > 0) return; MQTT_Init(&mqttClient, flashConfig.mqtt_hostname, flashConfig.mqtt_port, 0, 2, - flashConfig.mqtt_client, flashConfig.mqtt_username, flashConfig.mqtt_password, - 60, 1); + flashConfig.mqtt_client, flashConfig.mqtt_username, flashConfig.mqtt_password, 60); MQTT_Connect(&mqttClient); MQTT_Subscribe(&mqttClient, "system/time", 0); } diff --git a/esp-link/status.c b/esp-link/status.c index 5d9562f..2b08b20 100644 --- a/esp-link/status.c +++ b/esp-link/status.c @@ -95,7 +95,9 @@ static void ICACHE_FLASH_ATTR mqttStatusCb(void *v) { char buf[128]; mqttStatusMsg(buf); - MQTT_Publish(&mqttClient, flashConfig.mqtt_status_topic, buf, 0, 0); + MQTT_Publish(&mqttClient, flashConfig.mqtt_status_topic, buf, 1, 0); + + //espconn_disconnect(mqttClient.pCon); } //===== Init status stuff diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c index f70cda9..f4ca18c 100644 --- a/mqtt/mqtt.c +++ b/mqtt/mqtt.c @@ -325,6 +325,7 @@ mqtt_timer(void* arg) { client->msgQueue = PktBuf_Unshift(client->msgQueue, client->pending_buffer); client->pending_buffer = NULL; } + client->connect_info.clean_session = 0; // ask server to keep state MQTT_Connect(client); } } @@ -607,7 +608,7 @@ MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos) { void ICACHE_FLASH_ATTR MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security, uint8_t sendTimeout, char* client_id, char* client_user, char* client_pass, - uint8_t keepAliveTime, uint8_t cleanSession) { + uint8_t keepAliveTime) { os_printf("MQTT_Init\n"); os_memset(mqttClient, 0, sizeof(MQTT_Client)); @@ -633,7 +634,7 @@ MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security, ui os_strcpy(mqttClient->connect_info.password, client_pass); mqttClient->connect_info.keepalive = keepAliveTime; - mqttClient->connect_info.clean_session = cleanSession; + mqttClient->connect_info.clean_session = 1; mqttClient->in_buffer = (uint8_t *)os_zalloc(MQTT_MAX_RCV_MESSAGE); mqttClient->in_buffer_size = MQTT_MAX_RCV_MESSAGE; @@ -708,7 +709,6 @@ MQTT_Connect(MQTT_Client* mqttClient) { mqttClient->connState = TCP_CONNECTING; mqttClient->timeoutTick = 20; // generous timeout to allow for DNS, etc mqttClient->sending = FALSE; - mqttClient->msgQueue = NULL; } static void ICACHE_FLASH_ATTR @@ -741,6 +741,8 @@ MQTT_Disconnect(MQTT_Client* mqttClient) { return; } mqtt_doAbort(mqttClient); + //void *out_buffer = mqttClient->mqtt_connection.buffer; + //if (out_buffer != NULL) os_free(out_buffer); mqttClient->connState = MQTT_DISCONNECTED; // ensure we don't automatically reconnect } diff --git a/mqtt/mqtt.h b/mqtt/mqtt.h index 6ca7091..3155dc8 100644 --- a/mqtt/mqtt.h +++ b/mqtt/mqtt.h @@ -96,7 +96,7 @@ typedef struct { void MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security, uint8_t sendTimeout, char* client_id, char* client_user, char* client_pass, - uint8_t keepAliveTime, uint8_t cleanSession); + uint8_t keepAliveTime); // Set Last Will Topic on client, must be called before MQTT_InitConnection void MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, diff --git a/user/user_main.c b/user/user_main.c index e5dd622..72608c6 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -2,36 +2,12 @@ #include "cgiwifi.h" #include "mqtt.h" -#if 0 - -MQTT_Client mqttClient; - -static ETSTimer mqttTimer; - -static int once = 0; -static void ICACHE_FLASH_ATTR mqttTimerCb(void *arg) { - if (once++ > 0) return; - MQTT_Init(&mqttClient, "h.voneicken.com", 1883, 0, 2, "test1", "", "", 10, 1); - MQTT_Connect(&mqttClient); - MQTT_Subscribe(&mqttClient, "system/time", 0); -} - -void ICACHE_FLASH_ATTR -wifiStateChangeCb(uint8_t status) -{ - if (status == wifiGotIP) { - os_timer_disarm(&mqttTimer); - os_timer_setfn(&mqttTimer, mqttTimerCb, NULL); - os_timer_arm(&mqttTimer, 15000, 0); - } -} - - // initialize the custom stuff that goes beyond esp-link void app_init() { - wifiAddStateChangeCb(wifiStateChangeCb); + } +#if 0 MQTT_Client mqttClient; From b7dd0b180f8ff441a8cba513a488d3c612862082 Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 11 Sep 2015 15:56:51 -0500 Subject: [PATCH 076/104] lots of ifdef --- .gitignore | 1 + Makefile | 99 ++- cmd/cmd.c | 2 - cmd/cmd.h | 4 +- cmd/handlers.c | 22 +- esp-link.vcxproj | 11 +- esp-link/cgi.c | 12 +- esp-link/cgi.h | 1 - esp-link/cgiflash.c | 12 + esp-link/cgimqtt.c | 50 +- esp-link/cgipins.c | 3 +- esp-link/cgiwifi.c | 48 +- esp-link/config.c | 49 +- esp-link/config.h | 9 +- esp-link/log.c | 4 + esp-link/main.c | 5 +- esp-link/status.c | 15 +- espfs/espfs.c | 8 + html/console.js | 72 ++ html/favicon.ico | Bin 0 -> 498 bytes html/head- | 10 + html/home.html | 88 +- html/log.html | 49 +- html/mqtt.html | 23 +- html/pure.css | 1388 ++++++++++++++++++++++++++++++++ html/style.css | 5 + html/wifi/icons.png | Bin 0 -> 425 bytes html/wifi/wifi.html | 85 +- html/wifi/wifi.js | 200 +++++ httpd/httpd.c | 894 ++++++++++---------- include/user_config.h | 44 +- mqtt/mqtt.c | 78 +- mqtt/mqtt_cmd.c | 33 +- mqtt/pktbuf.c | 4 +- mqtt/pktbuf.h | 2 - rest/rest.c | 60 +- serial/serbridge.c | 41 +- serial/serled.c | 2 + serial/slip.c | 5 + {cmd => tcpclient}/tcpclient.c | 0 {cmd => tcpclient}/tcpclient.h | 0 user/latch_json.c | 64 ++ user/latch_json.h | 12 + user/user_funcs.c | 9 +- user/user_funcs.h | 1 + user/user_main.c | 76 +- 46 files changed, 2979 insertions(+), 621 deletions(-) create mode 100644 html/console.js create mode 100644 html/favicon.ico create mode 100644 html/pure.css create mode 100644 html/wifi/icons.png create mode 100644 html/wifi/wifi.js rename {cmd => tcpclient}/tcpclient.c (100%) rename {cmd => tcpclient}/tcpclient.h (100%) create mode 100644 user/latch_json.c create mode 100644 user/latch_json.h diff --git a/.gitignore b/.gitignore index 899b7c3..a541909 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ esp-link.opensdf esp-link.sdf espfs/mkespfsimage/mman-win32/libmman.a .localhistory/ +tools/ diff --git a/Makefile b/Makefile index 23e57be..4956590 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ ESPBAUD ?= 230400 # --------------- 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 @@ -70,7 +70,7 @@ ET_BLANK ?= 0x3FE000 # where to flash blank.bin to erase wireless set endif # hostname or IP address for wifi flashing -ESP_HOSTNAME ?= esp-link +ESP_HOSTNAME ?= esp-nodemcu # The pin assignments below are used when the settings in flash are invalid, they # can be changed via the web interface @@ -122,15 +122,20 @@ CHANGE_TO_STA ?= yes #Static gzipping is disabled by default. GZIP_COMPRESSION ?= yes -# If COMPRESS_W_YUI is set to "yes" then the static css and js files will be compressed with -# yui-compressor. This option works only when GZIP_COMPRESSION is set to "yes". +# If COMPRESS_W_HTMLCOMPRESSOR is set to "yes" then the static css and js files will be compressed with +# htmlcompressor and yui-compressor. This option works only when GZIP_COMPRESSION is set to "yes". +# https://code.google.com/p/htmlcompressor/#For_Non-Java_Projects # http://yui.github.io/yuicompressor/ -#Disabled by default. -COMPRESS_W_YUI ?= yes +# enabled by default. +COMPRESS_W_HTMLCOMPRESSOR ?= yes +HTML-COMPRESSOR ?= htmlcompressor-1.5.3.jar YUI-COMPRESSOR ?= yuicompressor-2.4.8.jar +HTML_PATH = $(abspath ./html)/ +WIFI_PATH = $(HTML_PATH)wifi/ + # Optional Modules -MODULES ?= +MODULES ?= mqtt rest # -------------- End of config options ------------- @@ -144,7 +149,6 @@ TARGET = httpd # espressif tool to concatenate sections for OTA upload using bootloader v1.2+ APPGEN_TOOL ?= gen_appbin.py - CFLAGS= # set defines for optional modules @@ -156,19 +160,23 @@ ifneq (,$(findstring rest,$(MODULES))) CFLAGS += -DREST endif +ifneq (,$(findstring tcpclient,$(MODULES))) + CFLAGS += -DTCPCLIENT +endif + # which modules (subdirectories) of the project to include in compiling -LIBRARIES_DIR = libraries -MODULES = espfs httpd user serial cmd mqtt esp-link +LIBRARIES_DIR = libraries +MODULES += espfs httpd user serial cmd esp-link MODULES += $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*)) -EXTRA_INCDIR = include . include/json +EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal phy pp net80211 wpa main lwip json +LIBS = c gcc hal phy pp net80211 wpa main lwip # compiler flags using during compilation of source files -CFLAGS += -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ +CFLAGS += -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ -nostdlib -mlongcalls -mtext-section-literals -ffunction-sections -fdata-sections \ - -Wno-unused-function \ + -Wno-unused-function -Wno-unused-variable \ -D__ets__ -DICACHE_FLASH -D_STDINT_H -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) \ -DMCU_RESET_PIN=$(MCU_RESET_PIN) -DMCU_ISP_PIN=$(MCU_ISP_PIN) \ -DLED_CONN_PIN=$(LED_CONN_PIN) -DLED_SERIAL_PIN=$(LED_SERIAL_PIN) \ @@ -203,17 +211,17 @@ BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) SDK_LDDIR := $(addprefix $(SDK_BASE)/,$(SDK_LDDIR)) SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) -SDK_TOOLS := $(addprefix $(SDK_BASE)/,$(SDK_TOOLSDIR)) +SDK_TOOLS := $(addprefix $(SDK_BASE)/,$(SDK_TOOLSDIR)) APPGEN_TOOL := $(addprefix $(SDK_TOOLS)/,$(APPGEN_TOOL)) -SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) -OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) $(BUILD_BASE)/espfs_img.o +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) USER1_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).user1.out) USER2_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).user2.out) -INCDIR := $(addprefix -I,$(SRC_DIR)) +INCDIR := $(addprefix -I,$(SRC_DIR)) EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) @@ -275,7 +283,7 @@ $(FW_BASE)/user1.bin: $(USER1_OUT) $(FW_BASE) $(Q) rm -f eagle.app.v6.*.bin $(Q) mv eagle.app.flash.bin $@ @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 + $(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; true; fi $(FW_BASE)/user2.bin: $(USER2_OUT) $(FW_BASE) $(Q) $(OBJCP) --only-section .text -O binary $(USER2_OUT) eagle.app.v6.text.bin @@ -285,7 +293,7 @@ $(FW_BASE)/user2.bin: $(USER2_OUT) $(FW_BASE) $(Q) COMPILE=gcc PATH=$(XTENSA_TOOLS_ROOT):$(PATH) python $(APPGEN_TOOL) $(USER2_OUT) 2 $(ESP_FLASH_MODE) $(ESP_FLASH_FREQ_DIV) $(ESP_SPI_SIZE) $(Q) rm -f eagle.app.v6.*.bin $(Q) mv eagle.app.flash.bin $@ - $(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi + $(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; true; fi $(APP_AR): $(OBJ) $(vecho) "AR $@" @@ -306,35 +314,52 @@ flash: all $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \ 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \ $(ET_BLANK) $(SDK_BASE)/bin/blank.bin - -yui/$(YUI-COMPRESSOR): - $(Q) mkdir -p yui + +tools/$(HTML-COMPRESSOR): + $(Q) mkdir -p tools ifeq ($(OS),Windows_NT) - cd yui; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) + cd tools; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) -O $(YUI-COMPRESSOR) + cd tools; wget --no-check-certificate https://htmlcompressor.googlecode.com/files/$(HTML-COMPRESSOR) -O $(HTML-COMPRESSOR) else - cd yui; wget https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) + cd tools; wget https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI-COMPRESSOR) + cd tools; wget https://htmlcompressor.googlecode.com/files/$(HTML-COMPRESSOR) endif -ifeq ("$(COMPRESS_W_YUI)","yes") -$(BUILD_BASE)/espfs_img.o: yui/$(YUI-COMPRESSOR) +ifeq ("$(COMPRESS_W_HTMLCOMPRESSOR)","yes") +$(BUILD_BASE)/espfs_img.o: tools/$(HTML-COMPRESSOR) endif $(BUILD_BASE)/espfs_img.o: html/ html/wifi/ espfs/mkespfsimage/mkespfsimage - $(Q) rm -rf html_compressed; - $(Q) cp -r html html_compressed; - $(Q) for file in `find html_compressed -type f -name "*.htm*"`; do \ - cat html_compressed/head- $$file >$${file}-; \ - mv $$file- $$file; \ - done -ifeq ("$(COMPRESS_W_YUI)","yes") + $(Q) rm -rf html_compressed; mkdir html_compressed; mkdir html_compressed/wifi; + $(Q) cp -r html/*.ico html_compressed; + $(Q) cp -r html/*.css html_compressed; + $(Q) cp -r html/*.js html_compressed; + $(Q) cp -r html/wifi/*.png html_compressed/wifi; + $(Q) cp -r html/wifi/*.js html_compressed/wifi; +ifeq ("$(COMPRESS_W_HTMLCOMPRESSOR)","yes") + $(Q) echo "Compression assets with htmlcompressor. This may take a while..." + $(Q) java -jar tools/$(HTML-COMPRESSOR) \ + -t html --remove-surrounding-spaces max --remove-quotes --remove-intertag-spaces \ + -o $(abspath ./html_compressed)/ \ + $(HTML_PATH)head- \ + $(HTML_PATH)*.html + $(Q) java -jar tools/$(HTML-COMPRESSOR) \ + -t html --remove-surrounding-spaces max --remove-quotes --remove-intertag-spaces \ + -o $(abspath ./html_compressed)/wifi/ \ + $(WIFI_PATH)*.html $(Q) echo "Compression assets with yui-compressor. This may take a while..." $(Q) for file in `find html_compressed -type f -name "*.js"`; do \ - java -jar yui/$(YUI-COMPRESSOR) $$file --nomunge --line-break 40 -o $$file; \ + java -jar tools/$(YUI-COMPRESSOR) $$file -o $$file; \ done $(Q) for file in `find html_compressed -type f -name "*.css"`; do \ - java -jar yui/$(YUI-COMPRESSOR) $$file -o $$file; \ + java -jar tools/$(YUI-COMPRESSOR) $$file -o $$file; \ done endif + $(Q) for file in `find html_compressed -type f -name "*.htm*"`; do \ + cat html_compressed/head- $$file >$${file}-; \ + mv $$file- $$file; \ + done + $(Q) rm html_compressed/head- $(Q) cd html_compressed; find . \! -name \*- | ../espfs/mkespfsimage/mkespfsimage > ../build/espfs.img; cd ..; $(Q) ls -sl build/espfs.img $(Q) cd build; $(OBJCP) -I binary -O elf32-xtensa-le -B xtensa --rename-section .data=.espfs \ @@ -384,7 +409,7 @@ clean: $(Q) make -C espfs/mkespfsimage/ clean $(Q) rm -rf $(FW_BASE) $(Q) rm -f webpages.espfs -ifeq ("$(COMPRESS_W_YUI)","yes") +ifeq ("$(COMPRESS_W_HTMLCOMPRESSOR)","yes") $(Q) rm -rf html_compressed endif diff --git a/cmd/cmd.c b/cmd/cmd.c index 07a92a5..f3bcbba 100644 --- a/cmd/cmd.c +++ b/cmd/cmd.c @@ -2,10 +2,8 @@ // // 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[]; diff --git a/cmd/cmd.h b/cmd/cmd.h index 97c8236..2d07e1c 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -55,8 +55,8 @@ typedef enum { CMD_REST_REQUEST, CMD_REST_SETHEADER, CMD_REST_EVENTS, - CMD_ADD_CALLBACK, // 15 - CMD_SENSOR_EVENTS + CMD_CB_ADD, // 15 + CMD_CB_EVENTS } CmdName; typedef uint32_t (*cmdfunc_t)(CmdPacket *cmd); diff --git a/cmd/handlers.c b/cmd/handlers.c index adf90b9..339c1dd 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -41,7 +41,7 @@ const CmdList commands[] = { {CMD_REST_REQUEST, REST_Request}, {CMD_REST_SETHEADER, REST_SetHeader}, #endif - { CMD_CB_ADD, CMD_AddCallback }, + {CMD_CB_ADD, CMD_AddCallback}, {CMD_NULL, NULL} }; @@ -52,14 +52,18 @@ cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset // Command handler for IsReady (healthcheck) command static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd) { +#ifdef CMD_DBG os_printf("CMD_IsReady: Check ready\n"); +#endif return 1; } // Command handler for Null command static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd) { +#ifdef CMD_DBG os_printf("CMD_Null: NULL/unsupported command\n"); +#endif return 1; } @@ -68,7 +72,9 @@ CMD_Null(CmdPacket *cmd) { // uC. static uint32_t ICACHE_FLASH_ATTR CMD_Reset(CmdPacket *cmd) { +#ifdef CMD_DBG os_printf("CMD_Reset\n"); +#endif // clear callbacks table os_memset(callbacks, 0, sizeof(callbacks)); return 1; @@ -83,7 +89,9 @@ CMD_AddCb(char* name, uint32_t cb) { if (os_strcmp(callbacks[i].name, name) == 0 || callbacks[i].name[0] == '\0') { os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name)); callbacks[i].callback = cb; +#ifdef CMD_DBG os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i); +#endif return 1; } } @@ -99,7 +107,9 @@ CMD_GetCbByName(char* name) { // (void *)callbacks[i].callback); // if callback doesn't exist or it's null if (os_strcmp(callbacks[i].name, checkname) == 0) { +#ifdef CMD_DBG os_printf("CMD_GetCbByName: cb %s found at index %d\n", callbacks[i].name, i); +#endif return &callbacks[i]; } } @@ -111,7 +121,9 @@ CMD_GetCbByName(char* name) { static void ICACHE_FLASH_ATTR CMD_WifiCb(uint8_t wifiStatus) { if (wifiStatus != lastWifiStatus){ +#ifdef CMD_DBG os_printf("CMD_WifiCb: wifiStatus=%d\n", wifiStatus); +#endif lastWifiStatus = wifiStatus; cmdCallback *wifiCb = CMD_GetCbByName("wifiCb"); if ((uint32_t)wifiCb->callback != -1) { @@ -128,7 +140,9 @@ static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); +#ifdef CMD_DBG os_printf("CMD_WifiConnect: setup argc=%ld\n", CMD_GetArgc(&req)); +#endif if(cmd->argc != 2 || cmd->callback == 0) return 0; @@ -148,7 +162,9 @@ static uint32_t ICACHE_FLASH_ATTR CMD_AddCallback(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); +#ifdef CMD_DBG os_printf("CMD_AddCallback: setup argc=%ld\n", CMD_GetArgc(&req)); +#endif if (cmd->argc != 1 || cmd->callback == 0) return 0; @@ -157,11 +173,15 @@ CMD_AddCallback(CmdPacket *cmd) { // get the sensor name len = CMD_ArgLen(&req); +#ifdef CMD_DBG os_printf("CMD_AddCallback: name len=%d\n", len); +#endif if (len > 15) return 0; // max size of name is 15 characters if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0; name[len] = 0; +#ifdef CMD_DBG os_printf("CMD_AddCallback: name=%s\n", name); +#endif return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback } diff --git a/esp-link.vcxproj b/esp-link.vcxproj index 029fbb7..6a17160 100644 --- a/esp-link.vcxproj +++ b/esp-link.vcxproj @@ -65,9 +65,11 @@ + + + - @@ -103,6 +105,7 @@ + @@ -111,9 +114,12 @@ + + + - + @@ -150,6 +156,7 @@ + diff --git a/esp-link/cgi.c b/esp-link/cgi.c index 2b814fe..c99bada 100644 --- a/esp-link/cgi.c +++ b/esp-link/cgi.c @@ -16,14 +16,6 @@ Some random cgi routines. #include #include "cgi.h" -static char* chipIdStr = ""; -char* ICACHE_FLASH_ATTR system_get_chip_id_str(){ - if (os_strlen(chipIdStr) == 0) { - chipIdStr = (char*)os_zalloc(9); - os_sprintf(chipIdStr, "%06x", system_get_chip_id()); - } - return chipIdStr; -} void noCacheHeaders(HttpdConnData *connData, int code) { httpdStartResponse(connData, code); @@ -44,7 +36,9 @@ errorResponse(HttpdConnData *connData, int code, char *message) { noCacheHeaders(connData, code); httpdEndHeaders(connData); httpdSend(connData, message, -1); +#ifdef CGI_DBG os_printf("HTTP %d error response: \"%s\"\n", code, message); +#endif } // look for the HTTP arg 'name' and store it at 'config' with max length 'max_len' (incl @@ -58,7 +52,7 @@ getStringArg(HttpdConnData *connData, char *name, char *config, int max_len) { os_sprintf(buff, "Value for %s too long (%d > %d allowed)", name, len, max_len-1); errorResponse(connData, 400, buff); return -1; - } + } strcpy(config, buff); return 1; } diff --git a/esp-link/cgi.h b/esp-link/cgi.h index d1d73c6..aa124f1 100644 --- a/esp-link/cgi.h +++ b/esp-link/cgi.h @@ -10,6 +10,5 @@ int getStringArg(HttpdConnData *connData, char *name, char *config, int max_len) int getBoolArg(HttpdConnData *connData, char *name, bool*config); int cgiMenu(HttpdConnData *connData); uint8_t UTILS_StrToIP(const char* str, void *ip); -char* system_get_chip_id_str(); #endif diff --git a/esp-link/cgiflash.c b/esp-link/cgiflash.c index e07443b..ccd6865 100644 --- a/esp-link/cgiflash.c +++ b/esp-link/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; +#ifdef CGIFLASH_DBG uint32_t *buf32 = buf; os_printf("%p: %08lX %08lX %08lX %08lX\n", buf, buf32[0], buf32[1], buf32[2], buf32[3]); +#endif if (cd[0] != 0xEA) return "IROM magic missing"; 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"; @@ -42,7 +44,9 @@ int ICACHE_FLASH_ATTR cgiGetFirmwareNext(HttpdConnData *connData) { httpdEndHeaders(connData); char *next = id == 1 ? "user1.bin" : "user2.bin"; httpdSend(connData, next, -1); +#ifdef CGIFLASH_DBG os_printf("Next firmware: %s (got %d)\n", next, id); +#endif return HTTPD_CGI_DONE; } @@ -80,7 +84,9 @@ int ICACHE_FLASH_ATTR cgiUploadFirmware(HttpdConnData *connData) { // return an error if there is one if (err != NULL) { +#ifdef CGIFLASH_DBG os_printf("Error %d: %s\n", code, err); +#endif httpdStartResponse(connData, code); httpdHeader(connData, "Content-Type", "text/plain"); //httpdHeader(connData, "Content-Length", strlen(err)+2); @@ -99,7 +105,9 @@ int ICACHE_FLASH_ATTR cgiUploadFirmware(HttpdConnData *connData) { // erase next flash block if necessary if (address % SPI_FLASH_SEC_SIZE == 0){ +#ifdef CGIFLASH_DBG os_printf("Flashing 0x%05x (id=%d)\n", address, 2-id); +#endif spi_flash_erase_sector(address/SPI_FLASH_SEC_SIZE); } @@ -129,11 +137,15 @@ 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]; +#ifdef CGIFLASH_DBG os_printf("Checking %p\n", (void *)address); +#endif spi_flash_read(address, buf, sizeof(buf)); char *err = check_header(buf); if (err != NULL) { +#ifdef CGIFLASH_DBG os_printf("Error %d: %s\n", 400, err); +#endif httpdStartResponse(connData, 400); httpdHeader(connData, "Content-Type", "text/plain"); //httpdHeader(connData, "Content-Length", strlen(err)+2); diff --git a/esp-link/cgimqtt.c b/esp-link/cgimqtt.c index cbf4c16..2c3c7b0 100644 --- a/esp-link/cgimqtt.c +++ b/esp-link/cgimqtt.c @@ -17,17 +17,29 @@ int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { "\"slip-enable\":%d, " "\"mqtt-enable\":%d, " "\"mqtt-status-enable\":%d, " + "\"mqtt-clean-session\":%d, " "\"mqtt-port\":%d, " + "\"mqtt-timeout\":%d, " + "\"mqtt-keepalive\":%d, " "\"mqtt-host\":\"%s\", " "\"mqtt-client-id\":\"%s\", " "\"mqtt-username\":\"%s\", " "\"mqtt-password\":\"%s\", " "\"mqtt-status-topic\":\"%s\", " "\"mqtt-state\":\"%s\" }", - flashConfig.slip_enable, flashConfig.mqtt_enable, flashConfig.mqtt_status_enable, - flashConfig.mqtt_port, flashConfig.mqtt_hostname, flashConfig.mqtt_client, - flashConfig.mqtt_username, flashConfig.mqtt_password, - flashConfig.mqtt_status_topic, "connected"); + flashConfig.slip_enable, + flashConfig.mqtt_enable, + flashConfig.mqtt_status_enable, + flashConfig.mqtt_clean_session, + flashConfig.mqtt_port, + flashConfig.mqtt_timeout, + flashConfig.mqtt_keepalive, + flashConfig.mqtt_host, + flashConfig.mqtt_clientid, + flashConfig.mqtt_username, + flashConfig.mqtt_password, + flashConfig.mqtt_status_topic, + "connected"); jsonHeader(connData, 200); httpdSend(connData, buff, len); @@ -41,22 +53,30 @@ int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { // handle MQTT server settings int mqtt_server = 0; // accumulator for changes/errors mqtt_server |= getStringArg(connData, "mqtt-host", - flashConfig.mqtt_hostname, sizeof(flashConfig.mqtt_hostname)); + flashConfig.mqtt_host, sizeof(flashConfig.mqtt_host)); if (mqtt_server < 0) return HTTPD_CGI_DONE; mqtt_server |= getStringArg(connData, "mqtt-client-id", - flashConfig.mqtt_client, sizeof(flashConfig.mqtt_client)); + flashConfig.mqtt_clientid, sizeof(flashConfig.mqtt_clientid)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; mqtt_server |= getStringArg(connData, "mqtt-username", flashConfig.mqtt_username, sizeof(flashConfig.mqtt_username)); if (mqtt_server < 0) return HTTPD_CGI_DONE; mqtt_server |= getStringArg(connData, "mqtt-password", flashConfig.mqtt_password, sizeof(flashConfig.mqtt_password)); + + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getBoolArg(connData, "mqtt-clean-session", + &flashConfig.mqtt_clean_session); + if (mqtt_server < 0) return HTTPD_CGI_DONE; mqtt_server |= getBoolArg(connData, "mqtt-enable", &flashConfig.mqtt_enable); - // handle mqtt port + char buff[16]; + + // handle mqtt port if (httpdFindArg(connData->getArgs, "mqtt-port", buff, sizeof(buff)) > 0) { int32_t port = atoi(buff); if (port > 0 && port < 65536) { @@ -68,9 +88,23 @@ int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { } } + // handle mqtt timeout + if (httpdFindArg(connData->getArgs, "mqtt-timeout", buff, sizeof(buff)) > 0) { + int32_t timeout = atoi(buff); + flashConfig.mqtt_timeout = timeout; + } + + // handle mqtt keepalive + if (httpdFindArg(connData->getArgs, "mqtt-keepalive", buff, sizeof(buff)) > 0) { + int32_t keepalive = atoi(buff); + flashConfig.mqtt_keepalive = keepalive; + } + // if server setting changed, we need to "make it so" if (mqtt_server) { +#ifdef CGIMQTT_DBG os_printf("MQTT server settings changed, enable=%d\n", flashConfig.mqtt_enable); +#endif // TODO } @@ -85,9 +119,11 @@ int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { // if SLIP-enable is toggled it gets picked-up immediately by the parser int slip_update = getBoolArg(connData, "slip-enable", &flashConfig.slip_enable); if (slip_update < 0) return HTTPD_CGI_DONE; +#ifdef CGIMQTT_DBG if (slip_update > 0) os_printf("SLIP-enable changed: %d\n", flashConfig.slip_enable); os_printf("Saving config\n"); +#endif if (configSave()) { httpdStartResponse(connData, 200); httpdEndHeaders(connData); diff --git a/esp-link/cgipins.c b/esp-link/cgipins.c index dabc1fd..80326df 100644 --- a/esp-link/cgipins.c +++ b/esp-link/cgipins.c @@ -85,8 +85,9 @@ int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { jsonHeader(connData, 400); return HTTPD_CGI_DONE; } - +#ifdef CGIPINS_DBG os_printf("Switching pin map to %s (%d)\n", map_names[m], m); +#endif int8_t *map = map_asn[m]; flashConfig.reset_pin = map[0]; flashConfig.isp_pin = map[1]; diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index 69d59b4..7292447 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -55,36 +55,48 @@ static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) { case EVENT_STAMODE_CONNECTED: wifiState = wifiIsConnected; wifiReason = 0; +#ifdef CGIWIFI_DBG os_printf("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, evt->event_info.connected.channel); +#endif statusWifiUpdate(wifiState); break; case EVENT_STAMODE_DISCONNECTED: wifiState = wifiIsDisconnected; wifiReason = evt->event_info.disconnected.reason; +#ifdef CGIWIFI_DBG os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n", evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); +#endif statusWifiUpdate(wifiState); break; case EVENT_STAMODE_AUTHMODE_CHANGE: +#ifdef CGIWIFI_DBG os_printf("Wifi auth mode: %d -> %d\n", evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); +#endif break; case EVENT_STAMODE_GOT_IP: wifiState = wifiGotIP; wifiReason = 0; +#ifdef CGIWIFI_DBG os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), IP2STR(&evt->event_info.got_ip.gw)); +#endif statusWifiUpdate(wifiState); break; case EVENT_SOFTAPMODE_STACONNECTED: +#ifdef CGIWIFI_DBG os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n", MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); +#endif break; case EVENT_SOFTAPMODE_STADISCONNECTED: +#ifdef CGIWIFI_DBG os_printf("Wifi AP: station " MACSTR " left, AID = %d\n", MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); +#endif break; default: break; @@ -103,7 +115,9 @@ wifiAddStateChangeCb(WifiStateChangeCb cb) { return; } } +#ifdef CGIWIFI_DBG os_printf("WIFI: max state change cb count exceeded\n"); +#endif } // ===== wifi scanning @@ -132,7 +146,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { struct bss_info *bss_link = (struct bss_info *)arg; if (status!=OK) { +#ifdef CGIWIFI_DBG os_printf("wifiScanDoneCb status=%d\n", status); +#endif cgiWifiAps.scanInProgress=0; return; } @@ -152,7 +168,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { //Allocate memory for access point data cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); cgiWifiAps.noAps=n; +#ifdef CGIWIFI_DBG os_printf("Scan done: found %d APs\n", n); +#endif //Copy access point data to the static struct n=0; @@ -161,7 +179,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { if (n>=cgiWifiAps.noAps) { //This means the bss_link changed under our nose. Shouldn't happen! //Break because otherwise we will write in unallocated memory. +#ifdef CGIWIFI_DBG os_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); +#endif break; } //Save the ap data. @@ -169,7 +189,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { cgiWifiAps.apData[n]->rssi=bss_link->rssi; cgiWifiAps.apData[n]->enc=bss_link->authmode; strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); +#ifdef CGIWIFI_DBG os_printf("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi); +#endif bss_link = bss_link->next.stqe_next; n++; @@ -180,7 +202,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { static ETSTimer scanTimer; static void ICACHE_FLASH_ATTR scanStartCb(void *arg) { +#ifdef CGIWIFI_DBG os_printf("Starting a scan\n"); +#endif wifi_station_scan(NULL, wifiScanDoneCb); } @@ -245,13 +269,17 @@ static ETSTimer resetTimer; static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { int x = wifi_station_get_connect_status(); int m = wifi_get_opmode() & 0x3; +#ifdef CGIWIFI_DBG os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x); +#endif if (x == STATION_GOT_IP) { if (m != 1) { #ifdef CHANGE_TO_STA // We're happily connected, go to STA mode +#ifdef CGIWIFI_DBG os_printf("Wifi got IP. Going into STA mode..\n"); +#endif wifi_set_opmode(1); wifi_set_sleep_type(SLEEP_MODE); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); @@ -261,11 +289,15 @@ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { // no more resetTimer at this point, gotta use physical reset to recover if in trouble } else { if (m != 3) { +#ifdef CGIWIFI_DBG os_printf("Wifi connect failed. Going into STA+AP mode..\n"); +#endif wifi_set_opmode(3); } log_uart(true); +#ifdef CGIWIFI_DBG os_printf("Enabling/continuing uart log\n"); +#endif os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } } @@ -277,7 +309,9 @@ static ETSTimer reassTimer; // Callback actually doing reassociation static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { +#ifdef CGIWIFI_DBG os_printf("Wifi changing association\n"); +#endif wifi_station_disconnect(); stconf.bssid_set = 0; wifi_station_set_config(&stconf); @@ -303,7 +337,9 @@ int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { //Set to 0 if you want to disable the actual reconnecting bit os_strncpy((char*)stconf.ssid, essid, 32); os_strncpy((char*)stconf.password, passwd, 64); +#ifdef CGIWIFI_DBG os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd); +#endif //Schedule disconnect/connect os_timer_disarm(&reassTimer); @@ -342,7 +378,6 @@ static bool parse_ip(char *buff, ip_addr_t *ip_ptr) { return false; } -#define DEBUGIP #ifdef DEBUGIP static void ICACHE_FLASH_ATTR debugIP() { struct ip_info info; @@ -365,7 +400,9 @@ static void ICACHE_FLASH_ATTR configWifiIP() { if (wifi_station_dhcpc_status() == DHCP_STARTED) wifi_station_dhcpc_stop(); wifi_station_dhcpc_start(); +#ifdef CGIWIFI_DBG os_printf("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); +#endif } else { // no DHCP, we got static network config! wifi_station_dhcpc_stop(); @@ -374,7 +411,9 @@ static void ICACHE_FLASH_ATTR configWifiIP() { ipi.netmask.addr = flashConfig.netmask; ipi.gw.addr = flashConfig.gateway; wifi_set_ip_info(0, &ipi); +#ifdef CGIWIFI_DBG os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); +#endif } #ifdef DEBUGIP debugIP(); @@ -454,7 +493,9 @@ int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); if (len!=0) { int m = atoi(buff); +#ifdef CGIWIFI_DBG os_printf("Wifi switching to mode %d\n", m); +#endif wifi_set_opmode(m&3); if (m == 1) { wifi_set_sleep_type(SLEEP_MODE); @@ -557,8 +598,9 @@ int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) { #endif len += os_sprintf(buff+len, "\"x\":0}\n"); - +#ifdef CGIWIFI_DBG os_printf(" -> %s\n", buff); +#endif httpdSend(connData, buff, len); return HTTPD_CGI_DONE; } @@ -582,8 +624,10 @@ int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) { // so we can revert to STA+AP mode if we can't connect. void ICACHE_FLASH_ATTR wifiInit() { wifi_set_phy_mode(2); +#ifdef CGIWIFI_DBG int x = wifi_get_opmode() & 0x3; os_printf("Wifi init, mode=%s\n", wifiMode[x]); +#endif configWifiIP(); wifi_set_event_handler_cb(wifiHandleEventCb); diff --git a/esp-link/config.c b/esp-link/config.c index 2f405e7..0ab4745 100644 --- a/esp-link/config.c +++ b/esp-link/config.c @@ -6,6 +6,7 @@ #include "config.h" #include "espfs.h" #include "crc16.h" +#include FlashConfig flashConfig; FlashConfig flashDefault = { @@ -19,8 +20,9 @@ FlashConfig flashDefault = { 1, 0, // tcp_enable, rssi_enable "\0", // api_key 0, 0, 0, // slip_enable, mqtt_enable, mqtt_status_enable - 1833, // mqtt port - "\0", "\0", "\0", "\0", "\0", // mqtt host, client, user, password, status-topic + 2, 1, // mqtt_timeout, mqtt_clean_session + 1833, 600, // mqtt port, mqtt_keepalive + "\0", "\0", "\0", "\0", "\0", // mqtt host, client_id, user, password, status-topic }; typedef union { @@ -51,8 +53,8 @@ static void memDump(void *addr, int len) { bool ICACHE_FLASH_ATTR configSave(void) { FlashFull ff; - memset(&ff, 0, sizeof(ff)); - memcpy(&ff, &flashConfig, sizeof(FlashConfig)); + os_memset(&ff, 0, sizeof(ff)); + os_memcpy(&ff, &flashConfig, sizeof(FlashConfig)); uint32_t seq = ff.fc.seq+1; // erase secondary uint32_t addr = FLASH_ADDR + (1-flash_pri)*FLASH_SECT; @@ -104,19 +106,28 @@ bool ICACHE_FLASH_ATTR configRestore(void) { FlashFull ff0, ff1; // read both flash sectors if (spi_flash_read(FLASH_ADDR, (void *)&ff0, sizeof(ff0)) != SPI_FLASH_RESULT_OK) - memset(&ff0, 0, sizeof(ff0)); // clear in case of error + os_memset(&ff0, 0, sizeof(ff0)); // clear in case of error if (spi_flash_read(FLASH_ADDR+FLASH_SECT, (void *)&ff1, sizeof(ff1)) != SPI_FLASH_RESULT_OK) - memset(&ff1, 0, sizeof(ff1)); // clear in case of error + os_memset(&ff1, 0, sizeof(ff1)); // clear in case of error // figure out which one is good flash_pri = selectPrimary(&ff0, &ff1); // if neither is OK, we revert to defaults if (flash_pri < 0) { - memcpy(&flashConfig, &flashDefault, sizeof(FlashConfig)); + os_memcpy(&flashConfig, &flashDefault, sizeof(FlashConfig)); + char chipIdStr[6]; + os_sprintf(chipIdStr, "%06x", system_get_chip_id()); + os_memcpy(&flashConfig.mqtt_clientid, chipIdStr, os_strlen(chipIdStr)); +#ifdef CHIP_IN_HOSTNAME + char hostname[16]; + os_strcpy(hostname, "esp-link-"); + os_strcat(hostname, chipIdStr); + os_memcpy(&flashConfig.hostname, hostname, os_strlen(hostname)); +#endif flash_pri = 0; return false; } // copy good one into global var and return - memcpy(&flashConfig, flash_pri == 0 ? &ff0.fc : &ff1.fc, sizeof(FlashConfig)); + os_memcpy(&flashConfig, flash_pri == 0 ? &ff0.fc : &ff1.fc, sizeof(FlashConfig)); return true; } @@ -125,10 +136,14 @@ static int ICACHE_FLASH_ATTR selectPrimary(FlashFull *ff0, FlashFull *ff1) { uint16_t crc = ff0->fc.crc; ff0->fc.crc = 0; bool ff0_crc_ok = crc16_data((unsigned char*)ff0, sizeof(FlashFull), 0) == crc; - - os_printf("FLASH chk=0x%04x crc=0x%04x full_sz=%d sz=%d\n", +#ifdef CONFIG_DBG + os_printf("FLASH chk=0x%04x crc=0x%04x full_sz=%d sz=%d chip_sz=%d\n", crc16_data((unsigned char*)ff0, sizeof(FlashFull), 0), - crc, sizeof(FlashFull), sizeof(FlashConfig)); + crc, + sizeof(FlashFull), + sizeof(FlashConfig), + getFlashSize()); +#endif // check CRC of ff1 crc = ff1->fc.crc; @@ -144,3 +159,15 @@ static int ICACHE_FLASH_ATTR selectPrimary(FlashFull *ff0, FlashFull *ff1) { else return ff1_crc_ok ? 1 : -1; } + +// returns the flash chip's size, in BYTES +const size_t ICACHE_FLASH_ATTR +getFlashSize() { + uint32_t id = spi_flash_get_id(); + uint8_t mfgr_id = id & 0xff; + uint8_t type_id = (id >> 8) & 0xff; // not relevant for size calculation + uint8_t size_id = (id >> 16) & 0xff; // lucky for us, WinBond ID's their chips as a form that lets us calculate the size + if (mfgr_id != 0xEF) // 0xEF is WinBond; that's all we care about (for now) + return 0; + return 1 << size_id; +} diff --git a/esp-link/config.h b/esp-link/config.h index 89287e6..5a67058 100644 --- a/esp-link/config.h +++ b/esp-link/config.h @@ -17,9 +17,11 @@ typedef struct { uint8_t tcp_enable, rssi_enable; // TCP client settings char api_key[48]; // RSSI submission API key (Grovestreams for now) uint8_t slip_enable, mqtt_enable, // SLIP protocol, MQTT client - mqtt_status_enable; // MQTT status reporting - uint16_t mqtt_port; - char mqtt_hostname[32], mqtt_client[48], mqtt_username[32], mqtt_password[32]; + mqtt_status_enable, // MQTT status reporting + mqtt_timeout, // MQTT send timeout + mqtt_clean_session; // MQTT clean session + uint16_t mqtt_port, mqtt_keepalive; // MQTT Host port, MQTT Keepalive timer + char mqtt_host[32], mqtt_clientid[48], mqtt_username[32], mqtt_password[32]; char mqtt_status_topic[32]; } FlashConfig; extern FlashConfig flashConfig; @@ -27,5 +29,6 @@ extern FlashConfig flashConfig; bool configSave(void); bool configRestore(void); void configWipe(void); +const size_t getFlashSize(); #endif diff --git a/esp-link/log.c b/esp-link/log.c index 9103a41..9a58b4d 100644 --- a/esp-link/log.c +++ b/esp-link/log.c @@ -25,14 +25,18 @@ log_uart(bool enable) { if (!enable && !log_no_uart && flashConfig.log_mode != LOG_MODE_ON) { // we're asked to turn uart off, and uart is on, and the flash setting isn't always-on #if 1 +#ifdef LOG_DBG os_printf("Turning OFF uart log\n"); +#endif os_delay_us(4*1000L); // time for uart to flush log_no_uart = !enable; #endif } else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) { // we're asked to turn uart on, and uart is off, and the flash setting isn't always-off log_no_uart = !enable; +#ifdef LOG_DBG os_printf("Turning ON uart log\n"); +#endif } } diff --git a/esp-link/main.c b/esp-link/main.c index aed1c7f..27a50e5 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -30,7 +30,7 @@ #include "log.h" #include -#define SHOW_HEAP_USE +//#define SHOW_HEAP_USE //Function that tells the authentication system what users/passwords live on the system. //This is disabled in the default build; if you want to try it, enable the authBasic line in @@ -97,9 +97,6 @@ HttpdBuiltInUrl builtInUrls[] = { { NULL, NULL, NULL } }; - -//#define SHOW_HEAP_USE - #ifdef SHOW_HEAP_USE static ETSTimer prHeapTimer; diff --git a/esp-link/status.c b/esp-link/status.c index 4e0da55..2a8346f 100644 --- a/esp-link/status.c +++ b/esp-link/status.c @@ -4,8 +4,9 @@ #include "config.h" #include "serled.h" #include "cgiwifi.h" -#include "tcpclient.h" - +#ifdef TCPCLIENT +#include +#endif //===== "CONN" LED status indication static ETSTimer ledTimer; @@ -66,6 +67,8 @@ void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { os_timer_arm(&ledTimer, 500, 0); } +#ifdef TCPCLIENT + //===== RSSI Status update sent to GroveStreams #define RSSI_INTERVAL (60*1000) @@ -112,6 +115,7 @@ static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { } tcpClientSendPush(chan); } +#endif // TCPCLIENT //===== Init status stuff @@ -120,15 +124,20 @@ void ICACHE_FLASH_ATTR statusInit(void) { makeGpio(flashConfig.conn_led_pin); setLed(1); } +#ifdef STATUS_DBG os_printf("CONN led=%d\n", flashConfig.conn_led_pin); +#endif os_timer_disarm(&ledTimer); os_timer_setfn(&ledTimer, ledTimerCb, NULL); os_timer_arm(&ledTimer, 2000, 0); - +#ifdef TCPCLIENT os_timer_disarm(&rssiTimer); os_timer_setfn(&rssiTimer, rssiTimerCb, NULL); os_timer_arm(&rssiTimer, RSSI_INTERVAL, 1); // recurring timer +#endif // TCPCLIENT } + + diff --git a/espfs/espfs.c b/espfs/espfs.c index 128ca29..f9942f3 100644 --- a/espfs/espfs.c +++ b/espfs/espfs.c @@ -109,7 +109,9 @@ void ICACHE_FLASH_ATTR memcpyAligned(char *dst, char *src, int len) { // Returns flags of opened file. int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) { if (fh == NULL) { +#ifdef ESPFS_DBG os_printf("File handle not ready\n"); +#endif return -1; } @@ -121,7 +123,9 @@ int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) { //Open a file and return a pointer to the file desc struct. EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { if (espFsData == NULL) { +#ifdef ESPFS_DBG os_printf("Call espFsInit first!\n"); +#endif return NULL; } char *p=espFsData; @@ -137,7 +141,9 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { //Grab the next file header. os_memcpy(&h, p, sizeof(EspFsHeader)); if (h.magic!=ESPFS_MAGIC) { +#ifdef ESPFS_DBG os_printf("Magic mismatch. EspFS image broken.\n"); +#endif return NULL; } if (h.flags&FLAG_LASTFILE) { @@ -163,7 +169,9 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { if (h.compression==COMPRESS_NONE) { r->decompData=NULL; } else { +#ifdef ESPFS_DBG os_printf("Invalid compression: %d\n", h.compression); +#endif return NULL; } return r; diff --git a/html/console.js b/html/console.js new file mode 100644 index 0000000..87f7d1a --- /dev/null +++ b/html/console.js @@ -0,0 +1,72 @@ +function fetchText(delay, repeat) { + var el = $("#console"); + if (el.textEnd == undefined) { + el.textEnd = 0; + el.innerHTML = ""; + } + window.setTimeout(function() { + ajaxJson('GET', console_url + "?start=" + el.textEnd, + function(resp) { + var dly = updateText(resp); + if (repeat) fetchText(dly, repeat); + }, + function() { retryLoad(repeat); }); + }, delay); +} + +function updateText(resp) { + var el = $("#console"); + + var delay = 3000; + if (resp != null && resp.len > 0) { + console.log("updateText got", resp.len, "chars at", resp.start); + if (resp.start > el.textEnd) { + el.innerHTML = el.innerHTML.concat("\r\n'+baud+'')); + + $("#"+baud+"-button").addEventListener("click", function(e) { + e.preventDefault(); + ajaxSpin('POST', "/console/baud?rate="+baud, + function(resp) { showNotification("" + baud + " baud set"); showRate(baud); }, + function(s, st) { showWarning("Error setting baud rate: " + st); } + ); + }); +} + +//===== Log page + +function showDbgMode(mode) { + var btns = $('.dbg-btn'); + for (var i=0; i < btns.length; i++) { + if (btns[i].id === "dbg-"+mode) + addClass(btns[i], "button-selected"); + else + removeClass(btns[i], "button-selected"); + } +} diff --git a/html/favicon.ico b/html/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bf372a1ce1a5468add7ffb6c17f1f431adede77e GIT binary patch literal 498 zcmVae+Y6JjdoD4Rr-D+HzZfGc75b2lRF>jE1No=0LrkgEht=$M$z8 zIEYYa2w2l#V|S!@z&Q0M85DUa_CWa1=f@d@hdHUY$k%83c*EN1zyrBDs#)_27F(%0 zH##focdN(4$9{EI8xuH0*rc8*F>)OC9kI-njf&TFc$- zS=^M}9{ql!A%BEhzusyd0mcVRyyyuG$m>$MP0^UeOE}LRLx1CP#nK0a-bBP?_u%P* zxKHB7Kn_1XGq!Cc$vnEy^~#-#XjQ9YvwL2Ij62t5eCsvHeFc%9~e(22$hw>W6bsbk&!kJWNE1>BnkzvC0 olOZdJ3K`P(;0xG~Wxu9f+qP%iv32uKA^SJ%)xwJ#7f=8IJJEdaM*si- literal 0 HcmV?d00001 diff --git a/html/head- b/html/head- index e69de29..514ff53 100644 --- a/html/head- +++ b/html/head- @@ -0,0 +1,10 @@ + + + esp-link + + + + + + +
diff --git a/html/home.html b/html/home.html index 2677264..46a984d 100644 --- a/html/home.html +++ b/html/home.html @@ -1 +1,87 @@ - +
+
+
JEELABS
+

esp-link

+

+
+ +
+
+
+

The JeeLabs esp-link firmware bridges the ESP8266 serial port to Wifi and can + program microcontrollers over the serial port, in particular Arduinos, AVRs, and + NXP's LPC800 and other ARM processors.

+

Program an Arduino/AVR using avrdude using a command + line similar to:

+
/home/arduino-1.0.5/hardware/tools/avrdude \
+   -DV -patmega328p -Pnet:esp-link.local:23 -carduino -b115200 -U \
+   -C /home/arduino-1.0.5/hardware/tools/avrdude.conf flash:w:my_sketch.hex:i +
+

where -Pnet:esp-link.local:23 tells avrdude to connect to port 23 of esp-link. + You can substitute the IP address of your esp-link for esp-link.local if necessary.

+

Please refer to + the online README + for up-to-date help and to the forthcoming + JeeLabs blog for an intro to the codebase.

+
+
+
+
+
+

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 +
+
+
+
+
+
+
+
+
+
+ + + diff --git a/html/log.html b/html/log.html index 7bec267..46ec785 100644 --- a/html/log.html +++ b/html/log.html @@ -1 +1,48 @@ - +
+
+

Debug Log

+
+ +
+

The debug log shows the most recent characters printed by the esp-link software itself to + its own debug log.

+
+

+ Refresh +

+

+ UART debug log: + auto + off + on +

+
+

+    
+
+ + + + + + diff --git a/html/mqtt.html b/html/mqtt.html index 257c137..edddb88 100644 --- a/html/mqtt.html +++ b/html/mqtt.html @@ -28,27 +28,30 @@
+ + +
+
+

Special Settings

+
+ Special settings, use with care! +
+ + +
+
+ + +
+
+ + + + + + +
+ +
+
+
+ + + + + + + + diff --git a/html/wifi/wifi.js b/html/wifi/wifi.js new file mode 100644 index 0000000..0024248 --- /dev/null +++ b/html/wifi/wifi.js @@ -0,0 +1,200 @@ +var currAp = ""; +var blockScan = 0; + +function createInputForAp(ap) { + if (ap.essid=="" && ap.rssi==0) return; + + var input = e("input"); + input.type = "radio"; + input.name = "essid"; + input.value=ap.essid; + input.id = "opt-" + ap.essid; + if (currAp == ap.essid) input.checked = "1"; + + var bars = e("div"); + var rssiVal = -Math.floor(ap.rssi/51)*32; + bars.className = "lock-icon"; + bars.style.backgroundPosition = "0px "+rssiVal+"px"; + + var rssi = e("div"); + rssi.innerHTML = "" + ap.rssi +"dB"; + + var encrypt = e("div"); + var encVal = "-64"; //assume wpa/wpa2 + if (ap.enc == "0") encVal = "0"; //open + if (ap.enc == "1") encVal = "-32"; //wep + encrypt.className = "lock-icon"; + encrypt.style.backgroundPosition = "-32px "+encVal+"px"; + + var label = e("div"); + label.innerHTML = ap.essid; + + var div = m('').childNodes[0]; + div.appendChild(input); + div.appendChild(encrypt); + div.appendChild(bars); + div.appendChild(rssi); + div.appendChild(label); + return div; +} + +function getSelectedEssid() { + var e = document.forms.wifiform.elements; + for (var i=0; i 60) { + return scanAPs(); + } + scanReqCnt += 1; + ajaxJson('GET', "scan", function(data) { + currAp = getSelectedEssid(); + if (data.result.inProgress == "0" && data.result.APs.length > 1) { + $("#aps").innerHTML = ""; + var n = 0; + for (var i=0; i"+data.ip+", else connect to network "+data.ssid+" first."; + } else { + blockScan = 0; + showWarning("Connection failed: " + data.status + ", " + data.reason); + $("#aps").innerHTML = + "Check password and selected AP. Go Back"; + } + }, function(s, st) { + //showWarning("Can't get status: " + st); + window.setTimeout(getStatus, 2000); + }); +} + +function changeWifiMode(m) { + blockScan = 1; + hideWarning(); + ajaxSpin("POST", "setmode?mode=" + m, function(resp) { + showNotification("Mode changed"); + window.setTimeout(getWifiInfo, 100); + blockScan = 0; + }, function(s, st) { + showWarning("Error changing mode: " + st); + window.setTimeout(getWifiInfo, 100); + blockScan = 0; + }); +} + +function changeWifiAp(e) { + e.preventDefault(); + var passwd = $("#wifi-passwd").value; + var essid = getSelectedEssid(); + showNotification("Connecting to " + essid); + var url = "connect?essid="+encodeURIComponent(essid)+"&passwd="+encodeURIComponent(passwd); + + hideWarning(); + $("#reconnect").setAttribute("hidden", ""); + $("#wifi-passwd").value = ""; + var cb = $("#connect-button"); + var cn = cb.className; + cb.className += ' pure-button-disabled'; + blockScan = 1; + ajaxSpin("POST", url, function(resp) { + $("#spinner").removeAttribute('hidden'); // hack + showNotification("Waiting for network change..."); + window.scrollTo(0, 0); + window.setTimeout(getStatus, 2000); + }, function(s, st) { + showWarning("Error switching network: "+st); + cb.className = cn; + window.setTimeout(scanAPs, 1000); + }); +} + +function changeSpecial(e) { + e.preventDefault(); + var url = "special"; + url += "?dhcp=" + document.querySelector('input[name="dhcp"]:checked').value; + url += "&hostname=" + encodeURIComponent($("#wifi-hostname").value); + url += "&staticip=" + encodeURIComponent($("#wifi-staticip").value); + url += "&netmask=" + encodeURIComponent($("#wifi-netmask").value); + url += "&gateway=" + encodeURIComponent($("#wifi-gateway").value); + + hideWarning(); + var cb = $("#special-button"); + addClass(cb, 'pure-button-disabled'); + ajaxSpin("POST", url, function(resp) { + removeClass(cb, 'pure-button-disabled'); + getWifiInfo(); + }, function(s, st) { + showWarning("Error: "+st); + removeClass(cb, 'pure-button-disabled'); + getWifiInfo(); + }); +} + +function doDhcp() { + $('#dhcp-on').removeAttribute('hidden'); + $('#dhcp-off').setAttribute('hidden', ''); +} + +function doStatic() { + $('#dhcp-off').removeAttribute('hidden'); + $('#dhcp-on').setAttribute('hidden', ''); +} diff --git a/httpd/httpd.c b/httpd/httpd.c index bccbbb0..2db8051 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -3,15 +3,15 @@ Esp8266 http server - core routines */ /* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * Jeroen Domburg wrote this file. As long as you retain - * this notice you can do whatever you want with this stuff. If we meet some day, - * and you think this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - * Modified and enhanced by Thorsten von Eicken in 2015 - * ---------------------------------------------------------------------------- - */ +* ---------------------------------------------------------------------------- +* "THE BEER-WARE LICENSE" (Revision 42): +* Jeroen Domburg wrote this file. As long as you retain +* this notice you can do whatever you want with this stuff. If we meet some day, +* and you think this stuff is worth it, you can buy me a beer in return. +* ---------------------------------------------------------------------------- +* Modified and enhanced by Thorsten von Eicken in 2015 +* ---------------------------------------------------------------------------- +*/ #include @@ -33,10 +33,10 @@ static HttpdBuiltInUrl *builtInUrls; //Private data for http connection struct HttpdPriv { - char head[MAX_HEAD_LEN]; - int headPos; - char *sendBuff; - int sendBuffLen; + char head[MAX_HEAD_LEN]; + int headPos; + char *sendBuff; + int sendBuffLen; }; //Connection pool @@ -50,36 +50,36 @@ static esp_tcp httpdTcp; //Struct to keep extension->mime data in typedef struct { - const char *ext; - const char *mimetype; + const char *ext; + const char *mimetype; } MimeMap; //The mappings from file extensions to mime types. If you need an extra mime type, //add it here. -static const MimeMap mimeTypes[]={ - {"htm", "text/htm"}, - {"html", "text/html; charset=UTF-8"}, - {"css", "text/css"}, - {"js", "text/javascript"}, - {"txt", "text/plain"}, - {"jpg", "image/jpeg"}, - {"jpeg", "image/jpeg"}, - {"png", "image/png"}, - {"tpl", "text/html; charset=UTF-8"}, - {NULL, "text/html"}, //default value +static const MimeMap mimeTypes[] = { + { "htm", "text/htm" }, + { "html", "text/html; charset=UTF-8" }, + { "css", "text/css" }, + { "js", "text/javascript" }, + { "txt", "text/plain" }, + { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, + { "png", "image/png" }, + { "tpl", "text/html; charset=UTF-8" }, + { NULL, "text/html" }, //default value }; //Returns a static char* to a mime type for a given url to a file. const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { - int i=0; - //Go find the extension - char *ext=url+(strlen(url)-1); - while (ext!=url && *ext!='.') ext--; - if (*ext=='.') ext++; - - //ToDo: os_strcmp is case sensitive; we may want to do case-intensive matching here... - while (mimeTypes[i].ext!=NULL && os_strcmp(ext, mimeTypes[i].ext)!=0) i++; - return mimeTypes[i].mimetype; + int i = 0; + //Go find the extension + char *ext = url + (strlen(url) - 1); + while (ext != url && *ext != '.') ext--; + if (*ext == '.') ext++; + + //ToDo: os_strcmp is case sensitive; we may want to do case-intensive matching here... + while (mimeTypes[i].ext != NULL && os_strcmp(ext, mimeTypes[i].ext) != 0) i++; + return mimeTypes[i].mimetype; } // debug string to identify connection (ip address & port) @@ -87,59 +87,65 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { static char connStr[24]; static void debugConn(void *arg, char *what) { - struct espconn *espconn = arg; - esp_tcp *tcp = espconn->proto.tcp; - os_sprintf(connStr, "%d.%d.%d.%d:%d", - tcp->remote_ip[0], tcp->remote_ip[1], tcp->remote_ip[2], tcp->remote_ip[3], - tcp->remote_port); - //os_printf("%s %s\n", connStr, what); + struct espconn *espconn = arg; + esp_tcp *tcp = espconn->proto.tcp; + os_sprintf(connStr, "%d.%d.%d.%d:%d", + tcp->remote_ip[0], tcp->remote_ip[1], tcp->remote_ip[2], tcp->remote_ip[3], + tcp->remote_port); + //os_printf("%s %s\n", connStr, what); } //Looks up the connData info for a specific esp connection static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) { - struct espconn *espconn = arg; - for (int i=0; iproto.tcp->remote_port && - os_memcmp(connData[i].remote_ip, espconn->proto.tcp->remote_ip, 4) == 0) - { + struct espconn *espconn = arg; + for (int i = 0; iproto.tcp->remote_port && + os_memcmp(connData[i].remote_ip, espconn->proto.tcp->remote_ip, 4) == 0) + { #if 0 - os_printf("FindConn: 0x%p->0x%p", arg, &connData[i]); - if (arg == connData[i].conn) os_printf("\n"); - else os_printf(" *** was 0x%p\n", connData[i].conn); +#ifdef HTTPD_DBG + os_printf("FindConn: 0x%p->0x%p", arg, &connData[i]); + if (arg == connData[i].conn) os_printf("\n"); + else os_printf(" *** was 0x%p\n", connData[i].conn); +#endif #endif - if (arg != connData[i].conn) connData[i].conn = arg; // yes, this happens!? - return &connData[i]; - } - } - //Shouldn't happen. - os_printf("%s *** Unknown connection 0x%p\n", connStr, arg); - return NULL; + if (arg != connData[i].conn) connData[i].conn = arg; // yes, this happens!? + return &connData[i]; + } + } + //Shouldn't happen. +#ifdef HTTPD_DBG + os_printf("%s *** Unknown connection 0x%p\n", connStr, arg); +#endif + return NULL; } //Retires a connection for re-use static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) { - conn->conn = NULL; // don't try to send anything, the SDK crashes... - if (conn->cgi != NULL) conn->cgi(conn); // free cgi data - if (conn->post->buff != NULL) { - os_free(conn->post->buff); - } - conn->cgi=NULL; - conn->post->buff=NULL; - conn->remote_port = 0; - conn->remote_ip[0] = 0; - - uint32 dt = conn->startTime; - if (dt > 0) dt = (system_get_time() - dt)/1000; - os_printf("%s Closed, %ums, heap=%ld\n", connStr, dt, - (unsigned long)system_get_free_heap_size()); + conn->conn = NULL; // don't try to send anything, the SDK crashes... + if (conn->cgi != NULL) conn->cgi(conn); // free cgi data + if (conn->post->buff != NULL) { + os_free(conn->post->buff); + } + conn->cgi = NULL; + conn->post->buff = NULL; + conn->remote_port = 0; + conn->remote_ip[0] = 0; + + uint32 dt = conn->startTime; + if (dt > 0) dt = (system_get_time() - dt) / 1000; +#ifdef HTTPD_DBG + os_printf("%s Closed, %ums, heap=%ld\n", connStr, dt, + (unsigned long)system_get_free_heap_size()); +#endif } //Stupid li'l helper function that returns the value of a hex char. static int httpdHexVal(char c) { - if (c>='0' && c<='9') return c-'0'; - if (c>='A' && c<='F') return c-'A'+10; - if (c>='a' && c<='f') return c-'a'+10; - return 0; + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return 0; } //Decode a percent-encoded value. @@ -147,27 +153,31 @@ static int httpdHexVal(char c) { //are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also //zero-terminates the ret buffer. int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) { - int s=0, d=0; - int esced=0, escVal=0; - while (s1) { - *ret++=*p++; - retLen--; - } - //Zero-terminate string - *ret=0; - //All done :) - return 1; - } - p+=strlen(p)+1; //Skip past end of string and \0 terminator - } - return 0; + char *p = conn->priv->head; + p = p + strlen(p) + 1; //skip GET/POST part + p = p + strlen(p) + 1; //skip HTTP part + while (p<(conn->priv->head + conn->priv->headPos)) { + while (*p <= 32 && *p != 0) p++; //skip crap at start + //See if this is the header + if (os_strncmp(p, header, strlen(header)) == 0 && p[strlen(header)] == ':') { + //Skip 'key:' bit of header line + p = p + strlen(header) + 1; + //Skip past spaces after the colon + while (*p == ' ') p++; + //Copy from p to end + while (*p != 0 && *p != '\r' && *p != '\n' && retLen>1) { + *ret++ = *p++; + retLen--; + } + //Zero-terminate string + *ret = 0; + //All done :) + return 1; + } + p += strlen(p) + 1; //Skip past end of string and \0 terminator + } + return 0; } //Start the response headers. void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code) { - char buff[128]; - int l; - char *status = code < 400 ? "OK" : "ERROR"; - l = os_sprintf(buff, "HTTP/1.0 %d %s\r\nServer: esp-link\r\nConnection: close\r\n", code, status); - httpdSend(conn, buff, l); + char buff[128]; + int l; + char *status = code < 400 ? "OK" : "ERROR"; + l = os_sprintf(buff, "HTTP/1.0 %d %s\r\nServer: esp-link\r\nConnection: close\r\n", code, status); + httpdSend(conn, buff, l); } //Send a http header. void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val) { - char buff[256]; - int l; + char buff[256]; + int l; - l=os_sprintf(buff, "%s: %s\r\n", field, val); - httpdSend(conn, buff, l); + l = os_sprintf(buff, "%s: %s\r\n", field, val); + httpdSend(conn, buff, l); } //Finish the headers. void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) { - httpdSend(conn, "\r\n", -1); + httpdSend(conn, "\r\n", -1); } //ToDo: sprintf->snprintf everywhere... esp doesn't have snprintf tho' :/ //Redirect to the given URL. void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl) { - char buff[1024]; - int l; - l=os_sprintf(buff, "HTTP/1.0 302 Found\r\nServer: esp8266-link\r\nConnection: close\r\nLocation: %s\r\n\r\nRedirecting to %s\r\n", newUrl, newUrl); - httpdSend(conn, buff, l); + char buff[1024]; + int l; + l = os_sprintf(buff, "HTTP/1.0 302 Found\r\nServer: esp8266-link\r\nConnection: close\r\nLocation: %s\r\n\r\nRedirecting to %s\r\n", newUrl, newUrl); + httpdSend(conn, buff, l); } //Use this as a cgi function to redirect one url to another. int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) { - if (connData->conn==NULL) { - //Connection aborted. Clean up. - return HTTPD_CGI_DONE; - } - httpdRedirect(connData, (char*)connData->cgiArg); - return HTTPD_CGI_DONE; + if (connData->conn == NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + httpdRedirect(connData, (char*)connData->cgiArg); + return HTTPD_CGI_DONE; } @@ -270,321 +280,349 @@ int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) { //the data is seen as a C-string. //Returns 1 for success, 0 for out-of-memory. int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) { - if (len<0) len=strlen(data); - if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) { - os_printf("%s ERROR! httpdSend full (%d of %d)\n", - connStr, conn->priv->sendBuffLen, MAX_SENDBUFF_LEN); - return 0; - } - os_memcpy(conn->priv->sendBuff+conn->priv->sendBuffLen, data, len); - conn->priv->sendBuffLen+=len; - return 1; + if (len<0) len = strlen(data); + if (conn->priv->sendBuffLen + len>MAX_SENDBUFF_LEN) { +#ifdef HTTPD_DBG + os_printf("%s ERROR! httpdSend full (%d of %d)\n", + connStr, conn->priv->sendBuffLen, MAX_SENDBUFF_LEN); +#endif + return 0; + } + os_memcpy(conn->priv->sendBuff + conn->priv->sendBuffLen, data, len); + conn->priv->sendBuffLen += len; + return 1; } //Helper function to send any data in conn->priv->sendBuff static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) { - if (conn->priv->sendBuffLen!=0) { - sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen); - if (status != 0) { - os_printf("%s ERROR! espconn_sent returned %d\n", connStr, status); - } - conn->priv->sendBuffLen=0; - } + if (conn->priv->sendBuffLen != 0) { + sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen); + if (status != 0) { +#ifdef HTTPD_DBG + os_printf("%s ERROR! espconn_sent returned %d\n", connStr, status); +#endif + } + conn->priv->sendBuffLen = 0; + } } //Callback called when the data on a socket has been successfully sent. static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { - debugConn(arg, "httpdSentCb"); - int r; - HttpdConnData *conn=httpdFindConnData(arg); - char sendBuff[MAX_SENDBUFF_LEN]; - - if (conn==NULL) return; - conn->priv->sendBuff=sendBuff; - conn->priv->sendBuffLen=0; - - if (conn->cgi==NULL) { //Marked for destruction? - //os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn); - espconn_disconnect(conn->conn); // we will get a disconnect callback - return; //No need to call xmitSendBuff. - } - - r=conn->cgi(conn); //Execute cgi fn. - if (r==HTTPD_CGI_DONE) { - conn->cgi=NULL; //mark for destruction. - } - if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { - os_printf("%s ERROR! Bad CGI code %d\n", connStr, r); - conn->cgi=NULL; //mark for destruction. - } - xmitSendBuff(conn); + debugConn(arg, "httpdSentCb"); + int r; + HttpdConnData *conn = httpdFindConnData(arg); + char sendBuff[MAX_SENDBUFF_LEN]; + + if (conn == NULL) return; + conn->priv->sendBuff = sendBuff; + conn->priv->sendBuffLen = 0; + + if (conn->cgi == NULL) { //Marked for destruction? + //os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn); + espconn_disconnect(conn->conn); // we will get a disconnect callback + return; //No need to call xmitSendBuff. + } + + r = conn->cgi(conn); //Execute cgi fn. + if (r == HTTPD_CGI_DONE) { + conn->cgi = NULL; //mark for destruction. + } + if (r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED) { +#ifdef HTTPD_DBG + os_printf("%s ERROR! Bad CGI code %d\n", connStr, r); +#endif + conn->cgi = NULL; //mark for destruction. + } + xmitSendBuff(conn); } -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"; +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. //We need to find the CGI function to call, call it, and dependent on what it returns either //find the next cgi function, wait till the cgi data is sent or close up the connection. static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) { - int r; - int i=0; - if (conn->url==NULL) { - os_printf("%s WtF? url = NULL\n", connStr); - return; //Shouldn't happen - } - //See if we can find a CGI that's happy to handle the request. - while (1) { - //Look up URL in the built-in URL table. - while (builtInUrls[i].url!=NULL) { - int match=0; - //See if there's a literal match - if (os_strcmp(builtInUrls[i].url, conn->url)==0) match=1; - //See if there's a wildcard match - if (builtInUrls[i].url[os_strlen(builtInUrls[i].url)-1]=='*' && - os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url)-1)==0) match=1; - if (match) { - //os_printf("Is url index %d\n", i); - conn->cgiData=NULL; - conn->cgi=builtInUrls[i].cgiCb; - conn->cgiArg=builtInUrls[i].cgiArg; - break; - } - i++; - } - if (builtInUrls[i].url==NULL) { - //Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just - //generate a built-in 404 to handle this. - os_printf("%s %s not found. 404!\n", connStr, conn->url); - httpdSend(conn, httpNotFoundHeader, -1); - xmitSendBuff(conn); - conn->cgi=NULL; //mark for destruction - return; - } - - //Okay, we have a CGI function that matches the URL. See if it wants to handle the - //particular URL we're supposed to handle. - r=conn->cgi(conn); - if (r==HTTPD_CGI_MORE) { - //Yep, it's happy to do so and has more data to send. - xmitSendBuff(conn); - return; - } else if (r==HTTPD_CGI_DONE) { - //Yep, it's happy to do so and already is done sending data. - xmitSendBuff(conn); - conn->cgi=NULL; //mark conn for destruction - return; - } else if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { - //URL doesn't want to handle the request: either the data isn't found or there's no - //need to generate a login screen. - i++; //look at next url the next iteration of the loop. - } - } + int r; + int i = 0; + if (conn->url == NULL) { +#ifdef HTTPD_DBG + os_printf("%s WtF? url = NULL\n", connStr); +#endif + return; //Shouldn't happen + } + //See if we can find a CGI that's happy to handle the request. + while (1) { + //Look up URL in the built-in URL table. + while (builtInUrls[i].url != NULL) { + int match = 0; + //See if there's a literal match + if (os_strcmp(builtInUrls[i].url, conn->url) == 0) match = 1; + //See if there's a wildcard match + if (builtInUrls[i].url[os_strlen(builtInUrls[i].url) - 1] == '*' && + os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url) - 1) == 0) match = 1; + if (match) { + //os_printf("Is url index %d\n", i); + conn->cgiData = NULL; + conn->cgi = builtInUrls[i].cgiCb; + conn->cgiArg = builtInUrls[i].cgiArg; + break; + } + i++; + } + if (builtInUrls[i].url == NULL) { + //Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just + //generate a built-in 404 to handle this. +#ifdef HTTPD_DBG + os_printf("%s %s not found. 404!\n", connStr, conn->url); +#endif + httpdSend(conn, httpNotFoundHeader, -1); + xmitSendBuff(conn); + conn->cgi = NULL; //mark for destruction + return; + } + + //Okay, we have a CGI function that matches the URL. See if it wants to handle the + //particular URL we're supposed to handle. + r = conn->cgi(conn); + if (r == HTTPD_CGI_MORE) { + //Yep, it's happy to do so and has more data to send. + xmitSendBuff(conn); + return; + } + else if (r == HTTPD_CGI_DONE) { + //Yep, it's happy to do so and already is done sending data. + xmitSendBuff(conn); + conn->cgi = NULL; //mark conn for destruction + return; + } + else if (r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED) { + //URL doesn't want to handle the request: either the data isn't found or there's no + //need to generate a login screen. + i++; //look at next url the next iteration of the loop. + } + } } //Parse a line of header data and modify the connection data accordingly. static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { - int i; - char first_line = false; - - if (os_strncmp(h, "GET ", 4)==0) { - conn->requestType = HTTPD_METHOD_GET; - first_line = true; - } else if (os_strncmp(h, "POST ", 5)==0) { - conn->requestType = HTTPD_METHOD_POST; - first_line = true; - } - - if (first_line) { - char *e; - - //Skip past the space after POST/GET - i=0; - while (h[i]!=' ') i++; - conn->url=h+i+1; - - //Figure out end of url. - e=(char*)os_strstr(conn->url, " "); - if (e==NULL) return; //wtf? - *e=0; //terminate url part - - // Count number of open connections - int open = 0; - for (int j=0; jrequestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, open); - //Parse out the URL part before the GET parameters. - conn->getArgs=(char*)os_strstr(conn->url, "?"); - if (conn->getArgs!=0) { - *conn->getArgs=0; - conn->getArgs++; - os_printf("%s args = %s\n", connStr, conn->getArgs); - } else { - conn->getArgs=NULL; - } - - } else if (os_strncmp(h, "Content-Length:", 15)==0) { - i=15; - //Skip trailing spaces - while (h[i]==' ') i++; - //Get POST data length - conn->post->len=atoi(h+i); - - // Allocate the buffer - if (conn->post->len > MAX_POST) { - // we'll stream this in in chunks - conn->post->buffSize = MAX_POST; - } else { - conn->post->buffSize = conn->post->len; - } - //os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize); - conn->post->buff=(char*)os_malloc(conn->post->buffSize + 1); - conn->post->buffLen=0; - } else if (os_strncmp(h, "Content-Type: ", 14)==0) { - if (os_strstr(h, "multipart/form-data")) { - // It's multipart form data so let's pull out the boundary for future use - char *b; - if ((b = os_strstr(h, "boundary=")) != NULL) { - conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes - conn->post->multipartBoundary[0] = '-'; - conn->post->multipartBoundary[1] = '-'; - //os_printf("boundary = %s\n", conn->post->multipartBoundary); - } - } - } + int i; + char first_line = false; + + if (os_strncmp(h, "GET ", 4) == 0) { + conn->requestType = HTTPD_METHOD_GET; + first_line = true; + } + else if (os_strncmp(h, "POST ", 5) == 0) { + conn->requestType = HTTPD_METHOD_POST; + first_line = true; + } + + if (first_line) { + char *e; + + //Skip past the space after POST/GET + i = 0; + while (h[i] != ' ') i++; + conn->url = h + i + 1; + + //Figure out end of url. + e = (char*)os_strstr(conn->url, " "); + if (e == NULL) return; //wtf? + *e = 0; //terminate url part + + // Count number of open connections + int open = 0; + for (int j = 0; jrequestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, open); +#endif + //Parse out the URL part before the GET parameters. + conn->getArgs = (char*)os_strstr(conn->url, "?"); + if (conn->getArgs != 0) { + *conn->getArgs = 0; + conn->getArgs++; +#ifdef HTTPD_DBG + os_printf("%s args = %s\n", connStr, conn->getArgs); +#endif + } + else { + conn->getArgs = NULL; + } + + } + else if (os_strncmp(h, "Content-Length:", 15) == 0) { + i = 15; + //Skip trailing spaces + while (h[i] == ' ') i++; + //Get POST data length + conn->post->len = atoi(h + i); + + // Allocate the buffer + if (conn->post->len > MAX_POST) { + // we'll stream this in in chunks + conn->post->buffSize = MAX_POST; + } + else { + conn->post->buffSize = conn->post->len; + } + //os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize); + conn->post->buff = (char*)os_malloc(conn->post->buffSize + 1); + conn->post->buffLen = 0; + } + else if (os_strncmp(h, "Content-Type: ", 14) == 0) { + if (os_strstr(h, "multipart/form-data")) { + // It's multipart form data so let's pull out the boundary for future use + char *b; + if ((b = os_strstr(h, "boundary=")) != NULL) { + conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes + conn->post->multipartBoundary[0] = '-'; + conn->post->multipartBoundary[1] = '-'; + //os_printf("boundary = %s\n", conn->post->multipartBoundary); + } + } + } } //Callback called when there's data available on a socket. static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) { - debugConn(arg, "httpdRecvCb"); - int x; - char *p, *e; - char sendBuff[MAX_SENDBUFF_LEN]; - HttpdConnData *conn=httpdFindConnData(arg); - if (conn==NULL) return; - conn->priv->sendBuff=sendBuff; - conn->priv->sendBuffLen=0; - - //This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are: - //<0 (-1): Post len unknown because we're still receiving headers - //==0: No post data - //>0: Need to receive post data - //ToDo: See if we can use something more elegant for this. - - for (x=0; xpost->len<0) { - //This byte is a header byte. - if (conn->priv->headPos!=MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++]=data[x]; - conn->priv->head[conn->priv->headPos]=0; - //Scan for /r/n/r/n. Receiving this indicate the headers end. - if (data[x]=='\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n")!=NULL) { - //Indicate we're done with the headers. - conn->post->len=0; - //Reset url data - conn->url=NULL; - //Iterate over all received headers and parse them. - p=conn->priv->head; - while(p<(&conn->priv->head[conn->priv->headPos-4])) { - e=(char *)os_strstr(p, "\r\n"); //Find end of header line - if (e==NULL) break; //Shouldn't happen. - e[0]=0; //Zero-terminate header - httpdParseHeader(p, conn); //and parse it. - p=e+2; //Skip /r/n (now /0/n) - } - //If we don't need to receive post data, we can send the response now. - if (conn->post->len==0) { - httpdProcessRequest(conn); - } - } - } else if (conn->post->len!=0) { - //This byte is a POST byte. - conn->post->buff[conn->post->buffLen++]=data[x]; - conn->post->received++; - if (conn->post->buffLen >= conn->post->buffSize || conn->post->received == conn->post->len) { - //Received a chunk of post data - conn->post->buff[conn->post->buffLen]=0; //zero-terminate, in case the cgi handler knows it can use strings - //Send the response. - httpdProcessRequest(conn); - conn->post->buffLen = 0; - } - } - } + debugConn(arg, "httpdRecvCb"); + int x; + char *p, *e; + char sendBuff[MAX_SENDBUFF_LEN]; + HttpdConnData *conn = httpdFindConnData(arg); + if (conn == NULL) return; + conn->priv->sendBuff = sendBuff; + conn->priv->sendBuffLen = 0; + + //This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are: + //<0 (-1): Post len unknown because we're still receiving headers + //==0: No post data + //>0: Need to receive post data + //ToDo: See if we can use something more elegant for this. + + for (x = 0; xpost->len<0) { + //This byte is a header byte. + if (conn->priv->headPos != MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++] = data[x]; + conn->priv->head[conn->priv->headPos] = 0; + //Scan for /r/n/r/n. Receiving this indicate the headers end. + if (data[x] == '\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n") != NULL) { + //Indicate we're done with the headers. + conn->post->len = 0; + //Reset url data + conn->url = NULL; + //Iterate over all received headers and parse them. + p = conn->priv->head; + while (p<(&conn->priv->head[conn->priv->headPos - 4])) { + e = (char *)os_strstr(p, "\r\n"); //Find end of header line + if (e == NULL) break; //Shouldn't happen. + e[0] = 0; //Zero-terminate header + httpdParseHeader(p, conn); //and parse it. + p = e + 2; //Skip /r/n (now /0/n) + } + //If we don't need to receive post data, we can send the response now. + if (conn->post->len == 0) { + httpdProcessRequest(conn); + } + } + } + else if (conn->post->len != 0) { + //This byte is a POST byte. + conn->post->buff[conn->post->buffLen++] = data[x]; + conn->post->received++; + if (conn->post->buffLen >= conn->post->buffSize || conn->post->received == conn->post->len) { + //Received a chunk of post data + conn->post->buff[conn->post->buffLen] = 0; //zero-terminate, in case the cgi handler knows it can use strings + //Send the response. + httpdProcessRequest(conn); + conn->post->buffLen = 0; + } + } + } } static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) { - debugConn(arg, "httpdDisconCb"); - HttpdConnData *conn = httpdFindConnData(arg); - if (conn == NULL) return; - httpdRetireConn(conn); + debugConn(arg, "httpdDisconCb"); + HttpdConnData *conn = httpdFindConnData(arg); + if (conn == NULL) return; + httpdRetireConn(conn); } // Callback indicating a failure in the connection. "Recon" is probably intended in the sense // of "you need to reconnect". Sigh... Note that there is no DisconCb after ReconCb static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) { - debugConn(arg, "httpdReconCb"); - HttpdConnData *conn = httpdFindConnData(arg); - os_printf("%s ***** reset, err=%d\n", connStr, err); - if (conn == NULL) return; - httpdRetireConn(conn); + debugConn(arg, "httpdReconCb"); + HttpdConnData *conn = httpdFindConnData(arg); +#ifdef HTTPD_DBG + os_printf("%s ***** reset, err=%d\n", connStr, err); +#endif + if (conn == NULL) return; + httpdRetireConn(conn); } static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { - debugConn(arg, "httpdConnectCb"); - struct espconn *conn=arg; - int i; - //Find empty conndata in pool - for (i=0; iproto.tcp->remote_port; - os_memcpy(connData[i].remote_ip, conn->proto.tcp->remote_ip, 4); - connData[i].priv->headPos=0; - connData[i].post=&connPostData[i]; - connData[i].post->buff=NULL; - connData[i].post->buffLen=0; - connData[i].post->received=0; - connData[i].post->len=-1; - connData[i].startTime = system_get_time(); - - espconn_regist_recvcb(conn, httpdRecvCb); - espconn_regist_reconcb(conn, httpdReconCb); - espconn_regist_disconcb(conn, httpdDisconCb); - espconn_regist_sentcb(conn, httpdSentCb); - - espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); + connData[i].priv = &connPrivData[i]; + connData[i].conn = conn; + connData[i].remote_port = conn->proto.tcp->remote_port; + os_memcpy(connData[i].remote_ip, conn->proto.tcp->remote_ip, 4); + connData[i].priv->headPos = 0; + connData[i].post = &connPostData[i]; + connData[i].post->buff = NULL; + connData[i].post->buffLen = 0; + connData[i].post->received = 0; + connData[i].post->len = -1; + connData[i].startTime = system_get_time(); + + espconn_regist_recvcb(conn, httpdRecvCb); + espconn_regist_reconcb(conn, httpdReconCb); + espconn_regist_disconcb(conn, httpdDisconCb); + espconn_regist_sentcb(conn, httpdSentCb); + + espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY); } //Httpd initialization routine. Call this to kick off webserver functionality. void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) { - int i; - - for (i=0; i #endif -//#define CMD_DBG -#define MQTT_RECONNECT_TIMEOUT 5 // seconds -#define MQTT_BUF_SIZE 512 -#define QUEUE_BUFFER_SIZE 512 -#define MQTT_HOST "10.0.0.220" // "mqtt.yourdomain.com" or ip "10.0.0.1" -#define MQTT_PORT 1883 -#define MQTT_SECURITY 0 - -#define MQTT_CLIENT_ID system_get_chip_id_str() // "esp-link" -#define MQTT_USER "" -#define MQTT_PASS "" -#define MQTT_KEEPALIVE 120 // seconds -#define MQTT_CLSESSION true - -#define PROTOCOL_NAMEv31 // MQTT version 3.1 compatible with Mosquitto v0.15/ -//PROTOCOL_NAMEv311 // MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/ +#define CMD_DBG +#define ESPFS_DBG +#define CGI_DBG +#define CGIFLASH_DBG +#define CGIMQTT_DBG +#define CGIPINS_DBG +#define CGIWIFI_DBG +#define CONFIG_DBG +#define LOG_DBG +#define STATUS_DBG +#define HTTPD_DBG +#define MQTT_DBG +#define MQTTCMD_DBG +#define PKTBUF_DBG +#define REST_DBG +#define RESTCMD_DBG +#define SERBR_DBG +#define SERLED_DBG +#define SLIP_DBG +#define UART_DBG +#define CHIP_IN_HOSTNAME + +//#define REST extern char* esp_link_version; - extern uint8_t UTILS_StrToIP(const char* str, void *ip); -extern void ICACHE_FLASH_ATTR init(void); - -extern char* ICACHE_FLASH_ATTR system_get_chip_id_str(); - #endif \ No newline at end of file diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c index ba81b00..d65870b 100644 --- a/mqtt/mqtt.c +++ b/mqtt/mqtt.c @@ -57,10 +57,12 @@ sint8 espconn_secure_sent(struct espconn *espconn, uint8 *psent, uint16 length) // max message size for sending (except publish) #define MQTT_MAX_SHORT_MESSAGE 128 +#ifdef MQTT_DBG static char* mqtt_msg_type[] = { "NULL", "TYPE_CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", "DISCONNECT", "RESV", }; +#endif // forward declarations static void mqtt_enq_message(MQTT_Client *client, const uint8_t *data, uint16_t len); @@ -125,7 +127,9 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { if (msg_len > client->in_buffer_size) { // oops, too long a message for us to digest, disconnect and hope for a miracle +#ifdef MQTT_DBG os_printf("MQTT: Too long a message (%d bytes)\n", msg_len); +#endif mqtt_doAbort(client); return; } @@ -135,7 +139,9 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { if (client->connState != MQTT_CONNECTED) { // why are we receiving something?? +#ifdef MQTT_DBG os_printf("MQTT ERROR: recv in invalid state %d\n", client->connState); +#endif mqtt_doAbort(client); return; } @@ -147,13 +153,16 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { pending_msg_type = mqtt_get_type(client->pending_buffer->data); pending_msg_id = mqtt_get_id(client->pending_buffer->data, client->pending_buffer->filled); } - +#ifdef MQTT_DBG os_printf("MQTT: Recv type=%s id=%04X len=%d; Pend type=%s id=%02X\n", mqtt_msg_type[msg_type], msg_id, msg_len, mqtt_msg_type[pending_msg_type], pending_msg_id); +#endif switch (msg_type) { case MQTT_MSG_TYPE_CONNACK: +#ifdef MQTT_DBG os_printf("MQTT: Connect successful\n"); +#endif // callbacks for internal and external clients if (client->connectedCb) client->connectedCb((uint32_t*)client); if (client->cmdConnectedCb) client->cmdConnectedCb((uint32_t*)client); @@ -161,28 +170,36 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_SUBACK: if (pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && pending_msg_id == msg_id) { +#ifdef MQTT_DBG os_printf("MQTT: Subscribe successful\n"); +#endif client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } break; case MQTT_MSG_TYPE_UNSUBACK: if (pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && pending_msg_id == msg_id) { +#ifdef MQTT_DBG os_printf("MQTT: Unsubscribe successful\n"); +#endif client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } break; case MQTT_MSG_TYPE_PUBACK: // ack for a publish we sent if (pending_msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg_id == msg_id) { +#ifdef MQTT_DBG os_printf("MQTT: QoS1 Publish successful\n"); +#endif client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } break; case MQTT_MSG_TYPE_PUBREC: // rec for a publish we sent if (pending_msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg_id == msg_id) { +#ifdef MQTT_DBG os_printf("MQTT: QoS2 publish cont\n"); +#endif client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); // we need to send PUBREL mqtt_msg_pubrel(&client->mqtt_connection, msg_id); @@ -193,7 +210,9 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_PUBCOMP: // comp for a pubrel we sent (originally publish we sent) if (pending_msg_type == MQTT_MSG_TYPE_PUBREL && pending_msg_id == msg_id) { +#ifdef MQTT_DBG os_printf("MQTT: QoS2 Publish successful\n"); +#endif client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } break; @@ -201,9 +220,11 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_PUBLISH: { // incoming publish // we may need to ACK the publish uint8_t msg_qos = mqtt_get_qos(client->in_buffer); +#ifdef MQTT_DBG uint16_t topic_length = msg_len; os_printf("MQTT: Recv PUBLISH qos=%d %s\n", msg_qos, mqtt_get_publish_topic(client->in_buffer, &topic_length)); +#endif if (msg_qos == 1) mqtt_msg_puback(&client->mqtt_connection, msg_id); if (msg_qos == 2) mqtt_msg_pubrec(&client->mqtt_connection, msg_id); if (msg_qos == 1 || msg_qos == 2) { @@ -217,7 +238,9 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_PUBREL: // rel for a rec we sent (originally publish received) if (pending_msg_type == MQTT_MSG_TYPE_PUBREC && pending_msg_id == msg_id) { +#ifdef MQTT_DBG os_printf("MQTT: Cont QoS2 recv\n"); +#endif client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); // we need to send PUBCOMP mqtt_msg_pubcomp(&client->mqtt_connection, msg_id); @@ -294,7 +317,9 @@ mqtt_timer(void* arg) { // check whether our last keep-alive timed out if (client->keepAliveAckTick > 0 && --client->keepAliveAckTick == 0) { +#ifdef MQTT_DBG os_printf("\nMQTT ERROR: Keep-alive timed out\n"); +#endif mqtt_doAbort(client); return; } @@ -337,15 +362,17 @@ void ICACHE_FLASH_ATTR mqtt_tcpclient_discon_cb(void* arg) { struct espconn* pespconn = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pespconn->reverse; - +#ifdef MQTT_DBG os_printf("MQTT: Disconnect CB, freeing espconn %p\n", arg); +#endif if (pespconn->proto.tcp) os_free(pespconn->proto.tcp); os_free(pespconn); // if this is an aborted connection we're done if (client == NULL) return; - +#ifdef MQTT_DBG os_printf("MQTT: Disconnected from %s:%d\n", client->host, client->port); +#endif if (client->disconnectedCb) client->disconnectedCb((uint32_t*)client); if (client->cmdDisconnectedCb) client->cmdDisconnectedCb((uint32_t*)client); @@ -364,12 +391,14 @@ static void ICACHE_FLASH_ATTR mqtt_tcpclient_recon_cb(void* arg, int8_t err) { struct espconn* pespconn = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pespconn->reverse; - +#ifdef MQTT_DBG os_printf("MQTT: Reset CB, freeing espconn %p (err=%d)\n", arg, err); +#endif if (pespconn->proto.tcp) os_free(pespconn->proto.tcp); os_free(pespconn); - +#ifdef MQTT_DBG os_printf("MQTT: Connection reset from %s:%d\n", client->host, client->port); +#endif if (client->disconnectedCb) client->disconnectedCb((uint32_t*)client); if (client->cmdDisconnectedCb) client->cmdDisconnectedCb((uint32_t*)client); @@ -394,7 +423,9 @@ mqtt_tcpclient_connect_cb(void* arg) { espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv); espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb); +#ifdef MQTT_DBG os_printf("MQTT: TCP connected to %s:%d\n", client->host, client->port); +#endif // send MQTT connect message to broker mqtt_msg_connect(&client->mqtt_connection, &client->connect_info); @@ -435,8 +466,9 @@ mqtt_send_message(MQTT_Client* client) { // get some details about the message uint16_t msg_type = mqtt_get_type(buf->data); uint8_t msg_id = mqtt_get_id(buf->data, buf->filled); + msg_id = msg_id; +#ifdef MQTT_DBG os_printf("MQTT: Send type=%s id=%04X len=%d\n", mqtt_msg_type[msg_type], msg_id, buf->filled); -#if 0 for (int i=0; ifilled; i++) { if (buf->data[i] >= ' ' && buf->data[i] <= '~') os_printf("%c", buf->data[i]); else os_printf("\\x%02X", buf->data[i]); @@ -481,17 +513,20 @@ mqtt_dns_found(const char* name, ip_addr_t* ipaddr, void* arg) { MQTT_Client* client = (MQTT_Client *)pConn->reverse; if (ipaddr == NULL) { +#ifdef MQTT_DBG os_printf("MQTT DNS: Got no ip, try to reconnect\n"); +#endif client->timeoutTick = 10; client->connState = TCP_RECONNECT_REQ; // the timer will kick-off a reconnection return; } - +#ifdef MQTT_DBG os_printf("MQTT DNS: found ip %d.%d.%d.%d\n", *((uint8 *)&ipaddr->addr), *((uint8 *)&ipaddr->addr + 1), *((uint8 *)&ipaddr->addr + 2), *((uint8 *)&ipaddr->addr + 3)); +#endif if (client->ip.addr == 0 && ipaddr->addr != 0) { os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); @@ -501,11 +536,15 @@ mqtt_dns_found(const char* name, ip_addr_t* ipaddr, void* arg) { else err = espconn_connect(client->pCon); if (err != 0) { +#ifdef MQTT_DBG os_printf("MQTT ERROR: Failed to connect\n"); +#endif client->connState = TCP_RECONNECT_REQ; client->timeoutTick = 10; } else { +#ifdef MQTT_DBG os_printf("MQTT: connecting...\n"); +#endif } } } @@ -539,7 +578,9 @@ MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t q uint16_t buf_len = 3 + 2 + 2 + topic_length + data_length + 16; PktBuf *buf = PktBuf_New(buf_len); if (buf == NULL) { +#ifdef MQTT_DBG os_printf("MQTT ERROR: Cannot allocate buffer for %d byte publish\n", buf_len); +#endif return FALSE; } // use a temporary mqtt_message_t pointing to our buffer, this is a bit of a mess because we @@ -548,13 +589,16 @@ MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t q msg_conn_init(&msg, &client->mqtt_connection, buf->data, buf_len); uint16_t msg_id; if (!mqtt_msg_publish(&msg, topic, data, data_length, qos, retain, &msg_id)){ +#ifdef MQTT_DBG os_printf("MQTT ERROR: Queuing Publish failed\n"); +#endif os_free(buf); return FALSE; } client->mqtt_connection.message_id = msg.message_id; - +#ifdef MQTT_DBG os_printf("MQTT: Publish, topic: \"%s\", length: %d\n", topic, msg.message.length); +#endif client->msgQueue = PktBuf_Push(client->msgQueue, buf); if (!client->sending && client->pending_buffer == NULL) { @@ -574,10 +618,14 @@ bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos) { uint16_t msg_id; if (!mqtt_msg_subscribe(&client->mqtt_connection, topic, 0, &msg_id)) { +#ifdef MQTT_DBG os_printf("MQTT ERROR: Queuing Subscribe failed (too long)\n"); +#endif return FALSE; } +#ifdef MQTT_DBG os_printf("MQTT: Subscribe, topic: \"%s\"\n", topic); +#endif mqtt_enq_message(client, client->mqtt_connection.message.data, client->mqtt_connection.message.length); return TRUE; @@ -595,14 +643,20 @@ MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos) { * @param client_user: MQTT client user * @param client_pass: MQTT client password * @param keepAliveTime: MQTT keep alive timer, in second -* @param cleanSession: MQTT ... +* @param cleanSession: On connection, a client sets the "clean session" flag, which is sometimes also known as the "clean start" flag. +* If clean session is set to false, then the connection is treated as durable. This means that when the client +* disconnects, any subscriptions it has will remain and any subsequent QoS 1 or 2 messages will be stored until +* it connects again in the future. If clean session is true, then all subscriptions will be removed for the client +* when it disconnects. * @retval None */ void ICACHE_FLASH_ATTR MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security, uint8_t sendTimeout, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) { +#ifdef MQTT_DBG os_printf("MQTT_Init\n"); +#endif os_memset(mqttClient, 0, sizeof(MQTT_Client)); @@ -679,7 +733,9 @@ MQTT_Connect(MQTT_Client* mqttClient) { os_timer_arm(&mqttClient->mqttTimer, 1000, 1); // initiate the TCP connection or DNS lookup +#ifdef MQTT_DBG os_printf("MQTT: Connect to %s:%d %p\n", mqttClient->host, mqttClient->port, mqttClient->pCon); +#endif if (UTILS_StrToIP((const char *)mqttClient->host, (void*)&mqttClient->pCon->proto.tcp->remote_ip)) { uint8_t err; @@ -688,7 +744,9 @@ MQTT_Connect(MQTT_Client* mqttClient) { else err = espconn_connect(mqttClient->pCon); if (err != 0) { +#ifdef MQTT_DBG os_printf("MQTT ERROR: Failed to connect\n"); +#endif os_free(mqttClient->pCon->proto.tcp); os_free(mqttClient->pCon); mqttClient->pCon = NULL; @@ -707,7 +765,9 @@ MQTT_Connect(MQTT_Client* mqttClient) { static void ICACHE_FLASH_ATTR mqtt_doAbort(MQTT_Client* client) { +#ifdef MQTT_DBG os_printf("MQTT: Disconnecting from %s:%d (%p)\n", client->host, client->port, client->pCon); +#endif client->pCon->reverse = NULL; // ensure we jettison this pCon... if (client->security) espconn_secure_disconnect(client->pCon); diff --git a/mqtt/mqtt_cmd.c b/mqtt/mqtt_cmd.c index d2a01e2..ed2eb68 100644 --- a/mqtt/mqtt_cmd.c +++ b/mqtt/mqtt_cmd.c @@ -8,11 +8,13 @@ void ICACHE_FLASH_ATTR cmdMqttConnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; +#ifdef MQTTCMD_DBG os_printf("MQTT: Connected connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n", (void*)cb->connectedCb, (void*)cb->disconnectedCb, (void*)cb->publishedCb, (void*)cb->dataCb); +#endif uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0); CMD_ResponseEnd(crc); } @@ -21,7 +23,9 @@ void ICACHE_FLASH_ATTR cmdMqttTcpDisconnectedCb(uint32_t *args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb *cb = (MqttCmdCb*)client->user_data; +#ifdef MQTTCMD_DBG os_printf("MQTT: TCP Disconnected\n"); +#endif uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->tcpDisconnectedCb, 0, 0); CMD_ResponseEnd(crc); } @@ -30,7 +34,9 @@ void ICACHE_FLASH_ATTR cmdMqttDisconnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; +#ifdef MQTTCMD_DBG os_printf("MQTT: Disconnected\n"); +#endif uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0); CMD_ResponseEnd(crc); } @@ -39,7 +45,9 @@ void ICACHE_FLASH_ATTR cmdMqttPublishedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; +#ifdef MQTTCMD_DBG os_printf("MQTT: Published\n"); +#endif uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0); CMD_ResponseEnd(crc); } @@ -103,8 +111,9 @@ MQTTCMD_Setup(CmdPacket *cmd) { // get clean session CMD_PopArg(&req, (uint8_t*)&clean_session, 4); - +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Setup clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session); +#endif // init client // TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again? @@ -155,7 +164,9 @@ MQTTCMD_Lwt(CmdPacket *cmd) { uint32_t client_ptr; CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); MQTT_Client* client = (MQTT_Client*)client_ptr; +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr); +#endif uint16_t len; @@ -182,12 +193,13 @@ MQTTCMD_Lwt(CmdPacket *cmd) { // get retain CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); - +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%d, retain=%d\n", client->connect_info.will_topic, client->connect_info.will_message, client->connect_info.will_qos, client->connect_info.will_retain); +#endif return 1; } @@ -203,7 +215,9 @@ MQTTCMD_Connect(CmdPacket *cmd) { uint32_t client_ptr; CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); MQTT_Client* client = (MQTT_Client*)client_ptr; +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Connect client ptr=%p\n", (void*)client_ptr); +#endif uint16_t len; @@ -221,11 +235,12 @@ MQTTCMD_Connect(CmdPacket *cmd) { // get security CMD_PopArg(&req, (uint8_t*)&client->security, 4); - +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Connect host=%s, port=%d, security=%d\n", client->host, client->port, client->security); +#endif MQTT_Connect(client); return 1; @@ -243,7 +258,9 @@ MQTTCMD_Disconnect(CmdPacket *cmd) { uint32_t client_ptr; CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); MQTT_Client* client = (MQTT_Client*)client_ptr; +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Disconnect client ptr=%p\n", (void*)client_ptr); +#endif // disconnect MQTT_Disconnect(client); @@ -262,7 +279,9 @@ MQTTCMD_Publish(CmdPacket *cmd) { uint32_t client_ptr; CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); MQTT_Client* client = (MQTT_Client*)client_ptr; +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Publish client ptr=%p\n", (void*)client_ptr); +#endif uint16_t len; uint8_t *topic, *data; @@ -293,12 +312,13 @@ MQTTCMD_Publish(CmdPacket *cmd) { // get retain CMD_PopArg(&req, (uint8_t*)&retain, 4); - +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n", topic, os_strlen((char*)data), qos, retain); +#endif MQTT_Publish(client, (char*)topic, (char*)data, (uint8_t)qos, (uint8_t)retain); os_free(topic); @@ -318,7 +338,9 @@ MQTTCMD_Subscribe(CmdPacket *cmd) { uint32_t client_ptr; CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); MQTT_Client* client = (MQTT_Client*)client_ptr; +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Subscribe client ptr=%p\n", (void*)client_ptr); +#endif uint16_t len; uint8_t* topic; @@ -333,8 +355,9 @@ MQTTCMD_Subscribe(CmdPacket *cmd) { // get qos CMD_PopArg(&req, (uint8_t*)&qos, 4); - +#ifdef MQTTCMD_DBG os_printf("MQTT: MQTTCMD_Subscribe topic=%s, qos=%ld\n", topic, qos); +#endif MQTT_Subscribe(client, (char*)topic, (uint8_t)qos); os_free(topic); return 1; diff --git a/mqtt/pktbuf.c b/mqtt/pktbuf.c index 2984fb4..a3ac7af 100644 --- a/mqtt/pktbuf.c +++ b/mqtt/pktbuf.c @@ -3,7 +3,8 @@ #include #include "pktbuf.h" -void ICACHE_FLASH_ATTR +#ifdef PKTBUF_DBG +static void ICACHE_FLASH_ATTR PktBuf_Print(PktBuf *buf) { os_printf("PktBuf:"); for (int i=-16; i<0; i++) @@ -15,6 +16,7 @@ PktBuf_Print(PktBuf *buf) { os_printf("PktBuf: next=%p len=0x%04x\n", ((void**)buf)[-4], ((uint16_t*)buf)[-6]); } +#endif PktBuf * ICACHE_FLASH_ATTR diff --git a/mqtt/pktbuf.h b/mqtt/pktbuf.h index 0c8ff2c..bccd60a 100644 --- a/mqtt/pktbuf.h +++ b/mqtt/pktbuf.h @@ -24,6 +24,4 @@ PktBuf *PktBuf_Shift(PktBuf *headBuf); // Shift first buffer off queue, free it, return new head PktBuf *PktBuf_ShiftFree(PktBuf *headBuf); -void PktBuf_Print(PktBuf *buf); - #endif diff --git a/rest/rest.c b/rest/rest.c index 40b62de..fbbd358 100644 --- a/rest/rest.c +++ b/rest/rest.c @@ -73,16 +73,20 @@ tcpclient_recv(void *arg, char *pdata, unsigned short len) { // collect body and send it uint16_t crc; int body_len = len-pi; +#ifdef REST_DBG os_printf("REST: status=%ld, body=%d\n", code, body_len); +#endif if (pi == len) { crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0); } else { crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1); crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+pi), body_len); CMD_ResponseEnd(crc); +#ifdef REST_DBG os_printf("REST: body="); for (int j=pi; jsecurity) @@ -96,7 +100,9 @@ static void ICACHE_FLASH_ATTR tcpclient_sent_cb(void *arg) { struct espconn *pCon = (struct espconn *)arg; RestClient* client = (RestClient *)pCon->reverse; +#ifdef REST_DBG os_printf("REST: Sent\n"); +#endif if (client->data_sent != client->data_len) { // we only sent part of the buffer, send the rest espconn_sent(client->pCon, (uint8_t*)(client->data+client->data_sent), @@ -113,14 +119,17 @@ static void ICACHE_FLASH_ATTR tcpclient_connect_cb(void *arg) { struct espconn *pCon = (struct espconn *)arg; RestClient* client = (RestClient *)pCon->reverse; +#ifdef REST_DBG os_printf("REST #%d: connected\n", client-restClient); - +#endif espconn_regist_disconcb(client->pCon, tcpclient_discon_cb); espconn_regist_recvcb(client->pCon, tcpclient_recv); espconn_regist_sentcb(client->pCon, tcpclient_sent_cb); client->data_sent = client->data_len <= 1400 ? client->data_len : 1400; +#ifdef REST_DBG os_printf("REST #%d: sending %d\n", client-restClient, client->data_sent); +#endif //if(client->security){ // espconn_secure_sent(client->pCon, client->data, client->data_sent); //} @@ -133,7 +142,9 @@ static void ICACHE_FLASH_ATTR tcpclient_recon_cb(void *arg, sint8 errType) { struct espconn *pCon = (struct espconn *)arg; RestClient* client = (RestClient *)pCon->reverse; +#ifdef REST_DBG os_printf("REST #%d: conn reset\n", client-restClient); +#endif } static void ICACHE_FLASH_ATTR @@ -142,16 +153,18 @@ rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { RestClient* client = (RestClient *)pConn->reverse; if(ipaddr == NULL) { +#ifdef REST_DBG os_printf("REST DNS: Got no ip, try to reconnect\n"); +#endif return; } - +#ifdef REST_DBG 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)); - +#endif if(client->ip.addr == 0 && ipaddr->addr != 0) { os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); #ifdef CLIENT_SSL_ENABLE @@ -159,8 +172,10 @@ rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { espconn_secure_connect(client->pCon); } else #endif - espconn_connect(client->pCon); + espconn_connect(client->pCon); +#ifdef REST_DBG os_printf("REST: connecting...\n"); +#endif } } @@ -213,8 +228,9 @@ REST_Setup(CmdPacket *cmd) { os_free(client->pCon); } os_memset(client, 0, sizeof(RestClient)); - +#ifdef CMDREST_DBG os_printf("REST: setup #%d host=%s port=%ld security=%ld\n", clientNum, rest_host, port, security); +#endif client->resp_cb = cmd->callback; @@ -273,7 +289,9 @@ REST_SetHeader(CmdPacket *cmd) { client->header[len] = '\r'; client->header[len+1] = '\n'; client->header[len+2] = 0; +#ifdef CMDREST_DBG os_printf("REST: Set header: %s\r\n", client->header); +#endif break; case HEADER_CONTENT_TYPE: if(client->content_type) os_free(client->content_type); @@ -282,7 +300,9 @@ REST_SetHeader(CmdPacket *cmd) { client->content_type[len] = '\r'; client->content_type[len+1] = '\n'; client->content_type[len+2] = 0; +#ifdef CMDREST_DBG os_printf("REST: Set content_type: %s\r\n", client->content_type); +#endif break; case HEADER_USER_AGENT: if(client->user_agent) os_free(client->user_agent); @@ -291,7 +311,9 @@ REST_SetHeader(CmdPacket *cmd) { client->user_agent[len] = '\r'; client->user_agent[len+1] = '\n'; client->user_agent[len+2] = 0; +#ifdef CMDREST_DBG os_printf("REST: Set user_agent: %s\r\n", client->user_agent); +#endif break; } return 1; @@ -301,32 +323,36 @@ uint32_t ICACHE_FLASH_ATTR REST_Request(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); +#ifdef CMDREST_DBG os_printf("REST: request"); - +#endif // Get client uint32_t clientNum; if (CMD_PopArg(&req, (uint8_t*)&clientNum, 4)) goto fail; if ((clientNum & 0xffff0000) != REST_CB) goto fail; clientNum &= 0xffff; RestClient *client = restClient + clientNum % MAX_REST; +#ifdef CMDREST_DBG os_printf(" #%ld", clientNum); - +#endif // Get HTTP method uint16_t len = CMD_ArgLen(&req); if (len > 15) goto fail; char method[16]; CMD_PopArg(&req, method, len); method[len] = 0; +#ifdef CMDREST_DBG os_printf(" method=%s", method); - +#endif // Get HTTP path len = CMD_ArgLen(&req); if (len > 1023) goto fail; char path[1024]; CMD_PopArg(&req, path, len); path[len] = 0; +#ifdef CMDREST_DBG os_printf(" path=%s", path); - +#endif // Get HTTP body uint32_t realLen = 0; if (CMD_GetArgc(&req) == 3) { @@ -338,7 +364,9 @@ REST_Request(CmdPacket *cmd) { len = CMD_ArgLen(&req); if (len > 2048 || realLen > len) goto fail; } +#ifdef CMDREST_DBG os_printf(" bodyLen=%ld", realLen); +#endif // we need to allocate memory for the header plus the body. First we count the length of the // header (including some extra counted "%s" and then we add the body length. We allocate the @@ -353,30 +381,40 @@ REST_Request(CmdPacket *cmd) { "User-Agent: %s\r\n\r\n"; uint16_t headerLen = strlen(headerFmt) + strlen(method) + strlen(path) + strlen(client->host) + strlen(client->header) + strlen(client->content_type) + strlen(client->user_agent); +#ifdef CMDREST_DBG os_printf(" hdrLen=%d", headerLen); +#endif if (client->data) os_free(client->data); client->data = (char*)os_zalloc(headerLen + realLen); if (client->data == NULL) goto fail; +#ifdef CMDREST_DBG os_printf(" totLen=%ld data=%p", headerLen + realLen, client->data); +#endif client->data_len = os_sprintf((char*)client->data, headerFmt, method, path, client->host, client->header, realLen, client->content_type, client->user_agent); +#ifdef CMDREST_DBG os_printf(" hdrLen=%d", client->data_len); +#endif if (realLen > 0) { CMD_PopArg(&req, client->data + client->data_len, realLen); client->data_len += realLen; } +#ifdef CMDREST_DBG os_printf("\n"); //os_printf("REST request: %s", (char*)client->data); os_printf("REST: pCon state=%d\n", client->pCon->state); +#endif 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)) { +#ifdef CMDREST_DBG os_printf("REST: Connect to ip %s:%ld\n",client->host, client->port); +#endif //if(client->security){ // espconn_secure_connect(client->pCon); //} @@ -384,13 +422,17 @@ REST_Request(CmdPacket *cmd) { espconn_connect(client->pCon); //} } else { +#ifdef CMDREST_DBG os_printf("REST: Connect to host %s:%ld\n", client->host, client->port); +#endif espconn_gethostbyname(client->pCon, (char *)client->host, &client->ip, rest_dns_found); } return 1; fail: +#ifdef CMDREST_DBG os_printf("\n"); +#endif return 0; } diff --git a/serial/serbridge.c b/serial/serbridge.c index b91dbbc..ea5c848 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -100,10 +100,15 @@ telnetUnwrap(uint8_t *inBuf, int len, uint8_t state) switch (c) { case DTR_ON: if (mcu_reset_pin >= 0) { +#ifdef SERBR_DBG os_printf("MCU reset gpio%d\n", mcu_reset_pin); +#endif GPIO_OUTPUT_SET(mcu_reset_pin, 0); os_delay_us(100L); - } else os_printf("MCU reset: no pin\n"); + } +#ifdef SERBR_DBG + else os_printf("MCU reset: no pin\n"); +#endif break; case DTR_OFF: if (mcu_reset_pin >= 0) { @@ -113,10 +118,15 @@ telnetUnwrap(uint8_t *inBuf, int len, uint8_t state) break; case RTS_ON: if (mcu_isp_pin >= 0) { +#ifdef SERBR_DBG os_printf("MCU ISP gpio%d\n", mcu_isp_pin); +#endif GPIO_OUTPUT_SET(mcu_isp_pin, 0); os_delay_us(100L); - } else os_printf("MCU isp: no pin\n"); + } +#ifdef SERBR_DBG + else os_printf("MCU isp: no pin\n"); +#endif slip_disabled++; break; case RTS_OFF: @@ -137,11 +147,16 @@ telnetUnwrap(uint8_t *inBuf, int len, uint8_t state) // Generate a reset pulse for the attached microcontroller void ICACHE_FLASH_ATTR serbridgeReset() { if (mcu_reset_pin >= 0) { +#ifdef SERBR_DBG os_printf("MCU reset gpio%d\n", mcu_reset_pin); +#endif 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"); + } +#ifdef SERBR_DBG + else os_printf("MCU reset: no pin\n"); +#endif } // Receive callback @@ -162,7 +177,9 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh if ((len == 2 && strncmp(data, "0 ", 2) == 0) || (len == 2 && strncmp(data, "?\n", 2) == 0) || (len == 3 && strncmp(data, "?\r\n", 3) == 0)) { +#ifdef SERBR_DBG os_printf("MCU Reset=gpio%d ISP=gpio%d\n", mcu_reset_pin, mcu_isp_pin); +#endif 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); @@ -177,14 +194,18 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh 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) { + } + 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 +#ifdef SERBR_DBG os_printf("telnet mode\n"); +#endif // looks like a plain-vanilla connection! - } else { + } + else { conn->conn_mode = cmTransparent; } @@ -218,9 +239,11 @@ sendtxbuffer(serbridgeConnData *conn) { conn->readytosend = false; result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); conn->txbufferlen = 0; +#ifdef SERBR_DBG if (result != ESPCONN_OK) { os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); } +#endif } return result; } @@ -233,7 +256,9 @@ sendtxbuffer(serbridgeConnData *conn) { static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) { if (conn->txbufferlen + len > MAX_TXBUFFER) { +#ifdef SERBR_DBG os_printf("espbuffsend: txbuffer full on conn %p\n", conn); +#endif return -128; } os_memcpy(conn->txbuffer + conn->txbufferlen, data, len); @@ -323,10 +348,14 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { // Find empty conndata in pool int i; for (i=0; i= ' ' && slip_buf[i] <= '~') os_printf("%c", slip_buf[i]); @@ -54,6 +56,7 @@ slip_process() { os_printf("\\%02X", slip_buf[i]); } os_printf("\n"); +#endif } } } @@ -83,7 +86,9 @@ slip_parse_char(char c) { if (slip_len > 0) console_process(slip_buf, slip_len); slip_reset(); slip_inpkt = true; +#ifdef SLIP_DBG os_printf("SLIP: start\n"); +#endif return; } } else if (slip_escaped) { diff --git a/cmd/tcpclient.c b/tcpclient/tcpclient.c similarity index 100% rename from cmd/tcpclient.c rename to tcpclient/tcpclient.c diff --git a/cmd/tcpclient.h b/tcpclient/tcpclient.h similarity index 100% rename from cmd/tcpclient.h rename to tcpclient/tcpclient.h diff --git a/user/latch_json.c b/user/latch_json.c new file mode 100644 index 0000000..0994894 --- /dev/null +++ b/user/latch_json.c @@ -0,0 +1,64 @@ +#include "latch_json.h" +#include "user_funcs.h" +#include +#include +#include + +static LatchState latch; + +static void ICACHE_FLASH_ATTR +updateLatch() { + os_printf("ESP: Latch Callback\n"); + cmdCallback* latchCb = CMD_GetCbByName("Latch"); + if (latchCb->callback != -1) { + uint16_t crc = CMD_ResponseStart(CMD_CB_EVENTS, (uint32_t)&latchCb->callback, 0, 1); + crc = CMD_ResponseBody(crc, (uint8_t*)&latch, sizeof(LatchState)); + CMD_ResponseEnd(crc); + } +} + +static int ICACHE_FLASH_ATTR +latchGet(struct jsontree_context *js_ctx) { + return 0; +} + +static int ICACHE_FLASH_ATTR +latchSet(struct jsontree_context *js_ctx, struct jsonparse_state *parser) { + int type; + int ix = -1; + while ((type = jsonparse_next(parser)) != 0) { + if (type == JSON_TYPE_ARRAY) { + ix = -1; + } + else if (type == JSON_TYPE_OBJECT) { + ix++; + } + else if (type == JSON_TYPE_PAIR_NAME) { + if (jsonparse_strcmp_value(parser, "states") == 0) { + char latchStates[9]; + jsonparse_next(parser); jsonparse_next(parser); + jsonparse_copy_value(parser, latchStates, sizeof(latchStates)); + os_printf("latch states %s\n", latchStates); + uint8_t states = binToByte(latchStates); + latch.stateBits = states; + } + else if (jsonparse_strcmp_value(parser, "fallbackstates") == 0) { + char fallbackStates[9]; + jsonparse_next(parser); jsonparse_next(parser); + jsonparse_copy_value(parser, fallbackStates, sizeof(fallbackStates)); + os_printf("latch states %s\n", fallbackStates); + uint8_t fbstates = binToByte(fallbackStates); + latch.fallbackStateBits = fbstates; + } + } + } + return 0; +} + +static struct jsontree_callback latchCallback = JSONTREE_CALLBACK(latchGet, latchSet); +static char* latchQueueName; + +JSONTREE_OBJECT(latchJsonObj, + JSONTREE_PAIR("states", &latchCallback), + JSONTREE_PAIR("fallbackstates", &latchCallback)); + diff --git a/user/latch_json.h b/user/latch_json.h new file mode 100644 index 0000000..0bc4135 --- /dev/null +++ b/user/latch_json.h @@ -0,0 +1,12 @@ +#ifndef _LATCH_JSON_H_ +#define _LATCH_JSON_H_ +#include + +typedef struct { + uint8_t fallbackStateBits; + uint8_t stateBits; + uint8_t init; + uint8_t fallbackSecondsForBits[8]; +} LatchState; + +#endif // _LATCH_JSON_H_ \ No newline at end of file diff --git a/user/user_funcs.c b/user/user_funcs.c index bdb082b..49dedbd 100644 --- a/user/user_funcs.c +++ b/user/user_funcs.c @@ -1,6 +1,7 @@ #include "user_funcs.h" -bool ICACHE_FLASH_ATTR pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, uint8_t offHour, uint8_t offMinute) { +bool ICACHE_FLASH_ATTR +pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, uint8_t offHour, uint8_t offMinute) { uint16_t NumMinsToday = totalMinutes(hour(), minute()); bool state = false; @@ -19,7 +20,8 @@ bool ICACHE_FLASH_ATTR pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, return state; } -const char* ICACHE_FLASH_ATTR byteToBin(uint8_t num) { +const char* ICACHE_FLASH_ATTR +byteToBin(uint8_t num) { static char b[9]; b[0] = '\0'; @@ -30,7 +32,8 @@ const char* ICACHE_FLASH_ATTR byteToBin(uint8_t num) { return b; } -const uint8_t ICACHE_FLASH_ATTR binToByte(char* bin_str) { +const uint8_t ICACHE_FLASH_ATTR +binToByte(char* bin_str) { char * tmp; long x = strtol(bin_str, &tmp, 2); return (x <= 255) ? (uint8_t)x : -1; diff --git a/user/user_funcs.h b/user/user_funcs.h index 156e432..b1da228 100644 --- a/user/user_funcs.h +++ b/user/user_funcs.h @@ -7,4 +7,5 @@ bool pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, uint8_t offHour, u const char* byteToBin(uint8_t num); const uint8_t binToByte(char* bin_str); + #endif // _USER_FUNCS_H_ \ No newline at end of file diff --git a/user/user_main.c b/user/user_main.c index cd0dbf0..a796e14 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -1,38 +1,14 @@ #include #include "cgiwifi.h" -#include "mqtt.h" +#include +#include +#include "latch_json.h" +#include "user_funcs.h" -MQTT_Client mqttClient; - -static ETSTimer mqttTimer; - -static int once = 0; -static void ICACHE_FLASH_ATTR mqttTimerCb(void *arg) { - if (once++ > 0) return; - MQTT_Init(&mqttClient, "h.voneicken.com", 1883, 0, 2, "test1", "", "", 10, 1); - MQTT_Connect(&mqttClient); - MQTT_Subscribe(&mqttClient, "system/time", 0); -} - -void ICACHE_FLASH_ATTR -wifiStateChangeCb(uint8_t status) -{ - if (status == wifiGotIP) { - os_timer_disarm(&mqttTimer); - os_timer_setfn(&mqttTimer, mqttTimerCb, NULL); - os_timer_arm(&mqttTimer, 15000, 0); - } -} - - -// initialize the custom stuff that goes beyond esp-link -void app_init() { - wifiAddStateChangeCb(wifiStateChangeCb); -} -#if 0 MQTT_Client mqttClient; +//static ETSTimer mqttTimer; void ICACHE_FLASH_ATTR mqttConnectedCb(uint32_t *args) { @@ -60,11 +36,10 @@ mqttPublishedCb(uint32_t *args) { void ICACHE_FLASH_ATTR mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { +// MQTT_Client* client = (MQTT_Client*)args; char *topicBuf = (char*)os_zalloc(topic_len + 1); char *dataBuf = (char*)os_zalloc(data_len + 1); -// MQTT_Client* client = (MQTT_Client*)args; - os_memcpy(topicBuf, topic, topic_len); topicBuf[topic_len] = 0; @@ -76,12 +51,33 @@ mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *da os_free(dataBuf); } - MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); - MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); - MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); - MQTT_OnConnected(&mqttClient, mqttConnectedCb); - MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); - MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); - MQTT_OnPublished(&mqttClient, mqttPublishedCb); - MQTT_OnData(&mqttClient, mqttDataCb); -#endif +void ICACHE_FLASH_ATTR +wifiStateChangeCb(uint8_t status) { + if (flashConfig.mqtt_enable) { + if (status == wifiGotIP && mqttClient.connState != TCP_CONNECTING) { + MQTT_Connect(&mqttClient); + } + else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING) { + MQTT_Disconnect(&mqttClient); + } + } +} + +// initialize the custom stuff that goes beyond esp-link +void app_init() { + if (flashConfig.mqtt_enable) { + MQTT_Init(&mqttClient, flashConfig.mqtt_host, flashConfig.mqtt_port, 0, flashConfig.mqtt_timeout, + flashConfig.mqtt_clientid, flashConfig.mqtt_username, flashConfig.mqtt_password, flashConfig.mqtt_keepalive, flashConfig.mqtt_clean_session + ); + + MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); + MQTT_OnConnected(&mqttClient, mqttConnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); + MQTT_OnPublished(&mqttClient, mqttPublishedCb); + MQTT_OnData(&mqttClient, mqttDataCb); + } + + wifiAddStateChangeCb(wifiStateChangeCb); +} + From b080f5243b103e564284fcbe1db033c4fb9677bd Mon Sep 17 00:00:00 2001 From: Benjamin Runnels Date: Fri, 11 Sep 2015 16:48:56 -0500 Subject: [PATCH 077/104] Fixed whitespace --- esp-link/cgi.c | 68 ++++++++-------- esp-link/log.c | 212 ++++++++++++++++++++++++------------------------- 2 files changed, 140 insertions(+), 140 deletions(-) diff --git a/esp-link/cgi.c b/esp-link/cgi.c index c99bada..1c870f5 100644 --- a/esp-link/cgi.c +++ b/esp-link/cgi.c @@ -18,17 +18,17 @@ Some random cgi routines. #include "cgi.h" void noCacheHeaders(HttpdConnData *connData, int code) { - httpdStartResponse(connData, code); - httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); - httpdHeader(connData, "Pragma", "no-cache"); - httpdHeader(connData, "Expires", "0"); + httpdStartResponse(connData, code); + httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); + httpdHeader(connData, "Pragma", "no-cache"); + httpdHeader(connData, "Expires", "0"); } void ICACHE_FLASH_ATTR jsonHeader(HttpdConnData *connData, int code) { noCacheHeaders(connData, code); - httpdHeader(connData, "Content-Type", "application/json"); - httpdEndHeaders(connData); + httpdHeader(connData, "Content-Type", "application/json"); + httpdEndHeaders(connData); } void ICACHE_FLASH_ATTR @@ -119,43 +119,43 @@ UTILS_StrToIP(const char* str, void *ip){ // Handle system information variables and print their value, returns the number of // characters appended to buff int ICACHE_FLASH_ATTR printGlobalInfo(char *buff, int buflen, char *token) { - if (TOKEN("si_chip_id")) { - return os_sprintf(buff, "0x%x", system_get_chip_id()); - } else if (TOKEN("si_freeheap")) { - return os_sprintf(buff, "%dKB", system_get_free_heap_size()/1024); - } else if (TOKEN("si_uptime")) { - uint32 t = system_get_time() / 1000000; // in seconds - return os_sprintf(buff, "%dd%dh%dm%ds", t/(24*3600), (t/(3600))%24, (t/60)%60, t%60); - } else if (TOKEN("si_boot_version")) { - return os_sprintf(buff, "%d", system_get_boot_version()); - } else if (TOKEN("si_boot_address")) { - return os_sprintf(buff, "0x%x", system_get_userbin_addr()); - } else if (TOKEN("si_cpu_freq")) { - return os_sprintf(buff, "%dMhz", system_get_cpu_freq()); - } else { - return 0; - } + if (TOKEN("si_chip_id")) { + return os_sprintf(buff, "0x%x", system_get_chip_id()); + } else if (TOKEN("si_freeheap")) { + return os_sprintf(buff, "%dKB", system_get_free_heap_size()/1024); + } else if (TOKEN("si_uptime")) { + uint32 t = system_get_time() / 1000000; // in seconds + return os_sprintf(buff, "%dd%dh%dm%ds", t/(24*3600), (t/(3600))%24, (t/60)%60, t%60); + } else if (TOKEN("si_boot_version")) { + return os_sprintf(buff, "%d", system_get_boot_version()); + } else if (TOKEN("si_boot_address")) { + return os_sprintf(buff, "0x%x", system_get_userbin_addr()); + } else if (TOKEN("si_cpu_freq")) { + return os_sprintf(buff, "%dMhz", system_get_cpu_freq()); + } else { + return 0; + } } #endif extern char *esp_link_version; // in user_main.c int ICACHE_FLASH_ATTR cgiMenu(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[1024]; - // don't use jsonHeader so the response does get cached - httpdStartResponse(connData, 200); - httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate"); - httpdHeader(connData, "Content-Type", "application/json"); - httpdEndHeaders(connData); - // construct json response - os_sprintf(buff, + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[1024]; + // don't use jsonHeader so the response does get cached + httpdStartResponse(connData, 200); + httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate"); + httpdHeader(connData, "Content-Type", "application/json"); + httpdEndHeaders(connData); + // construct json response + os_sprintf(buff, "{\"menu\": [\"Home\", \"/home.html\", " "\"Wifi\", \"/wifi/wifi.html\"," "\"\xC2\xB5" "C Console\", \"/console.html\", " "\"REST/MQTT\", \"/mqtt.html\"," "\"Debug log\", \"/log.html\" ],\n" - " \"version\": \"%s\" }", esp_link_version); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + " \"version\": \"%s\" }", esp_link_version); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } diff --git a/esp-link/log.c b/esp-link/log.c index 713b9c3..ca8e0ad 100644 --- a/esp-link/log.c +++ b/esp-link/log.c @@ -22,139 +22,139 @@ static bool log_newline; // at start of a new line // when we connect to wifi AP. Here this is gated by the flash setting void ICACHE_FLASH_ATTR log_uart(bool enable) { - if (!enable && !log_no_uart && flashConfig.log_mode != LOG_MODE_ON) { - // we're asked to turn uart off, and uart is on, and the flash setting isn't always-on + if (!enable && !log_no_uart && flashConfig.log_mode != LOG_MODE_ON) { + // we're asked to turn uart off, and uart is on, and the flash setting isn't always-on #if 1 #ifdef LOG_DBG - os_printf("Turning OFF uart log\n"); + os_printf("Turning OFF uart log\n"); #endif - os_delay_us(4*1000L); // time for uart to flush - log_no_uart = !enable; + os_delay_us(4*1000L); // time for uart to flush + log_no_uart = !enable; #endif - } else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) { - // we're asked to turn uart on, and uart is off, and the flash setting isn't always-off - log_no_uart = !enable; + } else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) { + // we're asked to turn uart on, and uart is off, and the flash setting isn't always-off + log_no_uart = !enable; #ifdef LOG_DBG - os_printf("Turning ON uart log\n"); + os_printf("Turning ON uart log\n"); #endif - } + } } static void ICACHE_FLASH_ATTR log_write(char c) { - log_buf[log_wr] = c; - log_wr = (log_wr+1) % BUF_MAX; - if (log_wr == log_rd) { - log_rd = (log_rd+1) % BUF_MAX; // full, eat first char - log_pos++; - } + log_buf[log_wr] = c; + log_wr = (log_wr+1) % BUF_MAX; + if (log_wr == log_rd) { + log_rd = (log_rd+1) % BUF_MAX; // full, eat first char + log_pos++; + } } #if 0 static char ICACHE_FLASH_ATTR log_read(void) { - char c = 0; - if (log_rd != log_wr) { - c = log_buf[log_rd]; - log_rd = (log_rd+1) % BUF_MAX; - } - return c; + char c = 0; + if (log_rd != log_wr) { + c = log_buf[log_rd]; + log_rd = (log_rd+1) % BUF_MAX; + } + return c; } #endif static void ICACHE_FLASH_ATTR log_write_char(char c) { - // Uart output unless disabled - if (!log_no_uart) { - if (log_newline) { - char buff[16]; - int l = os_sprintf(buff, "%6d> ", (system_get_time()/1000)%1000000); - for (int i=0; i ", (system_get_time()/1000)%1000000); + for (int i=0; iconn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - - // figure out where to start in buffer based on URI param - len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); - if (len > 0) { - start = atoi(buff); - if (start < log_pos) { - start = 0; - } else if (start >= log_pos+log_len) { - start = log_len; - } else { - start = start - log_pos; - } - } - - // start outputting - len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", - log_len-start, log_pos+start); - - int rd = (log_rd+start) % BUF_MAX; - while (len < 2040 && rd != log_wr) { - uint8_t c = log_buf[rd]; - if (c == '\\' || c == '"') { - buff[len++] = '\\'; - buff[len++] = c; - } 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; + char buff[2048]; + int len; // length of text in buff + int log_len = (log_wr+BUF_MAX-log_rd) % BUF_MAX; // num chars in log_buf + int start = 0; // offset onto log_wr to start sending out chars + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + + // figure out where to start in buffer based on URI param + len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); + if (len > 0) { + start = atoi(buff); + if (start < log_pos) { + start = 0; + } else if (start >= log_pos+log_len) { + start = log_len; + } else { + start = start - log_pos; + } + } + + // start outputting + len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", + log_len-start, log_pos+start); + + int rd = (log_rd+start) % BUF_MAX; + while (len < 2040 && rd != log_wr) { + uint8_t c = log_buf[rd]; + if (c == '\\' || c == '"') { + buff[len++] = '\\'; + buff[len++] = c; + } 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; } static char *dbg_mode[] = { "auto", "off", "on" }; int ICACHE_FLASH_ATTR ajaxLogDbg(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, "mode", buff, sizeof(buff)); - if (len > 0) { - int8_t mode = -1; - if (os_strcmp(buff, "auto") == 0) mode = LOG_MODE_AUTO; - if (os_strcmp(buff, "off") == 0) mode = LOG_MODE_OFF; - if (os_strcmp(buff, "on") == 0) mode = LOG_MODE_ON; - if (mode >= 0) { - flashConfig.log_mode = mode; - if (mode != LOG_MODE_AUTO) log_uart(mode == LOG_MODE_ON); - status = configSave() ? 200 : 400; - } - } else if (connData->requestType == HTTPD_METHOD_GET) { - status = 200; - } - - jsonHeader(connData, status); - os_sprintf(buff, "{\"mode\": \"%s\"}", dbg_mode[flashConfig.log_mode]); - 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, "mode", buff, sizeof(buff)); + if (len > 0) { + int8_t mode = -1; + if (os_strcmp(buff, "auto") == 0) mode = LOG_MODE_AUTO; + if (os_strcmp(buff, "off") == 0) mode = LOG_MODE_OFF; + if (os_strcmp(buff, "on") == 0) mode = LOG_MODE_ON; + if (mode >= 0) { + flashConfig.log_mode = mode; + if (mode != LOG_MODE_AUTO) log_uart(mode == LOG_MODE_ON); + status = configSave() ? 200 : 400; + } + } else if (connData->requestType == HTTPD_METHOD_GET) { + status = 200; + } + + jsonHeader(connData, status); + os_sprintf(buff, "{\"mode\": \"%s\"}", dbg_mode[flashConfig.log_mode]); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } void ICACHE_FLASH_ATTR dumpMem(void *addr, int len) { @@ -172,9 +172,9 @@ void ICACHE_FLASH_ATTR dumpMem(void *addr, int len) { } void ICACHE_FLASH_ATTR logInit() { - log_no_uart = flashConfig.log_mode == LOG_MODE_OFF; // ON unless set to always-off - log_wr = 0; - log_rd = 0; + log_no_uart = flashConfig.log_mode == LOG_MODE_OFF; // ON unless set to always-off + log_wr = 0; + log_rd = 0; os_install_putc1((void *)log_write_char); } From 45cfe914d5a3a3b668ecafd6be8f936cee17b7d9 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 11 Sep 2015 20:56:51 -0700 Subject: [PATCH 078/104] rewrite serbridge buffering --- serial/serbridge.c | 556 +++++++++++++++++++++++---------------------- serial/serbridge.h | 13 +- 2 files changed, 288 insertions(+), 281 deletions(-) diff --git a/serial/serbridge.c b/serial/serbridge.c index 0d6dc57..8a912c8 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -22,65 +22,79 @@ sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, // 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) { - 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; + 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); + if (result != ESPCONN_OK) { + os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); + conn->txbufferlen = 0; + } else { + conn->sentbuffer = conn->txbuffer; + conn->txbuffer = NULL; + conn->txbufferlen = 0; + } + } + return result; } +static char *tx_full_msg = "espbuffsend: txbuffer full on conn %p\n"; + // 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; + if (conn->txbufferlen >= MAX_TXBUFFER) { + os_printf(tx_full_msg, conn); + return -128; + } + + // make sure we indeed have a buffer + if (conn->txbuffer == NULL) conn->txbuffer = os_zalloc(MAX_TXBUFFER); + if (conn->txbuffer == NULL) { + os_printf("espbuffsend: cannot alloc tx buffer\n"); + return -128; + } + + // add to send buffer + uint16_t avail = conn->txbufferlen+len > MAX_TXBUFFER ? MAX_TXBUFFER-conn->txbufferlen : len; + os_memcpy(conn->txbuffer + conn->txbufferlen, data, avail); + conn->txbufferlen += len; + + // try to send + sint8 result = ESPCONN_OK; + if (conn->readytosend) result = sendtxbuffer(conn); + + if (avail < len) { + // some data didn't fit into the buffer + if (conn->txbufferlen == 0) { + // we sent the prior buffer, so try again + return espbuffsend(conn, data+avail, len-avail); + } + os_printf(tx_full_msg, conn); + return -128; + } + return result; } //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 + serbridgeConnData *conn = ((struct espconn*)arg)->reverse; + //os_printf("Sent callback on conn %p\n", conn); + if (conn == NULL) return; + //os_printf("%d ST\n", system_get_time()); + if (conn->sentbuffer != NULL) os_free(conn->sentbuffer); + conn->sentbuffer = NULL; + conn->readytosend = true; + sendtxbuffer(conn); // send possible new data in txbuffer } // Telnet protocol characters @@ -102,265 +116,257 @@ 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"); + 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; } 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; - } - } - - // 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 -} - -// 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; - // Close the connection - espconn_disconnect(conn->conn); - conn->conn = NULL; + serbridgeConnData *conn = ((struct espconn*)arg)->reverse; + //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; + } + } + + // 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 } // 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 *conn = ((struct espconn*)arg)->reverse; + if (conn == NULL) return; + // Free buffers + if (conn->sentbuffer != NULL) os_free(conn->sentbuffer); + conn->sentbuffer = NULL; + if (conn->txbuffer != NULL) os_free(conn->txbuffer); + conn->txbuffer = NULL; + conn->txbufferlen = 0; + // Send reset to attached uC if it was in programming mode + if (conn->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); + } + conn->conn = NULL; +} + +// Connection reset callback (note that there will be no DisconCb) +static void ICACHE_FLASH_ATTR serbridgeResetCb(void *arg, sint8 err) { + os_printf("serbridge: connection reset err=%d\n", err); + serbridgeDisconCb(arg); } // 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].readytosend = true; + connData[i].conn_mode = cmInit; + + espconn_regist_recvcb(conn, serbridgeRecvCb); + espconn_regist_disconcb(conn, serbridgeDisconCb); + espconn_regist_reconcb(conn, serbridgeResetCb); + espconn_regist_sentcb(conn, serbridgeSentCb); + + 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= 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(); + + for (int i = 0; i < MAX_CONN; i++) { + connData[i].conn = NULL; + } + 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 364f833..f824027 100644 --- a/serial/serbridge.h +++ b/serial/serbridge.h @@ -8,8 +8,8 @@ #define MAX_CONN 4 #define SER_BRIDGE_TIMEOUT 28799 -//Max send buffer len -#define MAX_TXBUFFER 1024 +// Send buffer size +#define MAX_TXBUFFER 2048 typedef struct serbridgeConnData serbridgeConnData; @@ -25,10 +25,11 @@ enum connModes { 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 + enum connModes conn_mode; // connection mode + char *txbuffer; // buffer for the data to send + uint16 txbufferlen; // length of data in txbuffer + char *sentbuffer; // buffer sent, awaiting callback to get freed + bool readytosend; // true, if txbuffer can be sent by espconn_sent uint8_t telnet_state; }; From 2a8f7583ea8368b2afc1b79c7439801735cb8744 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 11 Sep 2015 21:23:54 -0700 Subject: [PATCH 079/104] fix serbridge issue --- serial/serbridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serial/serbridge.c b/serial/serbridge.c index 8a912c8..765ed77 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -67,7 +67,7 @@ sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, u // add to send buffer uint16_t avail = conn->txbufferlen+len > MAX_TXBUFFER ? MAX_TXBUFFER-conn->txbufferlen : len; os_memcpy(conn->txbuffer + conn->txbufferlen, data, avail); - conn->txbufferlen += len; + conn->txbufferlen += avail; // try to send sint8 result = ESPCONN_OK; From b2d7d2a579d480adf3e8fd5b4ee655f713ff92ad Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Fri, 11 Sep 2015 23:31:03 -0700 Subject: [PATCH 080/104] fix-up MQTT page --- esp-link/cgimqtt.c | 30 +++++++++-- esp-link/mqtt_client.c | 8 ++- esp-link/status.c | 2 +- esp-link/status.h | 1 + html/home.html | 118 ++++++++++++++++------------------------- html/mqtt.html | 4 +- html/ui.js | 38 ------------- 7 files changed, 83 insertions(+), 118 deletions(-) diff --git a/esp-link/cgimqtt.c b/esp-link/cgimqtt.c index cbf4c16..ea46fad 100644 --- a/esp-link/cgimqtt.c +++ b/esp-link/cgimqtt.c @@ -4,18 +4,39 @@ #include #include "cgi.h" #include "config.h" +#include "status.h" +#include "mqtt_client.h" #include "cgimqtt.h" +static char *mqtt_states[] = { + "disconnected", "reconnecting", "connecting", "connected", +}; + // Cgi to return MQTT settings int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { - char buff[2048]; + char buff[1024]; int len; if (connData->conn==NULL) return HTTPD_CGI_DONE; + // get the current status topic for display + char status_buf1[128], *sb1=status_buf1; + char status_buf2[128], *sb2=status_buf2; + mqttStatusMsg(status_buf1); + // quote all " for the json, sigh... + for (int i=0; i<127 && *sb1; i++) { + if (*sb1 == '"') { + *sb2++ = '\\'; + i++; + } + *sb2++ = *sb1++; + } + *sb2 = 0; + len = os_sprintf(buff, "{ " "\"slip-enable\":%d, " "\"mqtt-enable\":%d, " + "\"mqtt-state\":\"%s\", " "\"mqtt-status-enable\":%d, " "\"mqtt-port\":%d, " "\"mqtt-host\":\"%s\", " @@ -23,11 +44,12 @@ int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { "\"mqtt-username\":\"%s\", " "\"mqtt-password\":\"%s\", " "\"mqtt-status-topic\":\"%s\", " - "\"mqtt-state\":\"%s\" }", - flashConfig.slip_enable, flashConfig.mqtt_enable, flashConfig.mqtt_status_enable, + "\"mqtt-status-value\":\"%s\" }", + flashConfig.slip_enable, flashConfig.mqtt_enable, + mqtt_states[mqttClient.connState], flashConfig.mqtt_status_enable, flashConfig.mqtt_port, flashConfig.mqtt_hostname, flashConfig.mqtt_client, flashConfig.mqtt_username, flashConfig.mqtt_password, - flashConfig.mqtt_status_topic, "connected"); + flashConfig.mqtt_status_topic, status_buf2); jsonHeader(connData, 200); httpdSend(connData, buff, len); diff --git a/esp-link/mqtt_client.c b/esp-link/mqtt_client.c index 229d996..9d7f331 100644 --- a/esp-link/mqtt_client.c +++ b/esp-link/mqtt_client.c @@ -8,7 +8,9 @@ MQTT_Client mqttClient; static ETSTimer mqttTimer; static int once = 0; -static void ICACHE_FLASH_ATTR mqttTimerCb(void *arg) { +static void ICACHE_FLASH_ATTR +mqttTimerCb(void *arg) +{ if (once++ > 0) return; MQTT_Init(&mqttClient, flashConfig.mqtt_hostname, flashConfig.mqtt_port, 0, 2, flashConfig.mqtt_client, flashConfig.mqtt_username, flashConfig.mqtt_password, 60); @@ -28,7 +30,9 @@ wifiStateChangeCb(uint8_t status) // initialize the custom stuff that goes beyond esp-link -void mqtt_client_init() { +void ICACHE_FLASH_ATTR +mqtt_client_init() +{ wifiAddStateChangeCb(wifiStateChangeCb); } diff --git a/esp-link/status.c b/esp-link/status.c index 2b08b20..1cb4069 100644 --- a/esp-link/status.c +++ b/esp-link/status.c @@ -75,7 +75,7 @@ void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { static ETSTimer mqttStatusTimer; -static int ICACHE_FLASH_ATTR +int ICACHE_FLASH_ATTR mqttStatusMsg(char *buf) { sint8 rssi = wifi_station_get_rssi(); if (rssi > 0) rssi = 0; // not connected or other error diff --git a/esp-link/status.h b/esp-link/status.h index 0f89edf..c6fb855 100644 --- a/esp-link/status.h +++ b/esp-link/status.h @@ -1,6 +1,7 @@ #ifndef STATUS_H #define STATUS_H +int mqttStatusMsg(char *buf); void statusWifiUpdate(uint8_t state); void statusInit(void); diff --git a/html/home.html b/html/home.html index 46a984d..e7454f9 100644 --- a/html/home.html +++ b/html/home.html @@ -1,87 +1,63 @@
-
JEELABS
+
JEELABS

esp-link

-
-
-
-

The JeeLabs esp-link firmware bridges the ESP8266 serial port to Wifi and can - program microcontrollers over the serial port, in particular Arduinos, AVRs, and - NXP's LPC800 and other ARM processors.

-

Program an Arduino/AVR using avrdude using a command - line similar to:

-
/home/arduino-1.0.5/hardware/tools/avrdude \
-   -DV -patmega328p -Pnet:esp-link.local:23 -carduino -b115200 -U \
-   -C /home/arduino-1.0.5/hardware/tools/avrdude.conf flash:w:my_sketch.hex:i -
-

where -Pnet:esp-link.local:23 tells avrdude to connect to port 23 of esp-link. - You can substitute the IP address of your esp-link for esp-link.local if necessary.

-

Please refer to - the online README - for up-to-date help and to the forthcoming - JeeLabs blog for an intro to the codebase.

-
-
-
-
-
-

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 -
-
-
-
-
-
-
+
+
+
+

The JeeLabs esp-link firmware bridges the ESP8266 serial port to Wifi and can + program microcontrollers over the serial port, in particular Arduinos, AVRs, and + NXP's LPC800 and other ARM processors.

+

Program an Arduino/AVR using avrdude using a command + line similar to:

+
/home/arduino-1.0.5/hardware/tools/avrdude \
+   -DV -patmega328p -Pnet:esp-link.local:23 -carduino -b115200 -U \
+   -C /home/arduino-1.0.5/hardware/tools/avrdude.conf flash:w:my_sketch.hex:i +
+

where -Pnet:esp-link.local:23 tells avrdude to connect to port 23 of esp-link. + You can substitute the IP address of your esp-link for esp-link.local if necessary.

+

Please refer to + the online README + for up-to-date help and to the forthcoming + JeeLabs blog for an intro to the codebase.

+
+
+
+
+
+

Wifi summary

+
+ + + + + + + + +
+
+
+

Pin assignment

+ Select one of the following signal/pin assignments to match your hardware +
+
+
+
+
+
+
diff --git a/html/mqtt.html b/html/mqtt.html index 257c137..033dfe8 100644 --- a/html/mqtt.html +++ b/html/mqtt.html @@ -69,9 +69,9 @@
Status reporting settings
- + - Suffixes: rssi, heap-free, ... + Message: