-
Notifications
You must be signed in to change notification settings - Fork 7.6k
I2C issues, what are best practices for multiple I2C devices running in different tasks #5292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
we have been looking onto ways to make this work for everyone, but unfortunately the Arduino API for I2C is really designed to never be used in multitasking environment. I would say, your best bet is to implement a semaphore yourself and use that to ensure that two tasks will not try to use I2C at the same time. Something like the code below would work: xSemaphoreHandle wireSemaphore;
void readMPU(){
xSemaphoreTake(wireSemaphore, portMAX_DELAY);
Wire.beginTransaction(mpu_addr);
.......
Wire.endTransaction();
Wire.readFrom(mpu_addr, 2);
uint8_t x = Wire.read();
uint8_t y = Wire.read();
xSemaphoreGive(wireSemaphore);
}
void readBMP(){
xSemaphoreTake(wireSemaphore, portMAX_DELAY);
Wire.beginTransaction(bmp_addr);
.......
Wire.endTransaction();
Wire.readFrom(bmp_addr, 2);
uint8_t t = Wire.read();
uint8_t p = Wire.read();
xSemaphoreGive(wireSemaphore);
}
void setup(){
wireSemaphore = xSemaphoreCreateMutex();
} |
@me-no-dev pitty but clear answer, thx. We are already using the semaphore, but still getting the errors. I will triple check if I missed a wire call somewhere. Out of curiosity/interest, why is the semaphore not implemented at the lower level in the API? And before I redesign my pcb... ;-) is the SPI implementation thread safe? |
SPI is thread safe as long as you use transactions. The reason why Wire is not on it's own thread safe is because in the case of reading, you first read into a buffer and then you Wire.read() from that buffer. While we can take the semaphore in Wire.readFrom, it is not given that all bytes will be read to give back the semaphore when the buffer is empty. Also in cases of write->no stop->read, the whole thing needs to be executed at once by the peripheral, so if there is a chance that some other thread grabs the peripheral in between Wire.endTransaction() and Wire.readFrom(), result will be unknown for both threads. |
Hi,
We have a custom PCB with multiple I2C devices:
-BMP280 (barometer) getting values every 10 S
-2x SX1509 (IO-multiplexer) getting/sending values when needed
-ICM-20948 (9 axis motion tracking) getting values every 500 mS
-AB0805 (RTC) getting values every 10 S
16mB ESP32-D0WD
platform IO
espressif32@~3.2.0
4K7 pull ups on SCL and SDA
I2C speed 100Khz
Next to the I2C devices we are running:
-DHT22 (temp/humidity) via onewire, getting values every 10 S
-Several analog sensors, getting values every 10-30 S
-SIM7000 (GSM) via HW serial, values sent when changed (via MQTT), checking connection every 10 S
-Nextion (display) via HW serial, values are sent/received when changed or action is done on display
-Victron VE-Direct via software serial, values are read every 5 S
-ESP CAN, values are sent/received when changed or action is required
-BLE (NimBle) sending data to an app when values change or there is action on the app
We use several tasks for reading data and controlling outputs.
Some data handling/logic is timed but mostly using an observer pattern.
We have put a semaphore around all I2C actions in our code/libraries to prevent I2C actions from different tasks to occur simultaneously
Issue is that we are getting (seems random) I2C errors like:
-i2cProcQueue(): Bus busy, reinit
-emptyRxFifo(): no Storage location for 1
-division by 0 which seems to be related to I2C
below some different log outputs.
Frequency of errors varies, sometimes it runs without errors for several minutes, sometimes we get 1 "set" of I2C error data, sometimes three "sets" in a row.
I2C bus mostly recovers, sometimes with erroneous data being received from barometer (ie Node [28]: P: 744 hPa T: 33.2 C in stead of Node [28]: P: 1019 hPa T: 33.2 C). This would be acceptable.
But what is not acceptable is that it also happens that reading of the ICM-20948 keeps on failing (I2C is still working because correct BMP values are still being read/displayed)
The division by 0 obviously crashes the ESP.
We are unable to structurally reproduce the error
Pinning tasks to different cores seems to influence the error frequency/type
Adding logging seems to influence error frequency/type.
So we suspect timing to influence the issue...
Are there any best practices for I2C wrt
-Task priority
-Core pinning
-Using semaphores around I2C calls/blocks
-Need to separate interrupt heavy tasks (Software serial is a heavy interrupt user, could this interfere)
-Polling of I2C sensors
-Troubleshooting (how can I pinpoint if there is a specific I2C ic/sensor that is causing this
Is there a way to streamline I2C bus usage in a better way than using semaphores?
@stickbreaker , I have seen loads of great advice/help from you wrt I2C, would appreciate if you could give some advice on how to troubleshoot this.
Thx!!
Used I2C related HW libraries:
https://github.com/curransinha/AB0805_lib/blob/master/AB0805.cpp (we made a fork and refactored to not having to use external I2C lib)
https://github.com/adafruit/Adafruit_BMP280_Library
https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary.git
https://github.com/sparkfun/SparkFun_SX1509_Arduino_Library
The text was updated successfully, but these errors were encountered: