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

Flickering when using wifi from esp_idf_svc with this crate #33

Open
RoyalFoxy opened this issue Oct 5, 2023 · 16 comments
Open

Flickering when using wifi from esp_idf_svc with this crate #33

RoyalFoxy opened this issue Oct 5, 2023 · 16 comments

Comments

@RoyalFoxy
Copy link

So I encounter flickering of random colors on my led board. I already checked many different pins (to be exact 2,4,13,16,17,18) and all had the same problems.
A small video showing what I mean.

Video.Oct.4.mp4

Here's a demo of my code I use. You may need to adjust the led amount and what pin.

I tested it and it flickers when the wifi is started up.

@RoyalFoxy RoyalFoxy changed the title Flickering when using wifi from esp_idf_svc Flickering when using wifi from esp_idf_svc with this crate Oct 6, 2023
@thorhs
Copy link

thorhs commented Jan 7, 2024

I am also experiencing some flickering/incorrect output from time to time. If I run my test pattern before starting the Wi-Fi, it works flawlessly. With Wi-Fi, I get the incorrect output.

Most likely this is due to Wi-Fi interrupts firing during a critical section causing a slight delay in the output timing.

I have not been able to dig deeper into the code to try to figure out what is causing it.

IMG_9972.mov

@RoyalFoxy
Copy link
Author

It irritates me tho that this is not happening with WLED considering that codebase also runs on esp32, has wifi and can control hundreds of leds. Maybe they implemented it without anything from esp-idf or they edited the source from esp-idf to make it not flicker.

I don't have a video of it at hand but I can confirm that a lamp with 240 leds in serial powered by WLED is not affected by our issue

@y34hbuddy
Copy link

y34hbuddy commented Jan 9, 2024

If your hardware is dual-core, try running all of your display code on Core 1 as all of the wifi activity (and its interrupts, as @thorhs mentions) will run on Core 0.

Try using unsafe { xTaskCreatePinnedToCore() }

@thorhs
Copy link

thorhs commented Jan 9, 2024

Interesting idea. I'm currently using std threads, is there a simple migration between them that you know of?

@y34hbuddy
Copy link

I don't think it'll be too difficult to migrate from std threads, and in fact, you might not have to do so everywhere in your code. Depending on your implementation, you may just need to call xTaskCreatePinnedToCore() once to spawn your display code.

In my use case, my recommendation works perfectly -- and it took quite a bit of digging into the C++ ESP-IDF and Arduino universe to find the solution, ex: FastLED/FastLED#507 😄

@thorhs
Copy link

thorhs commented Jan 9, 2024

This worked perfectly, haven't gotten a single flicker after switching to core 1.

For others that have the same issue, I moved my display loop to a new function and created a wrapper function to handle the C ffi.

Snippets:

    unsafe {
        xTaskCreatePinnedToCore(
            Some(start_display_loop),
            CString::new("Display Task").unwrap().as_ptr(),
            10000,
            unsafe { &panel_sender as *const _ as *mut std::ffi::c_void },
            0,
            std::ptr::null_mut(),
            1,
        );
    }
unsafe extern "C" fn start_display_loop(receiver: *mut core::ffi::c_void) {
    let panel_receiver: &std::sync::mpsc::Receiver<PanelCommand<'_>> =
        &*(receiver as *const std::sync::mpsc::Receiver<PanelCommand<'_>>);

    display_loop(panel_receiver);
}

fn display_loop(panel_receiver: &std::sync::mpsc::Receiver<PanelCommand<'_>>) {
...
}

There may very well be better ways to go about it, but this works fine. I had to tune the stack size to get my code working, so that is somethign to keep in mind.

@RoyalFoxy
Copy link
Author

In the past I had this code which didn't work even tho the commented line sounds familiar to xTaskCreatePinnedToCore. Do you know why this doesn't work?

ThreadSpawnConfiguration {
    name: Some(b"LED-THREAD\0"),
    stack_size: STACK_SIZE,
    priority: 1,
    pin_to_core: Some(Core::Core1), // <- Sounds familiar
    ..Default::default()
}
.set()?;

std::thread::spawn(|| -> anyhow::Result<()> {
    // LED Code
});

@DavidVentura
Copy link
Contributor

I'm seeing the flicker with both a normal thread on core1 and xTaskCreatePinnedToCore -- I see a 3μs interrupt every 1 or 2 seconds when Wifi is enabled.

This is with my task on core 1; I don't think it's a problem on this library, but I wonder why xTaskCreatePinnedToCore works for you

@DavidVentura
Copy link
Contributor

I've found what the issue is:
When creating the RMT driver, the current core affinity is stored.
Later, when starting a transmission, the RMT driver spawns a thread based on the stored affinity.
To fix the issue, you need to call Ws2812Esp32Rmt::new from within Core1 -- calling ws2812.write from Core1 is not enough.

With this, my 1280pix matrix is flicker-free

@aligator
Copy link

I tried to do the same.
When connecting to a wifi and running it in the normal thread it flickers some times (as expected).
If I create a simple webserver and spam it with calls it flickers more (as expected).

But when using the core1 it still does it. It feels a bit less, but even when I send a request only every second, it sometimes still flickers. If I spam many requests, it flickers also very noticeable...

So running the Ws2812Esp32Rmt on core 1 does not really seem to resolve this problem as expected...

As I am not very experienced with rust on the esp, it took me some time to get it working in the extra thread.
This is what I use now:
https://github.com/aligator/e-chess/blob/d46b8fe666993aa59dee65ecf640145bafa625e4/firmware/src/main.rs

I call Ws2812Esp32Rmt::new from within the core1 thread.

@DavidVentura
Copy link
Contributor

In my project it is working fine, check: https://github.com/DavidVentura/matrix-fire/blob/master/esp/src/main.rs#L24

the wifi settings must be done with affinity on core0 and the call to
Ws2812Esp32Rmt::new must be done

  • in core1
  • with global affinity for future threads set to core1

this is because the rmt peripheral wil later just start a thread for interrupt handling with the global affinity state

@aligator
Copy link

I just found out that it much better runs with release mode with these settings.

[profile.release]
opt-level = "s"

[profile.dev]
debug = true    # Symbols are nice and they don't increase the size on Flash
opt-level = "z"

Now I can spam many requests and only get some flickers. Not zero but ok for now.

However thanks for your code, I will try it how you do it now, since you do not use the low level xTaskCreatePinnedToCore.

@aligator
Copy link

Ok, I don't know why, but using the std threads does not work reliable for me. It always throws a stack overflow as soon as I do something inside the thread... (with the same stacksize configuration as I use with xTaskCreatePinnedToCore)

Not exactly sure whats happening there...

However using xTaskCreatePinnedToCore and your tipps regarding the global affinity I got it working more reliable now
even in non-release mode.

For me this is ok for now.

About this lib:
Shouldn't there be either a more easy way to just configure core the lib uses to send the data to the leds?
Or there should be at least some "best practice" example for usage together with WIFI, as I suspect this is a very common usecase on the esp.

@y34hbuddy
Copy link

Same, I also get stack overflows when I refactor from xTaskCreatePinnedToCore to std threads, even if I specify a huge stack size.

Anyone know why?

@RoyalFoxy
Copy link
Author

@y34hbuddy You should set CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=10000 in your sdkconfig.defaults file and it should work

@RoyalFoxy
Copy link
Author

I've gathered the info from this issue so far and created a small project trying to make it work but I still get flickering when sending requests

The program looks something like this

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

    let peripherals = Peripherals::take().unwrap();
    let modem = peripherals.modem;
    let led_pin = peripherals.pins.gpio13;
    let channel = peripherals.rmt.channel0;

    ThreadSpawnConfiguration {
        pin_to_core: Some(Core::Core0),
        priority: 1,
        name: Some("SERVER\0".as_bytes()),
        stack_size: 0x4000,
        ..Default::default()
    }
    .set()
    .expect("Cannot set thread spawn config");

    std::thread::spawn(move || -> anyhow::Result<()> {
        let sysloop = EspSystemEventLoop::take()?;
        let nvs = EspDefaultNvsPartition::take()?;

        let mut wifi =
            BlockingWifi::wrap(EspWifi::new(modem, sysloop.clone(), Some(nvs))?, sysloop)?;

        // Connect to wifi with BlockingWifi
        // Create http server with EspHttpServer

        loop {
            sleep(Duration::from_millis(10000));
        }
    });

    ThreadSpawnConfiguration {
        name: Some("PIXELS\0".as_bytes()),
        stack_size: 0x4000,
        pin_to_core: Some(Core::Core1),
        priority: 24,
        ..Default::default()
    }
    .set()
    .expect("Cannot set thread spawn config");

    std::thread::spawn(|| -> anyhow::Result<()> {
        let mut data = [RGB8::default(); 100];
        data[0] = RGB8::new(10, 0, 0);
        data[1] = RGB8::new(10, 0, 0);
        data[2] = RGB8::new(10, 0, 0);

        let mut led_driver = Ws2812Esp32Rmt::new(channel, led_pin).unwrap();

        loop {
            led_driver.write_nocopy(data.into_iter())?;

            std::thread::sleep(Duration::from_millis(1000 / 60))
        }
    });

    loop {
        std::thread::sleep(Duration::from_secs(10))
    }
}

I'm unsure what I am doing wrong, I tested various pins again and it seems to not work at all and it always will flicker

I guess the problems are interrupts but shouldn't they not trigger on the second core? And how can I disable them for a certain block of code, I know that I can I just couldn't find how

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

No branches or pull requests

5 participants