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