Monitor for central heating system (e.g. 2zones+hw) Supports up to 15 temp probes (DS18B20/DS18S20) 3 valve monitors Gas pulse meter recording Use stand-alone or with nodeEnergyServer See http://robdobson.com/2015/09/central-heating-monitor
Dependencies: EthernetInterfacePlusHostname NTPClient Onewire RdWebServer SDFileSystem-RTOS mbed-rtos mbed-src
Revision 14:3c3aa4fd7e1a, committed 2015-03-03
- Comitter:
- Bobty
- Date:
- Tue Mar 03 12:53:37 2015 +0000
- Parent:
- 13:9ec0e11cf3c1
- Child:
- 15:29902a6b3c89
- Commit message:
- Added other sensor readings to web view
Changed in this revision
| main.cpp | Show annotated file Show diff for this revision Revisions of this file |
| public/index.html | Show annotated file Show diff for this revision Revisions of this file |
--- a/main.cpp Sun Feb 22 22:33:25 2015 +0000
+++ b/main.cpp Tue Mar 03 12:53:37 2015 +0000
@@ -73,9 +73,9 @@
// "bt": %d
// }
const char broadcastMsgPrefix[] = "{\"e\":[";
-const char broadcastMsgGasFormat[] = "{\"n\":\"gasCount\",\"v\":%d},{\"n\":\"gasPulseRateMs\",\"v\":%d,\"u\":\"ms\"}";
+const char broadcastMsgGasFormat[] = "{\"n\":\"gasCount\",\"v\":%d,\"u\":\"count\"},{\"n\":\"gasPulseRateMs\",\"v\":%d,\"u\":\"ms\"}";
const char broadcastTemperatureFormat[] = "{\"n\":\"temp_%s\",\"v\":%0.1f,\"u\":\"degC\"}";
-const char broadcastVoltAlerterFormat[] = "{\"n\":\"pump_%d\",\"v\":%d}";
+const char broadcastVoltAlerterFormat[] = "{\"n\":\"pump_%d\",\"bv\":%d}";
const char broadcastMsgSuffix[] = "],\"bt\":%d}";
// Broadcast message length and buffer
@@ -87,16 +87,9 @@
60;
char broadcastMsgBuffer[broadcastMsgLen];
-// Send broadcast message with current data
-void SendInfoBroadcast()
+// Format broadcast message
+void GenBroadcastMessage()
{
- led3 = true;
-
- // Init the sending socket
- sendUDPSocket.init();
- sendUDPSocket.set_broadcasting();
- broadcastEndpoint.set_address("255.255.255.255", BROADCAST_PORT);
-
// Get temperature values
TemperatureValue tempValues[Thermometers::MAX_THERMOMETERS];
int numTempValues = thermometers.GetTemperatureValues(Thermometers::MAX_THERMOMETERS, tempValues, 100);
@@ -104,7 +97,7 @@
// {
// printf("Temp: %.1f, Addr: %s, Time: %d\r\n", tempValues[tempIdx].tempInCentigrade, tempValues[tempIdx].address, tempValues[tempIdx].timeStamp);
// }
-
+
// Format the broadcast message
time_t timeNow = time(NULL);
strcpy(broadcastMsgBuffer, broadcastMsgPrefix);
@@ -121,7 +114,21 @@
strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 3, voltAlerter3.GetState());
sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgSuffix, timeNow);
+}
+
+// Send broadcast message with current data
+void SendInfoBroadcast()
+{
+ led3 = true;
+
+ // Init the sending socket
+ sendUDPSocket.init();
+ sendUDPSocket.set_broadcasting();
+ broadcastEndpoint.set_address("255.255.255.255", BROADCAST_PORT);
+ // Format the message
+ GenBroadcastMessage();
+
// Send
int bytesToSend = strlen(broadcastMsgBuffer);
int rslt = sendUDPSocket.sendTo(broadcastEndpoint, broadcastMsgBuffer, bytesToSend);
@@ -144,11 +151,11 @@
led3 = false;
}
-char* getGasUseCallback(int method, char* cmdStr, char* argStr)
+char* getCurDataCallback(int method, char* cmdStr, char* argStr)
{
- char* pResp = gasUseCounter.getGasUseCallback(cmdStr, argStr);
- pc.printf("Returning gas use %s\r\n", pResp);
- return pResp;
+ // Format message
+ GenBroadcastMessage();
+ return broadcastMsgBuffer;
}
char* setGasUseCallback(int method, char* cmdStr, char* argStr)
@@ -171,7 +178,7 @@
webServer.addCommand("", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "index.htm", false);
webServer.addCommand("gear-gr.png", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, NULL, true);
webServer.addCommand("listfiles", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "/", false);
- webServer.addCommand("getgascount", RdWebServerCmdDef::CMD_CALLBACK, &getGasUseCallback);
+ webServer.addCommand("getcurdata", RdWebServerCmdDef::CMD_CALLBACK, &getCurDataCallback);
webServer.addCommand("setgascount", RdWebServerCmdDef::CMD_CALLBACK, &setGasUseCallback);
webServer.init(WEBPORT, &led4, baseWebFolder);
webServer.run();
@@ -183,7 +190,7 @@
while (1)
{
pc.printf("Trying to update time...\r\n");
- if (ntp.setTime("0.pool.ntp.org") == 0)
+ if (ntp.setTime("0.pool.ntp.org") == NTP_OK)
{
printf("Set time successfully\r\n");
time_t ctTime;
@@ -239,13 +246,11 @@
// Web Server
Thread httpServer(&http_thread, NULL, osPriorityNormal, (DEFAULT_STACK_SIZE * 3));
+
+ // Store reason for restart
+ bool watchdogCausedRestart = watchdog.WatchdogCausedRestart();
+ bool restartCauseRecorded = false;
- // Record the reason for restarting in the log file
- if (watchdog.WatchdogCausedRestart())
- logger.LogEvent("Watchdog Restart");
- else
- logger.LogEvent("Normal Restart");
-
// Setup the watchdog for 10s reset
watchdog.SetTimeoutSecs(10);
@@ -254,7 +259,25 @@
const int TIME_BETWEEN_BROADCASTS_IN_SECS = 60;
while(true)
{
+ // Check if we can record the reason for restart (i.e. if time is now set)
+ if (!restartCauseRecorded)
+ {
+ time_t nowTime = time(NULL);
+ if (nowTime > 1000000000)
+ {
+ // Record the reason for restarting in the log file
+ if (watchdogCausedRestart)
+ logger.LogEvent("Watchdog Restart");
+ else
+ logger.LogEvent("Normal Restart");
+ restartCauseRecorded = true;
+ }
+ }
+
+ // Loop delay
osDelay(LOOP_DELAY_IN_MS);
+
+ // Feed the watchdog and show the flashing LED
led1 = !led1;
watchdog.Feed();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/public/index.html Tue Mar 03 12:53:37 2015 +0000
@@ -0,0 +1,320 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Gas Use Monitor</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <script type="text/javascript">
+ function callAjax(url, callback, contentType, usePost)
+ {
+ var xmlhttp;
+ // code for IE7+, Firefox, Chrome, Opera, Safari
+ xmlhttp = new XMLHttpRequest();
+ xmlhttp.onreadystatechange = function()
+ {
+ if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
+ {
+ callback(xmlhttp.responseText);
+ }
+ }
+
+ if (typeof usePost !== "undefined")
+ {
+ xmlhttp.open("POST", url, true);
+ }
+ else
+ {
+ xmlhttp.open("GET", url, true);
+ }
+ if (typeof contentType !== "undefined")
+ {
+ if (contentType === "json")
+ {
+ xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ }
+ }
+ xmlhttp.send();
+ }
+ function ajaxCallback()
+ {
+ }
+ function dataRefresh()
+ {
+ updateCurData();
+ setTimeout(function(){ dataRefresh();}, 10000);
+ }
+ function updateCurData()
+ {
+ callAjax("./getcurdata", curDataCallback, "json");
+ }
+ function curDataCallback(resp)
+ {
+ console.log("Response " + resp);
+ var respObj = JSON.parse(resp);
+ // Handle elements
+ if ("e" in respObj)
+ {
+ // Remove temperature and pump elements
+ var elem = document.getElementById("tempValues");
+ while (elem.firstChild)
+ {
+ elem.removeChild(elem.firstChild);
+ }
+ var elem = document.getElementById("pumpValues");
+ while (elem.firstChild)
+ {
+ elem.removeChild(elem.firstChild);
+ }
+
+ // Extract data from ajax json block and show
+ var i;
+ for (i = 0; i < respObj.e.length; i++)
+ {
+ dataRec = respObj.e[i];
+ if ("n" in dataRec)
+ {
+ if (dataRec.n === "gasCount")
+ {
+ var elem = document.getElementById("gasCountValue");
+ elem.innerHTML = "" + dataRec.v;
+ }
+ else if (dataRec.n.indexOf("temp_") > -1)
+ {
+ var tempStr = dataRec.n.slice(5);
+ var elem = document.getElementById("tempValues");
+ var para = document.createElement("p");
+ para.className = "label";
+ var node = document.createTextNode(tempStr);
+ para.appendChild(node);
+ elem.appendChild(para);
+ para = document.createElement("p");
+ para.className = "value";
+ node = document.createTextNode("" + dataRec.v);
+ para.appendChild(node);
+ elem.appendChild(para);
+ }
+ else if (dataRec.n.indexOf("pump_") > -1)
+ {
+ var pumpStr = dataRec.n.slice(5);
+ var elem = document.getElementById("pumpValues");
+ var para = document.createElement("p");
+ para.className = "label";
+ var node = document.createTextNode("Pump " + pumpStr);
+ para.appendChild(node);
+ elem.appendChild(para);
+ para = document.createElement("p");
+ para.className = "value";
+ node = document.createTextNode(dataRec.bv ? "On" : "Off");
+ para.appendChild(node);
+ elem.appendChild(para);
+ }
+ }
+ }
+ }
+ if ("bt" in respObj)
+ {
+ var elem = document.getElementById("lastUpdateTime");
+ ud = new Date(respObj.bt * 1000);
+ elem.innerHTML = "" + ud.getFullYear() + "/" + padZero(ud.getMonth()+1,2) + "/" + padZero(ud.getDate(),2) + " " + padZero(ud.getHours(),2) + ":" + padZero(ud.getMinutes(),2) + ":" + padZero(ud.getSeconds(),2);
+ }
+
+ }
+ function padZero(val, zeroes)
+ {
+ return("00000000" + val).slice(-zeroes);
+ }
+ function dispGasNewVal()
+ {
+ document.getElementById("gasadjust").style.display = "none";
+ document.getElementById("gasnewval").style.display = "block";
+ document.getElementById("setgasnewval").style.display = "block";
+ }
+ function setGasNewVal()
+ {
+ document.getElementById("gasadjust").style.display = "block";
+ document.getElementById("gasnewval").style.display = "none";
+ document.getElementById("setgasnewval").style.display = "none";
+ var elem = document.getElementById("gasnewval");
+ var newVal = elem.value;
+ callAjax("./setgascount?newVal=" + newVal, setValCallback, "json", "POST")
+ }
+ function setValCallback(resp)
+ {
+ console.log("setGasValue returned " + resp)
+ updateCurData();
+ }
+ function showLog()
+ {
+ document.getElementById("logtext").style.display = "block";
+ callAjax("log.txt", getLogTextCallback);
+ }
+ function getLogTextCallback(resp)
+ {
+ var elem = document.getElementById("logtext");
+ elem.innerHTML = resp;
+ }
+ </script>
+ <style>
+ body {
+ font-family: Helvetica;
+ line-height: 1.7rem;
+ font-size:12pt;
+ }
+ .layout { display:table; width: 100%; max-width: 800px; margin: 0 auto 0 auto; }
+ .pg-hd { background-color: #00344c; display: block; }
+ .hd-txt { padding: 10px; color: White; vertical-align: middle; text-align:center; }
+ .hd-com
+ {
+ float: right;
+ height:0;
+ width:0;
+ }
+ .hd-ico
+ {
+ position: relative;
+ top: -37px;
+ left: -60px;
+ }
+ .hd-com div
+ {
+
+ }
+ .hd-com div a
+ {
+ background: transparent url('gear-gr.png') no-repeat;
+ padding: 40px 60px 20px 13px;
+ color: white;
+ }
+ .hd-com div a.text
+ {
+ left: -20px;
+ }
+ .lk-hd
+ {
+ color: #fff;
+ background-color: #8898aB !important;
+ display: table-cell;
+ width: 20%;
+ border-bottom: 1px solid #666;
+ text-align:center;
+ vertical-align: middle;
+ border-right: 1px solid #666;
+ }
+ .lk-hd:last-child
+ {
+ border-right: none !important;
+ }
+ .lk-5x
+ {
+ color: #315D96;
+ background-color: #e6ebf0 !important;
+ background: -webkit-gradient(linear, center top, center bottom, color-stop(0, #E6E8EC), color-stop(0.5, #E6E8EC), color-stop(1, #98a0b0)) !important;
+ background: -moz-linear-gradient(top, #E6E8EC 0%, #E6E8EC 50%, #98a0b0 100%) !important;
+ background: linear-gradient(to bottom, #E6E8EC 0%, #E6E8EC 50%, #98a0b0 100%) !important;
+ display: table-cell;
+ width: 20%;
+ border-bottom: 1px solid #666;
+ text-align:center;
+ vertical-align: middle;
+ border-right: 1px solid #666;
+ }
+ .lk-5x:last-child
+ {
+ border-right: none !important;
+ }
+ .lk-5x a
+ {
+ color: #315D96;
+ font-weight: bold;
+ }
+ .up-ico a, .st-ico a, .dn-ico a
+ {
+ display: block;
+ vertical-align: middle;
+ text-decoration:none;
+ padding: 25px 0 25px 0;
+ font-size: 150%;
+ }
+ .hd-lin
+ {
+ display: block;
+ vertical-align: middle;
+ text-decoration:none;
+ font-size: 150%;
+ font-weight: bold;
+ padding: 30px 0 30px 0;
+ }
+ .un-pad {
+ padding: 2px;
+ }
+ .label, .value, #gasnewval {
+ width: 100%; text-align: center;
+ }
+ #gasadjust, #setgasnewval {
+ text-align: center;
+ padding: 10px;
+ border: 10px;
+ margin: 5%;
+ width: 90%;
+ }
+ .label {
+ color: grey;
+ margin: 0.6em 0 0.6em 0;
+ }
+ .value {
+ font-size: 180%;
+ font-weight: bold;
+ color: green;
+ margin: 0.6em 0 0.6em 0;
+ }
+ .datacell {
+ display:block; width: 100%; max-width: 200px; margin: 0; border: solid lightgrey 2px; padding: 5px;
+ float: left;
+ }
+ #gasnewval {
+ display: none;
+ font-size: 200%;
+ }
+ #setgasnewval {
+ display: none;
+ }
+ #logtext {
+ display: none;
+ margin: 0; border: solid lightgrey 2px; padding: 5px;
+ width: 100%;
+ height: 100%;
+ min-height: 300px;
+ }
+ </style>
+</head>
+<body onload="dataRefresh();">
+ <div class="layout">
+ <div class="pg-hd">
+ <div class="hd-txt">
+ <h1>Gas Use Monitor</h1>
+ </div>
+ <div class="hd-com"><div class="hd-ico">
+ <div id="showlog"><a href="#" onclick="showLog();">Log</a></div>
+ </div></div>
+ </div>
+ </div>
+ <div class="layout">
+ <div class="datacell">
+ <p class="label">Update time</p>
+ <p class="value" id="lastUpdateTime"></p>
+ <p class="label">Gas Count</p>
+ <p class="value" id="gasCountValue"></p>
+ <button id="gasadjust" href="#" onclick="dispGasNewVal();">Change</button>
+ <input id="gasnewval" type="text" name="newGasCountValue" />
+ <button id="setgasnewval" type="button" href="#" onclick="setGasNewVal();">Set New Value</button>
+ </div>
+ <div class="datacell" id="tempValues">
+ </div>
+ <div class="datacell" id="pumpValues">
+ </div>
+ </div>
+ <div class="layout">
+ <textarea id="logtext">
+ </textarea>
+ </div>
+</body>
+</html>