diff --git a/platformio.ini b/platformio.ini index 46170fe0981..9cec5613fce 100644 --- a/platformio.ini +++ b/platformio.ini @@ -229,7 +229,9 @@ lib_deps = https://github.com/Sensirion/arduino-i2c-scd30/archive/refs/tags/1.0.0.zip # renovate: datasource=github-tags depName=arduino-sht packageName=sensirion/arduino-sht https://github.com/Sensirion/arduino-sht/archive/refs/tags/v1.2.6.zip - + # renovate: datasource=github-tags depName=Adafruit DS248x packageName=adafruit/Adafruit_DS248x + https://github.com/adafruit/Adafruit_DS248x/archive/refs/tags/1.2.0.zip + ; Environmental sensors with BSEC2 (Bosch proprietary IAQ) [environmental_extra] lib_deps = diff --git a/src/configuration.h b/src/configuration.h index 84dabee4e83..963686b3d93 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -271,6 +271,14 @@ along with this program. If not, see . #define LTR553ALS_ADDR 0x23 #define SEN5X_ADDR 0x69 #define SCD30_ADDR 0x61 +#define DS248X_ADDR 0x18 // same as MCP9808_ADDR, STK8BXX_ADDR and LIS3DH_ADDR +#define DS248X_ADDR_ALT1 0x19 // same as LIS3DH_ADDR_ALT and BMA423_ADDR +#define DS248X_ADDR_ALT2 0x1A // same as CST328_ADDR +#define DS248X_ADDR_ALT3 0x1B +#define DS248X_ADDR_ALT4 0x1C // same as QMC6310U_ADDR +#define DS248X_ADDR_ALT5 0x1D // same as DFROBOT_RAIN_ADDR +#define DS248X_ADDR_ALT6 0x1E // same as HMC5883L_ADDR +#define DS248X_ADDR_ALT7 0x1F // same as BBQ10_KB_ADDR // ----------------------------------------------------------------------------- // ACCELEROMETER diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index d451d394836..67892e7d523 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -92,7 +92,8 @@ class ScanI2C CW2015, SCD30, ADS1115, - SHTXX + SHTXX, + DS248X } DeviceType; // typedef uint8_t DeviceAddress; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 052b2245a1b..ac54fcc5474 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -338,8 +338,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) type = TDECKKB; } break; - SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address); - + case BBQ10_KB_ADDR: + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS2482-800", (uint8_t)addr.address); + break; + } + type = BBQ10KB; + logFoundDevice("BB Q10", (uint8_t)addr.address); + break; SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address); #ifdef HAS_NCP5623 SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623", (uint8_t)addr.address); @@ -478,7 +487,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; } #endif - // Check register 0x07 for 0x0400 response to ID MCP9808 chip. registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2); if (registerValue == 0x0400) { @@ -492,6 +500,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 type = LIS3DH; logFoundDevice("LIS3DH", (uint8_t)addr.address); + break; + } + + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS248X", (uint8_t)addr.address); } break; } @@ -536,7 +552,27 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) } break; SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB", (uint8_t)addr.address) - SCAN_SIMPLE_CASE(QMC6310U_ADDR, QMC6310U, "QMC6310U", (uint8_t)addr.address) + case DS248X_ADDR_ALT3: + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS2482-800", (uint8_t)addr.address); + break; + } + break; + + case QMC6310U_ADDR: + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS2482-800", (uint8_t)addr.address); + break; + } + type = QMC6310U; + logFoundDevice("QMC6310U", (uint8_t)addr.address); + break; case QMI8658_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0A), 1); // get ID @@ -562,7 +598,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L", (uint8_t)addr.address) - SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L", (uint8_t)addr.address) + case HMC5883L_ADDR: + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS2482-800", (uint8_t)addr.address); + break; + } + type = HMC5883L; + logFoundDevice("HMC5883L", (uint8_t)addr.address); + break; #ifdef HAS_QMA6100P SCAN_SIMPLE_CASE(QMA6100P_ADDR, QMA6100P, "QMA6100P", (uint8_t)addr.address) #else @@ -573,10 +619,20 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 type = LIS3DH; logFoundDevice("LIS3DH", (uint8_t)addr.address); - } else { - type = BMA423; - logFoundDevice("BMA423", (uint8_t)addr.address); + break; + } + + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS2482-800", (uint8_t)addr.address); + break; } + + type = BMA423; + logFoundDevice("BMA423", (uint8_t)addr.address); + break; case TCA9535_ADDR: case RAK120352_ADDR: @@ -624,11 +680,28 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address); SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address); SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address); - SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address); + case DFROBOT_RAIN_ADDR: + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS2482-800", (uint8_t)addr.address); + break; + } + type = DFROBOT_RAIN; + logFoundDevice("DFRobot Rain Gauge", (uint8_t)addr.address); + break; SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address); SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address); SCAN_SIMPLE_CASE(SCD30_ADDR, SCD30, "SCD30", (uint8_t)addr.address); case CST328_ADDR: + // Check status register (0xF0) for DS284X status and one-wire reset + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xF0), 1); + if (registerValue & 0x16) { // One-wire reset after power-on + type = DS248X; + logFoundDevice("DS2482-800", (uint8_t)addr.address); + break; + } // Do we have the CST328 or the CST226SE registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1); if (registerValue == 0xA9) { diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 684d408a1cc..db70da31144 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -131,6 +131,10 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c #include "Sensor/BH1750Sensor.h" #endif +#if __has_include() +#include "Sensor/DS248XSensor.h" +#endif + #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true @@ -240,6 +244,10 @@ void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner) // TODO Can we scan for multiple sensors connected on the same bus? addSensor(i2cScanner, ScanI2C::DeviceType::SHTXX); #endif +#if __has_include() + addSensor(i2cScanner, ScanI2C::DeviceType::DS248X); +#endif + #endif } diff --git a/src/modules/Telemetry/Sensor/DS248XSensor.cpp b/src/modules/Telemetry/Sensor/DS248XSensor.cpp new file mode 100644 index 00000000000..79d4e440438 --- /dev/null +++ b/src/modules/Telemetry/Sensor/DS248XSensor.cpp @@ -0,0 +1,314 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../detect/reClockI2C.h" +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "DS248XSensor.h" +#include "TelemetrySensor.h" +#include + +DS248XSensor::DS248XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DS248X, "DS248X") {} + +ds248x_variant_t DS248XSensor::detectVariant() +{ + + // Wait until idle + if (!ds248x.busyWait(1000)) { + return DS248X_UNKNOWN; + } + + // Try Channel Select command (only valid on DS2482-800) + if (!ds248x.selectChannel(0)) { + _variant = DS248X_DS2484; + } else { + _variant = DS248X_DS2482_800; + } + + return _variant; +} + +void DS248XSensor::printROM(const uint8_t *rom) +{ + LOG_INFO("%s: ROM found - %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", sensorName, rom[0], rom[1], rom[2], rom[3], rom[4], + rom[5], rom[6], rom[7]); +} + +bool DS248XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) +{ + _address = dev->address.address; + _bus = bus; + LOG_INFO("Init sensor: %s", sensorName); + +#ifdef DS248X_I2C_CLOCK_SPEED +#ifdef CAN_RECLOCK_I2C + uint32_t currentClock = reClockI2C(DS248X_I2C_CLOCK_SPEED, _bus, false); +#elif !HAS_SCREEN + reClockI2C(DS248X_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 */ +#endif /* DS248X_I2C_CLOCK_SPEED */ + + // #ifdef DS248X_I2C_CLOCK_SPEED + // uint32_t currentClock = reClockI2C(DS248X_I2C_CLOCK_SPEED, _bus, false); + // #endif /* DS248X_I2C_CLOCK_SPEED */ + + if (!ds248x.begin(bus, _address)) { +#if defined(DS248X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) + reClockI2C(currentClock, _bus, false); +#endif + return false; + } + + // Try to init One-Wire with 3 retries. This detects ROMs consistently + // on the second one. + uint8_t numRetries = 3; + uint8_t rom[8]{}; + + for (uint8_t retry = 1; retry <= numRetries; retry++) { + bool initError = false; + uint8_t nROMDetected = 0; + + if (detectVariant() == DS248X_DS2482_800) { + + LOG_INFO("%s: Multi-channel DS2482-800 detected", sensorName); + + for (uint8_t channel = 0; channel < 8; channel++) { + + if (ds248x.selectChannel(channel)) { + + ds248x.OneWireReset(); + + if (!ds248x.OneWireSearch(ds2482800Data.ds248xData[channel].rom)) { + LOG_DEBUG("%s: no one-wire rom detected on channel %u (%u/%u)", sensorName, channel, retry, numRetries); + for (uint8_t i = 0; i < 8; i++) { + ds2482800Data.ds248xData[channel].rom[i] = 0; + } + } else { + LOG_INFO("%s: One-wire rom detected on channel %u (%u/%u)", sensorName, channel, retry, numRetries); + printROM(ds2482800Data.ds248xData[channel].rom); + nROMDetected += 1; + } + + } else { + LOG_WARN("%s: Failed to select channel %u", sensorName, channel); + } + } + + if (!nROMDetected) { + initError = true; + } + + } else { + LOG_INFO("%s: Single-channel DS2484 detected", sensorName); + + if (!ds248x.OneWireReset()) { + LOG_WARN("%s: One-wire reset unsuccessful (%u/%u)", sensorName, retry, numRetries); + initError = true; + } + + if (ds248x.shortDetected()) { + LOG_WARN("%s: One-wire short detected (%u/%u)", sensorName, retry, numRetries); + initError = true; + } + + if (!ds248x.presencePulseDetected()) { + LOG_WARN("%s: One-wire no presence pulse detected (%u/%u)", sensorName, retry, numRetries); + initError = true; + } + + // TODO - This will detect a ROM and will always read the same throughout runtime for the DS2484 + // If someone connects more than one one-wire temperature sensor, currently it will + // only read the first one (we only have one temperature to report) + if (!ds248x.OneWireSearch(ds248xData.rom)) { + LOG_WARN("%s: no one-wire rom detected (%u/%u)", sensorName, retry, numRetries); + initError = true; + } else { + LOG_INFO("%s: One-wire rom detected (%u/%u)", sensorName, retry, numRetries); + printROM(ds248xData.rom); + } + } + + if (initError && retry == numRetries) { +#if defined(DS248X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) + reClockI2C(currentClock, _bus, false); +#endif + LOG_ERROR("%s: Max retries for one-wire init (%u/%u). Aborting", sensorName, retry, numRetries); + return false; + } + + if (!initError) { + LOG_INFO("%s: Started one-wire (%u/%u)", sensorName, retry, numRetries); + status = true; + // We want to keep searching for ROMs on the DS248X_DS2482_800 + // and always do the three passes + if (_variant == ds248x_variant_t::DS248X_DS2484) { + break; + } + } + // TODO Potentially not needed, but taken from Adafruit's library example + delay(500); + } + + initI2CSensor(); + return status; +} + +bool DS248XSensor::isValidROM(const uint8_t *rom) +{ + return (rom[0] || rom[1] || rom[2] || rom[3] || rom[4] || rom[5] || rom[6] || rom[7]); +} + +// Read a one-wire temperature sensor by matching it's ROM +float DS248XSensor::readTemperatureROM(const uint8_t *rom) +{ +#ifdef DS248X_I2C_CLOCK_SPEED +#ifdef CAN_RECLOCK_I2C + uint32_t currentClock = reClockI2C(DS248X_I2C_CLOCK_SPEED, _bus, false); +#elif !HAS_SCREEN + reClockI2C(DS248X_I2C_CLOCK_SPEED, _bus, true); +#else + LOG_WARN("%s can't be used at this clock speed, with a screen", sensorName); + return -1000.0; +#endif /* CAN_RECLOCK_I2C */ +#endif /* DS248X_I2C_CLOCK_SPEED */ + + // #ifdef DS248X_I2C_CLOCK_SPEED + // uint32_t currentClock = reClockI2C(DS248X_I2C_CLOCK_SPEED, _bus, false); + // #endif /* DS248X_I2C_CLOCK_SPEED */ + + // Select the DS18B20 device + ds248x.OneWireReset(); + ds248x.OneWireWriteByte(DS18B20_CMD_MATCH_ROM); // Match ROM command + for (int i = 0; i < 8; i++) { + ds248x.OneWireWriteByte(rom[i]); + } + + // Start temperature conversion + ds248x.OneWireWriteByte(DS18B20_CMD_CONVERT_T); // Convert T command + delay(750); // Wait for conversion (750ms for maximum precision) + + // Read scratchpad + ds248x.OneWireReset(); + ds248x.OneWireWriteByte(DS18B20_CMD_MATCH_ROM); // Match ROM command + for (int i = 0; i < 8; i++) { + ds248x.OneWireWriteByte(rom[i]); + } + ds248x.OneWireWriteByte(DS18B20_CMD_READ_SCRATCHPAD); // Read Scratchpad command + + uint8_t data[9]; + for (int i = 0; i < 9; i++) { + ds248x.OneWireReadByte(&data[i]); + } + +#if defined(DS248X_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) + reClockI2C(currentClock, _bus, false); +#endif + + // Calculate temperature + int16_t raw = (data[1] << 8) | data[0]; + float celsius = (float)raw / 16.0; + + return celsius; +} + +bool DS248XSensor::readTemperatureChannel(uint8_t channel) +{ + if (!isValidROM(ds2482800Data.ds248xData[channel].rom)) { + LOG_DEBUG("%s: No ROM in channel %u", sensorName, channel); + return false; + } + // Select the channel on the DS2482-800 + if (!ds248x.selectChannel(channel)) { + // Handle error if channel selection fails + LOG_WARN("%s: Failed to select channel %u", sensorName, channel); + return false; + } + + float temperature; + temperature = readTemperatureROM(ds2482800Data.ds248xData[channel].rom); + + if (temperature != -1000.0) { + ds2482800Data.ds248xData[channel].temperature = temperature; + LOG_DEBUG("%s: read temperature in channel %u: %0.2f", sensorName, channel, temperature); + } + return true; +} + +bool DS248XSensor::getMetrics(meshtastic_Telemetry *measurement) +{ + if (_variant == ds248x_variant_t::DS248X_DS2484) { + float temperature = readTemperatureROM(ds248xData.rom); + if (temperature != -1000.0) { + measurement->variant.environment_metrics.temperature = temperature; + measurement->variant.environment_metrics.has_temperature = true; + LOG_DEBUG("Got %s readings: temperature=%.2f", sensorName, measurement->variant.environment_metrics.temperature); + return true; + } + } else if (_variant == ds248x_variant_t::DS248X_DS2482_800) { + // If using DS248X_DS2482_800, we read all channels + uint8_t channelCount = 0; + + // Note, the reason why we are using an unpacked version of this message + // (instead of repeated) it's to save space. With repeated, we have to send all + // channels (even if null) or otherwise we don't know where each channel is + // being reported + for (uint8_t channel = 0; channel < 8; channel++) { + if (readTemperatureChannel(channel)) { + channelCount += 1; + + switch (channel) { + case 0: + measurement->variant.environment_metrics.has_one_wire_temperature_ch0 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch0 = + ds2482800Data.ds248xData[channel].temperature; + break; + case 1: + measurement->variant.environment_metrics.has_one_wire_temperature_ch1 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch1 = + ds2482800Data.ds248xData[channel].temperature; + break; + case 2: + measurement->variant.environment_metrics.has_one_wire_temperature_ch2 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch2 = + ds2482800Data.ds248xData[channel].temperature; + break; + case 3: + measurement->variant.environment_metrics.has_one_wire_temperature_ch3 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch3 = + ds2482800Data.ds248xData[channel].temperature; + break; + case 4: + measurement->variant.environment_metrics.has_one_wire_temperature_ch4 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch4 = + ds2482800Data.ds248xData[channel].temperature; + break; + case 5: + measurement->variant.environment_metrics.has_one_wire_temperature_ch5 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch5 = + ds2482800Data.ds248xData[channel].temperature; + break; + case 6: + measurement->variant.environment_metrics.has_one_wire_temperature_ch6 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch6 = + ds2482800Data.ds248xData[channel].temperature; + break; + case 7: + measurement->variant.environment_metrics.has_one_wire_temperature_ch7 = true; + measurement->variant.environment_metrics.one_wire_temperature_ch7 = + ds2482800Data.ds248xData[channel].temperature; + break; + } + + LOG_DEBUG("Got %s readings: temperature_ch%u=%.2f", sensorName, channel, + ds2482800Data.ds248xData[channel].temperature); + } + } + return channelCount > 0; + } + return false; +} + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/DS248XSensor.h b/src/modules/Telemetry/Sensor/DS248XSensor.h new file mode 100644 index 00000000000..54924fef526 --- /dev/null +++ b/src/modules/Telemetry/Sensor/DS248XSensor.h @@ -0,0 +1,77 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +#ifndef DS248X_I2C_CLOCK_SPEED +#define DS248X_I2C_CLOCK_SPEED 400000 +#endif + +#ifndef DS18B20_CMD_SKIP_ROM +#define DS18B20_CMD_SKIP_ROM 0xCC +#endif + +#ifndef DS18B20_CMD_CONVERT_T +#define DS18B20_CMD_CONVERT_T 0x44 +#endif + +#ifndef DS18B20_CMD_READ_SCRATCHPAD +#define DS18B20_CMD_READ_SCRATCHPAD 0xBE +#endif + +#ifndef DS18B20_FAMILY_CODE +#define DS18B20_FAMILY_CODE 0x28 +#endif + +#ifndef DS18B20_CMD_MATCH_ROM +#define DS18B20_CMD_MATCH_ROM 0x55 +#endif + +#ifndef DS248X_CMD_CHANNEL_SELECT +#define DS248X_CMD_CHANNEL_SELECT 0xC3 +#endif + +#ifndef DS248X_REG_CHANNEL +#define DS248X_REG_CHANNEL 0xD2 +#endif + +#ifndef DS248X_CH0 +#define DS248X_CH0 0xF0 +#endif + +typedef enum { DS248X_UNKNOWN = 0, DS248X_DS2484, DS248X_DS2482_800 } ds248x_variant_t; + +struct _DS248XData { + uint8_t rom[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + float temperature; +}; + +struct _DS2482800Data { + _DS248XData ds248xData[8]; +}; + +class DS248XSensor : public TelemetrySensor +{ + private: + Adafruit_DS248x ds248x; + TwoWire *_bus{}; + uint8_t _address{}; + ds248x_variant_t _variant = DS248X_UNKNOWN; + _DS248XData ds248xData{}; + _DS2482800Data ds2482800Data{}; + void printROM(const uint8_t *rom); + bool isValidROM(const uint8_t *rom); + float readTemperatureROM(const uint8_t *rom); + bool readTemperatureChannel(uint8_t channel); + + public: + DS248XSensor(); + ds248x_variant_t detectVariant(); + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; + virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; +}; + +#endif \ No newline at end of file