|
| 1 | +//! Basic Embassy example for the Raspberry Pi Pico 2 |
| 2 | +//! Showcase sleep and wake up functionality of the DFPlayer Mini |
| 3 | +//! |
| 4 | +//! Assumes the following connections: |
| 5 | +//! |
| 6 | +//! - UART0 TX/RX on GPIO 17/16 |
| 7 | +//! - Busy Pin on GPIO 18 |
| 8 | +//! |
| 9 | +//! Assumes the following for the DFPlayer Mini: |
| 10 | +//! |
| 11 | +//! - Powered and ground connected |
| 12 | +//! - TX/RX connected |
| 13 | +//! - Busy Pin connected |
| 14 | +//! - Speaker connected |
| 15 | +//! - Using SD card for audio files. Place mp3 files on the SD card into a folder structure as documented in the DFPlayer Mini manual. For example: |
| 16 | +//! - no folders, use root directory |
| 17 | +//! - 0001-whatevernameX.mp3 |
| 18 | +//! - 0002-whatevernameY.mp3 |
| 19 | +//! - 0003-whatevernameZ.mp3 |
| 20 | +
|
| 21 | +#![no_std] |
| 22 | +#![no_main] |
| 23 | + |
| 24 | +use defmt::{error, info, Debug2Format}; |
| 25 | +use dfplayer_async::{DfPlayer, TimeSource}; |
| 26 | +use embassy_executor::Spawner; |
| 27 | +use embassy_rp::{ |
| 28 | + bind_interrupts, |
| 29 | + block::ImageDef, |
| 30 | + config::Config, |
| 31 | + gpio::{Input, Pull}, |
| 32 | + peripherals::UART0, |
| 33 | + uart::{ |
| 34 | + BufferedInterruptHandler, BufferedUart, Config as UartConfig, DataBits, |
| 35 | + Parity, StopBits, |
| 36 | + }, |
| 37 | +}; |
| 38 | +use embassy_time::{Delay, Duration, Instant, Timer}; |
| 39 | +use static_cell::StaticCell; |
| 40 | +use {defmt_rtt as _, panic_probe as _}; |
| 41 | + |
| 42 | +/// Firmware image type for bootloader |
| 43 | +#[unsafe(link_section = ".start_block")] |
| 44 | +#[used] |
| 45 | +pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); |
| 46 | + |
| 47 | +bind_interrupts!(pub struct Irqs { |
| 48 | + UART0_IRQ => BufferedInterruptHandler<UART0>; |
| 49 | +}); |
| 50 | + |
| 51 | +/// Firmware entry point |
| 52 | +#[embassy_executor::main] |
| 53 | +async fn main(_spawner: Spawner) { |
| 54 | + let p = embassy_rp::init(Config::default()); |
| 55 | + |
| 56 | + // Initialize the DFPlayer Mini |
| 57 | + // The modules usually have a busy pin that can be used to determine if the module is currently playing audio. Low if busy, high if not busy. |
| 58 | + let mut busy = Input::new(p.PIN_18, Pull::None); |
| 59 | + |
| 60 | + // We need a UART port to communicate with the DFPlayer Mini |
| 61 | + let mut uart_config = UartConfig::default(); |
| 62 | + uart_config.baudrate = 9600; |
| 63 | + uart_config.data_bits = DataBits::DataBits8; |
| 64 | + uart_config.stop_bits = StopBits::STOP1; |
| 65 | + uart_config.parity = Parity::ParityNone; |
| 66 | + |
| 67 | + // Create a buffered UART instance |
| 68 | + static TX_BUF: StaticCell<[u8; 64]> = StaticCell::new(); |
| 69 | + let tx_buf = &mut TX_BUF.init([0; 64])[..]; |
| 70 | + static RX_BUF: StaticCell<[u8; 64]> = StaticCell::new(); |
| 71 | + let rx_buf = &mut RX_BUF.init([0; 64])[..]; |
| 72 | + let mut uart = BufferedUart::new( |
| 73 | + p.UART0, |
| 74 | + Irqs, |
| 75 | + p.PIN_16, |
| 76 | + p.PIN_17, |
| 77 | + tx_buf, |
| 78 | + rx_buf, |
| 79 | + uart_config, |
| 80 | + ); |
| 81 | + |
| 82 | + // Configure DFPlayer parameters |
| 83 | + let feedback_enable = true; |
| 84 | + let timeout_ms = 1000; |
| 85 | + let delay = Delay; |
| 86 | + let reset_duration_override = None; |
| 87 | + |
| 88 | + // Implement the TimeSource trait for the DFPlayer Mini |
| 89 | + struct MyTimeSource; |
| 90 | + impl TimeSource for MyTimeSource { |
| 91 | + type Instant = Instant; |
| 92 | + |
| 93 | + fn now(&self) -> Self::Instant { |
| 94 | + Instant::now() |
| 95 | + } |
| 96 | + |
| 97 | + fn is_elapsed(&self, since: Self::Instant, timeout_ms: u64) -> bool { |
| 98 | + Instant::now().duration_since(since) |
| 99 | + >= Duration::from_millis(timeout_ms) |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + // Create the DFPlayer Mini instance |
| 104 | + let mut dfplayer = match DfPlayer::try_new( |
| 105 | + &mut uart, |
| 106 | + feedback_enable, |
| 107 | + timeout_ms, |
| 108 | + MyTimeSource, |
| 109 | + delay, |
| 110 | + reset_duration_override, |
| 111 | + ) |
| 112 | + .await |
| 113 | + { |
| 114 | + Ok(dfplayer) => dfplayer, |
| 115 | + Err(e) => { |
| 116 | + error!("Error initializing DFPlayer Mini: {}", Debug2Format(&e)); |
| 117 | + return; |
| 118 | + } |
| 119 | + }; |
| 120 | + |
| 121 | + // Set the volume to a low level |
| 122 | + let volume = 1; |
| 123 | + match dfplayer.set_volume(volume).await { |
| 124 | + Ok(_) => info!("Volume {} set successfully", volume), |
| 125 | + Err(e) => error!("Failed to set volume: {}", Debug2Format(&e)), |
| 126 | + } |
| 127 | + |
| 128 | + // Play the first track for a few seconds, then stop |
| 129 | + info!("Playing track 1"); |
| 130 | + match dfplayer.play(1).await { |
| 131 | + Ok(_) => info!("Play track 1 command sent successfully"), |
| 132 | + Err(e) => error!("Failed to play track: {}", Debug2Format(&e)), |
| 133 | + } |
| 134 | + Timer::after(Duration::from_secs(5)).await; |
| 135 | + match dfplayer.stop().await { |
| 136 | + Ok(_) => info!("Stop command sent successfully"), |
| 137 | + Err(e) => error!("Failed to stop track: {}", Debug2Format(&e)), |
| 138 | + } |
| 139 | + |
| 140 | + // Wait a moment before putting the device to sleep |
| 141 | + Timer::after(Duration::from_secs(1)).await; |
| 142 | + |
| 143 | + // Put the device to sleep |
| 144 | + info!("Putting DFPlayer Mini to sleep"); |
| 145 | + match dfplayer.sleep().await { |
| 146 | + Ok(_) => info!("Sleep command sent successfully"), |
| 147 | + Err(e) => error!("Failed to put device to sleep: {}", Debug2Format(&e)), |
| 148 | + } |
| 149 | + |
| 150 | + // Wait while the device is in sleep mode |
| 151 | + info!("Device is now in sleep mode. Waiting for 5 seconds..."); |
| 152 | + Timer::after(Duration::from_secs(5)).await; |
| 153 | + |
| 154 | + // Wake up the device |
| 155 | + // info!("Waking up DFPlayer Mini"); |
| 156 | + // match dfplayer.wake_up(true, None).await { |
| 157 | + // Ok(_) => info!("Wake up command sent successfully"), |
| 158 | + // Err(e) => error!("Failed to wake up device: {}", Debug2Format(&e)), |
| 159 | + // } |
| 160 | + // Wake up the device |
| 161 | + info!("Waking up DFPlayer Mini"); |
| 162 | + match dfplayer.reset(None).await { |
| 163 | + Ok(_) => info!("Wake up command sent successfully"), |
| 164 | + Err(e) => error!("Failed to wake up device: {}", Debug2Format(&e)), |
| 165 | + } |
| 166 | + |
| 167 | + |
| 168 | + // Wait a moment for the device to fully wake up |
| 169 | + Timer::after(Duration::from_secs(1)).await; |
| 170 | + |
| 171 | + // Play another track to verify the device is working after waking up |
| 172 | + info!("Playing track 2 to verify device is awake"); |
| 173 | + match dfplayer.play(2).await { |
| 174 | + Ok(_) => info!("Play track 2 command sent successfully"), |
| 175 | + Err(e) => error!("Failed to play track: {}", Debug2Format(&e)), |
| 176 | + } |
| 177 | + Timer::after(Duration::from_secs(5)).await; |
| 178 | + match dfplayer.stop().await { |
| 179 | + Ok(_) => info!("Stop command sent successfully"), |
| 180 | + Err(e) => error!("Failed to stop track: {}", Debug2Format(&e)), |
| 181 | + } |
| 182 | + |
| 183 | + // Example complete |
| 184 | + info!("Sleep/wake up example complete"); |
| 185 | + |
| 186 | + // Loop forever |
| 187 | + loop { |
| 188 | + Timer::after(Duration::from_secs(1)).await; |
| 189 | + } |
| 190 | +} |
0 commit comments