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

main.cpp

Committer:
Bobty
Date:
2015-02-21
Revision:
8:5980547ae71c
Parent:
7:113c68639d10
Child:
9:0e103c2f869a

File content as of revision 8:5980547ae71c:

#include "mbed.h"
#include "EthernetInterface.h"
#include "NTPClient.h"
#include "RdWebServer.h"
#include "GasUseCounter.h"
#include "RdDS18B20.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");

// Gas use counter
DigitalIn gasPulsePin(p21);
const char* gasPulseFileName = "/sd/curPulse.txt";
const char* logFilename = "/sd/log.txt";
GasUseCounter gasUseCounter(gasPulseFileName, gasPulsePin, pc);

// Thermometers - DS18B20 OneWire Thermometer connections
const PinName tempSensorPins[] = { p22 };
const int NUM_THERM_BUSES = sizeof(tempSensorPins)/sizeof(int);
DS18B20* thermometerBuses[NUM_THERM_BUSES];

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;
}

// 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);
        }
        break;
    }
}
    
int main()
{
    pc.baud(115200);
    pc.printf("Gas Monitor V2 - Rob Dobson 2014\r\n");

//    ticker.attach(&TickFunction,TICK_MS / 1000.0);

    // Setup the thermometers
    for (int thermIdx = 0; thermIdx < NUM_THERM_BUSES; thermIdx++)
        thermometerBuses[thermIdx] = new DS18B20(tempSensorPins[thermIdx]);
        
    // Initialise thermometers   
    for (int thermIdx = 0; thermIdx < NUM_THERM_BUSES; thermIdx++)
    {
        DS18B20* pThermBus = thermometerBuses[thermIdx];
        pThermBus->SearchToGetAddresses();
        pThermBus->ReqConvert();
    }

    // Get the current count from the SD Card
    gasUseCounter.Init();
    
    // setup ethernet interface
    eth.init(); //Use DHCP
    eth.connect();
    
    pc.printf("IP Address is %s\n\r", eth.getIPAddress());
    
    // NTP Time setter
    Thread ntpTimeSetter(&ntp_thread);
    
    // Web Server
    Thread httpServer(&http_thread, NULL, osPriorityNormal, (DEFAULT_STACK_SIZE * 3));
    
    const int loopDelayInMs = 250;
    const int numSecondsBetweenThermReadings = 10;
    const int numLoopsPerThermReading = numSecondsBetweenThermReadings*1000/loopDelayInMs;
    const int timeForThermReadingInSecs = 2;
    const int loopCountForRequestingThermReading = numLoopsPerThermReading - (timeForThermReadingInSecs*1000/loopDelayInMs);
    int countForThermReadings = 0;
    const int reGetThermometerAddressesAfterNumReadings = 100;
    int countForGetThermometerAddresses = 0;
    while(true)
    {
        osDelay(loopDelayInMs);
        led1 = !led1;

        // Service gas count
        if (gasUseCounter.Service())
            SendInfoBroadcast();
        
        // Check if thermometer addresses need to be got
        if (countForThermReadings++ == 0)
        {
            if (countForGetThermometerAddresses++ == 0)
            {
                printf("Requested Addresses\n\r");
                for (int thermIdx = 0; thermIdx < NUM_THERM_BUSES; thermIdx++)
                {
                    DS18B20* pThermBus = thermometerBuses[thermIdx];
                    pThermBus->SearchToGetAddresses();
                }
            }
            else if (countForGetThermometerAddresses > reGetThermometerAddressesAfterNumReadings)
            {
                countForGetThermometerAddresses = 0;
            }
        }
        else
        {
            // Check if time to request thermometer readings
            if (countForThermReadings == loopCountForRequestingThermReading)
            {
                printf("Requested Convert\n\r");
                for (int thermIdx = 0; thermIdx < NUM_THERM_BUSES; thermIdx++)
                {
                    DS18B20* pThermBus = thermometerBuses[thermIdx];
                    printf("Bus %d Num therms %d\n\r", thermIdx, pThermBus->GetNumAddresses());
                    pThermBus->ReqConvert();
                }                
            }
        
            // Read thermometers
            if (countForThermReadings > numLoopsPerThermReading)
            {
                countForThermReadings = 0;
                printf("Reading Temp\n\r");
                for (int thermIdx = 0; thermIdx < NUM_THERM_BUSES; thermIdx++)
                {
                    DS18B20* pThermBus = thermometerBuses[thermIdx];
                    for (int addrIdx = 0; addrIdx < pThermBus->GetNumAddresses(); addrIdx++)
                    {
                        printf("Bus %d Therm %d === %.2fC ... Addr = ", thermIdx, addrIdx, pThermBus->GetTemperature(addrIdx));
                        pThermBus->DebugPrintAddress(addrIdx);
                        printf("\r\n");
                    }
                }                
            }
        }
    }
}