ESPHome  2024.4.0
i2c_bus_esp_idf.cpp
Go to the documentation of this file.
1 #ifdef USE_ESP_IDF
2 
3 #include "i2c_bus_esp_idf.h"
4 #include <cinttypes>
5 #include <cstring>
7 #include "esphome/core/hal.h"
8 #include "esphome/core/helpers.h"
9 #include "esphome/core/log.h"
10 
11 namespace esphome {
12 namespace i2c {
13 
14 static const char *const TAG = "i2c.idf";
15 
17  ESP_LOGCONFIG(TAG, "Setting up I2C bus...");
18  static i2c_port_t next_port = I2C_NUM_0;
19  port_ = next_port;
20 #if I2C_NUM_MAX > 1
21  next_port = (next_port == I2C_NUM_0) ? I2C_NUM_1 : I2C_NUM_MAX;
22 #else
23  next_port = I2C_NUM_MAX;
24 #endif
25 
26  if (port_ == I2C_NUM_MAX) {
27  ESP_LOGE(TAG, "Too many I2C buses configured");
28  this->mark_failed();
29  return;
30  }
31 
32  recover_();
33 
34  i2c_config_t conf{};
35  memset(&conf, 0, sizeof(conf));
36  conf.mode = I2C_MODE_MASTER;
37  conf.sda_io_num = sda_pin_;
38  conf.sda_pullup_en = sda_pullup_enabled_;
39  conf.scl_io_num = scl_pin_;
40  conf.scl_pullup_en = scl_pullup_enabled_;
41  conf.master.clk_speed = frequency_;
42  esp_err_t err = i2c_param_config(port_, &conf);
43  if (err != ESP_OK) {
44  ESP_LOGW(TAG, "i2c_param_config failed: %s", esp_err_to_name(err));
45  this->mark_failed();
46  return;
47  }
48  if (timeout_ > 0) { // if timeout specified in yaml:
49  if (timeout_ > 13000) {
50  ESP_LOGW(TAG, "i2c timeout of %" PRIu32 "us greater than max of 13ms on esp-idf, setting to max", timeout_);
51  timeout_ = 13000;
52  }
53  err = i2c_set_timeout(port_, timeout_ * 80); // unit: APB 80MHz clock cycle
54  if (err != ESP_OK) {
55  ESP_LOGW(TAG, "i2c_set_timeout failed: %s", esp_err_to_name(err));
56  this->mark_failed();
57  return;
58  } else {
59  ESP_LOGV(TAG, "i2c_timeout set to %d ticks (%d us)", timeout_ * 80, timeout_);
60  }
61  }
62  err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM);
63  if (err != ESP_OK) {
64  ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err));
65  this->mark_failed();
66  return;
67  }
68  initialized_ = true;
69  if (this->scan_) {
70  ESP_LOGV(TAG, "Scanning i2c bus for active devices...");
71  this->i2c_scan_();
72  }
73 }
75  ESP_LOGCONFIG(TAG, "I2C Bus:");
76  ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
77  ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
78  ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_);
79  if (timeout_ > 0) {
80  ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 "us", this->timeout_);
81  }
82  switch (this->recovery_result_) {
83  case RECOVERY_COMPLETED:
84  ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
85  break;
87  ESP_LOGCONFIG(TAG, " Recovery: failed, SCL is held low on the bus");
88  break;
90  ESP_LOGCONFIG(TAG, " Recovery: failed, SDA is held low on the bus");
91  break;
92  }
93  if (this->scan_) {
94  ESP_LOGI(TAG, "Results from i2c bus scan:");
95  if (scan_results_.empty()) {
96  ESP_LOGI(TAG, "Found no i2c devices!");
97  } else {
98  for (const auto &s : scan_results_) {
99  if (s.second) {
100  ESP_LOGI(TAG, "Found i2c device at address 0x%02X", s.first);
101  } else {
102  ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
103  }
104  }
105  }
106  }
107 }
108 
109 ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
110  // logging is only enabled with vv level, if warnings are shown the caller
111  // should log them
112  if (!initialized_) {
113  ESP_LOGVV(TAG, "i2c bus not initialized!");
114  return ERROR_NOT_INITIALIZED;
115  }
116  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
117  esp_err_t err = i2c_master_start(cmd);
118  if (err != ESP_OK) {
119  ESP_LOGVV(TAG, "RX from %02X master start failed: %s", address, esp_err_to_name(err));
120  i2c_cmd_link_delete(cmd);
121  return ERROR_UNKNOWN;
122  }
123  err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true);
124  if (err != ESP_OK) {
125  ESP_LOGVV(TAG, "RX from %02X address write failed: %s", address, esp_err_to_name(err));
126  i2c_cmd_link_delete(cmd);
127  return ERROR_UNKNOWN;
128  }
129  for (size_t i = 0; i < cnt; i++) {
130  const auto &buf = buffers[i];
131  if (buf.len == 0)
132  continue;
133  err = i2c_master_read(cmd, buf.data, buf.len, i == cnt - 1 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK);
134  if (err != ESP_OK) {
135  ESP_LOGVV(TAG, "RX from %02X data read failed: %s", address, esp_err_to_name(err));
136  i2c_cmd_link_delete(cmd);
137  return ERROR_UNKNOWN;
138  }
139  }
140  err = i2c_master_stop(cmd);
141  if (err != ESP_OK) {
142  ESP_LOGVV(TAG, "RX from %02X stop failed: %s", address, esp_err_to_name(err));
143  i2c_cmd_link_delete(cmd);
144  return ERROR_UNKNOWN;
145  }
146  err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS);
147  // i2c_master_cmd_begin() will block for a whole second if no ack:
148  // https://github.com/espressif/esp-idf/issues/4999
149  i2c_cmd_link_delete(cmd);
150  if (err == ESP_FAIL) {
151  // transfer not acked
152  ESP_LOGVV(TAG, "RX from %02X failed: not acked", address);
153  return ERROR_NOT_ACKNOWLEDGED;
154  } else if (err == ESP_ERR_TIMEOUT) {
155  ESP_LOGVV(TAG, "RX from %02X failed: timeout", address);
156  return ERROR_TIMEOUT;
157  } else if (err != ESP_OK) {
158  ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err));
159  return ERROR_UNKNOWN;
160  }
161 
162 #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
163  char debug_buf[4];
164  std::string debug_hex;
165 
166  for (size_t i = 0; i < cnt; i++) {
167  const auto &buf = buffers[i];
168  for (size_t j = 0; j < buf.len; j++) {
169  snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]);
170  debug_hex += debug_buf;
171  }
172  }
173  ESP_LOGVV(TAG, "0x%02X RX %s", address, debug_hex.c_str());
174 #endif
175 
176  return ERROR_OK;
177 }
178 ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) {
179  // logging is only enabled with vv level, if warnings are shown the caller
180  // should log them
181  if (!initialized_) {
182  ESP_LOGVV(TAG, "i2c bus not initialized!");
183  return ERROR_NOT_INITIALIZED;
184  }
185 
186 #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
187  char debug_buf[4];
188  std::string debug_hex;
189 
190  for (size_t i = 0; i < cnt; i++) {
191  const auto &buf = buffers[i];
192  for (size_t j = 0; j < buf.len; j++) {
193  snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]);
194  debug_hex += debug_buf;
195  }
196  }
197  ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str());
198 #endif
199 
200  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
201  esp_err_t err = i2c_master_start(cmd);
202  if (err != ESP_OK) {
203  ESP_LOGVV(TAG, "TX to %02X master start failed: %s", address, esp_err_to_name(err));
204  i2c_cmd_link_delete(cmd);
205  return ERROR_UNKNOWN;
206  }
207  err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
208  if (err != ESP_OK) {
209  ESP_LOGVV(TAG, "TX to %02X address write failed: %s", address, esp_err_to_name(err));
210  i2c_cmd_link_delete(cmd);
211  return ERROR_UNKNOWN;
212  }
213  for (size_t i = 0; i < cnt; i++) {
214  const auto &buf = buffers[i];
215  if (buf.len == 0)
216  continue;
217  err = i2c_master_write(cmd, buf.data, buf.len, true);
218  if (err != ESP_OK) {
219  ESP_LOGVV(TAG, "TX to %02X data write failed: %s", address, esp_err_to_name(err));
220  i2c_cmd_link_delete(cmd);
221  return ERROR_UNKNOWN;
222  }
223  }
224  if (stop) {
225  err = i2c_master_stop(cmd);
226  if (err != ESP_OK) {
227  ESP_LOGVV(TAG, "TX to %02X master stop failed: %s", address, esp_err_to_name(err));
228  i2c_cmd_link_delete(cmd);
229  return ERROR_UNKNOWN;
230  }
231  }
232  err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS);
233  i2c_cmd_link_delete(cmd);
234  if (err == ESP_FAIL) {
235  // transfer not acked
236  ESP_LOGVV(TAG, "TX to %02X failed: not acked", address);
237  return ERROR_NOT_ACKNOWLEDGED;
238  } else if (err == ESP_ERR_TIMEOUT) {
239  ESP_LOGVV(TAG, "TX to %02X failed: timeout", address);
240  return ERROR_TIMEOUT;
241  } else if (err != ESP_OK) {
242  ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err));
243  return ERROR_UNKNOWN;
244  }
245  return ERROR_OK;
246 }
247 
251 void IDFI2CBus::recover_() {
252  ESP_LOGI(TAG, "Performing I2C bus recovery");
253 
254  const gpio_num_t scl_pin = static_cast<gpio_num_t>(scl_pin_);
255  const gpio_num_t sda_pin = static_cast<gpio_num_t>(sda_pin_);
256 
257  // For the upcoming operations, target for a 60kHz toggle frequency.
258  // 1000kHz is the maximum frequency for I2C running in standard-mode,
259  // but lower frequencies are not a problem.
260  // Note: the timing that is used here is chosen manually, to get
261  // results that are close to the timing that can be archieved by the
262  // implementation for the Arduino framework.
263  const auto half_period_usec = 7;
264 
265  // Configure SCL pin for open drain input/output, with a pull up resistor.
266  gpio_set_level(scl_pin, 1);
267  gpio_config_t scl_config{};
268  scl_config.pin_bit_mask = 1ULL << scl_pin_;
269  scl_config.mode = GPIO_MODE_INPUT_OUTPUT_OD;
270  scl_config.pull_up_en = GPIO_PULLUP_ENABLE;
271  scl_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
272  scl_config.intr_type = GPIO_INTR_DISABLE;
273  gpio_config(&scl_config);
274 
275  // Configure SDA pin for open drain input/output, with a pull up resistor.
276  gpio_set_level(sda_pin, 1);
277  gpio_config_t sda_conf{};
278  sda_conf.pin_bit_mask = 1ULL << sda_pin_;
279  sda_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD;
280  sda_conf.pull_up_en = GPIO_PULLUP_ENABLE;
281  sda_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
282  sda_conf.intr_type = GPIO_INTR_DISABLE;
283  gpio_config(&sda_conf);
284 
285  // If SCL is pulled low on the I2C bus, then some device is interfering
286  // with the SCL line. In that case, the I2C bus cannot be recovered.
287  delayMicroseconds(half_period_usec);
288  if (gpio_get_level(scl_pin) == 0) {
289  ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the I2C bus");
290  recovery_result_ = RECOVERY_FAILED_SCL_LOW;
291  return;
292  }
293 
294  // From the specification:
295  // "If the data line (SDA) is stuck LOW, send nine clock pulses. The
296  // device that held the bus LOW should release it sometime within
297  // those nine clocks."
298  // We don't really have to detect if SDA is stuck low. We'll simply send
299  // nine clock pulses here, just in case SDA is stuck. Actual checks on
300  // the SDA line status will be done after the clock pulses.
301  for (auto i = 0; i < 9; i++) {
302  gpio_set_level(scl_pin, 0);
303  delayMicroseconds(half_period_usec);
304  gpio_set_level(scl_pin, 1);
305  delayMicroseconds(half_period_usec);
306 
307  // When SCL is kept LOW at this point, we might be looking at a device
308  // that applies clock stretching. Wait for the release of the SCL line,
309  // but not forever. There is no specification for the maximum allowed
310  // time. We yield and reset the WDT, so as to avoid triggering reset.
311  // No point in trying to recover the bus by forcing a uC reset. Bus
312  // should recover in a few ms or less else not likely to recovery at
313  // all.
314  auto wait = 250;
315  while (wait-- && gpio_get_level(scl_pin) == 0) {
316  App.feed_wdt();
317  delayMicroseconds(half_period_usec * 2);
318  }
319  if (gpio_get_level(scl_pin) == 0) {
320  ESP_LOGE(TAG, "Recovery failed: SCL is held LOW during clock pulse cycle");
321  recovery_result_ = RECOVERY_FAILED_SCL_LOW;
322  return;
323  }
324  }
325 
326  // By now, any stuck device ought to have sent all remaining bits of its
327  // transaction, meaning that it should have freed up the SDA line, resulting
328  // in SDA being pulled up.
329  if (gpio_get_level(sda_pin) == 0) {
330  ESP_LOGE(TAG, "Recovery failed: SDA is held LOW after clock pulse cycle");
331  recovery_result_ = RECOVERY_FAILED_SDA_LOW;
332  return;
333  }
334 
335  // From the specification:
336  // "I2C-bus compatible devices must reset their bus logic on receipt of
337  // a START or repeated START condition such that they all anticipate
338  // the sending of a target address, even if these START conditions are
339  // not positioned according to the proper format."
340  // While the 9 clock pulses from above might have drained all bits of a
341  // single byte within a transaction, a device might have more bytes to
342  // transmit. So here we'll generate a START condition to snap the device
343  // out of this state.
344  // SCL and SDA are already high at this point, so we can generate a START
345  // condition by making the SDA signal LOW.
346  delayMicroseconds(half_period_usec);
347  gpio_set_level(sda_pin, 0);
348 
349  // From the specification:
350  // "A START condition immediately followed by a STOP condition (void
351  // message) is an illegal format. Many devices however are designed to
352  // operate properly under this condition."
353  // Finally, we'll bring the I2C bus into a starting state by generating
354  // a STOP condition.
355  delayMicroseconds(half_period_usec);
356  gpio_set_level(sda_pin, 1);
357 
358  recovery_result_ = RECOVERY_COMPLETED;
359 }
360 
361 } // namespace i2c
362 } // namespace esphome
363 
364 #endif // USE_ESP_IDF
the WriteBuffer structure stores a pointer to a write buffer and its length
Definition: i2c_bus.h:30
void dump_config() override
void i2c_scan_()
Scans the I2C bus for devices.
Definition: i2c_bus.h:97
std::vector< std::pair< uint8_t, bool > > scan_results_
array containing scan results
Definition: i2c_bus.h:107
the ReadBuffer structure stores a pointer to a read buffer and its length
Definition: i2c_bus.h:24
ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override
timeout while waiting to receive bytes
Definition: i2c_bus.h:16
ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override
No error found during execution of method.
Definition: i2c_bus.h:13
I2C bus acknowledgment not received.
Definition: i2c_bus.h:15
Application App
Global storage of Application pointer - only one Application can exist.
bool scan_
Should we scan ? Can be set in the yaml.
Definition: i2c_bus.h:108
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
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
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition: i2c_bus.h:11
miscellaneous I2C error during execution
Definition: i2c_bus.h:19
call method to a not initialized bus
Definition: i2c_bus.h:17
stm32_cmd_t * cmd
Definition: stm32flash.h:96