Merge branch 'reporting'

Former-commit-id: f403fab0c01dd863a3c803eb5573c39a517945ad
master
Ondřej Hruška 8 years ago
commit 758ab2d132
  1. 4
      .gitignore
  2. 3
      _web-build_do.sh
  3. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/aes-cbc.o
  4. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/aes-internal-dec.o
  5. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/aes-internal-enc.o
  6. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/aes-internal.o
  7. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/aes-wrap.o
  8. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/bignum.o
  9. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/crypto_internal-cipher.o
  10. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/crypto_internal-modexp.o
  11. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/crypto_internal-rsa.o
  12. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/crypto_internal.o
  13. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/dh_group5.o
  14. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/dh_groups.o
  15. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/sha256-internal.o
  16. BIN
      esp_iot_sdk_v1.5.2/lib/tmp/sha256.o
  17. 8
      esp_meas.pro
  18. 169
      esphttpclient/httpclient.c
  19. 39
      esphttpclient/httpclient.h
  20. 2
      html/css/app.css
  21. 1
      html/js/.gitignore
  22. 6
      html/js/all.js
  23. 13
      html/pages/about.tpl
  24. 2
      html/pages/fft.html
  25. 102
      html/pages/monitoring.tpl
  26. 2
      html/pages/sgm.html
  27. 4
      html/pages/status.tpl
  28. 2
      html/pages/wfm.html
  29. 2
      html/pages/wifi.tpl
  30. 11
      html_src/_start.php
  31. 2
      html_src/css/app.css
  32. 1
      html_src/css/app.css.map
  33. 3
      html_src/gulpfile.js
  34. 76
      html_src/js-src/page_mon.js
  35. 1
      html_src/js/.gitignore
  36. 6
      html_src/js/all.js
  37. 1
      html_src/js/all.js.map
  38. 1
      html_src/node_modules.tar.REMOVED.git-id
  39. 7
      html_src/package.json
  40. 11
      html_src/page_about.php
  41. 73
      html_src/page_monitoring.php
  42. 2
      html_src/page_status.php
  43. 5
      html_src/sass/layout/_box.scss
  44. 109
      libesphttpd/core/httpd.c
  45. 12
      libesphttpd/core/httpdespfs.c
  46. 2
      libesphttpd/html-minifier-conf.json
  47. 8
      libesphttpd/include/cgiflash.h
  48. 9
      libesphttpd/include/cgiwebsocket.h
  49. 14
      libesphttpd/include/cgiwifi.h
  50. 36
      libesphttpd/include/httpd.h
  51. 6
      libesphttpd/include/httpdespfs.h
  52. 8
      libesphttpd/util/cgiflash.c
  53. 45
      libesphttpd/util/cgiwebsocket.c
  54. 10
      libesphttpd/util/cgiwifi.c
  55. 2
      sbmp
  56. 3
      user/cgi_ping.c
  57. 2
      user/cgi_ping.h
  58. 3
      user/cgi_reset.c
  59. 2
      user/cgi_reset.h
  60. 4
      user/datalink.h
  61. 2
      user/page_about.c
  62. 2
      user/page_about.h
  63. 189
      user/page_monitoring.c
  64. 15
      user/page_monitoring.h
  65. 2
      user/page_status.c
  66. 2
      user/page_status.h
  67. 8
      user/page_waveform.c
  68. 4
      user/page_waveform.h
  69. 243
      user/reporting.c
  70. 52
      user/reporting.h
  71. 7
      user/routes.c
  72. 7
      user/user_main.c

4
.gitignore vendored

@ -18,3 +18,7 @@ build/
.idea
*.pro.user
*.o
*.d
*.map

@ -26,6 +26,7 @@ mkdir -p "$BLDDIR/pages"
php "$SRCDIR/page_status.php" > "$BLDDIR/pages/status.tpl"
php "$SRCDIR/page_about.php" > "$BLDDIR/pages/about.tpl"
php "$SRCDIR/page_wifi.php" > "$BLDDIR/pages/wifi.tpl"
php "$SRCDIR/page_monitoring.php" > "$BLDDIR/pages/monitoring.tpl"
php "$SRCDIR/page_waveform.php" > "$BLDDIR/pages/wfm.html" # no substitutions, .html allows to gzip it.
php "$SRCDIR/page_fft.php" > "$BLDDIR/pages/fft.html" # same
php "$SRCDIR/page_spectrogram.php" > "$BLDDIR/pages/sgm.html" # same
php "$SRCDIR/page_spectrogram.php" > "$BLDDIR/pages/sgm.html" # same

@ -66,7 +66,9 @@ SOURCES += \
user/cgi_reset.c \
user/cgi_ping.c \
esphttpclient/test/httpclient_test.c \
esphttpclient/httpclient.c
esphttpclient/httpclient.c \
user/page_monitoring.c \
user/reporting.c
HEADERS += \
include/uart_hw.h \
@ -154,7 +156,9 @@ HEADERS += \
user/cgi_reset.h \
user/cgi_ping.h \
esphttpclient/espmissingincludes.h \
esphttpclient/httpclient.h
esphttpclient/httpclient.h \
user/page_monitoring.h \
user/reporting.h
DISTFILES += \
style.astylerc \

@ -10,6 +10,7 @@
// FIXME: sprintf->snprintf everywhere.
#include <esp8266.h>
#include <httpd.h>
#include "httpclient.h"
#include <limits.h>
@ -27,6 +28,7 @@ typedef struct {
httpclient_cb user_callback;
int timeout;
os_timer_t timeout_timer;
http_method method;
} request_args;
static char *FLASH_FN esp_strdup(const char *str)
@ -241,20 +243,24 @@ static void FLASH_FN sent_callback(void * arg)
static void FLASH_FN connect_callback(void * arg)
{
info("Connected!");
struct espconn * conn = (struct espconn *)arg;
request_args * req = (request_args *)conn->reserve;
struct espconn *conn = (struct espconn *)arg;
request_args *req = (request_args *)conn->reserve;
espconn_regist_recvcb(conn, receive_callback);
espconn_regist_sentcb(conn, sent_callback);
const char *method = "GET";
char post_headers[32] = "";
if (req->post_data != NULL) { // If there is data this is a POST request.
method = "POST";
sprintf(post_headers, "Content-Length: %d\r\n", strlen(req->post_data));
if (req->method == HTTP_GET) {
req->method = HTTP_POST;
}
}
const char* method = http_method_str(req->method);
if (req->headers == NULL) { /* Avoid NULL pointer, it may cause exception */
req->headers = (char *)malloc(sizeof(char));
req->headers[0] = '\0';
@ -438,68 +444,30 @@ static void FLASH_FN dns_callback(const char *hostname, ip_addr_t *addr, void *a
}
}
void FLASH_FN http_raw_request(const char *hostname, int port, bool secure, const char *path, const char *post_data, const char *headers, httpclient_cb user_callback)
bool FLASH_FN http_request(
const char *url,
http_method method,
const char *body,
const char *headers,
httpclient_cb user_callback)
{
info("HTTP request: %s:%d%s", hostname, port, path);
request_args * req = (request_args *)malloc(sizeof(request_args));
req->hostname = esp_strdup(hostname);
req->path = esp_strdup(path);
req->port = port;
req->secure = secure;
req->headers = esp_strdup(headers);
req->post_data = esp_strdup(post_data);
req->buffer_size = 1;
req->buffer = (char *)malloc(1);
req->buffer[0] = '\0'; // Empty string.
req->user_callback = user_callback;
req->timeout = HTTP_REQUEST_TIMEOUT_MS;
ip_addr_t addr;
err_t error = espconn_gethostbyname((struct espconn *)req, // It seems we don't need a real espconn pointer here.
hostname, &addr, dns_callback);
if (error == ESPCONN_INPROGRESS) {
dbg("DNS pending");
} else if (error == ESPCONN_OK) {
// Already in the local names table (or hostname was an IP address), execute the callback ourselves.
dns_callback(hostname, &addr, req);
} else {
if (error == ESPCONN_ARG) {
error("DNS arg error %s", hostname);
} else {
error("DNS error code %d", error);
}
dns_callback(hostname, NULL, req); // Handle all DNS errors the same way.
}
}
// --- prepare port, secure... ---
/*
* Parse an URL of the form http://host:port/path
* <host> can be a hostname or an IP address
* <port> is optional
*/
void FLASH_FN http_post(const char *url, const char *post_data, const char *headers, httpclient_cb user_callback)
{
// FIXME: handle HTTP auth with http://user:pass@host/
// FIXME: get rid of the #anchor part if present.
char hostname[128] = "";
int port = 80;
bool secure = false;
bool is_http = strstarts(url, "http://");
bool is_https = strstarts(url, "https://");
if (is_http)
if (strstarts(url, "http://"))
url += strlen("http://"); // Get rid of the protocol.
else if (is_https) {
else if (strstarts(url, "https://")) {
port = 443;
secure = true;
url += strlen("https://"); // Get rid of the protocol.
} else {
error("URL is not HTTP or HTTPS %s", url);
return;
error("Invalid URL protocol: %s", url);
return false;
}
char *path = strchr(url, '/');
@ -513,38 +481,111 @@ void FLASH_FN http_post(const char *url, const char *post_data, const char *head
}
if (colon == NULL) { // The port is not present.
memcpy(hostname, url, path - url);
memcpy(hostname, url, (size_t)(path - url));
hostname[path - url] = '\0';
} else {
port = atoi(colon + 1);
if (port == 0) {
error("Port error %s\n", url);
return;
return false;
}
memcpy(hostname, url, colon - url);
memcpy(hostname, url, (size_t)(colon - url));
hostname[colon - url] = '\0';
}
if (path[0] == '\0') { // Empty path is not allowed.
path = "/";
}
http_raw_request(hostname, port, secure, path, post_data, headers, user_callback);
// ---
info("HTTP request: %s:%d%s", hostname, port, path);
request_args * req = (request_args *)malloc(sizeof(request_args));
req->hostname = esp_strdup(hostname);
req->path = esp_strdup(path);
// remove #anchor
char *hash = strchr(req->path, '#');
if (hash != NULL) *hash = '\0'; // remove the hash part
req->port = port;
req->secure = secure;
req->headers = esp_strdup(headers);
req->post_data = esp_strdup(body);
req->buffer_size = 1;
req->buffer = (char *)malloc(1);
req->buffer[0] = '\0'; // Empty string.
req->user_callback = user_callback;
req->timeout = HTTP_REQUEST_TIMEOUT_MS;
req->method = method;
ip_addr_t addr;
err_t error = espconn_gethostbyname((struct espconn *)req, // It seems we don't need a real espconn pointer here.
hostname, &addr, dns_callback);
if (error == ESPCONN_INPROGRESS) {
dbg("DNS pending");
} else if (error == ESPCONN_OK) {
// Already in the local names table (or hostname was an IP address), execute the callback ourselves.
dns_callback(hostname, &addr, req);
} else {
if (error == ESPCONN_ARG) {
error("DNS arg error %s", hostname);
} else {
error("DNS error code %d", error);
}
dns_callback(hostname, NULL, req); // Handle all DNS errors the same way.
}
return true;
}
bool FLASH_FN http_post(const char *url, const char *body, const char *headers, httpclient_cb user_callback)
{
return http_request(url, HTTP_POST, body, headers, user_callback);
}
bool http_get(const char *url, const char *headers, httpclient_cb user_callback)
{
return http_request(url, HTTP_GET, NULL, headers, user_callback);
}
void FLASH_FN http_get(const char *url, const char *headers, httpclient_cb user_callback)
bool http_put(const char *url, const char *body, const char *headers, httpclient_cb user_callback)
{
http_post(url, NULL, headers, user_callback);
return http_request(url, HTTP_PUT, body, headers, user_callback);
}
void FLASH_FN http_callback_example(char *response_body, int http_status, char *response_headers, int body_size)
{
dbg("http_status=%d", http_status);
dbg("Response: code %d", http_status);
if (http_status != HTTP_STATUS_GENERIC_ERROR) {
dbg("strlen(headers)=%d", (int)strlen(response_headers));
dbg("body_size=%d", body_size);
dbg("body=%s<EOF>", response_body); // FIXME: this does not handle binary data.
dbg("len(headers) = %d, len(body) = %d", (int)strlen(response_headers), body_size);
dbg("body: %s<EOF>", response_body); // FIXME: this does not handle binary data.
}
}
void FLASH_FN http_callback_showstatus(char *response_body, int code, char *response_headers, int body_size)
{
(void)response_body;
(void)response_headers;
(void)body_size;
if (code == 200) {
info("Response OK (200)");
} else if (code >= 400) {
error("Response ERROR (%d)", code);
dbg("Body: %s<EOF>",response_body);
} else {
// ???
warn("Response %d", code);
dbg("Body: %s<EOF>",response_body);
}
}

@ -11,6 +11,7 @@
#define HTTPCLIENT_H
#include <esp8266.h>
#include <httpd.h>
#define HTTP_STATUS_GENERIC_ERROR -1 // In case of TCP or DNS error the callback is called with this status.
#define BUFFER_SIZE_MAX 5000 // Size of http responses that will cause an error.
@ -27,38 +28,48 @@
* A successful request corresponds to an HTTP status code of 200 (OK).
* More info at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
*/
typedef void (* httpclient_cb)(char * response_body, int http_status, char * response_headers, int body_size);
typedef void (* httpclient_cb)(char *response_body, int http_status, char *response_headers, int body_size);
/**
* Download a web page from its URL.
* @brief Download a web page from its URL.
*
* Try:
* http_get("http://wtfismyip.com/text", http_callback_example);
*/
void http_get(const char * url, const char *headers, httpclient_cb user_callback);
bool http_get(const char * url, const char *headers, httpclient_cb user_callback);
/**
* Post data to a web form.
* @brief Post data to a web form.
*
* The data should be encoded as application/x-www-form-urlencoded.
*
* Try:
* http_post("http://httpbin.org/post", "first_word=hello&second_word=world", http_callback_example);
*/
void http_post(const char *url, const char *post_data, const char *headers, httpclient_cb user_callback);
bool http_post(const char *url, const char *post_data, const char *headers, httpclient_cb user_callback);
/** Like POST, but with the PUT method. */
bool http_put(const char *url, const char *body, const char *headers, httpclient_cb user_callback);
/**
* Call this function to skip URL parsing if the arguments are already in separate variables.
* @brief Send a HTTP request
* @param url : protocol://host[:port][/path]
* @param method : get, post, ...
* @param body : request body. If GET & body != NULL, method changes to POST.
* @param headers : additional headers string. Must end with \r\n
* @param user_callback : callback for parsing the response
* @return success (in sending)
*/
void http_raw_request(
const char * hostname,
int port,
bool secure,
const char *path,
const char *post_data,
const char *headers,
httpclient_cb user_callback);
bool http_request(const char *url, http_method method, const char *body, const char *headers, httpclient_cb user_callback);
/**
* Output on the UART.
*/
void http_callback_example(char *response_body, int http_status, char *response_headers, int body_size);
/**
* Show status code, and body on error. Error/warn log msg on error.
*/
void http_callback_showstatus(char *response_body, int code, char *response_headers, int body_size);
#endif

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
*.map

File diff suppressed because one or more lines are too long

@ -19,7 +19,7 @@
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/about" class="selected">About</a></nav>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about" class="selected">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
@ -39,11 +39,6 @@
<div class="Box">
<h2>Firmware</h2>
<p>
The ESP8266 firmware is based on the amazing <a href="https://github.com/Spritetm/esphttpd" target="blank">esp-httpd</a>
library by Jeroen Domburg.
</p>
<table>
<tr>
<th>Firmware</th>
@ -61,7 +56,13 @@
<th>IoT SDK</th>
<td>v%vers_sdk%</td>
</tr>
<!-- Read & show version of the stm32 firmware -->
</table>
<p>
The webserver is built using the great <a href="https://github.com/Spritetm/esphttpd" target="blank">esp-httpd</a>
library by Jeroen Domburg.
</p>
</div>

@ -19,7 +19,7 @@
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft" class="selected">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/about">About</a></nav>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft" class="selected">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">

@ -0,0 +1,102 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Monitoring - Current Analyser</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
<script>
// server root (or URL) - used for local development with remote AJAX calls
// (this needs CORS working on the target - which I added to esp-httpd)
var _root = "";
</script>
</head>
<body class="page-monitoring">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring" class="selected">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1>Monitoring &amp; Reporting</h1>
<div class="Box">
<h2>Status</h2>
<table>
<tr>
<th>Reference:</th>
<td>
<span id="hasref" class="Valfield">%refStored%</span>
<a onclick="page_mon.captureRef()" class="button btn-green">Capture</a>
</td>
</tr>
<tr>
<th>Status:</th>
<td>
<span class="Valfield" style="vertical-align:middle;">
Δ = <span id="actual-dev">%curDeviation%</span><br>
I<sub>RMS</sub> = <span id="actual-rms">%curRMS%</span>&nbsp;mA
</span>
<a onclick="page_mon.compareNow()" class="button btn-blue">Measure</a>
</td>
</tr>
</table>
</div>
<div class="Box">
<h2>Reporting</h2>
<form action="/mon/config" method="POST">
<table>
<tr>
<th><label for="rep-on">Reporting:</label></th>
<td>
<input type="checkbox" id="rep-on" name="enabled" value="1" %rep_en%><!--
-->&nbsp;<label for="rep-on">enabled</label>
</td>
</tr>
<tr>
<th><label for="rep-interval">Interval:</label></th>
<td>
<input type="number" name="interval" id="rep-interval" style="max-width: 10em" value="%repInterval%"><!--
-->&nbsp;seconds
</td>
</tr>
<tr>
<th>Service:</th>
<td>
<input type="radio" name="service" value="xv" id="rep-svc-xv" %svc_xv%>&nbsp;<label for="rep-svc-xv">Xively</label>&nbsp;
<input type="radio" name="service" value="ts" id="rep-svc-ts" %svc_ts%>&nbsp;<label for="rep-svc-ts">ThingSpeak</label>
</td>
</tr>
<tr>
<th><label for="rep-feed">Feed/Channel:</label></th>
<td><input type="text" name="feed" id="rep-feed" value="%repFeed%"></td>
</tr>
<tr>
<th><label for="rep-key">API key:</label></th>
<td><input type="text" name="key" id="rep-key" value="%repKey%"></td>
</tr>
<tr>
<th>&nbsp;</th>
<td><input type="submit" value="Save changes"></td>
</tr>
</table>
</form>
</div>
<script>
$().ready(page_mon.init);
</script>
<div class="ErrMsg hidden" id="notif"></div>
</div><!-- content -->
</div><!-- outer -->
</body>
</html>

@ -19,7 +19,7 @@
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram" class="selected">Spectrogram</a><a href="/about">About</a></nav>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram" class="selected">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">

@ -19,7 +19,7 @@
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status" class="selected">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/about">About</a></nav>
<a href="/status" class="selected">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
@ -38,7 +38,7 @@
</tr>
<tr>
<th></th>
<td><a onclick="page_status.trigReset()" class="button btn-red">SW reset</a></td>
<td><a onclick="page_status.trigReset()" class="button btn-red">Restart system</a></td>
</tr>
</table>
</div>

@ -19,7 +19,7 @@
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform" class="selected">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/about">About</a></nav>
<a href="/status">Home</a><a href="/wifi">WiFi config</a><a href="/waveform" class="selected">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">

@ -19,7 +19,7 @@
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/status">Home</a><a href="/wifi" class="selected">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/about">About</a></nav>
<a href="/status">Home</a><a href="/wifi" class="selected">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/spectrogram">Spectrogram</a><a href="/monitoring">Monitoring</a><a href="/about">About</a></nav>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">

@ -4,11 +4,12 @@
$root = $prod ? '' : 'http://192.168.1.13';
$menu = [
'home' => [ $prod ? '/status' : '/page_status.php', 'Home' ],
'wifi' => [ $prod ? '/wifi' : '/page_wifi.php', 'WiFi config' ],
'waveform' => [ $prod ? '/waveform' : '/page_waveform.php', 'Waveform' ],
'fft' => [ $prod ? '/fft' : '/page_fft.php', 'FFT' ],
'spectrogram' => [ $prod ? '/spectrogram' : '/page_spectrogram.php', 'Spectrogram' ],
'home' => [ $prod ? '/status' : '/page_status.php', 'Home' ],
'wifi' => [ $prod ? '/wifi' : '/page_wifi.php', 'WiFi config' ],
'waveform' => [ $prod ? '/waveform' : '/page_waveform.php', 'Waveform' ],
'fft' => [ $prod ? '/fft' : '/page_fft.php', 'FFT' ],
'spectrogram' => [ $prod ? '/spectrogram' : '/page_spectrogram.php','Spectrogram' ],
'monitoring' => [ $prod ? '/monitoring' : '/page_monitoring.php', 'Monitoring' ],
// 'transient' => [ '/transient', 'Power-on transient' ],
'about' => [ $prod ? '/about' : '/page_about.php', 'About' ],
];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -5,7 +5,7 @@ elixir.config.css.autoprefix.options.browsers = ["> 2.5% in CZ"];
elixir.config.css.autoprefix.options.flexbox = "no-2009";
elixir.config.css.cssnano.pluginOptions.discardComments = {removeAll: true};
elixir.config.css.cssnano.pluginOptions.colormin = false;
elixir.config.js.uglify.options.compress.drop_console = false;
//elixir.config.js.uglify.options.compress.drop_console = false;
// var info = autoprefixer(elixir.config.css.autoprefix).info();
// console.log(info);
@ -33,5 +33,6 @@ elixir(function (mix) {
'js-src/page_waveform.js',
'js-src/page_spectrogram.js',
'js-src/page_status.js',
'js-src/page_mon.js',
], 'js/all.js');
});

@ -0,0 +1,76 @@
var page_mon = (function() {
var mon = {};
function updRefInfoField(ok) {
$('#hasref').html(ok ? 'OK' : 'Not set!');
}
/** Capture reference & save to flash */
mon.captureRef = function() {
$().get(_root + '/mon/setref', function(resp, status) {
if (status != 200) {
// bad response
errorMsg('Operation failed.');
} else {
try {
// OK
var j = JSON.parse(resp);
updRefInfoField(j.success);
} catch(e) {
errorMsg(e);
updRefInfoField(false);
}
}
});
};
/** Capture waveform and compare with reference */
mon.compareNow = function() {
$().get(_root + '/mon/compare', function(resp, status) {
if (status != 200) {
// bad response
errorMsg('Operation failed.');
} else {
try {
// OK
var j = JSON.parse(resp);
if (j.success) {
$('#actual-dev').html(numfmt(j.deviation, 2));
$('#actual-rms').html(numfmt(j.rms, 2));
} else {
throw 'Capture failed.';
}
} catch(e) {
errorMsg(e);
$('#actual-dev').html('--');
$('#actual-rms').html('--');
}
}
});
};
mon.init = function() {
setInterval(function() {
$().get(_root + '/mon/status', function(resp, status) {
if (status == 200) {
try {
// OK
var j = JSON.parse(resp);
if (j.success) {
$('#actual-dev').html(numfmt(j.deviation, 2));
$('#actual-rms').html(numfmt(j.rms, 2));
} else {
throw 'Capture failed.';
}
} catch(e) {
errorMsg(e);
$('#actual-dev').html('--');
$('#actual-rms').html('--');
}
}
});
}, 10000);
};
return mon;
})();

@ -0,0 +1 @@
*.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
0648559aed5a9ec1d478b445839bdb807ced7fc5

@ -0,0 +1,7 @@
{
"private": true,
"dependencies": {
"laravel-elixir": "^4.0.0",
"gulp": "^3.8.8"
}
}

@ -16,11 +16,6 @@
<div class="Box">
<h2>Firmware</h2>
<p>
The ESP8266 firmware is based on the amazing <a href="https://github.com/Spritetm/esphttpd" target="blank">esp-httpd</a>
library by Jeroen Domburg.
</p>
<table>
<tr>
<th>Firmware</th>
@ -38,7 +33,13 @@
<th>IoT SDK</th>
<td>v%vers_sdk%</td>
</tr>
<!-- Read & show version of the stm32 firmware -->
</table>
<p>
The webserver is built using the great <a href="https://github.com/Spritetm/esphttpd" target="blank">esp-httpd</a>
library by Jeroen Domburg.
</p>
</div>
<?php include "_end.php"; ?>

@ -0,0 +1,73 @@
<?php $page = 'monitoring'; include "_start.php"; ?>
<h1>Monitoring &amp; Reporting</h1>
<div class="Box">
<h2>Status</h2>
<table>
<tr>
<th>Reference:</th>
<td>
<span id="hasref" class="Valfield">%refStored%</span>
<a onclick="page_mon.captureRef()" class="button btn-green">Capture</a>
</td>
</tr>
<tr>
<th>Status:</th>
<td>
<span class="Valfield" style="vertical-align:middle;">
Δ = <span id="actual-dev">%curDeviation%</span><br>
I<sub>RMS</sub> = <span id="actual-rms">%curRMS%</span>&nbsp;mA
</span>
<a onclick="page_mon.compareNow()" class="button btn-blue">Measure</a>
</td>
</tr>
</table>
</div>
<div class="Box">
<h2>Reporting</h2>
<form action="<?=$root?>/mon/config" method="POST">
<table>
<tr>
<th><label for="rep-on">Reporting:</label></th>
<td>
<input type="checkbox" id="rep-on" name="enabled" value="1" %rep_en%><!--
-->&nbsp;<label for="rep-on">enabled</label>
</td>
</tr>
<tr>
<th><label for="rep-interval">Interval:</label></th>
<td>
<input type="number" name="interval" id="rep-interval" style="max-width: 10em" value="%repInterval%"><!--
-->&nbsp;seconds
</td>
</tr>
<tr>
<th>Service:</th>
<td>
<input type="radio" name="service" value="xv" id="rep-svc-xv" %svc_xv%>&nbsp;<label for="rep-svc-xv">Xively</label>&nbsp;
<input type="radio" name="service" value="ts" id="rep-svc-ts" %svc_ts%>&nbsp;<label for="rep-svc-ts">ThingSpeak</label>
</td>
</tr>
<tr>
<th><label for="rep-feed">Feed/Channel:</label></th>
<td><input type="text" name="feed" id="rep-feed" value="%repFeed%"></td>
</tr>
<tr>
<th><label for="rep-key">API key:</label></th>
<td><input type="text" name="key" id="rep-key" value="%repKey%"></td>
</tr>
<tr>
<th>&nbsp;</th>
<td><input type="submit" value="Save changes"></td>
</tr>
</table>
</form>
</div>
<script>
$().ready(page_mon.init);
</script>
<?php include "_end.php"; ?>

@ -15,7 +15,7 @@
</tr>
<tr>
<th></th>
<td><a onclick="page_status.trigReset()" class="button btn-red">SW reset</a></td>
<td><a onclick="page_status.trigReset()" class="button btn-red">Restart system</a></td>
</tr>
</table>
</div>

@ -28,4 +28,9 @@
&.medium {
max-width: 1200px;
}
.Valfield {
display: inline-block;
min-width: 10em;
}
}

@ -62,7 +62,7 @@ struct HttpdPriv {
//Connection pool
static HttpdConnData *connData[HTTPD_MAX_CONNECTIONS];
static HttpdConnData *connPool[HTTPD_MAX_CONNECTIONS];
//Struct to keep extension->mime data in
typedef struct {
@ -115,13 +115,27 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(const char *filepath) {
return mimeTypes[i].mimetype;
}
const char* ICACHE_FLASH_ATTR http_method_str(http_method m)
{
switch (m) {
default:
case HTTP_GET: return "GET";
case HTTP_POST: return "POST";
case HTTP_OPTIONS: return "OPTIONS";
case HTTP_PUT: return "PUT";
case HTTP_DELETE: return "DELETE";
case HTTP_PATCH: return "PATCH";
case HTTP_HEAD: return "HEAD";
}
}
//Looks up the connData info for a specific connection
static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(ConnTypePtr conn, const char *remIp, int remPort) {
for (int i=0; i<HTTPD_MAX_CONNECTIONS; i++) {
if (connData[i] && connData[i]->remote_port == remPort &&
memcmp(connData[i]->remote_ip, remIp, 4) == 0) {
connData[i]->conn=conn;
return connData[i];
if (connPool[i] && connPool[i]->remote_port == remPort &&
memcmp(connPool[i]->remote_ip, remIp, 4) == 0) {
connPool[i]->conn=conn;
return connPool[i];
}
}
//Shouldn't happen.
@ -146,7 +160,7 @@ static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
if (conn->priv!=NULL) free(conn->priv);
if (conn) free(conn);
for (int i=0; i<HTTPD_MAX_CONNECTIONS; i++) {
if (connData[i]==conn) connData[i]=NULL;
if (connPool[i]==conn) connPool[i]=NULL;
}
}
@ -207,7 +221,7 @@ int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLe
p=(char*)strstr(p, "&");
if (p!=NULL) p+=1;
}
warn("Finding %s in %s: Not found :/", arg, line);
// warn("Finding %s in %s: Not found :/", arg, line);
return -1; //not found
}
@ -285,7 +299,7 @@ void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl) {
}
//Use this as a cgi function to redirect one url to another.
int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) {
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
@ -295,7 +309,7 @@ int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) {
}
//Used to spit out a 404 error
static int ICACHE_FLASH_ATTR cgiNotFound(HttpdConnData *connData) {
static httpd_cgi_state ICACHE_FLASH_ATTR cgiNotFound(HttpdConnData *connData) {
if (connData->conn==NULL) return HTTPD_CGI_DONE;
httpdStartResponse(connData, 404);
httpdEndHeaders(connData);
@ -308,7 +322,7 @@ static int ICACHE_FLASH_ATTR cgiNotFound(HttpdConnData *connData) {
//ESP in order to load a HTML page as soon as a phone, tablet etc connects to the ESP. Watch out:
//this will also redirect connections when the ESP is in STA mode, potentially to a hostname that is not
//in the 'official' DNS and so will fail.
int ICACHE_FLASH_ATTR cgiRedirectToHostname(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiRedirectToHostname(HttpdConnData *connData) {
static const char hostFmt[]="http://%s/";
char *buff;
int isIP=0;
@ -345,7 +359,7 @@ int ICACHE_FLASH_ATTR cgiRedirectToHostname(HttpdConnData *connData) {
//Same as above, but will only redirect clients with an IP that is in the range of
//the SoftAP interface. This should preclude clients connected to the STA interface
//to be redirected to nowhere.
int ICACHE_FLASH_ATTR cgiRedirectApClientToHostname(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiRedirectApClientToHostname(HttpdConnData *connData) {
#ifndef FREERTOS
uint32 *remadr;
struct ip_info apip;
@ -521,7 +535,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
return; //Shouldn't happen
}
if (conn->requestType == HTTPD_METHOD_OPTIONS /*&& conn->priv->chunkHdr[0] != 0*/) {
if (conn->requestType == HTTP_OPTIONS /*&& conn->priv->chunkHdr[0] != 0*/) {
// we have a CORS preflight
httpdStartResponse(conn, 200);
httpdHeader(conn, "Access-Control-Allow-Headers", conn->priv->corsToken);
@ -588,19 +602,22 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
int i;
char firstLine=0;
if (strncmp(h, "GET ", 4)==0) {
conn->requestType = HTTPD_METHOD_GET;
if (strstarts(h, "GET ")) {
conn->requestType = HTTP_GET;
firstLine=1;
} else if (strstarts(h, "POST ")) {
conn->requestType = HTTP_POST;
firstLine=1;
} else if (strncmp(h, "Host:", 5)==0) {
} else if (strstarts(h, "PUT ")) {
conn->requestType = HTTP_PUT;
firstLine=1;
} else if (strstarts(h, "OPTIONS ")) {
conn->requestType = HTTP_OPTIONS;
firstLine=1;
} else if (strstarts(h, "Host:")) {
i=5;
while (h[i]==' ') i++;
conn->hostName=&h[i];
} else if (strncmp(h, "POST ", 5)==0) {
conn->requestType = HTTPD_METHOD_POST;
firstLine=1;
} else if (strncmp(h, "OPTIONS ", 8)==0) {
conn->requestType = HTTPD_METHOD_OPTIONS;
firstLine=1;
}
if (firstLine) {
@ -630,12 +647,12 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
} else {
conn->getArgs=NULL;
}
} else if (strncmp(h, "Connection:", 11)==0) {
} else if (strstarts(h, "Connection:")) {
i=11;
//Skip trailing spaces
while (h[i]==' ') i++;
if (strncmp(&h[i], "close", 5)==0) conn->priv->flags&=~HFL_CHUNKED; //Don't use chunked conn
} else if (strncmp(h, "Content-Length:", 15)==0) {
if (strstarts(&h[i], "close")) conn->priv->flags&=~HFL_CHUNKED; //Don't use chunked conn
} else if (strstarts(h, "Content-Length:")) {
i=15;
//Skip trailing spaces
while (h[i]==' ') i++;
@ -652,7 +669,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
dbg("Malloc'd buffer for %d + 1 bytes of post data.", conn->post->buffSize);
conn->post->buff=(char*)malloc(conn->post->buffSize + 1);
conn->post->buffLen=0;
} else if (strncmp(h, "Content-Type: ", 14)==0) {
} else if (strstarts(h, "Content-Type: ")) {
if (strstr(h, "multipart/form-data")) {
// It's multipart form data so let's pull out the boundary for future use
char *b;
@ -663,7 +680,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
dbg("boundary = %s", conn->post->multipartBoundary);
}
}
} else if (strncmp(h, "Access-Control-Request-Headers: ", 32)==0) {
} else if (strstarts(h, "Access-Control-Request-Headers: ")) {
// CORS crap that needs to be repeated in the response
info("CORS preflight request.");
@ -783,7 +800,7 @@ int ICACHE_FLASH_ATTR httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort)
int i;
//Find empty conndata in pool
for (i=0; i<HTTPD_MAX_CONNECTIONS; i++) if (connData[i]==NULL) break;
for (i=0; i<HTTPD_MAX_CONNECTIONS; i++) if (connPool[i]==NULL) break;
info("Conn req from %d.%d.%d.%d:%d, using pool slot %d", remIp[0]&0xff, remIp[1]&0xff, remIp[2]&0xff, remIp[3]&0xff, remPort, i);
if (i==HTTPD_MAX_CONNECTIONS) {
error("Aiee, conn pool overflow!");
@ -799,24 +816,24 @@ int ICACHE_FLASH_ATTR httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort)
failed_cnt = 0;
connData[i]=malloc(sizeof(HttpdConnData));
memset(connData[i], 0, sizeof(HttpdConnData));
connData[i]->priv=malloc(sizeof(HttpdPriv));
memset(connData[i]->priv, 0, sizeof(HttpdPriv));
connData[i]->conn=conn;
connData[i]->slot=i;
connData[i]->priv->headPos=0;
connData[i]->post=malloc(sizeof(HttpdPostData));
memset(connData[i]->post, 0, sizeof(HttpdPostData));
connData[i]->post->buff=NULL;
connData[i]->post->buffLen=0;
connData[i]->post->received=0;
connData[i]->post->len=-1;
connData[i]->hostName=NULL;
connData[i]->remote_port=remPort;
connData[i]->priv->sendBacklog=NULL;
connData[i]->priv->sendBacklogSize=0;
memcpy(connData[i]->remote_ip, remIp, 4);
connPool[i]=malloc(sizeof(HttpdConnData));
memset(connPool[i], 0, sizeof(HttpdConnData));
connPool[i]->priv=malloc(sizeof(HttpdPriv));
memset(connPool[i]->priv, 0, sizeof(HttpdPriv));
connPool[i]->conn=conn;
connPool[i]->slot=i;
connPool[i]->priv->headPos=0;
connPool[i]->post=malloc(sizeof(HttpdPostData));
memset(connPool[i]->post, 0, sizeof(HttpdPostData));
connPool[i]->post->buff=NULL;
connPool[i]->post->buffLen=0;
connPool[i]->post->received=0;
connPool[i]->post->len=-1;
connPool[i]->hostName=NULL;
connPool[i]->remote_port=remPort;
connPool[i]->priv->sendBacklog=NULL;
connPool[i]->priv->sendBacklogSize=0;
memcpy(connPool[i]->remote_ip, remIp, 4);
return 1;
}
@ -826,7 +843,7 @@ void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) {
int i;
for (i=0; i<HTTPD_MAX_CONNECTIONS; i++) {
connData[i]=NULL;
connPool[i]=NULL;
}
builtInUrls=fixedUrls;

@ -73,7 +73,7 @@ EspFsFile *tryOpenIndex(const char *path)
}
int ICACHE_FLASH_ATTR serveStaticFile(HttpdConnData *connData, const char* filepath)
httpd_cgi_state ICACHE_FLASH_ATTR serveStaticFile(HttpdConnData *connData, const char* filepath)
{
EspFsFile *file = connData->cgiData;
int len;
@ -153,13 +153,13 @@ int ICACHE_FLASH_ATTR serveStaticFile(HttpdConnData *connData, const char* filep
//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)
httpd_cgi_state cgiEspFsHook(HttpdConnData *connData)
{
return serveStaticFile(connData, connData->url);
}
int ICACHE_FLASH_ATTR cgiEspFsFile(HttpdConnData *connData)
httpd_cgi_state ICACHE_FLASH_ATTR cgiEspFsFile(HttpdConnData *connData)
{
return serveStaticFile(connData, connData->cgiArg);
}
@ -183,9 +183,9 @@ typedef struct {
char *buff_e;
} TplData;
typedef int (* TplCallback)(HttpdConnData *connData, char *token, void **arg);
typedef httpd_cgi_state (* TplCallback)(HttpdConnData *connData, char *token, void **arg);
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData)
httpd_cgi_state ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData)
{
TplData *tpd = connData->cgiData;
int len;
@ -289,7 +289,7 @@ int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData)
tpd->chunk_resume = false;
int status = ((TplCallback)(connData->cgiArg))(connData, tpd->token, &tpd->tplArg);
httpd_cgi_state status = ((TplCallback)(connData->cgiArg))(connData, tpd->token, &tpd->tplArg);
if (status == HTTPD_CGI_MORE) {
// dbg("Multi-part tpl subst, saving parser state");

@ -7,7 +7,7 @@
"collapseBooleanAttributes": true,
"removeTagWhitespace": true,
"removeAttributeQuotes": true,
"removeRedundantAttributes": true,
"removeRedundantAttributes": false,
"useShortDoctype": true,
"removeEmptyAttributes": true,
"removeScriptTypeAttributes": true,

@ -14,9 +14,9 @@ typedef struct {
char *tagName;
} CgiUploadFlashDef;
int cgiReadFlash(HttpdConnData *connData);
int cgiGetFirmwareNext(HttpdConnData *connData);
int cgiUploadFirmware(HttpdConnData *connData);
int cgiRebootFirmware(HttpdConnData *connData);
httpd_cgi_state cgiReadFlash(HttpdConnData *connData);
httpd_cgi_state cgiGetFirmwareNext(HttpdConnData *connData);
httpd_cgi_state cgiUploadFirmware(HttpdConnData *connData);
httpd_cgi_state cgiRebootFirmware(HttpdConnData *connData);
#endif

@ -27,11 +27,12 @@ struct Websock {
WebsockPriv *priv;
};
int ICACHE_FLASH_ATTR cgiWebsocket(HttpdConnData *connData);
httpd_cgi_state cgiWebsocket(HttpdConnData *connData);
int ICACHE_FLASH_ATTR cgiWebsocketSend(Websock *ws, char *data, int len, int flags);
void ICACHE_FLASH_ATTR cgiWebsocketClose(Websock *ws, int reason);
int ICACHE_FLASH_ATTR cgiWebSocketRecv(HttpdConnData *connData, char *data, int len);
int ICACHE_FLASH_ATTR cgiWebsockBroadcast(char *resource, char *data, int len, int flags);
httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, char *data, int len);
//int ICACHE_FLASH_ATTR cgiWebsockBroadcast(char *resource, char *data, int len, int flags);
#endif
#endif

@ -3,11 +3,11 @@
#include "httpd.h"
int cgiWiFiScan(HttpdConnData *connData);
int tplWlan(HttpdConnData *connData, char *token, void **arg);
int cgiWiFi(HttpdConnData *connData);
int cgiWiFiConnect(HttpdConnData *connData);
int cgiWiFiSetMode(HttpdConnData *connData);
int cgiWiFiConnStatus(HttpdConnData *connData);
httpd_cgi_state cgiWiFiScan(HttpdConnData *connData);
httpd_cgi_state tplWlan(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state cgiWiFi(HttpdConnData *connData);
httpd_cgi_state cgiWiFiConnect(HttpdConnData *connData);
httpd_cgi_state cgiWiFiSetMode(HttpdConnData *connData);
httpd_cgi_state cgiWiFiConnStatus(HttpdConnData *connData);
#endif
#endif

@ -5,26 +5,35 @@
#define HTTPDVER "0.4-based"
#define HTTPD_CGI_MORE 0
#define HTTPD_CGI_DONE 1
#define HTTPD_CGI_NOTFOUND 2
#define HTTPD_CGI_AUTHENTICATED 3
typedef enum {
HTTPD_CGI_MORE = 0,
HTTPD_CGI_DONE = 1,
HTTPD_CGI_NOTFOUND = 2,
HTTPD_CGI_AUTHENTICATED = 3,
} httpd_cgi_state;
typedef enum {
HTTP_GET = 1,
HTTP_POST = 2,
HTTP_OPTIONS = 3,
HTTP_PUT = 4,
HTTP_DELETE = 5,
HTTP_PATCH = 6,
HTTP_HEAD = 7,
} http_method;
#define HTTPD_METHOD_GET 1
#define HTTPD_METHOD_POST 2
#define HTTPD_METHOD_OPTIONS 3
typedef struct HttpdPriv HttpdPriv;
typedef struct HttpdConnData HttpdConnData;
typedef struct HttpdPostData HttpdPostData;
typedef int (* cgiSendCallback)(HttpdConnData *connData);
typedef int (* cgiRecvHandler)(HttpdConnData *connData, char *data, int len);
typedef httpd_cgi_state (* cgiSendCallback)(HttpdConnData *connData);
typedef httpd_cgi_state (* cgiRecvHandler)(HttpdConnData *connData, char *data, int len);
//A struct describing a http connection. This gets passed to cgi functions.
struct HttpdConnData {
ConnTypePtr conn; // The TCP connection. Exact type depends on the platform.
char requestType; // One of the HTTPD_METHOD_* values
http_method requestType; // method type
char *url; // The URL requested, without hostname or GET arguments
char *getArgs; // The GET arguments for this request, if any.
@ -87,11 +96,12 @@ typedef struct {
#define ROUTE_END() {NULL, NULL, NULL, NULL}
const char *http_method_str(http_method m);
httpd_cgi_state cgiRedirect(HttpdConnData *connData);
httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData);
httpd_cgi_state cgiRedirectApClientToHostname(HttpdConnData *connData);
int cgiRedirect(HttpdConnData *connData);
int cgiRedirectToHostname(HttpdConnData *connData);
int cgiRedirectApClientToHostname(HttpdConnData *connData);
void httpdRedirect(HttpdConnData *conn, char *newUrl);
int httpdUrlDecode(char *val, int valLen, char *ret, int retLen);
int httpdFindArg(char *line, char *arg, char *buff, int buffLen);

@ -4,12 +4,12 @@
#include "httpd.h"
/** Catch-all, use in '*' routes */
int cgiEspFsHook(HttpdConnData *connData);
httpd_cgi_state cgiEspFsHook(HttpdConnData *connData);
/** Template route */
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData);
httpd_cgi_state ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData);
/** Static file route with the file as the first arg. */
int ICACHE_FLASH_ATTR cgiEspFsFile(HttpdConnData *connData);
httpd_cgi_state ICACHE_FLASH_ATTR cgiEspFsFile(HttpdConnData *connData);
#endif

@ -43,7 +43,7 @@ static int ICACHE_FLASH_ATTR checkEspfsHeader(void *buf) {
// Cgi to query which firmware needs to be uploaded next
int ICACHE_FLASH_ATTR cgiGetFirmwareNext(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiGetFirmwareNext(HttpdConnData *connData) {
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;
@ -62,7 +62,7 @@ int ICACHE_FLASH_ATTR cgiGetFirmwareNext(HttpdConnData *connData) {
//Cgi that reads the SPI flash. Assumes 512KByte flash.
//ToDo: Figure out real flash size somehow?
int ICACHE_FLASH_ATTR cgiReadFlash(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiReadFlash(HttpdConnData *connData) {
int *pos=(int *)&connData->cgiData;
if (connData->conn==NULL) {
//Connection aborted. Clean up.
@ -123,7 +123,7 @@ typedef struct __attribute__((packed)) {
} OtaHeader;
int ICACHE_FLASH_ATTR cgiUploadFirmware(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiUploadFirmware(HttpdConnData *connData) {
CgiUploadFlashDef *def=(CgiUploadFlashDef*)connData->cgiArg;
UploadState *state=(UploadState *)connData->cgiData;
int len;
@ -302,7 +302,7 @@ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
}
// Handle request to reboot into the new firmware
int ICACHE_FLASH_ATTR cgiRebootFirmware(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiRebootFirmware(HttpdConnData *connData) {
if (connData->conn==NULL) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE;

@ -119,26 +119,27 @@ int ICACHE_FLASH_ATTR cgiWebsocketSend(Websock *ws, char *data, int len, int fla
return r;
}
//Broadcast data to all websockets at a specific url. Returns the amount of connections sent to.
int ICACHE_FLASH_ATTR cgiWebsockBroadcast(char *resource, char *data, int len, int flags) {
//This is majorly broken (and actually, always, it just tended to work because the circumstances
//were juuuust right). Because the socket is used outside of the httpd send/receive context, it
//will not have an associated send buffer. This means httpdSend will write to a dangling pointer!
//Disabled for now. If you really need this, open an issue on github or otherwise poke me and I'll
//see what I can do.
/*
Websock *lw=llStart;
int ret=0;
while (lw!=NULL) {
if (strcmp(lw->conn->url, resource)==0) {
cgiWebsocketSend(lw, data, len, flags);
ret++;
}
lw=lw->priv->next;
}
return ret;*/
return 0;
}
////Broadcast data to all websockets at a specific url. Returns the amount of connections sent to.
//int ICACHE_FLASH_ATTR cgiWebsockBroadcast(char *resource, char *data, int len, int flags) {
////This is majorly broken (and actually, always, it just tended to work because the circumstances
////were juuuust right). Because the socket is used outside of the httpd send/receive context, it
////will not have an associated send buffer. This means httpdSend will write to a dangling pointer!
////Disabled for now. If you really need this, open an issue on github or otherwise poke me and I'll
////see what I can do.
///*
// Websock *lw=llStart;
// int ret=0;
// while (lw!=NULL) {
// if (strcmp(lw->conn->url, resource)==0) {
// cgiWebsocketSend(lw, data, len, flags);
// ret++;
// }
// lw=lw->priv->next;
// }
// return ret;*/
// return 0;
//}
void ICACHE_FLASH_ATTR cgiWebsocketClose(Websock *ws, int reason) {
@ -167,7 +168,7 @@ static void ICACHE_FLASH_ATTR websockFree(Websock *ws) {
if (ws->priv) free(ws->priv);
}
int ICACHE_FLASH_ATTR cgiWebSocketRecv(HttpdConnData *connData, char *data, int len) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiWebSocketRecv(HttpdConnData *connData, char *data, int len) {
int i, j, sl;
int r=HTTPD_CGI_MORE;
int wasHeaderByte;
@ -280,7 +281,7 @@ int ICACHE_FLASH_ATTR cgiWebSocketRecv(HttpdConnData *connData, char *data, int
}
//Websocket 'cgi' implementation
int ICACHE_FLASH_ATTR cgiWebsocket(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiWebsocket(HttpdConnData *connData) {
char buff[256];
int i;
sha1nfo s;

@ -130,7 +130,7 @@ static void ICACHE_FLASH_ATTR wifiStartScan() {
//This CGI is called from the bit of AJAX-code in wifi.tpl. It will initiate a
//scan for access points and if available will return the result of an earlier scan.
//The result is embedded in a bit of JSON parsed by the javascript in wifi.tpl.
int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) {
int pos=(int)connData->cgiData;
int len;
char buff[256];
@ -226,7 +226,7 @@ static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) {
//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) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) {
char essid[128];
char passwd[128];
static os_timer_t reassTimer;
@ -258,7 +258,7 @@ int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) {
//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 cgiWiFiSetMode(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) {
int len;
char buff[64];
@ -282,7 +282,7 @@ int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) {
return HTTPD_CGI_DONE;
}
int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
char buff[256];
int len;
struct ip_info info;
@ -314,7 +314,7 @@ int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
}
//Template code for the WLAN page.
int ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) {
httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) {
char buff[256];
int x;
static struct station_config stconf;

@ -1 +1 @@
Subproject commit b2fca263f880869744bb086047d394d32e13117d
Subproject commit 70e41c91e5daabeb6e39a02df9baeadb3995b700

@ -1,6 +1,6 @@
#include "cgi_ping.h"
int FLASH_FN cgiPing(HttpdConnData *connData)
httpd_cgi_state FLASH_FN cgiPing(HttpdConnData *connData)
{
if (connData->conn==NULL) {
//Connection aborted. Clean up.
@ -8,6 +8,7 @@ int FLASH_FN cgiPing(HttpdConnData *connData)
}
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
httpdSend(connData, "pong\n", -1);

@ -6,6 +6,6 @@
// this is used by the UI to check if server is already restarted and working again.
int cgiPing(HttpdConnData *connData);
httpd_cgi_state cgiPing(HttpdConnData *connData);
#endif // CGI_PING_H

@ -7,7 +7,7 @@ static void FLASH_FN tmrCb(void *arg)
system_restart();
}
int FLASH_FN cgiResetDevice(HttpdConnData *connData)
httpd_cgi_state FLASH_FN cgiResetDevice(HttpdConnData *connData)
{
if (connData->conn==NULL) {
//Connection aborted. Clean up.
@ -15,6 +15,7 @@ int FLASH_FN cgiResetDevice(HttpdConnData *connData)
}
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "text/plain");
httpdEndHeaders(connData);
os_timer_disarm(&tmr);

@ -4,6 +4,6 @@
#include <esp8266.h>
#include <httpd.h>
int cgiResetDevice(HttpdConnData *connData);
httpd_cgi_state cgiResetDevice(HttpdConnData *connData);
#endif // CGI_RESET_H

@ -7,6 +7,10 @@
// request to capture data...
#define DG_REQUEST_RAW 40
#define DG_REQUEST_FFT 41
// reporting helpers
#define DG_REQUEST_STORE_REF 42
#define DG_REQUEST_COMPARE_REF 43
extern SBMP_Endpoint *dlnk_ep;

@ -5,7 +5,7 @@
#include "sbmp.h"
/** "About" page */
int FLASH_FN tplAbout(HttpdConnData *connData, char *token, void **arg)
httpd_cgi_state FLASH_FN tplAbout(HttpdConnData *connData, char *token, void **arg)
{
// arg is unused
(void)arg;

@ -3,6 +3,6 @@
#include <httpd.h>
int tplAbout(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state tplAbout(HttpdConnData *connData, char *token, void **arg);
#endif // PAGE_ABOUT_H

@ -0,0 +1,189 @@
#include <esp8266.h>
#include <httpd.h>
#include "page_monitoring.h"
#include "reporting.h"
#include "ftoa.h"
httpd_cgi_state FLASH_FN cgiMonCompare(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData);
// this is semi-async (waits for completion)
bool suc = capture_and_report(false);
char buf[100];
if (suc && rpt_result.ready) {
// success
char *bb = buf;
bb += sprintf(bb, "{\"success\": true, \"deviation\": ");
bb += my_ftoa(bb,rpt_result.deviation, 2);
bb += sprintf(bb, ", \"rms\": ");
bb += my_ftoa(bb,rpt_result.i_rms, 2);
bb += sprintf(bb, "}");
httpdSend(connData, buf, -1);
} else {
httpdSend(connData, "{\"success\": false}", -1);
}
return HTTPD_CGI_DONE;
}
/** This is an automated poll for current state, to update the display (measured by reporting func) */
httpd_cgi_state FLASH_FN cgiMonStatus(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData);
char buf[100];
if (rpt_result.ready) {
// success
char *bb = buf;
bb += sprintf(bb, "{\"success\": true, \"deviation\": ");
bb += my_ftoa(bb,rpt_result.deviation, 2);
bb += sprintf(bb, ", \"rms\": ");
bb += my_ftoa(bb,rpt_result.i_rms, 2);
bb += sprintf(bb, "}");
httpdSend(connData, buf, -1);
} else {
httpdSend(connData, "{\"success\": false}", -1);
}
return HTTPD_CGI_DONE;
}
httpd_cgi_state FLASH_FN cgiMonSetRef(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData);
// this is semi-async (waits for completion)
bool suc = capture_reporting_reference();
httpdSend(connData, suc ? "{\"success\": true}" : "{\"success\": false}", -1);
return HTTPD_CGI_DONE;
}
httpd_cgi_state FLASH_FN cgiMonitoringCfg(HttpdConnData *connData)
{
if (connData->conn == NULL) return HTTPD_CGI_DONE;
// TODO
HttpdPostData *post = connData->post;
if (post != NULL) {
char buf[64];
int blen;
// enabled=1
blen = httpdFindArg(post->buff, "enabled", buf, 64);
if (blen == -1) {
// wasn't found
rpt_conf.enabled = false;
} else {
rpt_conf.enabled = (buf[0] == '1');
}
// interval=secs
blen = httpdFindArg(post->buff, "interval", buf, 64);
if (blen != -1) {
rpt_conf.interval = (uint32_t)atoi(buf);
}
// service=xv or ts
blen = httpdFindArg(post->buff, "service", buf, 64);
if (blen != -1) {
rpt_conf.service = (buf[0] == 'x' ? RPT_XIVELY: RPT_THINGSPEAK);
}
// feed
blen = httpdFindArg(post->buff, "feed", buf, 64);
if (blen != -1) {
strcpy(rpt_conf.feed, buf);
}
// key
blen = httpdFindArg(post->buff, "key", buf, 64);
if (blen != -1) {
strcpy(rpt_conf.key, buf);
}
// Save & Apply
reporting_cfg_save();
}
httpdRedirect(connData, "/monitoring");
return HTTPD_CGI_DONE;
}
/** "Monitoring" page - fill form fields */
httpd_cgi_state FLASH_FN tplMonitoring(HttpdConnData *connData, char *token, void **arg)
{
(void)arg;
char buf[20];
if (token == NULL) return HTTPD_CGI_DONE;
if (streq(token, "refStored")) {
httpdSend(connData, true ? "OK" : "Not set!", -1); // fixme
} else if (streq(token, "curDeviation")) {
// current deviation
if (rpt_result.ready) {
my_ftoa(buf, rpt_result.deviation, 2);
} else {
sprintf(buf, "--");
}
httpdSend(connData, buf, -1);
} else if (streq(token, "curRMS")) {
// current deviation
if (rpt_result.ready) {
my_ftoa(buf, rpt_result.i_rms, 2);
} else {
sprintf(buf, "--");
}
httpdSend(connData, buf, -1);
} else if (streq(token, "rep_en")) {
if (rpt_conf.enabled) httpdSend(connData, "checked", -1);
} else if (streq(token, "repInterval")) { // interval in seconds
sprintf(buf, "%d", rpt_conf.interval);
httpdSend(connData, buf, -1);
} else if (streq(token, "svc_xv")) { // Xively checkbox
if (rpt_conf.service == RPT_XIVELY) {
httpdSend(connData, "checked", -1);
}
} else if (streq(token, "svc_ts")) { // ThingSpeak checkbox
if (rpt_conf.service == RPT_THINGSPEAK) {
httpdSend(connData, "checked", -1);
}
} else if (streq(token, "repFeed")) { // reporting feed ID
httpdSend(connData, rpt_conf.feed, -1);
} else if (streq(token, "repKey")) { // reporting key
httpdSend(connData, rpt_conf.key, -1);
}
return HTTPD_CGI_DONE;
}

@ -0,0 +1,15 @@
#ifndef PAGE_MONITORING_H
#define PAGE_MONITORING_H
#include <httpd.h>
httpd_cgi_state tplMonitoring(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state cgiMonCompare(HttpdConnData *connData);
httpd_cgi_state cgiMonStatus(HttpdConnData *connData); // same result as "compare", but doesn't measure anything
httpd_cgi_state cgiMonSetRef(HttpdConnData *connData);
httpd_cgi_state cgiMonitoringCfg(HttpdConnData *connData);
#endif // PAGE_MONITORING_H

@ -7,7 +7,7 @@
/** System Status page */
int FLASH_FN tplSystemStatus(HttpdConnData *connData, char *token, void **arg)
httpd_cgi_state FLASH_FN tplSystemStatus(HttpdConnData *connData, char *token, void **arg)
{
// arg is unused
(void)arg;

@ -3,6 +3,6 @@
#include <httpd.h>
int tplSystemStatus(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state tplSystemStatus(HttpdConnData *connData, char *token, void **arg);
#endif // PAGE_HOME_H

@ -19,22 +19,22 @@ typedef struct {
} tplReadSamplesJSON_state;
static int FLASH_FN tplSamplesJSON(MEAS_FORMAT fmt, HttpdConnData *connData, char *token, void **arg);
static httpd_cgi_state FLASH_FN tplSamplesJSON(MEAS_FORMAT fmt, HttpdConnData *connData, char *token, void **arg);
int FLASH_FN tplWaveformJSON(HttpdConnData *connData, char *token, void **arg)
httpd_cgi_state FLASH_FN tplWaveformJSON(HttpdConnData *connData, char *token, void **arg)
{
return tplSamplesJSON(RAW, connData, token, arg);
}
int FLASH_FN tplFourierJSON(HttpdConnData *connData, char *token, void **arg)
httpd_cgi_state FLASH_FN tplFourierJSON(HttpdConnData *connData, char *token, void **arg)
{
return tplSamplesJSON(FFT, connData, token, arg);
}
static int FLASH_FN tplSamplesJSON(MEAS_FORMAT fmt, HttpdConnData *connData, char *token, void **arg)
static httpd_cgi_state FLASH_FN tplSamplesJSON(MEAS_FORMAT fmt, HttpdConnData *connData, char *token, void **arg)
{
char buff[128];
int len;

@ -3,8 +3,8 @@
#include <httpd.h>
int tplWaveformJSON(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state tplWaveformJSON(HttpdConnData *connData, char *token, void **arg);
int tplFourierJSON(HttpdConnData *connData, char *token, void **arg);
httpd_cgi_state tplFourierJSON(HttpdConnData *connData, char *token, void **arg);
#endif // PAGE_WAVEFORM_H

@ -0,0 +1,243 @@
#include "reporting.h"
#include "datalink.h"
#include "serial.h"
#include "httpclient.h"
#include "ftoa.h"
#define RPT_CONF_MAGIC 0x24C595D5
ReportingResult rpt_result;
ReportingCfg rpt_conf;
static os_timer_t rpt_tim;
/** Timer cb */
static void FLASH_FN rpt_tim_cb(void *arg)
{
(void)arg;
// send report now...
if (rpt_conf.enabled) {
capture_and_report(true);
}
}
/** Stop / start timer & set interval based on rpt conf */
static FLASH_FN void set_timer(void)
{
os_timer_disarm(&rpt_tim);
if (rpt_conf.enabled) {
os_timer_setfn(&rpt_tim, rpt_tim_cb, NULL);
os_timer_arm(&rpt_tim, (int)(rpt_conf.interval * 1000), 1);
}
}
/** Fix unterminated strings, add magic, etc.. */
static FLASH_FN void normalize_rpt_conf(void)
{
// terminate strings
rpt_conf.feed[sizeof(rpt_conf.feed) - 1] = 0;
rpt_conf.key[sizeof(rpt_conf.key) - 1] = 0;
// set magic
rpt_conf.magic = RPT_CONF_MAGIC;
}
static FLASH_FN void dump_rpt_conf(void)
{
dbg("Enabled: %d | Interval: %d | Service: %s", rpt_conf.enabled, rpt_conf.interval, (rpt_conf.service == RPT_XIVELY ? "Xively" : "ThingSpeak"));
dbg("Key: %s | Feed: %s", rpt_conf.key, rpt_conf.feed);
}
/** Save reporting config to flash */
void FLASH_FN reporting_cfg_save(void)
{
normalize_rpt_conf(); // fix weirdness
info("Saving monitoring config");
dump_rpt_conf();
system_param_save_with_protect(0x3D, &rpt_conf, sizeof(ReportingCfg));
// start timer for the new interval time
set_timer();
info("Config saved.");
}
/** Load the reporting config from flash */
void FLASH_FN reporting_cfg_load(void)
{
info("Loading monitoring config");
system_param_load(0x3D, 0, &rpt_conf, sizeof(ReportingCfg));
if (rpt_conf.magic != RPT_CONF_MAGIC) {
warn("Config block corrupted, reset to defaults.");
// invalid config, zero out
memset(&rpt_conf, 0, sizeof(ReportingCfg));
rpt_conf.magic = RPT_CONF_MAGIC;
// save fixed
reporting_cfg_save();
} else {
dump_rpt_conf();
}
set_timer();
info("Config loaded.");
}
/** Called when response to Compare Ref is received */
static void FLASH_FN compare_ref_cb(SBMP_Endpoint *ep, SBMP_Datagram *dg, void **obj)
{
(void)obj;
sbmp_ep_remove_listener(ep, dg->session);
info("Measurement complete.");
rpt_result.success = (dg->type == DG_SUCCESS);
if (dg->type == DG_SUCCESS) {
PayloadParser pp = pp_start(dg->payload, dg->length);
rpt_result.deviation = pp_float(&pp);
rpt_result.i_rms = pp_float(&pp);
} else {
error("FAIL resp from sbmp.");
}
rpt_result.ready = true; // signal to waiting loop
}
/** Send report from rpt_result */
static void FLASH_FN do_send_report(void)
{
info("Sending report...");
char buf[100];
char *bb = buf;
char url_buf[200];
char hdrs_buf[100];
switch (rpt_conf.service) {
case RPT_XIVELY:;
bb += sprintf(bb, "deviation,");
bb += my_ftoa(bb, rpt_result.deviation, 2);
bb += sprintf(bb, "\nI_rms,");
bb += my_ftoa(bb, rpt_result.i_rms, 2);
// URL
// We technically could use HTTPS, but it's not tested and probably buggy as hell
sprintf(url_buf, "http://api.xively.com/v2/feeds/%s.csv", rpt_conf.feed);
// Key
sprintf(hdrs_buf, "X-ApiKey: %s\r\n", rpt_conf.key);
http_put(url_buf, buf, hdrs_buf, http_callback_showstatus);
break;
case RPT_THINGSPEAK:
warn("------- TODO: REPORT TO THINGSPEAK -------");
break;
}
}
/** Immediately send report to xively / thingspeak */
bool FLASH_FN capture_and_report(bool do_report)
{
info("Starting reporting measurmenet...");
if (rpt_result.busy) {
error("Capture busy.");
return false;
}
rpt_result.ready = false;
rpt_result.busy = true;
uint16_t sesn;
sbmp_ep_send_message(dlnk_ep, DG_REQUEST_COMPARE_REF, NULL, 0, &sesn, NULL);
sbmp_ep_add_listener(dlnk_ep, sesn, compare_ref_cb, NULL);
// poll & wait for response
const int timeout = 500;
for (uint32_t i = 0; i < timeout * 100; i++) {
uart_poll();
if (rpt_result.ready) {
if (rpt_result.success && do_report) {
do_send_report();
}
rpt_result.busy = false;
return true; // done
}
os_delay_us(10);
system_soft_wdt_feed(); // Feed the dog, or it'll bite.
}
// timeout - remove listener
error("Measure timeout - no resp received.");
sbmp_ep_remove_listener(dlnk_ep, sesn);
rpt_result.busy = false;
return false;
}
static bool capt_ref_done;
static bool capt_ref_success;
/** Callback for "store ref" */
static void FLASH_FN store_ref_cb(SBMP_Endpoint *ep, SBMP_Datagram *dg, void **obj)
{
(void)obj;
sbmp_ep_remove_listener(ep, dg->session);
capt_ref_done = true;
capt_ref_success = (dg->type == DG_SUCCESS);
}
/** Capture reference vector for monitoring */
bool FLASH_FN capture_reporting_reference(void)
{
info("Capturing reference...");
if (rpt_result.busy) {
error("Capture busy.");
return false;
}
rpt_result.busy = true;
uint16_t sesn;
sbmp_ep_send_message(dlnk_ep, DG_REQUEST_STORE_REF, NULL, 0, &sesn, NULL);
sbmp_ep_add_listener(dlnk_ep, sesn, store_ref_cb, NULL);
capt_ref_done = false;
capt_ref_success = false;
const int timeout = 500;
for (uint32_t i = 0; i < timeout * 100; i++) {
uart_poll();
if (capt_ref_done) {
rpt_result.busy = false;
return capt_ref_success; // done
}
os_delay_us(10);
system_soft_wdt_feed(); // Feed the dog, or it'll bite.
}
// timeout - remove listener
error("Ref capture timeout - no resp received.");
sbmp_ep_remove_listener(dlnk_ep, sesn);
rpt_result.busy = false;
return false;
}

@ -0,0 +1,52 @@
#ifndef REPORTING_H
#define REPORTING_H
#include <esp8266.h>
typedef struct {
// 0
bool enabled;
// 4
uint32_t interval;
// 8
enum {
RPT_XIVELY,
RPT_THINGSPEAK
} service;
// 12
char feed[64];
// 76
char key[64];
// 80
uint32_t magic;
} ReportingCfg;
/** Comapre result is stored here */
typedef struct {
bool ready;
bool success;
bool busy;
float deviation;
float i_rms;
} ReportingResult;
/** Report result */
extern ReportingResult rpt_result;
/** Reporting config struct */
extern ReportingCfg rpt_conf;
/** Save reporting config to flash */
void reporting_cfg_save(void);
/** Load the reporting config from flash */
void reporting_cfg_load(void);
/** Immediately capture & send report to xively / thingspeak (or dont send - just for view) */
bool capture_and_report(bool do_report);
/** Capture reference vector for monitoring */
bool capture_reporting_reference(void);
#endif // REPORTING_H

@ -13,6 +13,7 @@
#include "page_status.h"
#include "page_waveform.h"
#include "page_about.h"
#include "page_monitoring.h"
#include "cgi_reset.h"
#include "cgi_ping.h"
@ -46,11 +47,17 @@ HttpdBuiltInUrl builtInUrls[] = {
ROUTE_TPL_FILE("/measure/raw", tplWaveformJSON, "/json/samples.tpl"),
ROUTE_TPL_FILE("/measure/fft", tplFourierJSON, "/json/samples.tpl"),
ROUTE_CGI("/mon/compare", cgiMonCompare),
ROUTE_CGI("/mon/status", cgiMonStatus),
ROUTE_CGI("/mon/setref", cgiMonSetRef),
ROUTE_CGI("/mon/config", cgiMonitoringCfg), // redirects to /monitoring
// --- UI pages ---
// System Status page
ROUTE_TPL_FILE("/", tplSystemStatus, "/pages/status.tpl"),
ROUTE_TPL_FILE("/status", tplSystemStatus, "/pages/status.tpl"),
ROUTE_TPL_FILE("/about", tplAbout, "/pages/about.tpl"),
ROUTE_TPL_FILE("/monitoring", tplMonitoring, "/pages/monitoring.tpl"),
ROUTE_FILE("/waveform", "/pages/wfm.html"), // static file, html -> can use gzip
ROUTE_FILE("/fft", "/pages/fft.html"), // static file, html -> can use gzip
ROUTE_FILE("/spectrogram", "/pages/sgm.html"), // static file, html -> can use gzip

@ -23,14 +23,14 @@
#include "routes.h"
#include "fw_version.h"
#include "httpclient.h"
#include "reporting.h"
extern HttpdBuiltInUrl builtInUrls[];
static ETSTimer prSecondTimer;
/** Timer called each second */
static void ICACHE_FLASH_ATTR prSecondTimerCb(void *arg)
static void FLASH_FN prSecondTimerCb(void *arg)
{
(void)arg;
@ -112,6 +112,9 @@ void user_init(void)
httpdInit(builtInUrls, 80);
// start reporting timer
reporting_cfg_load();
printf(LOG_EOL);
info("Ready");
printf(LOG_EOL);

Loading…
Cancel
Save