ESP32: I2C transfers block the scheduler, and each i2c call costs about 1 ms

Đang mở
#2,430 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 2 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
35/100
Loại issue
Tính năng
Độ rõ ràng
Cần làm rõ
Mức độ hoạt động
Sôi nổi
Công nghệ
c, erlang

Hướng nghiên cứu

Start by reading the ESP32 I2C port driver and event loop; the issue does not name specific files or tests. The report identifies synchronous transfers and delayed event polling as possible bottlenecks, but leaves open whether to add a non-blocking path, change scheduler wakeups, or expose batched sensor results. Done would require agreeing on the desired behavior and validating its effect on scheduler delays.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

I've been writing firmware for a little robot with an ESP32-C6 (Microbots CodeCell C6 Drive) that has a BNO085 IMU and a VCNL4040 proximity sensor on the same I2C bus at 400 kHz. The BNO085 clock-stretching timeouts were the first problem, and the new I2C driver PR sorts those out. But once the IMU was talking, my control loops still crawled, so I spent some time measuring where the time goes. I'm not sure this counts as a bug, but the numbers might be useful.

I wrote a small Erlang module that does nothing except read the BNO085: soft reset, turn on the rotation vector at 100 Hz, then read each packet (a 4-byte header read, then the full packet) whenever INT goes low. Each loop it also reads two bytes from the VCNL4040, which never stretches the clock, as a baseline. A second process just runs timer:sleep(1) in a loop and records how late it wakes up. This is all on v0.7.0-beta.0 with ESP-IDF 5.5.5.

With the VM otherwise idle, a two-byte i2c:read_bytes/4 from the VCNL4040 takes about 1.1 ms on the release image. For comparison, a gpio:read/2 (a port:call to a port that doesn't touch any hardware) takes about 0.7 ms, and a plain message round trip to another process takes about 270 µs. I put some timers inside the port driver (debug build, so a bit slower than release) and the bus transfer itself, i2c_master_cmd_begin, averages about 0.57 ms across all transfers including the BNO085's, while the whole i2cdriver_consume_mailbox handler averages about 0.82 ms. So for a simple read, most of the cost is the round trip through the port rather than the bus.
With the IMU streaming at 100 Hz, each BNO085 read takes about 2.5–3 ms and the VCNL4040 read about 2.2 ms, and the sleep(1) process wakes up 0.7–1.8 ms late on average and occasionally 18–35 ms late, depending on the build (legacy driver with the timeout raised, or the new driver PR). Because the transfers run synchronously on the scheduler, nothing else gets to run while one is in flight. In the real firmware, with a handful of other processes going, it all piled up: a GenServer.call with no I/O took 8–20 ms and loops meant to run at 20 Hz ran at about 9 Hz.

I tried moving the legacy port driver's i2c_master_cmd_begin onto a FreeRTOS worker task and replying from an event listener on a queue in event_set. That stopped the scheduler blocking, but the replies then sat around until the scheduler's next event poll. On a busy VM an idle two-byte read went from about 1.7 ms to 19 ms, so it made things worse overall. I don't know the event loop well enough to tell whether there's a cheap way for a finished job to wake the scheduler straight away. Changing the build from -Og to -O2 shaved 10–25% off everything but didn't change the overall picture.

A few things I'm curious about. Is a ~270 µs message round trip normal on a single-core C6, or does that suggest something's off in how I'm building? Would a non-blocking I2C path be welcome, and if so, how should a completed transfer wake the scheduler? And for sensors like this, would you rather people batch into fewer, larger reads, or move the protocol into a native task and just expose the results?

Happy to share the test module, or to try things on the hardware.

Ngôn ngữ chính
Erlang
Star
2.3k
Fork
171
Merge trung bình
5 ngày 11 giờ
Pull request đã merge (30 ngày)
19

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của atomvm/AtomVM

Tất cả issue của atomvm/AtomVM

Issue tương tự

Thêm issue về Embedded & IoT

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.