Temperature usermod rewrite
This commit is contained in:
parent
bf6a18a414
commit
cdfc0f6b71
@ -1,13 +1,12 @@
|
|||||||
; Options
|
; Options
|
||||||
; -------
|
; -------
|
||||||
; USERMOD_DALLASTEMPERATURE - define this to have this user mod included wled00\usermods_list.cpp
|
; 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_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]
|
[env:d1_mini_usermod_dallas_temperature_C]
|
||||||
extends = env:d1_mini
|
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}
|
lib_deps = ${env.lib_deps}
|
||||||
milesburton/DallasTemperature@^3.9.0
|
paulstoffregen/OneWire@~2.3.7
|
||||||
OneWire@~2.3.5
|
# you may want to use following with ESP32
|
||||||
|
; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32
|
@ -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.
|
If temperature sensor is not detected during boot, this usermod will be disabled.
|
||||||
|
|
||||||
|
Maintained by @blazoncek
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
|
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
|
### Define Your Options
|
||||||
|
|
||||||
* `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp
|
* `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.
|
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 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`:
|
If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
@ -43,8 +44,9 @@ default_envs = d1_mini
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
...
|
...
|
||||||
#For Dallas sensor uncomment following line
|
#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
|
## 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
|
* Report the number of seconds until the first read in the info screen instead of sensor error
|
||||||
2021-04
|
2021-04
|
||||||
* Adaptation for runtime configuration.
|
* Adaptation for runtime configuration.
|
||||||
|
2023-05
|
||||||
|
* Rewrite to conform to newer recommendations.
|
||||||
|
* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error
|
@ -57,343 +57,372 @@ class UsermodTemperature : public Usermod {
|
|||||||
static const char _parasitePin[];
|
static const char _parasitePin[];
|
||||||
|
|
||||||
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
|
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
|
||||||
float readDallas() {
|
float readDallas();
|
||||||
byte data[9];
|
void requestTemperatures();
|
||||||
int16_t result; // raw data from sensor
|
void readTemperature();
|
||||||
float retVal = -127.0f;
|
bool findSensor();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_MQTT
|
#ifndef WLED_DISABLE_MQTT
|
||||||
void publishHomeAssistantAutodiscovery() {
|
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;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
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
|
* API calls te enable data exchange between WLED modules
|
||||||
*/
|
*/
|
||||||
inline float getTemperatureC() {
|
inline float getTemperatureC() { return temperature; }
|
||||||
return (float)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() {
|
#endif
|
||||||
return (float)temperature * 1.8f + 32;
|
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 UsermodTemperature::requestTemperatures() {
|
||||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
DEBUG_PRINTLN(F("Requesting temperature."));
|
||||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
oneWire->reset();
|
||||||
* Below it is shown how this could be used for e.g. a light sensor
|
oneWire->skip(); // skip ROM
|
||||||
*/
|
oneWire->write(0x44,parasite); // request new temperature reading
|
||||||
void addToJsonInfo(JsonObject& root) {
|
if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET)
|
||||||
// dont add temperature to info if we are disabled
|
lastTemperaturesRequest = millis();
|
||||||
if (!enabled) return;
|
waitingForConversion = true;
|
||||||
|
}
|
||||||
|
|
||||||
JsonObject user = root["u"];
|
void UsermodTemperature::readTemperature() {
|
||||||
if (user.isNull()) user = root.createNestedObject("u");
|
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));
|
bool UsermodTemperature::findSensor() {
|
||||||
|
DEBUG_PRINTLN(F("Searching for sensor..."));
|
||||||
if (temperature <= -100.0f) {
|
uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0};
|
||||||
temp.add(0);
|
// find out if we have DS18xxx sensor attached
|
||||||
temp.add(F(" Sensor Error!"));
|
oneWire->reset_search();
|
||||||
return;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
#ifndef WLED_DISABLE_MQTT
|
||||||
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
|
void UsermodTemperature::publishHomeAssistantAutodiscovery() {
|
||||||
* Values in the state object may be modified by connected clients
|
if (!WLED_MQTT_CONNECTED) return;
|
||||||
*/
|
|
||||||
//void addToJsonState(JsonObject &root)
|
|
||||||
//{
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
char json_str[1024], buf[128];
|
||||||
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
|
size_t payload_size;
|
||||||
* Values in the state object may be modified by connected clients
|
StaticJsonDocument<1024> json;
|
||||||
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
|
|
||||||
*/
|
|
||||||
//void readFromJsonState(JsonObject &root) {
|
|
||||||
// if (!initDone) return; // prevent crash on boot applyPreset()
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
sprintf_P(buf, PSTR("%s Temperature"), serverDescription);
|
||||||
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
|
json[F("name")] = buf;
|
||||||
*/
|
strcpy(buf, mqttDeviceTopic);
|
||||||
void addToConfig(JsonObject &root) {
|
strcat_P(buf, PSTR("/temperature"));
|
||||||
// we add JSON object: {"Temperature": {"pin": 0, "degC": true}}
|
json[F("state_topic")] = buf;
|
||||||
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
|
json[F("device_class")] = F("temperature");
|
||||||
top[FPSTR(_enabled)] = enabled;
|
json[F("unique_id")] = escapedMac.c_str();
|
||||||
top["pin"] = temperaturePin; // usermodparam
|
json[F("unit_of_measurement")] = F("°C");
|
||||||
top["degC"] = degC; // usermodparam
|
payload_size = serializeJson(json, json_str);
|
||||||
top[FPSTR(_readInterval)] = readingInterval / 1000;
|
|
||||||
top[FPSTR(_parasite)] = parasite;
|
|
||||||
top[FPSTR(_parasitePin)] = parasitePin;
|
|
||||||
DEBUG_PRINTLN(F("Temperature config saved."));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str());
|
||||||
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
|
mqtt->publish(buf, 0, true, json_str, payload_size);
|
||||||
*
|
HApublished = true;
|
||||||
* The function should return true if configuration was successfully loaded or false if there was no configuration.
|
}
|
||||||
*/
|
#endif
|
||||||
bool 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)];
|
void UsermodTemperature::setup() {
|
||||||
if (top.isNull()) {
|
int retries = 10;
|
||||||
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
|
sensorFound = 0;
|
||||||
return false;
|
temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C
|
||||||
}
|
if (enabled) {
|
||||||
|
// config says we are enabled
|
||||||
enabled = top[FPSTR(_enabled)] | enabled;
|
DEBUG_PRINTLN(F("Allocating temperature pin..."));
|
||||||
newTemperaturePin = top["pin"] | newTemperaturePin;
|
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||||
degC = top["degC"] | degC;
|
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
|
||||||
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
|
oneWire = new OneWire(temperaturePin);
|
||||||
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
|
if (oneWire->reset()) {
|
||||||
parasite = top[FPSTR(_parasite)] | parasite;
|
while (!findSensor() && retries--) {
|
||||||
parasitePin = top[FPSTR(_parasitePin)] | parasitePin;
|
delay(25); // try to find sensor
|
||||||
|
|
||||||
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
|
if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) {
|
||||||
return !top[FPSTR(_parasitePin)].isNull();
|
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()
|
void UsermodTemperature::loop() {
|
||||||
{
|
if (!enabled || !sensorFound || strip.isUpdating()) return;
|
||||||
oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str());
|
|
||||||
oappend(SET_F("',1,'<i>(if no Vcc connected)</i>');")); // 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,'<i>(for external MOSFET)</i>');")); // 0 is field type, 1 is actual field
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t getId()
|
static uint8_t errorCount = 0;
|
||||||
{
|
unsigned long now = millis();
|
||||||
return USERMOD_ID_TEMPERATURE;
|
|
||||||
|
// 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 "<usermodname>_<usermodparam>" 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,'<i>(if no Vcc connected)</i>');")); // 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,'<i>(for external MOSFET)</i>');")); // 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)
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
const char UsermodTemperature::_name[] PROGMEM = "Temperature";
|
const char UsermodTemperature::_name[] PROGMEM = "Temperature";
|
||||||
|
@ -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());
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user