Refurbish ReClockI2C API

* Make ReClockI2C API class
* Use new API in AirQualityTelemetry module
* Minor changes on some logs
This commit is contained in:
oscgonfer
2026-06-14 14:48:40 +02:00
parent 0200d45241
commit b7ec5cd35a
15 changed files with 338 additions and 320 deletions
+98
View File
@@ -0,0 +1,98 @@
#pragma once
#ifndef RECLOCK_I2C_
#define RECLOCK_I2C_
#include "../graphics/Screen.h"
#include "ScanI2CTwoWire.h"
#include <Wire.h>
#include <stdint.h>
/* Class to set and restore the I2C clock temporarily on a i2cBus
See https://github.com/arduino/Arduino/issues/11457
Currently, only ESP32 can getClock()
While all cores can setClock()
https://github.com/sandeepmistry/arduino-nRF5/blob/master/libraries/Wire/Wire.h#L50
https://github.com/earlephilhower/arduino-pico/blob/master/libraries/Wire/src/Wire.h#L60
https://github.com/stm32duino/Arduino_Core_STM32/blob/main/libraries/Wire/src/Wire.h#L103
For cases when I2C speed is different to the ones defined by sensors (see defines in sensor classes)
we need to reclock I2C and set it back to the previous stablished speed.
Only for cases where we can know it (ESP32 or known screen) we can do this.
*/
extern graphics::Screen *screen;
class ReClockI2C
{
public:
void setup(TwoWire *i2cBus, ScanI2C::I2CPort port)
{
this->i2cBus = i2cBus;
this->port = port;
this->previousClock = 0;
}
bool setClock(uint32_t desiredClock)
{
uint32_t currentClock = this->getClock();
if (currentClock) {
LOG_DEBUG("Current I2C frequency: %uHz", currentClock);
}
if (currentClock != desiredClock) {
LOG_DEBUG("Changing I2C clock to %uHz", desiredClock);
this->i2cBus->setClock(desiredClock);
// If the clock is 0Hz, we still store it
// We'll check in restoreClock function
setPreviousClock(currentClock);
LOG_DEBUG("Stored previous clock I2C clock: %uHz", this->previousClock);
return true;
}
LOG_DEBUG("I2C clock was already %uHz. Skipping", desiredClock);
return false;
}
bool restoreClock()
{
if (this->previousClock) {
LOG_DEBUG("Restoring I2C clock to %uHz", this->previousClock);
i2cBus->setClock(this->previousClock);
return true;
}
LOG_DEBUG("I2C clock was unknown. Not restored");
return false;
}
private:
TwoWire *i2cBus{};
ScanI2C::I2CPort port{};
uint32_t previousClock = 0;
void setPreviousClock(uint32_t clock) { this->previousClock = clock; }
uint32_t getClock()
{
#ifdef CAN_GET_I2C_CLOCK
return this->i2cBus->getClock();
#elif HAS_SCREEN
if (screen) {
// If we get a non-zero response here, the screen has set a speed
uint32_t screenClock = 0;
ScanI2C::I2CPort screenPort = ScanI2C::I2CPort::NO_I2C;
screenClock = screen->getI2cFrequency();
screenPort = screen->getI2CPort();
// Check if i2c port is the same, and that we got a screenClock back (0 means the screen didn't set it)
if (screenClock && (screenPort == this->port)) {
LOG_DEBUG("Screen defined I2C frequency: %uHz", screenClock);
return screenClock;
}
}
#endif
return 0;
}
};
#endif
-44
View File
@@ -1,44 +0,0 @@
#include "reClockI2C.h"
#include "ScanI2CTwoWire.h"
/* See https://github.com/arduino/Arduino/issues/11457
Currently, only ESP32 can getClock()
While all cores can setClock()
https://github.com/sandeepmistry/arduino-nRF5/blob/master/libraries/Wire/Wire.h#L50
https://github.com/earlephilhower/arduino-pico/blob/master/libraries/Wire/src/Wire.h#L60
https://github.com/stm32duino/Arduino_Core_STM32/blob/main/libraries/Wire/src/Wire.h#L103
For cases when I2C speed is different to the ones defined by sensors (see defines in sensor classes)
we need to reclock I2C and set it back to the previous stablished speed.
Only for cases where we can know it (ESP32 or known screen) we can do this.
*/
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus, ScanI2C::I2CPort port)
{
uint32_t currentClock = 0;
uint32_t screenClock = 0;
ScanI2C::I2CPort screenPort = ScanI2C::I2CPort::NO_I2C;
// Assume that if we can't getClock, or there is no screen, we can set the clock speed at will
#ifdef CAN_RECLOCK_I2C
currentClock = i2cBus->getClock();
LOG_DEBUG("Current I2C frequency: %uHz", currentClock);
#elif HAS_SCREEN
if (screen) {
// If we get a non-zero response here, the screen has set a speed
screenClock = screen->getI2cFrequency();
screenPort = screen->getI2CPort();
// Check if i2c port is the same, and that we got a screenClock back (0 means the screen didn't set it)
if (screenClock && (screenPort == port))
currentClock = screenClock;
LOG_DEBUG("Screen defined I2C frequency: %uHz", screenClock);
}
#endif
if (currentClock != desiredClock) {
LOG_DEBUG("Changing I2C clock to %uHz", desiredClock);
i2cBus->setClock(desiredClock);
}
return currentClock;
}
-14
View File
@@ -1,14 +0,0 @@
#ifndef RECLOCK_I2C_
#define RECLOCK_I2C_
#include "../graphics/Screen.h"
#include "ScanI2CTwoWire.h"
#include <Wire.h>
#include <stdint.h>
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus, ScanI2C::I2CPort port);
extern graphics::Screen *screen;
#endif
@@ -110,10 +110,10 @@ int32_t AirQualityTelemetryModule::runOnce()
} }
// Wake up the sensors that need it // Wake up the sensors that need it
LOG_INFO("Waking up sensors...");
uint32_t lastTelemetry = uint32_t lastTelemetry =
transmitHistory ? transmitHistory->getLastSentToMeshMillis(TX_HISTORY_KEY_AIR_QUALITY_TELEMETRY) : 0; transmitHistory ? transmitHistory->getLastSentToMeshMillis(TX_HISTORY_KEY_AIR_QUALITY_TELEMETRY) : 0;
for (TelemetrySensor *sensor : sensors) { for (TelemetrySensor *sensor : sensors) {
LOG_DEBUG("Checking if %s needs to wake up", sensor->sensorName);
if (!sensor->canSleep()) { if (!sensor->canSleep()) {
LOG_DEBUG("%s sensor doesn't have sleep feature. Skipping", sensor->sensorName); LOG_DEBUG("%s sensor doesn't have sleep feature. Skipping", sensor->sensorName);
} else if (((lastTelemetry == 0) || !Throttle::isWithinTimespanMs(lastTelemetry - sensor->wakeUpTimeMs(), } else if (((lastTelemetry == 0) || !Throttle::isWithinTimespanMs(lastTelemetry - sensor->wakeUpTimeMs(),
@@ -154,8 +154,8 @@ int32_t AirQualityTelemetryModule::runOnce()
} }
// Send to sleep sensors that consume power // Send to sleep sensors that consume power
LOG_DEBUG("Sending sensors to sleep");
for (TelemetrySensor *sensor : sensors) { for (TelemetrySensor *sensor : sensors) {
LOG_DEBUG("Checking if %s can be sent to sleep", sensor->sensorName);
if (sensor->isActive() && sensor->canSleep()) { if (sensor->isActive() && sensor->canSleep()) {
if (sensor->wakeUpTimeMs() < if (sensor->wakeUpTimeMs() <
(int32_t)Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval, (int32_t)Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval,
+26 -20
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "PMSA003ISensor.h" #include "PMSA003ISensor.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
@@ -13,31 +12,36 @@ PMSA003ISensor::PMSA003ISensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp
bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("%s: Init sensor", sensorName);
#ifdef PMSA003I_ENABLE_PIN #ifdef PMSA003I_ENABLE_PIN
pinMode(PMSA003I_ENABLE_PIN, OUTPUT); pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
#endif #endif
// TODO PMS5003I sometimes get late to the party...
_bus = bus; _bus = bus;
_address = dev->address.address; _address = dev->address.address;
_port = dev->address.port;
#ifdef PMSA003I_I2C_CLOCK_SPEED #ifdef PMSA003I_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, PMSA003I_I2C_CLOCK_SPEED); _port = dev->address.port;
uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus, _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 */ #endif /* PMSA003I_I2C_CLOCK_SPEED */
_bus->beginTransmission(_address); _bus->beginTransmission(_address);
if (_bus->endTransmission() != 0) { if (_bus->endTransmission() != 0) {
LOG_WARN("%s not found on I2C at 0x12", sensorName); 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; return false;
} }
#ifdef PMSA003I_I2C_CLOCK_SPEED #ifdef PMSA003I_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* PMSA003I_I2C_CLOCK_SPEED */ #endif /* PMSA003I_I2C_CLOCK_SPEED */
status = 1; status = 1;
@@ -55,13 +59,17 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
} }
#ifdef PMSA003I_I2C_CLOCK_SPEED #ifdef PMSA003I_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, PMSA003I_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, PMSA003I_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(PMSA003I_I2C_CLOCK_SPEED);
#endif /* PMSA003I_I2C_CLOCK_SPEED */ #endif /* PMSA003I_I2C_CLOCK_SPEED */
_bus->requestFrom(_address, (uint8_t)PMSA003I_FRAME_LENGTH); _bus->requestFrom(_address, (uint8_t)PMSA003I_FRAME_LENGTH);
if (_bus->available() < 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_INFO("%s: restoring clock speed", sensorName);
reClockI2C.restoreClock();
#endif /* PMSA003I_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -70,14 +78,12 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
} }
#ifdef PMSA003I_I2C_CLOCK_SPEED #ifdef PMSA003I_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* PMSA003I_I2C_CLOCK_SPEED */ #endif /* PMSA003I_I2C_CLOCK_SPEED */
if (buffer[0] != 0x42 || buffer[1] != 0x4D) { 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; return false;
} }
@@ -133,7 +139,7 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.air_quality_metrics.has_particles_100um = true; measurement->variant.air_quality_metrics.has_particles_100um = true;
measurement->variant.air_quality_metrics.particles_100um = read16(buffer, 26); 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.pm10_standard, measurement->variant.air_quality_metrics.pm25_standard,
measurement->variant.air_quality_metrics.pm100_standard); measurement->variant.air_quality_metrics.pm100_standard);
@@ -192,7 +198,7 @@ void PMSA003ISensor::sleep()
uint32_t PMSA003ISensor::wakeUp() uint32_t PMSA003ISensor::wakeUp()
{ {
#ifdef PMSA003I_ENABLE_PIN #ifdef PMSA003I_ENABLE_PIN
LOG_INFO("Waking up %s", sensorName); LOG_INFO("%s: Waking up", sensorName);
digitalWrite(PMSA003I_ENABLE_PIN, HIGH); digitalWrite(PMSA003I_ENABLE_PIN, HIGH);
state = State::ACTIVE; state = State::ACTIVE;
pmMeasureStarted = getTime(); pmMeasureStarted = getTime();
@@ -2,6 +2,7 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h" #include "RTC.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
@@ -35,7 +36,10 @@ class PMSA003ISensor : public TelemetrySensor
uint8_t buffer[PMSA003I_FRAME_LENGTH]{}; uint8_t buffer[PMSA003I_FRAME_LENGTH]{};
TwoWire *_bus{}; TwoWire *_bus{};
uint8_t _address{}; uint8_t _address{};
#ifdef PMSA003I_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C; ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
}; };
#endif #endif
+32 -46
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd30.h>) #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd30.h>)
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SCD30Sensor.h" #include "SCD30Sensor.h"
@@ -12,15 +11,16 @@ SCD30Sensor::SCD30Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD3
bool SCD30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) bool SCD30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{ {
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("%s: Init sensor", sensorName);
_bus = bus; _bus = bus;
_address = dev->address.address; _address = dev->address.address;
_port = dev->address.port;
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED); _port = dev->address.port;
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, _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 */ #endif /* SCD30_I2C_CLOCK_SPEED */
scd30.begin(*_bus, _address); scd30.begin(*_bus, _address);
@@ -28,10 +28,8 @@ bool SCD30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (!startMeasurement()) { if (!startMeasurement()) {
LOG_ERROR("%s: Failed to start periodic measurement", sensorName); LOG_ERROR("%s: Failed to start periodic measurement", sensorName);
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -41,10 +39,8 @@ bool SCD30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
} }
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
if (state == SCD30_MEASUREMENT) { if (state == SCD30_MEASUREMENT) {
@@ -63,31 +59,27 @@ bool SCD30Sensor::getMetrics(meshtastic_Telemetry *measurement)
float co2, temperature, humidity; float co2, temperature, humidity;
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
if (scd30.readMeasurementData(co2, temperature, humidity) != SCD30_NO_ERROR) { if (scd30.readMeasurementData(co2, temperature, humidity) != SCD30_NO_ERROR) {
LOG_ERROR("SCD30: Failed to read measurement data."); LOG_ERROR("%s: Failed to read measurement data", sensorName);
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
return false; return false;
} }
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
if (co2 == 0) { if (co2 == 0) {
LOG_ERROR("SCD30: Invalid CO₂ reading."); LOG_ERROR("%s: Invalid CO₂ reading", sensorName);
return false; return false;
} }
@@ -98,7 +90,7 @@ bool SCD30Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.air_quality_metrics.co2_temperature = temperature; measurement->variant.air_quality_metrics.co2_temperature = temperature;
measurement->variant.air_quality_metrics.co2_humidity = humidity; 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; return true;
} }
@@ -285,7 +277,7 @@ bool SCD30Sensor::setTemperature(float tempReference)
tempOffset = (temperature - tempReference); tempOffset = (temperature - tempReference);
if (tempOffset < 0) { if (tempOffset < 0) {
LOG_ERROR("%s temperature offset is only positive", sensorName); LOG_ERROR("%s: temperature offset is only positive", sensorName);
return false; return false;
} }
@@ -375,17 +367,15 @@ bool SCD30Sensor::isActive()
uint32_t SCD30Sensor::wakeUp() uint32_t SCD30Sensor::wakeUp()
{ {
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
startMeasurement(); startMeasurement();
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
return 0; return 0;
@@ -398,17 +388,15 @@ uint32_t SCD30Sensor::wakeUp()
void SCD30Sensor::sleep() void SCD30Sensor::sleep()
{ {
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
stopMeasurement(); stopMeasurement();
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
} }
@@ -433,8 +421,8 @@ AdminMessageHandleResult SCD30Sensor::handleAdminMessage(const meshtastic_MeshPa
AdminMessageHandleResult result; AdminMessageHandleResult result;
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD30_I2C_CLOCK_SPEED);
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
switch (request->which_payload_variant) { switch (request->which_payload_variant) {
@@ -491,10 +479,8 @@ AdminMessageHandleResult SCD30Sensor::handleAdminMessage(const meshtastic_MeshPa
} }
#ifdef SCD30_I2C_CLOCK_SPEED #ifdef SCD30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD30_I2C_CLOCK_SPEED */ #endif /* SCD30_I2C_CLOCK_SPEED */
return result; return result;
@@ -2,6 +2,7 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd30.h>) #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd30.h>)
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <SensirionI2cScd30.h> #include <SensirionI2cScd30.h>
@@ -14,7 +15,10 @@ class SCD30Sensor : public TelemetrySensor
SensirionI2cScd30 scd30; SensirionI2cScd30 scd30;
TwoWire *_bus{}; TwoWire *_bus{};
uint8_t _address{}; uint8_t _address{};
#ifdef SCD30_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C; ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool performFRC(uint16_t targetCO2); bool performFRC(uint16_t targetCO2);
bool setASC(bool ascEnabled); bool setASC(bool ascEnabled);
+43 -69
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd4x.h>) #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd4x.h>)
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SCD4XSensor.h" #include "SCD4XSensor.h"
@@ -18,8 +17,11 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
_address = dev->address.address; _address = dev->address.address;
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED); _port = dev->address.port;
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, _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 */ #endif /* SCD4X_I2C_CLOCK_SPEED */
scd4x.begin(*_bus, _address); scd4x.begin(*_bus, _address);
@@ -30,10 +32,8 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
// Stop periodic measurement // Stop periodic measurement
if (!stopMeasurement()) { if (!stopMeasurement()) {
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -46,10 +46,8 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (!powerUp()) { if (!powerUp()) {
LOG_ERROR("%s: Error trying to execute powerUp()", sensorName); LOG_ERROR("%s: Error trying to execute powerUp()", sensorName);
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -58,10 +56,8 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (!getASC(ascActive)) { if (!getASC(ascActive)) {
LOG_ERROR("%s: Unable to check if ASC is enabled", sensorName); LOG_ERROR("%s: Unable to check if ASC is enabled", sensorName);
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -70,19 +66,15 @@ bool SCD4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (!startMeasurement()) { if (!startMeasurement()) {
LOG_ERROR("%s: Couldn't start measurement", sensorName); LOG_ERROR("%s: Couldn't start measurement", sensorName);
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return false; return false;
} }
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
if (state == SCD4X_MEASUREMENT) { if (state == SCD4X_MEASUREMENT) {
@@ -108,18 +100,16 @@ bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
float temperature, humidity; float temperature, humidity;
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
bool dataReady; bool dataReady;
error = scd4x.getDataReadyStatus(dataReady); error = scd4x.getDataReadyStatus(dataReady);
if (error != SCD4X_NO_ERROR || !dataReady) { if (error != SCD4X_NO_ERROR || !dataReady) {
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
LOG_ERROR("SCD4X: Data is not ready"); LOG_ERROR("SCD4X: Data is not ready");
return false; return false;
@@ -128,10 +118,8 @@ bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
error = scd4x.readMeasurement(co2, temperature, humidity); error = scd4x.readMeasurement(co2, temperature, humidity);
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
LOG_DEBUG("Got %s readings: co2=%u, co2_temp=%.2f, co2_hum%.2f", sensorName, co2, temperature, humidity); LOG_DEBUG("Got %s readings: co2=%u, co2_temp=%.2f, co2_hum%.2f", sensorName, co2, temperature, humidity);
@@ -646,16 +634,14 @@ bool SCD4XSensor::powerDown()
} }
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
if (!stopMeasurement()) { if (!stopMeasurement()) {
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -663,19 +649,15 @@ bool SCD4XSensor::powerDown()
if (scd4x.powerDown() != SCD4X_NO_ERROR) { if (scd4x.powerDown() != SCD4X_NO_ERROR) {
LOG_ERROR("%s: Error trying to execute sleep()", sensorName); LOG_ERROR("%s: Error trying to execute sleep()", sensorName);
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return false; return false;
} }
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
state = SCD4X_OFF; state = SCD4X_OFF;
@@ -723,26 +705,22 @@ uint32_t SCD4XSensor::wakeUp()
{ {
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
if (startMeasurement()) { if (startMeasurement()) {
co2MeasureStarted = getTime(); co2MeasureStarted = getTime();
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return SCD4X_WARMUP_MS; return SCD4X_WARMUP_MS;
} }
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return 0; return 0;
@@ -755,17 +733,15 @@ uint32_t SCD4XSensor::wakeUp()
void SCD4XSensor::sleep() void SCD4XSensor::sleep()
{ {
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
stopMeasurement(); stopMeasurement();
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
} }
@@ -806,8 +782,8 @@ AdminMessageHandleResult SCD4XSensor::handleAdminMessage(const meshtastic_MeshPa
AdminMessageHandleResult result; AdminMessageHandleResult result;
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED); LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, SCD4X_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SCD4X_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SCD4X_I2C_CLOCK_SPEED);
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
// TODO: potentially add selftest command? // TODO: potentially add selftest command?
@@ -911,10 +887,8 @@ AdminMessageHandleResult SCD4XSensor::handleAdminMessage(const meshtastic_MeshPa
this->startMeasurement(); this->startMeasurement();
#ifdef SCD4X_I2C_CLOCK_SPEED #ifdef SCD4X_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SCD4X_I2C_CLOCK_SPEED */ #endif /* SCD4X_I2C_CLOCK_SPEED */
return result; return result;
+5 -1
View File
@@ -2,13 +2,14 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd4x.h>) #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cScd4x.h>)
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h" #include "RTC.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <SensirionI2cScd4x.h> #include <SensirionI2cScd4x.h>
// Max speed 400kHz // Max speed 400kHz
#define SCD4X_I2C_CLOCK_SPEED 100000 #define SCD4X_I2C_CLOCK_SPEED 400000
#define SCD4X_WARMUP_MS 5000 #define SCD4X_WARMUP_MS 5000
class SCD4XSensor : public TelemetrySensor class SCD4XSensor : public TelemetrySensor
@@ -17,7 +18,10 @@ class SCD4XSensor : public TelemetrySensor
SensirionI2cScd4x scd4x; SensirionI2cScd4x scd4x;
TwoWire *_bus{}; TwoWire *_bus{};
uint8_t _address{}; uint8_t _address{};
#ifdef SCD4X_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C; ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool performFRC(uint32_t targetCO2); bool performFRC(uint32_t targetCO2);
bool setASCBaseline(uint32_t targetCO2); bool setASCBaseline(uint32_t targetCO2);
+92 -85
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "FSCommon.h" #include "FSCommon.h"
#include "SEN5XSensor.h" #include "SEN5XSensor.h"
@@ -18,7 +17,7 @@ SEN5XSensor::SEN5XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SEN5
bool SEN5XSensor::getVersion() bool SEN5XSensor::getVersion()
{ {
if (!sendCommand(SEN5X_GET_FIRMWARE_VERSION)) { if (!sendCommand(SEN5X_GET_FIRMWARE_VERSION)) {
LOG_ERROR("SEN5X: Error sending version command"); LOG_ERROR("%s: Error sending version command", sensorName);
return false; return false;
} }
delay(20); // From Sensirion Datasheet delay(20); // From Sensirion Datasheet
@@ -26,7 +25,7 @@ bool SEN5XSensor::getVersion()
uint8_t versionBuffer[12]{}; uint8_t versionBuffer[12]{};
size_t charNumber = readBuffer(&versionBuffer[0], 3); size_t charNumber = readBuffer(&versionBuffer[0], 3);
if (charNumber == 0) { if (charNumber == 0) {
LOG_ERROR("SEN5X: Error getting data ready flag value"); LOG_ERROR("%s: Error getting data ready flag value", sensorName);
return false; return false;
} }
@@ -34,9 +33,9 @@ bool SEN5XSensor::getVersion()
hardwareVer = versionBuffer[3] + (versionBuffer[4] / 10); hardwareVer = versionBuffer[3] + (versionBuffer[4] / 10);
protocolVer = versionBuffer[5] + (versionBuffer[6] / 10); protocolVer = versionBuffer[5] + (versionBuffer[6] / 10);
LOG_INFO("SEN5X Firmware Version: %0.2f", firmwareVer); LOG_INFO("%s: Firmware Version: %0.2f", sensorName, firmwareVer);
LOG_INFO("SEN5X Hardware Version: %0.2f", hardwareVer); LOG_INFO("%s: Hardware Version: %0.2f", sensorName, hardwareVer);
LOG_INFO("SEN5X Protocol Version: %0.2f", protocolVer); LOG_INFO("%s: Protocol Version: %0.2f", sensorName, protocolVer);
return true; return true;
} }
@@ -44,7 +43,7 @@ bool SEN5XSensor::getVersion()
bool SEN5XSensor::findModel() bool SEN5XSensor::findModel()
{ {
if (!sendCommand(SEN5X_GET_PRODUCT_NAME)) { 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; return false;
} }
delay(50); // From Sensirion Datasheet delay(50); // From Sensirion Datasheet
@@ -53,7 +52,7 @@ bool SEN5XSensor::findModel()
uint8_t name[nameSize]; uint8_t name[nameSize];
size_t charNumber = readBuffer(&name[0], nameSize); size_t charNumber = readBuffer(&name[0], nameSize);
if (charNumber == 0) { if (charNumber == 0) {
LOG_ERROR("SEN5X: Error getting device name"); LOG_ERROR("%s: Error getting device name", sensorName);
return false; return false;
} }
@@ -61,15 +60,15 @@ bool SEN5XSensor::findModel()
switch (name[4]) { switch (name[4]) {
case 48: case 48:
model = SEN50; model = SEN50;
LOG_INFO("SEN5X: found sensor model SEN50"); LOG_INFO("%s: found sensor model SEN50", sensorName);
break; break;
case 52: case 52:
model = SEN54; model = SEN54;
LOG_INFO("SEN5X: found sensor model SEN54"); LOG_INFO("%s: found sensor model SEN54", sensorName);
break; break;
case 53: case 53:
model = SEN55; model = SEN55;
LOG_INFO("SEN5X: found sensor model SEN55"); LOG_INFO("%s: found sensor model SEN55", sensorName);
break; break;
} }
@@ -134,8 +133,8 @@ bool SEN5XSensor::sendCommand(uint16_t command, uint8_t *buffer, uint8_t byteNum
} }
#ifdef SEN5X_I2C_CLOCK_SPEED #ifdef SEN5X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SEN5X_I2C_CLOCK_SPEED); LOG_DEBUG("%s: Attempting to reclock speed to %uHz", sensorName, SEN5X_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SEN5X_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SEN5X_I2C_CLOCK_SPEED);
#endif /* SEN5X_I2C_CLOCK_SPEED */ #endif /* SEN5X_I2C_CLOCK_SPEED */
// Transmit the data // Transmit the data
@@ -147,19 +146,17 @@ bool SEN5XSensor::sendCommand(uint16_t command, uint8_t *buffer, uint8_t byteNum
uint8_t i2c_error = _bus->endTransmission(); uint8_t i2c_error = _bus->endTransmission();
#ifdef SEN5X_I2C_CLOCK_SPEED #ifdef SEN5X_I2C_CLOCK_SPEED
if (currentClock) { LOG_DEBUG("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SEN5X_I2C_CLOCK_SPEED */ #endif /* SEN5X_I2C_CLOCK_SPEED */
if (writtenBytes != bufferSize) { if (writtenBytes != bufferSize) {
LOG_ERROR("SEN5X: Error writting on I2C bus"); LOG_ERROR("%s: Error writting on I2C bus", sensorName);
return false; return false;
} }
if (i2c_error != 0) { 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 false;
} }
return true; return true;
@@ -168,13 +165,17 @@ bool SEN5XSensor::sendCommand(uint16_t command, uint8_t *buffer, uint8_t byteNum
uint8_t SEN5XSensor::readBuffer(uint8_t *buffer, uint8_t byteNumber) uint8_t SEN5XSensor::readBuffer(uint8_t *buffer, uint8_t byteNumber)
{ {
#ifdef SEN5X_I2C_CLOCK_SPEED #ifdef SEN5X_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SEN5X_I2C_CLOCK_SPEED); LOG_DEBUG("%s: Attempting to reclock speed to %uHz", sensorName, SEN5X_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SEN5X_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SEN5X_I2C_CLOCK_SPEED);
#endif /* SEN5X_I2C_CLOCK_SPEED */ #endif /* SEN5X_I2C_CLOCK_SPEED */
size_t readBytes = _bus->requestFrom(_address, byteNumber); size_t readBytes = _bus->requestFrom(_address, byteNumber);
if (readBytes != 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; return 0;
} }
@@ -186,7 +187,11 @@ uint8_t SEN5XSensor::readBuffer(uint8_t *buffer, uint8_t byteNumber)
uint8_t recvCRC = _bus->read(); uint8_t recvCRC = _bus->read();
uint8_t calcCRC = sen5xCRC(&buffer[i - 2]); uint8_t calcCRC = sen5xCRC(&buffer[i - 2]);
if (recvCRC != calcCRC) { 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; return 0;
} }
readBytes -= 3; readBytes -= 3;
@@ -194,10 +199,8 @@ uint8_t SEN5XSensor::readBuffer(uint8_t *buffer, uint8_t byteNumber)
} }
#ifdef SEN5X_I2C_CLOCK_SPEED #ifdef SEN5X_I2C_CLOCK_SPEED
if (currentClock) { LOG_DEBUG("%s: restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SEN5X_I2C_CLOCK_SPEED */ #endif /* SEN5X_I2C_CLOCK_SPEED */
return receivedBytes; return receivedBytes;
@@ -326,24 +329,24 @@ bool SEN5XSensor::vocStateToSensor()
} }
if (!vocStateValid()) { if (!vocStateValid()) {
LOG_INFO("SEN5X: VOC state is invalid, not sending"); LOG_INFO("%s: VOC state is invalid, not sending", sensorName);
return true; return true;
} }
if (!sendCommand(SEN5X_STOP_MEASUREMENT)) { if (!sendCommand(SEN5X_STOP_MEASUREMENT)) {
LOG_ERROR("SEN5X: Error stoping measurement"); LOG_ERROR("%s: Error stoping measurement", sensorName);
return false; return false;
} }
delay(200); // From Sensirion Datasheet 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], 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]); vocState[6], vocState[7]);
// Note: send command already takes into account the CRC // Note: send command already takes into account the CRC
// buffer size increment needed // buffer size increment needed
if (!sendCommand(SEN5X_RW_VOCS_STATE, vocState, SEN5X_VOC_STATE_BUFFER_SIZE)) { 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; return false;
} }
@@ -356,10 +359,10 @@ bool SEN5XSensor::vocStateFromSensor()
return true; 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 // Ask VOCs state from the sensor
if (!sendCommand(SEN5X_RW_VOCS_STATE)) { 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; return false;
} }
@@ -371,13 +374,13 @@ bool SEN5XSensor::vocStateFromSensor()
delay(20); // From Sensirion Datasheet delay(20); // From Sensirion Datasheet
if (receivedNumber == 0) { if (receivedNumber == 0) {
LOG_DEBUG("SEN5X: Error getting VOC's state"); LOG_DEBUG("%s: Error getting VOC's state", sensorName);
return false; return false;
} }
// Print the state (if debug is on) // 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], LOG_DEBUG("%s: VOC state retrieved from sensor: [%u, %u, %u, %u, %u, %u, %u, %u]", sensorName, vocState[0], vocState[1],
vocState[3], vocState[4], vocState[5], vocState[6], vocState[7]); vocState[2], vocState[3], vocState[4], vocState[5], vocState[6], vocState[7]);
return true; return true;
} }
@@ -389,11 +392,11 @@ bool SEN5XSensor::loadState()
auto file = FSCom.open(sen5XStateFileName, FILE_O_READ); auto file = FSCom.open(sen5XStateFileName, FILE_O_READ);
bool okay = false; bool okay = false;
if (file) { 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}; pb_istream_t stream = {&readcb, &file, meshtastic_SEN5XState_size};
if (!pb_decode(&stream, &meshtastic_SEN5XState_msg, &sen5xstate)) { 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 { } else {
lastCleaning = sen5xstate.last_cleaning_time; lastCleaning = sen5xstate.last_cleaning_time;
lastCleaningValid = sen5xstate.last_cleaning_valid; lastCleaningValid = sen5xstate.last_cleaning_valid;
@@ -425,12 +428,12 @@ bool SEN5XSensor::loadState()
} }
file.close(); file.close();
} else { } else {
LOG_INFO("No %s state found (File: %s)", sensorName, sen5XStateFileName); LOG_INFO("%s: No state found (File: %s)", sensorName, sen5XStateFileName);
} }
spiLock->unlock(); spiLock->unlock();
return okay; return okay;
#else #else
LOG_ERROR("SEN5X: ERROR - Filesystem not implemented"); LOG_ERROR("%s: Filesystem not implemented", sensorName);
#endif #endif
} }
@@ -463,7 +466,7 @@ bool SEN5XSensor::saveState()
pb_ostream_t stream = {&writecb, static_cast<Print *>(&file), meshtastic_SEN5XState_size}; pb_ostream_t stream = {&writecb, static_cast<Print *>(&file), meshtastic_SEN5XState_size};
if (!pb_encode(&stream, &meshtastic_SEN5XState_msg, &sen5xstate)) { 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 { } else {
okay = true; okay = true;
} }
@@ -475,7 +478,7 @@ bool SEN5XSensor::saveState()
return okay; return okay;
#else #else
LOG_ERROR("%s: ERROR - Filesystem not implemented", sensorName); LOG_ERROR("%s: Filesystem not implemented", sensorName);
#endif #endif
} }
@@ -487,10 +490,10 @@ bool SEN5XSensor::isActive()
uint32_t SEN5XSensor::wakeUp() uint32_t SEN5XSensor::wakeUp()
{ {
LOG_DEBUG("SEN5X: Waking up sensor"); LOG_DEBUG("%s: Waking up sensor", sensorName);
if (!sendCommand(SEN5X_START_MEASUREMENT)) { 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? // TODO - what should this return?? Something actually on the default interval?
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
@@ -502,7 +505,7 @@ uint32_t SEN5XSensor::wakeUp()
pmMeasureStarted = getTime(); pmMeasureStarted = getTime();
state = SEN5X_MEASUREMENT; state = SEN5X_MEASUREMENT;
if (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; return SEN5X_WARMUP_MS_1;
} }
@@ -511,7 +514,7 @@ bool SEN5XSensor::vocStateStable()
uint32_t now; uint32_t now;
now = getTime(); now = getTime();
uint32_t sinceFirstMeasureStarted = (now - rhtGasMeasureStarted); uint32_t sinceFirstMeasureStarted = (now - rhtGasMeasureStarted);
LOG_DEBUG("sinceFirstMeasureStarted: %us", sinceFirstMeasureStarted); LOG_DEBUG("%s: sinceFirstMeasureStarted: %us", sensorName, sinceFirstMeasureStarted);
return sinceFirstMeasureStarted > SEN5X_VOC_STATE_WARMUP_S; return sinceFirstMeasureStarted > SEN5X_VOC_STATE_WARMUP_S;
} }
@@ -523,25 +526,25 @@ bool SEN5XSensor::startCleaning()
// Note that cleaning command can only be run when the sensor is in measurement mode // Note that cleaning command can only be run when the sensor is in measurement mode
if (!sendCommand(SEN5X_START_MEASUREMENT)) { if (!sendCommand(SEN5X_START_MEASUREMENT)) {
LOG_ERROR("SEN5X: Error starting measurment mode"); LOG_ERROR("%s: Error starting measurment mode", sensorName);
return false; return false;
} }
delay(50); // From Sensirion Datasheet delay(50); // From Sensirion Datasheet
if (!sendCommand(SEN5X_START_FAN_CLEANING)) { if (!sendCommand(SEN5X_START_FAN_CLEANING)) {
LOG_ERROR("SEN5X: Error starting fan cleaning"); LOG_ERROR("%s: Error starting fan cleaning", sensorName);
return false; return false;
} }
delay(20); // From Sensirion Datasheet delay(20); // From Sensirion Datasheet
// This message will be always printed so the user knows the device it's not hung // 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(); uint16_t started = millis();
while (millis() - started < 10500) { while (millis() - started < 10500) {
delay(500); 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 // Save timestamp in flash so we know when a week has passed
uint32_t now; uint32_t now;
@@ -558,22 +561,25 @@ bool SEN5XSensor::startCleaning()
bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{ {
state = SEN5X_NOT_DETECTED; state = SEN5X_NOT_DETECTED;
LOG_INFO("Init sensor: %s", sensorName); LOG_INFO("%s: Init sensor", sensorName);
_bus = bus; _bus = bus;
_address = dev->address.address; _address = dev->address.address;
#ifdef SEN5X_I2C_CLOCK_SPEED
_port = dev->address.port; _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 delay(50); // without this there is an error on the deviceReset function
if (!sendCommand(SEN5X_RESET)) { if (!sendCommand(SEN5X_RESET)) {
LOG_ERROR("SEN5X: Error reseting device"); LOG_ERROR("%s: error reseting device", sensorName);
return false; return false;
} }
delay(200); // From Sensirion Datasheet delay(200); // From Sensirion Datasheet
if (!findModel()) { if (!findModel()) {
LOG_ERROR("SEN5X: error finding sensor model"); LOG_ERROR("%s: error finding sensor model", sensorName);
return false; return false;
} }
@@ -581,7 +587,7 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (!getVersion()) if (!getVersion())
return false; return false;
if (firmwareVer < 2) { 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; return false;
} }
delay(200); // From Sensirion Datasheet delay(200); // From Sensirion Datasheet
@@ -606,11 +612,12 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (passed > ONE_WEEK_IN_SECONDS && (now > SEN5X_VOC_VALID_DATE)) { if (passed > ONE_WEEK_IN_SECONDS && (now > SEN5X_VOC_VALID_DATE)) {
// If current date greater than 01/01/2018 (validity check) // 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, LOG_INFO("%s: More than a week (%us) since last cleaning in epoch (%us). Trigger, cleaning...", sensorName,
lastCleaning); passed, lastCleaning);
startCleaning(); startCleaning();
} else { } 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 { } else {
// We assume the device has just been updated or it is new, // We assume the device has just been updated or it is new,
@@ -619,29 +626,29 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
// Otherwise, we will never trigger cleaning in some cases // Otherwise, we will never trigger cleaning in some cases
lastCleaning = now; lastCleaning = now;
lastCleaningValid = true; 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(); saveState();
} }
if (model != SEN50) { if (model != SEN50) {
if (!vocValid) { if (!vocValid) {
LOG_INFO("SEN5X: No valid VOC's state found"); LOG_INFO("%s: No valid VOC's state found", sensorName);
} else { } else {
// Check if state is recent // Check if state is recent
if (vocStateRecent(now)) { if (vocStateRecent(now)) {
// If current date greater than 01/01/2018 (validity check) // If current date greater than 01/01/2018 (validity check)
// Send it to the sensor // 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(); vocStateToSensor();
} else { } else {
LOG_INFO("SEN5X: VOC state is too old or date is invalid"); LOG_INFO("%s: VOC state is too old or date is invalid", sensorName);
LOG_DEBUG("SEN5X: vocTime %u, Passed %u, and now %u", vocTime, passed, now); LOG_DEBUG("%s: vocTime %u, Passed %u, and now %u", sensorName, vocTime, passed, now);
} }
} }
} }
} else { } else {
// TODO - Should this actually ignore? We could end up never cleaning... // 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); idle(false);
@@ -654,16 +661,16 @@ bool SEN5XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
bool SEN5XSensor::readValues() bool SEN5XSensor::readValues()
{ {
if (!sendCommand(SEN5X_READ_VALUES)) { if (!sendCommand(SEN5X_READ_VALUES)) {
LOG_ERROR("SEN5X: Error sending read command"); LOG_ERROR("%s: Error sending read command", sensorName);
return false; return false;
} }
LOG_DEBUG("SEN5X: Reading PM Values"); LOG_DEBUG("%s: Reading PM Values", sensorName);
delay(20); // From Sensirion Datasheet delay(20); // From Sensirion Datasheet
uint8_t dataBuffer[16]{}; uint8_t dataBuffer[16]{};
size_t receivedNumber = readBuffer(&dataBuffer[0], 24); size_t receivedNumber = readBuffer(&dataBuffer[0], 24);
if (receivedNumber == 0) { if (receivedNumber == 0) {
LOG_ERROR("SEN5X: Error getting values"); LOG_ERROR("%s: Error getting values", sensorName);
return false; return false;
} }
@@ -688,16 +695,16 @@ bool SEN5XSensor::readValues()
sen5xmeasurement.vocIndex = !isnan(int_vocIndex) ? int_vocIndex / 10.0f : FLT_MAX; sen5xmeasurement.vocIndex = !isnan(int_vocIndex) ? int_vocIndex / 10.0f : FLT_MAX;
sen5xmeasurement.noxIndex = !isnan(int_noxIndex) ? int_noxIndex / 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); sen5xmeasurement.pM2p5, sen5xmeasurement.pM4p0, sen5xmeasurement.pM10p0);
if (model != SEN50) { 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); sen5xmeasurement.temperature, sen5xmeasurement.vocIndex);
} }
if (model == SEN55) { 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; return true;
@@ -706,17 +713,17 @@ bool SEN5XSensor::readValues()
bool SEN5XSensor::readPNValues(bool cumulative) bool SEN5XSensor::readPNValues(bool cumulative)
{ {
if (!sendCommand(SEN5X_READ_PM_VALUES)) { if (!sendCommand(SEN5X_READ_PM_VALUES)) {
LOG_ERROR("SEN5X: Error sending read command"); LOG_ERROR("%s: Error sending read command", sensorName);
return false; return false;
} }
LOG_DEBUG("SEN5X: Reading PN Values"); LOG_DEBUG("%s: Reading PN Values", sensorName);
delay(20); // From Sensirion Datasheet delay(20); // From Sensirion Datasheet
uint8_t dataBuffer[20]{}; uint8_t dataBuffer[20]{};
size_t receivedNumber = readBuffer(&dataBuffer[0], 30); size_t receivedNumber = readBuffer(&dataBuffer[0], 30);
if (receivedNumber == 0) { if (receivedNumber == 0) {
LOG_ERROR("SEN5X: Error getting PN values"); LOG_ERROR("%s: Error getting PN values", sensorName);
return false; return false;
} }
@@ -750,7 +757,7 @@ bool SEN5XSensor::readPNValues(bool cumulative)
sen5xmeasurement.pN1p0 -= sen5xmeasurement.pN0p5; 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.pN0p5, sen5xmeasurement.pN1p0, sen5xmeasurement.pN2p5, sen5xmeasurement.pN4p0,
sen5xmeasurement.pN10p0, sen5xmeasurement.tSize); sen5xmeasurement.pN10p0, sen5xmeasurement.tSize);
@@ -764,7 +771,7 @@ uint8_t SEN5XSensor::getMeasurements()
// Try to get new data // Try to get new data
if (!sendCommand(SEN5X_READ_DATA_READY)) { 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; return 2;
} }
delay(20); // From Sensirion Datasheet delay(20); // From Sensirion Datasheet
@@ -772,7 +779,7 @@ uint8_t SEN5XSensor::getMeasurements()
uint8_t dataReadyBuffer[3]; uint8_t dataReadyBuffer[3];
size_t charNumber = readBuffer(&dataReadyBuffer[0], 3); size_t charNumber = readBuffer(&dataReadyBuffer[0], 3);
if (charNumber == 0) { if (charNumber == 0) {
LOG_ERROR("SEN5X: Error getting device version value"); LOG_ERROR("%s: Error getting device version value", sensorName);
return 2; return 2;
} }
@@ -780,17 +787,17 @@ uint8_t SEN5XSensor::getMeasurements()
uint32_t sinceLastDataPollMs = (now - lastDataPoll) * 1000; uint32_t sinceLastDataPollMs = (now - lastDataPoll) * 1000;
// Check if data is ready, and if since last time we requested is less than SEN5X_POLL_INTERVAL // Check if data is ready, and if since last time we requested is less than SEN5X_POLL_INTERVAL
if (!dataReady && (sinceLastDataPollMs > 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; return 1;
} }
if (!readValues()) { if (!readValues()) {
LOG_ERROR("SEN5X: Error getting readings"); LOG_ERROR("%s: Error getting readings", sensorName);
return 2; return 2;
} }
if (!readPNValues(false)) { if (!readPNValues(false)) {
LOG_ERROR("SEN5X: Error getting PN readings"); LOG_ERROR("%s: Error getting PN readings", sensorName);
return 2; return 2;
} }
@@ -809,13 +816,13 @@ int32_t SEN5XSensor::pendingForReadyMs()
uint32_t now; uint32_t now;
now = getTime(); now = getTime();
uint32_t sincePmMeasureStarted = (now - pmMeasureStarted) * 1000; 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) { switch (state) {
case SEN5X_MEASUREMENT: { case SEN5X_MEASUREMENT: {
if (sincePmMeasureStarted < SEN5X_WARMUP_MS_1) { 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; return SEN5X_WARMUP_MS_1 - sincePmMeasureStarted;
} }
@@ -829,7 +836,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 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) { 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; state = SEN5X_MEASUREMENT_2;
// Report how many seconds are pending to cover the first warm up period // Report how many seconds are pending to cover the first warm up period
return SEN5X_WARMUP_MS_2 - sincePmMeasureStarted; return SEN5X_WARMUP_MS_2 - sincePmMeasureStarted;
@@ -851,9 +858,9 @@ int32_t SEN5XSensor::pendingForReadyMs()
bool SEN5XSensor::getMetrics(meshtastic_Telemetry *measurement) bool SEN5XSensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
LOG_INFO("SEN5X: Attempting to get metrics"); LOG_INFO("%s: Attempting to get metrics", sensorName);
if (!isActive()) { if (!isActive()) {
LOG_INFO("SEN5X: not in measurement mode"); LOG_INFO("%s: not in measurement mode", sensorName);
return false; return false;
} }
@@ -943,9 +950,9 @@ void SEN5XSensor::setMode(bool setOneShot)
{ {
oneShotMode = setOneShot; oneShotMode = setOneShot;
if (oneShotMode) { if (oneShotMode) {
LOG_INFO("%s setting mode to one shot mode", sensorName); LOG_INFO("%s: setting mode to one shot mode", sensorName);
} else { } 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 #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h" #include "RTC.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
@@ -62,7 +63,10 @@ class SEN5XSensor : public TelemetrySensor
private: private:
TwoWire *_bus{}; TwoWire *_bus{};
uint8_t _address{}; uint8_t _address{};
#ifdef SEN5X_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C; ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool getVersion(); bool getVersion();
float firmwareVer = -1; float firmwareVer = -1;
+23 -38
View File
@@ -2,7 +2,6 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cSfa3x.h>) #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cSfa3x.h>)
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "SFA30Sensor.h" #include "SFA30Sensor.h"
@@ -14,11 +13,13 @@ bool SFA30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
_bus = bus; _bus = bus;
_address = dev->address.address; _address = dev->address.address;
_port = dev->address.port;
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
_port = dev->address.port;
reClockI2C.setup(_bus, _port);
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED); LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
sfa30.begin(*_bus, _address); sfa30.begin(*_bus, _address);
@@ -26,10 +27,8 @@ bool SFA30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
if (this->isError(sfa30.deviceReset())) { if (this->isError(sfa30.deviceReset())) {
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -37,10 +36,8 @@ bool SFA30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
state = State::IDLE; state = State::IDLE;
if (this->isError(sfa30.startContinuousMeasurement())) { if (this->isError(sfa30.startContinuousMeasurement())) {
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
return false; return false;
} }
@@ -48,10 +45,8 @@ bool SFA30Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
LOG_INFO("%s starting measurement", sensorName); LOG_INFO("%s starting measurement", sensorName);
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
status = 1; status = 1;
@@ -77,7 +72,7 @@ void SFA30Sensor::sleep()
{ {
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED); LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
// Note - not recommended for this sensor on a periodic basis // Note - not recommended for this sensor on a periodic basis
@@ -86,10 +81,8 @@ void SFA30Sensor::sleep()
}; };
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
LOG_INFO("%s: stop measurement", sensorName); LOG_INFO("%s: stop measurement", sensorName);
@@ -101,25 +94,21 @@ uint32_t SFA30Sensor::wakeUp()
{ {
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED); LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
LOG_INFO("Waking up %s", sensorName); LOG_INFO("Waking up %s", sensorName);
if (this->isError(sfa30.startContinuousMeasurement())) { if (this->isError(sfa30.startContinuousMeasurement())) {
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
return 0; return 0;
} }
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
state = State::ACTIVE; state = State::ACTIVE;
@@ -166,25 +155,21 @@ bool SFA30Sensor::getMetrics(meshtastic_Telemetry *measurement)
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED); LOG_INFO("%s attempting to reclock speed to %uHz", sensorName, SFA30_I2C_CLOCK_SPEED);
uint32_t currentClock = reClockI2C(SFA30_I2C_CLOCK_SPEED, _bus, _port); reClockI2C.setClock(SFA30_I2C_CLOCK_SPEED);
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
if (this->isError(sfa30.readMeasuredValues(hcho, humidity, temperature))) { if (this->isError(sfa30.readMeasuredValues(hcho, humidity, temperature))) {
LOG_WARN("%s: No values", sensorName); LOG_WARN("%s: No values", sensorName);
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
return false; return false;
} }
#ifdef SFA30_I2C_CLOCK_SPEED #ifdef SFA30_I2C_CLOCK_SPEED
if (currentClock) { LOG_INFO("%s restoring clock speed", sensorName);
LOG_INFO("%s restoring clock speed to %uHz", sensorName, currentClock); reClockI2C.restoreClock();
reClockI2C(currentClock, _bus, _port);
}
#endif /* SFA30_I2C_CLOCK_SPEED */ #endif /* SFA30_I2C_CLOCK_SPEED */
measurement->variant.air_quality_metrics.has_form_temperature = true; measurement->variant.air_quality_metrics.has_form_temperature = true;
@@ -2,6 +2,7 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cSfa3x.h>) #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR && __has_include(<SensirionI2cSfa3x.h>)
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "RTC.h" #include "RTC.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
@@ -21,7 +22,10 @@ class SFA30Sensor : public TelemetrySensor
SensirionI2cSfa3x sfa30; SensirionI2cSfa3x sfa30;
TwoWire *_bus{}; TwoWire *_bus{};
uint8_t _address{}; uint8_t _address{};
#ifdef SFA30_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C; ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
bool isError(uint16_t response); bool isError(uint16_t response);
+1 -1
View File
@@ -56,7 +56,7 @@ build_flags =
-DLIBPAX_BLE -DLIBPAX_BLE
-DHAS_UDP_MULTICAST=1 -DHAS_UDP_MULTICAST=1
;-DDEBUG_HEAP ;-DDEBUG_HEAP
-DCAN_RECLOCK_I2C -DCAN_GET_I2C_CLOCK
lib_deps = lib_deps =
${arduino_base.lib_deps} ${arduino_base.lib_deps}