Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 10 additions & 58 deletions src/detect/ScanI2CTwoWire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,44 +156,11 @@ bool ScanI2CTwoWire::i2cCommandResponseLength(ScanI2C::DeviceAddress addr, uint1
}

#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
// FIXME Move to a separate file for detection of sensors that require more complex interactions?
// For SEN5X detection
// Note, this code needs to be called before setting the I2C bus speed
// for the screen at high speed. The speed needs to be at 100kHz, otherwise
// detection will not work
String readSEN5xProductName(TwoWire *i2cBus, uint8_t address)
#include "../modules/Telemetry/Sensor/SEN5XSensor.h"
bool probeSEN5X(TwoWire *i2cBus, uint8_t address, ScanI2C::I2CPort port)
{
uint8_t cmd[] = {0xD0, 0x14};
uint8_t response[48] = {0};

i2cBus->beginTransmission(address);
i2cBus->write(cmd, 2);
if (i2cBus->endTransmission() != 0)
return "";

delay(20);
if (i2cBus->requestFrom(address, (uint8_t)48) != 48)
return "";

for (int i = 0; i < 48 && i2cBus->available(); ++i) {
response[i] = i2cBus->read();
}

char productName[33] = {0};
int j = 0;
for (int i = 0; i < 48 && j < 32; i += 3) {
if (response[i] >= 32 && response[i] <= 126)
productName[j++] = response[i];
else
break;

if (response[i + 1] >= 32 && response[i + 1] <= 126)
productName[j++] = response[i + 1];
else
break;
}

return String(productName);
SEN5XSensor sen5xsensor;
return sen5xsensor.probe(i2cBus, address, port);
}
Comment on lines +159 to 164

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I agree with this. I understand the module coupling, but there are two considerations:

  1. The sensor has quite a bit of details that are handled in the class directly (changing bus speed when sending commands, for one) that would need to be replicated in a separate class
  2. The sensor also has an internal firmware versioning, which is also detected in the class. If in the future there are new firmware versions for the sensor, we would only need to update de class, and not the probing on a different function.

#endif

Expand Down Expand Up @@ -861,33 +828,18 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
type = ICM42607P;
logFoundDevice("ICM-42607-P", (uint8_t)addr.address);
break;
} else if (registerValue == 0x68) { // WHO_AM_I from datasheet
type = MPU6050;
logFoundDevice("MPU6050", (uint8_t)addr.address);
break;
}
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
String prod = "";
prod = readSEN5xProductName(i2cBus, addr.address);
if (prod.startsWith("SEN55")) {
type = SEN5X;
logFoundDevice("Sensirion SEN55", addr.address);
break;
} else if (prod.startsWith("SEN54")) {
if (probeSEN5X(i2cBus, addr.address, port)) {
type = SEN5X;
logFoundDevice("Sensirion SEN54", addr.address);
break;
} else if (prod.startsWith("SEN50")) {
type = SEN5X;
logFoundDevice("Sensirion SEN50", addr.address);
logFoundDevice("SEN5X", addr.address);
break;
}
#endif
if (addr.address == BMX160_ADDR) {
type = BMX160;
logFoundDevice("BMX160", (uint8_t)addr.address);
break;
} else {
type = MPU6050;
logFoundDevice("MPU6050", (uint8_t)addr.address);
break;
}
}
break;

Expand Down
56 changes: 55 additions & 1 deletion src/modules/Telemetry/AirQualityTelemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -41,19 +42,66 @@ 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)
*/

// moduleConfig.telemetry.air_quality_enabled = 1;
// 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);
Expand Down Expand Up @@ -94,6 +142,12 @@ int32_t AirQualityTelemetryModule::runOnce()
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: init");

#if !MESHTASTIC_EXCLUDE_I2C
// Re-scan I2C bus
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
i2cScanFinished(i2cScanner.get());
#endif

// check if we have at least one sensor
if (!sensors.empty()) {
result = DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
Expand Down
6 changes: 5 additions & 1 deletion src/modules/Telemetry/AirQualityTelemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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>
Expand Down Expand Up @@ -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
9 changes: 9 additions & 0 deletions src/modules/Telemetry/Sensor/AddI2CSensorTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 1 addition & 4 deletions src/modules/Telemetry/Sensor/PMSA003ISensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +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
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif
};

#endif
#endif
5 changes: 1 addition & 4 deletions src/modules/Telemetry/Sensor/SCD30Sensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ class SCD30Sensor : public TelemetrySensor
{
private:
SensirionI2cScd30 scd30;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SCD30_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif

Expand Down Expand Up @@ -55,4 +52,4 @@ class SCD30Sensor : public TelemetrySensor
meshtastic_AdminMessage *response) override;
};

#endif
#endif
5 changes: 1 addition & 4 deletions src/modules/Telemetry/Sensor/SCD4XSensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ class SCD4XSensor : public TelemetrySensor
{
private:
SensirionI2cScd4x scd4x;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SCD4X_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif

Expand Down Expand Up @@ -65,4 +62,4 @@ class SCD4XSensor : public TelemetrySensor
meshtastic_AdminMessage *response) override;
};

#endif
#endif
18 changes: 18 additions & 0 deletions src/modules/Telemetry/Sensor/SEN5XSensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,24 @@ bool SEN5XSensor::findModel()
return true;
}

bool SEN5XSensor::probe(TwoWire *bus, uint8_t address, ScanI2C::I2CPort port)
{
LOG_INFO("SEN5X: probing sensor");
Comment thread
caveman99 marked this conversation as resolved.

_bus = bus;
_address = address;
#ifdef SEN5X_I2C_CLOCK_SPEED
_port = port;
reClockI2C.setup(_bus, _port);
#endif /* SEN5X_I2C_CLOCK_SPEED */

if (!findModel()) {
Comment thread
oscgonfer marked this conversation as resolved.
LOG_DEBUG("SEN5X: can't find SEN5X model");
return false;
}
return true;
}

bool SEN5XSensor::sendCommand(uint16_t command)
{
uint8_t nothing;
Expand Down
6 changes: 2 additions & 4 deletions src/modules/Telemetry/Sensor/SEN5XSensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ struct _SEN5XMeasurements {
class SEN5XSensor : public TelemetrySensor
{
private:
TwoWire *_bus{};
uint8_t _address{};
#ifdef SEN5X_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif

Expand Down Expand Up @@ -158,6 +155,7 @@ See: https://sensirion.com/resource/application_note/low_power_mode/sen5x

public:
SEN5XSensor();
bool probe(TwoWire *bus, uint8_t address, ScanI2C::I2CPort port);
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;

Expand All @@ -172,4 +170,4 @@ See: https://sensirion.com/resource/application_note/low_power_mode/sen5x
meshtastic_AdminMessage *response) override;
};

#endif
#endif
3 changes: 0 additions & 3 deletions src/modules/Telemetry/Sensor/SFA30Sensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ class SFA30Sensor : public TelemetrySensor
uint32_t measureStarted = 0;

SensirionI2cSfa3x sfa30;
TwoWire *_bus{};
uint8_t _address{};
#ifdef SFA30_I2C_CLOCK_SPEED
ScanI2C::I2CPort _port = ScanI2C::I2CPort::NO_I2C;
ReClockI2C reClockI2C;
#endif

Expand Down
5 changes: 5 additions & 0 deletions src/modules/Telemetry/Sensor/TelemetrySensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
Loading