Async temperature usermod (#1178)

* Change Temperature usermod to use async reading to avoid blocking

* Fix comparison on wait time

* Add

* Simplify logic in the loop method

* Optimize calls to minimize latency, inform user how long till first measurement

* disable usermod if sensor not found

* Added debug statements on init, update readme

* fix spelling error of celsius
This commit is contained in:
Phil Bolduc 2020-09-13 10:26:27 -07:00 committed by GitHub
parent 63d92381ee
commit 57421d2392
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 166 additions and 30 deletions

View File

@ -0,0 +1,13 @@
; 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
lib_deps = ${env.lib_deps}
milesburton/DallasTemperature@^3.9.0
OneWire@~2.3.5

View File

@ -5,11 +5,18 @@ This usermod will read from an attached DS18B20 temperature sensor (as available
The temperature is displayed both in the Info section of the web UI as well as published to the `/temperature` MQTT topic if enabled.
This usermod will be expanded with support for different sensor types in the future.
If temperature sensor is not detected during boot, this usermod will be disabled.
## Installation
Copy `usermod_temperature.h` to the wled00 directory.
Uncomment the corresponding lines in `usermods_list.cpp` and compile!
If this is the only v2 usermod you plan to use, you can alternatively replace `usermods_list.h` in wled00 with the one in this folder.
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
### Define Your 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 celsious, 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
## Project link
@ -17,7 +24,10 @@ If this is the only v2 usermod you plan to use, you can alternatively replace `u
### PlatformIO requirements
You might have to uncomment `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
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 `DallasTemperature@~3.8.0`,`OneWire@~2.3.5 under` `[common]` section in `platformio.ini`:
```ini
# platformio.ini
@ -38,3 +48,11 @@ lib_deps_external =
OneWire@~2.3.5
...
```
## Change Log
2020-09-12
* Changed to use async, non-blocking implementation
* Do not report low temperatures that indicate an error to mqtt
* Disable plugin if temperature sensor not detected
* Report the number of seconds until the first read in the info screen instead of sensor error

View File

@ -11,61 +11,156 @@
#define TEMPERATURE_PIN 14
#endif
#define TEMP_CELSIUS // Comment out for Fahrenheit
// the frequency to check temperature, 1 minute
#ifndef USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL
#define USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL 60000
#endif
#define MEASUREMENT_INTERVAL 60000 //1 Minute
// how many seconds after boot to take first measurement, 20 seconds
#ifndef USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT
#define USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT 20000
#endif
OneWire oneWire(TEMPERATURE_PIN);
DallasTemperature sensor(&oneWire);
class UsermodTemperature : public Usermod {
private:
//set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - 40000;
float temperature = 0.0f;
public:
void getReading() {
sensor.requestTemperatures();
#ifdef TEMP_CELSIUS
temperature = sensor.getTempCByIndex(0);
#else
temperature = sensor.getTempFByIndex(0);
#endif
// The device's unique 64-bit serial code stored in on-board ROM.
// Reading directly from the sensor device address is faster than
// reading from index. When reading by index, DallasTemperature
// must first look up the device address at the specified index.
DeviceAddress sensorDeviceAddress;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - (USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT);
// last time requestTemperatures was called
// used to determine when we can read the sensors temperature
// we have to wait at least 93.75 ms after requestTemperatures() is called
unsigned long lastTemperaturesRequest;
float temperature = -100; // default to -100, DS18B20 only goes down to -50C
// indicates requestTemperatures has been called but the sensor measurement is not complete
bool waitingForConversion = false;
// flag to indicate we have finished the first getTemperature call
// allows this library to report to the user how long until the first
// measurement
bool getTemperatureComplete = false;
// flag set at startup if DS18B20 sensor not found, avoids trying to keep getting
// temperature if flashed to a board without a sensor attached
bool disabled = false;
void requestTemperatures() {
// there is requestTemperaturesByAddress however it
// appears to do more work,
// TODO: measure exection time difference
sensor.requestTemperatures();
lastTemperaturesRequest = millis();
waitingForConversion = true;
}
void getTemperature() {
#ifdef USERMOD_DALLASTEMPERATURE_CELSIUS
temperature = sensor.getTempC(sensorDeviceAddress);
#else
temperature = sensor.getTempF(sensorDeviceAddress);
#endif
lastMeasurement = millis();
waitingForConversion = false;
getTemperatureComplete = true;
}
public:
void setup() {
sensor.begin();
sensor.setResolution(9);
// get the unique 64-bit serial code stored in on-board ROM
// if getAddress returns false, the sensor was not found
disabled = !sensor.getAddress(sensorDeviceAddress, 0);
if (!disabled) {
DEBUG_PRINTLN("Dallas Temperature found");
// set the resolution for this specific device
sensor.setResolution(sensorDeviceAddress, 9, true);
// do not block waiting for reading
sensor.setWaitForConversion(false);
} else {
DEBUG_PRINTLN("Dallas Temperature not found");
}
}
void loop() {
if (millis() - lastMeasurement > MEASUREMENT_INTERVAL)
{
getReading();
if (disabled) {
return;
}
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 < USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL)
{
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 >= 94 /* 93.75ms per the datasheet */)
{
getTemperature();
if (WLED_MQTT_CONNECTED) {
char subuf[38];
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/temperature");
mqtt->publish(subuf, 0, true, String(temperature).c_str());
if (-100 <= temperature) {
// 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(subuf, "/temperature");
mqtt->publish(subuf, 0, true, String(temperature).c_str());
} else {
// publish something else to indicate status?
}
}
lastMeasurement = millis();
}
}
void addToJsonInfo(JsonObject& root) {
// dont add temperature to info if we are disabled
if (disabled) {
return;
}
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray temp = user.createNestedArray("Temperature");
if (temperature == DEVICE_DISCONNECTED_C) {
if (!getTemperatureComplete) {
// if we haven't read the sensor yet, let the user know
// that we are still waiting for the first measurement
temp.add((USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - millis()) / 1000);
temp.add(" sec until read");
return;
}
if (temperature <= -100) {
temp.add(0);
temp.add(" Sensor Error!");
return;
}
temp.add(temperature);
#ifdef TEMP_CELSIUS
#ifdef USERMOD_DALLASTEMPERATURE_CELSIUS
temp.add("°C");
#else
temp.add("°F");
@ -76,4 +171,4 @@ class UsermodTemperature : public Usermod {
{
return USERMOD_ID_TEMPERATURE;
}
};
};

View File

@ -9,7 +9,10 @@
* \/ \/ \/
*/
//#include "usermod_v2_example.h"
#include "usermod_temperature.h"
#ifdef USERMOD_DALLASTEMPERATURE
#include "../usermods/Temperature/usermod_temperature.h"
#endif
//#include "usermod_v2_empty.h"
void registerUsermods()
@ -20,6 +23,9 @@ void registerUsermods()
* \/ \/ \/
*/
//usermods.add(new MyExampleUsermod());
#ifdef USERMOD_DALLASTEMPERATURE
usermods.add(new UsermodTemperature());
#endif
//usermods.add(new UsermodRenameMe());
}

View File

@ -10,7 +10,9 @@
* \/ \/ \/
*/
//#include "usermod_v2_example.h"
//#include "usermod_temperature.h"
#ifdef USERMOD_DALLASTEMPERATURE
#include "../usermods/Temperature/usermod_temperature.h"
#endif
//#include "usermod_v2_empty.h"
void registerUsermods()
@ -21,6 +23,8 @@ void registerUsermods()
* \/ \/ \/
*/
//usermods.add(new MyExampleUsermod());
//usermods.add(new UsermodTemperature());
#ifdef USERMOD_DALLASTEMPERATURE
usermods.add(new UsermodTemperature());
#endif
//usermods.add(new UsermodRenameMe());
}