ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
gpio_one_wire.cpp
Go to the documentation of this file.
1#include "gpio_one_wire.h"
3#include "esphome/core/log.h"
4
5namespace esphome::gpio {
6
7static const char *const TAG = "gpio.one_wire";
8
10 this->t_pin_->setup();
12 // clear bus with 480µs high, otherwise initial reset in search might fail
13 this->pin_.digital_write(true);
16 this->search();
17}
18
20 ESP_LOGCONFIG(TAG, "GPIO 1-wire bus:");
21 LOG_PIN(" Pin: ", this->t_pin_);
22 this->dump_devices_(TAG);
23}
24
25int HOT IRAM_ATTR GPIOOneWireBus::reset_int() {
27 // See reset here:
28 // https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
29 // Wait for communication to clear (delay G)
31 uint8_t retries = 125;
32 do {
33 if (--retries == 0)
34 return -1;
36 } while (!this->pin_.digital_read());
37
38 bool r = false;
39
40 // Send 480µs LOW TX reset pulse (drive bus low, delay H)
41 this->pin_.digital_write(false);
44
45 // Release the bus, delay I
47 uint32_t start = micros();
49
50 while (micros() - start < 300) {
51 // sample bus, 0=device(s) present, 1=no device present
52 r = !this->pin_.digital_read();
53 if (r)
54 break;
56 }
57
58 // delay J: finish the 480us slot, but never spin if it already elapsed
59 // (unsigned wrap here would busy-wait for minutes with interrupts off)
60 uint32_t elapsed = micros() - start;
61 if (elapsed < 480)
62 delayMicroseconds(480 - elapsed);
63 this->pin_.digital_write(true);
65 return r ? 1 : 0;
66}
67
68void HOT IRAM_ATTR GPIOOneWireBus::write_bit_(bool bit) {
69 // drive bus low
70 this->pin_.digital_write(false);
71
72 // from datasheet:
73 // write 0 low time: t_low0: min=60µs, max=120µs
74 // write 1 low time: t_low1: min=1µs, max=15µs
75 // time slot: t_slot: min=60µs, max=120µs
76 // recovery time: t_rec: min=1µs
77 // ds18b20 appears to read the bus after roughly 14µs
78 uint32_t delay0 = bit ? 6 : 60;
79 uint32_t delay1 = bit ? 64 : 10;
80
81 // delay A/C
82 delayMicroseconds(delay0);
83 // release bus
84 this->pin_.digital_write(true);
85 // delay B/D
86 delayMicroseconds(delay1);
87}
88
89bool HOT IRAM_ATTR GPIOOneWireBus::read_bit_() {
90 // drive bus low
91 this->pin_.digital_write(false);
92
93 // datasheet says >= 1µs
95
96 // release bus, delay E
98
100 // sample bus to read bit from peer
101 bool r = this->pin_.digital_read();
102
103 // read slot is at least 60µs
105
106 this->pin_.digital_write(true);
108 return r;
109}
110
111void IRAM_ATTR GPIOOneWireBus::write8(uint8_t val) {
113 for (uint8_t i = 0; i < 8; i++) {
114 this->write_bit_(bool((1u << i) & val));
115 }
116}
117
118void IRAM_ATTR GPIOOneWireBus::write64(uint64_t val) {
120 for (uint8_t i = 0; i < 64; i++) {
121 this->write_bit_(bool((1ULL << i) & val));
122 }
123}
124
125uint8_t IRAM_ATTR GPIOOneWireBus::read8() {
127 uint8_t ret = 0;
128 for (uint8_t i = 0; i < 8; i++)
129 ret |= (uint8_t(this->read_bit_()) << i);
130 return ret;
131}
132
133uint64_t IRAM_ATTR GPIOOneWireBus::read64() {
135 uint64_t ret = 0;
136 for (uint8_t i = 0; i < 64; i++) {
137 ret |= (uint64_t(this->read_bit_()) << i);
138 }
139 return ret;
140}
141
143 this->last_discrepancy_ = 0;
144 this->last_device_flag_ = false;
145 this->address_ = 0;
146}
147
148uint64_t IRAM_ATTR GPIOOneWireBus::search_int() {
150 if (this->last_device_flag_)
151 return 0u;
152
153 uint8_t last_zero = 0;
154 uint64_t bit_mask = 1;
155 uint64_t address = this->address_;
156
157 // Initiate search
158 for (int bit_number = 1; bit_number <= 64; bit_number++, bit_mask <<= 1) {
159 // read bit
160 bool id_bit = this->read_bit_();
161 // read its complement
162 bool cmp_id_bit = this->read_bit_();
163
164 if (id_bit && cmp_id_bit) {
165 // No devices participating in search
166 return 0;
167 }
168
169 bool branch;
170
171 if (id_bit != cmp_id_bit) {
172 // only chose one branch, the other one doesn't have any devices.
173 branch = id_bit;
174 } else {
175 // there are devices with both 0s and 1s at this bit
176 if (bit_number < this->last_discrepancy_) {
177 branch = (address & bit_mask) > 0;
178 } else {
179 branch = bit_number == this->last_discrepancy_;
180 }
181
182 if (!branch) {
183 last_zero = bit_number;
184 }
185 }
186
187 if (branch) {
188 address |= bit_mask;
189 } else {
190 address &= ~bit_mask;
191 }
192
193 // choose/announce branch
194 this->write_bit_(branch);
195 }
196
197 this->last_discrepancy_ = last_zero;
198 if (this->last_discrepancy_ == 0) {
199 // we're at root and have no choices left, so this was the last one.
200 this->last_device_flag_ = true;
201 }
202
203 this->address_ = address;
204 return address;
205}
206
207} // namespace esphome::gpio
uint8_t address
Definition bl0906.h:4
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
void digital_write(bool value)
Definition gpio.cpp:151
void pin_mode(gpio::Flags flags)
Definition gpio.cpp:160
Helper class to disable interrupts.
Definition helpers.h:2001
void write64(uint64_t val) override
void write8(uint8_t val) override
void dump_devices_(const char *tag)
log the found devices
void search()
Search for 1-Wire devices on the bus.
int ret
mopeka_std_values val[3]
@ FLAG_OUTPUT
Definition gpio.h:26
@ FLAG_PULLUP
Definition gpio.h:28
@ FLAG_INPUT
Definition gpio.h:25
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
static void uint32_t
SemaphoreHandle_t lock