diff --git a/hardware_code/.cargo/config.toml b/hardware_code/.cargo/config.toml new file mode 100644 index 00000000000..c7522fd2c5d --- /dev/null +++ b/hardware_code/.cargo/config.toml @@ -0,0 +1,13 @@ + +[target.thumbv6m-none-eabi] +runner = "probe-rs run --chip RP2040 --protocol swd" +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + "-C", "inline-threshold=5", + "-C", "no-vectorize-loops", +] + +[build] +target = "thumbv6m-none-eabi" diff --git a/hardware_code/Cargo.toml b/hardware_code/Cargo.toml new file mode 100644 index 00000000000..929665209f3 --- /dev/null +++ b/hardware_code/Cargo.toml @@ -0,0 +1,55 @@ + +[package] +name = "parking_sensor" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[[bin]] +name = "parking_sensor" +path = "src/main.rs" + +[dependencies] +embassy-executor = { version = "0.7", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-rp = { version = "0.3", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl"] } +embassy-time = { version = "0.4", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-sync = { version = "0.6", features = ["defmt"] } +embassy-futures = { version = "0.1" } + +# I2C async +embedded-hal-async = "1" +embedded-hal = "1" + +# LCD 1602 over I2C (PCF8574 backpack) +ag-lcd = { version = "0.2", default-features = false } +port-expander = "0.7" + +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"] } + +cortex-m = { version = "0.7", features = ["inline-asm"] } +cortex-m-rt = "0.7" +static_cell = "2" +fixed = "1" +fixed-macro = "1" + +[profile.dev] +debug = 2 +debug-assertions = true +opt-level = 1 +overflow-checks = true + +[profile.release] +debug = 2 +debug-assertions = false +opt-level = "s" +overflow-checks = false +lto = "fat" +codegen-units = 1 + +# cargo build/run +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip RP2040 --protocol swd" + +[build-dependencies] diff --git a/hardware_code/build.rs b/hardware_code/build.rs new file mode 100644 index 00000000000..fcc9cea7cf4 --- /dev/null +++ b/hardware_code/build.rs @@ -0,0 +1,5 @@ +fn main() { + + println!("cargo:rerun-if-changed=memory.x"); + println!("cargo:rerun-if-changed=build.rs"); +} \ No newline at end of file diff --git a/hardware_code/memory.x b/hardware_code/memory.x new file mode 100644 index 00000000000..abdd87c52cf --- /dev/null +++ b/hardware_code/memory.x @@ -0,0 +1,18 @@ +/* Raspberry Pi Pico WH/ RP2040 memory layout */ +MEMORY { + /* NOTE: RP2040 boots from external QSPI flash. + The first 256 bytes are reserved for the second-stage bootloader. */ + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* Second-stage bootloader – must be the very first section in flash. */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; diff --git a/hardware_code/src/buzzer.rs b/hardware_code/src/buzzer.rs new file mode 100644 index 00000000000..da31d60f414 --- /dev/null +++ b/hardware_code/src/buzzer.rs @@ -0,0 +1,52 @@ +use embassy_rp::pwm::{Config as PwmConfig, Pwm}; +use embassy_time::{Duration, Timer}; +use crate::distance::Zone; + + +const BEEP_FREQ_HZ: u32 = 2_700; + + +const SYS_CLK_HZ: u32 = 125_000_000; + + +pub fn make_beep_config() -> PwmConfig { + let wrap = (SYS_CLK_HZ / BEEP_FREQ_HZ).saturating_sub(1) as u16; + let mut cfg = PwmConfig::default(); + cfg.top = wrap; + cfg.compare_a = wrap / 2; + cfg +} + + +pub fn make_silent_config() -> PwmConfig { + let mut cfg = PwmConfig::default(); + cfg.top = 0xFFFF; + cfg.compare_a = 0; // 0 % duty → no sound + cfg +} + + +pub fn set_buzzer_zone(pwm: &mut Pwm<'_>, zone: Zone, beeping_on: bool) { + let cfg = match zone { + Zone::Clear => make_silent_config(), + Zone::Warning => { + if beeping_on { + make_beep_config() + } else { + make_silent_config() + } + } + Zone::Danger => make_beep_config(), + }; + pwm.set_config(&cfg); +} + +pub async fn half_period(zone: Zone) { + let period_ms = zone.beep_period_ms(); + if period_ms > 0 { + Timer::after(Duration::from_millis(period_ms / 2)).await; + } else { + + Timer::after(Duration::from_millis(50)).await; + } +} diff --git a/hardware_code/src/distance.rs b/hardware_code/src/distance.rs new file mode 100644 index 00000000000..68c04b4d515 --- /dev/null +++ b/hardware_code/src/distance.rs @@ -0,0 +1,34 @@ + +#[derive(Clone, Copy, PartialEq, Eq, defmt::Format)] +pub enum Zone { + Clear, + Warning, + Danger, +} + +impl Zone { + + pub fn from_sensors(s1_triggered: bool, s2_triggered: bool) -> Self { + match (s1_triggered, s2_triggered) { + (false, false) => Zone::Clear, + (true, false) | (false, true) => Zone::Warning, + (true, true) => Zone::Danger, + } + } + + pub fn label(self) -> &'static str { + match self { + Zone::Clear => "CLEAR >100cm ", + Zone::Warning => "WARNING ~50cm ", + Zone::Danger => "DANGER! <20cm ", + } + } + + pub fn beep_period_ms(self) -> u64 { + match self { + Zone::Clear => 0, + Zone::Warning => 600, // slow beep + Zone::Danger => 150, // rapid beep + } + } +} diff --git a/hardware_code/src/lcd.rs b/hardware_code/src/lcd.rs new file mode 100644 index 00000000000..8e7bb8da5af --- /dev/null +++ b/hardware_code/src/lcd.rs @@ -0,0 +1,84 @@ +//lcd.rs + +use embassy_rp::i2c::I2c; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::i2c::I2c as AsyncI2c; + +const BACKLIGHT: u8 = 0b0000_1000; +const ENABLE: u8 = 0b0000_0100; +const RS_DATA: u8 = 0b0000_0001; + +pub struct Lcd1602 { + i2c: I2C, + addr: u8, +} + +impl Lcd1602 +where + I2C: AsyncI2c, + E: core::fmt::Debug, +{ + pub async fn new(i2c: I2C, addr: u8) -> Self { + let mut lcd = Self { i2c, addr }; + + Timer::after(Duration::from_millis(50)).await; + + lcd.write_nibble(0x03, false).await; + Timer::after(Duration::from_millis(5)).await; + lcd.write_nibble(0x03, false).await; + Timer::after(Duration::from_millis(1)).await; + lcd.write_nibble(0x03, false).await; + Timer::after(Duration::from_micros(150)).await; + lcd.write_nibble(0x02, false).await; + lcd.send_cmd(0x28).await; + lcd.send_cmd(0x0C).await; + lcd.send_cmd(0x06).await; + lcd.clear().await; + lcd + } + + pub async fn clear(&mut self) { + self.send_cmd(0x01).await; + Timer::after(Duration::from_millis(2)).await; + } + + pub async fn set_cursor(&mut self, col: u8, row: u8) { + let row_offset = [0x00u8, 0x40]; + let addr = 0x80 | (col + row_offset[row as usize % 2]); + self.send_cmd(addr).await; + } + + pub async fn print(&mut self, s: &str) { + for byte in s.bytes() { + self.send_data(byte).await; + } + } + + + + async fn send_cmd(&mut self, cmd: u8) { + self.send_byte(cmd, false).await; + } + + async fn send_data(&mut self, data: u8) { + self.send_byte(data, true).await; + } + + async fn send_byte(&mut self, byte: u8, rs: bool) { + let hi = (byte >> 4) & 0x0F; + let lo = byte & 0x0F; + self.write_nibble(hi, rs).await; + self.write_nibble(lo, rs).await; + } + + async fn write_nibble(&mut self, nibble: u8, rs: bool) { + let rs_bit = if rs { RS_DATA } else { 0 }; + let data = (nibble << 4) | BACKLIGHT | rs_bit; + + + let _ = self.i2c.write(self.addr, &[data | ENABLE]).await; + Timer::after(Duration::from_micros(1)).await; + let _ = self.i2c.write(self.addr, &[data & !ENABLE]).await; + Timer::after(Duration::from_micros(50)).await; + } +} diff --git a/hardware_code/src/led.rs b/hardware_code/src/led.rs new file mode 100644 index 00000000000..0bda4585e7e --- /dev/null +++ b/hardware_code/src/led.rs @@ -0,0 +1,53 @@ +//led.rs +use embassy_rp::gpio::{Level, Output}; +use crate::distance::Zone; + +pub struct RgbLed<'d> { + r: Output<'d>, + g: Output<'d>, + b: Output<'d>, +} + +impl<'d> RgbLed<'d> { + pub fn new(r: Output<'d>, g: Output<'d>, b: Output<'d>) -> Self { + let mut led = Self { r, g, b }; + led.off(); + led + } + pub fn off(&mut self) { + self.r.set_high(); + self.g.set_high(); + self.b.set_high(); + } + + pub fn green(&mut self) { + self.r.set_high(); + self.g.set_low(); + self.b.set_high(); + } + + pub fn yellow(&mut self) { + self.r.set_low(); + self.g.set_low(); + self.b.set_high(); + } + + pub fn red(&mut self) { + self.r.set_low(); + self.g.set_high(); + self.b.set_high(); + } + + pub fn set_zone(&mut self, zone: Zone) { + match zone { + Zone::Clear => self.green(), + Zone::Warning => self.yellow(), + Zone::Danger => self.red(), + } + } +} + +pub fn set_both_leds(led1: &mut RgbLed<'_>, led2: &mut RgbLed<'_>, zone: Zone) { + led1.set_zone(zone); + led2.set_zone(zone); +} diff --git a/hardware_code/src/main.rs b/hardware_code/src/main.rs new file mode 100644 index 00000000000..5453567dd5d --- /dev/null +++ b/hardware_code/src/main.rs @@ -0,0 +1,120 @@ + +#![no_std] +#![no_main] + +use defmt::*; +use defmt_rtt as _; +use panic_probe as _; + +use embassy_executor::Spawner; +use embassy_rp::{ + bind_interrupts, + gpio::{Input, Level, Output, Pull}, + i2c::{Config as I2cConfig, I2c, InterruptHandler}, + peripherals::{I2C0, I2C1}, + pwm::{Config as PwmConfig, Pwm}, +}; +use embassy_time::{Duration, Timer}; + +mod buzzer; +mod distance; +mod lcd; +mod led; +mod pins; + +use distance::Zone; +use led::{set_both_leds, RgbLed}; +use lcd::Lcd1602; + + + +bind_interrupts!(struct Irqs { + I2C0_IRQ => InterruptHandler; + I2C1_IRQ => InterruptHandler; +}); + + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Parking Sensor starting…"); + + let p = embassy_rp::init(Default::default()); + + let i2c0 = I2c::new_async( + p.I2C0, + p.PIN_5, + p.PIN_4, + Irqs, + { + let mut cfg = I2cConfig::default(); + cfg.frequency = 100_000; + cfg + }, + ); + + let _i2c1 = I2c::new_async( + p.I2C1, + p.PIN_9, + p.PIN_8, + Irqs, + { + let mut cfg = I2cConfig::default(); + cfg.frequency = 100_000; + cfg + }, + ); + let mut lcd = Lcd1602::new(i2c0, pins::LCD_I2C_ADDR).await; + lcd.set_cursor(0, 0).await; + lcd.print("Parking Sensor ").await; + lcd.set_cursor(0, 1).await; + lcd.print("Initialising... ").await; + Timer::after(Duration::from_millis(1000)).await; + + let led1_r = Output::new(p.PIN_10, Level::High); + let led1_g = Output::new(p.PIN_11, Level::High); + let led1_b = Output::new(p.PIN_12, Level::High); + let mut led1 = RgbLed::new(led1_r, led1_g, led1_b); + + let led2_r = Output::new(p.PIN_13, Level::High); + let led2_g = Output::new(p.PIN_14, Level::High); + let led2_b = Output::new(p.PIN_15, Level::High); + let mut led2 = RgbLed::new(led2_r, led2_g, led2_b); + + let mut bz1 = Pwm::new_output_a(p.PWM_SLICE0, p.PIN_16, buzzer::make_silent_config()); + let mut bz2 = Pwm::new_output_a(p.PWM_SLICE1, p.PIN_17, buzzer::make_silent_config()); + + let sensor1 = Input::new(p.PIN_20, Pull::Up); + let sensor2 = Input::new(p.PIN_21, Pull::Up); + + info!("Hardware initialised."); + + let mut beep_on = false; + let mut prev_zone = Zone::Clear; + + loop { + let s1 = sensor1.is_low(); + let s2 = sensor2.is_low(); + let zone = Zone::from_sensors(s1, s2); + + if zone != prev_zone { + info!("Zone changed: {}", zone); + + set_both_leds(&mut led1, &mut led2, zone); + + lcd.set_cursor(0, 0).await; + lcd.print("Distance Status ").await; + lcd.set_cursor(0, 1).await; + lcd.print(zone.label()).await; + + prev_zone = zone; + beep_on = false; + } + + beep_on = !beep_on; + + buzzer::set_buzzer_zone(&mut bz1, zone, beep_on); + buzzer::set_buzzer_zone(&mut bz2, zone, beep_on); + + buzzer::half_period(zone).await; + } +} diff --git a/hardware_code/src/pins.rs b/hardware_code/src/pins.rs new file mode 100644 index 00000000000..23e49e83714 --- /dev/null +++ b/hardware_code/src/pins.rs @@ -0,0 +1,24 @@ +//pin.rs +pub const I2C0_SDA: u8 = 4; +pub const I2C0_SCL: u8 = 5; + +pub const I2C1_SDA: u8 = 8; +pub const I2C1_SCL: u8 = 9; + +pub const LED1_R: u8 = 10; +pub const LED1_G: u8 = 11; +pub const LED1_B: u8 = 12; + +pub const LED2_R: u8 = 13; +pub const LED2_G: u8 = 14; +pub const LED2_B: u8 = 15; + +pub const BUZZER1: u8 = 16; +pub const BUZZER2: u8 = 17; + +pub const SENSOR1_OUT: u8 = 20; +pub const SENSOR2_OUT: u8 = 21; + +pub const LCD_I2C_ADDR: u8 = 0x27; +pub const SENSOR1_I2C_ADDR: u8 = 0x29; +pub const SENSOR2_I2C_ADDR: u8 = 0x29; diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/README.md b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/README.md new file mode 100644 index 00000000000..9b97eec51d1 --- /dev/null +++ b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/README.md @@ -0,0 +1,98 @@ +# Parking Sensor – Raspberry Pi Pico WH (Rust / Embassy) + +Smart parking sensor system using digital IR obstacle modules, RGB LEDs, passive buzzers and an LCD 1602 display. + +## Wiring (from schematic) + +| Signal | Pico GPIO | Physical Pin | +|---------------|-----------|--------------| +| I²C0 SDA | GPIO 4 | 6 | +| I²C0 SCL | GPIO 5 | 7 | +| I²C1 SDA | GPIO 8 | 11 | +| I²C1 SCL | GPIO 9 | 12 | +| LED1 Red | GPIO 10 | 14 | +| LED1 Green | GPIO 11 | 15 | +| LED1 Blue | GPIO 12 | 16 | +| LED2 Red | GPIO 13 | 17 | +| LED2 Green | GPIO 14 | 19 | +| LED2 Blue | GPIO 15 | 20 | +| Buzzer 1 | GPIO 16 | 21 | +| Buzzer 2 | GPIO 17 | 22 | +| IR Sensor 1 | GPIO 20 | 26 | +| IR Sensor 2 | GPIO 21 | 27 | + +> **RGB LEDs** are common-anode: connect the common pin to 3.3 V and drive each colour channel LOW to light it. +> **IR sensors** output LOW when an obstacle is detected. + +## Prerequisites + +```bash +# Install Rust target for Cortex-M0+ (RP2040) +rustup target add thumbv6m-none-eabi + +# Install the probe-rs flash/debug tool +cargo install probe-rs-tools --locked + +# (Linux) Add yourself to the plugdev group so probe-rs can access the probe +sudo usermod -aG plugdev $USER +``` + +## Build + +```bash +cargo build --release +``` + +## Flash + +Connect a **Raspberry Pi Debug Probe** (or a second Pico flashed as picoprobe) to the SWD pins, then: + +```bash +cargo run --release +``` + +Or flash the UF2 directly: + +```bash +# Convert ELF → UF2 +cargo install elf2uf2-rs --locked +elf2uf2-rs target/thumbv6m-none-eabi/release/parking_sensor parking_sensor.uf2 + +# Hold BOOTSEL while plugging in the Pico, then copy the UF2 +cp parking_sensor.uf2 /media/$USER/RPI-RP2/ +``` + +## Debug output + +Real-time `defmt` logs are streamed over RTT. With probe-rs connected: + +```bash +probe-rs run --chip RP2040 target/thumbv6m-none-eabi/release/parking_sensor +``` + +## Logic overview + +| Sensors triggered | Zone | LEDs | Buzzer | LCD | +|-------------------|---------|--------|--------------|----------------------| +| neither | CLEAR | Green | Silent | `CLEAR >100cm` | +| one | WARNING | Yellow | Slow beep | `WARNING ~50cm` | +| both | DANGER | Red | Rapid beep | `DANGER! <20cm` | + +## Project structure + +``` +parking_sensor/ +├── .cargo/ +│ └── config.toml # build target + linker flags +├── src/ +│ ├── main.rs # Embassy entry point, hardware init, main loop +│ ├── pins.rs # GPIO / I²C address constants +│ ├── distance.rs # Zone enum + sensor logic +│ ├── led.rs # RgbLed helper +│ ├── buzzer.rs # PWM buzzer helpers +│ └── lcd.rs # LCD 1602 I²C driver (4-bit mode via PCF8574) +├── build.rs # Linker script notification +├── memory.x # RP2040 flash / RAM layout +├── Cargo.toml +└── README.md +``` diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/arch.webp b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/arch.webp similarity index 100% rename from website/versioned_docs/version-fils_en/project/2026/denisa.filip/arch.webp rename to website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/arch.webp diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/h1.webp b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h1.webp similarity index 100% rename from website/versioned_docs/version-fils_en/project/2026/denisa.filip/h1.webp rename to website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h1.webp diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/h2.webp b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h2.webp similarity index 100% rename from website/versioned_docs/version-fils_en/project/2026/denisa.filip/h2.webp rename to website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h2.webp diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/h3.webp b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h3.webp similarity index 100% rename from website/versioned_docs/version-fils_en/project/2026/denisa.filip/h3.webp rename to website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h3.webp diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/h4.webp b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h4.webp similarity index 100% rename from website/versioned_docs/version-fils_en/project/2026/denisa.filip/h4.webp rename to website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/h4.webp diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/sch.svg b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/sch.svg similarity index 100% rename from website/versioned_docs/version-fils_en/project/2026/denisa.filip/sch.svg rename to website/versioned_docs/version-fils_en/project/2026/denisa.filip/images/sch.svg diff --git a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/index.md b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/index.md index 4db40148733..d737140839e 100644 --- a/website/versioned_docs/version-fils_en/project/2026/denisa.filip/index.md +++ b/website/versioned_docs/version-fils_en/project/2026/denisa.filip/index.md @@ -33,7 +33,7 @@ It’s built around the Raspberry Pi Pico and programmed in Rust, which not only ## Architecture -![architecture](arch.webp) +![architecture](images/arch.webp) @@ -41,31 +41,26 @@ It’s built around the Raspberry Pi Pico and programmed in Rust, which not only -### Week 5 - 30 March -Decided the project theme and searched for informations. +### Week 5 - 30 March: decided the project theme and searched for informations. -### Week 6 - 6 April -Bought all the components and soldered the raspberry pi and started creating the prototype. +### Week 6 - 6 April: bought all the components and soldered the raspberry pi and started creating the prototype. -### Week 7 - 13 April -Started on the documentation. +### Week 7 - 13 April: started on the documentation. -### Week 8 - 20 April -Started on the hardware and software milestones of the project. +### Week 8 - 20 April: started on the hardware and software milestones of the project. -### Week 9 - 27 April -Edited the documentation to submit the first milestone. -### Week 10 - 4 May -Submitted the documentation and made the necessary changes. +### Week 9 - 27 April: edited the documentation to submit the first milestone. -### Week 11 - 11 May +### Week 10 - 4 May: I submitted the documentation and made the necessary changes. -### Week 12 - 18 May +### Week 11 - 11 May: I finished the software milestone. + +### Week 12 - 18 May: I submitted the software milestone. ## Hardware @@ -78,10 +73,10 @@ RGB LEDs (2x): Provide visual feedback—colors change (e.g., green to red) base Buzzers (2x): Emit sound alerts that increase in frequency as the object gets closer, enhancing the warning system. -![Hardware](h1.webp) -![Hardware](h2.webp) -![Hardware](h3.webp) -![Hardware](h4.webp) +![Hardware 1](./images/h1.webp) +![Hardware 2](./images/h2.webp) +![Hardware 3](./images/h3.webp) +![Hardware 4](./images/h4.webp) @@ -90,7 +85,7 @@ Buzzers (2x): Emit sound alerts that increase in frequency as the object gets cl Schematic of the project on KiCad Application. -![Schematic of the project with using KiCad](sch.svg) +![Schematic of the project with using KiCad](images/sch.svg)