Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controlling a WS2812 LED #448

Closed
weiying-chen opened this issue Jul 6, 2024 · 8 comments
Closed

Controlling a WS2812 LED #448

weiying-chen opened this issue Jul 6, 2024 · 8 comments

Comments

@weiying-chen
Copy link

weiying-chen commented Jul 6, 2024

I want to control the WS2812 LED of my ESP32-C3-Zero using esp_idf_hal's SPI. I tried this:

use anyhow::Result;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::spi::{config::Config, SpiDeviceDriver, SpiDriver, SpiDriverConfig, SPI2};
use esp_idf_svc::log::EspLogger;
use esp_idf_sys::{self as _};
use log::info;

fn main() -> Result<()> {
    esp_idf_svc::sys::link_patches();
    EspLogger::initialize_default();

    let peripherals = Peripherals::take()?;
    let spi = peripherals.spi2;

    let sclk = peripherals.pins.gpio6; // SCLK
    let serial_in = peripherals.pins.gpio4; // SDI (MISO)
    let serial_out = peripherals.pins.gpio7; // SDO (MOSI)
    let cs_1 = peripherals.pins.gpio10; // Chip Select for device 1 (LED pin)

    println!("Starting SPI loopback test");

    let driver = SpiDriver::new::<SPI2>(
        spi,
        sclk,
        serial_out,
        Some(serial_in),
        &SpiDriverConfig::new(),
    )?;

    let config_1 = Config::new().baudrate(3_000_000.into());
    let mut device_1 = SpiDeviceDriver::new(&driver, Some(cs_1), &config_1)?;

    let led_data = [
        0b11101110, 0b10001000, 0b10001000, // Red
        0b10001000, 0b11101110, 0b10001000, // Green
        0b10001000, 0b10001000, 0b11101110, // Blue
    ]; // Buffer to hold the LED data (8 bits per color, 3 colors)

    loop {
        FreeRtos::delay_ms(500);

        device_1.write(&led_data)?;
        info!("WS2812: Sent LED data {:x?}", led_data);
    }
}

No compiler errors. And there's output. But the WS2812 LED doesn't turn on.

Note: This is a rust library for WS2812. But I don't think it's compatible with esp_idf_hal.

@weiying-chen
Copy link
Author

weiying-chen commented Jul 6, 2024

I also tried something a little more complex like this:

use anyhow::Result;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::gpio::{AnyOutputPin, Output, OutputPin, PinDriver};
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::spi::{config::Config, SpiDeviceDriver, SpiDriver, SpiDriverConfig, SPI2};
use esp_idf_svc::log::EspLogger;
use esp_idf_sys::{self as _};
use log::info;

struct WS2812<'a, CS: OutputPin> {
    spi: SpiDeviceDriver<'a, &'a SpiDriver<'a>>,
    cs: PinDriver<'a, CS, Output>,
}

impl<'a, CS> WS2812<'a, CS>
where
    CS: OutputPin,
{
    pub fn new(spi: SpiDeviceDriver<'a, &'a SpiDriver<'a>>, cs: PinDriver<'a, CS, Output>) -> Self {
        Self { spi, cs }
    }

    pub fn write_byte(&mut self, data: u8) -> Result<()> {
        let patterns = [0b1000_1000, 0b1000_1110, 0b1110_1000, 0b1110_1110];
        let mut buffer = [0u8; 4];
        let mut data = data;

        for i in 0..4 {
            let bits = (data & 0b1100_0000) >> 6;
            buffer[i] = patterns[bits as usize];
            data <<= 2;
        }

        self.cs.set_low()?;
        self.spi.write(&buffer)?;
        self.cs.set_high()?;

        Ok(())
    }

    pub fn flush(&mut self) -> Result<()> {
        let buffer = [0u8; 140];
        self.cs.set_low()?;
        self.spi.write(&buffer)?;
        self.cs.set_high()?;
        Ok(())
    }
}

fn main() -> Result<()> {
    esp_idf_svc::sys::link_patches();
    EspLogger::initialize_default();

    let peripherals = Peripherals::take()?;
    let spi = peripherals.spi2;

    let sclk = peripherals.pins.gpio6; // SCLK
    let serial_in = peripherals.pins.gpio4; // SDI (MISO)
    let serial_out = peripherals.pins.gpio7; // SDO (MOSI)
    let cs_pin = peripherals.pins.gpio10; // Chip Select for device (LED pin)

    println!("Starting SPI WS2812 control");

    let driver = SpiDriver::new(
        spi,
        sclk,
        serial_out,
        Some(serial_in),
        &SpiDriverConfig::new(),
    )?;

    let config = Config::new().baudrate(3_000_000.into());
    let spi_device = SpiDeviceDriver::new(&driver, None::<AnyOutputPin>, &config)?; // No CS pin here
    let cs = PinDriver::output(cs_pin)?; // CS pin managed separately

    let mut ws2812 = WS2812::new(spi_device, cs);

    let red = 255;
    let green = 0;
    let blue = 0;

    loop {
        FreeRtos::delay_ms(500);

        ws2812.flush()?;

        ws2812.write_byte(green)?;
        ws2812.write_byte(red)?;
        ws2812.write_byte(blue)?;

        ws2812.flush()?;
        info!("WS2812: Sent LED data for Red color");
    }
}

Same: No compile errors. There's output. But the WS2812 LED doesn't turn on.

@Vollbrecht
Copy link
Collaborator

Are you sure that the ws2812 are fast enough to be able to work with a 3MHz clocked signal?

@weiying-chen
Copy link
Author

weiying-chen commented Jul 6, 2024

Ah, maybe not (Sorry, I'm not very experienced with this). I guess it'll be too complicated to use SPI.

Now I'm using this RMT code. It works for external ws2812s. But not with the built-in ws2812 of the ESP32-C3-Zero. It used to be off all the time. And now it's always green no matter what. (The exact same thing happens with the official Arduino code for the ESP32-C3-Zero.)

@okhsunrog
Copy link
Contributor

okhsunrog commented Aug 13, 2024

The https://github.com/smart-leds-rs/ws2812-spi-rs you mentioned is fully compatible with esp-idf-hal. The pull request for embedded-hal 1.0 is merged now. You also need to enable features = ["mosi_idle_high"] for it to work, and probably need to change RGB order. I did a fork of ws2812-spi-rs before embedded-hal 1.0 support was merged, the fork consists of the pull request with embedded-hal 1.0 support + RGB color order. You can see the working code for esp32-c3 with SPI + DMA here: https://github.com/okhsunrog/esp_pd_rs
@Vollbrecht I think this issue can be closed

@okhsunrog
Copy link
Contributor

Are you sure that the ws2812 are fast enough to be able to work with a 3MHz clocked signal?

It most certainly is, I'm using 3.2 MHz in my code, the SPI signal is used to form correct timings, the timings I get with 3.2 MHz match the ones described in the datasheet for WS2812B

@okhsunrog
Copy link
Contributor

okhsunrog commented Aug 13, 2024

@weiying-chen the code you can find in my links is tested to work with ESP32-C3-Zero built-in WS2812 led

@weiying-chen
Copy link
Author

weiying-chen commented Aug 13, 2024

@okhsunrog Thanks a lot! I'll try it out.

@github-project-automation github-project-automation bot moved this from Todo to Done in esp-rs Aug 13, 2024
@weiying-chen
Copy link
Author

@okhsunrog Your code works. Thanks a lot again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

No branches or pull requests

3 participants