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:
21:ccf053bab795
Parent:
20:7933076df5af
--- a/main.cpp	Tue Oct 13 18:35:20 2015 +0000
+++ b/main.cpp	Fri Oct 16 08:37:30 2015 +0000
@@ -46,6 +46,9 @@
 // File system for SD card
 SDFileSystem sd(p5, p6, p7, p8, "sd");
 
+// Base folder for web file system
+char* baseWebFolder = "/sd/";
+
 // Log file names
 const char* gasPulseFileName1 = "/sd/curPulse.txt";
 const char* gasPulseFileName2 = "/sd/curPulse2.txt";
@@ -87,19 +90,68 @@
 // }
 const char broadcastMsgPrefix[] = "{\"e\":[";
 const char broadcastMsgGasFormat[] = "{\"n\":\"gasCount\",\"v\":%d,\"u\":\"count\"},{\"n\":\"gasPulseRateMs\",\"v\":%d,\"u\":\"ms\"}";
-const char broadcastTemperatureFormat[] = "{\"n\":\"temp_%s\",\"v\":%0.1f,\"u\":\"degC\"}";
-const char broadcastVoltAlerterFormat[] = "{\"n\":\"pump_%d\",\"bv\":%d}";
+const char broadcastTemperatureFormat[] = "{\"n\":\"temp_%s_%s\",\"v\":%0.1f,\"u\":\"degC\"}";
+const char broadcastVoltAlerterFormat[] = "{\"n\":\"pump_%d_%s\",\"bv\":%d}";
 const char broadcastMsgSuffix[] = "],\"bt\":%d}";
 
 // Broadcast message length and buffer
+const int MAX_DEVICE_NAME_LEN = 20;
 const int broadcastMsgLen = sizeof(broadcastMsgPrefix) + 
             sizeof(broadcastMsgGasFormat) + 
-            (sizeof(broadcastTemperatureFormat)*Thermometers::MAX_THERMOMETERS) + 
-            (sizeof(broadcastVoltAlerterFormat)*NUM_VOLT_ALERTERS) + 
+            ((sizeof(broadcastTemperatureFormat)+MAX_DEVICE_NAME_LEN)*Thermometers::MAX_THERMOMETERS) + 
+            ((sizeof(broadcastVoltAlerterFormat)+MAX_DEVICE_NAME_LEN)*NUM_VOLT_ALERTERS) + 
             sizeof(broadcastMsgSuffix) + 
             60;
 char broadcastMsgBuffer[broadcastMsgLen];
-    
+
+// Handling of SD card file delete and file upload commands
+const int MAX_FNAME_LENGTH = 127;
+char fileNameForWrite[MAX_FNAME_LENGTH+1] = "";
+int fileRemainingForWrite = 0;
+
+// Thermometer and pump names
+char thermometerAddrs[][MAX_DEVICE_NAME_LEN] =
+{
+    "28b1b1e0050000d0",
+    "289dd7c705000060",
+    "28b3b0c60500000a"
+};
+char thermometerNames[][MAX_DEVICE_NAME_LEN] =
+{
+    "HWCylinder",
+    "Inflow2",
+    "Outflow2"
+};
+int numNamedThermometers = 3;
+
+char voltAlerterNames[][MAX_DEVICE_NAME_LEN] =
+{
+    "PumpUpper",
+    "PumpLower",
+    "PumpHW"
+};
+
+// Get names of thermometers
+char* getThermometerName(char* thermAddr)
+{
+    for (int i = 0; i < numNamedThermometers; i++)
+    {
+        if (strcmp(thermometerAddrs[i], thermAddr) == 0)
+            return thermometerNames[i];
+    }
+    return "Unknown";
+}
+
+// Get names of volt alerters
+char* getVoltAlerterName(int idx)
+{
+    if (idx >= 0 && idx < NUM_VOLT_ALERTERS)
+    {
+        return voltAlerterNames[idx];
+    }
+    return "Unknown";
+}
+
 // Format broadcast message
 void GenBroadcastMessage()
 {
@@ -118,14 +170,16 @@
     strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
     for (int tempIdx = 0; tempIdx < numTempValues; tempIdx++)
     {
-        sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastTemperatureFormat, tempValues[tempIdx].address, tempValues[tempIdx].tempInCentigrade);
+        sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastTemperatureFormat, tempValues[tempIdx].address, 
+                        getThermometerName(tempValues[tempIdx].address),
+                        tempValues[tempIdx].tempInCentigrade);
         strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
     }
-    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 1, voltAlerter1.GetState());
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 1, getVoltAlerterName(0), voltAlerter1.GetState());
     strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
-    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 2, voltAlerter2.GetState());
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 2, getVoltAlerterName(1), voltAlerter2.GetState());
     strcpy(broadcastMsgBuffer+strlen(broadcastMsgBuffer), ",");
-    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 3, voltAlerter3.GetState());
+    sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastVoltAlerterFormat, 3, getVoltAlerterName(2), voltAlerter3.GetState());
     sprintf(broadcastMsgBuffer+strlen(broadcastMsgBuffer), broadcastMsgSuffix, timeNow);
 }
 
@@ -185,16 +239,192 @@
     return "SetGasValue OK";
 }
 
+// Handle SD Card File Delete
+char* sdCardDelete(int method, char* cmdStr, char* argStr, char* msgBuffer, int msgLen, 
+                int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
+{
+    printf("Delete file %s\r\n", argStr);
+    sdCardMutex.lock();
+    char* baseWebFolder = "/sd/";
+    char filename[MAX_FNAME_LENGTH+1];
+    sprintf(filename, "%s%s", baseWebFolder, argStr);
+    printf("Full filename for delete is %s\r\n", filename);
+    bool delOk = (remove(filename) == 0);
+    sdCardMutex.unlock();
+    if (delOk)
+        return "Delete OK";
+    return "Delete Failed";
+}
+
+// Handle SD Card File Upload
+char* sdCardUpload(int method, char* cmdStr, char* argStr, char* msgBuffer, int msgLen, 
+                int contentLen, unsigned char* pPayload, int payloadLen, int splitPayloadPos)
+{
+    printf("Upload file %s PayloadLen %d ContentLen %d MsgLen %d\r\n", argStr, payloadLen, contentLen, msgLen);
+    if (payloadLen == 0)
+    {
+        printf("Upload file - payload len == 0 - quitting\r\n");
+        return "OK";
+    }
+    
+    bool writeSuccess = false;
+    if (splitPayloadPos == 0)
+    {
+        // Get the filename
+        fileNameForWrite[0] = '\0';
+        fileRemainingForWrite = 0;
+        char* filenameText = " filename=\"";
+        char* pFilename = strstr((char*)pPayload, filenameText);
+        if ((pFilename != NULL) && (pFilename - ((char*)pPayload) < 200))
+        {
+            pFilename += strlen(filenameText);
+            char* pEndFileName = strstr(pFilename, "\"");
+            if (pEndFileName != NULL)
+            {
+                int fileNameLen = pEndFileName - pFilename;
+                if (fileNameLen + strlen(baseWebFolder) < MAX_FNAME_LENGTH)
+                {
+                    strcpy(fileNameForWrite, baseWebFolder);
+                    strncpy(fileNameForWrite+strlen(baseWebFolder), pFilename, fileNameLen);
+                    fileNameForWrite[fileNameLen+strlen(baseWebFolder)] = '\0';
+                    printf("Upload file - filename %s\r\n", fileNameForWrite);
+                }
+                else
+                {
+                    printf("Upload file - filename too long - quitting\r\n");
+                }
+            }
+            else
+            {
+                printf("Upload file - end of filename not found - quitting\r\n");
+            }
+        }
+        else
+        {
+            printf("Upload file - filename not found (or not near start of message) - quitting\r\n");
+        }
+        // Write first chunk to file
+        if (strlen(fileNameForWrite) > 0)
+        {
+            // Find the chunk start
+            char* chunkStartText = "\r\n\r\n";
+            char* pChunk = strstr((char*)pPayload, chunkStartText);
+            if ((pChunk != NULL) && (pChunk - ((char*)pPayload) < 300))
+            {
+                pChunk += strlen(chunkStartText);
+                // Find chunk len
+                int headerLen = pChunk - ((char*)pPayload);
+                int chunkLen = payloadLen - headerLen;
+                fileRemainingForWrite = contentLen - headerLen;
+                int numBytesToWrite = chunkLen;
+                if (fileRemainingForWrite > 0)
+                {
+                    // Check for end boundary of data
+                    if ((fileRemainingForWrite - chunkLen < 100) && (payloadLen > 50))
+                    {
+                        char* pEndForm = strstr(((char*)pPayload) + payloadLen - 50, "\r\n------");
+                        if (pEndForm != NULL)
+                        {
+                            numBytesToWrite = pEndForm - pChunk;
+                            printf("Upload file - payload end found writing %d bytes\r\n", numBytesToWrite);
+                        }
+                    }
+                    // Write chunk to file
+                    sdCardMutex.lock();    
+                    FILE* fp = fopen(fileNameForWrite, "w+");
+                    if (fp == NULL)
+                    {
+                        printf("Upload file - Failed to open output file\r\n");
+                    }
+                    else
+                    {
+                        fwrite(pChunk, sizeof(char), numBytesToWrite, fp);
+                        fclose(fp);
+                        printf("Upload file - written %d bytes\r\n", numBytesToWrite);
+                        writeSuccess = true;
+                    }
+                    sdCardMutex.unlock();
+                    // Reduce remaining bytes
+                    fileRemainingForWrite -= chunkLen;
+                }
+                else
+                {
+                    printf("Upload file - file remaining for write <= 0 - quitting\r\n");
+                }
+            }
+            else
+            {
+                printf("Upload file - can't find chunk start - quitting\r\n");
+            }
+        }
+        else
+        {
+            printf("Upload file - filename blank - quitting\r\n");
+        }
+
+    }
+    else
+    {
+        if (strlen(fileNameForWrite) != 0)
+        {
+            if (fileRemainingForWrite > 0)
+            {
+                int numBytesToWrite = payloadLen;
+                // Check for end boundary of data
+                if ((fileRemainingForWrite - payloadLen < 100) && (payloadLen > 50))
+                {
+                    char* pEndForm = strstr(((char*)pPayload) + payloadLen - 50, "\r\n------");
+                    if (pEndForm != NULL)
+                    {
+                        numBytesToWrite = pEndForm - ((char*)pPayload);
+                        printf("Upload file - payload end found writing %d bytes\r\n", numBytesToWrite);
+                    }
+                }
+                sdCardMutex.lock();    
+                FILE* fp = fopen(fileNameForWrite, "a");
+                if (fp == NULL)
+                {
+                    printf("Failed to open output file\r\n");
+                }
+                else
+                {
+                    fwrite(pPayload, sizeof(char), numBytesToWrite, fp);
+                    fclose(fp);
+                    writeSuccess = true;
+                    printf("Upload file - written %d bytes\r\n", numBytesToWrite);
+                }
+                sdCardMutex.unlock();
+                // Reduce remaining bytes
+                fileRemainingForWrite -= payloadLen;
+            }
+            else
+            {
+                printf("Upload file - file remaining for write <= 0 - quitting\r\n");
+            }
+        }
+        else
+        {
+            printf("Upload file - filename blank - quitting\r\n");
+        }
+    }
+    
+    // Return results
+    if (writeSuccess)
+        return "Write OK";
+    return "Write Failed";
+}
+
 // Create, configure and run the web server
 void http_thread(void const* arg)
 {
-    char* baseWebFolder = "/sd/";
     RdWebServer webServer(&sdCardMutex);
     webServer.addCommand("", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "index.htm", false);
     webServer.addCommand("gear-gr.png", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, NULL, true);
     webServer.addCommand("listfiles", RdWebServerCmdDef::CMD_SDORUSBFILE, NULL, "/", false);
     webServer.addCommand("getcurdata", RdWebServerCmdDef::CMD_CALLBACK, &getCurDataCallback);
     webServer.addCommand("setgascount", RdWebServerCmdDef::CMD_CALLBACK, &setGasUseCallback);
+    webServer.addCommand("delete", RdWebServerCmdDef::CMD_CALLBACK, &sdCardDelete);
+    webServer.addCommand("upload", RdWebServerCmdDef::CMD_CALLBACK, &sdCardUpload);
     webServer.init(WEBPORT, &led4, baseWebFolder);
     webServer.run();
 }
@@ -280,7 +510,7 @@
     bool restartCauseRecorded = false;
     
     // Setup the watchdog for reset
-    watchdog.SetTimeoutSecs(60);
+    watchdog.SetTimeoutSecs(300);
     
     // Time of last broadcast
     time_t timeOfLastBroadcast = time(NULL);