From d83a0b1818016068d4a8f3f37699eb0b18f84728 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 4 Feb 2023 13:07:14 -0600 Subject: [PATCH 1/2] Initial air quality telemetry feature --- platformio.ini | 3 +- protobufs | 2 +- src/configuration.h | 1 + src/detect/i2cScan.h | 4 + src/main.cpp | 1 - src/mesh/generated/meshtastic/admin.pb.c | 3 + src/mesh/generated/meshtastic/admin.pb.h | 33 +++++ src/mesh/generated/meshtastic/telemetry.pb.c | 3 + src/mesh/generated/meshtastic/telemetry.pb.h | 81 ++++++++++- src/modules/Modules.cpp | 4 + src/modules/Telemetry/AirQualityTelemetry.cpp | 128 ++++++++++++++++++ src/modules/Telemetry/AirQualityTelemetry.h | 37 +++++ .../Telemetry/EnvironmentTelemetry.cpp | 20 +-- 13 files changed, 301 insertions(+), 19 deletions(-) create mode 100644 src/modules/Telemetry/AirQualityTelemetry.cpp create mode 100644 src/modules/Telemetry/AirQualityTelemetry.h diff --git a/platformio.ini b/platformio.ini index 1d8083f70..a833c42a5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,7 +2,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -;default_envs = tbeam +default_envs = tbeam ;default_envs = pico ;default_envs = tbeam-s3-core ;default_envs = tbeam0.7 @@ -109,3 +109,4 @@ lib_deps = adafruit/Adafruit SHTC3 Library@^1.0.0 adafruit/Adafruit LPS2X@^2.0.4 adafruit/Adafruit SHT31 Library@^2.2.0 + adafruit/Adafruit PM25 AQI Sensor@^1.0.6 diff --git a/protobufs b/protobufs index 516074f2e..384b664a7 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 516074f2e49743c234430abb2ea43ea3f66b0acb +Subproject commit 384b664a759592d9393642ba98835f69bb8f2fb2 diff --git a/src/configuration.h b/src/configuration.h index 6181034a5..f35fff4e0 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -116,6 +116,7 @@ along with this program. If not, see . #define LPS22HB_ADDR 0x5C #define LPS22HB_ADDR_ALT 0x5D #define SHT31_ADDR 0x44 +#define PMSA0031_ADDR 0x12 // ----------------------------------------------------------------------------- // Security diff --git a/src/detect/i2cScan.h b/src/detect/i2cScan.h index 87ca55957..a2d7b7baf 100644 --- a/src/detect/i2cScan.h +++ b/src/detect/i2cScan.h @@ -218,6 +218,10 @@ void scanI2Cdevice() LOG_INFO("QMC5883L Highrate 3-Axis magnetic sensor found\n"); nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_QMC5883L] = addr; } + if (addr == PMSA0031_ADDR) { + LOG_INFO("PMSA0031 air quality sensor found\n"); + nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I] = addr; + } } else if (err == 4) { LOG_ERROR("Unknow error at address 0x%x\n", addr); } diff --git a/src/main.cpp b/src/main.cpp index 49963a415..89148c22b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -276,7 +276,6 @@ void setup() LOG_INFO("PCF8563 RTC found\n"); } #endif - // We need to scan here to decide if we have a screen for nodeDB.init() scanI2Cdevice(); diff --git a/src/mesh/generated/meshtastic/admin.pb.c b/src/mesh/generated/meshtastic/admin.pb.c index 1f668dabc..c897c1760 100644 --- a/src/mesh/generated/meshtastic/admin.pb.c +++ b/src/mesh/generated/meshtastic/admin.pb.c @@ -9,6 +9,9 @@ PB_BIND(meshtastic_AdminMessage, meshtastic_AdminMessage, 2) +PB_BIND(meshtastic_HamParameters, meshtastic_HamParameters, AUTO) + + diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index e35b79a57..25bc77241 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -57,6 +57,18 @@ typedef enum _meshtastic_AdminMessage_ModuleConfigType { } meshtastic_AdminMessage_ModuleConfigType; /* Struct definitions */ +/* Parameters for setting up Meshtastic for ameteur radio usage */ +typedef struct _meshtastic_HamParameters { + /* Amateur radio call sign, eg. KD2ABC */ + char call_sign[8]; + /* Transmit power in dBm at the LoRA transceiver, not including any amplification */ + int32_t tx_power; + /* The selected frequency of LoRA operation + Please respect your local laws, regulations, and band plans. + Ensure your radio is capable of operating of the selected frequency before setting this. */ + float frequency; +} meshtastic_HamParameters; + /* This message is handled by the Admin module and is responsible for all settings/channel read/write operations. This message is used to do settings operations to both remote AND local nodes. (Prior to 1.2 these operations were done via special ToRadio operations) */ @@ -96,6 +108,8 @@ typedef struct _meshtastic_AdminMessage { bool get_device_connection_status_request; /* Device connection status response */ meshtastic_DeviceConnectionStatus get_device_connection_status_response; + /* Setup a node for licensed amateur (ham) radio operation */ + meshtastic_HamParameters set_ham_mode; /* Set the owner for this node */ meshtastic_User set_owner; /* Set channels (using the new API). @@ -152,11 +166,17 @@ extern "C" { #define meshtastic_AdminMessage_payload_variant_get_module_config_request_ENUMTYPE meshtastic_AdminMessage_ModuleConfigType + /* Initializer values for message structs */ #define meshtastic_AdminMessage_init_default {0, {0}} +#define meshtastic_HamParameters_init_default {"", 0, 0} #define meshtastic_AdminMessage_init_zero {0, {0}} +#define meshtastic_HamParameters_init_zero {"", 0, 0} /* Field tags (for use in manual encoding/decoding) */ +#define meshtastic_HamParameters_call_sign_tag 1 +#define meshtastic_HamParameters_tx_power_tag 2 +#define meshtastic_HamParameters_frequency_tag 3 #define meshtastic_AdminMessage_get_channel_request_tag 1 #define meshtastic_AdminMessage_get_channel_response_tag 2 #define meshtastic_AdminMessage_get_owner_request_tag 3 @@ -173,6 +193,7 @@ extern "C" { #define meshtastic_AdminMessage_get_ringtone_response_tag 15 #define meshtastic_AdminMessage_get_device_connection_status_request_tag 16 #define meshtastic_AdminMessage_get_device_connection_status_response_tag 17 +#define meshtastic_AdminMessage_set_ham_mode_tag 18 #define meshtastic_AdminMessage_set_owner_tag 32 #define meshtastic_AdminMessage_set_channel_tag 33 #define meshtastic_AdminMessage_set_config_tag 34 @@ -206,6 +227,7 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,get_ringtone_request,get_rin X(a, STATIC, ONEOF, STRING, (payload_variant,get_ringtone_response,get_ringtone_response), 15) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,get_device_connection_status_request,get_device_connection_status_request), 16) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,get_device_connection_status_response,get_device_connection_status_response), 17) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_ham_mode,set_ham_mode), 18) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_owner,set_owner), 32) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_channel,set_channel), 33) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_config,set_config), 34) \ @@ -228,18 +250,29 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), #define meshtastic_AdminMessage_payload_variant_get_module_config_response_MSGTYPE meshtastic_ModuleConfig #define meshtastic_AdminMessage_payload_variant_get_device_metadata_response_MSGTYPE meshtastic_DeviceMetadata #define meshtastic_AdminMessage_payload_variant_get_device_connection_status_response_MSGTYPE meshtastic_DeviceConnectionStatus +#define meshtastic_AdminMessage_payload_variant_set_ham_mode_MSGTYPE meshtastic_HamParameters #define meshtastic_AdminMessage_payload_variant_set_owner_MSGTYPE meshtastic_User #define meshtastic_AdminMessage_payload_variant_set_channel_MSGTYPE meshtastic_Channel #define meshtastic_AdminMessage_payload_variant_set_config_MSGTYPE meshtastic_Config #define meshtastic_AdminMessage_payload_variant_set_module_config_MSGTYPE meshtastic_ModuleConfig +#define meshtastic_HamParameters_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, call_sign, 1) \ +X(a, STATIC, SINGULAR, INT32, tx_power, 2) \ +X(a, STATIC, SINGULAR, FLOAT, frequency, 3) +#define meshtastic_HamParameters_CALLBACK NULL +#define meshtastic_HamParameters_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_AdminMessage_msg; +extern const pb_msgdesc_t meshtastic_HamParameters_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_AdminMessage_fields &meshtastic_AdminMessage_msg +#define meshtastic_HamParameters_fields &meshtastic_HamParameters_msg /* Maximum encoded size of messages (where known) */ #define meshtastic_AdminMessage_size 234 +#define meshtastic_HamParameters_size 25 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/telemetry.pb.c b/src/mesh/generated/meshtastic/telemetry.pb.c index cdc01710a..cbcac3e20 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.c +++ b/src/mesh/generated/meshtastic/telemetry.pb.c @@ -12,6 +12,9 @@ PB_BIND(meshtastic_DeviceMetrics, meshtastic_DeviceMetrics, AUTO) PB_BIND(meshtastic_EnvironmentMetrics, meshtastic_EnvironmentMetrics, AUTO) +PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO) + + PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO) diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 188b27d77..6d4a5e1fc 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -10,7 +10,7 @@ #endif /* Enum definitions */ -/* TODO: REPLACE */ +/* Supported I2C Sensors for telemetry in Meshtastic */ typedef enum _meshtastic_TelemetrySensorType { /* No external telemetry sensor explicitly set */ meshtastic_TelemetrySensorType_SENSOR_UNSET = 0, @@ -37,7 +37,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* 3-Axis magnetic sensor */ meshtastic_TelemetrySensorType_QMC5883L = 11, /* High accuracy temperature and humidity */ - meshtastic_TelemetrySensorType_SHT31 = 12 + meshtastic_TelemetrySensorType_SHT31 = 12, + /* PM2.5 air quality sensor */ + meshtastic_TelemetrySensorType_PMSA003I = 13 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -69,6 +71,34 @@ typedef struct _meshtastic_EnvironmentMetrics { float current; } meshtastic_EnvironmentMetrics; +/* Air quality metrics */ +typedef struct _meshtastic_AirQualityMetrics { + /* Standard PM1.0 */ + uint32_t pm10_standard; + /* Standard PM2.5 */ + uint32_t pm25_standard; + /* Standard PM10.0 */ + uint32_t pm100_standard; + /* Environmental PM1.0 */ + uint32_t pm10_environmental; + /* Environmental PM2.5 */ + uint32_t pm25_environmental; + /* Environmental PM10.0 */ + uint32_t pm100_environmental; + /* 0.3um Particle Count */ + uint32_t particles_03um; + /* 0.5um Particle Count */ + uint32_t particles_05um; + /* 1.0um Particle Count */ + uint32_t particles_10um; + /* 2.5um Particle Count */ + uint32_t particles_25um; + /* 5.0um Particle Count */ + uint32_t particles_50um; + /* 10.0um Particle Count */ + uint32_t particles_100um; +} meshtastic_AirQualityMetrics; + /* Types of Measurements the telemetry module is equipped to handle */ typedef struct _meshtastic_Telemetry { /* This is usually not sent over the mesh (to save space), but it is sent @@ -83,6 +113,8 @@ typedef struct _meshtastic_Telemetry { meshtastic_DeviceMetrics device_metrics; /* Weather station or other environmental metrics */ meshtastic_EnvironmentMetrics environment_metrics; + /* Air quality metrics */ + meshtastic_AirQualityMetrics air_quality_metrics; } variant; } meshtastic_Telemetry; @@ -93,8 +125,9 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_SHT31 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_SHT31+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_PMSA003I +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_PMSA003I+1)) + @@ -103,9 +136,11 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0} #define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0} +#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0} #define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0} +#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} /* Field tags (for use in manual encoding/decoding) */ @@ -119,9 +154,22 @@ extern "C" { #define meshtastic_EnvironmentMetrics_gas_resistance_tag 4 #define meshtastic_EnvironmentMetrics_voltage_tag 5 #define meshtastic_EnvironmentMetrics_current_tag 6 +#define meshtastic_AirQualityMetrics_pm10_standard_tag 1 +#define meshtastic_AirQualityMetrics_pm25_standard_tag 2 +#define meshtastic_AirQualityMetrics_pm100_standard_tag 3 +#define meshtastic_AirQualityMetrics_pm10_environmental_tag 4 +#define meshtastic_AirQualityMetrics_pm25_environmental_tag 5 +#define meshtastic_AirQualityMetrics_pm100_environmental_tag 6 +#define meshtastic_AirQualityMetrics_particles_03um_tag 7 +#define meshtastic_AirQualityMetrics_particles_05um_tag 8 +#define meshtastic_AirQualityMetrics_particles_10um_tag 9 +#define meshtastic_AirQualityMetrics_particles_25um_tag 10 +#define meshtastic_AirQualityMetrics_particles_50um_tag 11 +#define meshtastic_AirQualityMetrics_particles_100um_tag 12 #define meshtastic_Telemetry_time_tag 1 #define meshtastic_Telemetry_device_metrics_tag 2 #define meshtastic_Telemetry_environment_metrics_tag 3 +#define meshtastic_Telemetry_air_quality_metrics_tag 4 /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceMetrics_FIELDLIST(X, a) \ @@ -142,28 +190,49 @@ X(a, STATIC, SINGULAR, FLOAT, current, 6) #define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL +#define meshtastic_AirQualityMetrics_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, pm10_standard, 1) \ +X(a, STATIC, SINGULAR, UINT32, pm25_standard, 2) \ +X(a, STATIC, SINGULAR, UINT32, pm100_standard, 3) \ +X(a, STATIC, SINGULAR, UINT32, pm10_environmental, 4) \ +X(a, STATIC, SINGULAR, UINT32, pm25_environmental, 5) \ +X(a, STATIC, SINGULAR, UINT32, pm100_environmental, 6) \ +X(a, STATIC, SINGULAR, UINT32, particles_03um, 7) \ +X(a, STATIC, SINGULAR, UINT32, particles_05um, 8) \ +X(a, STATIC, SINGULAR, UINT32, particles_10um, 9) \ +X(a, STATIC, SINGULAR, UINT32, particles_25um, 10) \ +X(a, STATIC, SINGULAR, UINT32, particles_50um, 11) \ +X(a, STATIC, SINGULAR, UINT32, particles_100um, 12) +#define meshtastic_AirQualityMetrics_CALLBACK NULL +#define meshtastic_AirQualityMetrics_DEFAULT NULL + #define meshtastic_Telemetry_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, FIXED32, time, 1) \ X(a, STATIC, ONEOF, MESSAGE, (variant,device_metrics,variant.device_metrics), 2) \ -X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) +X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,air_quality_metrics,variant.air_quality_metrics), 4) #define meshtastic_Telemetry_CALLBACK NULL #define meshtastic_Telemetry_DEFAULT NULL #define meshtastic_Telemetry_variant_device_metrics_MSGTYPE meshtastic_DeviceMetrics #define meshtastic_Telemetry_variant_environment_metrics_MSGTYPE meshtastic_EnvironmentMetrics +#define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg; extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg; +extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg; extern const pb_msgdesc_t meshtastic_Telemetry_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_DeviceMetrics_fields &meshtastic_DeviceMetrics_msg #define meshtastic_EnvironmentMetrics_fields &meshtastic_EnvironmentMetrics_msg +#define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg #define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg /* Maximum encoded size of messages (where known) */ +#define meshtastic_AirQualityMetrics_size 72 #define meshtastic_DeviceMetrics_size 21 #define meshtastic_EnvironmentMetrics_size 30 -#define meshtastic_Telemetry_size 37 +#define meshtastic_Telemetry_size 79 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index e8481356b..bc874a9a3 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -14,6 +14,7 @@ #include "modules/TraceRouteModule.h" #include "modules/WaypointModule.h" #if HAS_TELEMETRY +#include "modules/Telemetry/AirQualityTelemetry.h" #include "modules/Telemetry/DeviceTelemetry.h" #include "modules/Telemetry/EnvironmentTelemetry.h" #endif @@ -63,6 +64,9 @@ void setupModules() #if HAS_TELEMETRY new DeviceTelemetryModule(); new EnvironmentTelemetryModule(); + if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I] > 0) { + new AirQualityTelemetryModule(); + } #endif #if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2) new SerialModule(); diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp new file mode 100644 index 000000000..2b744c489 --- /dev/null +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -0,0 +1,128 @@ +#include "AirQualityTelemetry.h" +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "PowerFSM.h" +#include "RTC.h" +#include "Router.h" +#include "configuration.h" +#include "main.h" + +int32_t AirQualityTelemetryModule::runOnce() +{ +#ifndef ARCH_PORTDUINO + int32_t result = INT32_MAX; + /* + Uncomment the preferences below if you want to use the module + without having to configure it from the PythonAPI or WebUI. + */ + + // moduleConfig.telemetry.environment_measurement_enabled = 1; + + if (!(moduleConfig.telemetry.environment_measurement_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 disable(); + } + + if (firstTime) { + // This is the first time the OSThread library has called this function, so do some setup + firstTime = 0; + + if (moduleConfig.telemetry.environment_measurement_enabled) { + LOG_INFO("Air quality Telemetry: Initializing\n"); + if (!aqi.begin_I2C()) { + LOG_WARN("Could not establish i2c connection to AQI sensor\n"); + return disable(); + } + return 1000; + } + return result; + } else { + // if we somehow got to a second run of this module with measurement disabled, then just wait forever + if (!moduleConfig.telemetry.environment_measurement_enabled) + return result; + + uint32_t now = millis(); + if (((lastSentToMesh == 0) || + ((now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval))) && + airTime->isTxAllowedAirUtil()) { + sendTelemetry(); + lastSentToMesh = now; + } else if (service.isToPhoneQueueEmpty()) { + // Just send to phone when it's not our time to send to mesh yet + // Only send while queue is empty (phone assumed connected) + sendTelemetry(NODENUM_BROADCAST, true); + } + } + return sendToPhoneIntervalMs; +#endif +} + +bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) +{ + if (t->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { + const char *sender = getSenderShortName(mp); + + LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i\n", sender, + t->variant.air_quality_metrics.pm10_standard, t->variant.air_quality_metrics.pm25_standard, + t->variant.air_quality_metrics.pm100_standard); + + LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n", + t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental, + t->variant.air_quality_metrics.pm100_environmental); + + // release previous packet before occupying a new spot + if (lastMeasurementPacket != nullptr) + packetPool.release(lastMeasurementPacket); + + lastMeasurementPacket = packetPool.allocCopy(mp); + } + + return false; // Let others look at this message also if they want +} + +bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) +{ + if (!aqi.read(&data)) { + LOG_WARN("Skipping send measurements. Could not read AQIn\n"); + return false; + } + + meshtastic_Telemetry m; + m.time = getTime(); + m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag; + m.variant.air_quality_metrics.pm10_standard = data.pm10_standard; + m.variant.air_quality_metrics.pm25_standard = data.pm25_standard; + m.variant.air_quality_metrics.pm100_standard = data.pm100_standard; + + m.variant.air_quality_metrics.pm10_environmental = data.pm10_env; + m.variant.air_quality_metrics.pm25_environmental = data.pm25_env; + m.variant.air_quality_metrics.pm100_environmental = data.pm100_env; + + LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n", + m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard, + m.variant.air_quality_metrics.pm100_standard); + + LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n", + m.variant.air_quality_metrics.pm10_environmental, m.variant.air_quality_metrics.pm25_environmental, + m.variant.air_quality_metrics.pm100_environmental); + + meshtastic_MeshPacket *p = allocDataProtobuf(m); + p->to = dest; + p->decoded.want_response = false; + p->priority = meshtastic_MeshPacket_Priority_MIN; + + // release previous packet before occupying a new spot + if (lastMeasurementPacket != nullptr) + packetPool.release(lastMeasurementPacket); + + lastMeasurementPacket = packetPool.allocCopy(*p); + if (phoneOnly) { + LOG_INFO("Sending packet to phone\n"); + service.sendToPhone(p); + } else { + LOG_INFO("Sending packet to mesh\n"); + service.sendToMesh(p, RX_SRC_LOCAL, true); + } + return true; +} diff --git a/src/modules/Telemetry/AirQualityTelemetry.h b/src/modules/Telemetry/AirQualityTelemetry.h new file mode 100644 index 000000000..8900c0eab --- /dev/null +++ b/src/modules/Telemetry/AirQualityTelemetry.h @@ -0,0 +1,37 @@ +#pragma once +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "Adafruit_PM25AQI.h" +#include "NodeDB.h" +#include "ProtobufModule.h" + +class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule +{ + public: + AirQualityTelemetryModule() + : concurrency::OSThread("AirQualityTelemetryModule"), + ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) + { + lastMeasurementPacket = nullptr; + setIntervalFromNow(10 * 1000); + aqi = Adafruit_PM25AQI(); + } + + 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 meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override; + virtual int32_t runOnce() override; + /** + * Send our Telemetry into the mesh + */ + bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); + + private: + Adafruit_PM25AQI aqi; + PM25_AQI_Data data; + bool firstTime = 1; + meshtastic_MeshPacket *lastMeasurementPacket; + uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute + uint32_t lastSentToMesh = 0; +}; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index c1693a8c8..d9b129e70 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -118,6 +118,16 @@ int32_t EnvironmentTelemetryModule::runOnce() #endif } +bool EnvironmentTelemetryModule::wantUIFrame() +{ + return moduleConfig.telemetry.environment_screen_enabled; +} + +float EnvironmentTelemetryModule::CelsiusToFahrenheit(float c) +{ + return (c * 9) / 5 + 32; +} + uint32_t GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp) { uint32_t now = getTime(); @@ -130,16 +140,6 @@ uint32_t GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp) return delta; } -bool EnvironmentTelemetryModule::wantUIFrame() -{ - return moduleConfig.telemetry.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); From 1748db31605b289155177085fcfcc4fa319b08fe Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 4 Feb 2023 13:35:02 -0600 Subject: [PATCH 2/2] Init struct --- src/modules/Telemetry/AirQualityTelemetry.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Telemetry/AirQualityTelemetry.h b/src/modules/Telemetry/AirQualityTelemetry.h index 8900c0eab..ab77d61e7 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.h +++ b/src/modules/Telemetry/AirQualityTelemetry.h @@ -29,7 +29,7 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf private: Adafruit_PM25AQI aqi; - PM25_AQI_Data data; + PM25_AQI_Data data = {0}; bool firstTime = 1; meshtastic_MeshPacket *lastMeasurementPacket; uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute