Add one wire i2c bridge support (#10078)

* First version of DS248X bridge

* Add first iteration of DS248X sensor

* Supports single readings on DS2484
* Supports readings on ch0 for DS2484_800
* Detection of variant for DS248X

* Minor fix on retries for sensor init

* Allow multiple channel detect passes on 8-ch version

* Always read temperature via ROM matching

* Small comment to show how to send all channels

* Minor logging changes

* Prevent one-wire double definitions

* Detect ROMs per round

* Fix comment

* Prevent skipping on DS2482 ALT3 check

* Fix comment (again)

* Fix style  checks

* Remove comment for multiple measurements

* Address CodeRabbit review findings on DS248X sensor

Set _variant on every detectVariant path and branch on the member, so a
failed variant probe retries instead of falling into single-channel init.

Search DS2482-800 channels into a scratch buffer so a transient one-wire
failure cannot erase a ROM found on an earlier pass, and count channels
that already hold a ROM.

Check every one-wire return value in readTemperatureROM, validate the
scratchpad CRC, and return DS248X_INVALID_TEMPERATURE on failure so the
existing sentinel checks reject failed reads instead of reporting stale
or uninitialised data.

* Probe IIS2MDCTR WHO_AM_I before the DS248X status check

At HMC5883L_ADDR the DS248X probe reads 0xF0. On the IIS2MDCTR that
sub-address sets auto-increment and targets 0x70, which is reserved and
returns an unspecified value; any of bits 0x02, 0x04 or 0x10 makes the
probe claim the magnetometer as a DS2482.

Reading the WHO_AM_I at 0x4F first is deterministic for both parts. A
DS2482 does not acknowledge 0x4F, an invalid command code, and leaves
its read pointer untouched, so the subsequent read returns Status,
Configuration, Channel Selection or Read Data. None of those can hold
0x40 at scan time, so the DS248X probe still runs and detects it.

This also restores develop's detection order and matches the structure
already used at BMA423_ADDR: specific ID match, then probe, then the
generic fallback.

* Read only the reported channel in getMetrics

getMetrics walked all eight DS2482-800 channels, but only channel 0 is
ever written into the measurement. Each populated channel costs a
blocking 750ms conversion inside readTemperatureROM, so a fully wired
bridge stalled telemetry for roughly 6s per cycle and discarded seven of
the eight readings.

Channels without a sensor were already cheap thanks to the isValidROM
guard, so this only affects boards that actually use more than one
channel, which is the reason to fit a DS2482-800 in the first place.

Multi-channel reporting is handled separately in #10192; a note records
that it should start the conversion on every channel before waiting,
rather than reading each channel end to end.

* Trim DS248X comments to one line each

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
This commit is contained in:
oscgonfer
2026-08-03 13:52:02 +00:00
committed by GitHub
co-authored by Thomas Göttgens
parent 58657d484a
commit 8c0ef44b0b
7 changed files with 486 additions and 11 deletions
+2
View File
@@ -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]
+8
View File
@@ -295,6 +295,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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
+1
View File
@@ -103,6 +103,7 @@ class ScanI2C
IIS2MDCTR,
ISM330DHCX,
SPA06,
DS248X,
} DeviceType;
// typedef uint8_t DeviceAddress;
+84 -11
View File
@@ -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
@@ -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(<Adafruit_DS248x.h>)
#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<SHTXXSensor>(i2cScanner, ScanI2C::DeviceType::SHTXX);
#endif
#if __has_include(<Adafruit_DS248x.h>)
addSensor<DS248XSensor>(i2cScanner, ScanI2C::DeviceType::DS248X);
#endif
#endif
}
@@ -0,0 +1,299 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_DS248x.h>)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "DS248XSensor.h"
#include "TelemetrySensor.h"
#include <Adafruit_DS248x.h>
// 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
@@ -0,0 +1,84 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_DS248x.h>)
#include "../detect/ReClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_DS248x.h>
#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