diff --git a/proto b/proto index 94bd0aae4..270cbdb68 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 94bd0aae44e2c16c7776289225c804100c856cd4 +Subproject commit 270cbdb6801761f054cb79f64b68b8a75cfb50f6 diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index f9be1cada..7c0ac9145 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -67,7 +67,7 @@ extern const pb_msgdesc_t AdminMessage_msg; #define AdminMessage_fields &AdminMessage_msg /* Maximum encoded size of messages (where known) */ -#define AdminMessage_size 338 +#define AdminMessage_size 351 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index a1e6de3af..dd3451849 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -82,7 +82,7 @@ extern const pb_msgdesc_t DeviceState_msg; #define DeviceState_fields &DeviceState_msg /* Maximum encoded size of messages (where known) */ -#define DeviceState_size 6156 +#define DeviceState_size 6169 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/portnums.pb.h b/src/mesh/generated/portnums.pb.h index 758db47a6..5904a548c 100644 --- a/src/mesh/generated/portnums.pb.h +++ b/src/mesh/generated/portnums.pb.h @@ -20,10 +20,10 @@ typedef enum _PortNum { PortNum_ADMIN_APP = 6, PortNum_REPLY_APP = 32, PortNum_IP_TUNNEL_APP = 33, - PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 34, PortNum_SERIAL_APP = 64, PortNum_STORE_FORWARD_APP = 65, PortNum_RANGE_TEST_APP = 66, + PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67, PortNum_PRIVATE_APP = 256, PortNum_ATAK_FORWARDER = 257, PortNum_MAX = 511 diff --git a/src/mesh/generated/radioconfig.pb.c b/src/mesh/generated/radioconfig.pb.c index 85aa7aa18..716b804e8 100644 --- a/src/mesh/generated/radioconfig.pb.c +++ b/src/mesh/generated/radioconfig.pb.c @@ -17,3 +17,4 @@ PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2) + diff --git a/src/mesh/generated/radioconfig.pb.h b/src/mesh/generated/radioconfig.pb.h index b8a6bfd7a..85bfa1a2d 100644 --- a/src/mesh/generated/radioconfig.pb.h +++ b/src/mesh/generated/radioconfig.pb.h @@ -56,6 +56,10 @@ typedef enum _LocationSharing { LocationSharing_LocDisabled = 2 } LocationSharing; +typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType { + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0 +} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType; + /* Struct definitions */ typedef struct _RadioConfig_UserPreferences { uint32_t position_broadcast_secs; @@ -106,6 +110,9 @@ typedef struct _RadioConfig_UserPreferences { uint32_t environmental_measurement_plugin_read_error_count_threshold; uint32_t environmental_measurement_plugin_update_interval; uint32_t environmental_measurement_plugin_recovery_interval; + bool environmental_measurement_plugin_display_farenheit; + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType environmental_measurement_plugin_sensor_type; + uint32_t environmental_measurement_plugin_sensor_pin; } RadioConfig_UserPreferences; typedef struct _RadioConfig { @@ -131,6 +138,10 @@ typedef struct _RadioConfig { #define _LocationSharing_MAX LocationSharing_LocDisabled #define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1)) +#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 +#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 +#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11+1)) + #ifdef __cplusplus extern "C" { @@ -138,9 +149,9 @@ extern "C" { /* Initializer values for message structs */ #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default} -#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero} -#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0} /* Field tags (for use in manual encoding/decoding) */ #define RadioConfig_UserPreferences_position_broadcast_secs_tag 1 @@ -190,6 +201,9 @@ extern "C" { #define RadioConfig_UserPreferences_environmental_measurement_plugin_read_error_count_threshold_tag 142 #define RadioConfig_UserPreferences_environmental_measurement_plugin_update_interval_tag 143 #define RadioConfig_UserPreferences_environmental_measurement_plugin_recovery_interval_tag 144 +#define RadioConfig_UserPreferences_environmental_measurement_plugin_display_farenheit_tag 145 +#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_type_tag 146 +#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_pin_tag 147 #define RadioConfig_preferences_tag 1 /* Struct field encoding specification for nanopb */ @@ -246,7 +260,10 @@ X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_measurement_ X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_screen_enabled, 141) \ X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_read_error_count_threshold, 142) \ X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_update_interval, 143) \ -X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144) +X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144) \ +X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_display_farenheit, 145) \ +X(a, STATIC, SINGULAR, UENUM, environmental_measurement_plugin_sensor_type, 146) \ +X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_sensor_pin, 147) #define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_DEFAULT NULL @@ -258,8 +275,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg; #define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg /* Maximum encoded size of messages (where known) */ -#define RadioConfig_size 335 -#define RadioConfig_UserPreferences_size 332 +#define RadioConfig_size 348 +#define RadioConfig_UserPreferences_size 345 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp index b332efd6e..4ade032be 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp @@ -10,20 +10,10 @@ #include #include -EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin; -EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio; - -EnvironmentalMeasurementPlugin::EnvironmentalMeasurementPlugin() : concurrency::OSThread("EnvironmentalMeasurementPlugin") {} - -uint32_t sensor_read_error_count = 0; - -#define DHT_11_GPIO_PIN 13 #define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true -DHT dht(DHT_11_GPIO_PIN,DHT11); - #ifdef HAS_EINK // The screen is bigger so use bigger fonts @@ -49,11 +39,15 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { 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_recovery_interval = 600;*/ + radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60; + radioConfig.preferences.environmental_measurement_plugin_display_farenheit = true; + radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13; + radioConfig.preferences.environmental_measurement_plugin_sensor_type = RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11;*/ 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 @@ -62,20 +56,32 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { if (firstTime) { // This is the first time the OSThread library has called this function, so do some setup - DEBUG_MSG("EnvironmentalMeasurement: Initializing\n"); - environmentalMeasurementPluginRadio = new EnvironmentalMeasurementPluginRadio(); + firstTime = 0; - // 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()" + 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 - dht.begin(); + 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); + 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); @@ -96,6 +102,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { "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( @@ -110,7 +117,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { sensor_read_error_count, radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-sensor_read_error_count); } - if (! environmentalMeasurementPluginRadio->sendOurEnvironmentalMeasurement() ){ + 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); @@ -123,20 +130,10 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { #endif } -bool EnvironmentalMeasurementPluginRadio::wantUIFrame() { +bool EnvironmentalMeasurementPlugin::wantUIFrame() { return radioConfig.preferences.environmental_measurement_plugin_screen_enabled; } -void EnvironmentalMeasurementPluginRadio::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -{ - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(FONT_MEDIUM); - display->drawString(x, y, "Environment"); - display->setFont(FONT_SMALL); - display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": T:"+ String(lastMeasurement.temperature,2) + " H:" + String(lastMeasurement.relative_humidity,2)); - -} - String GetSenderName(const MeshPacket &mp) { String sender; @@ -149,55 +146,99 @@ String GetSenderName(const MeshPacket &mp) { return sender; } -bool EnvironmentalMeasurementPluginRadio::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *pptr) +uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) { + uint32_t now = getTime(); + + uint32_t last_seen = mp->rx_time; + int delta = (int)(now - last_seen); + if (delta < 0) // our clock must be slightly off still - not set from GPS yet + delta = 0; + + return delta; + +} + + +float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) { + return (c*9)/5 + 32; +} + + +void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { - const EnvironmentalMeasurement &p = *pptr; + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_MEDIUM); + display->drawString(x, y, "Environment"); + 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)) { + display->setFont(FONT_SMALL); + display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); + DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet"); + return; + } + + 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";; + } + + display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": "+last_temp +"/"+ String(lastMeasurement.relative_humidity,0) + "%("+String(agoSecs)+"s)"); + +} + +bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, const 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; } - bool wasBroadcast = mp.to == NODENUM_BROADCAST; String sender = GetSenderName(mp); - - // Show new nodes on LCD screen - if (DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN && wasBroadcast) { - String lcd = String("Env Measured: ") +sender + "\n" + - "T: " + p.temperature + "\n" + - "H: " + p.relative_humidity + "\n"; - screen->print(lcd.c_str()); - } - DEBUG_MSG("-----------------------------------------\n"); DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n", sender); - DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p.relative_humidity); - DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p.temperature); + DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p->relative_humidity); + DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p->temperature); + + lastMeasurementPacket = packetPool.allocCopy(mp); - lastMeasurement = p; - lastSender = sender; return false; // Let others look at this message also if they want } -bool EnvironmentalMeasurementPluginRadio::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies) +bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies) { EnvironmentalMeasurement m; m.barometric_pressure = 0; // TODO: Add support for barometric sensors - m.relative_humidity = dht.readHumidity(); - m.temperature = dht.readTemperature();; - DEBUG_MSG("-----------------------------------------\n"); DEBUG_MSG("EnvironmentalMeasurement: Read data\n"); - DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity); - DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature); - - if (isnan(m.relative_humidity) || isnan(m.temperature) ){ + 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(); + + DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity); + DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature); sensor_read_error_count = 0; diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h index e29ea983e..3123cc0f0 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h @@ -3,61 +3,32 @@ #include "../mesh/generated/environmental_measurement.pb.h" #include #include +#include - -class EnvironmentalMeasurementPlugin : private concurrency::OSThread +class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin { - bool firstTime = 1; - public: - EnvironmentalMeasurementPlugin(); + 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); protected: + /** Called to handle a particular incoming message + @return true if you've guaranteed you've handled this message and no other handlers should be considered for it + */ + virtual bool handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p); virtual int32_t runOnce(); -}; - -extern EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin; - -/** - * EnvironmentalMeasurementPluginRadio plugin for sending/receiving environmental measurements to/from the mesh - */ -class EnvironmentalMeasurementPluginRadio : public ProtobufPlugin -{ - public: - /** Constructor - * name is for debugging output - */ - EnvironmentalMeasurementPluginRadio() : ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) { - lastMeasurement.barometric_pressure = nanf(""); - lastMeasurement.relative_humidity = nanf(""); - lastMeasurement.temperature = nanf(""); - lastSender = "N/A"; - } - /** * Send our EnvironmentalMeasurement into the mesh */ bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); - - protected: - - /** Called to handle a particular incoming message - - @return true if you've guaranteed you've handled this message and no other handlers should be considered for it - */ - virtual bool handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p); - - virtual bool wantUIFrame(); - - private: - - EnvironmentalMeasurement lastMeasurement; - - String lastSender; - -}; - -extern EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio; \ No newline at end of file + float CelsiusToFarenheit(float c); + bool firstTime = 1; + DHT* dht; + const MeshPacket *lastMeasurementPacket; + uint32_t sensor_read_error_count = 0; +}; \ No newline at end of file