From e121ec75d896fa2ab55580bf8e6b3f95587dd67e Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Wed, 5 Aug 2020 19:55:16 +0200 Subject: [PATCH] Support multiple temperature sensors --- src/AmsConfiguration.cpp | 73 +++++++++++++++++++++++++++- src/AmsConfiguration.h | 27 +++++++--- src/AmsToMqttBridge.ino | 19 +++++--- src/HwTools.cpp | 100 ++++++++++++++++++++++++++++++++------ src/HwTools.h | 21 +++++++- src/web/AmsWebServer.cpp | 69 ++++++++++++++++++++++++++ src/web/AmsWebServer.h | 4 ++ web/temperature_foot.html | 11 +++++ web/temperature_head.html | 3 ++ web/temperature_row.html | 28 +++++++++++ 10 files changed, 325 insertions(+), 30 deletions(-) create mode 100644 web/temperature_foot.html create mode 100644 web/temperature_head.html create mode 100644 web/temperature_row.html diff --git a/src/AmsConfiguration.cpp b/src/AmsConfiguration.cpp index 5726cc45..184f80d2 100644 --- a/src/AmsConfiguration.cpp +++ b/src/AmsConfiguration.cpp @@ -675,8 +675,7 @@ bool AmsConfiguration::load() { bool success = false; EEPROM.begin(EEPROM_SIZE); - int cs = EEPROM.read(address); - address++; + int cs = EEPROM.read(address++); switch(cs) { case 81: // v1.2 success = loadConfig81(address); @@ -686,11 +685,13 @@ bool AmsConfiguration::load() { break; case 83: // v1.4 EEPROM.get(address, config); + loadTempSensors(); success = true; break; } EEPROM.end(); + if(config.apPin >= 0) pinMode(config.apPin, INPUT_PULLUP); meterChanged = true; @@ -698,6 +699,35 @@ bool AmsConfiguration::load() { return success; } +void AmsConfiguration::loadTempSensors() { + int address = EEPROM_TEMP_CONFIG_ADDRESS; + int c = 0; + int storedCount = EEPROM.read(address++); + if(storedCount > 0 && storedCount <= 32) { + for(int i = 0; i < storedCount; i++) { + TempSensorConfig* tsc = new TempSensorConfig(); + EEPROM.get(address, *tsc); + if(tsc->address[0] != 0xFF) { + tempSensors[c++] = tsc; + } + address += sizeof(*tsc); + } + } + tempSensorCount = c; +} + +void AmsConfiguration::saveTempSensors() { + int address = EEPROM_TEMP_CONFIG_ADDRESS; + EEPROM.put(address++, tempSensorCount); + for(int i = 0; i < tempSensorCount; i++) { + TempSensorConfig* tsc = tempSensors[i]; + if(tsc->address[0] != 0xFF) { + EEPROM.put(address, *tsc); + address += sizeof(*tsc); + } + } +} + bool AmsConfiguration::loadConfig82(int address) { ConfigObject82 config82; EEPROM.get(address, config82); @@ -855,6 +885,7 @@ bool AmsConfiguration::save() { EEPROM.put(address, EEPROM_CHECK_SUM); address++; EEPROM.put(address, config); + saveTempSensors(); bool success = EEPROM.commit(); EEPROM.end(); @@ -862,6 +893,42 @@ bool AmsConfiguration::save() { return success; } +uint8_t AmsConfiguration::getTempSensorCount() { + return tempSensorCount; +} + +TempSensorConfig* AmsConfiguration::getTempSensorConfig(uint8_t i) { + return tempSensors[i]; +} + +void AmsConfiguration::updateTempSensorConfig(uint8_t address[8], const char name[32], bool common) { + bool found = false; + for(int x = 0; x < tempSensorCount; x++) { + TempSensorConfig *data = tempSensors[x]; + if(isSensorAddressEqual(data->address, address)) { + found = true; + strcpy(data->name, name); + data->common = common; + } + } + if(!found) { + TempSensorConfig *data = new TempSensorConfig(); + memcpy(data->address, address, 8); + strcpy(data->name, name); + data->common = common; + tempSensors[tempSensorCount] = data; + tempSensorCount++; + } +} + +bool AmsConfiguration::isSensorAddressEqual(uint8_t a[8], uint8_t b[8]) { + for(int i = 0; i < 8; i++) { + if(a[i] != b[i]) return false; + } + return true; +} + + int AmsConfiguration::readString(int pAddress, char* pString[]) { int address = 0; byte length = EEPROM.read(pAddress + address); @@ -969,6 +1036,8 @@ void AmsConfiguration::print(Print* debugger) debugger->printf("NTP server: %s\r\n", this->getNtpServer()); debugger->printf("NTP DHCP: %s\r\n", this->isNtpDhcp() ? "Yes" : "No"); } + + debugger->printf("Temp sensor count: %i\r\n", this->getTempSensorCount()); debugger->println("-----------------------------------------------"); } diff --git a/src/AmsConfiguration.h b/src/AmsConfiguration.h index 3c320076..721f224b 100644 --- a/src/AmsConfiguration.h +++ b/src/AmsConfiguration.h @@ -67,8 +67,6 @@ struct ConfigObject { char ntpServer[64]; uint8_t tempAnalogSensorPin; - int8_t tempSensorInternal; // -128 = disabled, -1 = analog, 0-127 = digital sensor index - uint8_t tempSensorCount; }; struct ConfigObject82 { @@ -124,6 +122,12 @@ struct ConfigObject82 { uint16_t domoCL1IDX; }; +struct TempSensorConfig { + uint8_t address[8]; + char name[16]; + bool common; +}; + class AmsConfiguration { public: bool hasConfig(); @@ -285,6 +289,12 @@ public: bool isNtpChanged(); void ackNtpChange(); + uint8_t getTempSensorCount(); + TempSensorConfig* getTempSensorConfig(uint8_t i); + void updateTempSensorConfig(uint8_t address[8], const char name[32], bool common); + + bool isSensorAddressEqual(uint8_t a[8], uint8_t b[8]); + void clear(); protected: @@ -350,15 +360,20 @@ private: 360, // Summertime offset (*10) "pool.ntp.org", // NTP server 0xFF, // Analog temp sensor - 0xFF, // Internal temp sensor - 0, // Temp sensor count - // 900 bytes + // 894 bytes }; bool wifiChanged, mqttChanged, meterChanged = true, domoChanged, ntpChanged; - const int EEPROM_SIZE = 904; // Config size + 4 bytes for config version + uint8_t tempSensorCount = 0; + TempSensorConfig* tempSensors[32]; + + const int EEPROM_SIZE = 1024 * 3; const int EEPROM_CHECK_SUM = 83; // Used to check if config is stored. Change if structure changes const int EEPROM_CONFIG_ADDRESS = 0; + const int EEPROM_TEMP_CONFIG_ADDRESS = 2048; + + void loadTempSensors(); + void saveTempSensors(); bool loadConfig81(int address); bool loadConfig82(int address); diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index d521a8d5..54529c46 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -156,6 +156,14 @@ void setup() { #endif } + uint8_t c = config.getTempSensorCount(); + for(int i = 0; i < c; i++) { + TempSensorConfig* tsc = config.getTempSensorConfig(i); + hw.confTempSensor(tsc->address, tsc->name, tsc->common); + Debug.print("Sensor name: "); + Debug.println(tsc->name); + } + double vcc = hw.getVcc(); if (Debug.isActive(RemoteDebug::INFO)) { @@ -269,7 +277,6 @@ bool longPressActive = false; bool wifiConnected = false; unsigned long lastTemperatureRead = 0; -double temperature = -127; unsigned long lastRead = 0; unsigned long lastSuccessfulRead = 0; @@ -306,8 +313,8 @@ void loop() { } } - if(now - lastTemperatureRead > 5000) { - temperature = hw.getTemperature(); + if(now - lastTemperatureRead > 10000) { + hw.updateTemperatures(); lastTemperatureRead = now; } @@ -554,7 +561,7 @@ void readHanPort() { if(strlen(config.getMqttHost()) > 0 && strlen(config.getMqttPublishTopic()) > 0) { if(config.getMqttPayloadFormat() == 0) { StaticJsonDocument<512> json; - hanToJson(json, data, hw, temperature, config.getMqttClientId()); + hanToJson(json, data, hw, hw.getTemperature(), config.getMqttClientId()); if (Debug.isActive(RemoteDebug::INFO)) { debugI("Sending data to MQTT"); if (Debug.isActive(RemoteDebug::DEBUG)) { @@ -1012,7 +1019,7 @@ void sendSystemStatusToMqtt() { mqtt.publish(String(config.getMqttPublishTopic()) + "/vcc", String(vcc, 2)); } mqtt.publish(String(config.getMqttPublishTopic()) + "/rssi", String(hw.getWifiRssi())); - if(temperature != DEVICE_DISCONNECTED_C) { - mqtt.publish(String(config.getMqttPublishTopic()) + "/temperature", String(temperature, 2)); + if(hw.getTemperature() != DEVICE_DISCONNECTED_C) { + mqtt.publish(String(config.getMqttPublishTopic()) + "/temperature", String(hw.getTemperature(), 2)); } } diff --git a/src/HwTools.cpp b/src/HwTools.cpp index 2cb53efb..09f49582 100644 --- a/src/HwTools.cpp +++ b/src/HwTools.cpp @@ -3,8 +3,8 @@ void HwTools::setTempSensorPin(int tempSensorPin) { if(tempSensorPin != this->tempSensorPin) { this->tempSensorInit = false; - if(tempSensor) - delete tempSensor; + if(sensorApi) + delete sensorApi; if(oneWire) delete oneWire; if(tempSensorPin > 0 && tempSensorPin < 40) { @@ -50,27 +50,99 @@ double HwTools::getVcc() { return vccOffset + (volts > 0.0 ? volts * vccMultiplier : 0.0); } -double HwTools::getTemperature() { +void HwTools::confTempSensor(uint8_t address[8], const char name[32], bool common) { + bool found = false; + for(int x = 0; x < sensorCount; x++) { + TempSensorData *data = tempSensors[x]; + if(isSensorAddressEqual(data->address, address)) { + found = true; + strcpy(data->name, name); + data->common = common; + } + } + if(!found) { + TempSensorData *data = new TempSensorData(); + memcpy(data->address, address, 8); + strcpy(data->name, name); + data->common = common; + data->lastRead = DEVICE_DISCONNECTED_C; + data->lastValidRead = DEVICE_DISCONNECTED_C; + tempSensors[sensorCount] = data; + sensorCount++; + } +} + +uint8_t HwTools::getTempSensorCount() { + return sensorCount; +} + +TempSensorData* HwTools::getTempSensorData(uint8_t i) { + return tempSensors[i]; +} + +bool HwTools::updateTemperatures() { if(tempSensorPin != 0xFF) { if(!tempSensorInit) { oneWire = new OneWire(tempSensorPin); - tempSensor = new DallasTemperature(this->oneWire); - tempSensor->begin(); + sensorApi = new DallasTemperature(this->oneWire); + sensorApi->begin(); delay(50); - tempSensor->requestTemperatures(); - hasTempSensor = tempSensor->getTempCByIndex(0) != DEVICE_DISCONNECTED_C; tempSensorInit = true; } - if(hasTempSensor) { - tempSensor->requestTemperatures(); - return tempSensor->getTempCByIndex(0); - } else { - return DEVICE_DISCONNECTED_C; + DeviceAddress addr; + sensorApi->requestTemperatures(); + int c = sensorApi->getDeviceCount(); + for(int i = 0; i < c; i++) { + bool found = false; + sensorApi->getAddress(addr, i); + float t = sensorApi->getTempC(addr); + for(int x = 0; x < sensorCount; x++) { + TempSensorData *data = tempSensors[x]; + if(isSensorAddressEqual(data->address, addr)) { + found = true; + data->lastRead = t; + if(t > -85) { + data->lastValidRead = t; + } + } + } + if(!found) { + TempSensorData *data = new TempSensorData(); + memcpy(data->address, addr, 8); + data->common = true; + data->lastRead = t; + if(t > -85) { + data->lastValidRead = t; + } + + tempSensors[sensorCount] = data; + sensorCount++; + } + } + return true; + } + return false; +} + +bool HwTools::isSensorAddressEqual(uint8_t a[8], uint8_t b[8]) { + for(int i = 0; i < 8; i++) { + if(a[i] != b[i]) return false; + } + return true; +} + +double HwTools::getTemperature() { + uint8_t c = 0; + double ret = 0; + for(int x = 0; x < sensorCount; x++) { + TempSensorData data = *tempSensors[x]; + if(data.common && data.lastValidRead > -85) { + ret += data.lastValidRead; + c++; } } - - return DEVICE_DISCONNECTED_C; + return c == 0 ? DEVICE_DISCONNECTED_C : ret/c; } int HwTools::getWifiRssi() { diff --git a/src/HwTools.h b/src/HwTools.h index 46200929..20bb7345 100644 --- a/src/HwTools.h +++ b/src/HwTools.h @@ -18,6 +18,14 @@ #define LED_BLUE 3 #define LED_YELLOW 4 +struct TempSensorData { + uint8_t address[8]; + char name[32]; + bool common; + float lastRead; + float lastValidRead; +}; + class HwTools { public: void setTempSensorPin(int tempSensorPin); @@ -25,7 +33,12 @@ public: void setVccOffset(double vccOffset); void setVccMultiplier(double vccMultiplier); double getVcc(); + void confTempSensor(uint8_t address[8], const char name[32], bool common); + uint8_t getTempSensorCount(); + TempSensorData* getTempSensorData(uint8_t i); + bool updateTemperatures(); double getTemperature(); + double getTemperature(uint8_t address[8]); int getWifiRssi(); void setLed(uint8_t ledPin, bool ledInverted); void setLedRgb(uint8_t ledPinRed, uint8_t ledPinGreen, uint8_t ledPinBlue, bool ledRgbInverted); @@ -41,11 +54,15 @@ private: bool ledInverted, ledRgbInverted; double vccOffset = 0.0; double vccMultiplier = 1.0; - bool tempSensorInit, hasTempSensor; + + bool tempSensorInit; OneWire *oneWire; - DallasTemperature *tempSensor; + DallasTemperature *sensorApi; + uint8_t sensorCount = 0; + TempSensorData *tempSensors[32]; bool writeLedPin(uint8_t color, uint8_t state); + bool isSensorAddressEqual(uint8_t a[8], uint8_t b[8]); }; #endif diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index 8103305d..7748582f 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -22,6 +22,9 @@ #include "root/upload_html.h" #include "root/delete_html.h" #include "root/reset_html.h" +#include "root/temperature_head_html.h" +#include "root/temperature_row_html.h" +#include "root/temperature_foot_html.h" #include "base64.h" @@ -37,6 +40,7 @@ void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) { server.on("/", HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on("/", HTTP_POST, std::bind(&AmsWebServer::handleSetup, this)); server.on("/application.js", HTTP_GET, std::bind(&AmsWebServer::applicationJs, this)); + server.on("/temperature", HTTP_GET, std::bind(&AmsWebServer::temperature, this)); server.on("/config-meter", HTTP_GET, std::bind(&AmsWebServer::configMeterHtml, this)); server.on("/config-wifi", HTTP_GET, std::bind(&AmsWebServer::configWifiHtml, this)); server.on("/config-mqtt", HTTP_GET, std::bind(&AmsWebServer::configMqttHtml, this)); @@ -120,6 +124,36 @@ bool AmsWebServer::checkSecurity(byte level) { return access; } +void AmsWebServer::temperature() { + printD("Serving /temperature.html over http..."); + + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); + + String html; + uint8_t c = hw->getTempSensorCount(); + for(int i = 0; i < c; i++) { + TempSensorData* data = hw->getTempSensorData(i); + + String row = String((const __FlashStringHelper*) TEMPERATURE_ROW_HTML); + row.replace("${index}", String(i, DEC)); + row.replace("${address}", toHex(data->address)); + row.replace("${name}", data->name); + row.replace("${common}", data->common ? "checked" : ""); + row.replace("${value}", String(data->lastRead, 1)); + + html += row; + } + + server.setContentLength(html.length() + HEAD_HTML_LEN + TEMPERATURE_HEAD_HTML_LEN + FOOT_HTML_LEN + TEMPERATURE_FOOT_HTML_LEN); + server.send_P(200, "text/html", HEAD_HTML); + server.sendContent_P(TEMPERATURE_HEAD_HTML); + server.sendContent(html); + server.sendContent_P(TEMPERATURE_FOOT_HTML); + server.sendContent_P(FOOT_HTML); +} + void AmsWebServer::indexHtml() { printD("Serving /index.html over http..."); @@ -269,6 +303,26 @@ void AmsWebServer::configMeterHtml() { server.sendContent_P(FOOT_HTML); } +String AmsWebServer::toHex(uint8_t* in) { + String hex; + for(int i = 0; i < sizeof(in)*2; i++) { + if(in[i] < 0x10) { + hex += '0'; + } + hex += String(in[i], HEX); + } + hex.toUpperCase(); + return hex; +} + +uint8_t* AmsWebServer::fromHex(String in, uint8_t size) { + uint8_t ret[size]; + for(int i = 0; i < size*2; i += 2) { + ret[i/2] = strtol(in.substring(i, i+2).c_str(), 0, 16); + } + return ret; +} + void AmsWebServer::configWifiHtml() { printD("Serving /config-wifi.html over http..."); @@ -835,6 +889,16 @@ void AmsWebServer::handleSave() { config->setNtpServer(server.arg("ntpServer").c_str()); } + if(server.hasArg("tempConfig") && server.arg("tempConfig") == "true") { + for(int i = 0; i < 32; i++) { + if(!server.hasArg("sensor" + String(i, DEC))) break; + String address = server.arg("sensor" + String(i, DEC)); + String name = server.arg("sensor" + String(i, DEC) + "name"); + bool common = server.hasArg("sensor" + String(i, DEC) + "common") && server.arg("sensor" + String(i, DEC) + "common") == "true"; + config->updateTempSensorConfig(fromHex(address, 8), name.c_str(), common); + } + } + printI("Saving configuration now..."); if (debugger->isActive(RemoteDebug::DEBUG)) config->print(debugger); @@ -854,6 +918,11 @@ void AmsWebServer::handleSave() { hw->setVccPin(config->getVccPin()); hw->setVccOffset(config->getVccOffset()); hw->setVccMultiplier(config->getVccMultiplier()); + uint8_t c = config->getTempSensorCount(); + for(int i = 0; i < c; i++) { + TempSensorConfig* tsc = config->getTempSensorConfig(i); + hw->confTempSensor(tsc->address, tsc->name, tsc->common); + } } } else { printE("Error saving configuration"); diff --git a/src/web/AmsWebServer.h b/src/web/AmsWebServer.h index 7d7c83ca..a1748edc 100644 --- a/src/web/AmsWebServer.h +++ b/src/web/AmsWebServer.h @@ -60,6 +60,7 @@ private: void indexHtml(); void applicationJs(); + void temperature(); void configMeterHtml(); void configWifiHtml(); void configMqttHtml(); @@ -103,6 +104,9 @@ private: void notFound(); + String toHex(uint8_t* in); + uint8_t* fromHex(String in, uint8_t size); + void printD(String fmt, ...); void printI(String fmt, ...); void printW(String fmt, ...); diff --git a/web/temperature_foot.html b/web/temperature_foot.html new file mode 100644 index 00000000..6fd0cae3 --- /dev/null +++ b/web/temperature_foot.html @@ -0,0 +1,11 @@ + +
+
+
+ Back +
+
+ +
+
+ diff --git a/web/temperature_head.html b/web/temperature_head.html new file mode 100644 index 00000000..68d5399e --- /dev/null +++ b/web/temperature_head.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/web/temperature_row.html b/web/temperature_row.html new file mode 100644 index 00000000..1b181115 --- /dev/null +++ b/web/temperature_row.html @@ -0,0 +1,28 @@ + +
+
+
+
+ Address +
+ +
+
+
+
+
+ Name +
+ +
+
+
+
+ + +
+
+
+ ${value} °C +
+