Both cores with pinned tasks, is main loop needed?

Apologies if this is the incorrect place for this.

I am using an ESP32-S3 with arduino, I have two tasks each pinned to a core and all software is working fine (well for my future plans if anyone has some good reading on semaphore or mutex or even queues it would be helpful).

My question is more general; I have two tasks, pinned to both cores, both looping (one gets started and stopped, the other always runs). My main loop does nothing, but I cant help but think even empty, there is still time and cycles being spent on it. How can I minimize/eliminate that?

Uh...wat.

Not at all what I was asking.

Esp32-S3 has 2 cores; Core 0 and Core 1.

Core 0 is running task 0 pinned to the Core.
Core 1 is running task 1 pinned to the Core.
Both of these are by design.

There has to be some delay in task 0 and task 1 to allow the garbage collector and other functions to run. Arduino IDE requires void loop to compile.

Inevitably the empty void loop will run. Void loop is a waste of resources and time that would be better spent on task 1 (since loop is always on core 1). I suppose I could try vTaskDelete(Null) in the void loop and see what happens, or crank the desired task priority.

Empty functions are optimised out by the compiler.

But even so, loop() is doing a lot more than what you put in it. It's where millis() gets updated, where the ESP runs its WiFi stack and other processes, etc.

Good info, thank you.

This project is not running wifi or bluetooth (but may in the future. I am eyeballing Serial over Bluetooth), but I am curious what else the main loop is handling. I am likely going to try vtaskdelete(null) just to see what happens, but a safer course of action seems to be making my tasks higher priority.

Essentially task 1 is polling/getting data from a FIFO buffer of an RF chip, then passing that data to task 0 for decoding the RF signal. I do need to implement a Queue since task 0 can delay if there is no data to process.

I have working decoders for a variety of RF signals already working with the Yardstick One, figured I would create a cheaper (and less simple, <3 python) version that spits out decoded signals via Serial.

Maybe parlay the result into RF controlled stuff in the house with my own protocols.

Dunno if this is allowed, but after reading up on queues, mutex and semaphore I came across this video.

Interestingly he uses vtaskdelete(null) in his main loop, because like me, he has his work done with tasks. That is at 25:05. (no this isn't me obviously)

This is so incorrect I made an account.

Yes, empty functions can be optimized out by the compiler. They are not always, but most often are. It is unwise to speak in absolutes.

loop() is not doing anything except what's inside loop() -- Arduino cannot/does not arbitrarily add code within another function in this manner. On the AVR platform, loop() is called within an infinite loop which also processes serial events, immediately after the user's setup() is called, after core timers and ISRs (including timer0 for millis() which comes up soon...) and other things are initialized. On the Espressif32 platform, loop() is called by void loopTask(void *pvParameters) within cores/esp32/main.cpp --

void loopTask(void *pvParameters)
{
    setup();
    for(;;) {
#if CONFIG_FREERTOS_UNICORE
        yieldIfNecessary();
#endif
        if(loopTaskWDTEnabled){
            esp_task_wdt_reset();
        }
        loop();
        if (serialEventRun) serialEventRun();
    }
}

If you were to find a way to omit loop() from your own code, it's still called within a hard infinite loop with a few other tasks. Killing that loop (the one loop() is called inside) would disable the wdt timer reset (IE WDT would kick if not externally tamed) on the ESP32, and hardware serial event triggers on both ESP32 and AVR platforms.

The idea that millis() is "updated" conveys a full lack of comprehension of how any of this works behind the scenes. On AVR platforms, the millis() function boils down to returning the variable volatile unsigned long timer0_millis (aka uint64) declared in cores/arduino/wiring.c and that specific long is updated by the ISR for hardware timer0, it isn't "updated" in a loop.

On the Espressif32 platform, millis() is the result of dividing the return of int64_t esp_timer_get_time(void); (a core library function whose source is not provided in the platform files) which is, again, not "run in a loop" -- it's a core library call to a counter which is incremented in hardware every clock cycle the device is powered. It's hardware, not software, which is what makes it ground-truth level accurate. (Similar to the AVR's timer0 ISR; that's the closest the AVR platform has to "ground truth" regarding the concept of time.)

The Wifi stack and other processes, etc are run in their own functions, called by their own hardware and/or triggers. With Arduino, ESP32 runs FreeRTOS -- yes, an entire operating system -- and calls your setup() and loop() functions as tasks within that OS.

When you write code in void loop() { ... } Arduino DOES NOT add things to it. That's your function. Arduino calls it regularly.

@sword111 --- vTaskDelete(null) -- PS, case matters in C -- tells FreeRTOS to delete the task it is called from within. (source) On Espressif32 this means the entire loopTask function I mentioned above. Repercussions appear to be serialEventRun() and wdt taming events won't happen. Do note, these tasks are run by a scheduler and the core is designed to avoid running 100% full hard loop like the AVR platform did. You likely won't see much power savings or performance increase. There appears to be no harm to it.

I've been throwing baremetal and Arduino code at AVRs for decades. I just started working with the ESP32 three days ago and had to do a ton of learning this to rebase a very complicated timing-critical project onto something with considerably more torque behind it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.