From cdfc0f6b71436899edfbb03c48b0c8cd90c33158 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 11 May 2023 17:33:09 +0200 Subject: [PATCH] Temperature usermod rewrite --- usermods/Temperature/platformio_override.ini | 9 +- usermods/Temperature/readme.md | 13 +- usermods/Temperature/usermod_temperature.h | 651 ++++++++++--------- usermods/Temperature/usermods_list.cpp | 31 - 4 files changed, 353 insertions(+), 351 deletions(-) delete mode 100644 usermods/Temperature/usermods_list.cpp diff --git a/usermods/Temperature/platformio_override.ini b/usermods/Temperature/platformio_override.ini index d9e3fbac..0e354da9 100644 --- a/usermods/Temperature/platformio_override.ini +++ b/usermods/Temperature/platformio_override.ini @@ -1,13 +1,12 @@ ; Options ; ------- ; USERMOD_DALLASTEMPERATURE - define this to have this user mod included wled00\usermods_list.cpp -; USERMOD_DALLASTEMPERATURE_CELSIUS - define this to report temperatures in degrees celsius, otherwise fahrenheit will be reported ; USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds -; USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds ; [env:d1_mini_usermod_dallas_temperature_C] extends = env:d1_mini -build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE -D USERMOD_DALLASTEMPERATURE_CELSIUS +build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE lib_deps = ${env.lib_deps} - milesburton/DallasTemperature@^3.9.0 - OneWire@~2.3.5 + paulstoffregen/OneWire@~2.3.7 +# you may want to use following with ESP32 +; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32 \ No newline at end of file diff --git a/usermods/Temperature/readme.md b/usermods/Temperature/readme.md index c917461a..2657c6c8 100644 --- a/usermods/Temperature/readme.md +++ b/usermods/Temperature/readme.md @@ -7,6 +7,8 @@ May be expanded with support for different sensor types in the future. If temperature sensor is not detected during boot, this usermod will be disabled. +Maintained by @blazoncek + ## Installation Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`. @@ -14,7 +16,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho ### Define Your Options * `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp -* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - number of milliseconds after boot to take first measurement, defaults to 20000 ms +* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - number of milliseconds between measurements, defaults to 60000 ms (60s) All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval. @@ -27,7 +29,6 @@ All parameters can be configured at runtime via the Usermods settings page, incl If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`. - If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: ```ini @@ -43,8 +44,9 @@ default_envs = d1_mini lib_deps = ... #For Dallas sensor uncomment following line - OneWire@~2.3.5 -... + OneWire@~2.3.7 + # ... or you may want to use following with ESP32 +; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32... ``` ## Change Log @@ -56,3 +58,6 @@ lib_deps = * Report the number of seconds until the first read in the info screen instead of sensor error 2021-04 * Adaptation for runtime configuration. +2023-05 +* Rewrite to conform to newer recommendations. +* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error \ No newline at end of file diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index b55076c7..a15baf87 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -57,343 +57,372 @@ class UsermodTemperature : public Usermod { static const char _parasitePin[]; //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 - float readDallas() { - byte data[9]; - int16_t result; // raw data from sensor - float retVal = -127.0f; - if (oneWire->reset()) { // if reset() fails there are no OneWire devices - oneWire->skip(); // skip ROM - oneWire->write(0xBE); // read (temperature) from EEPROM - oneWire->read_bytes(data, 9); // first 2 bytes contain temperature - #ifdef WLED_DEBUG - if (OneWire::crc8(data,8) != data[8]) { - DEBUG_PRINTLN(F("CRC error reading temperature.")); - for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]); - DEBUG_PRINT(F(" => ")); - DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8)); - } - #endif - switch(sensorFound) { - case 0x10: // DS18S20 has 9-bit precision - result = (data[1] << 8) | data[0]; - retVal = float(result) * 0.5f; - break; - case 0x22: // DS18B20 - case 0x28: // DS1822 - case 0x3B: // DS1825 - case 0x42: // DS28EA00 - result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning - if (data[1] & 0x80) result |= 0xF000; // fix negative value - retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f); - break; - } - } - for (byte i=1; i<9; i++) data[0] &= data[i]; - return data[0]==0xFF ? -127.0f : retVal; - } - - void requestTemperatures() { - DEBUG_PRINTLN(F("Requesting temperature.")); - oneWire->reset(); - oneWire->skip(); // skip ROM - oneWire->write(0x44,parasite); // request new temperature reading - if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET) - lastTemperaturesRequest = millis(); - waitingForConversion = true; - } - - void readTemperature() { - if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) - temperature = readDallas(); - lastMeasurement = millis(); - waitingForConversion = false; - //DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266 - DEBUG_PRINT(F("Read temperature ")); - DEBUG_PRINTLN(temperature); - } - - bool findSensor() { - DEBUG_PRINTLN(F("Searching for sensor...")); - uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0}; - // find out if we have DS18xxx sensor attached - oneWire->reset_search(); - delay(10); - while (oneWire->search(deviceAddress)) { - DEBUG_PRINTLN(F("Found something...")); - if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) { - switch (deviceAddress[0]) { - case 0x10: // DS18S20 - case 0x22: // DS18B20 - case 0x28: // DS1822 - case 0x3B: // DS1825 - case 0x42: // DS28EA00 - DEBUG_PRINTLN(F("Sensor found.")); - sensorFound = deviceAddress[0]; - DEBUG_PRINTF("0x%02X\n", sensorFound); - return true; - } - } - } - DEBUG_PRINTLN(F("Sensor NOT found.")); - return false; - } - + float readDallas(); + void requestTemperatures(); + void readTemperature(); + bool findSensor(); #ifndef WLED_DISABLE_MQTT - void publishHomeAssistantAutodiscovery() { - if (!WLED_MQTT_CONNECTED) return; - - char json_str[1024], buf[128]; - size_t payload_size; - StaticJsonDocument<1024> json; - - sprintf_P(buf, PSTR("%s Temperature"), serverDescription); - json[F("name")] = buf; - strcpy(buf, mqttDeviceTopic); - strcat_P(buf, PSTR("/temperature")); - json[F("state_topic")] = buf; - json[F("device_class")] = F("temperature"); - json[F("unique_id")] = escapedMac.c_str(); - json[F("unit_of_measurement")] = F("°C"); - payload_size = serializeJson(json, json_str); - - sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str()); - mqtt->publish(buf, 0, true, json_str, payload_size); - HApublished = true; - } + void publishHomeAssistantAutodiscovery(); #endif public: - void setup() { - int retries = 10; - sensorFound = 0; - temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C - if (enabled) { - // config says we are enabled - DEBUG_PRINTLN(F("Allocating temperature pin...")); - // pin retrieved from cfg.json (readFromConfig()) prior to running setup() - if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) { - oneWire = new OneWire(temperaturePin); - if (oneWire->reset()) { - while (!findSensor() && retries--) { - delay(25); // try to find sensor - } - } - if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) { - pinMode(parasitePin, OUTPUT); - digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) - } else { - parasitePin = -1; - } - } else { - if (temperaturePin >= 0) { - DEBUG_PRINTLN(F("Temperature pin allocation failed.")); - } - temperaturePin = -1; // allocation failed - } - } - lastMeasurement = millis() - readingInterval + 10000; - initDone = true; - } - - void loop() { - if (!enabled || !sensorFound || strip.isUpdating()) return; - - static uint8_t errorCount = 0; - unsigned long now = millis(); - - // check to see if we are due for taking a measurement - // lastMeasurement will not be updated until the conversion - // is complete the the reading is finished - if (now - lastMeasurement < readingInterval) return; - - // we are due for a measurement, if we are not already waiting - // for a conversion to complete, then make a new request for temps - if (!waitingForConversion) { - requestTemperatures(); - return; - } - - // we were waiting for a conversion to complete, have we waited log enough? - if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) { - readTemperature(); - if (getTemperatureC() < -100.0f) { - if (++errorCount > 10) sensorFound = 0; - lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms - return; - } - errorCount = 0; - -#ifndef WLED_DISABLE_MQTT - if (WLED_MQTT_CONNECTED) { - char subuf[64]; - strcpy(subuf, mqttDeviceTopic); - if (temperature > -100.0f) { - // dont publish super low temperature as the graph will get messed up - // the DallasTemperature library returns -127C or -196.6F when problem - // reading the sensor - strcat_P(subuf, PSTR("/temperature")); - mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str()); - strcat_P(subuf, PSTR("_f")); - mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str()); - } else { - // publish something else to indicate status? - } - } -#endif - } - } - - /** - * connected() is called every time the WiFi is (re)connected - * Use it to initialize network interfaces - */ - //void connected() {} - -#ifndef WLED_DISABLE_MQTT - /** - * subscribe to MQTT topic if needed - */ - void onMqttConnect(bool sessionPresent) { - //(re)subscribe to required topics - //char subuf[64]; - if (mqttDeviceTopic[0] != 0) { - publishHomeAssistantAutodiscovery(); - } - } -#endif - /* * API calls te enable data exchange between WLED modules */ - inline float getTemperatureC() { - return (float)temperature; + inline float getTemperatureC() { return temperature; } + inline float getTemperatureF() { return temperature * 1.8f + 32.0f; } + float getTemperature(); + const char *getTemperatureUnit(); + uint16_t getId() { return USERMOD_ID_TEMPERATURE; } + + void setup(); + void loop(); + //void connected(); +#ifndef WLED_DISABLE_MQTT + void onMqttConnect(bool sessionPresent); +#endif + //void onUpdateBegin(bool init); + + //bool handleButton(uint8_t b); + //void handleOverlayDraw(); + + void addToJsonInfo(JsonObject& root); + //void addToJsonState(JsonObject &root); + //void readFromJsonState(JsonObject &root); + void addToConfig(JsonObject &root); + bool readFromConfig(JsonObject &root); + + void appendConfigData(); +}; + +//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 +float UsermodTemperature::readDallas() { + byte data[9]; + int16_t result; // raw data from sensor + float retVal = -127.0f; + if (oneWire->reset()) { // if reset() fails there are no OneWire devices + oneWire->skip(); // skip ROM + oneWire->write(0xBE); // read (temperature) from EEPROM + oneWire->read_bytes(data, 9); // first 2 bytes contain temperature + #ifdef WLED_DEBUG + if (OneWire::crc8(data,8) != data[8]) { + DEBUG_PRINTLN(F("CRC error reading temperature.")); + for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]); + DEBUG_PRINT(F(" => ")); + DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8)); } - inline float getTemperatureF() { - return (float)temperature * 1.8f + 32; + #endif + switch(sensorFound) { + case 0x10: // DS18S20 has 9-bit precision + result = (data[1] << 8) | data[0]; + retVal = float(result) * 0.5f; + break; + case 0x22: // DS18B20 + case 0x28: // DS1822 + case 0x3B: // DS1825 + case 0x42: // DS28EA00 + result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning + if (data[1] & 0x80) result |= 0xF000; // fix negative value + retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f); + break; } + } + for (byte i=1; i<9; i++) data[0] &= data[i]; + return data[0]==0xFF ? -127.0f : retVal; +} - /* - * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. - * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. - * Below it is shown how this could be used for e.g. a light sensor - */ - void addToJsonInfo(JsonObject& root) { - // dont add temperature to info if we are disabled - if (!enabled) return; +void UsermodTemperature::requestTemperatures() { + DEBUG_PRINTLN(F("Requesting temperature.")); + oneWire->reset(); + oneWire->skip(); // skip ROM + oneWire->write(0x44,parasite); // request new temperature reading + if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET) + lastTemperaturesRequest = millis(); + waitingForConversion = true; +} - JsonObject user = root["u"]; - if (user.isNull()) user = root.createNestedObject("u"); +void UsermodTemperature::readTemperature() { + if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) + temperature = readDallas(); + lastMeasurement = millis(); + waitingForConversion = false; + //DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266 + DEBUG_PRINT(F("Read temperature ")); + DEBUG_PRINTLN(temperature); +} - JsonArray temp = user.createNestedArray(FPSTR(_name)); - - if (temperature <= -100.0f) { - temp.add(0); - temp.add(F(" Sensor Error!")); - return; +bool UsermodTemperature::findSensor() { + DEBUG_PRINTLN(F("Searching for sensor...")); + uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0}; + // find out if we have DS18xxx sensor attached + oneWire->reset_search(); + delay(10); + while (oneWire->search(deviceAddress)) { + DEBUG_PRINTLN(F("Found something...")); + if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) { + switch (deviceAddress[0]) { + case 0x10: // DS18S20 + case 0x22: // DS18B20 + case 0x28: // DS1822 + case 0x3B: // DS1825 + case 0x42: // DS28EA00 + DEBUG_PRINTLN(F("Sensor found.")); + sensorFound = deviceAddress[0]; + DEBUG_PRINTF("0x%02X\n", sensorFound); + return true; } - - temp.add(degC ? getTemperatureC() : getTemperatureF()); - temp.add(degC ? F("°C") : F("°F")); - - JsonObject sensor = root[F("sensor")]; - if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); - temp = sensor.createNestedArray(F("temp")); - temp.add(degC ? temperature : (float)temperature * 1.8f + 32); - temp.add(degC ? F("°C") : F("°F")); } + } + DEBUG_PRINTLN(F("Sensor NOT found.")); + return false; +} - /** - * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). - * Values in the state object may be modified by connected clients - */ - //void addToJsonState(JsonObject &root) - //{ - //} +#ifndef WLED_DISABLE_MQTT +void UsermodTemperature::publishHomeAssistantAutodiscovery() { + if (!WLED_MQTT_CONNECTED) return; - /** - * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). - * Values in the state object may be modified by connected clients - * Read "_" from json state and and change settings (i.e. GPIO pin) used. - */ - //void readFromJsonState(JsonObject &root) { - // if (!initDone) return; // prevent crash on boot applyPreset() - //} + char json_str[1024], buf[128]; + size_t payload_size; + StaticJsonDocument<1024> json; - /** - * addToConfig() (called from set.cpp) stores persistent properties to cfg.json - */ - void addToConfig(JsonObject &root) { - // we add JSON object: {"Temperature": {"pin": 0, "degC": true}} - JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname - top[FPSTR(_enabled)] = enabled; - top["pin"] = temperaturePin; // usermodparam - top["degC"] = degC; // usermodparam - top[FPSTR(_readInterval)] = readingInterval / 1000; - top[FPSTR(_parasite)] = parasite; - top[FPSTR(_parasitePin)] = parasitePin; - DEBUG_PRINTLN(F("Temperature config saved.")); - } + sprintf_P(buf, PSTR("%s Temperature"), serverDescription); + json[F("name")] = buf; + strcpy(buf, mqttDeviceTopic); + strcat_P(buf, PSTR("/temperature")); + json[F("state_topic")] = buf; + json[F("device_class")] = F("temperature"); + json[F("unique_id")] = escapedMac.c_str(); + json[F("unit_of_measurement")] = F("°C"); + payload_size = serializeJson(json, json_str); - /** - * readFromConfig() is called before setup() to populate properties from values stored in cfg.json - * - * The function should return true if configuration was successfully loaded or false if there was no configuration. - */ - bool readFromConfig(JsonObject &root) { - // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}} - int8_t newTemperaturePin = temperaturePin; - DEBUG_PRINT(FPSTR(_name)); + sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str()); + mqtt->publish(buf, 0, true, json_str, payload_size); + HApublished = true; +} +#endif - JsonObject top = root[FPSTR(_name)]; - if (top.isNull()) { - DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); - return false; - } - - enabled = top[FPSTR(_enabled)] | enabled; - newTemperaturePin = top["pin"] | newTemperaturePin; - degC = top["degC"] | degC; - readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; - readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms - parasite = top[FPSTR(_parasite)] | parasite; - parasitePin = top[FPSTR(_parasitePin)] | parasitePin; - - if (!initDone) { - // first run: reading from cfg.json - temperaturePin = newTemperaturePin; - DEBUG_PRINTLN(F(" config loaded.")); - } else { - DEBUG_PRINTLN(F(" config (re)loaded.")); - // changing paramters from settings page - if (newTemperaturePin != temperaturePin) { - DEBUG_PRINTLN(F("Re-init temperature.")); - // deallocate pin and release memory - delete oneWire; - pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature); - temperaturePin = newTemperaturePin; - pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature); - // initialise - setup(); +void UsermodTemperature::setup() { + int retries = 10; + sensorFound = 0; + temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C + if (enabled) { + // config says we are enabled + DEBUG_PRINTLN(F("Allocating temperature pin...")); + // pin retrieved from cfg.json (readFromConfig()) prior to running setup() + if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) { + oneWire = new OneWire(temperaturePin); + if (oneWire->reset()) { + while (!findSensor() && retries--) { + delay(25); // try to find sensor } } - // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[FPSTR(_parasitePin)].isNull(); + if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) { + pinMode(parasitePin, OUTPUT); + digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) + } else { + parasitePin = -1; + } + } else { + if (temperaturePin >= 0) { + DEBUG_PRINTLN(F("Temperature pin allocation failed.")); + } + temperaturePin = -1; // allocation failed } + } + lastMeasurement = millis() - readingInterval + 10000; + initDone = true; +} - void appendConfigData() - { - oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str()); - oappend(SET_F("',1,'(if no Vcc connected)');")); // 0 is field type, 1 is actual field - oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasitePin)).c_str()); - oappend(SET_F("',1,'(for external MOSFET)');")); // 0 is field type, 1 is actual field - } +void UsermodTemperature::loop() { + if (!enabled || !sensorFound || strip.isUpdating()) return; - uint16_t getId() - { - return USERMOD_ID_TEMPERATURE; + static uint8_t errorCount = 0; + unsigned long now = millis(); + + // check to see if we are due for taking a measurement + // lastMeasurement will not be updated until the conversion + // is complete the the reading is finished + if (now - lastMeasurement < readingInterval) return; + + // we are due for a measurement, if we are not already waiting + // for a conversion to complete, then make a new request for temps + if (!waitingForConversion) { + requestTemperatures(); + return; + } + + // we were waiting for a conversion to complete, have we waited log enough? + if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) { + readTemperature(); + if (getTemperatureC() < -100.0f) { + if (++errorCount > 10) sensorFound = 0; + lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms + return; } -}; + errorCount = 0; + +#ifndef WLED_DISABLE_MQTT + if (WLED_MQTT_CONNECTED) { + char subuf[64]; + strcpy(subuf, mqttDeviceTopic); + if (temperature > -100.0f) { + // dont publish super low temperature as the graph will get messed up + // the DallasTemperature library returns -127C or -196.6F when problem + // reading the sensor + strcat_P(subuf, PSTR("/temperature")); + mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str()); + strcat_P(subuf, PSTR("_f")); + mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str()); + } else { + // publish something else to indicate status? + } + } +#endif + } +} + +/** + * connected() is called every time the WiFi is (re)connected + * Use it to initialize network interfaces + */ +//void UsermodTemperature::connected() {} + +#ifndef WLED_DISABLE_MQTT +/** + * subscribe to MQTT topic if needed + */ +void UsermodTemperature::onMqttConnect(bool sessionPresent) { + //(re)subscribe to required topics + //char subuf[64]; + if (mqttDeviceTopic[0] != 0) { + publishHomeAssistantAutodiscovery(); + } +} +#endif + +/* + * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. + * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. + * Below it is shown how this could be used for e.g. a light sensor + */ +void UsermodTemperature::addToJsonInfo(JsonObject& root) { + // dont add temperature to info if we are disabled + if (!enabled) return; + + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + + JsonArray temp = user.createNestedArray(FPSTR(_name)); + + if (temperature <= -100.0f) { + temp.add(0); + temp.add(F(" Sensor Error!")); + return; + } + + temp.add(getTemperature()); + temp.add(getTemperatureUnit()); + + JsonObject sensor = root[F("sensor")]; + if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); + temp = sensor.createNestedArray(F("temperature")); + temp.add(getTemperature()); + temp.add(getTemperatureUnit()); +} + +/** + * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients + */ +//void UsermodTemperature::addToJsonState(JsonObject &root) +//{ +//} + +/** + * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). + * Values in the state object may be modified by connected clients + * Read "_" from json state and and change settings (i.e. GPIO pin) used. + */ +//void UsermodTemperature::readFromJsonState(JsonObject &root) { +// if (!initDone) return; // prevent crash on boot applyPreset() +//} + +/** + * addToConfig() (called from set.cpp) stores persistent properties to cfg.json + */ +void UsermodTemperature::addToConfig(JsonObject &root) { + // we add JSON object: {"Temperature": {"pin": 0, "degC": true}} + JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname + top[FPSTR(_enabled)] = enabled; + top["pin"] = temperaturePin; // usermodparam + top["degC"] = degC; // usermodparam + top[FPSTR(_readInterval)] = readingInterval / 1000; + top[FPSTR(_parasite)] = parasite; + top[FPSTR(_parasitePin)] = parasitePin; + DEBUG_PRINTLN(F("Temperature config saved.")); +} + +/** + * readFromConfig() is called before setup() to populate properties from values stored in cfg.json + * + * The function should return true if configuration was successfully loaded or false if there was no configuration. + */ +bool UsermodTemperature::readFromConfig(JsonObject &root) { + // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}} + int8_t newTemperaturePin = temperaturePin; + DEBUG_PRINT(FPSTR(_name)); + + JsonObject top = root[FPSTR(_name)]; + if (top.isNull()) { + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } + + enabled = top[FPSTR(_enabled)] | enabled; + newTemperaturePin = top["pin"] | newTemperaturePin; + degC = top["degC"] | degC; + readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; + readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms + parasite = top[FPSTR(_parasite)] | parasite; + parasitePin = top[FPSTR(_parasitePin)] | parasitePin; + + if (!initDone) { + // first run: reading from cfg.json + temperaturePin = newTemperaturePin; + DEBUG_PRINTLN(F(" config loaded.")); + } else { + DEBUG_PRINTLN(F(" config (re)loaded.")); + // changing paramters from settings page + if (newTemperaturePin != temperaturePin) { + DEBUG_PRINTLN(F("Re-init temperature.")); + // deallocate pin and release memory + delete oneWire; + pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature); + temperaturePin = newTemperaturePin; + pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature); + // initialise + setup(); + } + } + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return !top[FPSTR(_parasitePin)].isNull(); +} + +void UsermodTemperature::appendConfigData() { + oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str()); + oappend(SET_F("',1,'(if no Vcc connected)');")); // 0 is field type, 1 is actual field + oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasitePin)).c_str()); + oappend(SET_F("',1,'(for external MOSFET)');")); // 0 is field type, 1 is actual field +} + +float UsermodTemperature::getTemperature() { + return degC ? getTemperatureC() : getTemperatureF(); +} + +const char *UsermodTemperature::getTemperatureUnit() { + return degC ? "°C" : "°F"; +} // strings to reduce flash memory usage (used more than twice) const char UsermodTemperature::_name[] PROGMEM = "Temperature"; diff --git a/usermods/Temperature/usermods_list.cpp b/usermods/Temperature/usermods_list.cpp deleted file mode 100644 index 50dd7816..00000000 --- a/usermods/Temperature/usermods_list.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "wled.h" -/* - * Register your v2 usermods here! - */ - -/* - * Add/uncomment your usermod filename here (and once more below) - * || || || - * \/ \/ \/ - */ -//#include "usermod_v2_example.h" -#ifdef USERMOD_DALLASTEMPERATURE -#include "../usermods/Temperature/usermod_temperature.h" -#endif - -//#include "usermod_v2_empty.h" - -void registerUsermods() -{ - /* - * Add your usermod class name here - * || || || - * \/ \/ \/ - */ - //usermods.add(new MyExampleUsermod()); -#ifdef USERMOD_DALLASTEMPERATURE - usermods.add(new UsermodTemperature()); -#endif - - //usermods.add(new UsermodRenameMe()); -} \ No newline at end of file