Table of content

What is this :grey_question:

The rules of the game are explained in my original.

59th Challenge

Challenge

Build on yesterday’s example and use the humdity and temperature sensor! Here is a picture of the generic node again. Its a piece of hardware with an STM32WL5x and has a bunch of sensors and actuators as well as the possibility to use LoRaWAN.

Solution :white_check_mark:

Lets start from a template again, same folder as yesterday.

    cargo generate \
    --git https://github.com/stm32-rs/stm32wlxx-hal\
    --name stm32
    

The template, looks like this, when running tree -L 3

    .
    ├── CHANGELOG.md
    ├── Cargo.lock
    ├── Cargo.toml
    ├── LICENSE-APACHE
    ├── LICENSE-MIT
    ├── README.md
    ├── examples
    │   ├── Cargo.toml
    │   ├── examples
    │   └── src
    ├── hal
    │   ├── Cargo.toml
    │   └── src
    ├── lora-e5-bsp
    │   ├── Cargo.toml
    │   ├── README.md
    │   └── src
    ├── memory.x
    ├── nucleo-wl55jc-bsp
    │   ├── Cargo.toml
    │   ├── README.md
    │   └── src
    ├── rustfmt.toml
    └── testsuite
        ├── Cargo.toml
        ├── README.md
        ├── runall.py
        └── src
    

So the i2c slave has the address 0x70 and is connected to first i2c bus of the stm32wl (SDA = PA10 and SCL = PA09 ). The slave has the part number SHTC3 which is a “±2% (20-80%RH) Digital humidity and temperature sensor” by the company senserion.

| Command         | Value   |
| --------        | ------- |
| Sleep           | 0xB098  |
| Wakeup          | 0x3517  |
| measurement cmd | 0x7866  |
| readout id      | 0xEFC8  |

The measurement command is without clockststreching and reading temperature first. To get the last measurement “by sending a START condition followed by an I2C read header. The sensor will acknowledge the reception of the read header and send two bytes of data followed by one byte CRC checksum and another two bytes of data followed by one byte CRC checksum.”

After starting, I realized, that the reposioriy already contains a very similar test case in the i2c file for sht3x which has a slave address of 0x44. There is also an offical c diver which can be used as a reference which is avaibable under documentation.

More importantly, however, there are load switched for the i2c slaves which I did not see at first. You must enable B12 so the sensors are powered. It took me longer than it should have to realize this.. As well as this a timeout of 240uS must be made when booting the device. Because I ran out of time I’m only going to read out the id of the i2c slave for now.

    #![no_std]
    #![no_main]

    use defmt::unwrap;
    use defmt_rtt as _; // global logger
    use nucleo_wl55jc_bsp::hal::{
        cortex_m::{self, delay::Delay},
        embedded_hal::blocking::i2c::{WriteRead,Write},
        gpio::{pins, Output, PinState,PortB,PortA},
        i2c::{I2c1},
        pac::{self},
        rcc,
        util::new_delay,
    };
    use panic_probe as _;

    const I2C_FREQUENCY: u32 = 100_000;

    #[defmt_test::tests]
    mod tests {
        use super::*;

        #[init]
        fn init() -> I2c1<(pins::A9, pins::A10)> {
            cortex_m::interrupt::free(|cs| {
                let mut dp: pac::Peripherals = unwrap!(pac::Peripherals::take());
                let cp: pac::CorePeripherals = defmt::unwrap!(pac::CorePeripherals::take());

                unsafe { rcc::set_sysclk_msi_max(&mut dp.FLASH, &mut dp.PWR, &mut dp.RCC, cs) };
                let gpioa: PortA = PortA::split(dp.GPIOA, &mut dp.RCC);
                let gpiob: PortB = PortB::split(dp.GPIOB, &mut dp.RCC);
     
                let mut sensor_power_enable: Output<pins::B12> =
                    cortex_m::interrupt::free(|cs| Output::default(gpiob.b12, cs));


                // Power the sensor
                sensor_power_enable.set_level(PinState::High);
                let mut delay: Delay = new_delay(cp.SYST, &dp.RCC);
                // wait for the sensor to be powered up

                delay.delay_us(240);

                I2c1::new(
                    dp.I2C1,
                    (gpioa.a9, gpioa.a10),
                    I2C_FREQUENCY,
                    &mut dp.RCC,
                    false,
                    cs,
                )
            })
        }


        #[test]
        fn shtc3_measurement(i2c: &mut I2c1<(pins::A9, pins::A10)>) {
            defmt::warn!("A SHTC3 sensor must be connected to the board on pins A9 (SCL) & A10 (SDA) for this test to work");
            let mut cmd: [u8; 2] = [0x35, 0x17];
            let mut response: [u8; 3] = [0; 3];

            let result = i2c.write(0x70, &cmd);

            match result {
                Ok(()) => defmt::info!("SHTC3 was woken up"),
                Err(e) => defmt::error!("I2C error: {}", e),
            }

            cmd = [0xEF, 0xC8];

            let result = i2c.write_read(0x70, &cmd, &mut response);

            match result {
                Ok(()) => defmt::info!("SHTC3 ID = {:x} ",response),
                Err(e) => defmt::error!("I2C error: {}", e),
            }
        }
    }
    

When compiling and running DEFMT_LOG=info cargo test -p testsuite --target thumbv7em-none-eabi --bin gn_i2cwe get

    (1/1) running `shtc3_measurement`...
    └─ i2c::tests::__defmt_test_entry @ src/i2c.rs:57
    WARN  A SHTC3 sensor must be connected to the board on pins A9 (SCL) & A10 (SDA) for this test to work
    └─ i2c::tests::shtc3_measurement @ src/i2c.rs:58
    INFO  SHTC3 was woken up
    └─ i2c::tests::shtc3_measurement @ src/i2c.rs:65
    INFO  SHTC3 ID = [8, 87, 5b] 
    └─ i2c::tests::shtc3_measurement @ src/i2c.rs:74
    all tests passed!
    └─ i2c::tests::__defmt_test_entry @ src/i2c.rs:19
    ───────────────────────────────────────────────
    See the repo on [as a fork of the original repos](https://github.com/maebli/stm32wlxx-hal).  For the lazy, here is the source embedded into this page, this was extremely simple as it is basically a copy of the existing example gpio-blink
    

The output is [8, 87, 5b] which means the CRC is 0xb5 according to the datasheet bits 11 & 5 to 0: SHTC3 identifier. 0x887 = 0b100010000111 = 0b1xxxxx000111. The datasheet makes it unclear what this number means, but it can probably be used to id the chip.