#include "gpio_one_wire.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { namespace gpio { static const char *const TAG = "gpio.one_wire"; void GPIOOneWireBus::setup() { this->t_pin_->setup(); this->t_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); // clear bus with 480µs high, otherwise initial reset in search might fail this->pin_.digital_write(true); this->pin_.pin_mode(gpio::FLAG_OUTPUT); delayMicroseconds(480); this->search(); } void GPIOOneWireBus::dump_config() { ESP_LOGCONFIG(TAG, "GPIO 1-wire bus:"); LOG_PIN(" Pin: ", this->t_pin_); this->dump_devices_(TAG); } int HOT IRAM_ATTR GPIOOneWireBus::reset_int() { InterruptLock lock; // See reset here: // https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html // Wait for communication to clear (delay G) this->pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); uint8_t retries = 125; do { if (--retries == 0) return -1; delayMicroseconds(2); } while (!this->pin_.digital_read()); bool r = false; // Send 480µs LOW TX reset pulse (drive bus low, delay H) this->pin_.digital_write(false); this->pin_.pin_mode(gpio::FLAG_OUTPUT); delayMicroseconds(480); // Release the bus, delay I this->pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); uint32_t start = micros(); delayMicroseconds(30); while (micros() - start < 300) { // sample bus, 0=device(s) present, 1=no device present r = !this->pin_.digital_read(); if (r) break; delayMicroseconds(1); } // delay J delayMicroseconds(start + 480 - micros()); this->pin_.digital_write(true); this->pin_.pin_mode(gpio::FLAG_OUTPUT); return r ? 1 : 0; } void HOT IRAM_ATTR GPIOOneWireBus::write_bit_(bool bit) { // drive bus low this->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 ? 64 : 10; // delay A/C delayMicroseconds(delay0); // release bus this->pin_.digital_write(true); // delay B/D delayMicroseconds(delay1); } bool HOT IRAM_ATTR GPIOOneWireBus::read_bit_() { // drive bus low this->pin_.digital_write(false); // datasheet says >= 1µs delayMicroseconds(5); // release bus, delay E this->pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); delayMicroseconds(8); // sample bus to read bit from peer bool r = this->pin_.digital_read(); // read slot is at least 60µs delayMicroseconds(50); this->pin_.digital_write(true); this->pin_.pin_mode(gpio::FLAG_OUTPUT); return r; } void IRAM_ATTR GPIOOneWireBus::write8(uint8_t val) { InterruptLock lock; for (uint8_t i = 0; i < 8; i++) { this->write_bit_(bool((1u << i) & val)); } } void IRAM_ATTR GPIOOneWireBus::write64(uint64_t val) { InterruptLock lock; for (uint8_t i = 0; i < 64; i++) { this->write_bit_(bool((1ULL << i) & val)); } } uint8_t IRAM_ATTR GPIOOneWireBus::read8() { InterruptLock lock; 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() { InterruptLock lock; uint64_t ret = 0; for (uint8_t i = 0; i < 64; 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() { InterruptLock lock; 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