From ba2fa84ebd247b180c13de27a7ba75354798f790 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 27 Mar 2022 14:55:35 +0000 Subject: [PATCH] =?UTF-8?q?Reworked=20metrics=20structure=20and=20split=20?= =?UTF-8?q?telemetry=20into=20device=20or=20environ=E2=80=A6=20(#1331)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Reworked metrics structure and split telemetry into device or environment * Comment cleanup --- proto | 2 +- src/mesh/NodeDB.cpp | 4 +- src/mesh/NodeDB.h | 5 +- src/mesh/ProtobufModule.h | 2 + src/mesh/generated/admin.pb.h | 2 +- src/mesh/generated/deviceonly.pb.h | 2 +- src/mesh/generated/mesh.pb.c | 2 +- src/mesh/generated/mesh.pb.h | 4 +- src/mesh/generated/radioconfig.pb.h | 61 ++-- src/mesh/generated/telemetry.pb.c | 6 + src/mesh/generated/telemetry.pb.h | 105 ++++-- src/modules/Modules.cpp | 6 +- src/modules/Telemetry/DeviceTelemetry.cpp | 89 ++++++ src/modules/Telemetry/DeviceTelemetry.h | 33 ++ .../Telemetry/EnvironmentTelemetry.cpp | 302 ++++++++++++++++++ .../{Telemetry.h => EnvironmentTelemetry.h} | 8 +- src/modules/Telemetry/Sensor/BME280Sensor.cpp | 6 +- src/modules/Telemetry/Sensor/BME680Sensor.cpp | 8 +- src/modules/Telemetry/Sensor/DHTSensor.cpp | 10 +- src/modules/Telemetry/Sensor/DallasSensor.cpp | 8 +- .../Telemetry/Sensor/MCP9808Sensor.cpp | 2 +- src/modules/Telemetry/Telemetry.cpp | 276 ---------------- src/mqtt/MQTT.cpp | 20 +- 23 files changed, 584 insertions(+), 379 deletions(-) create mode 100644 src/modules/Telemetry/DeviceTelemetry.cpp create mode 100644 src/modules/Telemetry/DeviceTelemetry.h create mode 100644 src/modules/Telemetry/EnvironmentTelemetry.cpp rename src/modules/Telemetry/{Telemetry.h => EnvironmentTelemetry.h} (77%) delete mode 100644 src/modules/Telemetry/Telemetry.cpp diff --git a/proto b/proto index 3689ca4bd..6d355041b 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 3689ca4bde316dca2601c2a2f91fa71204f7fa43 +Subproject commit 6d355041bb0a30d3fdb3f15a0c03963ec3fe2bf3 diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index a53074c21..52e76a88e 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -490,11 +490,13 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p, RxSource src) /** Update telemetry info for this node based on received metrics + * We only care about device telemetry here */ void NodeDB::updateTelemetry(uint32_t nodeId, const Telemetry &t, RxSource src) { NodeInfo *info = getOrCreateNode(nodeId); - if (!info) { + // Environment metrics should never go to NodeDb but we'll safegaurd anyway + if (!info || t.which_variant != Telemetry_device_metrics_tag) { return; } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 6e05a3d5e..551d30932 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -168,7 +168,10 @@ extern NodeDB nodeDB; PREF_GET(send_owner_interval, IF_ROUTER(2, 4)) PREF_GET(position_broadcast_secs, IF_ROUTER(12 * 60 * 60, 15 * 60)) -PREF_GET(telemetry_module_update_interval, 2 * 60) +// Defaulting Telemetry to the same as position interval for now +PREF_GET(telemetry_module_device_update_interval, IF_ROUTER(12 * 60 * 60, 15 * 60)) +PREF_GET(telemetry_module_environment_update_interval, IF_ROUTER(12 * 60 * 60, 15 * 60)) + // Each time we wake into the DARK state allow 1 minute to send and receive BLE packets to the phone PREF_GET(wait_bluetooth_secs, IF_ROUTER(1, 60)) diff --git a/src/mesh/ProtobufModule.h b/src/mesh/ProtobufModule.h index e7eaac57a..dad0eade3 100644 --- a/src/mesh/ProtobufModule.h +++ b/src/mesh/ProtobufModule.h @@ -22,6 +22,8 @@ template class ProtobufModule : protected SinglePortModule } protected: + + /** * Handle a received message, the data field in the message is already decoded and is provided * diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index 1e2e27ccf..1f93abd56 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -129,7 +129,7 @@ extern const pb_msgdesc_t AdminMessage_msg; #define AdminMessage_fields &AdminMessage_msg /* Maximum encoded size of messages (where known) */ -#define AdminMessage_size 603 +#define AdminMessage_size 610 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index 0e0a8b45d..626cc2eb4 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -108,7 +108,7 @@ extern const pb_msgdesc_t ChannelFile_msg; /* Maximum encoded size of messages (where known) */ #define ChannelFile_size 832 -#define DeviceState_size 11199 +#define DeviceState_size 10687 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/mesh.pb.c b/src/mesh/generated/mesh.pb.c index 2ebb89991..37fdda6eb 100644 --- a/src/mesh/generated/mesh.pb.c +++ b/src/mesh/generated/mesh.pb.c @@ -27,7 +27,7 @@ PB_BIND(Location, Location, AUTO) PB_BIND(MeshPacket, MeshPacket, 2) -PB_BIND(NodeInfo, NodeInfo, 2) +PB_BIND(NodeInfo, NodeInfo, AUTO) PB_BIND(MyNodeInfo, MyNodeInfo, AUTO) diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 5e639c7eb..3f9c48512 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -574,7 +574,7 @@ typedef struct _NodeInfo { float snr; /* Set to indicate the last time we received a packet from this node */ uint32_t last_heard; - /* The latest telemetry data for the node. */ + /* The latest device telemetry data for the node. */ bool has_telemetry; Telemetry telemetry; } NodeInfo; @@ -1079,7 +1079,7 @@ extern const pb_msgdesc_t ToRadio_PeerInfo_msg; #define LogRecord_size 81 #define MeshPacket_size 347 #define MyNodeInfo_size 210 -#define NodeInfo_size 315 +#define NodeInfo_size 299 #define Position_size 142 #define RouteDiscovery_size 40 #define Routing_size 42 diff --git a/src/mesh/generated/radioconfig.pb.h b/src/mesh/generated/radioconfig.pb.h index 97c542b71..b3acc3cb5 100644 --- a/src/mesh/generated/radioconfig.pb.h +++ b/src/mesh/generated/radioconfig.pb.h @@ -299,14 +299,14 @@ typedef struct _RadioConfig_UserPreferences { uint32_t store_forward_module_records; uint32_t store_forward_module_history_return_max; uint32_t store_forward_module_history_return_window; - bool telemetry_module_measurement_enabled; - bool telemetry_module_screen_enabled; - uint32_t telemetry_module_read_error_count_threshold; - uint32_t telemetry_module_update_interval; - uint32_t telemetry_module_recovery_interval; - bool telemetry_module_display_fahrenheit; - RadioConfig_UserPreferences_TelemetrySensorType telemetry_module_sensor_type; - uint32_t telemetry_module_sensor_pin; + bool telemetry_module_environment_measurement_enabled; + bool telemetry_module_environment_screen_enabled; + uint32_t telemetry_module_environment_read_error_count_threshold; + uint32_t telemetry_module_device_update_interval; + uint32_t telemetry_module_environment_recovery_interval; + bool telemetry_module_environment_display_fahrenheit; + RadioConfig_UserPreferences_TelemetrySensorType telemetry_module_environment_sensor_type; + uint32_t telemetry_module_environment_sensor_pin; bool store_forward_module_enabled; bool store_forward_module_heartbeat; uint32_t position_flags; @@ -332,6 +332,7 @@ typedef struct _RadioConfig_UserPreferences { bool mqtt_encryption_enabled; float adc_multiplier_override; RadioConfig_UserPreferences_Serial_Baud serial_module_baud; + uint32_t telemetry_module_environment_update_interval; } RadioConfig_UserPreferences; /* The entire set of user settable/readable settings for our radio device. @@ -388,9 +389,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, 0, _Role_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_Serial_Mode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_TelemetrySensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, 0, "", 0, 0, 0, _RadioConfig_UserPreferences_Serial_Baud_MIN} +#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _Role_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_Serial_Mode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_TelemetrySensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, 0, "", 0, 0, 0, _RadioConfig_UserPreferences_Serial_Baud_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, 0, _Role_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_Serial_Mode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_TelemetrySensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, 0, "", 0, 0, 0, _RadioConfig_UserPreferences_Serial_Baud_MIN} +#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _Role_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_Serial_Mode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_TelemetrySensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, 0, "", 0, 0, 0, _RadioConfig_UserPreferences_Serial_Baud_MIN, 0} /* Field tags (for use in manual encoding/decoding) */ #define RadioConfig_UserPreferences_position_broadcast_secs_tag 1 @@ -444,14 +445,14 @@ extern "C" { #define RadioConfig_UserPreferences_store_forward_module_records_tag 137 #define RadioConfig_UserPreferences_store_forward_module_history_return_max_tag 138 #define RadioConfig_UserPreferences_store_forward_module_history_return_window_tag 139 -#define RadioConfig_UserPreferences_telemetry_module_measurement_enabled_tag 140 -#define RadioConfig_UserPreferences_telemetry_module_screen_enabled_tag 141 -#define RadioConfig_UserPreferences_telemetry_module_read_error_count_threshold_tag 142 -#define RadioConfig_UserPreferences_telemetry_module_update_interval_tag 143 -#define RadioConfig_UserPreferences_telemetry_module_recovery_interval_tag 144 -#define RadioConfig_UserPreferences_telemetry_module_display_fahrenheit_tag 145 -#define RadioConfig_UserPreferences_telemetry_module_sensor_type_tag 146 -#define RadioConfig_UserPreferences_telemetry_module_sensor_pin_tag 147 +#define RadioConfig_UserPreferences_telemetry_module_environment_measurement_enabled_tag 140 +#define RadioConfig_UserPreferences_telemetry_module_environment_screen_enabled_tag 141 +#define RadioConfig_UserPreferences_telemetry_module_environment_read_error_count_threshold_tag 142 +#define RadioConfig_UserPreferences_telemetry_module_device_update_interval_tag 143 +#define RadioConfig_UserPreferences_telemetry_module_environment_recovery_interval_tag 144 +#define RadioConfig_UserPreferences_telemetry_module_environment_display_fahrenheit_tag 145 +#define RadioConfig_UserPreferences_telemetry_module_environment_sensor_type_tag 146 +#define RadioConfig_UserPreferences_telemetry_module_environment_sensor_pin_tag 147 #define RadioConfig_UserPreferences_store_forward_module_enabled_tag 148 #define RadioConfig_UserPreferences_store_forward_module_heartbeat_tag 149 #define RadioConfig_UserPreferences_position_flags_tag 150 @@ -477,6 +478,7 @@ extern "C" { #define RadioConfig_UserPreferences_mqtt_encryption_enabled_tag 174 #define RadioConfig_UserPreferences_adc_multiplier_override_tag 175 #define RadioConfig_UserPreferences_serial_module_baud_tag 176 +#define RadioConfig_UserPreferences_telemetry_module_environment_update_interval_tag 177 #define RadioConfig_preferences_tag 1 /* Struct field encoding specification for nanopb */ @@ -538,14 +540,14 @@ X(a, STATIC, SINGULAR, BOOL, range_test_module_save, 134) \ X(a, STATIC, SINGULAR, UINT32, store_forward_module_records, 137) \ X(a, STATIC, SINGULAR, UINT32, store_forward_module_history_return_max, 138) \ X(a, STATIC, SINGULAR, UINT32, store_forward_module_history_return_window, 139) \ -X(a, STATIC, SINGULAR, BOOL, telemetry_module_measurement_enabled, 140) \ -X(a, STATIC, SINGULAR, BOOL, telemetry_module_screen_enabled, 141) \ -X(a, STATIC, SINGULAR, UINT32, telemetry_module_read_error_count_threshold, 142) \ -X(a, STATIC, SINGULAR, UINT32, telemetry_module_update_interval, 143) \ -X(a, STATIC, SINGULAR, UINT32, telemetry_module_recovery_interval, 144) \ -X(a, STATIC, SINGULAR, BOOL, telemetry_module_display_fahrenheit, 145) \ -X(a, STATIC, SINGULAR, UENUM, telemetry_module_sensor_type, 146) \ -X(a, STATIC, SINGULAR, UINT32, telemetry_module_sensor_pin, 147) \ +X(a, STATIC, SINGULAR, BOOL, telemetry_module_environment_measurement_enabled, 140) \ +X(a, STATIC, SINGULAR, BOOL, telemetry_module_environment_screen_enabled, 141) \ +X(a, STATIC, SINGULAR, UINT32, telemetry_module_environment_read_error_count_threshold, 142) \ +X(a, STATIC, SINGULAR, UINT32, telemetry_module_device_update_interval, 143) \ +X(a, STATIC, SINGULAR, UINT32, telemetry_module_environment_recovery_interval, 144) \ +X(a, STATIC, SINGULAR, BOOL, telemetry_module_environment_display_fahrenheit, 145) \ +X(a, STATIC, SINGULAR, UENUM, telemetry_module_environment_sensor_type, 146) \ +X(a, STATIC, SINGULAR, UINT32, telemetry_module_environment_sensor_pin, 147) \ X(a, STATIC, SINGULAR, BOOL, store_forward_module_enabled, 148) \ X(a, STATIC, SINGULAR, BOOL, store_forward_module_heartbeat, 149) \ X(a, STATIC, SINGULAR, UINT32, position_flags, 150) \ @@ -570,7 +572,8 @@ X(a, STATIC, SINGULAR, STRING, canned_message_module_allow_input_source, 171 X(a, STATIC, SINGULAR, BOOL, canned_message_module_send_bell, 173) \ X(a, STATIC, SINGULAR, BOOL, mqtt_encryption_enabled, 174) \ X(a, STATIC, SINGULAR, FLOAT, adc_multiplier_override, 175) \ -X(a, STATIC, SINGULAR, UENUM, serial_module_baud, 176) +X(a, STATIC, SINGULAR, UENUM, serial_module_baud, 176) \ +X(a, STATIC, SINGULAR, UINT32, telemetry_module_environment_update_interval, 177) #define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_DEFAULT NULL @@ -582,8 +585,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_UserPreferences_size 597 -#define RadioConfig_size 600 +#define RadioConfig_UserPreferences_size 604 +#define RadioConfig_size 607 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/telemetry.pb.c b/src/mesh/generated/telemetry.pb.c index 11d7b0d06..674a2f84d 100644 --- a/src/mesh/generated/telemetry.pb.c +++ b/src/mesh/generated/telemetry.pb.c @@ -6,6 +6,12 @@ #error Regenerate this file with the current version of nanopb generator. #endif +PB_BIND(DeviceMetrics, DeviceMetrics, AUTO) + + +PB_BIND(EnvironmentMetrics, EnvironmentMetrics, AUTO) + + PB_BIND(Telemetry, Telemetry, AUTO) diff --git a/src/mesh/generated/telemetry.pb.h b/src/mesh/generated/telemetry.pb.h index f71aa58f2..1fb5ea0d7 100644 --- a/src/mesh/generated/telemetry.pb.h +++ b/src/mesh/generated/telemetry.pb.h @@ -10,23 +10,20 @@ #endif /* Struct definitions */ -/* TODO: REPLACE */ -typedef struct _Telemetry { - /* This is usually not sent over the mesh (to save space), but it is sent - from the phone so that the local device can set its RTC If it is sent over - the mesh (because there are devices on the mesh without GPS), it will only - be sent by devices which has a hardware GPS clock (IE Mobile Phone). - seconds since 1970 */ - uint32_t time; +/* Key native device metrics such as battery level */ +typedef struct _DeviceMetrics { /* 1-100 (0 means powered) */ uint32_t battery_level; + /* Voltage measured */ + float voltage; /* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */ float channel_utilization; /* Percent of airtime for transmission used within the last hour. */ float air_util_tx; - /* This is sent by node only if it a router and if hop_limit is set to 0 - and is not being sent as a reliable message. */ - bool router_heartbeat; +} DeviceMetrics; + +/* Weather station or other environmental metrics */ +typedef struct _EnvironmentMetrics { /* Temperature measured */ float temperature; /* Relative humidity percent measured */ @@ -39,6 +36,22 @@ typedef struct _Telemetry { float voltage; /* Current measured */ float current; +} EnvironmentMetrics; + +/* Types of Measurements the telemetry module is equipped to handle */ +typedef struct _Telemetry { + /* This is usually not sent over the mesh (to save space), but it is sent + from the phone so that the local device can set its RTC If it is sent over + the mesh (because there are devices on the mesh without GPS), it will only + be sent by devices which has a hardware GPS clock (IE Mobile Phone). + seconds since 1970 */ + uint32_t time; + /* Key native device metrics such as battery level */ + pb_size_t which_variant; + union { + DeviceMetrics device_metrics; + EnvironmentMetrics environment_metrics; + } variant; } Telemetry; @@ -47,45 +60,69 @@ extern "C" { #endif /* Initializer values for message structs */ -#define Telemetry_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define Telemetry_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define DeviceMetrics_init_default {0, 0, 0, 0} +#define EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0} +#define Telemetry_init_default {0, 0, {DeviceMetrics_init_default}} +#define DeviceMetrics_init_zero {0, 0, 0, 0} +#define EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0} +#define Telemetry_init_zero {0, 0, {DeviceMetrics_init_zero}} /* Field tags (for use in manual encoding/decoding) */ +#define DeviceMetrics_battery_level_tag 1 +#define DeviceMetrics_voltage_tag 2 +#define DeviceMetrics_channel_utilization_tag 3 +#define DeviceMetrics_air_util_tx_tag 4 +#define EnvironmentMetrics_temperature_tag 1 +#define EnvironmentMetrics_relative_humidity_tag 2 +#define EnvironmentMetrics_barometric_pressure_tag 3 +#define EnvironmentMetrics_gas_resistance_tag 4 +#define EnvironmentMetrics_voltage_tag 5 +#define EnvironmentMetrics_current_tag 6 #define Telemetry_time_tag 1 -#define Telemetry_battery_level_tag 2 -#define Telemetry_channel_utilization_tag 3 -#define Telemetry_air_util_tx_tag 4 -#define Telemetry_router_heartbeat_tag 5 -#define Telemetry_temperature_tag 6 -#define Telemetry_relative_humidity_tag 7 -#define Telemetry_barometric_pressure_tag 8 -#define Telemetry_gas_resistance_tag 9 -#define Telemetry_voltage_tag 10 -#define Telemetry_current_tag 11 +#define Telemetry_device_metrics_tag 2 +#define Telemetry_environment_metrics_tag 3 /* Struct field encoding specification for nanopb */ +#define DeviceMetrics_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, battery_level, 1) \ +X(a, STATIC, SINGULAR, FLOAT, voltage, 2) \ +X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 3) \ +X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 4) +#define DeviceMetrics_CALLBACK NULL +#define DeviceMetrics_DEFAULT NULL + +#define EnvironmentMetrics_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FLOAT, temperature, 1) \ +X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 2) \ +X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3) \ +X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 4) \ +X(a, STATIC, SINGULAR, FLOAT, voltage, 5) \ +X(a, STATIC, SINGULAR, FLOAT, current, 6) +#define EnvironmentMetrics_CALLBACK NULL +#define EnvironmentMetrics_DEFAULT NULL + #define Telemetry_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, FIXED32, time, 1) \ -X(a, STATIC, SINGULAR, UINT32, battery_level, 2) \ -X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 3) \ -X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 4) \ -X(a, STATIC, SINGULAR, BOOL, router_heartbeat, 5) \ -X(a, STATIC, SINGULAR, FLOAT, temperature, 6) \ -X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 7) \ -X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 8) \ -X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 9) \ -X(a, STATIC, SINGULAR, FLOAT, voltage, 10) \ -X(a, STATIC, SINGULAR, FLOAT, current, 11) +X(a, STATIC, ONEOF, MESSAGE, (variant,device_metrics,variant.device_metrics), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) #define Telemetry_CALLBACK NULL #define Telemetry_DEFAULT NULL +#define Telemetry_variant_device_metrics_MSGTYPE DeviceMetrics +#define Telemetry_variant_environment_metrics_MSGTYPE EnvironmentMetrics +extern const pb_msgdesc_t DeviceMetrics_msg; +extern const pb_msgdesc_t EnvironmentMetrics_msg; extern const pb_msgdesc_t Telemetry_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define DeviceMetrics_fields &DeviceMetrics_msg +#define EnvironmentMetrics_fields &EnvironmentMetrics_msg #define Telemetry_fields &Telemetry_msg /* Maximum encoded size of messages (where known) */ -#define Telemetry_size 53 +#define DeviceMetrics_size 21 +#define EnvironmentMetrics_size 30 +#define Telemetry_size 37 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 3c90243cb..06eb1c56d 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -12,7 +12,8 @@ #include "modules/RoutingModule.h" #include "modules/TextMessageModule.h" #ifndef PORTDUINO -#include "modules/Telemetry/Telemetry.h" +#include "modules/Telemetry/DeviceTelemetry.h" +#include "modules/Telemetry/EnvironmentTelemetry.h" #endif #ifndef NO_ESP32 #include "modules/esp32/RangeTestModule.h" @@ -42,7 +43,8 @@ void setupModules() upDownInterruptImpl1->init(); cannedMessageModule = new CannedMessageModule(); #ifndef PORTDUINO - new TelemetryModule(); + new DeviceTelemetryModule(); + new EnvironmentTelemetryModule(); #endif #ifndef NO_ESP32 // Only run on an esp32 based device. diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp new file mode 100644 index 000000000..ee96156db --- /dev/null +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -0,0 +1,89 @@ +#include "DeviceTelemetry.h" +#include "../mesh/generated/telemetry.pb.h" +#include "PowerFSM.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "RTC.h" +#include "Router.h" +#include "configuration.h" +#include "main.h" +#include +#include + +int32_t DeviceTelemetryModule::runOnce() +{ +#ifndef PORTDUINO + if (firstTime) { + // This is the first time the OSThread library has called this function, so do some setup + firstTime = 0; + DEBUG_MSG("Device Telemetry: Initializing\n"); + } + sendOurTelemetry(); + // OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds + return (getPref_telemetry_module_device_update_interval() * 1000); +#endif +} + +bool DeviceTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *t) +{ + if (t->which_variant == Telemetry_device_metrics_tag) { + String sender = getSenderName(mp); + + DEBUG_MSG("-----------------------------------------\n"); + DEBUG_MSG("Device Telemetry: Received data from %s\n", sender); + DEBUG_MSG("Telemetry->time: %i\n", t->time); + DEBUG_MSG("Telemetry->air_util_tx: %f\n", t->variant.device_metrics.air_util_tx ); + DEBUG_MSG("Telemetry->battery_level: %i\n", t->variant.device_metrics.battery_level); + DEBUG_MSG("Telemetry->channel_utilization: %f\n", t->variant.device_metrics.channel_utilization); + DEBUG_MSG("Telemetry->voltage: %f\n", t->variant.device_metrics.voltage); + + lastMeasurementPacket = packetPool.allocCopy(mp); + + nodeDB.updateTelemetry(getFrom(&mp), *t, RX_SRC_RADIO); + } + return false; // Let others look at this message also if they want +} + +String DeviceTelemetryModule::getSenderName(const MeshPacket &mp) +{ + String sender; + + auto node = nodeDB.getNode(getFrom(&mp)); + if (node) { + sender = node->user.short_name; + } else { + sender = "UNK"; + } + return sender; +} + +bool DeviceTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies) +{ + Telemetry m; + + m.time = getTime(); + m.which_variant = Telemetry_device_metrics_tag; + + m.variant.device_metrics.air_util_tx = myNodeInfo.air_util_tx; + m.variant.device_metrics.battery_level = powerStatus->getBatteryChargePercent(); + m.variant.device_metrics.channel_utilization = myNodeInfo.channel_utilization; + m.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0; + + DEBUG_MSG("-----------------------------------------\n"); + DEBUG_MSG("Device Telemetry: Read data\n"); + + DEBUG_MSG("Telemetry->time: %i\n", m.time); + DEBUG_MSG("Telemetry->air_util_tx: %f\n", m.variant.device_metrics.air_util_tx); + DEBUG_MSG("Telemetry->battery_level: %i\n", m.variant.device_metrics.battery_level); + DEBUG_MSG("Telemetry->channel_utilization: %f\n", m.variant.device_metrics.channel_utilization); + DEBUG_MSG("Telemetry->voltage: %f\n", m.variant.device_metrics.voltage); + + MeshPacket *p = allocDataProtobuf(m); + p->to = dest; + p->decoded.want_response = wantReplies; + + lastMeasurementPacket = packetPool.allocCopy(*p); + DEBUG_MSG("Device Telemetry: Sending packet to mesh"); + service.sendToMesh(p); + return true; +} diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h new file mode 100644 index 000000000..67c7ac305 --- /dev/null +++ b/src/modules/Telemetry/DeviceTelemetry.h @@ -0,0 +1,33 @@ +#pragma once +#include "../mesh/generated/telemetry.pb.h" +#include "ProtobufModule.h" +#include +#include + +class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModule +{ + public: + DeviceTelemetryModule() + : concurrency::OSThread("DeviceTelemetryModule"), + ProtobufModule("DeviceTelemetry", PortNum_TELEMETRY_APP, &Telemetry_msg) + { + lastMeasurementPacket = nullptr; + } + virtual bool wantUIFrame() { return false; } + + 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, Telemetry *p) override; + virtual int32_t runOnce() override; + /** + * Send our Telemetry into the mesh + */ + bool sendOurTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); + + private: + String getSenderName(const MeshPacket &mp); + bool firstTime = 1; + const MeshPacket *lastMeasurementPacket; +}; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp new file mode 100644 index 000000000..675969fd7 --- /dev/null +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -0,0 +1,302 @@ +#include "EnvironmentTelemetry.h" +#include "../mesh/generated/telemetry.pb.h" +#include "PowerFSM.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "RTC.h" +#include "Router.h" +#include "configuration.h" +#include "main.h" +#include +#include + +// Sensors +#include "Sensor/BME280Sensor.h" +#include "Sensor/BME680Sensor.h" +#include "Sensor/DHTSensor.h" +#include "Sensor/DallasSensor.h" +#include "Sensor/MCP9808Sensor.h" + +BME280Sensor bme280Sensor; +BME680Sensor bme680Sensor; +DHTSensor dhtSensor; +DallasSensor dallasSensor; +MCP9808Sensor mcp9808Sensor; + +#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 +#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) + +int32_t EnvironmentTelemetryModule::runOnce() +{ +#ifndef PORTDUINO + /* + Uncomment the preferences below if you want to use the module + without having to configure it from the PythonAPI or WebUI. + */ + /* + radioConfig.preferences.telemetry_module_environment_measurement_enabled = 1; + radioConfig.preferences.telemetry_module_environment_screen_enabled = 1; + radioConfig.preferences.telemetry_module_environment_read_error_count_threshold = 5; + radioConfig.preferences.telemetry_module_environment_update_interval = 600; + radioConfig.preferences.telemetry_module_environment_recovery_interval = 60; + radioConfig.preferences.telemetry_module_environment_sensor_pin = 13; // If one-wire + radioConfig.preferences.telemetry_module_environment_sensor_type = RadioConfig_UserPreferences_TelemetrySensorType::RadioConfig_UserPreferences_TelemetrySensorType_BME280; + */ + + if (!(radioConfig.preferences.telemetry_module_environment_measurement_enabled || + radioConfig.preferences.telemetry_module_environment_screen_enabled)) { + // If this module 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.telemetry_module_environment_measurement_enabled) { + DEBUG_MSG("Environment Telemetry: Initializing\n"); + // it's possible to have this module enabled, only for displaying values on the screen. + // therefore, we should only enable the sensor loop if measurement is also enabled + switch (radioConfig.preferences.telemetry_module_environment_sensor_type) { + + case RadioConfig_UserPreferences_TelemetrySensorType_DHT11: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT12: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT21: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT22: + return dhtSensor.runOnce(); + case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20: + return dallasSensor.runOnce(); + case RadioConfig_UserPreferences_TelemetrySensorType_BME280: + return bme280Sensor.runOnce(); + case RadioConfig_UserPreferences_TelemetrySensorType_BME680: + return bme680Sensor.runOnce(); + case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808: + return mcp9808Sensor.runOnce(); + default: + DEBUG_MSG("Environment Telemetry: Invalid sensor type selected; Disabling module"); + return (INT32_MAX); + break; + } + } + return (INT32_MAX); + } else { + // if we somehow got to a second run of this module with measurement disabled, then just wait forever + if (!radioConfig.preferences.telemetry_module_environment_measurement_enabled) + 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.telemetry_module_environment_read_error_count_threshold) { + if (radioConfig.preferences.telemetry_module_environment_recovery_interval > 0) { + DEBUG_MSG("Environment Telemetry: TEMPORARILY DISABLED; The " + "telemetry_module_environment_read_error_count_threshold has been exceed: %d. Will retry reads in " + "%d seconds\n", + radioConfig.preferences.telemetry_module_environment_read_error_count_threshold, + radioConfig.preferences.telemetry_module_environment_recovery_interval); + sensor_read_error_count = 0; + return (radioConfig.preferences.telemetry_module_environment_recovery_interval * 1000); + } + DEBUG_MSG("Environment Telemetry: DISABLED; The telemetry_module_environment_read_error_count_threshold has " + "been exceed: %d. Reads will not be retried until after device reset\n", + radioConfig.preferences.telemetry_module_environment_read_error_count_threshold); + return (INT32_MAX); + + } else if (sensor_read_error_count > 0) { + DEBUG_MSG("Environment Telemetry: 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.telemetry_module_environment_read_error_count_threshold - + sensor_read_error_count); + } + if (!sendOurTelemetry()) { + // if we failed to read the sensor, then try again + // as soon as we can according to the maximum polling frequency + + switch (radioConfig.preferences.telemetry_module_environment_sensor_type) { + case RadioConfig_UserPreferences_TelemetrySensorType_DHT11: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT12: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT21: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT22: + return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20: + return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + case RadioConfig_UserPreferences_TelemetrySensorType_BME280: + case RadioConfig_UserPreferences_TelemetrySensorType_BME680: + return (BME_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808: + return (MCP_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + default: + return (DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); + } + } + } + // OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds + return (getPref_telemetry_module_environment_update_interval() * 1000); +#endif +} + +String GetSenderName(const MeshPacket &mp) +{ + String sender; + + auto node = nodeDB.getNode(getFrom(&mp)); + if (node) { + sender = node->user.short_name; + } else { + sender = "UNK"; + } + return sender; +} + +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; +} + +bool EnvironmentTelemetryModule::wantUIFrame() +{ + return radioConfig.preferences.telemetry_module_environment_screen_enabled; +} + +float EnvironmentTelemetryModule::CelsiusToFahrenheit(float c) +{ + return (c * 9) / 5 + 32; +} + +void EnvironmentTelemetryModule::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"); + if (lastMeasurementPacket == nullptr) { + display->setFont(FONT_SMALL); + display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement"); + return; + } + + Telemetry 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, Telemetry_fields, &lastMeasurement)) { + display->setFont(FONT_SMALL); + display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); + DEBUG_MSG("Environment Telemetry: unable to decode last packet"); + return; + } + + display->setFont(FONT_SMALL); + String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; + if (radioConfig.preferences.telemetry_module_environment_display_fahrenheit) { + last_temp = String(CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; + } + display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + lastSender + "(" + String(agoSecs) + "s)"); + display->drawString(x, y += fontHeight(FONT_SMALL) - 2,"Temp/Hum: " + last_temp + " / " + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); + if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) + display->drawString(x, y += fontHeight(FONT_SMALL),"Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA"); +} + +bool EnvironmentTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *t) +{ + if (t->which_variant == Telemetry_environment_metrics_tag) { + String sender = GetSenderName(mp); + + DEBUG_MSG("-----------------------------------------\n"); + DEBUG_MSG("Environment Telemetry: Received data from %s\n", sender); + DEBUG_MSG("Telemetry->time: %i\n", t->time); + DEBUG_MSG("Telemetry->barometric_pressure: %f\n", t->variant.environment_metrics.barometric_pressure); + DEBUG_MSG("Telemetry->current: %f\n", t->variant.environment_metrics.current); + DEBUG_MSG("Telemetry->gas_resistance: %f\n", t->variant.environment_metrics.gas_resistance); + DEBUG_MSG("Telemetry->relative_humidity: %f\n", t->variant.environment_metrics.relative_humidity); + DEBUG_MSG("Telemetry->temperature: %f\n", t->variant.environment_metrics.temperature); + DEBUG_MSG("Telemetry->voltage: %f\n", t->variant.environment_metrics.voltage); + + lastMeasurementPacket = packetPool.allocCopy(mp); + } + + return false; // Let others look at this message also if they want +} + +bool EnvironmentTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies) +{ + Telemetry m; + m.time = getTime(); + m.which_variant = Telemetry_environment_metrics_tag; + + m.variant.environment_metrics.barometric_pressure = 0; + m.variant.environment_metrics.current = 0; + m.variant.environment_metrics.gas_resistance = 0; + m.variant.environment_metrics.relative_humidity = 0; + m.variant.environment_metrics.temperature = 0; + m.variant.environment_metrics.voltage = 0; + + DEBUG_MSG("-----------------------------------------\n"); + DEBUG_MSG("Environment Telemetry: Read data\n"); + + switch (radioConfig.preferences.telemetry_module_environment_sensor_type) { + case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20: + if (!dallasSensor.getMeasurement(&m)) + sensor_read_error_count++; + break; + case RadioConfig_UserPreferences_TelemetrySensorType_DHT11: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT12: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT21: + case RadioConfig_UserPreferences_TelemetrySensorType_DHT22: + if (!dhtSensor.getMeasurement(&m)) + sensor_read_error_count++; + break; + case RadioConfig_UserPreferences_TelemetrySensorType_BME280: + bme280Sensor.getMeasurement(&m); + break; + case RadioConfig_UserPreferences_TelemetrySensorType_BME680: + bme680Sensor.getMeasurement(&m); + break; + case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808: + mcp9808Sensor.getMeasurement(&m); + break; + default: + DEBUG_MSG("Environment Telemetry: No external sensor type selected; Only sending internal metrics\n"); + } + + DEBUG_MSG("Telemetry->time: %i\n", m.time); + DEBUG_MSG("Telemetry->barometric_pressure: %f\n", m.variant.environment_metrics.barometric_pressure); + DEBUG_MSG("Telemetry->current: %f\n", m.variant.environment_metrics.current); + DEBUG_MSG("Telemetry->gas_resistance: %f\n", m.variant.environment_metrics.gas_resistance); + DEBUG_MSG("Telemetry->relative_humidity: %f\n", m.variant.environment_metrics.relative_humidity); + DEBUG_MSG("Telemetry->temperature: %f\n", m.variant.environment_metrics.temperature); + DEBUG_MSG("Telemetry->voltage: %f\n", m.variant.environment_metrics.voltage); + + sensor_read_error_count = 0; + + MeshPacket *p = allocDataProtobuf(m); + p->to = dest; + p->decoded.want_response = wantReplies; + + lastMeasurementPacket = packetPool.allocCopy(*p); + DEBUG_MSG("Environment Telemetry: Sending packet to mesh"); + service.sendToMesh(p); + return true; +} diff --git a/src/modules/Telemetry/Telemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h similarity index 77% rename from src/modules/Telemetry/Telemetry.h rename to src/modules/Telemetry/EnvironmentTelemetry.h index 528c5b78a..c1cdc9994 100644 --- a/src/modules/Telemetry/Telemetry.h +++ b/src/modules/Telemetry/EnvironmentTelemetry.h @@ -4,12 +4,12 @@ #include #include -class TelemetryModule : private concurrency::OSThread, public ProtobufModule +class EnvironmentTelemetryModule : private concurrency::OSThread, public ProtobufModule { public: - TelemetryModule() - : concurrency::OSThread("TelemetryModule"), - ProtobufModule("Telemetry", PortNum_TELEMETRY_APP, &Telemetry_msg) + EnvironmentTelemetryModule() + : concurrency::OSThread("EnvironmentTelemetryModule"), + ProtobufModule("EnvironmentTelemetry", PortNum_TELEMETRY_APP, &Telemetry_msg) { lastMeasurementPacket = nullptr; } diff --git a/src/modules/Telemetry/Sensor/BME280Sensor.cpp b/src/modules/Telemetry/Sensor/BME280Sensor.cpp index 49c3564d2..fee3e2663 100644 --- a/src/modules/Telemetry/Sensor/BME280Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME280Sensor.cpp @@ -21,9 +21,9 @@ int32_t BME280Sensor::runOnce() { } bool BME280Sensor::getMeasurement(Telemetry *measurement) { - measurement->temperature = bme280.readTemperature(); - measurement->relative_humidity = bme280.readHumidity(); - measurement->barometric_pressure = bme280.readPressure() / 100.0F; + measurement->variant.environment_metrics.temperature = bme280.readTemperature(); + measurement->variant.environment_metrics.relative_humidity = bme280.readHumidity(); + measurement->variant.environment_metrics.barometric_pressure = bme280.readPressure() / 100.0F; return true; } \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp index f4e09e8b0..1e57302b6 100644 --- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp @@ -27,10 +27,10 @@ int32_t BME680Sensor::runOnce() { } bool BME680Sensor::getMeasurement(Telemetry *measurement) { - measurement->temperature = bme680.readTemperature(); - measurement->relative_humidity = bme680.readHumidity(); - measurement->barometric_pressure = bme680.readPressure() / 100.0F; - measurement->gas_resistance = bme680.readGas() / 1000.0; + measurement->variant.environment_metrics.temperature = bme680.readTemperature(); + measurement->variant.environment_metrics.relative_humidity = bme680.readHumidity(); + measurement->variant.environment_metrics.barometric_pressure = bme680.readPressure() / 100.0F; + measurement->variant.environment_metrics.gas_resistance = bme680.readGas() / 1000.0; return true; } \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/DHTSensor.cpp b/src/modules/Telemetry/Sensor/DHTSensor.cpp index bd9448245..063adced5 100644 --- a/src/modules/Telemetry/Sensor/DHTSensor.cpp +++ b/src/modules/Telemetry/Sensor/DHTSensor.cpp @@ -11,16 +11,16 @@ DHTSensor::DHTSensor() : TelemetrySensor {} { int32_t DHTSensor::runOnce() { if (RadioConfig_UserPreferences_TelemetrySensorType_DHT11 || RadioConfig_UserPreferences_TelemetrySensorType_DHT12) { - dht = new DHT(radioConfig.preferences.telemetry_module_sensor_pin, DHT11); + dht = new DHT(radioConfig.preferences.telemetry_module_environment_sensor_pin, DHT11); } else { - dht = new DHT(radioConfig.preferences.telemetry_module_sensor_pin, DHT22); + dht = new DHT(radioConfig.preferences.telemetry_module_environment_sensor_pin, DHT22); } dht->begin(); dht->read(); DEBUG_MSG("Telemetry: Opened DHT11/DHT12 on pin: %d\n", - radioConfig.preferences.telemetry_module_sensor_pin); + radioConfig.preferences.telemetry_module_environment_sensor_pin); return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); } @@ -30,7 +30,7 @@ bool DHTSensor::getMeasurement(Telemetry *measurement) { DEBUG_MSG("Telemetry: FAILED TO READ DATA\n"); return false; } - measurement->relative_humidity = dht->readHumidity(); - measurement->temperature = dht->readTemperature(); + measurement->variant.environment_metrics.relative_humidity = dht->readHumidity(); + measurement->variant.environment_metrics.temperature = dht->readTemperature(); return true; } \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/DallasSensor.cpp b/src/modules/Telemetry/Sensor/DallasSensor.cpp index 16db31ad5..1be369d79 100644 --- a/src/modules/Telemetry/Sensor/DallasSensor.cpp +++ b/src/modules/Telemetry/Sensor/DallasSensor.cpp @@ -10,20 +10,20 @@ DallasSensor::DallasSensor() : TelemetrySensor {} { } int32_t DallasSensor::runOnce() { - oneWire = new OneWire(radioConfig.preferences.telemetry_module_sensor_pin); + oneWire = new OneWire(radioConfig.preferences.telemetry_module_environment_sensor_pin); ds18b20 = new DS18B20(oneWire); ds18b20->begin(); ds18b20->setResolution(12); ds18b20->requestTemperatures(); DEBUG_MSG("Telemetry: Opened DS18B20 on pin: %d\n", - radioConfig.preferences.telemetry_module_sensor_pin); + radioConfig.preferences.telemetry_module_environment_sensor_pin); return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); } bool DallasSensor::getMeasurement(Telemetry *measurement) { if (ds18b20->isConversionComplete()) { - measurement->temperature = ds18b20->getTempC(); - measurement->relative_humidity = 0; + measurement->variant.environment_metrics.temperature = ds18b20->getTempC(); + measurement->variant.environment_metrics.relative_humidity = 0; ds18b20->requestTemperatures(); return true; } diff --git a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp index 701f27f96..eaeaba34c 100644 --- a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp @@ -22,7 +22,7 @@ int32_t MCP9808Sensor::runOnce() { } bool MCP9808Sensor::getMeasurement(Telemetry *measurement) { - measurement->temperature = mcp9808.readTempC(); + measurement->variant.environment_metrics.temperature = mcp9808.readTempC(); return true; } \ No newline at end of file diff --git a/src/modules/Telemetry/Telemetry.cpp b/src/modules/Telemetry/Telemetry.cpp deleted file mode 100644 index befbec109..000000000 --- a/src/modules/Telemetry/Telemetry.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include "Telemetry.h" -#include "../mesh/generated/telemetry.pb.h" -#include "PowerFSM.h" -#include "MeshService.h" -#include "NodeDB.h" -#include "RTC.h" -#include "Router.h" -#include "configuration.h" -#include "main.h" -#include -#include - -// Sensors -#include "Sensor/BME280Sensor.h" -#include "Sensor/BME680Sensor.h" -#include "Sensor/DHTSensor.h" -#include "Sensor/DallasSensor.h" -#include "Sensor/MCP9808Sensor.h" - -BME280Sensor bme280Sensor; -BME680Sensor bme680Sensor; -DHTSensor dhtSensor; -DallasSensor dallasSensor; -MCP9808Sensor mcp9808Sensor; - -#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 -#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) - -int32_t TelemetryModule::runOnce() -{ -#ifndef PORTDUINO - /* - Uncomment the preferences below if you want to use the module - without having to configure it from the PythonAPI or WebUI. - */ - /* - radioConfig.preferences.telemetry_module_screen_enabled = 1; - radioConfig.preferences.telemetry_module_read_error_count_threshold = 5; - radioConfig.preferences.telemetry_module_update_interval = 600; - radioConfig.preferences.telemetry_module_recovery_interval = 60; - radioConfig.preferences.telemetry_module_sensor_pin = 13; // If one-wire - radioConfig.preferences.telemetry_module_sensor_type = RadioConfig_UserPreferences_TelemetrySensorType::RadioConfig_UserPreferences_TelemetrySensorType_BME280; - */ - - if (firstTime) { - // This is the first time the OSThread library has called this function, so do some setup - firstTime = 0; - DEBUG_MSG("Telemetry: Initializing\n"); - // therefore, we should only enable the sensor loop if measurement is also enabled - switch (radioConfig.preferences.telemetry_module_sensor_type) { - case RadioConfig_UserPreferences_TelemetrySensorType_DHT11: - case RadioConfig_UserPreferences_TelemetrySensorType_DHT12: - case RadioConfig_UserPreferences_TelemetrySensorType_DHT21: - case RadioConfig_UserPreferences_TelemetrySensorType_DHT22: - dhtSensor.runOnce(); - case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20: - dallasSensor.runOnce(); - case RadioConfig_UserPreferences_TelemetrySensorType_BME280: - bme280Sensor.runOnce(); - case RadioConfig_UserPreferences_TelemetrySensorType_BME680: - bme680Sensor.runOnce(); - case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808: - mcp9808Sensor.runOnce(); - default: - DEBUG_MSG("Telemetry: No external sensor types selected;"); - break; - } - } else { - // 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.telemetry_module_read_error_count_threshold) { - if (getPref_telemetry_module_update_interval() > 0) { - DEBUG_MSG("Telemetry: TEMPORARILY DISABLED; The " - "telemetry_module_read_error_count_threshold has been exceed: %d. Will retry reads in " - "%d seconds\n", - radioConfig.preferences.telemetry_module_read_error_count_threshold, - getPref_telemetry_module_update_interval()); - sensor_read_error_count = 0; - return (getPref_telemetry_module_update_interval() * 1000); - } - DEBUG_MSG("Telemetry: DISABLED; The telemetry_module_read_error_count_threshold has " - "been exceed: %d. Reads will not be retried until after device reset\n", - radioConfig.preferences.telemetry_module_read_error_count_threshold); - return (INT32_MAX); - - } else if (sensor_read_error_count > 0) { - DEBUG_MSG("Telemetry: 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.telemetry_module_read_error_count_threshold - - sensor_read_error_count); - } - sendOurTelemetry(); - } - // OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds - return (getPref_telemetry_module_update_interval() * 1000); -#endif -} - -bool TelemetryModule::wantUIFrame() -{ - return radioConfig.preferences.telemetry_module_screen_enabled; -} - -String GetSenderName(const MeshPacket &mp) -{ - String sender; - - auto node = nodeDB.getNode(getFrom(&mp)); - if (node) { - sender = node->user.short_name; - } else { - sender = "UNK"; - } - return sender; -} - -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 TelemetryModule::CelsiusToFahrenheit(float c) -{ - return (c * 9) / 5 + 32; -} - -void TelemetryModule::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"); - if (lastMeasurementPacket == nullptr) { - display->setFont(FONT_SMALL); - display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement"); - return; - } - - Telemetry 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, Telemetry_fields, &lastMeasurement)) { - display->setFont(FONT_SMALL); - display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); - DEBUG_MSG("Telemetry: unable to decode last packet"); - return; - } - - display->setFont(FONT_SMALL); - String last_temp = String(lastMeasurement.temperature, 0) + "°C"; - if (radioConfig.preferences.telemetry_module_display_fahrenheit) { - last_temp = String(CelsiusToFahrenheit(lastMeasurement.temperature), 0) + "°F"; - } - display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + lastSender + "(" + String(agoSecs) + "s)"); - display->drawString(x, y += fontHeight(FONT_SMALL) - 2,"Temp/Hum: " + last_temp + " / " + String(lastMeasurement.relative_humidity, 0) + "%"); - if (lastMeasurement.barometric_pressure != 0) - display->drawString(x, y += fontHeight(FONT_SMALL),"Press: " + String(lastMeasurement.barometric_pressure, 0) + "hPA"); -} - -bool TelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *t) -{ - String sender = GetSenderName(mp); - - DEBUG_MSG("-----------------------------------------\n"); - DEBUG_MSG("Telemetry: Received data from %s\n", sender); - DEBUG_MSG("Telemetry->time: %i\n", t->time); - DEBUG_MSG("Telemetry->air_util_tx: %f\n", t->air_util_tx); - DEBUG_MSG("Telemetry->barometric_pressure: %f\n", t->barometric_pressure); - DEBUG_MSG("Telemetry->battery_level: %i\n", t->battery_level); - DEBUG_MSG("Telemetry->channel_utilization: %f\n", t->channel_utilization); - DEBUG_MSG("Telemetry->current: %f\n", t->current); - DEBUG_MSG("Telemetry->gas_resistance: %f\n", t->gas_resistance); - DEBUG_MSG("Telemetry->relative_humidity: %f\n", t->relative_humidity); - DEBUG_MSG("Telemetry->router_heartbeat: %i\n", t->router_heartbeat); - DEBUG_MSG("Telemetry->temperature: %f\n", t->temperature); - DEBUG_MSG("Telemetry->voltage: %f\n", t->voltage); - - lastMeasurementPacket = packetPool.allocCopy(mp); - - nodeDB.updateTelemetry(getFrom(&mp), *t, RX_SRC_RADIO); - - return false; // Let others look at this message also if they want -} - -bool TelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies) -{ - Telemetry m; - m.time = getTime(); - - m.air_util_tx = myNodeInfo.air_util_tx; - m.barometric_pressure = 0; - m.battery_level = powerStatus->getBatteryChargePercent(); - m.channel_utilization = myNodeInfo.channel_utilization; - m.current = 0; - m.gas_resistance = 0; - m.relative_humidity = 0; - m.router_heartbeat = 0; - m.temperature = 0; - m.voltage = 0; - - DEBUG_MSG("-----------------------------------------\n"); - DEBUG_MSG("Telemetry: Read data\n"); - - switch (radioConfig.preferences.telemetry_module_sensor_type) { - case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20: - if (!dallasSensor.getMeasurement(&m)) - sensor_read_error_count++; - break; - case RadioConfig_UserPreferences_TelemetrySensorType_DHT11: - case RadioConfig_UserPreferences_TelemetrySensorType_DHT12: - case RadioConfig_UserPreferences_TelemetrySensorType_DHT21: - case RadioConfig_UserPreferences_TelemetrySensorType_DHT22: - if (!dhtSensor.getMeasurement(&m)) - sensor_read_error_count++; - break; - case RadioConfig_UserPreferences_TelemetrySensorType_BME280: - bme280Sensor.getMeasurement(&m); - break; - case RadioConfig_UserPreferences_TelemetrySensorType_BME680: - bme680Sensor.getMeasurement(&m); - break; - case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808: - mcp9808Sensor.getMeasurement(&m); - break; - default: - DEBUG_MSG("Telemetry: No external sensor type selected; Only sending internal metrics\n"); - } - - DEBUG_MSG("Telemetry->time: %i\n", m.time); - DEBUG_MSG("Telemetry->air_util_tx: %f\n", m.air_util_tx); - DEBUG_MSG("Telemetry->barometric_pressure: %f\n", m.barometric_pressure); - DEBUG_MSG("Telemetry->battery_level: %i\n", m.battery_level); - DEBUG_MSG("Telemetry->channel_utilization: %f\n", m.channel_utilization); - DEBUG_MSG("Telemetry->current: %f\n", m.current); - DEBUG_MSG("Telemetry->gas_resistance: %f\n", m.gas_resistance); - DEBUG_MSG("Telemetry->relative_humidity: %f\n", m.relative_humidity); - DEBUG_MSG("Telemetry->router_heartbeat: %f\n", m.router_heartbeat); - DEBUG_MSG("Telemetry->temperature: %f\n", m.temperature); - DEBUG_MSG("Telemetry->voltage: %f\n", m.voltage); - - sensor_read_error_count = 0; - - MeshPacket *p = allocDataProtobuf(m); - p->to = dest; - p->decoded.want_response = wantReplies; - - lastMeasurementPacket = packetPool.allocCopy(*p); - DEBUG_MSG("Telemetry: Sending packet to mesh"); - service.sendToMesh(p); - return true; -} diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 6df41e829..5ecbd56c1 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -290,15 +290,17 @@ String MQTT::downstreamPacketToJson(MeshPacket *mp) memset(&scratch, 0, sizeof(scratch)); if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &Telemetry_msg, &scratch)) { - decoded = &scratch; - msgPayload = Json::object{ - {"temperature", decoded->temperature}, - {"relative_humidity", decoded->relative_humidity}, - {"barometric_pressure", decoded->barometric_pressure}, - {"gas_resistance", decoded->gas_resistance}, - {"voltage", decoded->voltage}, - {"current", decoded->current}, - }; + if (decoded->which_variant == Telemetry_environment_metrics_tag) { + decoded = &scratch; + msgPayload = Json::object{ + {"temperature", decoded->variant.environment_metrics.temperature}, + {"relative_humidity", decoded->variant.environment_metrics.relative_humidity}, + {"barometric_pressure", decoded->variant.environment_metrics.barometric_pressure}, + {"gas_resistance", decoded->variant.environment_metrics.gas_resistance}, + {"voltage", decoded->variant.environment_metrics.voltage}, + {"current", decoded->variant.environment_metrics.current}, + }; + } } else DEBUG_MSG("Error decoding protobuf for telemetry message!\n"); };