Compare commits

...

28 Commits

Author SHA1 Message Date
Riley Nielsen
e1d6ef423f
Merge f0775c586f into edb7ec58c6 2025-09-02 21:35:19 +02:00
renovate[bot]
edb7ec58c6
chore(deps): update platform-native digest to c490bcd (#7814)
Some checks are pending
CI / build-rp2040 (push) Blocked by required conditions
CI / build-rp2350 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (rp2350) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (rp2350) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-02 11:58:57 -05:00
Ben Meadors
655c6b51fe
Try-fix Cardkb detection (#7825)
* Try-fix: CardKB detection regression

* Correct macro
2025-09-02 09:50:15 -05:00
Austin
f0775c586f
Merge branch 'master' into scd4x 2025-07-21 16:30:32 -04:00
Thomas Göttgens
80175b1e17
Merge branch 'master' into scd4x 2025-07-13 18:17:58 +02:00
Tom Fifield
1eeadde989
Merge branch 'master' into scd4x 2025-07-02 09:24:21 +10:00
Tom Fifield
cfc355b29a
Update main.cpp 2025-07-01 21:31:23 +10:00
Tom Fifield
ef203b5dca
Merge branch 'master' into scd4x 2025-07-01 21:28:58 +10:00
Tom Fifield
58d916da39
Fix merge 2025-07-01 19:49:42 +10:00
Tom Fifield
02a84142df
Merge branch 'master' into scd4x 2025-07-01 19:35:38 +10:00
Tom Fifield
726cf8611b
Merge branch 'master' into scd4x 2025-07-01 18:33:09 +10:00
Thomas Göttgens
bac816d80a
Merge branch 'master' into scd4x 2025-04-11 15:45:09 +02:00
Thomas Göttgens
7fe9efe805
Wupps 2025-04-07 10:03:40 +02:00
Thomas Göttgens
ad7647ef95
Merge branch 'master' into scd4x 2025-04-07 09:22:38 +02:00
Thomas Göttgens
3cef1e89ed
Merge branch 'master' into scd4x 2025-03-31 11:34:04 +02:00
Tom Fifield
a1cbe8ebb8
Merge branch 'master' into scd4x 2024-11-19 13:55:28 +08:00
Thomas Göttgens
16e2540f0f
Merge branch 'master' into scd4x 2024-11-07 17:37:05 +01:00
Ben Meadors
e34aadabe1
Merge branch 'master' into scd4x 2024-10-20 05:44:08 -05:00
Tom Fifield
81f9b38c11
Merge branch 'master' into scd4x 2024-10-19 13:03:26 +11:00
Tom Fifield
8faf4665b0 Re-add I2C scan, fix frame. 2024-10-19 13:00:25 +11:00
Tom Fifield
318da220dc Abstract AirQualityTelemetry module
Previously the module was designed around a single sensor.
This patch brings it into line with the same design as
EnvironmentTelemetry.
2024-10-18 18:16:45 +11:00
Tom Fifield
a28e83c35a
Merge branch 'master' into scd4x 2024-10-18 16:48:27 +11:00
Riley Nielsen
8a4427a69f Merge remote-tracking branch 'upstream/master' into scd4x 2024-10-11 09:53:09 -07:00
Riley Nielsen
c9233fef1c Merge remote-tracking branch 'upstream/master' into scd4x 2024-10-11 09:19:55 -07:00
Riley Nielsen
f599984990 update from upstream 2024-09-25 18:34:17 -07:00
Riley Nielsen
e73ff62b22 Merge remote-tracking branch 'upstream/master' into scd4x 2024-09-25 17:38:00 -07:00
Thomas Göttgens
30532d4c6a
Merge branch 'master' into scd4x 2024-09-02 10:33:12 +02:00
Riley Nielsen
8fa513ff1f add CO2 sensing with SCD4X 2024-08-31 16:32:53 -07:00
9 changed files with 295 additions and 22 deletions

View File

@ -2,7 +2,7 @@
[portduino_base] [portduino_base]
platform = platform =
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop # renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
https://github.com/meshtastic/platform-native/archive/37d986499ce24511952d7146db72d667c6bdaff7.zip https://github.com/meshtastic/platform-native/archive/c490bcd019e0658404088a61b96e653c9da22c45.zip
framework = arduino framework = arduino
build_src_filter = build_src_filter =

View File

@ -13,7 +13,11 @@ void CardKbI2cImpl::init()
if (cardkb_found.address == 0x00) { if (cardkb_found.address == 0x00) {
LOG_DEBUG("Rescan for I2C keyboard"); LOG_DEBUG("Rescan for I2C keyboard");
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR, TCA8418_KB_ADDR}; uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR, TCA8418_KB_ADDR};
#if defined(T_LORA_PAGER)
uint8_t i2caddr_asize = sizeof(i2caddr_scan) / sizeof(i2caddr_scan[0]); uint8_t i2caddr_asize = sizeof(i2caddr_scan) / sizeof(i2caddr_scan[0]);
#else
uint8_t i2caddr_asize = 5;
#endif
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire()); auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
#if WIRE_INTERFACES_COUNT == 2 #if WIRE_INTERFACES_COUNT == 2

View File

@ -10,10 +10,19 @@
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RTC.h" #include "RTC.h"
#include "Router.h" #include "Router.h"
#include "UnitConversions.h"
#include "detect/ScanI2CTwoWire.h" #include "detect/ScanI2CTwoWire.h"
#include "graphics/ScreenFonts.h"
#include "main.h" #include "main.h"
#include "sleep.h"
#include <Throttle.h> #include <Throttle.h>
// Sensors
#include "Sensor/PMSA0031Sensor.h"
#include "Sensor/SCD4XSensor.h"
SCD4XSensor scd4xSensor;
PMSA0031Sensor pmsa0031Sensor;
#ifndef PMSA003I_WARMUP_MS #ifndef PMSA003I_WARMUP_MS
// from the PMSA003I datasheet: // from the PMSA003I datasheet:
// "Stable data should be got at least 30 seconds after the sensor wakeup // "Stable data should be got at least 30 seconds after the sensor wakeup
@ -23,11 +32,20 @@
int32_t AirQualityTelemetryModule::runOnce() int32_t AirQualityTelemetryModule::runOnce()
{ {
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval,
default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.", nightyNightMs);
doDeepSleep(nightyNightMs, true);
}
uint32_t result = UINT32_MAX;
/* /*
Uncomment the preferences below if you want to use the module Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI. without having to configure it from the PythonAPI or WebUI.
*/ */
// moduleConfig.telemetry.air_quality_enabled = 1; // moduleConfig.telemetry.air_quality_enabled = 1;
if (!(moduleConfig.telemetry.air_quality_enabled)) { if (!(moduleConfig.telemetry.air_quality_enabled)) {
@ -41,24 +59,27 @@ int32_t AirQualityTelemetryModule::runOnce()
if (moduleConfig.telemetry.air_quality_enabled) { if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: init"); LOG_INFO("Air quality Telemetry: init");
#ifdef PMSA003I_ENABLE_PIN #ifdef PMSA003I_ENABLE_PIN
// put the sensor to sleep on startup // put the sensor to sleep on startup
pinMode(PMSA003I_ENABLE_PIN, OUTPUT); pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
digitalWrite(PMSA003I_ENABLE_PIN, LOW); digitalWrite(PMSA003I_ENABLE_PIN, LOW);
#endif /* PMSA003I_ENABLE_PIN */ #endif /* PMSA003I_ENABLE_PIN */
if (aqi_found.address == 0x00) {
if (!aqi.begin_I2C()) {
#ifndef I2C_NO_RESCAN #ifndef I2C_NO_RESCAN
LOG_WARN("Could not establish i2c connection to AQI sensor. Rescan"); LOG_WARN("Rescan for I2C AQI Sensor");
// rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty. // rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty.
uint8_t i2caddr_scan[] = {PMSA0031_ADDR}; uint8_t i2caddr_scan[] = {PMSA0031_ADDR, SCD4X_ADDR};
uint8_t i2caddr_asize = 1; uint8_t i2caddr_asize = 2;
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire()); auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
#if defined(I2C_SDA1)
#if WIRE_INTERFACES_COUNT == 2
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize); i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize);
#endif #endif
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize); i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize);
auto found = i2cScanner->find(ScanI2C::DeviceType::PMSA0031); auto found = i2cScanner->find(ScanI2C::DeviceType::PMSA0031);
if (found.type != ScanI2C::DeviceType::NONE) { if (found.type != ScanI2C::DeviceType::NONE) {
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address;
@ -66,8 +87,36 @@ int32_t AirQualityTelemetryModule::runOnce()
i2cScanner->fetchI2CBus(found.address); i2cScanner->fetchI2CBus(found.address);
return setStartDelay(); return setStartDelay();
} }
#endif if (aqi_found.address == 0x00) {
return disable();
}
#endif
}
if (scd4xSensor.hasSensor())
result = scd4xSensor.runOnce();
if (pmsa0031Sensor.hasSensor())
result = pmsa0031Sensor.runOnce();
return result;
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.air_quality_enabled)
return disable(); return disable();
if (((lastSentToMesh == 0) ||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.air_quality_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(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);
lastSentToPhone = millis();
} }
return setStartDelay(); return setStartDelay();
} }
@ -115,6 +164,7 @@ int32_t AirQualityTelemetryModule::runOnce()
default: default:
return disable(); return disable();
} }
return min(sendToPhoneIntervalMs, result);
} }
} }
@ -124,9 +174,9 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) #if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
const char *sender = getSenderShortName(mp); const char *sender = getSenderShortName(mp);
LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i", sender, LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i, co2=%i ppm", sender,
t->variant.air_quality_metrics.pm10_standard, t->variant.air_quality_metrics.pm25_standard, t->variant.air_quality_metrics.pm10_standard, t->variant.air_quality_metrics.pm25_standard,
t->variant.air_quality_metrics.pm100_standard); t->variant.air_quality_metrics.pm100_standard, t->variant.air_quality_metrics.co2);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i", LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i",
t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental, t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental,
@ -142,13 +192,38 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
return false; // Let others look at this message also if they want return false; // Let others look at this message also if they want
} }
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m) bool AirQualityTelemetryModule::wantUIFrame()
{ {
if (!aqi.read(&data)) { return moduleConfig.telemetry.environment_screen_enabled;
LOG_WARN("Skip send measurements. Could not read AQIn"); }
return false;
void AirQualityTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
if (lastMeasurementPacket == nullptr) {
// If there's no valid packet, display "Environment"
display->drawString(x, y, "Air Quality");
display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement");
return;
} }
// Decode the last measurement packet
meshtastic_Telemetry lastMeasurement;
uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
const meshtastic_Data &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->drawString(x, y, "Measurement Error");
LOG_ERROR("Unable to decode last packet");
return;
}
// Display "Env. From: ..." on its own
display->drawString(x, y, "AQ. From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
m->time = getTime(); m->time = getTime();
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag; m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m->variant.air_quality_metrics.has_pm10_standard = true; m->variant.air_quality_metrics.has_pm10_standard = true;
@ -168,11 +243,42 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard, LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard,
m->variant.air_quality_metrics.pm25_standard, m->variant.air_quality_metrics.pm100_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", if (lastMeasurement.variant.air_quality_metrics.has_pm10_standard) {
m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental, display->drawString(x, y += _fontHeight(FONT_SMALL),
m->variant.air_quality_metrics.pm100_environmental); "PM1.0(Standard): " + String(lastMeasurement.variant.air_quality_metrics.pm10_standard, 0));
}
if (lastMeasurement.variant.air_quality_metrics.has_pm25_standard) {
display->drawString(x, y += _fontHeight(FONT_SMALL),
"PM2.5(Standard): " + String(lastMeasurement.variant.air_quality_metrics.pm25_standard, 0));
}
if (lastMeasurement.variant.air_quality_metrics.has_pm10_environmental) {
display->drawString(x, y += _fontHeight(FONT_SMALL),
"PM10.0(Standard): " + String(lastMeasurement.variant.air_quality_metrics.pm100_standard, 0));
}
if (lastMeasurement.variant.air_quality_metrics.has_co2) {
display->drawString(x, y += _fontHeight(FONT_SMALL),
"CO2: " + String(lastMeasurement.variant.air_quality_metrics.co2, 0) + " ppm");
}
}
return true; bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
{
bool valid = true;
bool hasSensor = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m->variant.air_quality_metrics = meshtastic_AirQualityMetrics_init_zero;
if (scd4xSensor.hasSensor()) {
valid = valid && scd4xSensor.getMetrics(m);
hasSensor = true;
}
if (pmsa0031Sensor.hasSensor()) {
valid = valid && pmsa0031Sensor.getMetrics(m);
hasSensor = true;
}
return valid && hasSensor;
} }
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply() meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
@ -207,6 +313,14 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{ {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) { if (getAirQualityTelemetry(&m)) {
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i, cO2=%i ppm",
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
m.variant.air_quality_metrics.pm100_standard, m.variant.air_quality_metrics.co2);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i",
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); meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest; p->to = dest;
p->decoded.want_response = false; p->decoded.want_response = false;

View File

@ -4,9 +4,10 @@
#pragma once #pragma once
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "Adafruit_PM25AQI.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "ProtobufModule.h" #include "ProtobufModule.h"
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry> class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
{ {
@ -20,9 +21,8 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{ {
lastMeasurementPacket = nullptr; lastMeasurementPacket = nullptr;
setIntervalFromNow(10 * 1000);
aqi = Adafruit_PM25AQI();
nodeStatusObserver.observe(&nodeStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
#ifdef PMSA003I_ENABLE_PIN #ifdef PMSA003I_ENABLE_PIN
// the PMSA003I sensor uses about 300mW on its own; support powering it off when it's not actively taking // the PMSA003I sensor uses about 300mW on its own; support powering it off when it's not actively taking
@ -32,6 +32,12 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
state = State::ACTIVE; state = State::ACTIVE;
#endif #endif
} }
virtual bool wantUIFrame() override;
#if !HAS_SCREEN
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
#else
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
#endif
protected: protected:
/** Called to handle a particular incoming message /** Called to handle a particular incoming message
@ -62,6 +68,8 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
meshtastic_MeshPacket *lastMeasurementPacket; meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0; uint32_t lastSentToMesh = 0;
uint32_t lastSentToPhone = 0;
uint32_t sensor_read_error_count = 0;
}; };
#endif #endif

View File

@ -510,6 +510,7 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
sender, t->variant.environment_metrics.barometric_pressure, t->variant.environment_metrics.current, sender, t->variant.environment_metrics.barometric_pressure, t->variant.environment_metrics.current,
t->variant.environment_metrics.gas_resistance, t->variant.environment_metrics.relative_humidity, t->variant.environment_metrics.gas_resistance, t->variant.environment_metrics.relative_humidity,
t->variant.environment_metrics.temperature); t->variant.environment_metrics.temperature);
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f, white_lux=%f", sender, LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f, white_lux=%f", sender,
t->variant.environment_metrics.voltage, t->variant.environment_metrics.iaq, t->variant.environment_metrics.voltage, t->variant.environment_metrics.iaq,
t->variant.environment_metrics.distance, t->variant.environment_metrics.lux, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux,

View File

@ -0,0 +1,47 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "PMSA0031Sensor.h"
#include "TelemetrySensor.h"
#include <Adafruit_PM25AQI.h>
PMSA0031Sensor::PMSA0031Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PMSA003I, "PMSA0031") {}
int32_t PMSA0031Sensor::runOnce()
{
LOG_INFO("Init sensor: %s\n", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
aqi = Adafruit_PM25AQI();
delay(10000);
aqi.begin_I2C();
/* nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address;
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second =
i2cScanner->fetchI2CBus(found.address););*/
return initI2CSensor();
}
void PMSA0031Sensor::setup() {}
bool PMSA0031Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
uint16_t co2, error;
float temperature, humidity;
if (!aqi.read(&data)) {
LOG_WARN("Skipping send measurements. Could not read AQIn");
return false;
}
measurement->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
measurement->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
measurement->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
measurement->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
measurement->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
measurement->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
return true;
}
#endif

View File

@ -0,0 +1,24 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_PM25AQI.h>
class PMSA0031Sensor : public TelemetrySensor
{
private:
Adafruit_PM25AQI aqi;
PM25_AQI_Data data = {0};
protected:
virtual void setup() override;
public:
PMSA0031Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif

View File

@ -0,0 +1,52 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SCD4XSensor.h"
#include "TelemetrySensor.h"
#include <SensirionI2CScd4x.h>
SCD4XSensor::SCD4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD4X, "SCD4X") {}
int32_t SCD4XSensor::runOnce()
{
LOG_INFO("Init sensor: %s\n", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
// scd4x = SensirionI2CScd4x(nodeTelemetrySensorsMap[sensorType].second);
// status = scd4x.begin(nodeTelemetrySensorsMap[sensorType].first);
scd4x.begin(*nodeTelemetrySensorsMap[sensorType].second);
scd4x.stopPeriodicMeasurement();
status = scd4x.startLowPowerPeriodicMeasurement();
if (status == 0) {
status = 1;
} else {
status = 0;
}
return initI2CSensor();
}
void SCD4XSensor::setup() {}
bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
{
uint16_t co2, error;
float temperature, humidity;
error = scd4x.readMeasurement(co2, temperature, humidity);
if (error || co2 == 0) {
LOG_DEBUG("Skipping invalid SCD4X measurement.\n");
return false;
} else {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.air_quality_metrics.has_co2 = true;
measurement->variant.environment_metrics.temperature = temperature;
measurement->variant.environment_metrics.relative_humidity = humidity;
measurement->variant.air_quality_metrics.co2 = co2;
return true;
}
}
#endif

View File

@ -0,0 +1,23 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <SensirionI2CScd4x.h>
class SCD4XSensor : public TelemetrySensor
{
private:
SensirionI2CScd4x scd4x = SensirionI2CScd4x();
protected:
virtual void setup() override;
public:
SCD4XSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif