I2C and BME280, missing trait

Hello Rustaceans,

I’m new to embedded in general and Rust’s embedded ecosystem in particular, so please bear with me :-). I’m trying to talk to the Bosch BME280 sensor from my Blue Pill board (STM32F103C8T6). I’ve found the following crate bme280. I’m using version 0.1.1 (latest) in combination with stm32f1xx-hal version 0.3.0.

When compiling the following code:

#[entry]
fn main() -> ! {
    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = pac::Peripherals::take().unwrap();
    let mut rcc = dp.RCC.constrain();

    // Set up the I2C bus
    let mut flash = dp.FLASH.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
    let scl = gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh);
    let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);
    let mode = Mode::Standard { frequency: 100 };
    let i2c = I2c::i2c1(
        dp.I2C1,
        (scl, sda),
        &mut afio.mapr,
        mode,
        clocks,
        &mut rcc.apb1);

    // Set up the BME280
    let delay = Delay::new(cp.SYST, clocks);
    let mut bme280 = BME280::new_primary(i2c, delay);
    bme280.init().unwrap();

    loop {}
}

I get the following error:

error[E0277]: the trait bound `stm32f1xx_hal::i2c::I2c<stm32f1::stm32f103::I2C1, (stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>: embedded_hal::blocking::i2c::Read` is not satisfied
  --> src/main.rs:63:22
   |
63 |     let mut bme280 = BME280::new_primary(i2c, delay);
   |                      ^^^^^^^^^^^^^^^^^^^ the trait `embedded_hal::blocking::i2c::Read` is not implemented for `stm32f1xx_hal::i2c::I2c<stm32f1::stm32f103::I2C1, (stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>`
   |
   = note: required by `bme280::BME280::<I2C, D>::new_primary`

I couldn’t find a suitable impl for the Read, trait. Should I implement it myself? I did found an implementation for the Read trait for stm32f1xx_hal::i2c::BlockingI2c, should I use that?

Any pointers to documentation, examples etc. is welcome. I’ve came this far by copy-pasting code from documentation, but I’m not really sure what I’m doing :wink:

Thanks in advance!

It looks like the bne crate requires a blocking i2c implementation byt you’re giving it a non-blocking impl. Try using this instead: https://docs.rs/stm32f1xx-hal/0.3.0/stm32f1xx_hal/i2c/struct.BlockingI2c.html

OK, thank you, that works. I now have an Arbitration error from I2C. Could this be due to the BME280 crate, hardware, or because of my configuration parameters?

    let i2c = i2c::BlockingI2c::i2c1(
        dp.I2C1,
        (scl, sda),
        &mut afio.mapr,
        mode,
        clocks,
        &mut rcc.apb1,
        1000000,
        5,
        1000000,
        1000000);

    // Set up the BME280
    let delay = Delay::new(cp.SYST, clocks);
    let mut bme280 = BME280::new_primary(i2c, delay);
    bme280.init().unwrap(); // Error I2c(Arbitration) here...

Nevermind, it works now. The code that works is:

#![deny(unsafe_code)]
#![no_std]
#![no_main]
extern crate panic_semihosting;
extern crate bme280;

use stm32f1xx_hal::{
    prelude::*,
    pac,
    rtc::Rtc,
    delay::Delay,
    i2c,
};

use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;
use bme280::BME280;

#[entry]
fn main() -> ! {
    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = pac::Peripherals::take().unwrap();

    let mut rcc = dp.RCC.constrain();

    // Set up the I2C bus
    let mut flash = dp.FLASH.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
    let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl);
    let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl);
    let mode = i2c::Mode::Standard { frequency: 40 };
    let i2c = i2c::BlockingI2c::i2c1(
        dp.I2C1,
        (scl, sda),
        &mut afio.mapr,
        mode,
        clocks,
        &mut rcc.apb1,
        10000,
        5,
        10000,
        10000);
    hprintln!("I2C ready.").unwrap();

    // Set up the BME280
    let delay = Delay::new(cp.SYST, clocks);
    let mut bme280 = BME280::new_primary(i2c, delay);
    bme280.init().expect("BME280 initialization failed.");
    hprintln!("BME280 ready.").unwrap();

    loop {
        cortex_m::asm::delay(1 * 100_000_000);

        let measurements = bme280.measure().unwrap();
        hprintln!("Temperature × 10°C: {}", (measurements.temperature * 10.0) as i32).unwrap();
    }
}

The strange thing is that when I try to use hprintln! with a float (f32 or f64) the linker complains that there is not enough space. Formatting an i32 works, so I just multiply the temperature by 10 and cast it to an i32.

Float formatting code is pretty large (in terms of number of instructions required). Rust only pulls it in if you use it, unlike printf in C, so this is probably a sign that your application is using most of Flash already.

If you aren’t already, compile your application in release mode (--release) or raise the optimization level for debug builds to 1 (which will slightly impact debuggability) to reduce Flash usage.

(raising the level for debug builds is…)

[profile.debug]
opt-level = "1"

@cbiffle: Thanks!

Adding:

[profile.dev]
opt-level = 1

to Cargo.toml fixed it. I now can format my float’s with the hprintln! macro!