From cb9429e83e06b29679b5c85272b62615ce583bed Mon Sep 17 00:00:00 2001 From: dmarman Date: Thu, 29 May 2025 16:09:33 +0200 Subject: [PATCH] Added full support for LTR390UV readings of UV and Lux (#6872) * Added full support for LTR390UV readings of UV and Lux * Trunk formatting * Added full support for LTR390UV readings of UV and Lux * Trunk formatting * fix library check and unnecessary bit resolution change * fixed log info messages and removed unused dependency * Hopefully fixes git mess * fix variable scope and getMetrics returns * set metrics flags bavk to false in case something wrong happens while reading LTR390UV mode --------- Co-authored-by: Domingo Co-authored-by: Ben Meadors --- platformio.ini | 4 +- .../Telemetry/EnvironmentTelemetry.cpp | 18 +++++ .../Telemetry/Sensor/LTR390UVSensor.cpp | 73 +++++++++++++++++++ src/modules/Telemetry/Sensor/LTR390UVSensor.h | 25 +++++++ 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/modules/Telemetry/Sensor/LTR390UVSensor.cpp create mode 100644 src/modules/Telemetry/Sensor/LTR390UVSensor.h diff --git a/platformio.ini b/platformio.ini index e5d36d862..125dfb573 100644 --- a/platformio.ini +++ b/platformio.ini @@ -161,6 +161,8 @@ lib_deps = sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2 # renovate: datasource=custom.pio depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/library/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@1.3.2 + # renovate: datasource=custom.pio depName=Adafruit LTR390 Library packageName=adafruit/library/Adafruit LTR390 Library + adafruit/Adafruit LTR390 Library@1.1.2 # renovate: datasource=custom.pio depName=Adafruit PCT2075 packageName=adafruit/Adafruit PCT2075 adafruit/Adafruit PCT2075@1.0.5 @@ -190,4 +192,4 @@ lib_deps = # renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library boschsensortec/BME68x Sensor Library@1.3.40408 # renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master - https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip + https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip \ No newline at end of file diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 51f076552..6d29fecb2 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -52,6 +52,13 @@ BMP280Sensor bmp280Sensor; NullSensor bme280Sensor; #endif +#if __has_include() +#include "Sensor/LTR390UVSensor.h" +LTR390UVSensor ltr390uvSensor; +#else +NullSensor ltr390uvSensor; +#endif + #if __has_include() #include "Sensor/BME680Sensor.h" BME680Sensor bme680Sensor; @@ -231,6 +238,8 @@ int32_t EnvironmentTelemetryModule::runOnce() #endif if (bme280Sensor.hasSensor()) result = bme280Sensor.runOnce(); + if (ltr390uvSensor.hasSensor()) + result = ltr390uvSensor.runOnce(); if (bmp3xxSensor.hasSensor()) result = bmp3xxSensor.runOnce(); if (bme680Sensor.hasSensor()) @@ -524,6 +533,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m valid = valid && bme280Sensor.getMetrics(m); hasSensor = true; } + if (ltr390uvSensor.hasSensor()) { + valid = valid && ltr390uvSensor.getMetrics(m); + hasSensor = true; + } if (bmp3xxSensor.hasSensor()) { valid = valid && bmp3xxSensor.getMetrics(m); hasSensor = true; @@ -752,6 +765,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } + if (ltr390uvSensor.hasSensor()) { + result = ltr390uvSensor.handleAdminMessage(mp, request, response); + if (result != AdminMessageHandleResult::NOT_HANDLED) + return result; + } if (bmp3xxSensor.hasSensor()) { result = bmp3xxSensor.handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED) diff --git a/src/modules/Telemetry/Sensor/LTR390UVSensor.cpp b/src/modules/Telemetry/Sensor/LTR390UVSensor.cpp new file mode 100644 index 000000000..fb84700c4 --- /dev/null +++ b/src/modules/Telemetry/Sensor/LTR390UVSensor.cpp @@ -0,0 +1,73 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "LTR390UVSensor.h" +#include "TelemetrySensor.h" +#include + +LTR390UVSensor::LTR390UVSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LTR390UV, "LTR390UV") {} + +int32_t LTR390UVSensor::runOnce() +{ + LOG_INFO("Init sensor: %s", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + status = ltr390uv.begin(nodeTelemetrySensorsMap[sensorType].second); + ltr390uv.setMode(LTR390_MODE_UVS); + ltr390uv.setGain(LTR390_GAIN_18); // Datasheet default + ltr390uv.setResolution(LTR390_RESOLUTION_20BIT); // Datasheet default + + return initI2CSensor(); +} + +void LTR390UVSensor::setup() {} + +bool LTR390UVSensor::getMetrics(meshtastic_Telemetry *measurement) +{ + LOG_DEBUG("LTR390UV getMetrics"); + + // Because the sensor does not measure Lux and UV at the same time, we need to read them in two passes. + if (ltr390uv.newDataAvailable()) { + measurement->variant.environment_metrics.has_lux = true; + measurement->variant.environment_metrics.has_uv_lux = true; + + if (ltr390uv.getMode() == LTR390_MODE_ALS) { + lastLuxReading = 0.6 * ltr390uv.readALS() / (1 * 4); // Datasheet page 23 for gain x1 and 20bit resolution + LOG_DEBUG("LTR390UV Lux reading: %f", lastLuxReading); + + measurement->variant.environment_metrics.lux = lastLuxReading; + measurement->variant.environment_metrics.uv_lux = lastUVReading; + + ltr390uv.setGain( + LTR390_GAIN_18); // Recommended for UVI - x18. Do not change, 2300 UV Sensitivity only specified for x18 gain + ltr390uv.setMode(LTR390_MODE_UVS); + + return true; + + } else if (ltr390uv.getMode() == LTR390_MODE_UVS) { + lastUVReading = ltr390uv.readUVS() / + 2300.f; // Datasheet page 23 and page 6, only characterisation for gain x18 and 20bit resolution + LOG_DEBUG("LTR390UV UV reading: %f", lastUVReading); + + measurement->variant.environment_metrics.lux = lastLuxReading; + measurement->variant.environment_metrics.uv_lux = lastUVReading; + + ltr390uv.setGain( + LTR390_GAIN_1); // x1 gain will already max out the sensor at direct sunlight, so no need to increase it + ltr390uv.setMode(LTR390_MODE_ALS); + + return true; + } + } + + // In case we fail to read the sensor mode, set the has_lux and has_uv_lux back to false + measurement->variant.environment_metrics.has_lux = false; + measurement->variant.environment_metrics.has_uv_lux = false; + + return false; +} +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/LTR390UVSensor.h b/src/modules/Telemetry/Sensor/LTR390UVSensor.h new file mode 100644 index 000000000..40206bce8 --- /dev/null +++ b/src/modules/Telemetry/Sensor/LTR390UVSensor.h @@ -0,0 +1,25 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class LTR390UVSensor : public TelemetrySensor +{ + private: + Adafruit_LTR390 ltr390uv = Adafruit_LTR390(); + float lastLuxReading = 0; + float lastUVReading = 0; + + protected: + virtual void setup() override; + + public: + LTR390UVSensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; +}; + +#endif \ No newline at end of file