device-envoy-esp 0.1.0

Build ESP32 applications with composable device abstractions
Documentation
{# @board-example audio #}
// @generated by cargo xtask generate-board-examples
//! 
//! Wiring:
//! - Audio data pin (`DIN`) -> GPIO{{ data_pin }}
//! - Audio bit clock pin (`BCLK`) -> GPIO{{ bit_clock_pin }}
//! - Audio word select pin (`LRC` / `LRCLK`) -> GPIO{{ word_select_pin }}
//! - Button -> GPIO{{ button_pin }} to GND
#![no_std]
#![no_main]

use core::convert::Infallible;
use core::time::Duration as StdDuration;

use embassy_futures::select::{select, Either};
use embassy_time::{Duration, Timer};

use device_envoy_core::{
    audio_player::{AtEnd, AudioPlayer, Gain, Playable, SilenceClip, Volume, VOICE_22050_HZ},
    button::Button,
};
use device_envoy_esp::{
    audio_player::{audio_player, pcm_clip},
    button::{ButtonEsp, PressedTo},
    tone,
};

use embassy_executor::Spawner;
use esp_backtrace as _;
use log::info;

use device_envoy_esp::{init_and_start, Result};

esp_bootloader_esp_idf::esp_app_desc!();

audio_player! {
    AudioPlayerBoard {
        data_pin: GPIO{{ data_pin }},
        bit_clock_pin: GPIO{{ bit_clock_pin }},
        word_select_pin: GPIO{{ word_select_pin }},
        sample_rate_hz: VOICE_22050_HZ,
        dma: {{ dma_identifier }},
        max_clips: 8,
        max_volume: Volume::spinal_tap(11),
        initial_volume: Volume::spinal_tap(5),
    }
}

pcm_clip! {
    Nasa {
        file: concat!(env!("CARGO_MANIFEST_DIR"), "/examples/data/audio/nasa_22k.s16"),
        source_sample_rate_hz: VOICE_22050_HZ,
    }
}

async fn play_nasa_with_runtime_volume(
    audio_player: &impl AudioPlayer<VOICE_22050_HZ>,
    button: &mut impl Button,
) -> ! {
    const fn ms(milliseconds: u64) -> StdDuration {
        StdDuration::from_millis(milliseconds)
    }

    type PlayableRef = &'static dyn Playable<VOICE_22050_HZ>;
    const NASA: PlayableRef = &Nasa::adpcm_clip();
    const GAP: PlayableRef = &SilenceClip::new(ms(80));
    const CHIME: PlayableRef = &tone!(880, VOICE_22050_HZ, ms(100)).with_gain(Gain::percent(20));
    const VOLUME_STEPS_PERCENT: [u8; 7] = [50, 25, 12, 6, 3, 1, 0];
    let initial_volume = audio_player.volume();

    loop {
        audio_player.play([CHIME, NASA, GAP], AtEnd::Loop);

        for volume_percent in VOLUME_STEPS_PERCENT {
            match select(
                button.wait_for_press(),
                Timer::after(Duration::from_secs(1)),
            )
            .await
            {
                Either::First(()) => break,
                Either::Second(()) => audio_player.set_volume(Volume::percent(volume_percent)),
            }
        }

        audio_player.stop();
        audio_player.set_volume(initial_volume);
        info!("Press the button to play again.");
        button.wait_for_press().await;
    }
}

#[esp_rtos::main]
async fn main(spawner: Spawner) -> ! {
    let err = inner_main(spawner).await.unwrap_err();
    panic!("{err:?}");
}

async fn inner_main(spawner: Spawner) -> Result<Infallible> {
    init_and_start!(p);
    esp_println::logger::init_logger(log::LevelFilter::Info);

    let mut button = ButtonEsp::new(p.GPIO{{ button_pin }}, PressedTo::Ground);

    let audio_player_board =
        AudioPlayerBoard::new(
            p.GPIO{{ data_pin }},
            p.GPIO{{ bit_clock_pin }},
            p.GPIO{{ word_select_pin }},
            p.I2S0,
            p.{{ dma_identifier }},
            spawner,
        )?;

    play_nasa_with_runtime_volume(audio_player_board, &mut button).await
}