rust-embedded-community / ssd1306

SSD1306 OLED driver
Apache License 2.0
299 stars 68 forks source link

How to use display with multiple I2C peripherals ([shared-bus] crate?) #171

Closed besselfunct closed 1 year ago

besselfunct commented 1 year ago

Description of the problem/feature request/other

I'm a relative newbie to Rust, and I'd like to perform a simple temperature reading and push it to a display. As far as I understand, I can't use the same I2C interface for all of the items on my bus, since a single peripheral takes ownership of the whole I2C interface. I'm trying to use the shared-bus crate as used in the Ferrous Systems training found here.

When I attempt to use this, I get an error that WriteOnlyDataCommand is not implemented for I2cProxy.

Given that I expect this is a fairly common problem, is there an established solution that works well with the Ssd1306 crate?

Test case (if applicable)

use std::sync::Arc;
use std::thread;
use std::time::Duration;

use anyhow;

use shared-bus;

use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported

use embedded_hal::digital::v2::OutputPin;

use esp_idf_hal::{
    delay::FreeRtos,
    i2c::{config::MasterConfig, Master, MasterPins, I2C0},
    peripherals::Peripherals,
    prelude::*,
};

use embedded_graphics::{
    mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
    pixelcolor::BinaryColor,
    prelude::*,
    text::{Baseline, Text},
};

use ssd1306::{mode::BufferedGraphicsMode, prelude::*, I2CDisplayInterface, Ssd1306};

use esp_idf_sys::EspError;

fn main() -> anyhow::Result<()> {
    esp_idf_sys::link_patches();
    println!("Hello, world!");

    let peripherals = Peripherals::take().unwrap();

    let sda = peripherals.pins.gpio7;
    let scl = peripherals.pins.gpio6;

    let i2c = Master::<I2C0, _, _>::new(
        peripherals.i2c0,
        MasterPins { sda, scl },
        <MasterConfig as Default>::default().baudrate(400.kHz().into()),
    )?;

    let bus = shared_bus::BusManagerSimple::new(i2c);

    let proxy_1 = bus.acquire_i2c();
    let proxy_2 = bus.acquire_i2c();

    let mut display = Ssd1306::new(proxy_1, DisplaySize128x32, DisplayRotation::Rotate0) // This line results in an error
        .into_buffered_graphics_mode();
    display.init().unwrap();

    let text_style = MonoTextStyleBuilder::new()
        .font(&FONT_6X10)
        .text_color(BinaryColor::On)
        .build();

    Text::with_baseline("Hello world!", Point::zero(), text_style, Baseline::Top)
        .draw(&mut display)
        .unwrap();

    display.flush().unwrap();

    let mut led = peripherals.pins.gpio4.into_output().unwrap();

    loop{
        led.set_high().unwrap();
        thread::sleep(Duration::from_millis(1000));

        led.set_low().unwrap();
        thread::sleep(Duration::from_millis(1000));
    }

    Ok(())

}
rfuest commented 1 year ago

Your code doesn't work because you aren't wrapping proxy_1 in a display interface. This is the step you are missing: https://github.com/jamwaffles/ssd1306/blob/897ab8e7b44779eea88449fbe058910720ee285d/examples/graphics_i2c_128x32.rs#L66 You'll need to add let interface = ssd1306::I2CDisplayInterface::new(proxy_1); and use interface as the first parameter to the Ssd1306::new constructor.

besselfunct commented 1 year ago

Ah! That makes sense. I really appreciate your assistance! I apologize for asking such a straightforward question, but I'm still quite new to Rust. I also haven't done much programming outside of data analysis so this constructor stuff is quite new to me.

I appreciate you taking the time to answer my question!

rfuest commented 1 year ago

I apologize for asking such a straightforward question,...

No need to apologize, I'm glad that I could help.