From fff4735a157401de1e05d61cba5cb338f1e93178 Mon Sep 17 00:00:00 2001 From: Joar Svensson Date: Sat, 23 Oct 2021 21:10:25 +0200 Subject: [PATCH] Add support for DS18B20 temperature sensor --- platformio.ini | 2 + src/mesh/generated/radioconfig.pb.h | 3 +- .../esp32/EnvironmentalMeasurementPlugin.cpp | 214 ++++++++++-------- .../esp32/EnvironmentalMeasurementPlugin.h | 19 +- 4 files changed, 139 insertions(+), 99 deletions(-) diff --git a/platformio.ini b/platformio.ini index 16d73dee..0081ffbd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -111,6 +111,8 @@ lib_deps = https://github.com/meshtastic/esp32_https_server.git adafruit/DHT sensor library@^1.4.1 adafruit/Adafruit Unified Sensor@^1.1.4 + paulstoffregen/OneWire@^2.3.5 + robtillaart/DS18B20@^0.1.11 # Hmm - this doesn't work yet # board_build.ldscript = linker/esp32.extram.bss.ld lib_ignore = segger_rtt diff --git a/src/mesh/generated/radioconfig.pb.h b/src/mesh/generated/radioconfig.pb.h index 0593bdcc..ee7b2aeb 100644 --- a/src/mesh/generated/radioconfig.pb.h +++ b/src/mesh/generated/radioconfig.pb.h @@ -80,7 +80,8 @@ typedef enum _PositionFlags { } PositionFlags; typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType { - RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0 + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0, + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20 = 1 } RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType; /* Struct definitions */ diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp index 5e5ad815..99205736 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp @@ -1,20 +1,23 @@ #include "EnvironmentalMeasurementPlugin.h" +#include "../mesh/generated/environmental_measurement.pb.h" #include "MeshService.h" #include "NodeDB.h" #include "RTC.h" #include "Router.h" #include "configuration.h" #include "main.h" -#include "../mesh/generated/environmental_measurement.pb.h" #include +#include #include #include +#include -#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts +#define DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 +#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 +#define DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true - #ifdef HAS_EINK // The screen is bigger so use bigger fonts #define FONT_SMALL ArialMT_Plain_16 @@ -31,123 +34,136 @@ #define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL) #define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) - -int32_t EnvironmentalMeasurementPlugin::runOnce() { +int32_t EnvironmentalMeasurementPlugin::runOnce() +{ #ifndef NO_ESP32 // this only works on ESP32 devices /* Uncomment the preferences below if you want to use the plugin without having to configure it from the PythonAPI or WebUI. */ - + /*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1; radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1; radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5; - radioConfig.preferences.environmental_measurement_plugin_update_interval = 30; + radioConfig.preferences.environmental_measurement_plugin_update_interval = 600; radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60; - radioConfig.preferences.environmental_measurement_plugin_display_farenheit = true; + radioConfig.preferences.environmental_measurement_plugin_display_farenheit = false; radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13; - radioConfig.preferences.environmental_measurement_plugin_sensor_type = RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11;*/ + radioConfig.preferences.environmental_measurement_plugin_sensor_type = + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType:: + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20; + */ - if (! (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){ + if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || + radioConfig.preferences.environmental_measurement_plugin_screen_enabled)) { // If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it return (INT32_MAX); } if (firstTime) { // This is the first time the OSThread library has called this function, so do some setup - firstTime = 0; - - if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) - { + + if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) { DEBUG_MSG("EnvironmentalMeasurement: Initializing\n"); // it's possible to have this plugin enabled, only for displaying values on the screen. // therefore, we should only enable the sensor loop if measurement is also enabled - switch(radioConfig.preferences.environmental_measurement_plugin_sensor_type) { - case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11: - dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin,DHT11); - this->dht->begin(); - this->dht->read(); - DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n",radioConfig.preferences.environmental_measurement_plugin_sensor_pin); + switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) { + case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11: + dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin, DHT11); + this->dht->begin(); + this->dht->read(); + DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n", + radioConfig.preferences.environmental_measurement_plugin_sensor_pin); + return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20: + oneWire = new OneWire(radioConfig.preferences.environmental_measurement_plugin_sensor_pin); + ds18b20 = new DS18B20(oneWire); + this->ds18b20->begin(); + this->ds18b20->setResolution(12); + this->ds18b20->requestTemperatures(); + DEBUG_MSG("EnvironmentalMeasurement: Opened DS18B20 on pin: %d\n", + radioConfig.preferences.environmental_measurement_plugin_sensor_pin); + return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + default: + DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin"); + return (INT32_MAX); break; - default: - DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin"); - return (INT32_MAX); - break; } - // begin reading measurements from the sensor - // DHT have a max read-rate of 1HZ, so we should wait at least 1 second - // after initializing the sensor before we try to read from it. - // returning the interval here means that the next time OSThread - // calls our plugin, we'll run the other branch of this if statement - // and actually do a "sendOurEnvironmentalMeasurement()" - return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); } return (INT32_MAX); - } - else { - if (!radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) - { + } else { + if (!radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) { // if we somehow got to a second run of this plugin with measurement disabled, then just wait forever // I can't imagine we'd ever get here though. return (INT32_MAX); } // this is not the first time OSThread library has called this function // so just do what we intend to do on the interval - if(sensor_read_error_count > radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold) - { - if (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0 ) { - DEBUG_MSG( - "EnvironmentalMeasurement: TEMPORARILY DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in %d seconds\n", - radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold, - radioConfig.preferences.environmental_measurement_plugin_recovery_interval); - sensor_read_error_count = 0; - return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000); + if (sensor_read_error_count > radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold) { + if (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0) { + DEBUG_MSG("EnvironmentalMeasurement: TEMPORARILY DISABLED; The " + "environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in " + "%d seconds\n", + radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold, + radioConfig.preferences.environmental_measurement_plugin_recovery_interval); + sensor_read_error_count = 0; + return (radioConfig.preferences.environmental_measurement_plugin_recovery_interval * 1000); } - DEBUG_MSG( - "EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Reads will not be retried until after device reset\n", - radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold); - return(INT32_MAX); + DEBUG_MSG("EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has " + "been exceed: %d. Reads will not be retried until after device reset\n", + radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold); + return (INT32_MAX); - + } else if (sensor_read_error_count > 0) { + DEBUG_MSG("EnvironmentalMeasurement: There have been %d sensor read failures. Will retry %d more times\n", + sensor_read_error_count, sensor_read_error_count, sensor_read_error_count, + radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold - + sensor_read_error_count); } - else if (sensor_read_error_count > 0){ - DEBUG_MSG("EnvironmentalMeasurement: There have been %d sensor read failures. Will retry %d more times\n", - sensor_read_error_count, - radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-sensor_read_error_count); - } - if (!sendOurEnvironmentalMeasurement() ){ - // if we failed to read the sensor, then try again + if (!sendOurEnvironmentalMeasurement()) { + // if we failed to read the sensor, then try again // as soon as we can according to the maximum polling frequency - return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + // return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + + switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) { + case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11: + return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20: + return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + default: + return (DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + } } } - // The return of runOnce is an int32 representing the desired number of - // miliseconds until the function should be called again by the + // The return of runOnce is an int32 representing the desired number of + // miliseconds until the function should be called again by the // OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds - return(radioConfig.preferences.environmental_measurement_plugin_update_interval * 1000); + return (radioConfig.preferences.environmental_measurement_plugin_update_interval * 1000); #endif } -bool EnvironmentalMeasurementPlugin::wantUIFrame() { +bool EnvironmentalMeasurementPlugin::wantUIFrame() +{ return radioConfig.preferences.environmental_measurement_plugin_screen_enabled; } -String GetSenderName(const MeshPacket &mp) { +String GetSenderName(const MeshPacket &mp) +{ String sender; auto node = nodeDB.getNode(getFrom(&mp)); - if (node){ + if (node) { sender = node->user.short_name; - } - else { + } else { sender = "UNK"; } return sender; } -uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) { +uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) +{ uint32_t now = getTime(); uint32_t last_seen = mp->rx_time; @@ -156,15 +172,13 @@ uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) { delta = 0; return delta; - } - -float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) { - return (c*9)/5 + 32; +float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) +{ + return (c * 9) / 5 + 32; } - void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->setTextAlignment(TEXT_ALIGN_LEFT); @@ -173,21 +187,16 @@ void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplay if (lastMeasurementPacket == nullptr) { display->setFont(FONT_SMALL); display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement"); - //DEBUG_MSG("EnvironmentalMeasurement: No previous measurement; not drawing frame\n"); return; } EnvironmentalMeasurement lastMeasurement; - - + uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket); String lastSender = GetSenderName(*lastMeasurementPacket); auto &p = lastMeasurementPacket->decoded; - if (!pb_decode_from_bytes(p.payload.bytes, - p.payload.size, - EnvironmentalMeasurement_fields, - &lastMeasurement)) { + if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, EnvironmentalMeasurement_fields, &lastMeasurement)) { display->setFont(FONT_SMALL); display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet"); @@ -195,20 +204,23 @@ void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplay } display->setFont(FONT_SMALL); - String last_temp = String(lastMeasurement.temperature,0) +"°C"; - if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit){ - last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature),0) +"°F";; + String last_temp = String(lastMeasurement.temperature, 0) + "°C"; + if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit) { + last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature), 0) + "°F"; + ; } - display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": "+last_temp +"/"+ String(lastMeasurement.relative_humidity,0) + "%("+String(agoSecs)+"s)"); - + display->drawString(x, y += fontHeight(FONT_MEDIUM), + lastSender + ": " + last_temp + "/" + String(lastMeasurement.relative_humidity, 0) + "%(" + + String(agoSecs) + "s)"); } bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, EnvironmentalMeasurement *p) { - if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){ - // If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume - return false; + if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || + radioConfig.preferences.environmental_measurement_plugin_screen_enabled)) { + // If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume + return false; } String sender = GetSenderName(mp); @@ -230,13 +242,32 @@ bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum des DEBUG_MSG("-----------------------------------------\n"); DEBUG_MSG("EnvironmentalMeasurement: Read data\n"); - if (!this->dht->read(true)){ - sensor_read_error_count++; - DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n"); + + switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) { + case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11: + if (!this->dht->read(true)) { + sensor_read_error_count++; + DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n"); + return false; + } + m.relative_humidity = this->dht->readHumidity(); + m.temperature = this->dht->readTemperature(); + break; + case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20: + if (this->ds18b20->isConversionComplete()) { + m.temperature = this->ds18b20->getTempC(); + m.relative_humidity = 0; // This sensor is temperature only + this->ds18b20->requestTemperatures(); + break; + } else { + sensor_read_error_count++; + DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n"); + return false; + } + default: + DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin"); return false; } - m.relative_humidity = this->dht->readHumidity(); - m.temperature = this->dht->readTemperature(); DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity); DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature); @@ -250,4 +281,3 @@ bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum des service.sendToMesh(p); return true; } - diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h index 88306ab2..e60a84c8 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h @@ -1,15 +1,20 @@ #pragma once -#include "ProtobufPlugin.h" #include "../mesh/generated/environmental_measurement.pb.h" +#include "ProtobufPlugin.h" +#include +#include #include #include -#include +#include class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin { public: - EnvironmentalMeasurementPlugin(): concurrency::OSThread("EnvironmentalMeasurementPlugin"), ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) { - lastMeasurementPacket = nullptr; + EnvironmentalMeasurementPlugin() + : concurrency::OSThread("EnvironmentalMeasurementPlugin"), + ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) + { + lastMeasurementPacket = nullptr; } virtual bool wantUIFrame(); virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); @@ -24,11 +29,13 @@ class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public Pro * Send our EnvironmentalMeasurement into the mesh */ bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - + private: float CelsiusToFarenheit(float c); bool firstTime = 1; - DHT* dht; + DHT *dht; + OneWire *oneWire; + DS18B20 *ds18b20; const MeshPacket *lastMeasurementPacket; uint32_t sensor_read_error_count = 0; }; \ No newline at end of file