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 5:5bccf48799d4, committed 2015-02-17
- Comitter:
- Bobty
- Date:
- Tue Feb 17 21:33:39 2015 +0000
- Parent:
- 4:0d3a207680b0
- Child:
- 6:b7064d33e402
- Commit message:
- Tidied up - but is unstable - web server crashes after some time
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GasUseCounter.cpp Tue Feb 17 21:33:39 2015 +0000 @@ -0,0 +1,71 @@ +#include "GasUseCounter.h" + +// Callback from web server to handle getting current gas count +char* GasUseCounter::getGasUseCallback(char* cmdStr, char* argStr) +{ + sprintf(_gasCountStr, "{\"gasUseCount\":\"%d\"}", GetCount()); + return _gasCountStr; +} + +// Init +void GasUseCounter::Init() +{ + GetGasCountFromSD(); +} + +// Service function +bool GasUseCounter::Service() +{ + // Check for an edge + bool edgeDetected = _pulseDetector->Service(); + if (edgeDetected) + { + // Check if we need to store in non-volatile storage + if (GetCount() >= _lastWrittenGasCount + MAX_PULSES_BEFORE_STORE_NV) + { + WriteGasCountToSD(); + _lastWrittenGasCount = GetCount(); + } + + // Show count + _pc.printf("Count %d Rate(ms) %d\r\n", _pulseDetector->GetPulseCount(), _pulseDetector->GetPulseRateMs()); + } + return edgeDetected; +} + +// Get the current usage count from SD card +void GasUseCounter::GetGasCountFromSD() +{ + FILE* fp = fopen(_gasUseFilename, "r"); + if (fp == NULL) + { + _pc.printf ("Read Gas ... Filename %s not found\r\n", _gasUseFilename); + } + else + { + int curCount = 0; + int retVal = fscanf(fp, "%d", &curCount); + fclose(fp); + if (retVal == 1) + _pc.printf ("Read from SD last gas count = %d\r\n", curCount); + else + _pc.printf ("Failed to read gas count from SD card\r\n"); + _lastWrittenGasCount = curCount; + _pulseDetector->SetPulseCount(curCount); + } +} + +void GasUseCounter::WriteGasCountToSD() +{ + FILE* fp = fopen(_gasUseFilename, "w"); + if (fp == NULL) + { + _pc.printf ("WriteGas ... Filename %s not found\r\n", _gasUseFilename); + } + else + { + fprintf(fp, "%d", GetCount()); + _pc.printf ("Written to SD last gas count = %d\r\n", GetCount()); + fclose(fp); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GasUseCounter.h Tue Feb 17 21:33:39 2015 +0000 @@ -0,0 +1,69 @@ +#ifndef __GASUSECOUNTER__H +#define __GASUSECOUNTER__H +#include "mbed.h" +#include "PulsePin.h" +#include "SDFileSystem.h" +#include <RawSerial.h> + +const int MAX_GAS_COUNT_STR_LEN = 50; +const int MAX_PULSES_BEFORE_STORE_NV = 1; // Store at each pulse + +class GasUseCounter +{ + public: + // Constructor + GasUseCounter(const char* gasUseFilename, DigitalIn& gasPulsePin, RawSerial &pc) : + _gasPulsePin(gasPulsePin), _pc(pc) + { + _gasUseFilename = gasUseFilename; + _lastWrittenGasCount = 0; + _pulseDetector = new PulsePin(_gasPulsePin, false, 200); + } + + // Init (get count from NV) + void Init(); + + // Callback from web server to handle getting current gas count + char* getGasUseCallback(char* cmdStr, char* argStr); + + // Service function to detect pulses + bool Service(); + + // Read/Write current gas count + void GetGasCountFromSD(); + void WriteGasCountToSD(); + + // Get Count + int GetCount() + { + return _pulseDetector->GetPulseCount(); + } + + // Set Count + void SetCount(int gasUseCount) + { + _pulseDetector->SetPulseCount(gasUseCount); + WriteGasCountToSD(); + } + + // Get inter-pulse time + int GetPulseRateMs() + { + return _pulseDetector->GetPulseRateMs(); + } + + private: + // Gas use filename for non-volatile + const char* _gasUseFilename; + int _curGasCount; + int _lastWrittenGasCount; + char _gasCountStr [MAX_GAS_COUNT_STR_LEN]; + DigitalIn& _gasPulsePin; + PulsePin* _pulseDetector; + RawSerial& _pc; +}; + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NTPClient.lib Tue Feb 17 21:33:39 2015 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/donatien/code/NTPClient/#881559865a93
--- a/PulsePin.cpp Mon Feb 02 16:24:30 2015 +0000 +++ b/PulsePin.cpp Tue Feb 17 21:33:39 2015 +0000 @@ -13,12 +13,27 @@ _lastStableTimeMs = _pinTimer.read_ms(); _firstEdgeDetected = false; _timeBetweenEdgesMs = 0; + _pulseCount = 0; + _pinTimerMinutes = 0; } bool PulsePin::Service() { // Check time since last edge - looking for stability int timeNowMs = _pinTimer.read_ms(); + + // Check if over 1 minute (as the timer wraps around after 30 mins) + if (timeNowMs > 60000) + { + _pinTimerMinutes++; + _pinTimer.reset(); + timeNowMs -= 60000; + } + + // Get the real time elapsed (still wraps but now only after about 500 hours) + timeNowMs = _pinTimerMinutes*60000 + timeNowMs; + + // Check for pin stabilization if (timeNowMs < _lastStableTimeMs + _waitForPinStabilisationMs) return false; @@ -36,6 +51,7 @@ // Reset the timer to avoid wrap around problems bool firstEdgeDetected = _firstEdgeDetected; + _pinTimerMinutes = 0; _pinTimer.reset(); _firstEdgeDetected = true; _lastStableTimeMs = 0; @@ -44,10 +60,21 @@ if (!firstEdgeDetected) return false; _timeBetweenEdgesMs = timeNowMs; + _pulseCount++; return true; } -int PulsePin::GetLastCycleTimeMs() +int PulsePin::GetPulseRateMs() { return _timeBetweenEdgesMs; } + +int PulsePin::GetPulseCount() +{ + return _pulseCount; +} + +void PulsePin::SetPulseCount(int pulseCount) +{ + _pulseCount = pulseCount; +}
--- a/PulsePin.h Mon Feb 02 16:24:30 2015 +0000 +++ b/PulsePin.h Tue Feb 17 21:33:39 2015 +0000 @@ -7,8 +7,10 @@ public: PulsePin(DigitalIn& pin, bool risingEdge, int pinStableTimeMs); bool Service(); - int GetLastCycleTimeMs(); - + int GetPulseRateMs(); + int GetPulseCount(); + void SetPulseCount(int pulseCount); + private: DigitalIn& _pin; Timer _pinTimer; @@ -18,6 +20,8 @@ int _waitForPinStabilisationMs; bool _detectRisingEdge; int _timeBetweenEdgesMs; + int _pulseCount; + int _pinTimerMinutes; };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RdWebServer.lib Tue Feb 17 21:33:39 2015 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/Bobty/code/RdWebServer/#fe7c33f7fbb8
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDFileSystem-RTOS.lib Tue Feb 17 21:33:39 2015 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/Sissors/code/SDFileSystem-RTOS/#da69a01b8096
--- a/SDFileSystem.lib Mon Feb 02 16:24:30 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://developer.mbed.org/users/mbed_official/code/SDFileSystem/#7b35d1709458
--- a/main.cpp Mon Feb 02 16:24:30 2015 +0000 +++ b/main.cpp Tue Feb 17 21:33:39 2015 +0000 @@ -1,93 +1,189 @@ #include "mbed.h" #include "EthernetInterface.h" -#include "PulsePin.h" -#include "SDFileSystem.h" - +#include "NTPClient.h" +#include "RdWebServer.h" +#include "GasUseCounter.h" +#include <stdarg.h> + +// Web and UDB ports +const int WEBPORT = 80; // Port for web server +const int BROADCAST_PORT = 42853; // Arbitrarily chosen port number + +// Ticker collects data +Ticker ticker; +const int TICK_MS = 250; + +// Debugging and status +RawSerial pc(USBTX, USBRX); +DigitalOut led1(LED1); //ticking (flashes) +DigitalOut led2(LED2); //server listning status (flashes) +DigitalOut led3(LED3); //socket connecting status +DigitalOut led4(LED4); //server status + +// Web server +EthernetInterface eth; +NTPClient ntp; +UDPSocket sendUDPSocket; +Endpoint broadcastEndpoint; + +// File system for SD card SDFileSystem sd(p5, p6, p7, p8, "sd"); -DigitalIn gpPin(p21); -PulsePin pulsePin(gpPin, false, 200); -DigitalOut led(LED1); + +// Gas use counter +DigitalIn gasPulsePin(p21); +const char* gasPulseFileName = "/sd/curPulse.txt"; +const char* logFilename = "/sd/log.txt"; +GasUseCounter gasUseCounter(gasPulseFileName, gasPulsePin, pc); + +void LogData(const char* format, ...) +{ + FILE* fp = fopen(logFilename, "a"); + if (fp == NULL) + { + pc.printf ("Log ... Filename %s not found\r\n", logFilename); + } + else + { + va_list argptr; + va_start(argptr, format); + vfprintf(fp, format, argptr); + va_end(argptr); + fclose(fp); + } +} + +void SendInfoBroadcast() +{ + led3 = true; + // Init the sending socket + sendUDPSocket.init(); + sendUDPSocket.set_broadcasting(); + broadcastEndpoint.set_address("255.255.255.255", BROADCAST_PORT); + + // Format message + char outBuf[200]; + sprintf(outBuf, "{\"e\":[{\"n\":\"gasCount\",\"v\":%d},{\"n\":\"gasPulseRateMs\",\"v\":%d,\"u\":\"ms\"}]}", + gasUseCounter.GetCount(), gasUseCounter.GetPulseRateMs()); + + // Send + int bytesToSend = strlen(outBuf); + int rslt = sendUDPSocket.sendTo(broadcastEndpoint, outBuf, bytesToSend); + if (rslt == bytesToSend) + { + pc.printf("Broadcast Sent ok %s\n", outBuf); + } + else if (rslt == -1) + { + pc.printf("Broadcast Failed to send %s\n", outBuf); + } + else + { + pc.printf("Broadcast Didn't send all of %s\n", outBuf); + } + + // Log + char timeBuf[32]; + time_t seconds = time(NULL); + strftime(timeBuf, 32, "%Y-%m-%d %H:%M:%S", localtime(&seconds)); + LogData("%s\t%d\t%d ms\n", timeBuf, gasUseCounter.GetCount(), gasUseCounter.GetPulseRateMs()); + + led3 = false; +} -const int BROADCAST_PORT = 42853; // Arbitrarily chosen port number -Serial pc(USBTX, USBRX); +// Ticker's tick function +void TickFunction() +{ +} + +char* getGasUseCallback(int method, char* cmdStr, char* argStr) +{ + char* pResp = gasUseCounter.getGasUseCallback(cmdStr, argStr); + pc.printf("Returning gas use %s\r\n", pResp); + return pResp; +} + +char* setGasUseCallback(int method, char* cmdStr, char* argStr) +{ + pc.printf("Setting gas use count %s\n\r", argStr); + int newGasUse = 0; + char* eqStr = strchr(argStr, '='); + if (eqStr == NULL) + return "SetGasValue FAILED"; + sscanf(eqStr+1, "%d", &newGasUse); + gasUseCounter.SetCount(newGasUse); + return "SetGasValue OK"; +} + +void http_thread(void const* arg) +{ + char* baseWebFolder = "/sd/"; + + RdWebServer webServer; + webServer.addCommand("", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "index.htm", false); + webServer.addCommand("gear-gr.png", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, NULL, true); + webServer.addCommand("getgascount", RdWebServerCmdDef::CMD_CALLBACK, &getGasUseCallback); + webServer.addCommand("setgascount", RdWebServerCmdDef::CMD_CALLBACK, &setGasUseCallback); + webServer.init(WEBPORT, &led4, baseWebFolder); + + webServer.run(); +} + +void ntp_thread(void const* arg) +{ + while (1) + { + pc.printf("Trying to update time...\r\n"); + if (ntp.setTime("0.pool.ntp.org") == 0) + { + printf("Set time successfully\r\n"); + time_t ctTime; + ctTime = time(NULL); + printf("Time is set to (UTC): %s\r\n", ctime(&ctTime)); + } + else + { + printf("Cannot set from NTP\r\n"); + } + // 1 hour + for (int i = 0; i < 60; i++) + { + for (int j = 0; j < 60; j++) + { + osDelay(1000); + } + pc.printf("Waited %d mins\r\n", i); + } + } +} + int main() { pc.baud(115200); - printf("Gas Monitor - Rob Dobson 2014\n"); + pc.printf("Gas Monitor V2 - Rob Dobson 2014\r\n"); - EthernetInterface eth; - eth.init(); //Use DHCP - UDPSocket sendSocket; - Endpoint broadcast; + ticker.attach(&TickFunction,TICK_MS / 1000.0); + + // Get the current count from the SD Card + gasUseCounter.Init(); - // Connection establishment/re-establishment - Timer connectRetryTimer; - connectRetryTimer.start(); - bool isConnected = false; + // setup ethernet interface + eth.init(); //Use DHCP + eth.connect(); - // Count of gas pulses - int gasCount = 0; + pc.printf("IP Address is %s\n\r", eth.getIPAddress()); + + Thread ntpTimeSetter(&ntp_thread); - // Forever - while (true) + Thread httpServer(&http_thread, NULL, osPriorityNormal, (DEFAULT_STACK_SIZE * 2.25)); + + while(true) { - // Check if already connected to ethernet - if (!isConnected) - { - if (connectRetryTimer.read_ms() > 1000) - { - isConnected = eth.connect() == 0; - connectRetryTimer.reset(); - if (isConnected) - { - printf("Eth Connected - IP Address is %s - MAC is %s\n", eth.getIPAddress(), eth.getMACAddress()); - sendSocket.init(); - sendSocket.set_broadcasting(); - broadcast.set_address("255.255.255.255", BROADCAST_PORT); - } - else - { - printf("Eth Connect Attempt Failed\n"); - } - } - } - else - { - led = gpPin; - // Check for an edge - bool edgeDetected = pulsePin.Service(); - if (edgeDetected) - { - gasCount++; - char outBuf[200]; - sprintf(outBuf, "{\"e\":[{\"n\":\"gasCount\",\"v\":%d},{\"n\":\"gasInterPulse\",\"v\":%d,\"u\":\"ms\"}]}", - gasCount, pulsePin.GetLastCycleTimeMs()); - int bytesToSend = strlen(outBuf); - int rslt = sendSocket.sendTo(broadcast, outBuf, bytesToSend); - if (rslt == bytesToSend) - { - printf("Sent ok %s\n", outBuf); - } - else if (rslt == -1) - { - printf("Failed to send %s\n", outBuf); - isConnected = false; - } - else - { - printf("Didn't send all of %s\n", outBuf); - isConnected = false; - } - } - - // See if anything has failed - if (!isConnected) - { - sendSocket.close(); - eth.disconnect(); - Thread::wait(1000); - } - } + osDelay(250); + led1 = !led1; + + // Service gas count + if (gasUseCounter.Service()) + SendInfoBroadcast(); } -} \ No newline at end of file +}
--- a/mbed-rtos.lib Mon Feb 02 16:24:30 2015 +0000 +++ b/mbed-rtos.lib Tue Feb 17 21:33:39 2015 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/mbed_official/code/mbed-rtos/#db1fc233faa9 +http://mbed.org/users/mbed_official/code/mbed-rtos/#5448826aa700
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-src.lib Tue Feb 17 21:33:39 2015 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/mbed_official/code/mbed-src/#e03396e14338
--- a/mbed.bld Mon Feb 02 16:24:30 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/7e6c9f46b3bd \ No newline at end of file