commit
758ab2d132
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
@ -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 & 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> 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%><!-- |
||||||
|
--> <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%"><!-- |
||||||
|
--> seconds |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th>Service:</th> |
||||||
|
<td> |
||||||
|
<input type="radio" name="service" value="xv" id="rep-svc-xv" %svc_xv%> <label for="rep-svc-xv">Xively</label> |
||||||
|
<input type="radio" name="service" value="ts" id="rep-svc-ts" %svc_ts%> <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> </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> |
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,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" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
<?php $page = 'monitoring'; include "_start.php"; ?> |
||||||
|
|
||||||
|
<h1>Monitoring & 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> 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%><!-- |
||||||
|
--> <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%"><!-- |
||||||
|
--> seconds |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
<tr> |
||||||
|
<th>Service:</th> |
||||||
|
<td> |
||||||
|
<input type="radio" name="service" value="xv" id="rep-svc-xv" %svc_xv%> <label for="rep-svc-xv">Xively</label> |
||||||
|
<input type="radio" name="service" value="ts" id="rep-svc-ts" %svc_ts%> <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> </th> |
||||||
|
<td><input type="submit" value="Save changes"></td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
$().ready(page_mon.init); |
||||||
|
</script> |
||||||
|
|
||||||
|
<?php include "_end.php"; ?> |
@ -1 +1 @@ |
|||||||
Subproject commit b2fca263f880869744bb086047d394d32e13117d |
Subproject commit 70e41c91e5daabeb6e39a02df9baeadb3995b700 |
@ -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
|
@ -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
|
Loading…
Reference in new issue