ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
i2c_bus_esp_idf.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2
3#include "i2c_bus_esp_idf.h"
4
5#include <driver/gpio.h>
6#include <cinttypes>
7#include <cstring>
9#include "esphome/core/hal.h"
11#include "esphome/core/log.h"
12
13namespace esphome::i2c {
14
15ESPHOME_LOG_TAG(TAG, "i2c");
16
17// Maximum bytes to log in hex format (truncates larger transfers)
18static constexpr size_t I2C_MAX_LOG_BYTES = 32;
19
21 static i2c_port_t next_hp_port = I2C_NUM_0;
22#if SOC_LP_I2C_SUPPORTED
23 static i2c_port_t next_lp_port = LP_I2C_NUM_0;
24#endif
25
26 if (this->timeout_ > 13000) {
27 ESP_LOGW(TAG, "Using max allowed timeout: 13 ms");
28 this->timeout_ = 13000;
29 }
30
31 this->recover_();
32
33 i2c_master_bus_config_t bus_conf{};
34 memset(&bus_conf, 0, sizeof(bus_conf));
35 bus_conf.sda_io_num = gpio_num_t(this->sda_pin_);
36 bus_conf.scl_io_num = gpio_num_t(this->scl_pin_);
37 bus_conf.glitch_ignore_cnt = 7;
38#if SOC_LP_I2C_SUPPORTED
39 if (this->lp_mode_) {
40 if ((next_lp_port - LP_I2C_NUM_0) == SOC_LP_I2C_NUM) {
41 ESP_LOGE(TAG, "No more than %u LP buses supported", SOC_LP_I2C_NUM);
42 this->mark_failed();
43 return;
44 }
45 this->port_ = next_lp_port;
46 next_lp_port = (i2c_port_t) (next_lp_port + 1);
47 bus_conf.lp_source_clk = LP_I2C_SCLK_DEFAULT;
48 } else {
49#endif
50 if (next_hp_port == SOC_HP_I2C_NUM) {
51 ESP_LOGE(TAG, "No more than %u HP buses supported", SOC_HP_I2C_NUM);
52 this->mark_failed();
53 return;
54 }
55 this->port_ = next_hp_port;
56 next_hp_port = (i2c_port_t) (next_hp_port + 1);
57 bus_conf.clk_source = I2C_CLK_SRC_DEFAULT;
58#if SOC_LP_I2C_SUPPORTED
59 }
60#endif
61 bus_conf.i2c_port = this->port_;
62 bus_conf.flags.enable_internal_pullup = this->sda_pullup_enabled_ || this->scl_pullup_enabled_;
63 esp_err_t err = i2c_new_master_bus(&bus_conf, &this->bus_);
64 if (err != ESP_OK) {
65 ESP_LOGW(TAG, "i2c_new_master_bus failed: %s", esp_err_to_name(err));
66 this->mark_failed();
67 return;
68 }
69
70 this->dev_ = this->add_device_(this->frequency_);
71 if (this->dev_ == nullptr) {
72 this->mark_failed();
73 return;
74 }
75#ifdef I2C_PORT_FREQUENCY_COUNT
76 this->add_frequency(this->frequency_);
77 for (auto &device : this->devices_) {
78 device.dev = device.frequency == this->frequency_ ? this->dev_ : this->add_device_(device.frequency);
79 if (device.dev == nullptr) {
80 this->mark_failed();
81 return;
82 }
83 }
84#endif
85
86 this->initialized_ = true;
87
88 if (this->scan_) {
89 ESP_LOGV(TAG, "Scanning for devices");
90 this->i2c_scan_();
91 }
92}
93
94i2c_master_dev_handle_t IDFI2CBus::add_device_(uint32_t frequency) {
95 i2c_device_config_t dev_conf{};
96 dev_conf.dev_addr_length = I2C_ADDR_BIT_LEN_7;
97 dev_conf.device_address = I2C_DEVICE_ADDRESS_NOT_USED;
98 dev_conf.scl_speed_hz = frequency;
99 dev_conf.scl_wait_us = this->timeout_;
100 i2c_master_dev_handle_t dev{};
101 const esp_err_t err = i2c_master_bus_add_device(this->bus_, &dev_conf, &dev);
102 if (err != ESP_OK) {
103 ESP_LOGW(TAG, "i2c_master_bus_add_device failed: %s", esp_err_to_name(err));
104 return nullptr;
105 }
106 return dev;
107}
108
110 ESP_LOGCONFIG(TAG, "I2C Bus:");
111 ESP_LOGCONFIG(TAG,
112 " SDA Pin: GPIO%u\n"
113 " SCL Pin: GPIO%u\n"
114 " Frequency: %" PRIu32 " Hz",
115 this->sda_pin_, this->scl_pin_, this->frequency_);
116 if (this->timeout_ > 0) {
117 ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 "us", this->timeout_);
118 }
119 switch (this->recovery_result_) {
121 ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
122 break;
124 ESP_LOGCONFIG(TAG, " Recovery: failed, SCL is held low on the bus");
125 break;
127 ESP_LOGCONFIG(TAG, " Recovery: failed, SDA is held low on the bus");
128 break;
129 }
130 if (this->scan_) {
131 ESP_LOGCONFIG(TAG, "Results from bus scan:");
132 if (this->scan_results_.empty()) {
133 ESP_LOGCONFIG(TAG, "Found no devices");
134 } else {
135 for (const auto &s : this->scan_results_) {
136 if (s.second) {
137 ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", s.first);
138 } else {
139 ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
140 }
141 }
142 }
143 }
144}
145
146ErrorCode IDFI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer,
147 size_t read_count) {
148 // logging is only enabled with v level, if warnings are shown the caller
149 // should log them
150 if (!this->initialized_) {
151 ESP_LOGW(TAG, "i2c bus not initialized!");
153 }
154
155 i2c_operation_job_t jobs[8]{};
156 size_t num_jobs = 0;
157 uint8_t write_addr = (address << 1) | I2C_MASTER_WRITE;
158 uint8_t read_addr = (address << 1) | I2C_MASTER_READ;
159 ESP_LOGV(TAG, "Writing %zu bytes, reading %zu bytes", write_count, read_count);
160 if (read_count == 0 && write_count == 0) {
161 // basically just a bus probe. Send a start, address and stop
162 ESP_LOGV(TAG, "0x%02X BUS PROBE", address);
163 jobs[num_jobs++].command = I2C_MASTER_CMD_START;
164 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
165 jobs[num_jobs].write.ack_check = true;
166 jobs[num_jobs].write.data = &write_addr;
167 jobs[num_jobs++].write.total_bytes = 1;
168 } else {
169 if (write_count != 0) {
170 ESP_LOGV_HEX(TAG, write_buffer, write_count, I2C_MAX_LOG_BYTES, "0x%02X TX %s", address);
171 jobs[num_jobs++].command = I2C_MASTER_CMD_START;
172 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
173 jobs[num_jobs].write.ack_check = true;
174 jobs[num_jobs].write.data = &write_addr;
175 jobs[num_jobs++].write.total_bytes = 1;
176 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
177 jobs[num_jobs].write.ack_check = true;
178 jobs[num_jobs].write.data = (uint8_t *) write_buffer;
179 jobs[num_jobs++].write.total_bytes = write_count;
180 }
181 if (read_count != 0) {
182 ESP_LOGV(TAG, "0x%02X RX bytes %zu", address, read_count);
183 jobs[num_jobs++].command = I2C_MASTER_CMD_START;
184 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
185 jobs[num_jobs].write.ack_check = true;
186 jobs[num_jobs].write.data = &read_addr;
187 jobs[num_jobs++].write.total_bytes = 1;
188 if (read_count > 1) {
189 jobs[num_jobs].command = I2C_MASTER_CMD_READ;
190 jobs[num_jobs].read.ack_value = I2C_ACK_VAL;
191 jobs[num_jobs].read.data = read_buffer;
192 jobs[num_jobs++].read.total_bytes = read_count - 1;
193 }
194 jobs[num_jobs].command = I2C_MASTER_CMD_READ;
195 jobs[num_jobs].read.ack_value = I2C_NACK_VAL;
196 jobs[num_jobs].read.data = read_buffer + read_count - 1;
197 jobs[num_jobs++].read.total_bytes = 1;
198 }
199 }
200 jobs[num_jobs++].command = I2C_MASTER_CMD_STOP;
201 ESP_LOGV(TAG, "Sending %zu jobs", num_jobs);
202 esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num_jobs, 100);
203 if (err == ESP_ERR_INVALID_STATE || err == ESP_ERR_INVALID_RESPONSE) {
204 ESP_LOGV(TAG, "TX to %02X failed: not acked", address);
206 } else if (err == ESP_ERR_TIMEOUT) {
207 ESP_LOGV(TAG, "TX to %02X failed: timeout", address);
208 return ERROR_TIMEOUT;
209 } else if (err != ESP_OK) {
210 ESP_LOGV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err));
211 return ERROR_UNKNOWN;
212 }
213 return ERROR_OK;
214}
215
216#ifdef I2C_PORT_FREQUENCY_COUNT
218 for (const auto &device : this->devices_) {
219 if (device.frequency == frequency) {
220 return;
221 }
222 }
223 this->devices_.push_back({frequency, nullptr});
224}
225
227 if (this->frequency_ == frequency) {
228 return ERROR_OK;
229 }
230 if (!this->initialized_) {
231 this->frequency_ = frequency;
232 return ERROR_OK;
233 }
234 // Every handle exists since setup(); a frequency nobody registered has none
235 for (const auto &device : this->devices_) {
236 if (device.frequency == frequency) {
237 this->dev_ = device.dev;
238 this->frequency_ = frequency;
239 return ERROR_OK;
240 }
241 }
242 return ERROR_UNKNOWN;
243}
244#endif
245
249void IDFI2CBus::recover_() {
250 ESP_LOGI(TAG, "Performing bus recovery");
251
252 const auto scl_pin = static_cast<gpio_num_t>(this->scl_pin_);
253 const auto sda_pin = static_cast<gpio_num_t>(this->sda_pin_);
254
255 // For the upcoming operations, target for a 60kHz toggle frequency.
256 // 1000kHz is the maximum frequency for I2C running in standard-mode,
257 // but lower frequencies are not a problem.
258 // Note: the timing that is used here is chosen manually, to get
259 // results that are close to the timing that can be archieved by the
260 // implementation for the Arduino framework.
261 const auto half_period_usec = 7;
262
263 // Configure SCL pin for open drain input/output, with a pull up resistor.
264 gpio_set_level(scl_pin, 1);
265 gpio_config_t scl_config{};
266 scl_config.pin_bit_mask = 1ULL << this->scl_pin_;
267 scl_config.mode = GPIO_MODE_INPUT_OUTPUT_OD;
268 scl_config.pull_up_en = GPIO_PULLUP_ENABLE;
269 scl_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
270 scl_config.intr_type = GPIO_INTR_DISABLE;
271 gpio_config(&scl_config);
272
273 // Configure SDA pin for open drain input/output, with a pull up resistor.
274 gpio_set_level(sda_pin, 1);
275 gpio_config_t sda_conf{};
276 sda_conf.pin_bit_mask = 1ULL << this->sda_pin_;
277 sda_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD;
278 sda_conf.pull_up_en = GPIO_PULLUP_ENABLE;
279 sda_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
280 sda_conf.intr_type = GPIO_INTR_DISABLE;
281 gpio_config(&sda_conf);
282
283 // If SCL is pulled low on the I2C bus, then some device is interfering
284 // with the SCL line. In that case, the I2C bus cannot be recovered.
285 delayMicroseconds(half_period_usec);
286 if (gpio_get_level(scl_pin) == 0) {
287 ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the bus");
288 this->recovery_result_ = RECOVERY_FAILED_SCL_LOW;
289 return;
290 }
291
292 // From the specification:
293 // "If the data line (SDA) is stuck LOW, send nine clock pulses. The
294 // device that held the bus LOW should release it sometime within
295 // those nine clocks."
296 // We don't really have to detect if SDA is stuck low. We'll simply send
297 // nine clock pulses here, just in case SDA is stuck. Actual checks on
298 // the SDA line status will be done after the clock pulses.
299 for (auto i = 0; i < 9; i++) {
300 gpio_set_level(scl_pin, 0);
301 delayMicroseconds(half_period_usec);
302 gpio_set_level(scl_pin, 1);
303 delayMicroseconds(half_period_usec);
304
305 // When SCL is kept LOW at this point, we might be looking at a device
306 // that applies clock stretching. Wait for the release of the SCL line,
307 // but not forever. There is no specification for the maximum allowed
308 // time. We yield and reset the WDT, so as to avoid triggering reset.
309 // No point in trying to recover the bus by forcing a uC reset. Bus
310 // should recover in a few ms or less else not likely to recovery at
311 // all.
312 auto wait = 250;
313 while (wait-- && gpio_get_level(scl_pin) == 0) {
314 App.feed_wdt();
315 delayMicroseconds(half_period_usec * 2);
316 }
317 if (gpio_get_level(scl_pin) == 0) {
318 ESP_LOGE(TAG, "Recovery failed: SCL is held LOW during clock pulse cycle");
319 this->recovery_result_ = RECOVERY_FAILED_SCL_LOW;
320 return;
321 }
322 }
323
324 // By now, any stuck device ought to have sent all remaining bits of its
325 // transaction, meaning that it should have freed up the SDA line, resulting
326 // in SDA being pulled up.
327 if (gpio_get_level(sda_pin) == 0) {
328 ESP_LOGE(TAG, "Recovery failed: SDA is held LOW after clock pulse cycle");
329 this->recovery_result_ = RECOVERY_FAILED_SDA_LOW;
330 return;
331 }
332
333 // From the specification:
334 // "I2C-bus compatible devices must reset their bus logic on receipt of
335 // a START or repeated START condition such that they all anticipate
336 // the sending of a target address, even if these START conditions are
337 // not positioned according to the proper format."
338 // While the 9 clock pulses from above might have drained all bits of a
339 // single byte within a transaction, a device might have more bytes to
340 // transmit. So here we'll generate a START condition to snap the device
341 // out of this state.
342 // SCL and SDA are already high at this point, so we can generate a START
343 // condition by making the SDA signal LOW.
344 delayMicroseconds(half_period_usec);
345 gpio_set_level(sda_pin, 0);
346
347 // From the specification:
348 // "A START condition immediately followed by a STOP condition (void
349 // message) is an illegal format. Many devices however are designed to
350 // operate properly under this condition."
351 // Finally, we'll bring the I2C bus into a starting state by generating
352 // a STOP condition.
353 delayMicroseconds(half_period_usec);
354 gpio_set_level(sda_pin, 1);
355
356 this->recovery_result_ = RECOVERY_COMPLETED;
357}
358
359} // namespace esphome::i2c
360#endif // USE_ESP32
uint8_t address
Definition bl0906.h:4
uint16_le_t frequency
Definition bl0942.h:6
void feed_wdt()
Feed the task watchdog.
void mark_failed()
Mark this component as failed.
bool scan_
Should we scan ? Can be set in the yaml.
Definition i2c_bus.h:71
std::vector< std::pair< uint8_t, bool > > scan_results_
array containing scan results
Definition i2c_bus.h:70
void i2c_scan_()
Scans the I2C bus for devices.
Definition i2c.cpp:11
ErrorCode switch_frequency(uint32_t frequency) override
StaticVector< FrequencyDevice, I2C_PORT_FREQUENCY_COUNT+1 > devices_
i2c_master_bus_handle_t bus_
void add_frequency(uint32_t frequency)
Register a frequency used on the bus so setup() creates its device handle.
i2c_master_dev_handle_t dev_
ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, size_t read_count) override
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:13
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
@ ERROR_TIMEOUT
timeout while waiting to receive bytes
Definition i2c_bus.h:18
@ ERROR_NOT_ACKNOWLEDGED
I2C bus acknowledgment not received.
Definition i2c_bus.h:17
@ ERROR_NOT_INITIALIZED
call method to a not initialized bus
Definition i2c_bus.h:19
@ ERROR_UNKNOWN
miscellaneous I2C error during execution
Definition i2c_bus.h:21
ESPHOME_LOG_TAG(TAG, "i2c")
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t