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
Diff: main.cpp
- 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);