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:
Sun Feb 22 20:23:43 2015 +0000
Parent:
9:0e103c2f869a
Child:
11:30182b9aa833
Commit message:
Now sending complete broadcast message with gas count, temperatures and voltage sensing

Changed in this revision

PulsePin.cpp Show annotated file Show diff for this revision Revisions of this file
RdDS18B20.cpp Show annotated file Show diff for this revision Revisions of this file
VoltAlerter.cpp Show annotated file Show diff for this revision Revisions of this file
VoltAlerter.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/PulsePin.cpp	Sun Feb 22 11:57:12 2015 +0000
+++ b/PulsePin.cpp	Sun Feb 22 20:23:43 2015 +0000
@@ -1,5 +1,6 @@
 // Handles a pin that has a slow pulse applied
 // Written for a gas meter monitor
+// Rob Dobson, 2015
 
 #include "PulsePin.h"
 
--- a/RdDS18B20.cpp	Sun Feb 22 11:57:12 2015 +0000
+++ b/RdDS18B20.cpp	Sun Feb 22 20:23:43 2015 +0000
@@ -78,7 +78,7 @@
 {
     if ((addrIdx >= _numValidAddresses) || (addrIdx < 0))
         return "";
-    sprintf(_addrStr, "%02x %02x %02x %02x %02x %02x %02x %02x", 
+    sprintf(_addrStr, "%02x%02x%02x%02x%02x%02x%02x%02x", 
         _addrTable[addrIdx][0], _addrTable[addrIdx][1], _addrTable[addrIdx][2], 
         _addrTable[addrIdx][3], _addrTable[addrIdx][4], _addrTable[addrIdx][5], 
         _addrTable[addrIdx][6], _addrTable[addrIdx][7]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VoltAlerter.cpp	Sun Feb 22 20:23:43 2015 +0000
@@ -0,0 +1,34 @@
+// Detect stat of Volt-Alerter
+// Device produces a square wave when voltage detected
+// Cycle time of square wave around 100ms
+// Rob Dobson, 2015
+
+#include "VoltAlerter.h"
+
+VoltAlerter::VoltAlerter(PinName pinName) :
+    _inPin(pinName, PullUp)
+{
+    _curPinState = 0;
+    _consecutiveLows = 0;
+}
+
+void VoltAlerter::Service()
+{
+    // Check pin
+    if (!_inPin)
+    {
+        _curPinState = 1;
+        _consecutiveLows = 0;
+        return;
+    }
+    
+    // Only set state low if we get X consecutive lows
+    _consecutiveLows++;
+    if (_consecutiveLows >= CONSECUTIVE_LOWS_REQD_FOR_LOW)
+    {
+        _curPinState = 0;
+        // The following is just to ensure the int doesn't overflow
+        _consecutiveLows = CONSECUTIVE_LOWS_REQD_FOR_LOW;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VoltAlerter.h	Sun Feb 22 20:23:43 2015 +0000
@@ -0,0 +1,29 @@
+// Detect stat of Volt-Alerter
+// Device produces a square wave when voltage detected
+// Cycle time of square wave around 100ms
+// Rob Dobson, 2015
+
+#ifndef __VOLTALERTER__H
+#define __VOLTALERTER__H
+#include "mbed.h"
+
+class VoltAlerter
+{
+    public:
+        VoltAlerter(PinName pinName);
+        void Service();
+        int GetState()
+        {
+            return _curPinState;
+        };
+
+        static const int CONSECUTIVE_LOWS_REQD_FOR_LOW = 10;
+        
+    private:
+        DigitalIn _inPin;
+        bool _curPinState;
+        int _consecutiveLows;
+};
+
+
+#endif
\ No newline at end of file
--- a/main.cpp	Sun Feb 22 11:57:12 2015 +0000
+++ b/main.cpp	Sun Feb 22 20:23:43 2015 +0000
@@ -4,6 +4,7 @@
 #include "RdWebServer.h"
 #include "GasUseCounter.h"
 #include "Thermometers.h"
+#include "VoltAlerter.h"
 #include <stdarg.h>
 
 // Web and UDB ports 
@@ -12,22 +13,25 @@
 
 // Ticker collects data
 //Ticker ticker;
-//const int TICK_MS = 250;
+//const int TICK_MS = 50;
 const int LOOP_DELAY_IN_MS = 250;
 
 // Debugging and status
 RawSerial pc(USBTX, USBRX);
 DigitalOut led1(LED1); //ticking (flashes)
-DigitalOut led2(LED2); //server listning status (flashes)
+DigitalOut led2(LED2); //
 DigitalOut led3(LED3); //socket connecting status
 DigitalOut led4(LED4); //server status
 
 // Web server
 EthernetInterface eth;
-NTPClient ntp;
 UDPSocket sendUDPSocket;
 Endpoint broadcastEndpoint;
 
+// Network Time Protocol (NTP)
+NTPClient ntp;
+const int NTP_REFRESH_INTERVAL_HOURS = 1;
+
 // File system for SD card
 SDFileSystem sd(p5, p6, p7, p8, "sd");
 
@@ -41,6 +45,28 @@
 const PinName tempSensorPins[] = { p22 };
 Thermometers thermometers(sizeof(tempSensorPins)/sizeof(PinName), tempSensorPins, LOOP_DELAY_IN_MS);
 
+// Voltage Sensors / Alerters
+const int NUM_VOLT_ALERTERS = 3;
+VoltAlerter voltAlerter1(p23);
+VoltAlerter voltAlerter2(p24);
+VoltAlerter voltAlerter3(p25);
+
+// Broadcast message
+const char broadcastMsgPrefix[] = "{\"e\":[";
+const char broadcastMsgGasFormat[] = "{\"n\":\"gasCount\",\"v\":%d},{\"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 broadcastMsgSuffix[] = "],\"bt\":%d}";
+
+// Format message
+const int broadcastMsgLen = sizeof(broadcastMsgPrefix) + 
+            sizeof(broadcastMsgGasFormat) + 
+            (sizeof(broadcastTemperatureFormat)*Thermometers::MAX_THERMOMETERS) + 
+            (sizeof(broadcastVoltAlerterFormat)*NUM_VOLT_ALERTERS) + 
+            sizeof(broadcastMsgSuffix) + 
+            60;
+char broadcastMsgBuffer[broadcastMsgLen];
+    
 // Utility function to log data
 void LogData(const char* format, ...)
 {
@@ -71,31 +97,54 @@
     // Get temperature values
     TemperatureValue tempValues[Thermometers::MAX_THERMOMETERS];
     int numTempValues = thermometers.GetTemperatureValues(Thermometers::MAX_THERMOMETERS, tempValues, 100);
-    char tempStr[200];
     for (int tempIdx = 0; tempIdx < numTempValues; tempIdx++)
     {
         printf("Temp: %.1f, Addr: %s, Time: %d\r\n", tempValues[tempIdx].tempInCentigrade, tempValues[tempIdx].address, tempValues[tempIdx].timeStamp);
     }
     
-    // Format message
-    char outBuf[200];
-    sprintf(outBuf, "{\"e\":[{\"n\":\"gasCount\",\"v\":%d},{\"n\":\"gasPulseRateMs\",\"v\":%d,\"u\":\"ms\"}]}", 
-                        gasUseCounter.GetCount(), gasUseCounter.GetPulseRateMs());
-    
+    // Data format of the message - senml - https://tools.ietf.org/html/draft-jennings-senml-08
+    // {
+    //     "e": [
+    //              {"n":"gasCount","v":%d}, 
+    //              {"n":"gasPulseRateMs","v":%d,"u":"ms"},
+    //              {"n":"temp_%s","v":%0.1f,"u":"degC"},
+    //              ...
+    //              {"n":"pump_%d","v":%d},
+    //              ...
+    //          ],
+    //      "bt": %d
+    // }
+
+    time_t timeNow = time(NULL);
+    strcpy(broadcastMsgBuffer, broadcastMsgPrefix);
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgGasFormat, gasUseCounter.GetCount(), gasUseCounter.GetPulseRateMs());
+    strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
+    for (int tempIdx = 0; tempIdx < numTempValues; tempIdx++)
+    {
+        sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastTemperatureFormat, tempValues[tempIdx].address, tempValues[tempIdx].tempInCentigrade);
+        strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
+    }
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 1, voltAlerter1.GetState());
+    strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 2, voltAlerter2.GetState());
+    strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 3, voltAlerter3.GetState());
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgSuffix, timeNow);
+        
     // Send
-    int bytesToSend = strlen(outBuf);
-    int rslt = sendUDPSocket.sendTo(broadcastEndpoint, outBuf, bytesToSend);
+    int bytesToSend = strlen(broadcastMsgBuffer);
+    int rslt = sendUDPSocket.sendTo(broadcastEndpoint, broadcastMsgBuffer, bytesToSend);
     if (rslt == bytesToSend)
     {
-        pc.printf("Broadcast Sent ok %s\n", outBuf);
+        pc.printf("Broadcast (len %d) Sent ok %s\r\n", bytesToSend, broadcastMsgBuffer);
     }
     else if (rslt == -1)
     {
-        pc.printf("Broadcast Failed to send %s\n", outBuf);
+        pc.printf("Broadcast Failed to send %s\r\n", broadcastMsgBuffer);
     }
     else
     {
-        pc.printf("Broadcast Didn't send all of %s\n", outBuf);
+        pc.printf("Broadcast Didn't send all of %s\r\n", broadcastMsgBuffer);
     }
     
     // Log
@@ -161,17 +210,21 @@
         else
         {
           printf("Cannot set from NTP\r\n");
-        } 
-        // 1 hour
-        for (int i = 0; i < 60; i++)
+        }
+        
+        // Refresh time every K hours
+        for (int k = 0; k < NTP_REFRESH_INTERVAL_HOURS; k++)
         {
-            for (int j = 0; j < 60; j++)
+            // 1 hour
+            for (int i = 0; i < 60; i++)
             {
-                osDelay(1000);
+                for (int j = 0; j < 60; j++)
+                {
+                    osDelay(1000);
+                }
+                pc.printf("%d mins to next NTP time refresh\r\n", (NTP_REFRESH_INTERVAL_HOURS-k-1)*60 + (59-i));
             }
-            pc.printf("%d mins since NTP\r\n", i);
         }
-        break;
     }
 }
 
@@ -180,6 +233,8 @@
     pc.baud(115200);
     pc.printf("Gas Monitor V2 - Rob Dobson 2014\r\n");
 
+    printf("Broadcast Msg Len %d\r\n", broadcastMsgLen);
+
 //    ticker.attach(&TickFunction,TICK_MS / 1000.0);
 
     // Initialise thermometers
@@ -224,6 +279,12 @@
             SendInfoBroadcast();
             timeOfLastBroadcast = time(NULL);
         }
+        
+        // Service volt alerters
+        voltAlerter1.Service();
+        voltAlerter2.Service();
+        voltAlerter3.Service();
+        led2 = voltAlerter1.GetState();
     }
 }