Embedded Rust - Capacitive Soil Moisture Sensor and the STM32F401
In this chapter, we'll build a simple embedded project using the STM32F401 microcontroller and a capacitive soil moisture sensor. We'll include an LED to indicate the moisture level of the soil. The LED will be blinking when the soil is too dry.
Getting prepared
To build this project, you need the following:
- Capacitive Soil Moisture Sensor, I've got mine from Az-Delivery,
- Orange LED,
- 150Ω resistor,
Wiring
Connect the sensor to the STM board like this:
As you can see, the sensor needs 5V to power up. You can use an external power supply or connect the 5V pin of the ST-Link to the sensor, which is what I did.
The sensor has an analog output that we'll connect to the PA7 pin of the STM32F401. The PA7 pin is connected to the ADC1 channel 7.
The LED is connected to the PA0 pin of the STM32F401.
Make main.rs
look like this:
#![no_std] #![no_main] use defmt::*; use embassy_executor::Spawner; use embassy_stm32::{ adc::Adc, gpio::{Level, Output, Speed}, peripherals::{ADC1, PA7}, }; use embassy_sync::{ blocking_mutex::raw::ThreadModeRawMutex, channel::{Channel, Sender}, }; use embassy_time::{Delay, Timer}; use {defmt_rtt as _, panic_probe as _}; const DRYNESS: u16 = 3000; static CHANNEL: Channel<ThreadModeRawMutex, u16, 64> = Channel::new(); #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); // orange LED let mut led = Output::new(p.PA0, Level::High, Speed::Low); info!("Setting up ADC"); let mut delay = Delay; let adc = Adc::new(p.ADC1, &mut delay); let pin = p.PA7; spawner .spawn(read_moisture(adc, pin, CHANNEL.sender())) .unwrap(); let rx_moisture = CHANNEL.receiver(); loop { let alarm_on = rx_moisture.receive().await > DRYNESS; // Blink the LED if the moisture is too low if alarm_on { led.set_high(); Timer::after_millis(500).await; } led.set_low(); } } #[embassy_executor::task(pool_size = 2)] async fn read_moisture( mut adc: Adc<'static, ADC1>, mut pin: PA7, tx_moisture: Sender<'static, ThreadModeRawMutex, u16, 64>, ) { loop { let v = adc.read(&mut pin); info!("ADC value: {}", v); tx_moisture.send(v).await; Timer::after_millis(1000).await; } }
You need these dependencies in your Cargo.toml
:
[dependencies]
embassy-stm32 = { version = "0.1.0", features = ["defmt", "stm32f401cc", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] }
embassy-sync = { version = "0.5.0", features = ["defmt"] }
embassy-executor = { version = "0.5.0", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.3.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", features = ["defmt"] }
embassy-net = { version = "0.4.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] }
defmt = "0.3"
defmt-rtt = "0.4"
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
embedded-io = { version = "0.6.0" }
embedded-io-async = { version = "0.6.1" }
panic-probe = { version = "0.3", features = ["print-defmt"] }
Run the project with:
$ cargo run --release
You should see the moisture level printed on the console. The LED should be blinking when the soil is too dry.
It could be that you need to adjust the DRYNESS
value to match your sensor.