Reclock I2C for sensors is aware of screen I2C frequency (#9898)

* Add define for screen I2C frequency

* Add functions to get I2C screen frequency and port

* Make reclock function aware of screen-set I2C speeds

* Refurbish ReClockI2C API

* Make ReClockI2C API class
* Use new API in AirQualityTelemetry module
* Minor changes on some logs

* Update esp8266-oled library

* Fix esp8266 library

* Minor logging changes

* Improve setClock and restoreClock cases

* Make getter functions const, fix capitalisaiton of new functions

* Minor typo, remove pragma once

* Low prio debug fixes

* Mark getter functions as const (properly)

* Fix LOG based on coderabbit feedback

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
oscgonfer
2026-07-02 14:32:48 -05:00
committed by GitHub
co-authored by GitHub Ben Meadors
parent a5cf82c69e
commit 4673ed22fc
17 changed files with 443 additions and 386 deletions
+30 -29
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "PMSA003ISensor.h"
#include "TelemetrySensor.h"
@@ -13,34 +12,37 @@ PMSA003ISensor::PMSA003ISensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp
bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
LOG_INFO("%s: Init sensor", sensorName);
#ifdef PMSA003I_ENABLE_PIN
pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
#endif
// TODO PMS5003I sometimes get late to the party...
_bus = bus;
_address = dev->address.address;
#ifdef PMSA003I_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
_port = dev->address.port;
reClockI2C.setup(_bus, _port);
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, PMSA003I_I2C_CLOCK_SPEED);
reClockI2C.setClock(PMSA003I_I2C_CLOCK_SPEED);
#endif /* PMSA003I_I2C_CLOCK_SPEED */
_bus->beginTransmission(_address);
if (_bus->endTransmission() != 0) {
LOG_WARN("%s not found on I2C at 0x12", sensorName);
#ifdef PMSA003I_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* PMSA003I_I2C_CLOCK_SPEED */
return false;
}
#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef PMSA003I_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* PMSA003I_I2C_CLOCK_SPEED */
status = 1;
LOG_INFO("%s Enabled", sensorName);
@@ -57,19 +59,17 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
}
#ifdef PMSA003I_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s: attempting to reclock speed to %uHz", sensorName, PMSA003I_I2C_CLOCK_SPEED);
reClockI2C.setClock(PMSA003I_I2C_CLOCK_SPEED);
#endif /* PMSA003I_I2C_CLOCK_SPEED */
_bus->requestFrom(_address, (uint8_t)PMSA003I_FRAME_LENGTH);
if (_bus->available() < PMSA003I_FRAME_LENGTH) {
LOG_WARN("%s read failed: incomplete data (%d bytes)", sensorName, _bus->available());
LOG_WARN("%s: read failed: incomplete data (%d bytes)", sensorName, _bus->available());
#ifdef PMSA003I_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* PMSA003I_I2C_CLOCK_SPEED */
return false;
}
@@ -77,12 +77,13 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
buffer[i] = _bus->read();
}
#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef PMSA003I_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* PMSA003I_I2C_CLOCK_SPEED */
if (buffer[0] != 0x42 || buffer[1] != 0x4D) {
LOG_WARN("%s frame header invalid: 0x%02X 0x%02X", sensorName, buffer[0], buffer[1]);
LOG_WARN("%s: frame header invalid: 0x%02X 0x%02X", sensorName, buffer[0], buffer[1]);
return false;
}
@@ -138,7 +139,7 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.air_quality_metrics.has_particles_100um = true;
measurement->variant.air_quality_metrics.particles_100um = read16(buffer, 26);
LOG_DEBUG("Got %s readings: pM1p0_standard=%u, pM2p5_standard=%u, pM10p0_standard=%u", sensorName,
LOG_DEBUG("%s: Got readings: pM1p0_standard=%u, pM2p5_standard=%u, pM10p0_standard=%u", sensorName,
measurement->variant.air_quality_metrics.pm10_standard, measurement->variant.air_quality_metrics.pm25_standard,
measurement->variant.air_quality_metrics.pm100_standard);
@@ -197,7 +198,7 @@ void PMSA003ISensor::sleep()
uint32_t PMSA003ISensor::wakeUp()
{
#ifdef PMSA003I_ENABLE_PIN
LOG_INFO("Waking up %s", sensorName);
LOG_INFO("%s: Waking up", sensorName);
digitalWrite(PMSA003I_ENABLE_PIN, HIGH);
state = State::ACTIVE;
pmMeasureStarted = getTime();
@@ -2,6 +2,7 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h"
#include "TelemetrySensor.h"
@@ -35,6 +36,10 @@ class PMSA003ISensor : public TelemetrySensor
uint8_t buffer[PMSA003I_FRAME_LENGTH]{};
TwoWire *_bus{};
uint8_t _address{};
#ifdef PMSA003I_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
};
#endif
+46 -69
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd30.h>)
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SCD30Sensor.h"
@@ -12,29 +11,26 @@ SCD30Sensor::SCD30Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD3
bool SCD30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
LOG_INFO("%s: Init sensor", sensorName);
_bus = bus;
_address = dev->address.address;
#ifdef SCD30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
_port = dev->address.port;
reClockI2C.setup(_bus, _port);
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */
scd30.begin(*_bus, _address);
if (!startMeasurement()) {
LOG_ERROR("%s: Failed to start periodic measurement", sensorName);
#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD30_I2C_CLOCK_SPEED */
return false;
}
@@ -42,9 +38,10 @@ bool SCD30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
LOG_WARN("%s: Could not determine ASC state", sensorName);
}
#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD30_I2C_CLOCK_SPEED */
if (state == SCD30_MEASUREMENT) {
status = 1;
@@ -62,30 +59,26 @@ bool SCD30Sensor::getMetrics(meshtastic_Telemetry *measurement)
float co2, temperature, humidity;
#ifdef SCD30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */
if (scd30.readMeasurementData(co2, temperature, humidity) != SCD30_NO_ERROR) {
LOG_ERROR("SCD30: Failed to read measurement data.");
#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
LOG_ERROR("%s: Failed to read measurement data", sensorName);
#ifdef SCD30_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD30_I2C_CLOCK_SPEED */
return false;
}
#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD30_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD30_I2C_CLOCK_SPEED */
if (co2 == 0) {
LOG_ERROR("SCD30: Invalid CO₂ reading.");
LOG_ERROR("%s: Invalid CO₂ reading", sensorName);
return false;
}
@@ -96,7 +89,7 @@ bool SCD30Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.air_quality_metrics.co2_temperature = temperature;
measurement->variant.air_quality_metrics.co2_humidity = humidity;
LOG_DEBUG("Got %s readings: co2=%u, co2_temp=%.2f, co2_hum=%.2f", sensorName, (uint32_t)co2, temperature, humidity);
LOG_DEBUG("%s: Got readings: co2=%u, co2_temp=%.2f, co2_hum=%.2f", sensorName, (uint32_t)co2, temperature, humidity);
return true;
}
@@ -283,7 +276,7 @@ bool SCD30Sensor::setTemperature(float tempReference)
tempOffset = (temperature - tempReference);
if (tempOffset < 0) {
LOG_ERROR("%s temperature offset is only positive", sensorName);
LOG_ERROR("%s: temperature offset is only positive", sensorName);
return false;
}
@@ -372,23 +365,17 @@ bool SCD30Sensor::isActive()
*/
uint32_t SCD30Sensor::wakeUp()
{
#ifdef SCD30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return 0;
#endif /* CAN_RECLOCK_I2C */
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */
startMeasurement();
#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD30_I2C_CLOCK_SPEED */
return 0;
}
@@ -400,21 +387,16 @@ uint32_t SCD30Sensor::wakeUp()
void SCD30Sensor::sleep()
{
#ifdef SCD30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return;
#endif /* CAN_RECLOCK_I2C */
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */
stopMeasurement();
#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD30_I2C_CLOCK_SPEED */
}
bool SCD30Sensor::canSleep()
@@ -438,14 +420,8 @@ AdminMessageHandleResult SCD30Sensor::handleAdminMessage(const meshtastic_MeshPa
AdminMessageHandleResult result;
#ifdef SCD30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return AdminMessageHandleResult::NOT_HANDLED;
#endif /* CAN_RECLOCK_I2C */
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */
switch (request->which_payload_variant) {
@@ -501,9 +477,10 @@ AdminMessageHandleResult SCD30Sensor::handleAdminMessage(const meshtastic_MeshPa
result = AdminMessageHandleResult::NOT_HANDLED;
}
#if defined(SCD30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD30_I2C_CLOCK_SPEED */
return result;
}
@@ -2,6 +2,7 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd30.h>)
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <SensirionI2cScd30.h>
@@ -14,6 +15,10 @@ class SCD30Sensor : public TelemetrySensor
SensirionI2cScd30 scd30;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SCD30_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool performFRC(uint16_t targetCO2);
bool setASC(bool ascEnabled);
+71 -91
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd4x.h>)
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SCD4XSensor.h"
@@ -18,14 +17,11 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
_address = dev->address.address;
#ifdef SCD4X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
_port = dev->address.port;
reClockI2C.setup(_bus, _port);
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */
scd4x.begin(*_bus, _address);
@@ -35,9 +31,10 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
// Stop periodic measurement
if (!stopMeasurement()) {
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return false;
}
@@ -48,33 +45,37 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
LOG_INFO("%s: Found SCD41", sensorName);
if (!powerUp()) {
LOG_ERROR("%s: Error trying to execute powerUp()", sensorName);
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return false;
}
}
if (!getASC(ascActive)) {
LOG_ERROR("%s: Unable to check if ASC is enabled", sensorName);
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return false;
}
// Start measurement in selected power mode (low power by default)
if (!startMeasurement()) {
LOG_ERROR("%s: Couldn't start measurement", sensorName);
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return false;
}
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
if (state == SCD4X_MEASUREMENT) {
status = 1;
@@ -99,31 +100,27 @@ bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
float temperature, humidity;
#ifdef SCD4X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */
bool dataReady;
error = scd4x.getDataReadyStatus(dataReady);
if (error != SCD4X_NO_ERROR || !dataReady) {
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
LOG_ERROR("SCD4X: Data is not ready");
return false;
}
error = scd4x.readMeasurement(co2, temperature, humidity);
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
LOG_DEBUG("Got %s readings: co2=%u, co2_temp=%.2f, co2_hum%.2f", sensorName, co2, temperature, humidity);
if (error != SCD4X_NO_ERROR) {
@@ -637,34 +634,31 @@ bool SCD4XSensor::powerDown()
}
#ifdef SCD4X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */
if (!stopMeasurement()) {
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return false;
}
if (scd4x.powerDown() != SCD4X_NO_ERROR) {
LOG_ERROR("%s: Error trying to execute sleep()", sensorName);
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return false;
}
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
state = SCD4X_OFF;
return true;
@@ -711,27 +705,23 @@ uint32_t SCD4XSensor::wakeUp()
{
#ifdef SCD4X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return 0;
#endif /* CAN_RECLOCK_I2C */
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */
if (startMeasurement()) {
co2MeasureStarted = getTime();
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return SCD4X_WARMUP_MS;
}
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return 0;
}
@@ -743,21 +733,16 @@ uint32_t SCD4XSensor::wakeUp()
void SCD4XSensor::sleep()
{
#ifdef SCD4X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return;
#endif /* CAN_RECLOCK_I2C */
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */
stopMeasurement();
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
}
/**
@@ -797,14 +782,8 @@ AdminMessageHandleResult SCD4XSensor::handleAdminMessage(const meshtastic_MeshPa
AdminMessageHandleResult result;
#ifdef SCD4X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return AdminMessageHandleResult::NOT_HANDLED;
#endif /* CAN_RECLOCK_I2C */
LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */
// TODO: potentially add selftest command?
@@ -907,9 +886,10 @@ AdminMessageHandleResult SCD4XSensor::handleAdminMessage(const meshtastic_MeshPa
// Start measurement mode
this->startMeasurement();
#if defined(SCD4X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SCD4X_I2C_CLOCK_SPEED */
return result;
}
+6 -1
View File
@@ -2,13 +2,14 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd4x.h>)
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h"
#include "TelemetrySensor.h"
#include <SensirionI2cScd4x.h>
// Max speed 400kHz
#define SCD4X_I2C_CLOCK_SPEED 100000
#define SCD4X_I2C_CLOCK_SPEED 400000
#define SCD4X_WARMUP_MS 5000
class SCD4XSensor : public TelemetrySensor
@@ -17,6 +18,10 @@ class SCD4XSensor : public TelemetrySensor
SensirionI2cScd4x scd4x;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SCD4X_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool performFRC(uint32_t targetCO2);
bool setASCBaseline(uint32_t targetCO2);
+98 -95
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "FSCommon.h"
#include "SEN5XSensor.h"
@@ -18,7 +17,7 @@ SEN5XSensor::SEN5XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SEN5
bool SEN5XSensor::getVersion()
{
if (!sendCommand(SEN5X_GET_FIRMWARE_VERSION)) {
LOG_ERROR("SEN5X: Error sending version command");
LOG_ERROR("%s: Error sending version command", sensorName);
return false;
}
delay(20); // From Sensirion Datasheet
@@ -26,7 +25,7 @@ bool SEN5XSensor::getVersion()
uint8_t versionBuffer[12]{};
size_t charNumber = readBuffer(&versionBuffer[0], 3);
if (charNumber == 0) {
LOG_ERROR("SEN5X: Error getting data ready flag value");
LOG_ERROR("%s: Error getting data ready flag value", sensorName);
return false;
}
@@ -34,9 +33,9 @@ bool SEN5XSensor::getVersion()
hardwareVer = versionBuffer[3] + (versionBuffer[4] / 10);
protocolVer = versionBuffer[5] + (versionBuffer[6] / 10);
LOG_INFO("SEN5X Firmware Version: %0.2f", firmwareVer);
LOG_INFO("SEN5X Hardware Version: %0.2f", hardwareVer);
LOG_INFO("SEN5X Protocol Version: %0.2f", protocolVer);
LOG_INFO("%s: Firmware Version: %0.2f", sensorName, firmwareVer);
LOG_INFO("%s: Hardware Version: %0.2f", sensorName, hardwareVer);
LOG_INFO("%s: Protocol Version: %0.2f", sensorName, protocolVer);
return true;
}
@@ -44,7 +43,7 @@ bool SEN5XSensor::getVersion()
bool SEN5XSensor::findModel()
{
if (!sendCommand(SEN5X_GET_PRODUCT_NAME)) {
LOG_ERROR("SEN5X: Error asking for product name");
LOG_ERROR("%s: Error asking for product name", sensorName);
return false;
}
delay(50); // From Sensirion Datasheet
@@ -53,7 +52,7 @@ bool SEN5XSensor::findModel()
uint8_t name[nameSize];
size_t charNumber = readBuffer(&name[0], nameSize);
if (charNumber == 0) {
LOG_ERROR("SEN5X: Error getting device name");
LOG_ERROR("%s: Error getting device name", sensorName);
return false;
}
@@ -61,15 +60,15 @@ bool SEN5XSensor::findModel()
switch (name[4]) {
case 48:
model = SEN50;
LOG_INFO("SEN5X: found sensor model SEN50");
LOG_INFO("%s: found sensor model SEN50", sensorName);
break;
case 52:
model = SEN54;
LOG_INFO("SEN5X: found sensor model SEN54");
LOG_INFO("%s: found sensor model SEN54", sensorName);
break;
case 53:
model = SEN55;
LOG_INFO("SEN5X: found sensor model SEN55");
LOG_INFO("%s: found sensor model SEN55", sensorName);
break;
}
@@ -108,14 +107,8 @@ bool SEN5XSensor::sendCommand(uint16_t command, uint8_t *buffer, uint8_t byteNum
}
#ifdef SEN5X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SEN5X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SEN5X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s: Attempting to reclock speed to %uHz", sensorName, SEN5X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SEN5X_I2C_CLOCK_SPEED);
#endif /* SEN5X_I2C_CLOCK_SPEED */
// Transmit the data
@@ -126,17 +119,18 @@ bool SEN5XSensor::sendCommand(uint16_t command, uint8_t *buffer, uint8_t byteNum
size_t writtenBytes = _bus->write(toSend, bufferSize);
uint8_t i2c_error = _bus->endTransmission();
#if defined(SEN5X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SEN5X_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SEN5X_I2C_CLOCK_SPEED */
if (writtenBytes != bufferSize) {
LOG_ERROR("SEN5X: Error writting on I2C bus");
LOG_ERROR("%s: Error writing on I2C bus", sensorName);
return false;
}
if (i2c_error != 0) {
LOG_ERROR("SEN5X: Error on I2C communication: %x", i2c_error);
LOG_ERROR("%s: Error on I2C communication: %x", sensorName, i2c_error);
return false;
}
return true;
@@ -145,19 +139,17 @@ bool SEN5XSensor::sendCommand(uint16_t command, uint8_t *buffer, uint8_t byteNum
uint8_t SEN5XSensor::readBuffer(uint8_t *buffer, uint8_t byteNumber)
{
#ifdef SEN5X_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SEN5X_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SEN5X_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s: Attempting to reclock speed to %uHz", sensorName, SEN5X_I2C_CLOCK_SPEED);
reClockI2C.setClock(SEN5X_I2C_CLOCK_SPEED);
#endif /* SEN5X_I2C_CLOCK_SPEED */
size_t readBytes = _bus->requestFrom(_address, byteNumber);
if (readBytes != byteNumber) {
LOG_ERROR("SEN5X: Error reading I2C bus");
LOG_ERROR("%s: Error reading I2C bus", sensorName);
#ifdef SEN5X_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SEN5X_I2C_CLOCK_SPEED */
return 0;
}
@@ -169,15 +161,21 @@ uint8_t SEN5XSensor::readBuffer(uint8_t *buffer, uint8_t byteNumber)
uint8_t recvCRC = _bus->read();
uint8_t calcCRC = sen5xCRC(&buffer[i - 2]);
if (recvCRC != calcCRC) {
LOG_ERROR("SEN5X: Checksum error while receiving msg");
LOG_ERROR("%s: Checksum error while receiving msg", sensorName);
#ifdef SEN5X_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SEN5X_I2C_CLOCK_SPEED */
return 0;
}
readBytes -= 3;
receivedBytes += 2;
}
#if defined(SEN5X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SEN5X_I2C_CLOCK_SPEED
LOG_DEBUG("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SEN5X_I2C_CLOCK_SPEED */
return receivedBytes;
}
@@ -305,24 +303,24 @@ bool SEN5XSensor::vocStateToSensor()
}
if (!vocStateValid()) {
LOG_INFO("SEN5X: VOC state is invalid, not sending");
LOG_INFO("%s: VOC state is invalid, not sending", sensorName);
return true;
}
if (!sendCommand(SEN5X_STOP_MEASUREMENT)) {
LOG_ERROR("SEN5X: Error stoping measurement");
LOG_ERROR("%s: Error stopping measurement", sensorName);
return false;
}
delay(200); // From Sensirion Datasheet
LOG_DEBUG("SEN5X: Sending VOC state to sensor");
LOG_DEBUG("%s: Sending VOC state to sensor", sensorName);
LOG_DEBUG("[%u, %u, %u, %u, %u, %u, %u, %u]", vocState[0], vocState[1], vocState[2], vocState[3], vocState[4], vocState[5],
vocState[6], vocState[7]);
// Note: send command already takes into account the CRC
// buffer size increment needed
if (!sendCommand(SEN5X_RW_VOCS_STATE, vocState, SEN5X_VOC_STATE_BUFFER_SIZE)) {
LOG_ERROR("SEN5X: Error sending VOC's state command'");
LOG_ERROR("%s: Error sending VOC's state command", sensorName);
return false;
}
@@ -335,10 +333,10 @@ bool SEN5XSensor::vocStateFromSensor()
return true;
}
LOG_INFO("SEN5X: Getting VOC state from sensor");
LOG_INFO("%s: Getting VOC state from sensor", sensorName);
// Ask VOCs state from the sensor
if (!sendCommand(SEN5X_RW_VOCS_STATE)) {
LOG_ERROR("SEN5X: Error sending VOC's state command'");
LOG_ERROR("%s: Error sending VOC's state command", sensorName);
return false;
}
@@ -350,13 +348,13 @@ bool SEN5XSensor::vocStateFromSensor()
delay(20); // From Sensirion Datasheet
if (receivedNumber == 0) {
LOG_DEBUG("SEN5X: Error getting VOC's state");
LOG_DEBUG("%s: Error getting VOC's state", sensorName);
return false;
}
// Print the state (if debug is on)
LOG_DEBUG("SEN5X: VOC state retrieved from sensor: [%u, %u, %u, %u, %u, %u, %u, %u]", vocState[0], vocState[1], vocState[2],
vocState[3], vocState[4], vocState[5], vocState[6], vocState[7]);
LOG_DEBUG("%s: VOC state retrieved from sensor: [%u, %u, %u, %u, %u, %u, %u, %u]", sensorName, vocState[0], vocState[1],
vocState[2], vocState[3], vocState[4], vocState[5], vocState[6], vocState[7]);
return true;
}
@@ -368,11 +366,11 @@ bool SEN5XSensor::loadState()
auto file = FSCom.open(sen5XStateFileName, FILE_O_READ);
bool okay = false;
if (file) {
LOG_INFO("%s state read from %s", sensorName, sen5XStateFileName);
LOG_INFO("%s: state read from %s", sensorName, sen5XStateFileName);
pb_istream_t stream = {&readcb, &file, meshtastic_SEN5XState_size};
if (!pb_decode(&stream, &meshtastic_SEN5XState_msg, &sen5xstate)) {
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
LOG_ERROR("%s: can't decode protobuf %s", sensorName, PB_GET_ERROR(&stream));
} else {
lastCleaning = sen5xstate.last_cleaning_time;
lastCleaningValid = sen5xstate.last_cleaning_valid;
@@ -404,12 +402,12 @@ bool SEN5XSensor::loadState()
}
file.close();
} else {
LOG_INFO("No %s state found (File: %s)", sensorName, sen5XStateFileName);
LOG_INFO("%s: No state found (File: %s)", sensorName, sen5XStateFileName);
}
spiLock->unlock();
return okay;
#else
LOG_ERROR("SEN5X: ERROR - Filesystem not implemented");
LOG_ERROR("%s: Filesystem not implemented", sensorName);
#endif
}
@@ -442,7 +440,7 @@ bool SEN5XSensor::saveState()
pb_ostream_t stream = {&writecb, static_cast<Print *>(&file), meshtastic_SEN5XState_size};
if (!pb_encode(&stream, &meshtastic_SEN5XState_msg, &sen5xstate)) {
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
LOG_ERROR("%s: can't encode protobuf %s", sensorName, PB_GET_ERROR(&stream));
} else {
okay = true;
}
@@ -454,7 +452,7 @@ bool SEN5XSensor::saveState()
return okay;
#else
LOG_ERROR("%s: ERROR - Filesystem not implemented", sensorName);
LOG_ERROR("%s: Filesystem not implemented", sensorName);
#endif
}
@@ -466,10 +464,10 @@ bool SEN5XSensor::isActive()
uint32_t SEN5XSensor::wakeUp()
{
LOG_DEBUG("SEN5X: Waking up sensor");
LOG_DEBUG("%s: Waking up sensor", sensorName);
if (!sendCommand(SEN5X_START_MEASUREMENT)) {
LOG_ERROR("SEN5X: Error starting measurement");
LOG_ERROR("%s: Error starting measurement", sensorName);
// TODO - what should this return?? Something actually on the default interval?
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
@@ -481,7 +479,7 @@ uint32_t SEN5XSensor::wakeUp()
pmMeasureStarted = getTime();
state = SEN5X_MEASUREMENT;
if (state == SEN5X_MEASUREMENT)
LOG_INFO("SEN5X: Started measurement mode");
LOG_INFO("%s: Started measurement mode", sensorName);
return SEN5X_WARMUP_MS_1;
}
@@ -490,7 +488,7 @@ bool SEN5XSensor::vocStateStable()
uint32_t now;
now = getTime();
uint32_t sinceFirstMeasureStarted = (now - rhtGasMeasureStarted);
LOG_DEBUG("sinceFirstMeasureStarted: %us", sinceFirstMeasureStarted);
LOG_DEBUG("%s: sinceFirstMeasureStarted: %us", sensorName, sinceFirstMeasureStarted);
return sinceFirstMeasureStarted > SEN5X_VOC_STATE_WARMUP_S;
}
@@ -502,25 +500,25 @@ bool SEN5XSensor::startCleaning()
// Note that cleaning command can only be run when the sensor is in measurement mode
if (!sendCommand(SEN5X_START_MEASUREMENT)) {
LOG_ERROR("SEN5X: Error starting measurment mode");
LOG_ERROR("%s: Error starting measurement mode", sensorName);
return false;
}
delay(50); // From Sensirion Datasheet
if (!sendCommand(SEN5X_START_FAN_CLEANING)) {
LOG_ERROR("SEN5X: Error starting fan cleaning");
LOG_ERROR("%s: Error starting fan cleaning", sensorName);
return false;
}
delay(20); // From Sensirion Datasheet
// This message will be always printed so the user knows the device it's not hung
LOG_INFO("SEN5X: Started fan cleaning it will take 10 seconds...");
LOG_INFO("%s: Started fan cleaning it will take 10 seconds...", sensorName);
uint16_t started = millis();
while (millis() - started < 10500) {
delay(500);
}
LOG_INFO("SEN5X: Cleaning done!!");
LOG_INFO("%s: Cleaning done", sensorName);
// Save timestamp in flash so we know when a week has passed
uint32_t now;
@@ -537,21 +535,25 @@ bool SEN5XSensor::startCleaning()
bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
state = SEN5X_NOT_DETECTED;
LOG_INFO("Init sensor: %s", sensorName);
LOG_INFO("%s: Init sensor", sensorName);
_bus = bus;
_address = dev->address.address;
#ifdef SEN5X_I2C_CLOCK_SPEED
_port = dev->address.port;
reClockI2C.setup(_bus, _port);
#endif /* SEN5X_I2C_CLOCK_SPEED */
delay(50); // without this there is an error on the deviceReset function
if (!sendCommand(SEN5X_RESET)) {
LOG_ERROR("SEN5X: Error reseting device");
LOG_ERROR("%s: error resetting device", sensorName);
return false;
}
delay(200); // From Sensirion Datasheet
if (!findModel()) {
LOG_ERROR("SEN5X: error finding sensor model");
LOG_ERROR("%s: error finding sensor model", sensorName);
return false;
}
@@ -559,7 +561,7 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (!getVersion())
return false;
if (firmwareVer < 2) {
LOG_ERROR("SEN5X: error firmware is too old and will not work with this implementation");
LOG_ERROR("%s: firmware is too old and will not work with this implementation", sensorName);
return false;
}
delay(200); // From Sensirion Datasheet
@@ -584,11 +586,12 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (passed > ONE_WEEK_IN_SECONDS && (now > SEN5X_VOC_VALID_DATE)) {
// If current date greater than 01/01/2018 (validity check)
LOG_INFO("SEN5X: More than a week (%us) since last cleaning in epoch (%us). Trigger, cleaning...", passed,
lastCleaning);
LOG_INFO("%s: More than a week (%us) since last cleaning in epoch (%us). Trigger, cleaning...", sensorName,
passed, lastCleaning);
startCleaning();
} else {
LOG_INFO("SEN5X: Cleaning not needed (%ds passed). Last cleaning date (in epoch): %us", passed, lastCleaning);
LOG_INFO("%s: Cleaning not needed (%ds passed). Last cleaning date (in epoch): %us", sensorName, passed,
lastCleaning);
}
} else {
// We assume the device has just been updated or it is new,
@@ -597,29 +600,29 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
// Otherwise, we will never trigger cleaning in some cases
lastCleaning = now;
lastCleaningValid = true;
LOG_INFO("SEN5X: No valid last cleaning date found, saving it now: %us", lastCleaning);
LOG_INFO("%s: No valid last cleaning date found, saving it now: %us", sensorName, lastCleaning);
saveState();
}
if (model != SEN50) {
if (!vocValid) {
LOG_INFO("SEN5X: No valid VOC's state found");
LOG_INFO("%s: No valid VOC's state found", sensorName);
} else {
// Check if state is recent
if (vocStateRecent(now)) {
// If current date greater than 01/01/2018 (validity check)
// Send it to the sensor
LOG_INFO("SEN5X: VOC state is valid and recent");
LOG_INFO("%s: VOC state is valid and recent", sensorName);
vocStateToSensor();
} else {
LOG_INFO("SEN5X: VOC state is too old or date is invalid");
LOG_DEBUG("SEN5X: vocTime %u, Passed %u, and now %u", vocTime, passed, now);
LOG_INFO("%s: VOC state is too old or date is invalid", sensorName);
LOG_DEBUG("%s: vocTime %u, Passed %u, and now %u", sensorName, vocTime, passed, now);
}
}
}
} else {
// TODO - Should this actually ignore? We could end up never cleaning...
LOG_INFO("SEN5X: Not enough RTCQuality, ignoring saved cleaning and VOC state");
LOG_INFO("%s: Not enough RTCQuality, ignoring saved cleaning and VOC state", sensorName);
}
idle(false);
@@ -632,16 +635,16 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
bool SEN5XSensor::readValues()
{
if (!sendCommand(SEN5X_READ_VALUES)) {
LOG_ERROR("SEN5X: Error sending read command");
LOG_ERROR("%s: Error sending read command", sensorName);
return false;
}
LOG_DEBUG("SEN5X: Reading PM Values");
LOG_DEBUG("%s: Reading PM Values", sensorName);
delay(20); // From Sensirion Datasheet
uint8_t dataBuffer[16]{};
size_t receivedNumber = readBuffer(&dataBuffer[0], 24);
if (receivedNumber == 0) {
LOG_ERROR("SEN5X: Error getting values");
LOG_ERROR("%s: Error getting values", sensorName);
return false;
}
@@ -666,16 +669,16 @@ bool SEN5XSensor::readValues()
sen5xmeasurement.vocIndex = !isnan(int_vocIndex) ? int_vocIndex / 10.0f : FLT_MAX;
sen5xmeasurement.noxIndex = !isnan(int_noxIndex) ? int_noxIndex / 10.0f : FLT_MAX;
LOG_DEBUG("Got %s readings: pM1p0=%u, pM2p5=%u, pM4p0=%u, pM10p0=%u", sensorName, sen5xmeasurement.pM1p0,
LOG_DEBUG("%s: Got readings: pM1p0=%u, pM2p5=%u, pM4p0=%u, pM10p0=%u", sensorName, sen5xmeasurement.pM1p0,
sen5xmeasurement.pM2p5, sen5xmeasurement.pM4p0, sen5xmeasurement.pM10p0);
if (model != SEN50) {
LOG_DEBUG("Got %s readings: humidity=%.2f, temperature=%.2f, vocIndex=%.2f", sensorName, sen5xmeasurement.humidity,
LOG_DEBUG("%s: Got readings: humidity=%.2f, temperature=%.2f, vocIndex=%.2f", sensorName, sen5xmeasurement.humidity,
sen5xmeasurement.temperature, sen5xmeasurement.vocIndex);
}
if (model == SEN55) {
LOG_DEBUG("Got %s readings: noxIndex=%.2f", sensorName, sen5xmeasurement.noxIndex);
LOG_DEBUG("%s: Got readings: noxIndex=%.2f", sensorName, sen5xmeasurement.noxIndex);
}
return true;
@@ -684,17 +687,17 @@ bool SEN5XSensor::readValues()
bool SEN5XSensor::readPNValues(bool cumulative)
{
if (!sendCommand(SEN5X_READ_PM_VALUES)) {
LOG_ERROR("SEN5X: Error sending read command");
LOG_ERROR("%s: Error sending read command", sensorName);
return false;
}
LOG_DEBUG("SEN5X: Reading PN Values");
LOG_DEBUG("%s: Reading PN Values", sensorName);
delay(20); // From Sensirion Datasheet
uint8_t dataBuffer[20]{};
size_t receivedNumber = readBuffer(&dataBuffer[0], 30);
if (receivedNumber == 0) {
LOG_ERROR("SEN5X: Error getting PN values");
LOG_ERROR("%s: Error getting PN values", sensorName);
return false;
}
@@ -728,7 +731,7 @@ bool SEN5XSensor::readPNValues(bool cumulative)
sen5xmeasurement.pN1p0 -= sen5xmeasurement.pN0p5;
}
LOG_DEBUG("Got %s readings: pN0p5=%u, pN1p0=%u, pN2p5=%u, pN4p0=%u, pN10p0=%u, tSize=%.2f", sensorName,
LOG_DEBUG("%s: Got readings: pN0p5=%u, pN1p0=%u, pN2p5=%u, pN4p0=%u, pN10p0=%u, tSize=%.2f", sensorName,
sen5xmeasurement.pN0p5, sen5xmeasurement.pN1p0, sen5xmeasurement.pN2p5, sen5xmeasurement.pN4p0,
sen5xmeasurement.pN10p0, sen5xmeasurement.tSize);
@@ -742,7 +745,7 @@ uint8_t SEN5XSensor::getMeasurements()
// Try to get new data
if (!sendCommand(SEN5X_READ_DATA_READY)) {
LOG_ERROR("SEN5X: Error sending command data ready flag");
LOG_ERROR("%s: Error sending command data ready flag", sensorName);
return 2;
}
delay(20); // From Sensirion Datasheet
@@ -750,7 +753,7 @@ uint8_t SEN5XSensor::getMeasurements()
uint8_t dataReadyBuffer[3];
size_t charNumber = readBuffer(&dataReadyBuffer[0], 3);
if (charNumber == 0) {
LOG_ERROR("SEN5X: Error getting device version value");
LOG_ERROR("%s: Error getting device version value", sensorName);
return 2;
}
@@ -758,17 +761,17 @@ uint8_t SEN5XSensor::getMeasurements()
uint32_t sinceLastDataPollMs = (now - lastDataPoll) * 1000;
// Check if data is ready, and if since last time we requested is less than SEN5X_POLL_INTERVAL
if (!dataReady && (sinceLastDataPollMs > SEN5X_POLL_INTERVAL)) {
LOG_INFO("SEN5X: Data is not ready");
LOG_INFO("%s: Data is not ready", sensorName);
return 1;
}
if (!readValues()) {
LOG_ERROR("SEN5X: Error getting readings");
LOG_ERROR("%s: Error getting readings", sensorName);
return 2;
}
if (!readPNValues(false)) {
LOG_ERROR("SEN5X: Error getting PN readings");
LOG_ERROR("%s: Error getting PN readings", sensorName);
return 2;
}
@@ -787,13 +790,13 @@ int32_t SEN5XSensor::pendingForReadyMs()
uint32_t now;
now = getTime();
uint32_t sincePmMeasureStarted = (now - pmMeasureStarted) * 1000;
LOG_DEBUG("SEN5X: Since measure started: %ums", sincePmMeasureStarted);
LOG_DEBUG("%s: Since measure started: %ums", sensorName, sincePmMeasureStarted);
switch (state) {
case SEN5X_MEASUREMENT: {
if (sincePmMeasureStarted < SEN5X_WARMUP_MS_1) {
LOG_INFO("SEN5X: not enough time passed since starting measurement");
LOG_INFO("%s: not enough time passed since starting measurement", sensorName);
return SEN5X_WARMUP_MS_1 - sincePmMeasureStarted;
}
@@ -807,7 +810,7 @@ int32_t SEN5XSensor::pendingForReadyMs()
// If the reading is low (the tyhreshold is in #/cm3) and second warmUp hasn't passed we return to come back later
if ((sen5xmeasurement.pN4p0 / 100) < SEN5X_PN4P0_CONC_THD && sincePmMeasureStarted < SEN5X_WARMUP_MS_2) {
LOG_INFO("SEN5X: Concentration is low, we will ask again in the second warm up period");
LOG_INFO("%s: Concentration is low, we will ask again in the second warm up period", sensorName);
state = SEN5X_MEASUREMENT_2;
// Report how many seconds are pending to cover the first warm up period
return SEN5X_WARMUP_MS_2 - sincePmMeasureStarted;
@@ -829,9 +832,9 @@ int32_t SEN5XSensor::pendingForReadyMs()
bool SEN5XSensor::getMetrics(meshtastic_Telemetry *measurement)
{
LOG_INFO("SEN5X: Attempting to get metrics");
LOG_INFO("%s: Attempting to get metrics", sensorName);
if (!isActive()) {
LOG_INFO("SEN5X: not in measurement mode");
LOG_INFO("%s: not in measurement mode", sensorName);
return false;
}
@@ -921,9 +924,9 @@ void SEN5XSensor::setMode(bool setOneShot)
{
oneShotMode = setOneShot;
if (oneShotMode) {
LOG_INFO("%s setting mode to one shot mode", sensorName);
LOG_INFO("%s: setting mode to one shot mode", sensorName);
} else {
LOG_INFO("%s setting mode to continuous mode", sensorName);
LOG_INFO("%s: setting mode to continuous mode", sensorName);
}
}
@@ -2,6 +2,7 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h"
#include "TelemetrySensor.h"
@@ -62,6 +63,10 @@ class SEN5XSensor : public TelemetrySensor
private:
TwoWire *_bus{};
uint8_t _address{};
#ifdef SEN5X_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool getVersion();
float firmwareVer = -1;
+45 -56
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cSfa3x.h>)
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SFA30Sensor.h"
@@ -16,39 +15,39 @@ bool SFA30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
_address = dev->address.address;
#ifdef SFA30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
_port = dev->address.port;
reClockI2C.setup(_bus, _port);
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */
sfa30.begin(*_bus, _address);
delay(20);
if (this->isError(sfa30.deviceReset())) {
#if defined(SFA30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_INFO("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
return false;
}
state = State::IDLE;
if (this->isError(sfa30.startContinuousMeasurement())) {
#if defined(SFA30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_INFO("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
return false;
}
LOG_INFO("%s starting measurement", sensorName);
#if defined(SFA30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_INFO("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
status = 1;
state = State::ACTIVE;
@@ -72,14 +71,8 @@ bool SFA30Sensor::isError(uint16_t response)
void SFA30Sensor::sleep()
{
#ifdef SFA30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */
// Note - not recommended for this sensor on a periodic basis
@@ -87,11 +80,12 @@ void SFA30Sensor::sleep()
LOG_ERROR("%s: can't stop measurement", sensorName);
};
#if defined(SFA30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_DEBUG("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
LOG_INFO("%s: stop measurement", sensorName);
LOG_DEBUG("%s: stop measurement", sensorName);
state = State::IDLE;
measureStarted = 0;
}
@@ -99,27 +93,23 @@ void SFA30Sensor::sleep()
uint32_t SFA30Sensor::wakeUp()
{
#ifdef SFA30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */
LOG_INFO("Waking up %s", sensorName);
LOG_DEBUG("Waking up %s", sensorName);
if (this->isError(sfa30.startContinuousMeasurement())) {
#if defined(SFA30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_DEBUG("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
return 0;
}
#if defined(SFA30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_DEBUG("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
state = State::ACTIVE;
measureStarted = getTime();
@@ -164,24 +154,23 @@ bool SFA30Sensor::getMetrics(meshtastic_Telemetry *measurement)
float temperature = 0.0;
#ifdef SFA30_I2C_CLOCK_SPEED
#ifdef CAN_RECLOCK_I2C
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, false);
#elif !HAS_SCREEN
reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, true);
#else
LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName);
return false;
#endif /* CAN_RECLOCK_I2C */
LOG_DEBUG("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */
if (this->isError(sfa30.readMeasuredValues(hcho, humidity, temperature))) {
LOG_WARN("%s: No values", sensorName);
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_DEBUG("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
return false;
}
#if defined(SFA30_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
reClockI2C(currentClock, _bus, false);
#endif
#ifdef SFA30_I2C_CLOCK_SPEED
LOG_DEBUG("%s restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* SFA30_I2C_CLOCK_SPEED */
measurement->variant.air_quality_metrics.has_form_temperature = true;
measurement->variant.air_quality_metrics.has_form_humidity = true;
@@ -2,6 +2,7 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cSfa3x.h>)
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h"
#include "TelemetrySensor.h"
@@ -21,6 +22,11 @@ class SFA30Sensor : public TelemetrySensor
SensirionI2cSfa3x sfa30;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SFA30_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool isError(uint16_t response);
public: