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