2021-01-17 15:59:48 -05:00
# include "EnvironmentalMeasurementPlugin.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 <DHT.h>
2021-02-21 16:46:46 -05:00
# include <OLEDDisplay.h>
# include <OLEDDisplayUi.h>
2021-01-17 15:59:48 -05:00
2021-02-22 19:50:51 -05:00
# define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts
2021-02-21 11:39:45 -05:00
# define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
# define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
2021-01-17 15:59:48 -05:00
2021-02-21 16:46:46 -05:00
# ifdef HAS_EINK
// The screen is bigger so use bigger fonts
# define FONT_SMALL ArialMT_Plain_16
# define FONT_MEDIUM ArialMT_Plain_24
# define FONT_LARGE ArialMT_Plain_24
# else
# define FONT_SMALL ArialMT_Plain_10
# define FONT_MEDIUM ArialMT_Plain_16
# define FONT_LARGE ArialMT_Plain_24
# endif
# define fontHeight(font) ((font)[1] + 1) // height is position 1
# define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
# define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
2021-01-17 15:59:48 -05:00
int32_t EnvironmentalMeasurementPlugin : : runOnce ( ) {
2021-02-22 19:50:51 -05:00
# 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 .
*/
2021-03-02 21:12:22 -05:00
2021-02-22 20:47:35 -05:00
/*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
radioConfig . preferences . environmental_measurement_plugin_screen_enabled = 1 ;
2021-02-22 19:50:51 -05:00
radioConfig . preferences . environmental_measurement_plugin_read_error_count_threshold = 5 ;
radioConfig . preferences . environmental_measurement_plugin_update_interval = 30 ;
2021-02-27 23:23:50 -05:00
radioConfig . preferences . environmental_measurement_plugin_recovery_interval = 60 ;
2021-03-02 21:12:22 -05:00
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 ; */
2021-02-22 19:50:51 -05:00
2021-02-22 20:47:35 -05:00
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
2021-02-21 11:48:32 -05:00
return ( INT32_MAX ) ;
}
2021-02-22 19:50:51 -05:00
2021-01-17 15:59:48 -05:00
if ( firstTime ) {
// This is the first time the OSThread library has called this function, so do some setup
2021-03-02 21:12:22 -05:00
2021-01-17 15:59:48 -05:00
firstTime = 0 ;
2021-03-02 21:12:22 -05:00
2021-02-22 20:47:35 -05:00
if ( radioConfig . preferences . environmental_measurement_plugin_measurement_enabled )
{
2021-03-02 21:12:22 -05:00
DEBUG_MSG ( " EnvironmentalMeasurement: Initializing \n " ) ;
2021-02-22 20:47:35 -05:00
// 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
2021-03-02 21:12:22 -05:00
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()"
2021-02-22 20:47:35 -05:00
return ( DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS ) ;
}
return ( INT32_MAX ) ;
2021-01-17 15:59:48 -05:00
}
else {
2021-02-22 20:47:35 -05:00
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 ) ;
}
2021-01-17 15:59:48 -05:00
// this is not the first time OSThread library has called this function
// so just do what we intend to do on the interval
2021-02-22 19:50:51 -05:00
if ( sensor_read_error_count > radioConfig . preferences . environmental_measurement_plugin_read_error_count_threshold )
2021-01-17 15:59:48 -05:00
{
2021-02-22 19:50:51 -05:00
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 ) ;
2021-02-27 23:23:50 -05:00
sensor_read_error_count = 0 ;
2021-02-22 19:50:51 -05:00
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 ) ;
2021-01-17 15:59:48 -05:00
}
else if ( sensor_read_error_count > 0 ) {
2021-02-22 19:50:51 -05:00
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 ) ;
2021-01-17 15:59:48 -05:00
}
2021-03-02 21:12:22 -05:00
if ( ! sendOurEnvironmentalMeasurement ( ) ) {
2021-01-17 15:59:48 -05:00
// 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 ) ;
}
}
// The return of runOnce is an int32 representing the desired number of
// miliseconds until the function should be called again by the
2021-02-22 19:50:51 -05:00
// OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds
return ( radioConfig . preferences . environmental_measurement_plugin_update_interval * 1000 ) ;
2021-01-17 15:59:48 -05:00
# endif
}
2021-03-02 21:12:22 -05:00
bool EnvironmentalMeasurementPlugin : : wantUIFrame ( ) {
2021-02-22 20:47:35 -05:00
return radioConfig . preferences . environmental_measurement_plugin_screen_enabled ;
}
2021-02-21 16:46:46 -05:00
String GetSenderName ( const MeshPacket & mp ) {
2021-02-21 11:39:45 -05:00
String sender ;
2021-02-21 16:46:46 -05:00
2021-03-05 10:19:27 +08:00
auto node = nodeDB . getNode ( getFrom ( & mp ) ) ;
if ( node ) {
sender = node - > user . short_name ;
2021-02-21 11:39:45 -05:00
}
else {
sender = " UNK " ;
}
2021-02-21 16:46:46 -05:00
return sender ;
}
2021-02-27 23:23:50 -05:00
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 ;
}
2021-03-02 21:12:22 -05:00
float EnvironmentalMeasurementPlugin : : CelsiusToFarenheit ( float c ) {
2021-02-27 23:23:50 -05:00
return ( c * 9 ) / 5 + 32 ;
}
2021-03-02 21:12:22 -05:00
void EnvironmentalMeasurementPlugin : : drawFrame ( OLEDDisplay * display , OLEDDisplayUiState * state , int16_t x , int16_t y )
2021-02-27 23:23:50 -05:00
{
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 " ) ;
2021-03-02 21:12:22 -05:00
//DEBUG_MSG("EnvironmentalMeasurement: No previous measurement; not drawing frame\n");
2021-02-27 23:23:50 -05:00
return ;
}
EnvironmentalMeasurement lastMeasurement ;
uint32_t agoSecs = GetTimeSinceMeshPacket ( lastMeasurementPacket ) ;
String lastSender = GetSenderName ( * lastMeasurementPacket ) ;
2021-03-02 21:12:22 -05:00
auto & p = lastMeasurementPacket - > decoded ;
2021-02-27 23:23:50 -05:00
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) " ) ;
}
2021-03-02 21:12:22 -05:00
bool EnvironmentalMeasurementPlugin : : handleReceivedProtobuf ( const MeshPacket & mp , const EnvironmentalMeasurement * p )
2021-02-21 16:46:46 -05:00
{
2021-02-22 20:47:35 -05:00
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
2021-02-21 16:46:46 -05:00
return false ;
}
String sender = GetSenderName ( mp ) ;
2021-02-21 11:39:45 -05:00
2021-02-21 16:46:46 -05:00
DEBUG_MSG ( " EnvironmentalMeasurement: Received data from %s \n " , sender ) ;
2021-03-02 21:12:22 -05:00
DEBUG_MSG ( " EnvironmentalMeasurement->relative_humidity: %f \n " , p - > relative_humidity ) ;
DEBUG_MSG ( " EnvironmentalMeasurement->temperature: %f \n " , p - > temperature ) ;
2021-02-21 16:46:46 -05:00
2021-02-27 23:23:50 -05:00
lastMeasurementPacket = packetPool . allocCopy ( mp ) ;
2021-01-17 15:59:48 -05:00
return false ; // Let others look at this message also if they want
}
2021-03-02 21:12:22 -05:00
bool EnvironmentalMeasurementPlugin : : sendOurEnvironmentalMeasurement ( NodeNum dest , bool wantReplies )
2021-01-17 15:59:48 -05:00
{
EnvironmentalMeasurement m ;
2021-02-21 11:00:58 -05:00
m . barometric_pressure = 0 ; // TODO: Add support for barometric sensors
2021-01-17 15:59:48 -05:00
DEBUG_MSG ( " ----------------------------------------- \n " ) ;
2021-02-21 11:39:20 -05:00
DEBUG_MSG ( " EnvironmentalMeasurement: Read data \n " ) ;
2021-03-02 21:12:22 -05:00
if ( ! this - > dht - > read ( true ) ) {
2021-01-17 15:59:48 -05:00
sensor_read_error_count + + ;
2021-02-21 11:39:20 -05:00
DEBUG_MSG ( " EnvironmentalMeasurement: FAILED TO READ DATA \n " ) ;
2021-01-17 15:59:48 -05:00
return false ;
}
2021-03-02 21:12:22 -05:00
m . relative_humidity = this - > dht - > readHumidity ( ) ;
m . temperature = this - > dht - > readTemperature ( ) ;
2021-02-27 23:23:50 -05:00
DEBUG_MSG ( " EnvironmentalMeasurement->relative_humidity: %f \n " , m . relative_humidity ) ;
DEBUG_MSG ( " EnvironmentalMeasurement->temperature: %f \n " , m . temperature ) ;
2021-01-17 15:59:48 -05:00
sensor_read_error_count = 0 ;
MeshPacket * p = allocDataProtobuf ( m ) ;
p - > to = dest ;
p - > decoded . want_response = wantReplies ;
service . sendToMesh ( p ) ;
return true ;
}