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

Non blocking smart led #6

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

robamu
Copy link

@robamu robamu commented Oct 12, 2024

No description provided.

@tschundler
Copy link
Contributor

I finally got around to some testing tonight. It only seems to only work with 2 of 6 ws28xx variants.

On my esp32, I measure about 25us between pixels and about 55us total.

From some research like https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/amp/ and the various datasheets, depending on the chip, the last time might be ~6us, ~50us, or ~250us

on the other 4, only one LED lights. So if this is accepted, it needs to be accepted with the caveat that it depends heavily on which LED are used Incidentally among those not working are what I'm pretty sure are genuine WorldSemi LEDs.

But... exciting news, I am able to drive two sets of the type that work in parallel using a join!
PXL_20241017_072807003 NIGHT RAW-01 COVER
(don't mind the bad grounding causing ghost signals)

But... here, the time between pixels is closer to 34us

I should go back to exploring double buffering in RMT async writes. Supposedly there's an ability now to force code into RAM to run faster?

@tschundler
Copy link
Contributor

one more very odd thing

with a buffer size of 65, I can only send 62 pixels of data. Otherwise I get BufferSizeExceeded. To send 64, I need to make a 67 pixel buffer.

#[main]
async fn main(spawner: Spawner) {
    let peripherals = esp_hal::init(esp_hal::Config::default());
    esp_println::logger::init_logger_from_env();

    esp_println::println!("Init!");

    let timg0 = TimerGroup::new(peripherals.TIMG0);
    esp_hal_embassy::init(timg0.timer0);

    let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);

    let led_pin = io.pins.gpio18;
    let l2_pin = io.pins.gpio23;

    let rmt = Rmt::new_async(peripherals.RMT, 80.MHz()).unwrap();

    let rmt_buffer = smartLedBuffer!(67);
    let rmt2_buffer = smartLedBuffer!(67);

    let mut l1 = asynch::SmartLedAdapterAsync::new(rmt.channel0, led_pin, rmt_buffer);
    let mut l2 = asynch::SmartLedAdapterAsync::new(rmt.channel1, l2_pin, rmt2_buffer);

    esp_println::println!("Start!");

    let mut ofs = 0;
    loop {
        let data = {
            let mut i = 0;
            [(); 64].map(|_| {
                i += 1;
                hsv2rgb(Hsv {
                    hue: (((i * 4) + ofs + 60) % 255) as u8,
                    sat: 240,
                    val: 10,
                })
            })
        };

        let f1 = l1.write(data[0..64].iter().cloned());
        let f2 = l2.write(data[0..64].iter().cloned());
        //let (o1, o2) = embassy_futures::join::join(f1, f2).await;
        let o1 = f1.await;
        let o2 = f2.await;
        o1.unwrap_or_else(|e| {
            esp_println::println!("Error1: {:?}", e);
        });
        o2.unwrap_or_else(|e| {
            esp_println::println!("Error2: {:?}", e);
        });

        ofs = (ofs + 1) % 255;
        esp_println::print!(".");
        Timer::after(Duration::from_millis(10)).await;
    }
}

changing the above to SmartLedBuffer!(64); will return errors when running.

@@ -14,18 +14,18 @@ targets = ["riscv32imac-unknown-none-elf"]
[dependencies]
defmt = { version = "0.3.8", optional = true }
document-features = "0.2.10"
esp-hal = "0.20.0"
esp-hal = ">=0.20.0, <=0.21.0"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why to exclude esp-hal 0.21.1?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, no. I just wonder whether <=0.21 or <=0.21.* would be cleaner/better (or even does the job)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh I see! It seems a bit misleading before a good Samaritan points you to the SemVer docs 😉

Copy link

@lulingar lulingar Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh it seems something is missing, this bit me:

error: failed to select a version for `esp-hal`.
    ... required by package `esp-hal-smartled v0.13.0`
    ... which satisfies dependency `esp-hal-smartled = "^0.13.0"` of package `test-xtensa-nostd v0.1.0 (/home/being/code/practice/rust/test-xtensa-nostd)`
versions that meet the requirements `^0.20.0` are: 0.20.1

the package `esp-hal` links to the native library `esp-hal`, but it conflicts with a previous package which links to `esp-hal` as well:
package `esp-hal v0.21.1`
    ... which satisfies dependency `esp-hal = "^0.21.1"` of package 

My relevant lines on Cargo.toml are:

esp-hal = { version = "0.21.1", features = ["esp32s3"] }
esp-hal-smartled = { version = "0.13.0", features = ["esp32s3"] }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just fixed the dependency specifier to also allow higher versions.. Can you try 0.14? I also bumped the minor version of the crate itself.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It worked fine! I had to set in Cargo.toml:

esp-hal-smartled = { git = "https://github.com/robamu/esp-hal-community.git", rev = "3562306", features = ["esp32s3"] }

@robamu
Copy link
Author

robamu commented Oct 17, 2024

On the second problem: the async API requires a different and slightly larger buffer size because each transfer must be 0 terminated. I have not created an async macro to declare that buffer yet, but i provided a const function which calculates the required buffer size.

It might be a reasonable idea to also add an async example?

@horho77
Copy link

horho77 commented Oct 23, 2024

It might be a reasonable idea to also add an async example?

async example sounds like a great idea 👍

@robamu robamu force-pushed the non-blocking-smart-led branch from aafb0d1 to 355dd33 Compare October 29, 2024 12:43
@robamu robamu force-pushed the non-blocking-smart-led branch from 355dd33 to 3562306 Compare October 29, 2024 12:43
@robamu
Copy link
Author

robamu commented Oct 29, 2024

I can try to add an example which is similar to the existing one, except it uses the async API.

@lulingar
Copy link

lulingar commented Oct 30, 2024

I can try to add an example which is similar to the existing one, except it uses the async API.

Here's mine:

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use core::{ptr::addr_of_mut, str::{self, FromStr}};

use alloc::string::String;

extern crate alloc;
use esp_alloc as _;

use esp_backtrace as _;
use esp_hal::{
    cpu_control::{CpuControl, Stack as CPUStack},
    gpio::{AnyPin, Input, Io, Level, Output, Pull},
    prelude::*,
    rmt::Rmt,
    timer::{
        timg::TimerGroup, AnyTimer
    },
    Async,
};

use esp_hal_embassy::Executor;
use embassy_executor::Spawner;
use embassy_time::{Delay, Duration, Instant, Timer};

use esp_println::println;

use esp_hal_smartled::{
    smart_led_buffer,
    asynch::SmartLedAdapterAsync,
};
use smart_leds::{
    brightness, gamma,
    hsv::{hsv2rgb, Hsv},
};


#[main]
async fn main(spawner: Spawner) -> ! {
    esp_println::logger::init_logger_from_env();

    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::Clock240MHz;
        config
    });

    let timg0 = TimerGroup::new(peripherals.TIMG0);
    let timer1: AnyTimer = timg0.timer1.into();
    esp_hal_embassy::init(timer1);

    let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL);
    let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);

    let led_pin = io.pins.gpio48;
    let freq = 80.MHz();
    let rmt = Rmt::new_async(peripherals.RMT, freq).unwrap();
    let rmt_buffer = smart_led_buffer!(1);
    let led = SmartLedAdapterAsync::new(rmt.channel0, led_pin, rmt_buffer);

    let mut color = Hsv {
        hue: 0,
        sat: 255,
        val: 255,
    };
    let mut data;

    loop {
        for hue in 0..=255 {
            color.hue = hue;
            // Convert from the HSV color space (where we can easily transition from one
            // color to the other) to the RGB color space that we can then send to the LED
            data = [hsv2rgb(color)];
            // When sending to the LED, we do a gamma correction first (see smart_leds
            // documentation for details) and then limit the brightness to 10 out of 255 so
            // that the output it's not too bright.
            if let Err(e) = led.write(brightness(gamma(data.iter().cloned()), 20)).await {
                log::error!("Driving LED: {:?}", e);
            }
            Timer::after(Duration::from_millis(10)).await;
        }
    }
}

@Be-ing
Copy link

Be-ing commented Jan 2, 2025

smart-leds-trait just added an async version of the trait, though it hasn't been released yet:

smart-leds-rs/smart-leds-trait#14
smart-leds-rs/smart-leds-trait#15

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

Successfully merging this pull request may close these issues.

5 participants