esp-idf-hal icon indicating copy to clipboard operation
esp-idf-hal copied to clipboard

Watchdog timer reset with fast main loop: How is it supposed to be written with safe Rust?

Open mbuesch opened this issue 1 year ago • 10 comments

Hi,

in my program I use a fast main loop without sleeps in it. I want it to be as fast as possible. The problem looks like this (unrelated parts left out):

fn main() {
    loop {
        // Some work, like fast pin toggling.
    }
}

This results in the watchdog to kill my tasks:

E (5342) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (5342) task_wdt:  - IDLE (CPU 0)
E (5342) task_wdt: Tasks currently running:
E (5342) task_wdt: CPU 0: main
E (5342) task_wdt: CPU 1: IDLE
E (5342) task_wdt: Print CPU 0 (current core) backtrace

I interpret this as such that the IDLE task on CPU0 is never executed. That's actually desired. That's what I told it to do. I don't want to give up my task execution. All other tasks shall run on the second CPU. But apparently the watchdog is handled by the IDLE task. Therefore it times out.

I do not want to add a sleep to the main loop (which would eventually trigger the IDLE task).

I came up with the following workaround (using esp_idf_sys crate):

fn main() {
    // Disable IDLE task WDT on this CPU.
    unsafe { esp_idf_sys::esp_task_wdt_delete(esp_idf_sys::xTaskGetIdleTaskHandleForCPU(esp_idf_hal::cpu::core() as u32)) };
    // Enable WDT on the main task (this task).
    unsafe { esp_idf_sys::esp_task_wdt_add(esp_idf_sys::xTaskGetCurrentTaskHandle()) };

    loop {
        // Some work, like fast pin toggling.

        // Feed WDT.
        unsafe { esp_idf_sys::esp_task_wdt_reset(); }
    }
}

That works. Now the watchdog doesn't trigger any more and my fast loop executes forever.

Now my question: Is there a safe-Rust way to do this with esp_idf_hal or another safe crate API?

mbuesch avatar Sep 08 '22 15:09 mbuesch

Hi, I don't know if this is the same issue as mine. I've a similar problem. I try to use the Blinky example, but the watchdog kills the app. If I change the main loop from this:

loop {
    led.set_high()?;
    thread::sleep(Duration::from_millis(1000));
    led.set_low()?;
    thread::sleep(Duration::from_millis(1000));
}

to this:

loop {
    led.set_high()?;
    thread::sleep(Duration::from_millis(500));
    led.set_low()?;
    thread::sleep(Duration::from_millis(1500));
}

Everything is fine. I've no idea why.

mr-sven avatar Sep 09 '22 08:09 mr-sven

This is probably due to this: https://github.com/esp-rs/rust/issues/137. I would recommend rolling back to 1.63.0.0 for now. I have been working on a proper fix which I am testing right now.

Apologies for the confusion!

MabezDev avatar Sep 09 '22 10:09 MabezDev

@mr-sven Can you test with the latest compiler and let us know if you still see the issue? Otherwise, your approach is correct - you have to - from time to time - put your main task to sleep for at least 10ms, so that the lower priority IDLE tasks get a chance to run. Or switch to an event driven model, where your threads are reacting to events and then go to sleep again, waiting on a queue, or mutex or whatever that signals that a new event is due.

ivmarkov avatar Sep 22 '22 08:09 ivmarkov

Hi, confirmed. I tested with 1.64 and default Blink example. thread::sleep is now working.

mr-sven avatar Sep 22 '22 09:09 mr-sven

@MabezDev Please re-open this issue. The questions from my original post have not been addressed or answered in any way. What has been resolved is the essentially (as it turned out later) unrelated sub discussion.

I am still wondering how to code a fast main loop without sleeping and without unsafe.

mbuesch avatar Oct 01 '22 17:10 mbuesch

Can somebody please re-open this issue? It has not been resolved. The problem that @mr-sven confimed as fixed turned out to be completely unrelated to my original question.

Thanks a lot :)

mbuesch avatar Oct 02 '22 15:10 mbuesch

If you want to busy-loop in the main (or whichever thread) forever, then I suggest you instead simply disable the TWDT as described here.

Note though that this is not a pattern encouraged by ESP-IDF, where the philosophy (as far as I understand it) is to have your threads reacting on events rather than busy looping.

With the above said, we can implement some sort of safe API around TWDT. It was postponed for now because (a) this is a relatively rare request (folks often just do not understand that busy looping has alternatives) and (b) the Watchdog traits in e-hal 0.2 disappeared (perhaps temporarily or because there was no consensus on those) from e-hal 1.0.

ivmarkov avatar Oct 02 '22 15:10 ivmarkov

I can also re-open this issue (you seem to feel very strongly about this :p) but I don't have time ATM to implement it. Would you be willing to spend some time and contribute a PR? Perhaps, to the esp-idf-hal::task module in master?

ivmarkov avatar Oct 02 '22 15:10 ivmarkov

Cool. Thanks for explaining the reasons behind this.

Yes, I do understand that often one shouldn't be using a busy loop.

However, how can we achieve small latencies without doing so? If I have to go sleeping a couple of milliseconds every now and then, then this is the lower limit to main task latency. For instance let's assume I need to toggle a pin every 100 µs. I don't think this is too uncommon. How would I implement that?

I am willing to contribute time and even PR for this. But I currently don't seem to understand the current architecture good enough and I don't fully understand how a safe wdt api could look like to fit your architecture design.

Thanks for your help.

mbuesch avatar Oct 02 '22 15:10 mbuesch

For instance let's assume I need to toggle a pin every 100 µs. I don't think this is too uncommon. How would I implement that?

100uS is quick, yet might be slow enough so that you can use the ESP timer service which is exposed in esp-idf-svc. Or some of the ESP IDF hardware timers which are also exposed in latest esp-idf-hal master. As for how a safe API might look like - I agree this deserves some thought. As for the architecture design - esp-idf-hal and esp-idf-svc are relatively thin wrappers.

ivmarkov avatar Oct 02 '22 16:10 ivmarkov