1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-02 01:52:21 +01:00

change to new 1-wire platform (#6860)

Co-authored-by: Samuel Sieb <samuel@sieb.net>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Samuel Sieb
2024-06-11 18:05:44 -07:00
committed by GitHub
parent 7b60543afd
commit 13fabf1cd8
28 changed files with 802 additions and 773 deletions

View File

@@ -0,0 +1,25 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_ID, CONF_PIN
from esphome.components.one_wire import OneWireBus
from .. import gpio_ns
CODEOWNERS = ["@ssieb"]
GPIOOneWireBus = gpio_ns.class_("GPIOOneWireBus", OneWireBus, cg.Component)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(GPIOOneWireBus),
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))

View File

@@ -0,0 +1,199 @@
#include "gpio_one_wire.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace gpio {
static const char *const TAG = "gpio.one_wire";
void GPIOOneWireBus::setup() {
ESP_LOGCONFIG(TAG, "Setting up 1-wire bus...");
this->search();
}
void GPIOOneWireBus::dump_config() {
ESP_LOGCONFIG(TAG, "GPIO 1-wire bus:");
LOG_PIN(" Pin: ", this->t_pin_);
this->dump_devices_(TAG);
}
bool HOT IRAM_ATTR GPIOOneWireBus::reset() {
// See reset here:
// https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
// Wait for communication to clear (delay G)
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
uint8_t retries = 125;
do {
if (--retries == 0)
return false;
delayMicroseconds(2);
} while (!pin_.digital_read());
bool r;
// Send 480µs LOW TX reset pulse (drive bus low, delay H)
pin_.pin_mode(gpio::FLAG_OUTPUT);
pin_.digital_write(false);
delayMicroseconds(480);
// Release the bus, delay I
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
delayMicroseconds(70);
// sample bus, 0=device(s) present, 1=no device present
r = !pin_.digital_read();
// delay J
delayMicroseconds(410);
return r;
}
void HOT IRAM_ATTR GPIOOneWireBus::write_bit_(bool bit) {
// drive bus low
pin_.pin_mode(gpio::FLAG_OUTPUT);
pin_.digital_write(false);
// from datasheet:
// write 0 low time: t_low0: min=60µs, max=120µs
// write 1 low time: t_low1: min=1µs, max=15µs
// time slot: t_slot: min=60µs, max=120µs
// recovery time: t_rec: min=1µs
// ds18b20 appears to read the bus after roughly 14µs
uint32_t delay0 = bit ? 6 : 60;
uint32_t delay1 = bit ? 54 : 5;
// delay A/C
delayMicroseconds(delay0);
// release bus
pin_.digital_write(true);
// delay B/D
delayMicroseconds(delay1);
}
bool HOT IRAM_ATTR GPIOOneWireBus::read_bit_() {
// drive bus low
pin_.pin_mode(gpio::FLAG_OUTPUT);
pin_.digital_write(false);
// note: for reading we'll need very accurate timing, as the
// timing for the digital_read() is tight; according to the datasheet,
// we should read at the end of 16µs starting from the bus low
// typically, the ds18b20 pulls the line high after 11µs for a logical 1
// and 29µs for a logical 0
uint32_t start = micros();
// datasheet says >1µs
delayMicroseconds(2);
// release bus, delay E
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
// measure from start value directly, to get best accurate timing no matter
// how long pin_mode/delayMicroseconds took
delayMicroseconds(12 - (micros() - start));
// sample bus to read bit from peer
bool r = pin_.digital_read();
// read slot is at least 60µs; get as close to 60µs to spend less time with interrupts locked
uint32_t now = micros();
if (now - start < 60)
delayMicroseconds(60 - (now - start));
return r;
}
void IRAM_ATTR GPIOOneWireBus::write8(uint8_t val) {
for (uint8_t i = 0; i < 8; i++) {
this->write_bit_(bool((1u << i) & val));
}
}
void IRAM_ATTR GPIOOneWireBus::write64(uint64_t val) {
for (uint8_t i = 0; i < 64; i++) {
this->write_bit_(bool((1ULL << i) & val));
}
}
uint8_t IRAM_ATTR GPIOOneWireBus::read8() {
uint8_t ret = 0;
for (uint8_t i = 0; i < 8; i++) {
ret |= (uint8_t(this->read_bit_()) << i);
}
return ret;
}
uint64_t IRAM_ATTR GPIOOneWireBus::read64() {
uint64_t ret = 0;
for (uint8_t i = 0; i < 8; i++) {
ret |= (uint64_t(this->read_bit_()) << i);
}
return ret;
}
void GPIOOneWireBus::reset_search() {
this->last_discrepancy_ = 0;
this->last_device_flag_ = false;
this->address_ = 0;
}
uint64_t IRAM_ATTR GPIOOneWireBus::search_int() {
if (this->last_device_flag_)
return 0u;
uint8_t last_zero = 0;
uint64_t bit_mask = 1;
uint64_t address = this->address_;
// Initiate search
for (int bit_number = 1; bit_number <= 64; bit_number++, bit_mask <<= 1) {
// read bit
bool id_bit = this->read_bit_();
// read its complement
bool cmp_id_bit = this->read_bit_();
if (id_bit && cmp_id_bit) {
// No devices participating in search
return 0;
}
bool branch;
if (id_bit != cmp_id_bit) {
// only chose one branch, the other one doesn't have any devices.
branch = id_bit;
} else {
// 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_;
}
if (!branch) {
last_zero = bit_number;
}
}
if (branch) {
address |= bit_mask;
} else {
address &= ~bit_mask;
}
// choose/announce branch
this->write_bit_(branch);
}
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 gpio
} // namespace esphome

View File

@@ -0,0 +1,41 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/one_wire/one_wire.h"
namespace esphome {
namespace gpio {
class GPIOOneWireBus : public one_wire::OneWireBus, public Component {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::BUS; }
void set_pin(InternalGPIOPin *pin) {
this->t_pin_ = pin;
this->pin_ = pin->to_isr();
}
bool reset() override;
void write8(uint8_t val) override;
void write64(uint64_t val) override;
uint8_t read8() override;
uint64_t read64() override;
protected:
InternalGPIOPin *t_pin_;
ISRInternalGPIOPin pin_;
uint8_t last_discrepancy_{0};
bool last_device_flag_{false};
uint64_t address_;
void reset_search() override;
uint64_t search_int() override;
void write_bit_(bool bit);
bool read_bit_();
};
} // namespace gpio
} // namespace esphome