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

Files at this revision

API Documentation at this revision

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>