From b9391f2cd415172b3ac697fd55ae322094d41006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Kry=C5=84ski?= Date: Wed, 2 Jul 2025 23:15:37 +0200 Subject: [PATCH] [ds2484] New component (#9147) --- CODEOWNERS | 1 + esphome/components/ds2484/__init__.py | 1 + esphome/components/ds2484/ds2484.cpp | 209 ++++++++++++++++++ esphome/components/ds2484/ds2484.h | 43 ++++ esphome/components/ds2484/one_wire.py | 37 ++++ tests/components/ds2484/common.yaml | 11 + tests/components/ds2484/test.esp32-ard.yaml | 5 + .../components/ds2484/test.esp32-c3-ard.yaml | 5 + .../components/ds2484/test.esp32-c3-idf.yaml | 5 + tests/components/ds2484/test.esp32-idf.yaml | 5 + tests/components/ds2484/test.esp8266-ard.yaml | 5 + tests/components/ds2484/test.rp2040-ard.yaml | 5 + 12 files changed, 332 insertions(+) create mode 100644 esphome/components/ds2484/__init__.py create mode 100644 esphome/components/ds2484/ds2484.cpp create mode 100644 esphome/components/ds2484/ds2484.h create mode 100644 esphome/components/ds2484/one_wire.py create mode 100644 tests/components/ds2484/common.yaml create mode 100644 tests/components/ds2484/test.esp32-ard.yaml create mode 100644 tests/components/ds2484/test.esp32-c3-ard.yaml create mode 100644 tests/components/ds2484/test.esp32-c3-idf.yaml create mode 100644 tests/components/ds2484/test.esp32-idf.yaml create mode 100644 tests/components/ds2484/test.esp8266-ard.yaml create mode 100644 tests/components/ds2484/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 16f38da725..295dd9b1b2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -124,6 +124,7 @@ esphome/components/dht/* @OttoWinter esphome/components/display_menu_base/* @numo68 esphome/components/dps310/* @kbx81 esphome/components/ds1307/* @badbadc0ffee +esphome/components/ds2484/* @mrk-its esphome/components/dsmr/* @glmnet @zuidwijk esphome/components/duty_time/* @dudanov esphome/components/ee895/* @Stock-M diff --git a/esphome/components/ds2484/__init__.py b/esphome/components/ds2484/__init__.py new file mode 100644 index 0000000000..3d9f24ff19 --- /dev/null +++ b/esphome/components/ds2484/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@mrk-its"] diff --git a/esphome/components/ds2484/ds2484.cpp b/esphome/components/ds2484/ds2484.cpp new file mode 100644 index 0000000000..c3df9786b6 --- /dev/null +++ b/esphome/components/ds2484/ds2484.cpp @@ -0,0 +1,209 @@ +#include "ds2484.h" + +namespace esphome { +namespace ds2484 { +static const char *const TAG = "ds2484.onewire"; + +void DS2484OneWireBus::setup() { + ESP_LOGCONFIG(TAG, "Running setup"); + this->reset_device(); + this->search(); +} + +void DS2484OneWireBus::dump_config() { + ESP_LOGCONFIG(TAG, "1-wire bus:"); + this->dump_devices_(TAG); +} + +bool DS2484OneWireBus::read_status_(uint8_t *status) { + for (uint8_t retry_nr = 0; retry_nr < 10; retry_nr++) { + if (this->read(status, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "read status error"); + return false; + } + ESP_LOGVV(TAG, "status: %02x", *status); + if (!(*status & 1)) { + return true; + } + } + ESP_LOGE(TAG, "read status error: too many retries"); + return false; +} + +bool DS2484OneWireBus::wait_for_completion_() { + uint8_t status; + return this->read_status_(&status); +} + +bool DS2484OneWireBus::reset_device() { + ESP_LOGVV(TAG, "reset_device"); + uint8_t device_reset_cmd = 0xf0; + uint8_t response; + if (this->write(&device_reset_cmd, 1) != i2c::ERROR_OK) { + return false; + } + if (!this->wait_for_completion_()) { + ESP_LOGE(TAG, "reset_device: can't complete"); + return false; + } + uint8_t config = (this->active_pullup_ ? 1 : 0) | (this->strong_pullup_ ? 4 : 0); + uint8_t write_config[2] = {0xd2, (uint8_t) (config | (~config << 4))}; + if (this->write(write_config, 2) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "reset_device: can't write config"); + return false; + } + if (this->read(&response, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "can't read read8 response"); + return false; + } + if (response != (write_config[1] & 0xf)) { + ESP_LOGE(TAG, "configuration didn't update"); + return false; + } + return true; +}; + +int DS2484OneWireBus::reset_int() { + ESP_LOGVV(TAG, "reset"); + uint8_t reset_cmd = 0xb4; + if (this->write(&reset_cmd, 1) != i2c::ERROR_OK) { + return -1; + } + return this->wait_for_completion_() ? 1 : 0; +}; + +void DS2484OneWireBus::write8_(uint8_t value) { + uint8_t buffer[2] = {0xa5, value}; + this->write(buffer, 2); + this->wait_for_completion_(); +}; + +void DS2484OneWireBus::write8(uint8_t value) { + ESP_LOGVV(TAG, "write8: %02x", value); + this->write8_(value); +}; + +void DS2484OneWireBus::write64(uint64_t value) { + ESP_LOGVV(TAG, "write64: %llx", value); + for (uint8_t i = 0; i < 8; i++) { + this->write8_((value >> (i * 8)) & 0xff); + } +} + +uint8_t DS2484OneWireBus::read8() { + uint8_t read8_cmd = 0x96; + uint8_t set_read_reg_cmd[2] = {0xe1, 0xe1}; + uint8_t response = 0; + if (this->write(&read8_cmd, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "can't write read8 cmd"); + return 0; + } + this->wait_for_completion_(); + if (this->write(set_read_reg_cmd, 2) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "can't set read data reg"); + return 0; + } + if (this->read(&response, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "can't read read8 response"); + return 0; + } + return response; +} + +uint64_t DS2484OneWireBus::read64() { + uint8_t response = 0; + for (uint8_t i = 0; i < 8; i++) { + response |= (this->read8() << (i * 8)); + } + return response; +} + +void DS2484OneWireBus::reset_search() { + this->last_discrepancy_ = 0; + this->last_device_flag_ = false; + this->address_ = 0; +} + +bool DS2484OneWireBus::one_wire_triple_(bool *branch, bool *id_bit, bool *cmp_id_bit) { + uint8_t buffer[2] = {(uint8_t) 0x78, (uint8_t) (*branch ? 0x80u : 0)}; + uint8_t status; + if (!this->read_status_(&status)) { + ESP_LOGE(TAG, "one_wire_triple start: read status error"); + return false; + } + if (this->write(buffer, 2) != i2c::ERROR_OK) { + ESP_LOGV(TAG, "one_wire_triple: can't write cmd"); + return false; + } + if (!this->read_status_(&status)) { + ESP_LOGE(TAG, "one_wire_triple: read status error"); + return false; + } + *id_bit = bool(status & 0x20); + *cmp_id_bit = bool(status & 0x40); + *branch = bool(status & 0x80); + return true; +} + +uint64_t IRAM_ATTR DS2484OneWireBus::search_int() { + ESP_LOGVV(TAG, "search_int"); + if (this->last_device_flag_) { + ESP_LOGVV(TAG, "last device flag set, quitting"); + return 0u; + } + + uint8_t last_zero = 0; + uint64_t bit_mask = 1; + uint64_t address = this->address_; + + // Initiate search + for (uint8_t bit_number = 1; bit_number <= 64; bit_number++, bit_mask <<= 1) { + bool branch; + + // compute branch value for the case when there is a discrepancy + // (there are devices with both 0s and 1s at this bit) + if (bit_number < this->last_discrepancy_) { + branch = (address & bit_mask) > 0; + } else { + branch = bit_number == this->last_discrepancy_; + } + + bool id_bit, cmp_id_bit; + bool branch_before = branch; + if (!this->one_wire_triple_(&branch, &id_bit, &cmp_id_bit)) { + ESP_LOGW(TAG, "one wire triple error, quitting"); + return 0; + } + + if (id_bit && cmp_id_bit) { + ESP_LOGW(TAG, "no devices on the bus, quitting"); + // No devices participating in search + return 0; + } + + if (!id_bit && !cmp_id_bit && !branch) { + last_zero = bit_number; + } + + ESP_LOGVV(TAG, "%d %d branch: %d %d", id_bit, cmp_id_bit, branch_before, branch); + + if (branch) { + address |= bit_mask; + } else { + address &= ~bit_mask; + } + } + ESP_LOGVV(TAG, "last_discepancy: %d", last_zero); + ESP_LOGVV(TAG, "address: %llx", address); + this->last_discrepancy_ = last_zero; + if (this->last_discrepancy_ == 0) { + // we're at root and have no choices left, so this was the last one. + this->last_device_flag_ = true; + } + + this->address_ = address; + return address; +} + +} // namespace ds2484 +} // namespace esphome diff --git a/esphome/components/ds2484/ds2484.h b/esphome/components/ds2484/ds2484.h new file mode 100644 index 0000000000..223227c0a2 --- /dev/null +++ b/esphome/components/ds2484/ds2484.h @@ -0,0 +1,43 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/preferences.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/one_wire/one_wire.h" + +namespace esphome { +namespace ds2484 { + +class DS2484OneWireBus : public one_wire::OneWireBus, public i2c::I2CDevice, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS - 1.0; } + + bool reset_device(); + int reset_int() override; + void write8(uint8_t) override; + void write64(uint64_t) override; + uint8_t read8() override; + uint64_t read64() override; + + void set_active_pullup(bool value) { this->active_pullup_ = value; } + void set_strong_pullup(bool value) { this->strong_pullup_ = value; } + + protected: + void reset_search() override; + uint64_t search_int() override; + bool read_status_(uint8_t *); + bool wait_for_completion_(); + void write8_(uint8_t); + bool one_wire_triple_(bool *branch, bool *id_bit, bool *cmp_id_bit); + + uint64_t address_; + uint8_t last_discrepancy_{0}; + bool last_device_flag_{false}; + bool active_pullup_{false}; + bool strong_pullup_{false}; +}; +} // namespace ds2484 +} // namespace esphome diff --git a/esphome/components/ds2484/one_wire.py b/esphome/components/ds2484/one_wire.py new file mode 100644 index 0000000000..384b2d01e6 --- /dev/null +++ b/esphome/components/ds2484/one_wire.py @@ -0,0 +1,37 @@ +import esphome.codegen as cg +from esphome.components import i2c +from esphome.components.one_wire import OneWireBus +import esphome.config_validation as cv +from esphome.const import CONF_ID + +ds2484_ns = cg.esphome_ns.namespace("ds2484") + +CONF_ACTIVE_PULLUP = "active_pullup" +CONF_STRONG_PULLUP = "strong_pullup" + +CODEOWNERS = ["@mrk-its"] +DEPENDENCIES = ["i2c"] + +DS2484OneWireBus = ds2484_ns.class_( + "DS2484OneWireBus", OneWireBus, i2c.I2CDevice, cg.Component +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DS2484OneWireBus), + cv.Optional(CONF_ACTIVE_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_STRONG_PULLUP, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x18)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await i2c.register_i2c_device(var, config) + await cg.register_component(var, config) + cg.add(var.set_active_pullup(config[CONF_ACTIVE_PULLUP])) + cg.add(var.set_strong_pullup(config[CONF_STRONG_PULLUP])) diff --git a/tests/components/ds2484/common.yaml b/tests/components/ds2484/common.yaml new file mode 100644 index 0000000000..9d2882a3c0 --- /dev/null +++ b/tests/components/ds2484/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_ds2484 + scl: ${scl_pin} + sda: ${sda_pin} + +one_wire: + platform: ds2484 + i2c_id: i2c_ds2484 + address: 0x18 + active_pullup: true + strong_pullup: false diff --git a/tests/components/ds2484/test.esp32-ard.yaml b/tests/components/ds2484/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/ds2484/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/ds2484/test.esp32-c3-ard.yaml b/tests/components/ds2484/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/ds2484/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ds2484/test.esp32-c3-idf.yaml b/tests/components/ds2484/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/ds2484/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ds2484/test.esp32-idf.yaml b/tests/components/ds2484/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/ds2484/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/ds2484/test.esp8266-ard.yaml b/tests/components/ds2484/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/ds2484/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ds2484/test.rp2040-ard.yaml b/tests/components/ds2484/test.rp2040-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/ds2484/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml