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

Committer:
Bobty
Date:
Sun Feb 22 20:23:43 2015 +0000
Revision:
10:72eb217def1f
Parent:
9:0e103c2f869a
Child:
11:30182b9aa833
Now sending complete broadcast message with gas count, temperatures and voltage sensing

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Bobty 0:f6611c8f453c 1 #include "mbed.h"
Bobty 0:f6611c8f453c 2 #include "EthernetInterface.h"
Bobty 5:5bccf48799d4 3 #include "NTPClient.h"
Bobty 5:5bccf48799d4 4 #include "RdWebServer.h"
Bobty 5:5bccf48799d4 5 #include "GasUseCounter.h"
Bobty 9:0e103c2f869a 6 #include "Thermometers.h"
Bobty 10:72eb217def1f 7 #include "VoltAlerter.h"
Bobty 5:5bccf48799d4 8 #include <stdarg.h>
Bobty 5:5bccf48799d4 9
Bobty 5:5bccf48799d4 10 // Web and UDB ports
Bobty 5:5bccf48799d4 11 const int WEBPORT = 80; // Port for web server
Bobty 5:5bccf48799d4 12 const int BROADCAST_PORT = 42853; // Arbitrarily chosen port number
Bobty 5:5bccf48799d4 13
Bobty 5:5bccf48799d4 14 // Ticker collects data
Bobty 6:b7064d33e402 15 //Ticker ticker;
Bobty 10:72eb217def1f 16 //const int TICK_MS = 50;
Bobty 9:0e103c2f869a 17 const int LOOP_DELAY_IN_MS = 250;
Bobty 5:5bccf48799d4 18
Bobty 5:5bccf48799d4 19 // Debugging and status
Bobty 5:5bccf48799d4 20 RawSerial pc(USBTX, USBRX);
Bobty 5:5bccf48799d4 21 DigitalOut led1(LED1); //ticking (flashes)
Bobty 10:72eb217def1f 22 DigitalOut led2(LED2); //
Bobty 5:5bccf48799d4 23 DigitalOut led3(LED3); //socket connecting status
Bobty 5:5bccf48799d4 24 DigitalOut led4(LED4); //server status
Bobty 5:5bccf48799d4 25
Bobty 5:5bccf48799d4 26 // Web server
Bobty 5:5bccf48799d4 27 EthernetInterface eth;
Bobty 5:5bccf48799d4 28 UDPSocket sendUDPSocket;
Bobty 5:5bccf48799d4 29 Endpoint broadcastEndpoint;
Bobty 5:5bccf48799d4 30
Bobty 10:72eb217def1f 31 // Network Time Protocol (NTP)
Bobty 10:72eb217def1f 32 NTPClient ntp;
Bobty 10:72eb217def1f 33 const int NTP_REFRESH_INTERVAL_HOURS = 1;
Bobty 10:72eb217def1f 34
Bobty 5:5bccf48799d4 35 // File system for SD card
Bobty 4:0d3a207680b0 36 SDFileSystem sd(p5, p6, p7, p8, "sd");
Bobty 5:5bccf48799d4 37
Bobty 5:5bccf48799d4 38 // Gas use counter
Bobty 5:5bccf48799d4 39 DigitalIn gasPulsePin(p21);
Bobty 5:5bccf48799d4 40 const char* gasPulseFileName = "/sd/curPulse.txt";
Bobty 5:5bccf48799d4 41 const char* logFilename = "/sd/log.txt";
Bobty 5:5bccf48799d4 42 GasUseCounter gasUseCounter(gasPulseFileName, gasPulsePin, pc);
Bobty 5:5bccf48799d4 43
Bobty 8:5980547ae71c 44 // Thermometers - DS18B20 OneWire Thermometer connections
Bobty 8:5980547ae71c 45 const PinName tempSensorPins[] = { p22 };
Bobty 9:0e103c2f869a 46 Thermometers thermometers(sizeof(tempSensorPins)/sizeof(PinName), tempSensorPins, LOOP_DELAY_IN_MS);
Bobty 8:5980547ae71c 47
Bobty 10:72eb217def1f 48 // Voltage Sensors / Alerters
Bobty 10:72eb217def1f 49 const int NUM_VOLT_ALERTERS = 3;
Bobty 10:72eb217def1f 50 VoltAlerter voltAlerter1(p23);
Bobty 10:72eb217def1f 51 VoltAlerter voltAlerter2(p24);
Bobty 10:72eb217def1f 52 VoltAlerter voltAlerter3(p25);
Bobty 10:72eb217def1f 53
Bobty 10:72eb217def1f 54 // Broadcast message
Bobty 10:72eb217def1f 55 const char broadcastMsgPrefix[] = "{\"e\":[";
Bobty 10:72eb217def1f 56 const char broadcastMsgGasFormat[] = "{\"n\":\"gasCount\",\"v\":%d},{\"n\":\"gasPulseRateMs\",\"v\":%d,\"u\":\"ms\"}";
Bobty 10:72eb217def1f 57 const char broadcastTemperatureFormat[] = "{\"n\":\"temp_%s\",\"v\":%0.1f,\"u\":\"degC\"}";
Bobty 10:72eb217def1f 58 const char broadcastVoltAlerterFormat[] = "{\"n\":\"pump_%d\",\"v\":%d}";
Bobty 10:72eb217def1f 59 const char broadcastMsgSuffix[] = "],\"bt\":%d}";
Bobty 10:72eb217def1f 60
Bobty 10:72eb217def1f 61 // Format message
Bobty 10:72eb217def1f 62 const int broadcastMsgLen = sizeof(broadcastMsgPrefix) +
Bobty 10:72eb217def1f 63 sizeof(broadcastMsgGasFormat) +
Bobty 10:72eb217def1f 64 (sizeof(broadcastTemperatureFormat)*Thermometers::MAX_THERMOMETERS) +
Bobty 10:72eb217def1f 65 (sizeof(broadcastVoltAlerterFormat)*NUM_VOLT_ALERTERS) +
Bobty 10:72eb217def1f 66 sizeof(broadcastMsgSuffix) +
Bobty 10:72eb217def1f 67 60;
Bobty 10:72eb217def1f 68 char broadcastMsgBuffer[broadcastMsgLen];
Bobty 10:72eb217def1f 69
Bobty 9:0e103c2f869a 70 // Utility function to log data
Bobty 5:5bccf48799d4 71 void LogData(const char* format, ...)
Bobty 5:5bccf48799d4 72 {
Bobty 5:5bccf48799d4 73 FILE* fp = fopen(logFilename, "a");
Bobty 5:5bccf48799d4 74 if (fp == NULL)
Bobty 5:5bccf48799d4 75 {
Bobty 5:5bccf48799d4 76 pc.printf ("Log ... Filename %s not found\r\n", logFilename);
Bobty 5:5bccf48799d4 77 }
Bobty 5:5bccf48799d4 78 else
Bobty 5:5bccf48799d4 79 {
Bobty 5:5bccf48799d4 80 va_list argptr;
Bobty 5:5bccf48799d4 81 va_start(argptr, format);
Bobty 5:5bccf48799d4 82 vfprintf(fp, format, argptr);
Bobty 5:5bccf48799d4 83 va_end(argptr);
Bobty 5:5bccf48799d4 84 fclose(fp);
Bobty 5:5bccf48799d4 85 }
Bobty 5:5bccf48799d4 86 }
Bobty 5:5bccf48799d4 87
Bobty 9:0e103c2f869a 88 // Send broadcast message with current data
Bobty 5:5bccf48799d4 89 void SendInfoBroadcast()
Bobty 5:5bccf48799d4 90 {
Bobty 5:5bccf48799d4 91 led3 = true;
Bobty 5:5bccf48799d4 92 // Init the sending socket
Bobty 5:5bccf48799d4 93 sendUDPSocket.init();
Bobty 5:5bccf48799d4 94 sendUDPSocket.set_broadcasting();
Bobty 5:5bccf48799d4 95 broadcastEndpoint.set_address("255.255.255.255", BROADCAST_PORT);
Bobty 5:5bccf48799d4 96
Bobty 9:0e103c2f869a 97 // Get temperature values
Bobty 9:0e103c2f869a 98 TemperatureValue tempValues[Thermometers::MAX_THERMOMETERS];
Bobty 9:0e103c2f869a 99 int numTempValues = thermometers.GetTemperatureValues(Thermometers::MAX_THERMOMETERS, tempValues, 100);
Bobty 9:0e103c2f869a 100 for (int tempIdx = 0; tempIdx < numTempValues; tempIdx++)
Bobty 9:0e103c2f869a 101 {
Bobty 9:0e103c2f869a 102 printf("Temp: %.1f, Addr: %s, Time: %d\r\n", tempValues[tempIdx].tempInCentigrade, tempValues[tempIdx].address, tempValues[tempIdx].timeStamp);
Bobty 9:0e103c2f869a 103 }
Bobty 9:0e103c2f869a 104
Bobty 10:72eb217def1f 105 // Data format of the message - senml - https://tools.ietf.org/html/draft-jennings-senml-08
Bobty 10:72eb217def1f 106 // {
Bobty 10:72eb217def1f 107 // "e": [
Bobty 10:72eb217def1f 108 // {"n":"gasCount","v":%d},
Bobty 10:72eb217def1f 109 // {"n":"gasPulseRateMs","v":%d,"u":"ms"},
Bobty 10:72eb217def1f 110 // {"n":"temp_%s","v":%0.1f,"u":"degC"},
Bobty 10:72eb217def1f 111 // ...
Bobty 10:72eb217def1f 112 // {"n":"pump_%d","v":%d},
Bobty 10:72eb217def1f 113 // ...
Bobty 10:72eb217def1f 114 // ],
Bobty 10:72eb217def1f 115 // "bt": %d
Bobty 10:72eb217def1f 116 // }
Bobty 10:72eb217def1f 117
Bobty 10:72eb217def1f 118 time_t timeNow = time(NULL);
Bobty 10:72eb217def1f 119 strcpy(broadcastMsgBuffer, broadcastMsgPrefix);
Bobty 10:72eb217def1f 120 sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgGasFormat, gasUseCounter.GetCount(), gasUseCounter.GetPulseRateMs());
Bobty 10:72eb217def1f 121 strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
Bobty 10:72eb217def1f 122 for (int tempIdx = 0; tempIdx < numTempValues; tempIdx++)
Bobty 10:72eb217def1f 123 {
Bobty 10:72eb217def1f 124 sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastTemperatureFormat, tempValues[tempIdx].address, tempValues[tempIdx].tempInCentigrade);
Bobty 10:72eb217def1f 125 strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
Bobty 10:72eb217def1f 126 }
Bobty 10:72eb217def1f 127 sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 1, voltAlerter1.GetState());
Bobty 10:72eb217def1f 128 strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
Bobty 10:72eb217def1f 129 sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 2, voltAlerter2.GetState());
Bobty 10:72eb217def1f 130 strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
Bobty 10:72eb217def1f 131 sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 3, voltAlerter3.GetState());
Bobty 10:72eb217def1f 132 sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgSuffix, timeNow);
Bobty 10:72eb217def1f 133
Bobty 5:5bccf48799d4 134 // Send
Bobty 10:72eb217def1f 135 int bytesToSend = strlen(broadcastMsgBuffer);
Bobty 10:72eb217def1f 136 int rslt = sendUDPSocket.sendTo(broadcastEndpoint, broadcastMsgBuffer, bytesToSend);
Bobty 5:5bccf48799d4 137 if (rslt == bytesToSend)
Bobty 5:5bccf48799d4 138 {
Bobty 10:72eb217def1f 139 pc.printf("Broadcast (len %d) Sent ok %s\r\n", bytesToSend, broadcastMsgBuffer);
Bobty 5:5bccf48799d4 140 }
Bobty 5:5bccf48799d4 141 else if (rslt == -1)
Bobty 5:5bccf48799d4 142 {
Bobty 10:72eb217def1f 143 pc.printf("Broadcast Failed to send %s\r\n", broadcastMsgBuffer);
Bobty 5:5bccf48799d4 144 }
Bobty 5:5bccf48799d4 145 else
Bobty 5:5bccf48799d4 146 {
Bobty 10:72eb217def1f 147 pc.printf("Broadcast Didn't send all of %s\r\n", broadcastMsgBuffer);
Bobty 5:5bccf48799d4 148 }
Bobty 5:5bccf48799d4 149
Bobty 5:5bccf48799d4 150 // Log
Bobty 5:5bccf48799d4 151 char timeBuf[32];
Bobty 5:5bccf48799d4 152 time_t seconds = time(NULL);
Bobty 5:5bccf48799d4 153 strftime(timeBuf, 32, "%Y-%m-%d %H:%M:%S", localtime(&seconds));
Bobty 5:5bccf48799d4 154 LogData("%s\t%d\t%d ms\n", timeBuf, gasUseCounter.GetCount(), gasUseCounter.GetPulseRateMs());
Bobty 5:5bccf48799d4 155
Bobty 5:5bccf48799d4 156 led3 = false;
Bobty 5:5bccf48799d4 157 }
Bobty 0:f6611c8f453c 158
Bobty 5:5bccf48799d4 159 // Ticker's tick function
Bobty 6:b7064d33e402 160 //void TickFunction()
Bobty 6:b7064d33e402 161 //{
Bobty 6:b7064d33e402 162 //}
Bobty 5:5bccf48799d4 163
Bobty 5:5bccf48799d4 164 char* getGasUseCallback(int method, char* cmdStr, char* argStr)
Bobty 5:5bccf48799d4 165 {
Bobty 5:5bccf48799d4 166 char* pResp = gasUseCounter.getGasUseCallback(cmdStr, argStr);
Bobty 5:5bccf48799d4 167 pc.printf("Returning gas use %s\r\n", pResp);
Bobty 5:5bccf48799d4 168 return pResp;
Bobty 5:5bccf48799d4 169 }
Bobty 5:5bccf48799d4 170
Bobty 5:5bccf48799d4 171 char* setGasUseCallback(int method, char* cmdStr, char* argStr)
Bobty 5:5bccf48799d4 172 {
Bobty 9:0e103c2f869a 173 pc.printf("Setting gas use count %s\r\n", argStr);
Bobty 5:5bccf48799d4 174 int newGasUse = 0;
Bobty 5:5bccf48799d4 175 char* eqStr = strchr(argStr, '=');
Bobty 5:5bccf48799d4 176 if (eqStr == NULL)
Bobty 5:5bccf48799d4 177 return "SetGasValue FAILED";
Bobty 5:5bccf48799d4 178 sscanf(eqStr+1, "%d", &newGasUse);
Bobty 5:5bccf48799d4 179 gasUseCounter.SetCount(newGasUse);
Bobty 5:5bccf48799d4 180 return "SetGasValue OK";
Bobty 5:5bccf48799d4 181 }
Bobty 5:5bccf48799d4 182
Bobty 5:5bccf48799d4 183 void http_thread(void const* arg)
Bobty 5:5bccf48799d4 184 {
Bobty 5:5bccf48799d4 185 char* baseWebFolder = "/sd/";
Bobty 5:5bccf48799d4 186
Bobty 5:5bccf48799d4 187 RdWebServer webServer;
Bobty 5:5bccf48799d4 188 webServer.addCommand("", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "index.htm", false);
Bobty 5:5bccf48799d4 189 webServer.addCommand("gear-gr.png", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, NULL, true);
Bobty 5:5bccf48799d4 190 webServer.addCommand("getgascount", RdWebServerCmdDef::CMD_CALLBACK, &getGasUseCallback);
Bobty 5:5bccf48799d4 191 webServer.addCommand("setgascount", RdWebServerCmdDef::CMD_CALLBACK, &setGasUseCallback);
Bobty 0:f6611c8f453c 192
Bobty 5:5bccf48799d4 193 webServer.init(WEBPORT, &led4, baseWebFolder);
Bobty 5:5bccf48799d4 194
Bobty 5:5bccf48799d4 195 webServer.run();
Bobty 5:5bccf48799d4 196 }
Bobty 5:5bccf48799d4 197
Bobty 5:5bccf48799d4 198 void ntp_thread(void const* arg)
Bobty 5:5bccf48799d4 199 {
Bobty 5:5bccf48799d4 200 while (1)
Bobty 5:5bccf48799d4 201 {
Bobty 5:5bccf48799d4 202 pc.printf("Trying to update time...\r\n");
Bobty 5:5bccf48799d4 203 if (ntp.setTime("0.pool.ntp.org") == 0)
Bobty 5:5bccf48799d4 204 {
Bobty 5:5bccf48799d4 205 printf("Set time successfully\r\n");
Bobty 5:5bccf48799d4 206 time_t ctTime;
Bobty 5:5bccf48799d4 207 ctTime = time(NULL);
Bobty 5:5bccf48799d4 208 printf("Time is set to (UTC): %s\r\n", ctime(&ctTime));
Bobty 5:5bccf48799d4 209 }
Bobty 5:5bccf48799d4 210 else
Bobty 5:5bccf48799d4 211 {
Bobty 5:5bccf48799d4 212 printf("Cannot set from NTP\r\n");
Bobty 10:72eb217def1f 213 }
Bobty 10:72eb217def1f 214
Bobty 10:72eb217def1f 215 // Refresh time every K hours
Bobty 10:72eb217def1f 216 for (int k = 0; k < NTP_REFRESH_INTERVAL_HOURS; k++)
Bobty 5:5bccf48799d4 217 {
Bobty 10:72eb217def1f 218 // 1 hour
Bobty 10:72eb217def1f 219 for (int i = 0; i < 60; i++)
Bobty 5:5bccf48799d4 220 {
Bobty 10:72eb217def1f 221 for (int j = 0; j < 60; j++)
Bobty 10:72eb217def1f 222 {
Bobty 10:72eb217def1f 223 osDelay(1000);
Bobty 10:72eb217def1f 224 }
Bobty 10:72eb217def1f 225 pc.printf("%d mins to next NTP time refresh\r\n", (NTP_REFRESH_INTERVAL_HOURS-k-1)*60 + (59-i));
Bobty 5:5bccf48799d4 226 }
Bobty 5:5bccf48799d4 227 }
Bobty 5:5bccf48799d4 228 }
Bobty 5:5bccf48799d4 229 }
Bobty 9:0e103c2f869a 230
Bobty 0:f6611c8f453c 231 int main()
Bobty 0:f6611c8f453c 232 {
Bobty 0:f6611c8f453c 233 pc.baud(115200);
Bobty 5:5bccf48799d4 234 pc.printf("Gas Monitor V2 - Rob Dobson 2014\r\n");
Bobty 0:f6611c8f453c 235
Bobty 10:72eb217def1f 236 printf("Broadcast Msg Len %d\r\n", broadcastMsgLen);
Bobty 10:72eb217def1f 237
Bobty 6:b7064d33e402 238 // ticker.attach(&TickFunction,TICK_MS / 1000.0);
Bobty 5:5bccf48799d4 239
Bobty 9:0e103c2f869a 240 // Initialise thermometers
Bobty 9:0e103c2f869a 241 thermometers.Init();
Bobty 9:0e103c2f869a 242
Bobty 5:5bccf48799d4 243 // Get the current count from the SD Card
Bobty 5:5bccf48799d4 244 gasUseCounter.Init();
Bobty 0:f6611c8f453c 245
Bobty 5:5bccf48799d4 246 // setup ethernet interface
Bobty 5:5bccf48799d4 247 eth.init(); //Use DHCP
Bobty 5:5bccf48799d4 248 eth.connect();
Bobty 2:6bfef0839102 249
Bobty 9:0e103c2f869a 250 pc.printf("IP Address is %s\r\n", eth.getIPAddress());
Bobty 8:5980547ae71c 251
Bobty 8:5980547ae71c 252 // NTP Time setter
Bobty 5:5bccf48799d4 253 Thread ntpTimeSetter(&ntp_thread);
Bobty 0:f6611c8f453c 254
Bobty 8:5980547ae71c 255 // Web Server
Bobty 6:b7064d33e402 256 Thread httpServer(&http_thread, NULL, osPriorityNormal, (DEFAULT_STACK_SIZE * 3));
Bobty 5:5bccf48799d4 257
Bobty 9:0e103c2f869a 258 // Time of last broadcast
Bobty 9:0e103c2f869a 259 time_t timeOfLastBroadcast = time(NULL);
Bobty 9:0e103c2f869a 260 const int TIME_BETWEEN_BROADCASTS_IN_SECS = 60;
Bobty 5:5bccf48799d4 261 while(true)
Bobty 0:f6611c8f453c 262 {
Bobty 9:0e103c2f869a 263 osDelay(LOOP_DELAY_IN_MS);
Bobty 5:5bccf48799d4 264 led1 = !led1;
Bobty 5:5bccf48799d4 265
Bobty 5:5bccf48799d4 266 // Service gas count
Bobty 5:5bccf48799d4 267 if (gasUseCounter.Service())
Bobty 8:5980547ae71c 268 {
Bobty 9:0e103c2f869a 269 SendInfoBroadcast();
Bobty 9:0e103c2f869a 270 timeOfLastBroadcast = time(NULL);
Bobty 9:0e103c2f869a 271 }
Bobty 8:5980547ae71c 272
Bobty 9:0e103c2f869a 273 // Service thermometers
Bobty 9:0e103c2f869a 274 thermometers.Service();
Bobty 9:0e103c2f869a 275
Bobty 9:0e103c2f869a 276 // Check if ready for a broadcast
Bobty 9:0e103c2f869a 277 if ((time(NULL) - timeOfLastBroadcast) >= TIME_BETWEEN_BROADCASTS_IN_SECS)
Bobty 9:0e103c2f869a 278 {
Bobty 9:0e103c2f869a 279 SendInfoBroadcast();
Bobty 9:0e103c2f869a 280 timeOfLastBroadcast = time(NULL);
Bobty 8:5980547ae71c 281 }
Bobty 10:72eb217def1f 282
Bobty 10:72eb217def1f 283 // Service volt alerters
Bobty 10:72eb217def1f 284 voltAlerter1.Service();
Bobty 10:72eb217def1f 285 voltAlerter2.Service();
Bobty 10:72eb217def1f 286 voltAlerter3.Service();
Bobty 10:72eb217def1f 287 led2 = voltAlerter1.GetState();
Bobty 0:f6611c8f453c 288 }
Bobty 5:5bccf48799d4 289 }
Bobty 9:0e103c2f869a 290