rust-embedded-community / ssd1306

SSD1306 OLED driver
Apache License 2.0
313 stars 70 forks source link

Using an async I2C instance errors on `I2CDisplayInterface::new()` #215

Open Sycrosity opened 2 months ago

Sycrosity commented 2 months ago

Description of the problem/feature request/other

When using an asynchronous I2C instance to initialise I2CDisplayInterface, (such as esp_hal::i2c::I2C<'static, esp_hal::peripherals::I2C0, Async>,), which implements embedded_hal_async::i2c::I2c rather than embedded_hal::i2c::I2c, an error occurs.

I wrote an implimentation in embedevices-rs/ssd1306 which creates the trait bounds for this, except it doesn't account for people using the library without async. Maybe an async version of I2CDisplayInterface (e.g. I2CDisplayInterfaceAsync) or additional trait bounds which are enabled when the async feature is enabled would fix this issue?

While this error isn't particularly readable, it shows the problem:

error[E0277]: the trait bound `embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice<'static, NoopRawMutex, I2C<'static, I2C0, Async>>: embedded_hal::i2c::I2c` is not satisfied
   --> src/display.rs:106:38
    |
106 |             I2CDisplayInterface::new(i2c),
    |             ------------------------ ^^^ the trait `embedded_hal::i2c::I2c` is not implemented for `I2cDevice<'static, NoopRawMutex, ...>`, which is required by `&mut embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice<'static, NoopRawMutex, I2C<'static, I2C0, Async>>: embedded_hal::i2c::I2c`
    |             |
    |             required by a bound introduced by this call
    |
    = help: the following other types implement trait `embedded_hal::i2c::I2c<A>`:
              <&mut T as embedded_hal::i2c::I2c<A>>
              <I2C<'_, T, DM> as embedded_hal::i2c::I2c>
              <I2cStub as embedded_hal::i2c::I2c>
              <embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice<'_, M, BUS> as embedded_hal::i2c::I2c>
              <embassy_embedded_hal::shared_bus::blocking::i2c::I2cDeviceWithConfig<'_, M, BUS> as embedded_hal::i2c::I2c>
    = note: required for `&mut I2cDevice<'static, NoopRawMutex, ...>` to implement `embedded_hal::i2c::I2c`
note: required by a bound in `I2CDisplayInterface::new`
   --> /Users/[me]/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ssd1306-0.9.0/src/i2c_interface.rs:15:12
    |
13  |     pub fn new<I>(i2c: I) -> I2CInterface<I>
    |            --- required by a bound in this associated function
14  |     where
15  |         I: embedded_hal::i2c::I2c,
    |            ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `I2CDisplayInterface::new`
    = note: the full name for the type has been written to '/Users/[me]/dev/embedded/teeny/target/riscv32imc-unknown-none-elf/debug/deps/teeny-833ec3d35043f488.long-type-11131129282391322325.txt'
    = note: consider using `--verbose` to print the full type name to the console
sjoerdsimons commented 2 months ago

Fwiw you can use the I2C interface directly by doing e.g. I2CInterface::new(i2c, 03xc, 0x40) rathe then going though the I2CDisplayInterface helper, avoiding the need to fork.

Creating a Async version with maybe_async_cfg should be trivial; The other option is to just drop the trait bound, though that would push the error down to later in the code if it doesn't do i2c (or async i2c)

Sycrosity commented 2 months ago
  ::: /Users/louis/.cargo/registry/src/index.crates.io-6f17d22bba15001f/display-interface-i2c-0.5.0/src/lib.rs:11:1
   |
11 | pub struct I2CInterface<I2C> {
   | ---------------------------- doesn't satisfy `_: WriteOnlyDataCommand`

unfortunately, using I2CInterface doesn't seem to work either (unless I'm missing something) as this does not satisfy the other trait bound. maybe_async_cfg seems like a decent way of going about this.