Compare commits

...
Author SHA1 Message Date
oscgonfer d021f4c4e7 Mark getter functions as const (properly) 2026-06-14 14:47:57 +02:00
oscgonfer 0fe8ed28f8 Style fix 2026-06-14 14:16:00 +02:00
oscgonfer 6d52fea75e Allow re-scan of AirQualityModule for late-coming sensors
* Define map for sensors to re-scan
* Add re-scan on runOnce
2026-06-14 14:16:00 +02:00
oscgonfer caa300d2fb Move address, bus and port to TelemetrySensor 2026-06-14 14:16:00 +02:00
oscgonfer 28dbb7cbe7 Low prio debug fixes 2026-06-14 14:16:00 +02:00
oscgonfer cba9439e62 Minor typo, remove pragma once 2026-06-14 14:16:00 +02:00
oscgonfer 9619c2d439 Make getter functions const, fix capitalisaiton of new functions 2026-06-14 14:16:00 +02:00
oscgonfer f5ce759af9 Improve setClock and restoreClock cases 2026-06-14 14:16:00 +02:00
oscgonfer 72205195f2 Minor logging changes 2026-06-14 14:16:00 +02:00
oscgonfer f008d9465f Fix esp8266 library 2026-06-14 14:16:00 +02:00
oscgonfer 5532ec023d Update esp8266-oled library 2026-06-14 14:16:00 +02:00
oscgonfer e96fa4c869 Refurbish ReClockI2C API
* Make ReClockI2C API class
* Use new API in AirQualityTelemetry module
* Minor changes on some logs
2026-06-14 14:16:00 +02:00
oscgonfer 87714450ca Make reclock function aware of screen-set I2C speeds 2026-06-14 14:16:00 +02:00
oscgonfer 70ecaccbb2 Add functions to get I2C screen frequency and port 2026-06-14 14:16:00 +02:00
oscgonfer f6a2acacd3 Add define for screen I2C frequency 2026-06-14 14:16:00 +02:00
21 changed files with 511 additions and 399 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/6bfd1f135e1ebe37afd6050bb4b9964cea3fcfda.zip
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2e26010040e028baee72e2093402fa7b3c59e430.zip
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
+98
View File
@@ -0,0 +1,98 @@
#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 established 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);
setPreviousClock(0);
return false;
}
bool restoreClock()
{
if (this->previousClock) {
LOG_DEBUG("Restoring I2C clock to %uHz", this->previousClock);
i2cBus->setClock(this->previousClock);
setPreviousClock(0);
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
-31
View File
@@ -1,31 +0,0 @@
#include "reClockI2C.h"
#include "ScanI2CTwoWire.h"
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus, bool force)
{
uint32_t currentClock = 0;
/* 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 desired speed.
Only for cases where we can know OR predefine the speed, we can do this.
*/
// TODO add getClock function or return a predefined clock speed per variant?
#ifdef CAN_RECLOCK_I2C
currentClock = i2cBus->getClock();
#endif
if ((currentClock != desiredClock) || force) {
LOG_DEBUG("Changing I2C clock to %u", desiredClock);
i2cBus->setClock(desiredClock);
}
return currentClock;
}
-11
View File
@@ -1,11 +0,0 @@
#ifndef RECLOCK_I2C_
#define RECLOCK_I2C_
#include "ScanI2CTwoWire.h"
#include <Wire.h>
#include <stdint.h>
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus, bool force);
#endif
+5
View File
@@ -478,6 +478,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
dispdev = new SH1106Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
isI2cScreen = true;
#elif defined(USE_ST7789)
#ifdef ESP_PLATFORM
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7789_SDA,
@@ -495,6 +496,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
isI2cScreen = true;
#if defined(OLED_Y_OFFSET_PAGES)
// Panels whose active window does not start at GDDRAM row 0 (e.g. 72x40
// modules on pages 3..7) need a fixed vertical page shift on every write.
@@ -523,6 +525,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#elif defined(USE_ST7567)
dispdev = new ST7567Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
isI2cScreen = true;
#elif ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
if (portduino_config.displayPanel != no_screen) {
@@ -533,12 +536,14 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
isAUTOOled = true;
isI2cScreen = true;
}
}
#else
dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
isAUTOOled = true;
isI2cScreen = true;
#endif
#if defined(USE_ST7789)
+20
View File
@@ -73,6 +73,9 @@ class Screen
void showOverlayBanner(BannerOverlayOptions) {}
void setFrames(FrameFocus focus) {}
void endAlert() {}
bool getIsI2cScreen() const { return false; }
uint32_t getI2cFrequency() const { return 0; }
ScanI2C::I2CPort getI2CPort() const { return ScanI2C::I2CPort::NO_I2C; }
};
} // namespace graphics
#else
@@ -260,6 +263,22 @@ class Screen : public concurrency::OSThread
Screen &operator=(const Screen &) = delete;
ScanI2C::DeviceAddress address_found;
bool getIsI2cScreen() const { return isI2cScreen; }
// Return I2C Speed, or 0 if none
uint32_t getI2cFrequency() const
{
if (getIsI2cScreen())
return dispdev->getI2cFrequency();
else
return 0;
}
ScanI2C::I2CPort getI2CPort() const
{
if (getIsI2cScreen())
return address_found.port;
else
return ScanI2C::I2CPort::NO_I2C;
}
meshtastic_Config_DisplayConfig_OledType model;
OLEDDISPLAY_GEOMETRY geometry;
@@ -654,6 +673,7 @@ class Screen : public concurrency::OSThread
int32_t runOnce() final;
bool isAUTOOled = false;
bool isI2cScreen = false;
// Screen dimensions (for convenience)
// Defined during Screen::setup
+55 -3
View File
@@ -13,6 +13,7 @@
#include "Router.h"
#include "TransmitHistory.h"
#include "UnitConversions.h"
#include "detect/ScanI2CTwoWire.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/images.h"
@@ -41,12 +42,13 @@ void AirQualityTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
if (!moduleConfig.telemetry.air_quality_enabled && !AIR_QUALITY_TELEMETRY_MODULE_ENABLE) {
return;
}
LOG_INFO("Air Quality Telemetry adding I2C devices...");
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
Note: this was previously on runOnce, which didnt take effect
Note: this was previously on runOnce, which didn't take effect
as other modules already had already been initialized (screen)
*/
@@ -54,6 +56,52 @@ void AirQualityTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
// moduleConfig.telemetry.air_quality_screen_enabled = 1;
// moduleConfig.telemetry.air_quality_interval = 15;
// Add here supported sensors in the Air Quality module
// These sensors will be scanned twice, once in the first scan,
// and secondly in the first run of the module
if (!supportedSensors.count(PMSA003I_ADDR))
supportedSensors[PMSA003I_ADDR] = ScanI2C::DeviceType::PMSA003I;
if (!supportedSensors.count(SEN5X_ADDR))
supportedSensors[SEN5X_ADDR] = ScanI2C::DeviceType::SEN5X;
#if __has_include(<SensirionI2cScd4x.h>)
if (!supportedSensors.count(SCD4X_ADDR))
supportedSensors[SCD4X_ADDR] = ScanI2C::DeviceType::SCD4X;
#endif
#if __has_include(<SensirionI2cSfa3x.h>)
if (!supportedSensors.count(SFA30_ADDR))
supportedSensors[SFA30_ADDR] = ScanI2C::DeviceType::SFA30;
#endif
#if __has_include(<SensirionI2cScd30.h>)
if (!supportedSensors.count(SCD30_ADDR))
supportedSensors[SCD30_ADDR] = ScanI2C::DeviceType::SCD30;
#endif
if (!firstTime) {
// Re-scan for late comming sensors
LOG_INFO("Re-scanning supported sensors...");
for (const auto &[address, type] : supportedSensors) {
if (!i2cScanner->exists(type)) {
LOG_INFO("Re-scanning on address 0x%x", address);
uint8_t array_address[1] = {address};
#if defined(I2C_SDA1) || (defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2))
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, array_address, sizeof(array_address));
#endif
#if defined(I2C_SDA)
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, array_address, sizeof(array_address));
#elif defined(ARCH_PORTDUINO)
if (portduino_config.i2cdev != "") {
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, array_address, sizeof(array_address));
}
#elif HAS_WIRE
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, array_address, sizeof(array_address));
#endif
}
}
}
// order by priority of metrics/values (low top, high bottom)
addSensor<PMSA003ISensor>(i2cScanner, ScanI2C::DeviceType::PMSA003I);
addSensor<SEN5XSensor>(i2cScanner, ScanI2C::DeviceType::SEN5X);
@@ -94,6 +142,10 @@ int32_t AirQualityTelemetryModule::runOnce()
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: init");
// Re-scan I2C bus
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
i2cScanFinished(i2cScanner.get());
// check if we have at least one sensor
if (!sensors.empty()) {
result = DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
@@ -110,10 +162,10 @@ int32_t AirQualityTelemetryModule::runOnce()
}
// Wake up the sensors that need it
LOG_INFO("Waking up sensors...");
uint32_t lastTelemetry =
transmitHistory ? transmitHistory->getLastSentToMeshMillis(TX_HISTORY_KEY_AIR_QUALITY_TELEMETRY) : 0;
for (TelemetrySensor *sensor : sensors) {
LOG_DEBUG("Checking if %s needs to wake up", sensor->sensorName);
if (!sensor->canSleep()) {
LOG_DEBUG("%s sensor doesn't have sleep feature. Skipping", sensor->sensorName);
} else if (((lastTelemetry == 0) || !Throttle::isWithinTimespanMs(lastTelemetry - sensor->wakeUpTimeMs(),
@@ -154,8 +206,8 @@ int32_t AirQualityTelemetryModule::runOnce()
}
// Send to sleep sensors that consume power
LOG_DEBUG("Sending sensors to sleep");
for (TelemetrySensor *sensor : sensors) {
LOG_DEBUG("Checking if %s can be sent to sleep", sensor->sensorName);
if (sensor->isActive() && sensor->canSleep()) {
if (sensor->wakeUpTimeMs() <
(int32_t)Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval,
+5 -1
View File
@@ -3,8 +3,8 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#pragma once
#include "BaseTelemetryModule.h"
#include <map>
#ifndef AIR_QUALITY_TELEMETRY_MODULE_ENABLE
#define AIR_QUALITY_TELEMETRY_MODULE_ENABLE 0
@@ -13,6 +13,7 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "NodeDB.h"
#include "ProtobufModule.h"
#include "detect/ScanI2C.h"
#include "detect/ScanI2CConsumer.h"
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
@@ -69,6 +70,9 @@ class AirQualityTelemetryModule : private concurrency::OSThread,
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
// uint32_t sendToPhoneIntervalMs = 1000; // Send to phone every minute
uint32_t lastSentToPhone = 0;
// Map for supported sensors to re-scan
std::map<uint8_t, ScanI2C::DeviceType> supportedSensors;
};
#endif
@@ -11,6 +11,15 @@ static std::forward_list<TelemetrySensor *> sensors;
template <typename T> void addSensor(const ScanI2C *i2cScanner, ScanI2C::DeviceType type)
{
ScanI2C::FoundDevice dev = i2cScanner->find(type);
// Avoid adding the same device twice
if (dev.type != ScanI2C::DeviceType::NONE) {
for (const TelemetrySensor *_sensor : sensors) {
if ((_sensor->_address == dev.address.address) && (_sensor->_port == dev.address.port)) {
return;
}
}
}
if (dev.type != ScanI2C::DeviceType::NONE || type == ScanI2C::DeviceType::NONE) {
TelemetrySensor *sensor = new T();
#if WIRE_INTERFACES_COUNT > 1
+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"
@@ -33,8 +34,9 @@ class PMSA003ISensor : public TelemetrySensor
uint32_t pmMeasureStarted = 0;
uint8_t buffer[PMSA003I_FRAME_LENGTH]{};
TwoWire *_bus{};
uint8_t _address{};
#ifdef PMSA003I_I2C_CLOCK_SPEED
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;
}
+4 -2
View File
@@ -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>
@@ -12,8 +13,9 @@ class SCD30Sensor : public TelemetrySensor
{
private:
SensirionI2cScd30 scd30;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SCD30_I2C_CLOCK_SPEED
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;
}
+5 -3
View File
@@ -2,21 +2,23 @@
#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
{
private:
SensirionI2cScd4x scd4x;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SCD4X_I2C_CLOCK_SPEED
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);
}
}
+4 -2
View File
@@ -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"
@@ -60,8 +61,9 @@ struct _SEN5XMeasurements {
class SEN5XSensor : public TelemetrySensor
{
private:
TwoWire *_bus{};
uint8_t _address{};
#ifdef SEN5X_I2C_CLOCK_SPEED
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;
+5 -2
View File
@@ -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"
@@ -19,8 +20,10 @@ class SFA30Sensor : public TelemetrySensor
uint32_t measureStarted = 0;
SensirionI2cSfa3x sfa30;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SFA30_I2C_CLOCK_SPEED
ReClockI2C reClockI2C;
#endif
bool isError(uint16_t response);
public:
@@ -56,6 +56,11 @@ class TelemetrySensor
}
const char *sensorName;
// TODO: Rename?
uint8_t _address = 0;
TwoWire *_bus{};
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
// TODO: delete after migration
bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
+1 -1
View File
@@ -56,7 +56,7 @@ build_flags =
-DLIBPAX_BLE
-DHAS_UDP_MULTICAST=1
;-DDEBUG_HEAP
-DCAN_RECLOCK_I2C
-DCAN_GET_I2C_CLOCK
lib_deps =
${arduino_base.lib_deps}