mirror of https://github.com/jeelabs/esp-link.git
Conflicts: Makefile cmd/handlers.c esp-link/cgi.c esp-link/cgiwifi.c html/console.html html/style.css html/ui.js httpd/httpd.c mqtt/mqtt.c mqtt/mqtt.h mqtt/mqtt_cmd.c user/user_main.cpull/47/head
commit
965b70a408
@ -0,0 +1,111 @@ |
||||
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
|
||||
// // TCP Client settings
|
||||
|
||||
#include <esp8266.h> |
||||
#include "cgi.h" |
||||
#include "config.h" |
||||
#include "cgimqtt.h" |
||||
|
||||
// Cgi to return MQTT settings
|
||||
int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { |
||||
char buff[2048]; |
||||
int len; |
||||
|
||||
if (connData->conn==NULL) return HTTPD_CGI_DONE; |
||||
|
||||
len = os_sprintf(buff, "{ " |
||||
"\"slip-enable\":%d, " |
||||
"\"mqtt-enable\":%d, " |
||||
"\"mqtt-status-enable\":%d, " |
||||
"\"mqtt-port\":%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"); |
||||
|
||||
jsonHeader(connData, 200); |
||||
httpdSend(connData, buff, len); |
||||
return HTTPD_CGI_DONE; |
||||
} |
||||
|
||||
// Cgi to change choice of pin assignments
|
||||
int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { |
||||
if (connData->conn==NULL) 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; |
||||
} |
||||
} |
||||
|
||||
// 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
|
||||
} |
||||
|
||||
// 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; |
||||
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); |
||||
} else { |
||||
httpdStartResponse(connData, 500); |
||||
httpdEndHeaders(connData); |
||||
httpdSend(connData, "Failed to save config", -1); |
||||
} |
||||
return HTTPD_CGI_DONE; |
||||
} |
||||
|
||||
int ICACHE_FLASH_ATTR cgiMqtt(HttpdConnData *connData) { |
||||
if (connData->requestType == HTTPD_METHOD_GET) { |
||||
return cgiMqttGet(connData); |
||||
} else if (connData->requestType == HTTPD_METHOD_POST) { |
||||
return cgiMqttSet(connData); |
||||
} else { |
||||
jsonHeader(connData, 404); |
||||
return HTTPD_CGI_DONE; |
||||
} |
||||
} |
@ -0,0 +1,8 @@ |
||||
#ifndef CGIMQTT_H |
||||
#define CGIMQTT_H |
||||
|
||||
#include "httpd.h" |
||||
|
||||
int cgiMqtt(HttpdConnData *connData); |
||||
|
||||
#endif |
@ -1 +1,46 @@ |
||||
<!DOCTYPE html><script src='http://linux-ws/esplink/console.js'></script> |
||||
<div id="main"> |
||||
<div class="header"> |
||||
<h1>Microcontroller Console</h1> |
||||
</div> |
||||
|
||||
<div class="content"> |
||||
<p>The Microcontroller console shows the last 1024 characters |
||||
received from UART0, to which a microcontroller is typically attached. |
||||
The UART is configured for 8 bits, no parity, 1 stop bit (8N1).</p> |
||||
<p> |
||||
<a id="reset-button" class="pure-button button-primary" href="#">Reset µC</a> |
||||
Baud: |
||||
<span id="baud-btns"></span> |
||||
</p> |
||||
<pre class="console" id="console"></pre> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<script type="text/javascript">console_url = "/console/text"</script> |
||||
<script src="console.js"></script> |
||||
<script type="text/javascript"> |
||||
var rates = [9600, 57600, 115200, 250000]; |
||||
|
||||
onLoad(function() { |
||||
fetchText(100, true); |
||||
|
||||
$("#reset-button").addEventListener("click", function(e) { |
||||
e.preventDefault(); |
||||
var co = $("#console"); |
||||
co.innerHTML = ""; |
||||
ajaxSpin('POST', "/console/reset", |
||||
function(resp) { showNotification("uC reset"); co.textEnd = 0; }, |
||||
function(s, st) { showWarning("Error resetting uC"); } |
||||
); |
||||
}); |
||||
|
||||
rates.forEach(function(r) { baudButton(r); }); |
||||
|
||||
ajaxJson('GET', "/console/baud", |
||||
function(data) { showRate(data.rate); }, |
||||
function(s, st) { showNotification(st); } |
||||
); |
||||
}); |
||||
</script> |
||||
</body></html> |
||||
|
@ -0,0 +1,99 @@ |
||||
<div id="main"> |
||||
<div class="header"> |
||||
<h1>REST & MQTT</h1> |
||||
</div> |
||||
|
||||
<div class="content"> |
||||
<div class="pure-g"> |
||||
<div class="pure-u-1"><div class="card"> |
||||
<p>The REST & MQTT support uses the SLIP protocol over the serial port to enable |
||||
the attached microcontroller to initiate outbound connections. |
||||
The REST support lets the uC initiate simple HTTP requests while the MQTT support |
||||
lets it communicate with an MQTT server bidirectionally at QoS 0 thru 2.</p> |
||||
<p>The MQTT support is in the form of a built-in client that connects to a server |
||||
using parameters set below and stored in esp-link's flash settings. This allows |
||||
esp-link to take care of connection parameters and disconnect/reconnect operations.</p> |
||||
<p>The MQTT client also supports sending periodic status messages about esp-link itself, |
||||
including Wifi RSSI, and free heap memory.</p> |
||||
<div class="form-horizontal"> |
||||
<input type="checkbox" name="slip-enable"/> |
||||
<label>Enable SLIP on serial port</label> |
||||
</div> |
||||
</div></div> |
||||
</div> |
||||
<div class="pure-g"> |
||||
<div class="pure-u-1 pure-u-md-1-2"> |
||||
<div class="card"> |
||||
<h1>MQTT |
||||
<div id="mqtt-spinner" class="spinner spinner-small"></div> |
||||
</h1> |
||||
<form action="#" id="mqtt-form" class="pure-form" hidden> |
||||
<div class="form-horizontal"> |
||||
<input type="checkbox" name="mqtt-enable"/> |
||||
<label>Enable MQTT client</label> |
||||
</div> |
||||
<div class="form-horizontal"> |
||||
<label>MQTT client state: </label> |
||||
<b id="mqtt-state"></b> |
||||
</div> |
||||
<br> |
||||
<legend>MQTT server settings</legend> |
||||
<div class="pure-form-stacked"> |
||||
<label>Server hostname/ip</label> |
||||
<input type="text" name="mqtt-host"/> |
||||
<label>Server port/ip</label> |
||||
<input type="text" name="mqtt-port"/> |
||||
<label>Client ID</label> |
||||
<input type="text" name="mqtt-client-id"/> |
||||
<label>Username</label> |
||||
<input type="text" name="mqtt-username"/> |
||||
<label>Password</label> |
||||
<input type="password" name="mqtt-password"/> |
||||
</div> |
||||
<button id="mqtt-button" type="submit" class="pure-button button-primary"> |
||||
Update server settings! |
||||
</button> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
<div class="pure-u-1 pure-u-md-1-2"> |
||||
<div class="card"> |
||||
<h1>Status reporting |
||||
<div id="mqtt-status-spinner" class="spinner spinner-small"></div> |
||||
</h1> |
||||
<form action="#" id="mqtt-status-form" class="pure-form" hidden> |
||||
<div class="form-horizontal"> |
||||
<input type="checkbox" name="mqtt-status-enable"/> |
||||
<label>Enable status reporting via MQTT</label> |
||||
</div> |
||||
<br> |
||||
<legend>Status reporting settings</legend> |
||||
<div class="pure-form-stacked"> |
||||
<label>Topic prefix</label> |
||||
<input type="text" name="mqtt-status-topic"/> |
||||
Suffixes: rssi, heap-free, ... |
||||
</div> |
||||
<button id="mqtt-status-button" type="submit" class="pure-button button-primary"> |
||||
Update status settings! |
||||
</button> |
||||
</form> |
||||
</div> |
||||
<div class="card"> |
||||
<h1>REST</h1> |
||||
<p>REST requests are enabled as soon as SLIP is enabled. |
||||
There are no REST-specific settings.</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<script type="text/javascript"> |
||||
onLoad(function() { |
||||
fetchMqtt(); |
||||
bnd($("#mqtt-form"), "submit", changeMqtt); |
||||
bnd($("#mqtt-status-form"), "submit", changeMqttStatus); |
||||
}); |
||||
</script> |
||||
</body></html> |
@ -0,0 +1,384 @@ |
||||
/* All fonts */ |
||||
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; |
||||
} |
||||
a:visited, a:link { |
||||
color: #009; |
||||
} |
||||
a:hover { |
||||
color: #00c; |
||||
} |
||||
|
||||
.card { |
||||
background-color: #eee; |
||||
padding: 1em; |
||||
margin: 0.5em; |
||||
-moz-border-radius: 0.5em; |
||||
-webkit-border-radius: 0.5em; |
||||
border-radius: 0.5em; |
||||
border: 0px solid #000000; |
||||
} |
||||
|
||||
/* wifi AP selection form */ |
||||
#aps label div { |
||||
display: inline-block; |
||||
margin: 0em 0.2em; |
||||
} |
||||
fieldset.radios { |
||||
border: none; |
||||
padding-left: 0px; |
||||
} |
||||
fieldset fields { |
||||
clear: both; |
||||
} |
||||
#pin-mux input { |
||||
display: block; |
||||
margin-top: 0.4em; |
||||
float: left; |
||||
} |
||||
#pin-mux label { |
||||
display: block; |
||||
margin: 0em 0.2em 0em 1em; |
||||
width: 90%; |
||||
} |
||||
|
||||
.pure-table td, .pure-table th { |
||||
padding: 0.5em 0.5em; |
||||
} |
||||
|
||||
/* make images size-up */ |
||||
.xx-pure-img-responsive { |
||||
max-width: 100%; |
||||
height: auto; |
||||
} |
||||
|
||||
/* Add transition to containers so they can push in and out */ |
||||
#layout, #menu, .menu-link { |
||||
-webkit-transition: all 0.2s ease-out; |
||||
-moz-transition: all 0.2s ease-out; |
||||
-ms-transition: all 0.2s ease-out; |
||||
-o-transition: all 0.2s ease-out; |
||||
transition: all 0.2s ease-out; |
||||
} |
||||
|
||||
/* This is the parent `<div>` that contains the menu and the content area */ |
||||
#layout { |
||||
position: relative; |
||||
padding-left: 0; |
||||
} |
||||
#layout.active #menu { |
||||
left: 150px; |
||||
width: 150px; |
||||
} |
||||
|
||||
#layout.active .menu-link { |
||||
left: 150px; |
||||
} |
||||
|
||||
div.tt { |
||||
font-family: monospace; |
||||
font-size: 120%; |
||||
color: #390; |
||||
background-color: #ddd; |
||||
padding: 2px; |
||||
margin: 2px 0; |
||||
line-height: 100%; |
||||
} |
||||
|
||||
/* The content `<div>` */ |
||||
.content { |
||||
margin: 0 auto; |
||||
padding: 0 2em; |
||||
max-width: 800px; |
||||
margin-bottom: 50px; |
||||
line-height: 1.6em; |
||||
} |
||||
|
||||
.header { |
||||
margin: 0; |
||||
color: #333; |
||||
text-align: center; |
||||
padding: 2.5em 2em 0; |
||||
border-bottom: 1px solid #eee; |
||||
background-color: #fc0; |
||||
} |
||||
.header h1 { |
||||
margin: 0.2em 0; |
||||
font-size: 3em; |
||||
font-weight: 300; |
||||
} |
||||
.header h1 .esp { |
||||
font-size: 1.25em; |
||||
} |
||||
.jl { |
||||
font: normal 800 1.5em sans-serif; |
||||
position: relative; |
||||
bottom: 19px; |
||||
color: #9d1414; |
||||
margin-left: 3px; |
||||
} |
||||
|
||||
.content-subhead { |
||||
margin: 50px 0 20px 0; |
||||
font-weight: 300; |
||||
color: #888; |
||||
} |
||||
|
||||
form button { |
||||
margin-top: 0.5em; |
||||
} |
||||
.button-primary { |
||||
background-color: #99f; |
||||
} |
||||
.button-selected { |
||||
background-color: #fc6; |
||||
} |
||||
|
||||
/* Text console */ |
||||
pre.console { |
||||
background-color: #663300; |
||||
-moz-border-radius: 5px; |
||||
-webkit-border-radius: 5px; |
||||
border-radius: 5px; |
||||
border: 0px solid #000000; |
||||
color: #66ff66; |
||||
padding: 5px; |
||||
} |
||||
|
||||
pre.console a { |
||||
color: #66ff66; |
||||
} |
||||
|
||||
/* log page */ |
||||
.dbg-btn, #refresh-button { |
||||
vertical-align: baseline; |
||||
} |
||||
|
||||
.lock-icon { |
||||
background-image: url("/wifi/icons.png"); |
||||
background-color: transparent; |
||||
width: 32px; |
||||
height: 32px; |
||||
display: inline-block; |
||||
} |
||||
|
||||
#menu { |
||||
margin-left: -150px; |
||||
width: 150px; |
||||
position: fixed; |
||||
top: 0; |
||||
left: 0; |
||||
bottom: 0; |
||||
z-index: 1000; |
||||
background: #191818; |
||||
overflow-y: auto; |
||||
-webkit-overflow-scrolling: touch; |
||||
} |
||||
|
||||
#menu a { |
||||
color: #999; |
||||
border: none; |
||||
padding: 0.6em 0 0.6em 0.6em; |
||||
} |
||||
|
||||
#menu .pure-menu, #menu .pure-menu ul { |
||||
border: none; |
||||
background: transparent; |
||||
} |
||||
|
||||
#menu .pure-menu ul, #menu .pure-menu .menu-item-divided { |
||||
border-top: 1px solid #333; |
||||
} |
||||
|
||||
#menu .pure-menu li a:hover, #menu .pure-menu li a:focus { |
||||
background: #333; |
||||
} |
||||
|
||||
#menu .pure-menu-selected, #menu .pure-menu-heading { |
||||
background: #9d1414; |
||||
} |
||||
|
||||
#menu .pure-menu-selected a { |
||||
color: #fff; |
||||
} |
||||
|
||||
#menu .pure-menu-heading { |
||||
font-size: 110%; |
||||
color: #fff; |
||||
margin: 0; |
||||
text-transform: none; |
||||
} |
||||
|
||||
#menu .pure-menu-heading img { |
||||
vertical-align: middle; |
||||
top: -1px; |
||||
position: relative; |
||||
} |
||||
|
||||
#menu .pure-menu-item { |
||||
height:2em; |
||||
} |
||||
|
||||
/* -- Dynamic Button For Responsive Menu -------------------------------------*/ |
||||
|
||||
.menu-link { |
||||
position: fixed; |
||||
display: block; |
||||
top: 0; |
||||
left: 0; |
||||
background: #000; |
||||
background: rgba(0,0,0,0.7); |
||||
font-size: 10px; |
||||
z-index: 10; |
||||
width: 2em; |
||||
height: auto; |
||||
padding: 2.1em 1.6em; |
||||
} |
||||
|
||||
.menu-link:hover, .menu-link:focus { |
||||
background: #000; |
||||
} |
||||
|
||||
.menu-link span { |
||||
position: relative; |
||||
display: block; |
||||
} |
||||
|
||||
.menu-link span, .menu-link span:before, .menu-link span:after { |
||||
background-color: #fff; |
||||
width: 100%; |
||||
height: 0.2em; |
||||
} |
||||
|
||||
.menu-link span:before, .menu-link span:after { |
||||
position: absolute; |
||||
margin-top: -0.6em; |
||||
content: " "; |
||||
} |
||||
|
||||
.menu-link span:after { |
||||
margin-top: 0.6em; |
||||
} |
||||
|
||||
/* -- Responsive Styles (Media Queries) ------------------------------------- */ |
||||
|
||||
@media (min-width: 56em) { |
||||
.header, .content { |
||||
padding-left: 2em; |
||||
padding-right: 2em; |
||||
} |
||||
|
||||
#layout { |
||||
padding-left: 150px; |
||||
left: 0; |
||||
} |
||||
#menu { |
||||
left: 150px; |
||||
} |
||||
|
||||
.menu-link { |
||||
position: fixed; |
||||
left: 150px; |
||||
display: none; |
||||
} |
||||
|
||||
#layout.active .menu-link { |
||||
left: 150px; |
||||
} |
||||
} |
||||
|
||||
@media (max-width: 56em) { |
||||
#layout.active { |
||||
position: relative; |
||||
left: 150px; |
||||
} |
||||
} |
||||
|
||||
/*===== spinners and notification messages */ |
||||
|
||||
#messages { |
||||
position: absolute; |
||||
left: 25%; |
||||
width: 50%; |
||||
top: 10; |
||||
z-index: 200; |
||||
font-size: 110%; |
||||
text-align: center; |
||||
} |
||||
#warning { |
||||
background-color: #933; |
||||
color: #fcc; |
||||
padding: 0.1em 0.4em; |
||||
} |
||||
#notification { |
||||
background-color: #693; |
||||
color: #cfc; |
||||
padding: 0.1em 0.4em; |
||||
} |
||||
|
||||
#spinner { |
||||
position: absolute; |
||||
right: 10%; |
||||
top: 20; |
||||
z-index: 1000; |
||||
} |
||||
.spinner { |
||||
height: 50px; |
||||
width: 50px; |
||||
-webkit-animation: rotation 1s infinite linear; |
||||
-moz-animation: rotation 1s infinite linear; |
||||
-o-animation: rotation 1s infinite linear; |
||||
animation: rotation 1s infinite linear; |
||||
border-left: 10px solid rgba(204, 51, 0, 0.15); |
||||
border-right: 10px solid rgba(204, 51, 0, 0.15); |
||||
border-bottom: 10px solid rgba(204, 51, 0, 0.15); |
||||
border-top: 10px solid rgba(204, 51, 0, 0.8); |
||||
border-radius: 100%; |
||||
} |
||||
.spinner-small { |
||||
display: inline-block; |
||||
height: 1em; |
||||
width: 1em; |
||||
border-width: 4px; |
||||
} |
||||
|
||||
@-webkit-keyframes rotation { |
||||
from { |
||||
-webkit-transform: rotate(0deg); |
||||
} |
||||
to { |
||||
-webkit-transform: rotate(359deg); |
||||
} |
||||
} |
||||
@-moz-keyframes rotation { |
||||
from { |
||||
-moz-transform: rotate(0deg); |
||||
} |
||||
to { |
||||
-moz-transform: rotate(359deg); |
||||
} |
||||
} |
||||
@-o-keyframes rotation { |
||||
from { |
||||
-o-transform: rotate(0deg); |
||||
} |
||||
to { |
||||
-o-transform: rotate(359deg); |
||||
} |
||||
} |
||||
@keyframes rotation { |
||||
from { |
||||
transform: rotate(0deg); |
||||
} |
||||
to { |
||||
transform: rotate(359deg); |
||||
} |
||||
} |
@ -0,0 +1,473 @@ |
||||
//===== Collection of small utilities
|
||||
|
||||
/* |
||||
* Bind/Unbind events |
||||
* |
||||
* Usage: |
||||
* var el = document.getElementyById('#container'); |
||||
* bnd(el, 'click', function() { |
||||
* console.log('clicked'); |
||||
* }); |
||||
*/ |
||||
|
||||
var bnd = function( |
||||
d, // a DOM element
|
||||
e, // an event name such as "click"
|
||||
f // a handler function
|
||||
){ |
||||
d.addEventListener(e, f, false); |
||||
} |
||||
|
||||
/* |
||||
* Create DOM element |
||||
* |
||||
* Usage: |
||||
* var el = m('<h1>Hello</h1>'); |
||||
* document.body.appendChild(el); |
||||
* |
||||
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
|
||||
* More: https://gist.github.com/966233
|
||||
*/ |
||||
|
||||
var m = function( |
||||
a, // an HTML string
|
||||
b, // placeholder
|
||||
c // placeholder
|
||||
){ |
||||
b = document; // get the document,
|
||||
c = b.createElement("p"); // create a container element,
|
||||
c.innerHTML = a; // write the HTML to it, and
|
||||
a = b.createDocumentFragment(); // create a fragment.
|
||||
|
||||
while ( // while
|
||||
b = c.firstChild // the container element has a first child
|
||||
) a.appendChild(b); // append the child to the fragment,
|
||||
|
||||
return a // and then return the fragment.
|
||||
} |
||||
|
||||
/* |
||||
* DOM selector |
||||
* |
||||
* Usage: |
||||
* $('div'); |
||||
* $('#name'); |
||||
* $('.name'); |
||||
* |
||||
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
|
||||
* More: https://gist.github.com/991057
|
||||
*/ |
||||
|
||||
var $ = function( |
||||
a, // take a simple selector like "name", "#name", or ".name", and
|
||||
b // an optional context, and
|
||||
){ |
||||
a = a.match(/^(\W)?(.*)/); // split the selector into name and symbol.
|
||||
return( // return an element or list, from within the scope of
|
||||
b // the passed context
|
||||
|| document // or document,
|
||||
)[ |
||||
"getElement" + ( // obtained by the appropriate method calculated by
|
||||
a[1] |
||||
? a[1] == "#" |
||||
? "ById" // the node by ID,
|
||||
: "sByClassName" // the nodes by class name, or
|
||||
: "sByTagName" // the nodes by tag name,
|
||||
) |
||||
]( |
||||
a[2] // called with the name.
|
||||
) |
||||
} |
||||
|
||||
/* |
||||
* Get cross browser xhr object |
||||
* |
||||
* Copyright (C) 2011 Jed Schmidt <http://jed.is>
|
||||
* More: https://gist.github.com/993585
|
||||
*/ |
||||
|
||||
var j = function( |
||||
a // cursor placeholder
|
||||
){ |
||||
for( // for all a
|
||||
a=0; // from 0
|
||||
a<4; // to 4,
|
||||
a++ // incrementing
|
||||
) try { // try
|
||||
return a // returning
|
||||
? new ActiveXObject( // a new ActiveXObject
|
||||
[ // reflecting
|
||||
, // (elided)
|
||||
"Msxml2", // the various
|
||||
"Msxml3", // working
|
||||
"Microsoft" // options
|
||||
][a] + // for Microsoft implementations, and
|
||||
".XMLHTTP" // the appropriate suffix,
|
||||
) // but make sure to
|
||||
: new XMLHttpRequest // try the w3c standard first, and
|
||||
} |
||||
|
||||
catch(e){} // ignore when it fails.
|
||||
} |
||||
|
||||
// createElement short-hand
|
||||
|
||||
e = function(a) { return document.createElement(a); } |
||||
|
||||
// chain onload handlers
|
||||
|
||||
function onLoad(f) { |
||||
var old = window.onload; |
||||
if (typeof old != 'function') { |
||||
window.onload = f; |
||||
} else { |
||||
window.onload = function() { |
||||
old(); |
||||
f(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
//===== helpers to add/remove/toggle HTML element classes
|
||||
|
||||
function addClass(el, cl) { |
||||
el.className += ' ' + cl; |
||||
} |
||||
function removeClass(el, cl) { |
||||
var cls = el.className.split(/\s+/), |
||||
l = cls.length; |
||||
for (var i=0; i<l; i++) { |
||||
if (cls[i] === cl) cls.splice(i, 1); |
||||
} |
||||
el.className = cls.join(' '); |
||||
return cls.length != l |
||||
} |
||||
function toggleClass(el, cl) { |
||||
if (!removeClass(el, cl)) addClass(el, cl); |
||||
} |
||||
|
||||
//===== AJAX
|
||||
|
||||
function ajaxReq(method, url, ok_cb, err_cb) { |
||||
var xhr = j(); |
||||
xhr.open(method, url, true); |
||||
var timeout = setTimeout(function() { |
||||
xhr.abort(); |
||||
console.log("XHR abort:", method, url); |
||||
xhr.status = 599; |
||||
xhr.responseText = "request time-out"; |
||||
}, 9000); |
||||
xhr.onreadystatechange = function() { |
||||
if (xhr.readyState != 4) { return; } |
||||
clearTimeout(timeout); |
||||
if (xhr.status >= 200 && xhr.status < 300) { |
||||
console.log("XHR done:", method, url, "->", xhr.status); |
||||
ok_cb(xhr.responseText); |
||||
} else { |
||||
console.log("XHR ERR :", method, url, "->", xhr.status, xhr.responseText, xhr); |
||||
err_cb(xhr.status, xhr.responseText); |
||||
} |
||||
} |
||||
console.log("XHR send:", method, url); |
||||
try { |
||||
xhr.send(); |
||||
} catch(err) { |
||||
console.log("XHR EXC :", method, url, "->", err); |
||||
err_cb(599, err); |
||||
} |
||||
} |
||||
|
||||
function dispatchJson(resp, ok_cb, err_cb) { |
||||
var j; |
||||
try { j = JSON.parse(resp); } |
||||
catch(err) { |
||||
console.log("JSON parse error: " + err + ". In: " + resp); |
||||
err_cb(500, "JSON parse error: " + err); |
||||
return; |
||||
} |
||||
ok_cb(j); |
||||
} |
||||
|
||||
function ajaxJson(method, url, ok_cb, err_cb) { |
||||
ajaxReq(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); |
||||
} |
||||
|
||||
function ajaxSpin(method, url, ok_cb, err_cb) { |
||||
$("#spinner").removeAttribute('hidden'); |
||||
ajaxReq(method, url, function(resp) { |
||||
$("#spinner").setAttribute('hidden', ''); |
||||
ok_cb(resp); |
||||
}, function(status, statusText) { |
||||
$("#spinner").setAttribute('hidden', ''); |
||||
//showWarning("Error: " + statusText);
|
||||
err_cb(status, statusText); |
||||
}); |
||||
} |
||||
|
||||
function ajaxJsonSpin(method, url, ok_cb, err_cb) { |
||||
ajaxSpin(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb); |
||||
} |
||||
|
||||
//===== main menu, header spinner and notification boxes
|
||||
|
||||
onLoad(function() { |
||||
var l = $("#layout"); |
||||
var o = l.childNodes[0]; |
||||
// spinner
|
||||
l.insertBefore(m('<div id="spinner" class="spinner" hidden></div>'), o); |
||||
// notification boxes
|
||||
l.insertBefore(m( |
||||
'<div id="messages"><div id="warning" hidden></div><div id="notification" hidden></div></div>'), o); |
||||
// menu hamburger button
|
||||
l.insertBefore(m('<a href="#menu" id="menuLink" class="menu-link"><span></span></a>'), o); |
||||
// menu left-pane
|
||||
var mm = m( |
||||
'<div id="menu">\ |
||||
<div class="pure-menu">\ |
||||
<a class="pure-menu-heading" href="https://github.com/jeelabs/esp-link">\ |
||||
<img src="/favicon.ico" height="32"> esp-link</a>\ |
||||
<ul id="menu-list" class="pure-menu-list"></ul>\ |
||||
</div>\ |
||||
</div>\ |
||||
'); |
||||
l.insertBefore(mm, o); |
||||
|
||||
// make hamburger button pull out menu
|
||||
var ml = $('#menuLink'), mm = $('#menu'); |
||||
bnd(ml, 'click', function (e) { |
||||
console.log("hamburger time"); |
||||
var active = 'active'; |
||||
e.preventDefault(); |
||||
toggleClass(l, active); |
||||
toggleClass(mm, active); |
||||
toggleClass(ml, active); |
||||
}); |
||||
|
||||
// populate menu via ajax call
|
||||
var getMenu = function() { |
||||
ajaxJson("GET", "/menu", function(data) { |
||||
var html = "", path = window.location.pathname; |
||||
for (var i=0; i<data.menu.length; i+=2) { |
||||
var href = data.menu[i+1]; |
||||
html = html.concat(" <li class=\"pure-menu-item" + |
||||
(path === href ? " pure-menu-selected" : "") + "\">" + |
||||
"<a href=\"" + href + "\" class=\"pure-menu-link\">" + |
||||
data.menu[i] + "</a></li>"); |
||||
} |
||||
$("#menu-list").innerHTML = html; |
||||
|
||||
v = $("#version"); |
||||
if (v != null) { v.innerHTML = data.version; } |
||||
}, function() { setTimeout(getMenu, 1000); }); |
||||
}; |
||||
getMenu(); |
||||
}); |
||||
|
||||
//===== Wifi info
|
||||
|
||||
function showWifiInfo(data) { |
||||
Object.keys(data).forEach(function(v) { |
||||
el = $("#wifi-" + v); |
||||
if (el != null) { |
||||
if (el.nodeName === "INPUT") el.value = data[v]; |
||||
else el.innerHTML = data[v]; |
||||
} |
||||
}); |
||||
var dhcp = $('#dhcp-r'+data.dhcp); |
||||
if (dhcp) dhcp.click(); |
||||
$("#wifi-spinner").setAttribute("hidden", ""); |
||||
$("#wifi-table").removeAttribute("hidden"); |
||||
currAp = data.ssid; |
||||
} |
||||
|
||||
function getWifiInfo() { |
||||
ajaxJson('GET', "/wifi/info", showWifiInfo, |
||||
function(s, st) { window.setTimeout(getWifiInfo, 1000); }); |
||||
} |
||||
|
||||
//===== Notifications
|
||||
|
||||
function showWarning(text) { |
||||
var el = $("#warning"); |
||||
el.innerHTML = text; |
||||
el.removeAttribute('hidden'); |
||||
} |
||||
function hideWarning() { |
||||
el = $("#warning").setAttribute('hidden', ''); |
||||
} |
||||
var notifTimeout = null; |
||||
function showNotification(text) { |
||||
var el = $("#notification"); |
||||
el.innerHTML = text; |
||||
el.removeAttribute('hidden'); |
||||
if (notifTimeout != null) clearTimeout(notifTimeout); |
||||
notifTimout = setTimeout(function() { |
||||
el.setAttribute('hidden', ''); |
||||
notifTimout = null; |
||||
}, 4000); |
||||
} |
||||
|
||||
//===== GPIO Pin mux card
|
||||
|
||||
var currPin; |
||||
// pin={reset:12, isp:13, LED_conn:0, LED_ser:2}
|
||||
function createInputForPin(pin) { |
||||
var input = document.createElement("input"); |
||||
input.type = "radio"; |
||||
input.name = "pins"; |
||||
input.data = pin.name; |
||||
input.className = "pin-input"; |
||||
input.value= pin.value; |
||||
input.id = "opt-" + pin.value; |
||||
if (currPin == pin.name) input.checked = "1"; |
||||
|
||||
var descr = m('<label for="opt-'+pin.value+'"><b>'+pin.name+":</b>"+pin.descr+"</label>"); |
||||
var div = document.createElement("div"); |
||||
div.appendChild(input); |
||||
div.appendChild(descr); |
||||
return div; |
||||
} |
||||
|
||||
function displayPins(resp) { |
||||
var po = $("#pin-mux"); |
||||
po.innerHTML = ""; |
||||
currPin = resp.curr; |
||||
resp.map.forEach(function(v) { |
||||
po.appendChild(createInputForPin(v)); |
||||
}); |
||||
var i, inputs = $(".pin-input"); |
||||
for (i=0; i<inputs.length; i++) { |
||||
inputs[i].onclick = function() { setPins(this.value, this.data) }; |
||||
}; |
||||
} |
||||
|
||||
function fetchPins() { |
||||
ajaxJson("GET", "/pins", displayPins, function() { |
||||
window.setTimeout(fetchPins, 1000); |
||||
}); |
||||
} |
||||
|
||||
function setPins(v, name) { |
||||
ajaxSpin("POST", "/pins?map="+v, function() { |
||||
showNotification("Pin assignment changed to " + name); |
||||
}, function() { |
||||
showNotification("Pin assignment change failed"); |
||||
window.setTimeout(fetchPins, 100); |
||||
}); |
||||
} |
||||
|
||||
//===== TCP client card
|
||||
|
||||
function tcpEn(){return document.querySelector('input[name="tcp_enable"]')} |
||||
function rssiEn(){return document.querySelector('input[name="rssi_enable"]')} |
||||
function apiKey(){return document.querySelector('input[name="api_key"]')} |
||||
|
||||
function changeTcpClient(e) { |
||||
e.preventDefault(); |
||||
var url = "tcpclient"; |
||||
url += "?tcp_enable=" + tcpEn().checked; |
||||
url += "&rssi_enable=" + rssiEn().checked; |
||||
url += "&api_key=" + encodeURIComponent(apiKey().value); |
||||
|
||||
hideWarning(); |
||||
var cb = $("#tcp-button"); |
||||
addClass(cb, 'pure-button-disabled'); |
||||
ajaxSpin("POST", url, function(resp) { |
||||
removeClass(cb, 'pure-button-disabled'); |
||||
fetchTcpClient(); |
||||
}, function(s, st) { |
||||
showWarning("Error: "+st); |
||||
removeClass(cb, 'pure-button-disabled'); |
||||
fetchTcpClient(); |
||||
}); |
||||
} |
||||
|
||||
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); |
||||
}); |
||||
} |
||||
|
||||
//===== MQTT cards
|
||||
|
||||
function changeMqtt(e) { |
||||
e.preventDefault(); |
||||
var url = "mqtt?1=1"; |
||||
var i, inputs = document.querySelectorAll('#mqtt-form input'); |
||||
for (i=0; i<inputs.length; i++) { |
||||
if (inputs[i].type != "checkbox") |
||||
url += "&" + inputs[i].name + "=" + inputs[i].value; |
||||
}; |
||||
|
||||
hideWarning(); |
||||
var cb = $("#mqtt-button"); |
||||
addClass(cb, 'pure-button-disabled'); |
||||
ajaxSpin("POST", url, function(resp) { |
||||
showNotification("MQTT updated"); |
||||
removeClass(cb, 'pure-button-disabled'); |
||||
}, function(s, st) { |
||||
showWarning("Error: "+st); |
||||
removeClass(cb, 'pure-button-disabled'); |
||||
window.setTimeout(fetchMqtt, 100); |
||||
}); |
||||
} |
||||
|
||||
function displayMqtt(data) { |
||||
Object.keys(data).forEach(function(v) { |
||||
el = $("#" + v); |
||||
if (el != null) { |
||||
if (el.nodeName === "INPUT") el.value = data[v]; |
||||
else el.innerHTML = data[v]; |
||||
return; |
||||
} |
||||
el = document.querySelector('input[name="' + v + '"]'); |
||||
if (el != null) { |
||||
if (el.type == "checkbox") el.checked = data[v] > 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; i<inputs.length; i++) { |
||||
if (inputs[i].type == "checkbox") |
||||
inputs[i].onclick = function() { console.log(this); setMqtt(this.name, this.checked) }; |
||||
} |
||||
} |
||||
|
||||
function fetchMqtt() { |
||||
ajaxJson("GET", "/mqtt", displayMqtt, function() { |
||||
window.setTimeout(fetchMqtt, 1000); |
||||
}); |
||||
} |
||||
|
||||
function changeMqttStatus(e) { |
||||
e.preventDefault(); |
||||
var v = document.querySelector('input[name="mqtt-status-topic"]').value; |
||||
ajaxSpin("POST", "/mqtt?mqtt-status-topic=" + v, function() { |
||||
showNotification("MQTT status settings updated"); |
||||
}, function(s, st) { |
||||
showWarning("Error: "+st); |
||||
window.setTimeout(fetchMqtt, 100); |
||||
}); |
||||
} |
||||
|
||||
function setMqtt(name, v) { |
||||
ajaxSpin("POST", "/mqtt?" + name + "=" + (v ? 1 : 0), function() { |
||||
var n = name.replace("-enable", ""); |
||||
showNotification(n + " is now " + (v ? "enabled" : "disabled")); |
||||
}, function() { |
||||
showWarning("Enable/disable failed"); |
||||
window.setTimeout(fetchMqtt, 100); |
||||
}); |
||||
} |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@ |
||||
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
|
||||
|
||||
#include <esp8266.h> |
||||
#include "pktbuf.h" |
||||
|
||||
void ICACHE_FLASH_ATTR |
||||
PktBuf_Print(PktBuf *buf) { |
||||
os_printf("PktBuf:"); |
||||
for (int i=-16; i<0; i++) |
||||
os_printf(" %02X", ((uint8_t*)buf)[i]); |
||||
os_printf(" %p", buf); |
||||
for (int i=0; i<16; i++) |
||||
os_printf(" %02X", ((uint8_t*)buf)[i]); |
||||
os_printf("\n"); |
||||
os_printf("PktBuf: next=%p len=0x%04x\n", |
||||
((void**)buf)[-4], ((uint16_t*)buf)[-6]); |
||||
} |
||||
|
||||
|
||||
PktBuf * ICACHE_FLASH_ATTR |
||||
PktBuf_New(uint16_t length) { |
||||
PktBuf *buf = os_zalloc(length+sizeof(PktBuf)); |
||||
buf->next = NULL; |
||||
buf->filled = 0; |
||||
//os_printf("PktBuf_New: %p l=%d->%d d=%p\n",
|
||||
// buf, length, length+sizeof(PktBuf), buf->data);
|
||||
return buf; |
||||
} |
||||
|
||||
PktBuf * ICACHE_FLASH_ATTR |
||||
PktBuf_Push(PktBuf *headBuf, PktBuf *buf) { |
||||
if (headBuf == NULL) { |
||||
//os_printf("PktBuf_Push: %p\n", buf);
|
||||
return buf; |
||||
} |
||||
PktBuf *h = headBuf; |
||||
while (h->next != NULL) h = h->next; |
||||
h->next = buf; |
||||
//os_printf("PktBuf_Push: %p->..->%p\n", headBuf, buf);
|
||||
return headBuf; |
||||
} |
||||
|
||||
PktBuf * ICACHE_FLASH_ATTR |
||||
PktBuf_Unshift(PktBuf *headBuf, PktBuf *buf) { |
||||
buf->next = headBuf; |
||||
//os_printf("PktBuf_Unshift: %p->%p\n", buf, buf->next);
|
||||
return buf; |
||||
} |
||||
|
||||
PktBuf * ICACHE_FLASH_ATTR |
||||
PktBuf_Shift(PktBuf *headBuf) { |
||||
PktBuf *buf = headBuf->next; |
||||
headBuf->next = NULL; |
||||
//os_printf("PktBuf_Shift: (%p)->%p\n", headBuf, buf);
|
||||
return buf; |
||||
} |
||||
|
||||
PktBuf * ICACHE_FLASH_ATTR |
||||
PktBuf_ShiftFree(PktBuf *headBuf) { |
||||
PktBuf *buf = headBuf->next; |
||||
//os_printf("PktBuf_ShiftFree: (%p)->%p\n", headBuf, buf);
|
||||
os_free(headBuf); |
||||
return buf; |
||||
} |
@ -0,0 +1,29 @@ |
||||
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
|
||||
|
||||
#ifndef PKTBUF_H |
||||
#define PKTBUF_H |
||||
|
||||
typedef struct PktBuf { |
||||
struct PktBuf *next; // next buffer in chain
|
||||
uint16_t filled; // number of bytes filled in buffer
|
||||
uint8_t data[0]; // data in buffer
|
||||
} PktBuf; |
||||
|
||||
// Allocate a new packet buffer of given length
|
||||
PktBuf *PktBuf_New(uint16_t length); |
||||
|
||||
// Append a buffer to the end of a packet buffer queue, returns new head
|
||||
PktBuf *PktBuf_Push(PktBuf *headBuf, PktBuf *buf); |
||||
|
||||
// Prepend a buffer to the beginning of a packet buffer queue, return new head
|
||||
PktBuf * PktBuf_Unshift(PktBuf *headBuf, PktBuf *buf); |
||||
|
||||
// Shift first buffer off queue, returns new head (not shifted buffer!)
|
||||
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 |
@ -1,86 +0,0 @@ |
||||
#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; |
||||
} |
@ -1,21 +0,0 @@ |
||||
#ifndef _PROTO_H_ |
||||
#define _PROTO_H_ |
||||
#include <esp8266.h> |
||||
#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 |
@ -1,53 +0,0 @@ |
||||
/* str_queue.c
|
||||
* |
||||
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot 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: |
||||
* |
||||
* * 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; |
||||
} |
@ -1,46 +0,0 @@ |
||||
/* str_queue.h --
|
||||
* |
||||
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot 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: |
||||
* |
||||
* * 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 <esp8266.h> |
||||
#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_ */ |
@ -1,63 +0,0 @@ |
||||
#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; |
||||
} |
@ -1,17 +0,0 @@ |
||||
#ifndef _RING_BUF_H_ |
||||
#define _RING_BUF_H_ |
||||
|
||||
#include <esp8266.h> |
||||
|
||||
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 |
@ -0,0 +1,6 @@ |
||||
#ifndef SLIP_H |
||||
#define SLIP_H |
||||
|
||||
void slip_parse_buf(char *buf, short length); |
||||
|
||||
#endif |
Loading…
Reference in new issue