ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
ds248x.cpp
Go to the documentation of this file.
1#include "ds248x.h"
2#include "esphome/core/log.h"
4
5namespace esphome::ds248x {
6
7static const char *const TAG = "ds248x";
8
10 ESP_LOGCONFIG(TAG, "Setting up DS248x...");
11
12 // Wake up device if sleep pin is configured
13 if (this->sleep_pin_) {
14 this->sleep_pin_->setup();
16 this->sleep_pin_->digital_write(true); // Wake up
17 delay(1); // DS2482-101 Datasheet: tOSCWUP = 100μs (using 10x margin)
18 }
19
20 // Probe device
21 ESP_LOGD(TAG, "Probing DS248x...");
22 uint8_t status = 0;
23 if (this->read(&status, 1) == i2c::ERROR_OK) {
24 ESP_LOGD(TAG, "Device responded! Status: 0x%02x", status);
25 } else {
26 ESP_LOGW(TAG, "Device did not respond. Trying reset anyway...");
27 }
28
29 if (!this->device_reset_()) {
30 ESP_LOGW(TAG, "DS248x reset failed during setup!");
31 }
32
33 // Configure device
34 if (!this->device_configure_()) {
35 ESP_LOGE(TAG, "DS248x configuration failed!");
36 this->mark_failed();
37 return;
38 }
39
40 // Reset to Channel 0
41 this->select_channel(0);
42
43 ESP_LOGI(TAG, "DS248x initialized successfully.");
44}
45
47 if (this->sleep_pin_ && (this->hub_sleep_ || this->bus_sleep_)) {
48 this->sleep_pin_->digital_write(false); // Sleep
49 }
50}
51
53 ESP_LOGCONFIG(TAG, "DS248x:");
54 LOG_I2C_DEVICE(this);
55 ESP_LOGCONFIG(TAG, " Channel Count: %d", this->channel_count_);
56 ESP_LOGCONFIG(TAG, " Active Pullup: %s", YESNO(this->active_pullup_));
57 if (this->ds2484_mode_) {
58 ESP_LOGCONFIG(TAG, " DS2484 Mode: enabled");
59 }
60}
61
62// --- Internal Helpers ---
63
64// Datasheet command durations are sub-2ms; allow a little margin before forcing recovery.
65static constexpr uint32_t BUSY_TIMEOUT_MS = 5;
66
67bool DS248xComponent::set_read_pointer_(uint8_t ptr) { return this->write_byte(DS248X_COMMAND_SETREADPTR, ptr); }
68
70 uint32_t start = millis();
71 do {
72 uint8_t status;
73 if (this->read(&status, 1) == i2c::ERROR_OK && !(status & DS248X_STATUS_BUSY))
74 return true;
76 } while (millis() - start < BUSY_TIMEOUT_MS);
77 ESP_LOGW(TAG, "DS248x busy timeout");
78 bool recovered = this->device_reset_() && this->device_configure_();
79 this->current_channel_ = -1;
80 if (!recovered) {
81 ESP_LOGE(TAG, "DS248x recovery failed after busy timeout");
82 this->mark_failed();
83 }
84 return false;
85}
86
88 ESP_LOGD(TAG, "Resetting device...");
89 uint8_t cmd = DS248X_COMMAND_RESET;
90 if (this->write(&cmd, 1) != i2c::ERROR_OK)
91 return false;
92
93 uint8_t status;
94 if (this->read(&status, 1) != i2c::ERROR_OK)
95 return false;
96
97 if (!(status & DS248X_STATUS_RST)) {
98 ESP_LOGW(TAG, "Device reset failed (RST bit not set)");
99 return false;
100 }
101
102 this->current_channel_ = -1;
103 return true;
104}
105
107 ESP_LOGD(TAG, "Configuring device...");
108
109 if (!this->write_config_()) {
110 ESP_LOGW(TAG, "Config write/verify failed");
111 return false;
112 }
113
114 ESP_LOGD(TAG, "Configured successfully");
115
116 // DS2484 Configuration
117 if (this->ds2484_mode_) {
118 if (this->ds2484_trstl_ != DS2484_PARAM_UNSET &&
119 !this->configure_ds2484_port_(DS2484_PORT_PARAM_TRSTL, this->ds2484_trstl_))
120 return false;
121 if (this->ds2484_tmsp_ != DS2484_PARAM_UNSET &&
122 !this->configure_ds2484_port_(DS2484_PORT_PARAM_TMSP, this->ds2484_tmsp_))
123 return false;
124 if (this->ds2484_tw0l_ != DS2484_PARAM_UNSET &&
125 !this->configure_ds2484_port_(DS2484_PORT_PARAM_TW0L, this->ds2484_tw0l_))
126 return false;
127 if (this->ds2484_trec0_ != DS2484_PARAM_UNSET &&
128 !this->configure_ds2484_port_(DS2484_PORT_PARAM_TREC0, this->ds2484_trec0_))
129 return false;
130 if (this->ds2484_rwpu_ != DS2484_PARAM_UNSET &&
131 !this->configure_ds2484_port_(DS2484_PORT_PARAM_RWPU, this->ds2484_rwpu_))
132 return false;
133 }
134
135 return true;
136}
137
138bool DS248xComponent::configure_ds2484_port_(uint8_t param, uint8_t val) {
139 uint8_t cmd = DS2484_COMMAND_ADJUSTPORT;
140 // Control Byte format (DS2484 Table 6): P[2:0] in bits 7:5, OD in bit 4, VAL[3:0] in bits 3:0
141 uint8_t data = ((param & 0x07) << 5) | (val & 0x0F);
142
143 // The DS2484 always acknowledges the Adjust 1-Wire Port control byte (datasheet "Adjust
144 // 1-Wire Port"), so a successful write confirms the update. We deliberately do not read
145 // back to verify: a single read of the Port Configuration register always returns the
146 // fixed 8-byte report starting at Byte 1 (tRSTL standard speed), not the parameter that
147 // was just written, so a per-parameter readback comparison would spuriously fail for
148 // tMSP/tW0L/tREC0/RWPU.
149 if (!this->write_byte(cmd, data)) {
150 ESP_LOGW(TAG, "DS2484 port config failed (param %d)", param);
151 return false;
152 }
153
154 return this->set_read_pointer_(DS248X_POINTER_STATUS);
155}
156
158 uint8_t config = 0;
159 if (this->active_pullup_)
160 config |= DS248X_CONFIG_ACTIVE_PULLUP;
161
162 // The DS248x only accepts the config byte if the upper nibble is the one's-complement of the lower nibble.
163 uint8_t config_byte = (config & 0x0F) | ((~config & 0x0F) << 4);
164
165 if (!this->write_byte(DS248X_COMMAND_WRITECONFIG, config_byte)) {
166 ESP_LOGW(TAG, "Failed to write config byte");
167 return false;
168 }
169
170 if (!this->set_read_pointer_(DS248X_POINTER_CONFIG)) {
171 return false;
172 }
173
174 uint8_t read_config;
175 if (this->read(&read_config, 1) != i2c::ERROR_OK) {
176 ESP_LOGW(TAG, "Failed to read back config byte");
177 return false;
178 }
179
180 if ((read_config & 0x0F) != (config_byte & 0x0F)) {
181 ESP_LOGW(TAG, "Config mismatch! Wrote 0x%02x, Read 0x%02x", config_byte, read_config);
182 return false;
183 }
184
185 return this->set_read_pointer_(DS248X_POINTER_STATUS);
186}
187
188// --- Channel Selection ---
189
190// Channel select codes: write code -> expected read code
191static constexpr uint8_t CHANNEL_WRITE_CODES[8] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87};
192static constexpr uint8_t CHANNEL_READ_CODES[8] = {0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87};
193
194bool DS248xComponent::select_channel(uint8_t channel) {
195 if (this->channel_count_ <= 1)
196 return true;
197 if (channel >= this->channel_count_)
198 return false;
199
200 if (this->current_channel_ == channel)
201 return true;
202
203 if (!this->write_byte(DS248X_COMMAND_CHANNELSELECT, CHANNEL_WRITE_CODES[channel])) {
204 this->current_channel_ = -1;
205 return false;
206 }
207
208 uint8_t read_code;
209 if (this->read(&read_code, 1) != i2c::ERROR_OK) {
210 this->current_channel_ = -1;
211 return false;
212 }
213
214 if (read_code != CHANNEL_READ_CODES[channel]) {
215 ESP_LOGW(TAG, "Channel select failed! Expected 0x%02x, got 0x%02x", CHANNEL_READ_CODES[channel], read_code);
216 this->current_channel_ = -1;
217 return false;
218 }
219
220 if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
221 return false;
222
223 this->current_channel_ = channel;
224 return true;
225}
226
227// --- 1-Wire Bus Operations ---
228
229bool DS248xComponent::ow_reset(bool &presence) {
230 if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
231 return false;
232
233 uint8_t cmd = DS248X_COMMAND_RESETWIRE;
234 if (this->write(&cmd, 1) != i2c::ERROR_OK)
235 return false;
236
237 if (!this->wait_busy_()) {
238 ESP_LOGW(TAG, "ow_reset: wait busy failed");
239 return false;
240 }
241
242 uint8_t status;
243 if (this->read(&status, 1) != i2c::ERROR_OK) {
244 ESP_LOGW(TAG, "ow_reset: read status failed");
245 return false;
246 }
247
248 if (status & DS248X_STATUS_SD) {
249 ESP_LOGW(TAG, "Short detected on 1-Wire bus!");
250 return false;
251 }
252
253 presence = (status & DS248X_STATUS_PPD);
254 return true;
255}
256
258 if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
259 return false;
260
261 if (!this->wait_busy_()) {
262 ESP_LOGW(TAG, "Device busy before writing byte 0x%02x", byte);
263 return false;
264 }
265
266 uint8_t cmd[2] = {DS248X_COMMAND_WRITEBYTE, byte};
267 if (this->write(cmd, 2) != i2c::ERROR_OK) {
268 ESP_LOGW(TAG, "I2C write failed for byte 0x%02x", byte);
269 return false;
270 }
271
272 if (!this->wait_busy_()) {
273 ESP_LOGW(TAG, "Timeout waiting for write byte to complete!");
274 return false;
275 }
276
277 return true;
278}
279
280bool DS248xComponent::ow_read_byte(uint8_t &byte) {
281 if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
282 return false;
283
284 uint8_t cmd = DS248X_COMMAND_READBYTE;
285 if (this->write(&cmd, 1) != i2c::ERROR_OK)
286 return false;
287
288 if (!this->wait_busy_())
289 return false;
290
291 if (!this->set_read_pointer_(DS248X_POINTER_DATA))
292 return false;
293
294 if (this->read(&byte, 1) != i2c::ERROR_OK)
295 return false;
296
297 return true;
298}
299
300bool DS248xComponent::search_triplet(bool search_direction, uint8_t &status) {
301 if (!this->set_read_pointer_(DS248X_POINTER_STATUS))
302 return false;
303
304 // DS248x Datasheet: 1-Wire Triplet command requires 2 bytes:
305 // Byte 1: Command code 0x78
306 // Byte 2: Direction byte (bit 7 = V, search direction if discrepancy)
307 uint8_t buffer[2] = {DS248X_COMMAND_TRIPLET, static_cast<uint8_t>(search_direction ? 0x80 : 0x00)};
308 if (this->write(buffer, 2) != i2c::ERROR_OK)
309 return false;
310
311 if (!this->wait_busy_())
312 return false;
313
314 if (this->read(&status, 1) != i2c::ERROR_OK)
315 return false;
316
317 return true;
318}
319
320} // namespace esphome::ds248x
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
virtual void digital_write(bool value)=0
bool configure_ds2484_port_(uint8_t param, uint8_t val)
Definition ds248x.cpp:138
bool select_channel(uint8_t channel)
Definition ds248x.cpp:194
bool ow_reset(bool &presence)
Definition ds248x.cpp:229
InternalGPIOPin * sleep_pin_
Definition ds248x.h:107
bool set_read_pointer_(uint8_t ptr)
Definition ds248x.cpp:67
static constexpr uint8_t DS2484_PARAM_UNSET
Definition ds248x.h:115
bool ow_read_byte(uint8_t &byte)
Definition ds248x.cpp:280
bool ow_write_byte(uint8_t byte)
Definition ds248x.cpp:257
bool search_triplet(bool search_direction, uint8_t &status)
Definition ds248x.cpp:300
ErrorCode write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
Definition i2c.h:183
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:163
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
mopeka_std_values val[3]
@ FLAG_OUTPUT
Definition gpio.h:26
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
static void uint32_t