ESPHome  2024.5.5
esp_one_wire.cpp
Go to the documentation of this file.
1 #include "esp_one_wire.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 
5 namespace esphome {
6 namespace dallas {
7 
8 static const char *const TAG = "dallas.one_wire";
9 
10 const uint8_t ONE_WIRE_ROM_SELECT = 0x55;
11 const int ONE_WIRE_ROM_SEARCH = 0xF0;
12 
14 
15 bool HOT IRAM_ATTR ESPOneWire::reset() {
16  // See reset here:
17  // https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
18  // Wait for communication to clear (delay G)
20  uint8_t retries = 125;
21  do {
22  if (--retries == 0)
23  return false;
25  } while (!pin_.digital_read());
26 
27  // Send 480µs LOW TX reset pulse (drive bus low, delay H)
29  pin_.digital_write(false);
30  delayMicroseconds(480);
31 
32  // Release the bus, delay I
35 
36  // sample bus, 0=device(s) present, 1=no device present
37  bool r = !pin_.digital_read();
38  // delay J
39  delayMicroseconds(410);
40  return r;
41 }
42 
43 void HOT IRAM_ATTR ESPOneWire::write_bit(bool bit) {
44  // drive bus low
46  pin_.digital_write(false);
47 
48  // from datasheet:
49  // write 0 low time: t_low0: min=60µs, max=120µs
50  // write 1 low time: t_low1: min=1µs, max=15µs
51  // time slot: t_slot: min=60µs, max=120µs
52  // recovery time: t_rec: min=1µs
53  // ds18b20 appears to read the bus after roughly 14µs
54  uint32_t delay0 = bit ? 6 : 60;
55  uint32_t delay1 = bit ? 54 : 5;
56 
57  // delay A/C
58  delayMicroseconds(delay0);
59  // release bus
60  pin_.digital_write(true);
61  // delay B/D
62  delayMicroseconds(delay1);
63 }
64 
65 bool HOT IRAM_ATTR ESPOneWire::read_bit() {
66  // drive bus low
68  pin_.digital_write(false);
69 
70  // note: for reading we'll need very accurate timing, as the
71  // timing for the digital_read() is tight; according to the datasheet,
72  // we should read at the end of 16µs starting from the bus low
73  // typically, the ds18b20 pulls the line high after 11µs for a logical 1
74  // and 29µs for a logical 0
75 
76  uint32_t start = micros();
77  // datasheet says >1µs
79 
80  // release bus, delay E
82 
83  // Unfortunately some frameworks have different characteristics than others
84  // esp32 arduino appears to pull the bus low only after the digital_write(false),
85  // whereas on esp-idf it already happens during the pin_mode(OUTPUT)
86  // manually correct for this with these constants.
87 
88 #ifdef USE_ESP32
89  uint32_t timing_constant = 12;
90 #else
91  uint32_t timing_constant = 14;
92 #endif
93 
94  // measure from start value directly, to get best accurate timing no matter
95  // how long pin_mode/delayMicroseconds took
96  while (micros() - start < timing_constant)
97  ;
98 
99  // sample bus to read bit from peer
100  bool r = pin_.digital_read();
101 
102  // read slot is at least 60µs; get as close to 60µs to spend less time with interrupts locked
103  uint32_t now = micros();
104  if (now - start < 60)
105  delayMicroseconds(60 - (now - start));
106 
107  return r;
108 }
109 
110 void IRAM_ATTR ESPOneWire::write8(uint8_t val) {
111  for (uint8_t i = 0; i < 8; i++) {
112  this->write_bit(bool((1u << i) & val));
113  }
114 }
115 
116 void IRAM_ATTR ESPOneWire::write64(uint64_t val) {
117  for (uint8_t i = 0; i < 64; i++) {
118  this->write_bit(bool((1ULL << i) & val));
119  }
120 }
121 
122 uint8_t IRAM_ATTR ESPOneWire::read8() {
123  uint8_t ret = 0;
124  for (uint8_t i = 0; i < 8; i++) {
125  ret |= (uint8_t(this->read_bit()) << i);
126  }
127  return ret;
128 }
129 uint64_t IRAM_ATTR ESPOneWire::read64() {
130  uint64_t ret = 0;
131  for (uint8_t i = 0; i < 8; i++) {
132  ret |= (uint64_t(this->read_bit()) << i);
133  }
134  return ret;
135 }
136 void IRAM_ATTR ESPOneWire::select(uint64_t address) {
137  this->write8(ONE_WIRE_ROM_SELECT);
138  this->write64(address);
139 }
140 void IRAM_ATTR ESPOneWire::reset_search() {
141  this->last_discrepancy_ = 0;
142  this->last_device_flag_ = false;
143  this->rom_number_ = 0;
144 }
145 uint64_t IRAM_ATTR ESPOneWire::search() {
146  if (this->last_device_flag_) {
147  return 0u;
148  }
149 
150  {
151  InterruptLock lock;
152  if (!this->reset()) {
153  // Reset failed or no devices present
154  this->reset_search();
155  return 0u;
156  }
157  }
158 
159  uint8_t id_bit_number = 1;
160  uint8_t last_zero = 0;
161  uint8_t rom_byte_number = 0;
162  bool search_result = false;
163  uint8_t rom_byte_mask = 1;
164 
165  {
166  InterruptLock lock;
167  // Initiate search
168  this->write8(ONE_WIRE_ROM_SEARCH);
169  do {
170  // read bit
171  bool id_bit = this->read_bit();
172  // read its complement
173  bool cmp_id_bit = this->read_bit();
174 
175  if (id_bit && cmp_id_bit) {
176  // No devices participating in search
177  break;
178  }
179 
180  bool branch;
181 
182  if (id_bit != cmp_id_bit) {
183  // only chose one branch, the other one doesn't have any devices.
184  branch = id_bit;
185  } else {
186  // there are devices with both 0s and 1s at this bit
187  if (id_bit_number < this->last_discrepancy_) {
188  branch = (this->rom_number8_()[rom_byte_number] & rom_byte_mask) > 0;
189  } else {
190  branch = id_bit_number == this->last_discrepancy_;
191  }
192 
193  if (!branch) {
194  last_zero = id_bit_number;
195  }
196  }
197 
198  if (branch) {
199  // set bit
200  this->rom_number8_()[rom_byte_number] |= rom_byte_mask;
201  } else {
202  // clear bit
203  this->rom_number8_()[rom_byte_number] &= ~rom_byte_mask;
204  }
205 
206  // choose/announce branch
207  this->write_bit(branch);
208  id_bit_number++;
209  rom_byte_mask <<= 1;
210  if (rom_byte_mask == 0u) {
211  // go to next byte
212  rom_byte_number++;
213  rom_byte_mask = 1;
214  }
215  } while (rom_byte_number < 8); // loop through all bytes
216  }
217 
218  if (id_bit_number >= 65) {
219  this->last_discrepancy_ = last_zero;
220  if (this->last_discrepancy_ == 0) {
221  // we're at root and have no choices left, so this was the last one.
222  this->last_device_flag_ = true;
223  }
224  search_result = true;
225  }
226 
227  search_result = search_result && (this->rom_number8_()[0] != 0);
228  if (!search_result) {
229  this->reset_search();
230  return 0u;
231  }
232 
233  return this->rom_number_;
234 }
235 std::vector<uint64_t> ESPOneWire::search_vec() {
236  std::vector<uint64_t> res;
237 
238  this->reset_search();
239  uint64_t address;
240  while ((address = this->search()) != 0u)
241  res.push_back(address);
242 
243  return res;
244 }
245 void IRAM_ATTR ESPOneWire::skip() {
246  this->write8(0xCC); // skip ROM
247 }
248 
249 uint8_t IRAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); }
250 
251 } // namespace dallas
252 } // namespace esphome
void reset_search()
Reset the device search.
void skip()
Write a command to the bus that addresses all devices by skipping the ROM.
const int ONE_WIRE_ROM_SEARCH
mopeka_std_values val[4]
bool reset()
Reset the bus, should be done before all write operations.
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
void write_bit(bool bit)
Write a single bit to the bus, takes about 70µs.
ESPOneWire(InternalGPIOPin *pin)
std::vector< uint64_t > search_vec()
Helper that wraps search in a std::vector.
const uint8_t ONE_WIRE_ROM_SELECT
bool read_bit()
Read a single bit from the bus, takes about 70µs.
void write8(uint8_t val)
Write a word to the bus. LSB first.
void pin_mode(gpio::Flags flags)
Definition: gpio.cpp:128
uint8_t read8()
Read an 8 bit word from the bus.
uint64_t read64()
Read an 64-bit unsigned integer from the bus.
ISRInternalGPIOPin pin_
Definition: esp_one_wire.h:61
virtual ISRInternalGPIOPin to_isr() const =0
Helper class to disable interrupts.
Definition: helpers.h:593
uint64_t search()
Search for a 1-Wire device on the bus. Returns 0 if all devices have been found.
uint8_t * rom_number8_()
Helper to get the internal 64-bit unsigned rom number as a 8-bit integer pointer. ...
void select(uint64_t address)
Select a specific address on the bus for the following command.
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
void write64(uint64_t val)
Write a 64 bit unsigned integer to the bus. LSB first.
void digital_write(bool value)
Definition: gpio.cpp:121