first working async
This commit is contained in:
parent
c7e0640401
commit
05893b1f0e
7 changed files with 121 additions and 75 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -19,19 +19,20 @@ opt-level = "z"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
experimental = ["esp-idf-svc/experimental"]
|
||||
|
||||
esp_idf_spi_master_isr_in_iram = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
esp-idf-svc = { version = "0.51", features = ["embassy-time-driver", "embassy-sync"] }
|
||||
anyhow = "1.0.97"
|
||||
bytemuck = { version="1.22.0", features = ["derive", "min_const_generics"] }
|
||||
embassy-executor = {version ="0.7.0" , features = ["arch-std", "executor-thread", "log"] }
|
||||
embassy-sync = "0.6.2"
|
||||
static_cell = "2.1.0"
|
||||
embassy-time = "0.4.0"
|
||||
assign-resources = "0.4.1"
|
||||
embassy-util = "0.0.0"
|
||||
esp-idf-svc = { version = "0.51", features = ["embassy-time-driver", "embassy-sync"] }
|
||||
log = "0.4"
|
||||
static_cell = "2.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
embuild = "0.33"
|
||||
|
|
|
@ -11,3 +11,5 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=40000
|
|||
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_DSP_MAX_FFT_SIZE_1024=y
|
||||
|
||||
CONFIG_SPI_MASTER_ISR_IN_IRAM=n
|
|
@ -130,11 +130,17 @@ pub async fn process_audio(
|
|||
|
||||
loop {
|
||||
let in_buf = input.receive().await;
|
||||
|
||||
if output.is_full() {
|
||||
output.clear();
|
||||
}
|
||||
|
||||
let (out_buf, stats) = output.send().await;
|
||||
|
||||
processor.process(in_buf, out_buf);
|
||||
*stats = processor.stats;
|
||||
|
||||
output.send_done();
|
||||
input.receive_done();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
pub const LED_COUNT: usize = 72;
|
||||
|
||||
pub const AUDIO_SAMPLES_PER_BUF: usize = 1024;
|
||||
|
||||
pub const AUDIO_BUFFERS: usize = 2;
|
||||
|
||||
pub const AUDIO_BANDS: usize = 3;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use embassy_sync::zerocopy_channel::Sender;
|
||||
use esp_idf_svc::hal::units::{FromValueType, MilliSeconds};
|
||||
|
||||
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, zerocopy_channel::Receiver};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
|
@ -20,26 +19,26 @@ impl Default for LedEffectBassSparks {
|
|||
}
|
||||
}
|
||||
impl LedEffect for LedEffectBassSparks {
|
||||
fn render(&mut self, _: &DspBuffer, stats: &AudioStats, leds: &mut LedColors) -> MilliSeconds {
|
||||
fn render(&mut self, _: &DspBuffer, stats: &AudioStats, leds: &mut LedColors) -> Duration {
|
||||
if stats.floating_max > 10100000 && (stats.current_powers[0] > 1.25 * stats.avg_powers[0]) {
|
||||
self.bass_color = Rgbv::new(127, 0, 255, 4)
|
||||
}
|
||||
|
||||
leds.fill(self.bass_color);
|
||||
|
||||
self.bass_color.decrease(3, 5, 5, 0);
|
||||
self.bass_color.decrease(10, 15, 20, 0);
|
||||
|
||||
if stats.floating_max > 10100000
|
||||
&& (stats.current_powers[1] > 1.35 * stats.avg_powers[1])
|
||||
&& (stats.current_powers[2] > 1.35 * stats.avg_powers[2])
|
||||
{
|
||||
for _ in 0..10 {
|
||||
for _ in 0..20 {
|
||||
let led_index = random_at_most(LED_COUNT as u32 - 1) as usize;
|
||||
leds[led_index] = Rgbv::white(31);
|
||||
}
|
||||
}
|
||||
|
||||
10.ms()
|
||||
Duration::from_hz(20)
|
||||
}
|
||||
|
||||
async fn process_led_effect(
|
||||
|
@ -47,12 +46,21 @@ impl LedEffect for LedEffectBassSparks {
|
|||
mut input: Receiver<'static, NoopRawMutex, (DspBuffer, AudioStats)>,
|
||||
mut output: Sender<'static, NoopRawMutex, LedData>,
|
||||
) {
|
||||
let mut ticker = Ticker::every(Duration::from_hz(20));
|
||||
let mut set_interval = Duration::from_hz(100);
|
||||
let mut ticker = Ticker::every(set_interval);
|
||||
loop {
|
||||
let (fft, stats) = input.receive().await;
|
||||
let leds = output.send().await;
|
||||
self.render(fft, stats, &mut leds.leds);
|
||||
|
||||
let interval = self.render(fft, stats, &mut leds.leds);
|
||||
|
||||
input.receive_done();
|
||||
output.send_done();
|
||||
|
||||
if set_interval != interval {
|
||||
set_interval = interval;
|
||||
ticker = Ticker::every(set_interval);
|
||||
}
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,28 +4,27 @@ use embassy_sync::{
|
|||
blocking_mutex::raw::NoopRawMutex,
|
||||
zerocopy_channel::{Receiver, Sender},
|
||||
};
|
||||
use esp_idf_svc::hal::units::MilliSeconds;
|
||||
use embassy_time::Duration;
|
||||
|
||||
use crate::{audio::DspBuffer, config::LED_COUNT, AudioStats};
|
||||
|
||||
pub trait LedEffect {
|
||||
fn render(&mut self, fft: &DspBuffer, stats: &AudioStats, leds: &mut LedColors)
|
||||
-> MilliSeconds;
|
||||
fn render(&mut self, fft: &DspBuffer, stats: &AudioStats, leds: &mut LedColors) -> Duration;
|
||||
|
||||
async fn process_led_effect(
|
||||
fn process_led_effect(
|
||||
&mut self,
|
||||
input: Receiver<'static, NoopRawMutex, (DspBuffer, AudioStats)>,
|
||||
output: Sender<'static, NoopRawMutex, LedData>,
|
||||
);
|
||||
) -> impl std::future::Future<Output = ()>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Pod, Zeroable)]
|
||||
#[repr(C, align(4))]
|
||||
pub struct Rgbv {
|
||||
_o: u8,
|
||||
b: u8,
|
||||
g: u8,
|
||||
r: u8,
|
||||
pub b: u8,
|
||||
pub g: u8,
|
||||
pub r: u8,
|
||||
}
|
||||
|
||||
impl Rgbv {
|
||||
|
@ -87,9 +86,8 @@ impl Rgbv {
|
|||
/// Converts hue, saturation, value to RGB
|
||||
/// // copied from rmt_neopixel example
|
||||
pub fn from_hsv(h: u32, s: u32, v: u32, o: u8) -> Result<Self> {
|
||||
assert!(o <= Self::MAX_O);
|
||||
if h > 360 || s > 100 || v > 100 {
|
||||
bail!("The given HSV values are not in valid range");
|
||||
if o > Self::MAX_O || h > 360 || s > 100 || v > 100 {
|
||||
bail!("The given HSVo values are not in valid range");
|
||||
}
|
||||
let s = s as f64 / 100.0;
|
||||
let v = v as f64 / 100.0;
|
||||
|
|
133
src/main.rs
133
src/main.rs
|
@ -3,9 +3,16 @@ pub mod config;
|
|||
pub mod effects;
|
||||
pub mod helpers;
|
||||
|
||||
use embassy_time::Duration;
|
||||
use esp_idf_svc::{
|
||||
hal::{gpio::AnyIOPin, i2s, peripherals::Peripherals, spi, units::FromValueType},
|
||||
sys::{esp_dsp, esp_nofail, TickType_t},
|
||||
hal::{
|
||||
gpio::{AnyIOPin, Gpio11, Gpio12},
|
||||
i2s::{self, I2sRx},
|
||||
peripherals::Peripherals,
|
||||
spi::{self, SPI2},
|
||||
units::FromValueType,
|
||||
},
|
||||
sys::{esp_dsp, esp_nofail},
|
||||
};
|
||||
|
||||
use bytemuck::{bytes_of, bytes_of_mut};
|
||||
|
@ -24,7 +31,45 @@ use effects::{
|
|||
};
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn output_leds() {}
|
||||
async fn effect_task(
|
||||
mut led_effect: LedEffectBassSparks,
|
||||
input: Receiver<'static, NoopRawMutex, (DspBuffer, AudioStats)>,
|
||||
output: Sender<'static, NoopRawMutex, LedData>,
|
||||
) {
|
||||
led_effect.process_led_effect(input, output).await;
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn output_leds(
|
||||
spi_per: SPI2,
|
||||
sdo: Gpio11,
|
||||
sck: Gpio12,
|
||||
mut receiver: Receiver<'static, NoopRawMutex, LedData>,
|
||||
) {
|
||||
let mut led_drv = spi::SpiDeviceDriver::new_single(
|
||||
spi_per,
|
||||
sck,
|
||||
sdo,
|
||||
AnyIOPin::none(),
|
||||
AnyIOPin::none(),
|
||||
&spi::config::DriverConfig::new(),
|
||||
&spi::config::Config::new()
|
||||
.baudrate(1.MHz().into())
|
||||
.data_mode(spi::config::MODE_3),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
let led_data = receiver.receive().await;
|
||||
|
||||
let output_buffer = bytes_of(led_data);
|
||||
led_drv
|
||||
.write_async(output_buffer)
|
||||
.await
|
||||
.expect("spi write failed");
|
||||
receiver.receive_done();
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
|
@ -64,7 +109,7 @@ async fn main(spawner: Spawner) {
|
|||
let leds = LED_DATA.init([LedData::default(); 1]);
|
||||
static LED_CHANNEL: StaticCell<Channel<'_, NoopRawMutex, LedData>> = StaticCell::new();
|
||||
let led_channel = LED_CHANNEL.init(Channel::new(leds));
|
||||
let (led_sender, mut led_receiver) = led_channel.split();
|
||||
let (led_sender, led_receiver) = led_channel.split();
|
||||
|
||||
// i2s config
|
||||
let mic_i2s_per = peripherals.i2s0;
|
||||
|
@ -107,24 +152,6 @@ async fn main(spawner: Spawner) {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
// spi config
|
||||
let led_spi_per = peripherals.spi2;
|
||||
let led_spi_sdo = peripherals.pins.gpio11;
|
||||
let led_spi_sck = peripherals.pins.gpio12;
|
||||
|
||||
let mut led_drv = spi::SpiDeviceDriver::new_single(
|
||||
led_spi_per,
|
||||
led_spi_sck,
|
||||
led_spi_sdo,
|
||||
AnyIOPin::none(),
|
||||
AnyIOPin::none(),
|
||||
&spi::config::DriverConfig::new(),
|
||||
&spi::config::Config::new()
|
||||
.baudrate(1.MHz().into())
|
||||
.data_mode(spi::config::MODE_3),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
esp_nofail!(esp_dsp::dsps_fft2r_init_fc32(
|
||||
std::ptr::null_mut(),
|
||||
|
@ -140,15 +167,6 @@ async fn main(spawner: Spawner) {
|
|||
.spawn(process_audio(audio_receiver, fft_sender))
|
||||
.expect("spawn failed");
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn effect_task(
|
||||
mut led_effect: LedEffectBassSparks,
|
||||
input: Receiver<'static, NoopRawMutex, (DspBuffer, AudioStats)>,
|
||||
output: Sender<'static, NoopRawMutex, LedData>,
|
||||
) {
|
||||
led_effect.process_led_effect(input, output).await;
|
||||
}
|
||||
|
||||
spawner
|
||||
.spawn(effect_task(
|
||||
LedEffectBassSparks::default(),
|
||||
|
@ -157,35 +175,46 @@ async fn main(spawner: Spawner) {
|
|||
))
|
||||
.expect("spawn failed");
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn output_leds() {}
|
||||
spawner
|
||||
.spawn(output_leds(
|
||||
peripherals.spi2,
|
||||
peripherals.pins.gpio11,
|
||||
peripherals.pins.gpio12,
|
||||
led_receiver,
|
||||
))
|
||||
.expect("spawn failed");
|
||||
|
||||
mic_drv.rx_enable().expect("rx not enabled");
|
||||
|
||||
async fn ignore_mic_startup(mic: &mut i2s::I2sDriver<'_, I2sRx>) {
|
||||
let mut tmp_buf: [u8; 128] = [0; 128];
|
||||
loop {
|
||||
mic.read_async(tmp_buf.as_mut_slice()).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
embassy_time::with_timeout(Duration::from_millis(100), ignore_mic_startup(&mut mic_drv))
|
||||
.await
|
||||
.expect_err("ignore died early");
|
||||
|
||||
loop {
|
||||
let audio_in_buf: &mut AudioBuffer = audio_sender.send().await;
|
||||
|
||||
let buffer = bytes_of_mut(audio_in_buf);
|
||||
let num_bytes_read = mic_drv.read(buffer, TickType_t::MAX).unwrap();
|
||||
let mut total_bytes_read: usize = 0;
|
||||
let mut remaining_bytes = AUDIO_SAMPLES_PER_BUF * 4;
|
||||
|
||||
if num_bytes_read != AUDIO_SAMPLES_PER_BUF * 4 {
|
||||
log::error!("buffer underflow");
|
||||
while total_bytes_read < AUDIO_SAMPLES_PER_BUF * 4 {
|
||||
let chunk = &mut buffer[total_bytes_read..total_bytes_read + remaining_bytes];
|
||||
let num_bytes_read = mic_drv.read_async(chunk).await.unwrap();
|
||||
total_bytes_read += num_bytes_read;
|
||||
remaining_bytes -= num_bytes_read;
|
||||
}
|
||||
|
||||
if total_bytes_read != AUDIO_SAMPLES_PER_BUF * 4 {
|
||||
log::warn!("buffer underflow: {}", total_bytes_read);
|
||||
}
|
||||
|
||||
audio_sender.send_done();
|
||||
|
||||
let led_data = led_receiver.receive().await;
|
||||
|
||||
let output_buffer = bytes_of(&led_data.leds);
|
||||
led_drv.write(output_buffer).expect("spi write failed");
|
||||
|
||||
// let current_fft_buf = processor.process(&audio[next_audio_buf]);
|
||||
|
||||
// let delay_ms: MilliSeconds = effect.render(&processor, current_fft_buf, &mut leds.leds);
|
||||
|
||||
// let output_buffer = bytes_of(&leds);
|
||||
// led_drv.write(output_buffer)?;
|
||||
|
||||
// next_audio_buf = (next_audio_buf + 1) % AUDIO_BUFFERS;
|
||||
|
||||
// FreeRtos::delay_ms(delay_ms.into());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue