diff --git a/platformio.ini b/platformio.ini index 4b67f5303..aef78a858 100644 --- a/platformio.ini +++ b/platformio.ini @@ -233,6 +233,8 @@ lib_deps = closedcube/ClosedCube OPT3001@1.1.2 # 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 + # 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] diff --git a/src/configuration.h b/src/configuration.h index ee5f55cbe..ccf4ba41f 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -295,6 +295,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 87965245a..491038878 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -103,6 +103,7 @@ class ScanI2C IIS2MDCTR, ISM330DHCX, SPA06, + DS248X, } DeviceType; // typedef uint8_t DeviceAddress; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 3ad42ab04..5f04755e6 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -412,8 +412,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); @@ -578,7 +587,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) { @@ -592,6 +600,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; } @@ -636,7 +652,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 @@ -666,16 +702,25 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L", (uint8_t)addr.address) case HMC5883L_ADDR: + // WHO_AM_I first: the DS248X probe below reads a reserved address on the IIS2MDCTR registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x4FU), 1); // get ID if (registerValue == 0x40) { type = IIS2MDCTR; logFoundDevice("IIS2MDCTR", (uint8_t)addr.address); break; - } else { - type = HMC5883L; - logFoundDevice("HMC5883L", (uint8_t)addr.address); + } + + // 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 @@ -698,14 +743,25 @@ 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; } else if ((registerValue & 0xFF00) == 0x1100) { // Silan SC7A20: LIS3DH register map, but answers 0x11 here. type = SC7A20; logFoundDevice("SC7A20", (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: @@ -753,11 +809,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,CST3530 { // T-Deck pro V1.1 new touch panel use CST3530 diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index b343b7b46..c2c819094 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 @@ -239,6 +243,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 000000000..c98d2d0e3 --- /dev/null +++ b/src/modules/Telemetry/Sensor/DS248XSensor.cpp @@ -0,0 +1,299 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "DS248XSensor.h" +#include "TelemetrySensor.h" +#include + +// Dallas/Maxim CRC8 (reflected polynomial 0x8C), used to validate a DS18B20 scratchpad +static uint8_t ds18b20CRC8(const uint8_t *data, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *data++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) { + crc ^= 0x8C; + } + inbyte >>= 1; + } + } + + return crc; +} + +DS248XSensor::DS248XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DS248X, "DS248X") {} + +ds248x_variant_t DS248XSensor::detectVariant() +{ + + // Wait until idle + if (!ds248x.busyWait(1000)) { + _variant = DS248X_UNKNOWN; + return _variant; + } + + // 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; + _port = dev->address.port; + LOG_INFO("Init sensor: %s", sensorName); + +#ifdef DS248X_I2C_CLOCK_SPEED + reClockI2C.setup(_bus, _port); + + LOG_INFO("%s: attempting to reclock speed to %uHz", sensorName, DS248X_I2C_CLOCK_SPEED); + reClockI2C.setClock(DS248X_I2C_CLOCK_SPEED); +#endif /* DS248X_I2C_CLOCK_SPEED */ + + if (!ds248x.begin(bus, _address)) { +#ifdef DS248X_I2C_CLOCK_SPEED + reClockI2C.restoreClock(); +#endif /* DS248X_I2C_CLOCK_SPEED */ + 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; + + detectVariant(); + + if (_variant == 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(); + + // Scratch buffer: a failed pass must not erase a ROM found on an earlier one + uint8_t foundROM[8]{}; + if (ds248x.OneWireSearch(foundROM)) { + memcpy(ds2482800Data.ds248xData[channel].rom, foundROM, sizeof(foundROM)); + LOG_INFO("%s: One-wire rom detected on channel %u (%u/%u)", sensorName, channel, retry, numRetries); + printROM(ds2482800Data.ds248xData[channel].rom); + } else { + LOG_DEBUG("%s: no one-wire rom detected on channel %u (%u/%u)", sensorName, channel, retry, numRetries); + } + + } else { + LOG_WARN("%s: Failed to select channel %u", sensorName, channel); + } + + // Count every channel holding a ROM, including ones carried over from an earlier pass + if (isValidROM(ds2482800Data.ds248xData[channel].rom)) { + nROMDetected += 1; + } + } + + if (!nROMDetected) { + initError = true; + } + + } else if (_variant == DS248X_DS2484) { + 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); + } + } else { + LOG_WARN("%s: Could not determine variant (%u/%u)", sensorName, retry, numRetries); + initError = true; + } + + if (initError && retry == numRetries) { +#ifdef DS248X_I2C_CLOCK_SPEED + reClockI2C.restoreClock(); +#endif /* DS248X_I2C_CLOCK_SPEED */ + 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); + } + +#ifdef DS248X_I2C_CLOCK_SPEED + LOG_INFO("%s: restoring clock speed", sensorName); + reClockI2C.restoreClock(); +#endif /* DS248X_I2C_CLOCK_SPEED */ + + 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 + LOG_DEBUG("%s: attempting to reclock speed to %uHz", sensorName, DS248X_I2C_CLOCK_SPEED); + reClockI2C.setClock(DS248X_I2C_CLOCK_SPEED); +#endif /* DS248X_I2C_CLOCK_SPEED */ + + uint8_t data[9]{}; + + // Select the DS18B20 device + bool ok = ds248x.OneWireReset() && ds248x.OneWireWriteByte(DS18B20_CMD_MATCH_ROM); // Match ROM command + for (int i = 0; ok && i < 8; i++) { + ok = ds248x.OneWireWriteByte(rom[i]); + } + + // Start temperature conversion + ok = ok && ds248x.OneWireWriteByte(DS18B20_CMD_CONVERT_T); // Convert T command + + if (ok) { + delay(750); // Wait for conversion (750ms for maximum precision) + + // Read scratchpad + ok = ds248x.OneWireReset() && ds248x.OneWireWriteByte(DS18B20_CMD_MATCH_ROM); // Match ROM command + for (int i = 0; ok && i < 8; i++) { + ok = ds248x.OneWireWriteByte(rom[i]); + } + ok = ok && ds248x.OneWireWriteByte(DS18B20_CMD_READ_SCRATCHPAD); // Read Scratchpad command + + for (int i = 0; ok && i < 9; i++) { + ok = ds248x.OneWireReadByte(&data[i]); + } + } + +#ifdef DS248X_I2C_CLOCK_SPEED + LOG_DEBUG("%s: restoring clock speed", sensorName); + reClockI2C.restoreClock(); +#endif /* DS248X_I2C_CLOCK_SPEED */ + + if (!ok) { + LOG_WARN("%s: One-wire transaction failed", sensorName); + return DS248X_INVALID_TEMPERATURE; + } + + // The scratchpad ends with a CRC8 over its first eight bytes + if (ds18b20CRC8(data, 8) != data[8]) { + LOG_WARN("%s: Scratchpad CRC mismatch", sensorName); + return DS248X_INVALID_TEMPERATURE; + } + + // 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 == DS248X_INVALID_TEMPERATURE) { + LOG_WARN("%s: Failed to read temperature in channel %u", sensorName, channel); + return false; + } + + 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 != DS248X_INVALID_TEMPERATURE) { + 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) { + // Only ch0 is reported, and each populated channel blocks 750ms on its conversion + // TODO Support more than one temperature via repeated (3.0) + // TODO Select which channel can be reported as main temperature + if (readTemperatureChannel(0)) { + measurement->variant.environment_metrics.temperature = ds2482800Data.ds248xData[0].temperature; + measurement->variant.environment_metrics.has_temperature = true; + LOG_DEBUG("Got %s readings: temperature=%.2f", sensorName, measurement->variant.environment_metrics.temperature); + return true; + } + return false; + } + 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 000000000..bfe18a035 --- /dev/null +++ b/src/modules/Telemetry/Sensor/DS248XSensor.h @@ -0,0 +1,84 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include() + +#include "../detect/ReClockI2C.h" +#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 + +// Returned by readTemperatureROM when the transaction fails or the scratchpad CRC mismatches +#ifndef DS248X_INVALID_TEMPERATURE +#define DS248X_INVALID_TEMPERATURE -1000.0f +#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; + ds248x_variant_t _variant = DS248X_UNKNOWN; + _DS248XData ds248xData{}; + _DS2482800Data ds2482800Data{}; +#ifdef DS248X_I2C_CLOCK_SPEED + ReClockI2C reClockI2C; +#endif + 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