Reworked metrics structure and split telemetry into device or environ… (#1331)

* Reworked metrics structure and split telemetry into device or environment

* Comment cleanup
This commit is contained in:
Ben Meadors 2022-03-27 14:55:35 +00:00 committed by GitHub
parent 7b8096f5b2
commit ba2fa84ebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 584 additions and 379 deletions

2
proto

@ -1 +1 @@
Subproject commit 3689ca4bde316dca2601c2a2f91fa71204f7fa43
Subproject commit 6d355041bb0a30d3fdb3f15a0c03963ec3fe2bf3

View File

@ -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;
}

View File

@ -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))

View File

@ -22,6 +22,8 @@ template <class T> class ProtobufModule : protected SinglePortModule
}
protected:
/**
* Handle a received message, the data field in the message is already decoded and is provided
*

View File

@ -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" */

View File

@ -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" */

View File

@ -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)

View File

@ -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

View File

@ -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" */

View File

@ -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)

View File

@ -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" */

View File

@ -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.

View File

@ -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 <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
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;
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "../mesh/generated/telemetry.pb.h"
#include "ProtobufModule.h"
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModule<Telemetry>
{
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;
};

View File

@ -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 <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
// 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;
}

View File

@ -4,12 +4,12 @@
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class TelemetryModule : private concurrency::OSThread, public ProtobufModule<Telemetry>
class EnvironmentTelemetryModule : private concurrency::OSThread, public ProtobufModule<Telemetry>
{
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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
// 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;
}

View File

@ -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");
};