Embasy-rp ws2812-pio does not work from a task

⚓ Rust    📅 2026-02-11    👤 surdeus    👁️ 1      

surdeus

I'm trying to use the neopixel PIO program from embassy_rp, based on this example. That works, but if I try to move my use of the RGB led to a task, it does not. It's not panicing, but the led does not change. I can verify the task is running by blinking a plain led in the same loop.

My board is the waveshare rp2040-zero, which unfortunately does not expose debug pins.

Can anyone help me figure out what's going on?

Full code below:

//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules.
//! See (https://www.sparkfun.com/categories/tags/ws2812)
//! original example: https://raw.githubusercontent.com/embassy-rs/embassy/refs/tags/embassy-rp-v0.9.0/examples/rp/src/bin/pio_ws2812.rs

#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_rp::bind_interrupts;
use embassy_rp::gpio::{Level, Output};
use embassy_rp::peripherals::PIO0;
use embassy_rp::pio::{InterruptHandler, Pio};
use embassy_rp::pio_programs::ws2812::{Grb, PioWs2812, PioWs2812Program};
use embassy_time::{Duration, Ticker};
use smart_leds::RGB8;

use panic_probe as _;

bind_interrupts!(struct Irqs {
    PIO0_IRQ_0 => InterruptHandler<PIO0>;
});

/// Input a value 0 to 255 to get a color value
/// The colours are a transition r - g - b - back to r.
fn wheel(mut wheel_pos: u8) -> RGB8 {
    wheel_pos = 255 - wheel_pos;
    if wheel_pos < 85 {
        return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
    }
    if wheel_pos < 170 {
        wheel_pos -= 85;
        return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
    }
    wheel_pos -= 170;
    (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
}


#[embassy_executor::main]
async fn main(#[allow(unused_variables)]spawner: Spawner) {
    //info!("Start");
    let p = embassy_rp::init(Default::default());

    // normal led on breadboard at pin 29
    let led = Output::new(p.PIN_29, Level::Low);

    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);

    // waveshare rp2040-zero neopixel on pin 16:
    let program = PioWs2812Program::new(&mut common);
    let ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program);

    // Works:
    // blink(led, ws2812).await;

    // Doesn't work, RBG does not change:
    spawner.spawn(blink_task(led, ws2812)).unwrap();
}

async fn blink(mut led: Output<'static>, mut ws2812: PioWs2812<'static, PIO0, 0, 1, Grb>) {
    // Loop forever making RGB values and pushing them out to the WS2812.
    const NUM_LEDS: usize = 1;
    let mut data = [RGB8::default(); NUM_LEDS];
    let mut ticker = Ticker::every(Duration::from_millis(100));
    loop {
        for j in 0..(256 * 5) {
            // debug!("New Colors:");
            for i in 0..NUM_LEDS {
                data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
                // debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
            }
            ws2812.write(&data).await;

            led.toggle(); // blink normal led as proof of life
            ticker.next().await;
        }
    }
}

#[embassy_executor::task]
async fn blink_task(led: Output<'static>, ws2812: PioWs2812<'static, PIO0, 0, 1, Grb>) {
    blink(led, ws2812).await;
}

Thanks.

4 posts - 2 participants

Read full topic

🏷️ Rust_feed