diff --git a/src/configuration.h b/src/configuration.h index 15912be3f..2e81557b1 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -148,6 +148,7 @@ along with this program. If not, see . #define NAU7802_ADDR 0x2A #define MAX30102_ADDR 0x57 #define MLX90614_ADDR_DEF 0x5A +#define CGRADSENS_ADDR 0x66 // ----------------------------------------------------------------------------- // ACCELEROMETER diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 8591b8433..f4516458b 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -63,7 +63,8 @@ class ScanI2C ICM20948, MAX30102, TPS65233, - MPR121KB + MPR121KB, + CGRADSENS } DeviceType; // typedef uint8_t DeviceAddress; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 2413c44c9..55f13c5a0 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -479,6 +479,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) } break; + case CGRADSENS_ADDR: + // Register 0x00 of the RadSens sensor contains is product identifier 0x7D + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); + if (registerValue == 0x7D) { + type = CGRADSENS; + LOG_INFO("ClimateGuard RadSens Geiger-Muller Sensor found"); + break; + } + break; + default: LOG_INFO("Device found at address 0x%x was not able to be enumerated", addr.address); } diff --git a/src/main.cpp b/src/main.cpp index 97a64a378..df18dae98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -614,6 +614,7 @@ void setup() SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS) i2cScanner.reset(); #endif diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 4ef68d4b7..92d964f7d 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -25,6 +25,7 @@ #include "Sensor/BMP085Sensor.h" #include "Sensor/BMP280Sensor.h" #include "Sensor/BMP3XXSensor.h" +#include "Sensor/CGRadSensSensor.h" #include "Sensor/DFRobotLarkSensor.h" #include "Sensor/LPS22HBSensor.h" #include "Sensor/MCP9808Sensor.h" @@ -60,6 +61,7 @@ BMP3XXSensor bmp3xxSensor; #ifdef T1000X_SENSOR_EN T1000xSensor t1000xSensor; #endif +CGRadSensSensor cgRadSens; #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true @@ -147,6 +149,8 @@ int32_t EnvironmentTelemetryModule::runOnce() result = nau7802Sensor.runOnce(); if (max17048Sensor.hasSensor()) result = max17048Sensor.runOnce(); + if (cgRadSens.hasSensor()) + result = cgRadSens.runOnce(); #endif } return result; @@ -210,16 +214,19 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt // Display "Env. From: ..." on its own display->drawString(x, y, "Env. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); - String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; - if (moduleConfig.telemetry.environment_display_fahrenheit) { - last_temp = - String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; - } + if (lastMeasurement.variant.environment_metrics.has_temperature || + lastMeasurement.variant.environment_metrics.has_relative_humidity) { + String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; + if (moduleConfig.telemetry.environment_display_fahrenheit) { + last_temp = + String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; + } - // Continue with the remaining details - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Temp/Hum: " + last_temp + " / " + - String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); + // Continue with the remaining details + display->drawString(x, y += _fontHeight(FONT_SMALL), + "Temp/Hum: " + last_temp + " / " + + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); + } if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) { display->drawString(x, y += _fontHeight(FONT_SMALL), @@ -243,6 +250,10 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt if (lastMeasurement.variant.environment_metrics.weight != 0) display->drawString(x, y += _fontHeight(FONT_SMALL), "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"); + + if (lastMeasurement.variant.environment_metrics.radiation != 0) + display->drawString(x, y += _fontHeight(FONT_SMALL), + "Rad: " + String(lastMeasurement.variant.environment_metrics.radiation, 2) + "µR/h"); } bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) @@ -263,6 +274,8 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction, t->variant.environment_metrics.weight); + LOG_INFO("(Received from %s): radiation=%fµR/h", sender, t->variant.environment_metrics.radiation); + #endif // release previous packet before occupying a new spot if (lastMeasurementPacket != nullptr) @@ -390,6 +403,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m valid = valid && max17048Sensor.getMetrics(m); hasSensor = true; } + if (cgRadSens.hasSensor()) { + valid = valid && cgRadSens.getMetrics(m); + hasSensor = true; + } #endif return valid && hasSensor; @@ -443,6 +460,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) LOG_INFO("Send: wind speed=%fm/s, direction=%d degrees, weight=%fkg", m.variant.environment_metrics.wind_speed, m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.weight); + LOG_INFO("Send: radiation=%fµR/h", m.variant.environment_metrics.radiation); + sensor_read_error_count = 0; meshtastic_MeshPacket *p = allocDataProtobuf(m); @@ -585,6 +604,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } + if (cgRadSens.hasSensor()) { + result = cgRadSens.handleAdminMessage(mp, request, response); + if (result != AdminMessageHandleResult::NOT_HANDLED) + return result; + } return result; } diff --git a/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp b/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp new file mode 100644 index 000000000..5e69cc22f --- /dev/null +++ b/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp @@ -0,0 +1,75 @@ +/* + * Support for the ClimateGuard RadSens Dosimeter + * A fun and educational sensor for Meshtastic; not for safety critical applications. + */ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "CGRadSensSensor.h" +#include "TelemetrySensor.h" +#include +#include + +CGRadSensSensor::CGRadSensSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RADSENS, "RadSens") {} + +int32_t CGRadSensSensor::runOnce() +{ + // Initialize the sensor following the same pattern as RCWL9620Sensor + LOG_INFO("Init sensor: %s", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + status = true; + begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first); + + return initI2CSensor(); +} + +void CGRadSensSensor::setup() {} + +void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr) +{ + // Store the Wire and address to the sensor following the same pattern as RCWL9620Sensor + _wire = wire; + _addr = addr; + _wire->begin(); +} + +float CGRadSensSensor::getStaticRadiation() +{ + // Read a register, following the same pattern as the RCWL9620Sensor + uint32_t data; + _wire->beginTransmission(_addr); // Transfer data to addr. + _wire->write(0x06); // Radiation intensity (static period T = 500 sec) + if (_wire->endTransmission() == 0) { + if (_wire->requestFrom(_addr, (uint8_t)3)) { + ; // Request 3 bytes + data = _wire->read(); + data <<= 8; + data |= _wire->read(); + data <<= 8; + data |= _wire->read(); + + // As per the data sheet for the RadSens + // Register 0x06 contains the reading in 0.1 * μR / h + float microRadPerHr = float(data) / 10.0; + return microRadPerHr; + } + } + return -1.0; +} + +bool CGRadSensSensor::getMetrics(meshtastic_Telemetry *measurement) +{ + // Store the meansurement in the the appropriate fields of the protobuf + measurement->variant.environment_metrics.has_radiation = true; + + LOG_DEBUG("CGRADSENS getMetrics"); + measurement->variant.environment_metrics.radiation = getStaticRadiation(); + + return true; +} +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/CGRadSensSensor.h b/src/modules/Telemetry/Sensor/CGRadSensSensor.h new file mode 100644 index 000000000..3b15a19a2 --- /dev/null +++ b/src/modules/Telemetry/Sensor/CGRadSensSensor.h @@ -0,0 +1,30 @@ +/* + * Support for the ClimateGuard RadSens Dosimeter + * A fun and educational sensor for Meshtastic; not for safety critical applications. + */ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class CGRadSensSensor : public TelemetrySensor +{ + private: + uint8_t _addr = 0x66; + TwoWire *_wire = &Wire; + + protected: + virtual void setup() override; + void begin(TwoWire *wire = &Wire, uint8_t addr = 0x66); + float getStaticRadiation(); + + public: + CGRadSensSensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; +}; + +#endif \ No newline at end of file diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index 05a98f7a6..b4603186b 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -78,6 +78,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction); msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust); msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull); + msgPayload["radiation"] = new JSONValue(decoded->variant.environment_metrics.radiation); } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { msgPayload["pm10"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_standard); msgPayload["pm25"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_standard); diff --git a/src/serialization/MeshPacketSerializer_nRF52.cpp b/src/serialization/MeshPacketSerializer_nRF52.cpp index 15b8b1a34..89ecddfad 100644 --- a/src/serialization/MeshPacketSerializer_nRF52.cpp +++ b/src/serialization/MeshPacketSerializer_nRF52.cpp @@ -77,6 +77,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction; jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust; jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull; + jsonObj["payload"]["radiation"] = decoded->variant.environment_metrics.radiation; } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard; jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard;