esp-rs / esp-idf-hal

embedded-hal implementation for Rust on ESP32 and ESP-IDF
https://docs.esp-rs.org/esp-idf-hal/
Apache License 2.0
470 stars 171 forks source link

How can I share a I2CDriver? #496

Closed empirephoenix closed 3 weeks ago

empirephoenix commented 4 weeks ago

I currently try to wrap my head around, how I could share a I2CDriver between multiple I2CDevices.

Currently I'm force creating a new driver based on the first ones port. But since these fields are private this is obviously no the correct way to do it. let driver = I2cDriver{ i2c: port as u8, _p: std::marker::PhantomData, };

I tried via unchecked_clone to clone all pins and then create a second driver via new(), but that one crashes since it tries to low lever init the i2c hardware a second time. E (1035) i2c: i2c driver install error thread 'main' panicked at src/plant_hal.rs:1016:65: called Result::unwrap() on an Err value: ESP_FAIL (error code -1) note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

Is there any solution to this that I do not see?

ivmarkov commented 4 weeks ago

It would've been better to ask this question on the Matrix ESP-RS chat rather than opening an issue here, but anyway.

In any case, you should absolutely not play and hack around the I2cDriver internals!

Currently I'm force creating a new driver based on the first ones port. But since these fields are private this is obviously no the correct way to do it. let driver = I2cDriver{ i2c: port as u8, _p: std::marker::PhantomData, };

The "port" ^^^ simply means which of the (potentially multiple) I2c peripherals (or "bus"-es) should be used by this I2cDriver. esp32 as far as I remember has only one i2c peripheral/bus, but the c3 and others have two.

( Note that an i2c "peripheral" (or bus) is NOT the same as the slave device(s) you attach to this bus, like your temperature sensor, or CMOS clock or whatever. All of these can be attached to a single i2c bus (one I2cDriver) and thus use the same i2c pins, or - which is simple - each device can be attached to one of the 2 i2c bus-es supported by newer chips. )

Now, I assume you have TWO (or more) such devices, but just one I2cDriver and so you are struggling because you cannot have your e.g. CMOS clock Rust driver and your e.g. temp sensor Rust driver "sharing" the single I2cDriver.

To do this:

  1. Wrap the Ic2Driver in a std::sync::Mutex with e.g. let my_i2c_driver_mutex = std::sync::Mutex::new(my_i2c_driver)
  2. Create a MutexDevice::new(&my_i2c_driver_mutex) and pass this one to e.g. your CMOS clock
  3. Create another MutexDevice::new(&my_i2c_driver_mutex) and pass this one to e.g. your temp sensor

If you want the mutex to be shared into the MutexDevice instances with an Arc instead of by & just copy-paste the mutex.rs file I referred to and replace the & references to the mutex with an Arc.

empirephoenix commented 3 weeks ago

Ah sorry did not know there is a matrix channel at all. Joined it now and will ask similar stuff there in the future.