app_main() vs loop()

SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

app_main() vs loop()

Postby SparkyNZ » Fri Jan 12, 2024 8:20 pm

What are the main differences between the loop() method when using the Arduino IDE and a tight loop inside app_main() when building a C++ app with IDF?

Code: Select all

void setup()
{
}

void loop()
{
}

extern "C" void app_main(void)
{
    setup();
    for(;;)
    {
      loop();
    }
}
Isn't the above code the same as what an Arduino IDE sketch would do, or does the Arduino sketch do something else behind the scenes? (such as servicing interrupts???)

The reason I ask is that I ported a .ino file over to main.cpp in my project using the FabGl library and the app appears to be stalling or rebooting. If I put a delay in after loop() it may work a bit longer but ultimately its doing the same thing.

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: app_main() vs loop()

Postby MicroController » Fri Jan 12, 2024 10:36 pm


SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

Re: app_main() vs loop()

Postby SparkyNZ » Sat Jan 13, 2024 7:13 pm

Thank you! Time to give it a shot..

SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

Re: app_main() vs loop()

Postby SparkyNZ » Sat Jan 13, 2024 8:09 pm

I have copied in some of the code from the Arduino source but I still can't get this loop to behave. In loopTask(), if I add a 2 second vTaskDelay(), I see some text on the VGA display. If I remove it, it doesn't work.

Am I missing something from the standard Arduino setup() and loop() ?

Code: Select all

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"

#include "esp_task.h"
#include "esp_task_wdt.h"
#include <Time.h>

#include "fabgl.h"

fabgl::VGAController DisplayController;
fabgl::Canvas        canvas(&DisplayController);

#define DOUBLEBUFFERING 1

struct Test {
  virtual ~Test() { };
  virtual void update() = 0;
  virtual bool nextState() = 0;
  virtual int testState() = 0;
  virtual char const * name() = 0;
};

#include "ballstest.h"
#include "polygonstest.h"
#include "spritestest.h"

void setupDisplay()
{
  DisplayController.begin();
  DisplayController.setResolution(VGA_320x200_75Hz, -1, -1, DOUBLEBUFFERING);
  //DisplayController.moveScreen(-8, 0);

  // get a font for about 40x14 text screen
  canvas.selectFont(&fabgl::FONT_8x8);
  canvas.setGlyphOptions(GlyphOptions().FillBackground(true));
}

int pds = 0;

void updateDisplay()
{
  static int64_t stime  = esp_timer_get_time();
  static int FPS        = 0;
  static int FPSCounter = 0;
  static int testIndex  = 0;
  static Test * test    = new BallsTest;

  if (test->nextState() == false) {
    delete test;
    ++testIndex;
    switch (testIndex) {
      case 1:
        test = new PolygonsTest;
        break;
      case 2:
        test = new SpritesTest;
        break;
      default:
        testIndex = 0;
        test = new BallsTest;
        break;
    }
  }

  if (esp_timer_get_time() - stime > 1000000) {
    // calculate FPS
    FPS = FPSCounter;
    stime = esp_timer_get_time();
    FPSCounter = 0;
  }
  ++FPSCounter;

  test->update();

  pds ++;

  // display test state and FPS
  canvas.setPenColor(Color::Blue);
  canvas.setBrushColor(Color::Yellow);
  canvas.drawTextFmt(80, 5, " %d My %s at %d FPS PDS: %d", test->testState(), test->name(), FPS, pds);

  if (DOUBLEBUFFERING)
    canvas.swapBuffers();
}


extern void serialEventRun(void) __attribute__((weak));

TaskHandle_t loopTaskHandle = NULL;

bool loopTaskWDTEnabled = false;

void enableLoopWDT(){
    if(loopTaskHandle != NULL){
        if(esp_task_wdt_add(loopTaskHandle) != ESP_OK){
            // PDS> log_e("Failed to add loop task to WDT");
        } else {
            loopTaskWDTEnabled = true;
        }
    }
}

#define ARDUINO_ISR_ATTR
unsigned long ARDUINO_ISR_ATTR millis()
{
    return (unsigned long) (esp_timer_get_time() / 1000ULL);
}

void yieldIfNecessary(void){
    static uint64_t lastYield = 0;
    uint64_t now = millis();
    if((now - lastYield) > 2000) {
        lastYield = now;
        vTaskDelay(5); //delay 1 RTOS tick
    }
}

void loopTask(void *pvParameters) {
    setupDisplay();

    for(;;)
    {
//#if CONFIG_FREERTOS_UNICORE
      yieldIfNecessary();
//#endif      

      if(loopTaskWDTEnabled){
          esp_task_wdt_reset();
      }

      updateDisplay();

      //if (serialEventRun) serialEventRun();
      //vTaskDelay(2000 / portTICK_PERIOD_MS); // The only way I can get something to appear
    }
}

extern "C" void app_main(void)
{
  // PDS> Do I need this?
  enableLoopWDT();

  //xTaskCreateUniversal(loopTask, "loopTask", getArduinoLoopTaskStackSize(), NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE);
  xTaskCreatePinnedToCore(
    loopTask
    ,  "loopTask"
    ,  2048  // Stack size
    ,  NULL  // When no parameter is used, simply pass NULL
    ,  1  // Priority
    ,  &loopTaskHandle // With task handle we will be able to manipulate with this task.
    ,  tskNO_AFFINITY  // Core on which the task will run
    );

}

SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

Re: app_main() vs loop()

Postby SparkyNZ » Sat Jan 13, 2024 8:24 pm

When I enable logging, I see this lots:

Code: Select all

Hello Paul!
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x400d67f5  PS      : 0x00060d30  A0      : 0x800d64f4  A1      : 0x3ffb7380  
A2      : 0x3ffb73a0  A3      : 0xce8b3138  A4      : 0x33145c07  A5      : 0xbca1a626  
A6      : 0x00000001  A7      : 0x00000010  A8      : 0x8e9006d4  A9      : 0x3ffb7350  
A10     : 0x00000000  A11     : 0xbca1a626  A12     : 0xffffffcc  A13     : 0x000003fe  
A14     : 0x7ff00000  A15     : 0x00000398  SAR     : 0x0000001f  EXCCAUSE: 0x0000001c  
EXCVADDR: 0xce8b3138  LBEG    : 0x4000c46c  LEND    : 0x4000c477  LCOUNT  : 0x00000000  

Backtrace:0x400d67f2:0x3ffb7380 0x400d64f1:0x3ffb73a0 0x400d5b12:0x3ffb73d0 0x400d61d2:0x3ffb7400 0x400d62ad:0x3ffb7430 0x4008b045:0x3ffb7450


ELF file SHA256: f2a626a674ba3250

Rebooting...

Does this relate to the watchdog resetting the device? I chose tskNO_AFFINITY for the Core when setting up my task because I have no idea which core it would be using. Is that my problem?

I've attached my putty.log - maybe it will show something that will mean more to others?
Attachments
putty.log
(60.46 KiB) Downloaded 1606 times

SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

Re: app_main() vs loop()

Postby SparkyNZ » Sat Jan 13, 2024 8:40 pm

After adding some debug, I can see that my updateDisplay() method is being called several times before the device crashes:

Code: Select all

[0;32mI (13) boot: compile time 10:04:07[0m
[0;32mI (13) boot: chip revision: v3.1[0m
[0;32mI (16) boot.esp32: SPI Speed      : 40MHz[0m
[0;32mI (20) boot.esp32: SPI Mode       : DIO[0m
[0;32mI (25) boot.esp32: SPI Flash Size : 2MB[0m
[0;32mI (30) boot: Enabling RNG early entropy source...[0m
[0;32mI (35) boot: Partition Table:[0m
[0;32mI (39) boot: ## Label            Usage          Type ST Offset   Length[0m
[0;32mI (46) boot:  0 nvs              WiFi data        01 02 00009000 00006000[0m
[0;32mI (53) boot:  1 phy_init         RF data          01 01 0000f000 00001000[0m
[0;32mI (61) boot:  2 factory          factory app      00 00 00010000 00100000[0m
[0;32mI (68) boot: End of partition table[0m
[0;32mI (72) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=0acc0h ( 44224) map[0m
[0;32mI (97) esp_image: segment 1: paddr=0001ace8 vaddr=3ffb0000 size=02194h (  8596) load[0m
[0;32mI (101) esp_image: segment 2: paddr=0001ce84 vaddr=40080000 size=03194h ( 12692) load[0m
[0;32mI (108) esp_image: segment 3: paddr=00020020 vaddr=400d0020 size=22510h (140560) map[0m
[0;32mI (162) esp_image: segment 4: paddr=00042538 vaddr=40083194 size=0b108h ( 45320) load[0m
[0;32mI (188) boot: Loaded app from partition at offset 0x10000[0m
[0;32mI (188) boot: Disabling RNG early entropy source...[0m
[0;32mI (199) cpu_start: Pro cpu up.[0m
[0;32mI (199) cpu_start: Starting app cpu, entry point is 0x40081184[0m
[0;32mI (0) cpu_start: App cpu up.[0m
[0;32mI (214) cpu_start: Pro cpu start user code[0m
[0;32mI (214) cpu_start: cpu freq: 160000000[0m
[0;32mI (214) cpu_start: Application information:[0m
[0;32mI (218) cpu_start: Project name:     blink[0m
[0;32mI (223) cpu_start: App version:      1[0m
[0;32mI (227) cpu_start: Compile time:     Jan 12 2024 10:03:51[0m
[0;32mI (233) cpu_start: ELF file SHA256:  e99dcf446f06f884...[0m
[0;32mI (239) cpu_start: ESP-IDF:          v4.3.6-dirty[0m
[0;32mI (245) cpu_start: Min chip rev:     v0.0[0m
[0;32mI (250) cpu_start: Max chip rev:     v3.99 [0m
[0;32mI (254) cpu_start: Chip rev:         v3.1[0m
[0;32mI (259) heap_init: Initializing. RAM available for dynamic allocation:[0m
[0;32mI (266) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM[0m
[0;32mI (272) heap_init: At 3FFB2DE0 len 0002D220 (180 KiB): DRAM[0m
[0;32mI (279) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM[0m
[0;32mI (285) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM[0m
[0;32mI (291) heap_init: At 4008E29C len 00011D64 (71 KiB): IRAM[0m
[0;32mI (299) spi_flash: detected chip: generic[0m
[0;32mI (302) spi_flash: flash io: dio[0m
[0;33mW (306) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.[0m
[0;32mI (321) cpu_start: Starting scheduler on PRO CPU.[0m
[0;32mI (0) cpu_start: Starting scheduler on APP CPU.[0m
Hello Paul!
PDS> updateDisplay (IN)
PDS> updateDisplay (OUT)
PDS> updateDisplay (IN)
PDS> updateDisplay (OUT)
PDS> updateDisplay (IN)
PDS> updateDisplay (OUT)
PDS> updateDisplay (IN)
PDS> updateDisplay (OUT)
PDS> updateDisplay (IN)
PDS> updateDisplay (OUT)
PDS> updateDisplay (IN)
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x400d6831  PS      : 0x00060d30  A0      : 0x800d6530  A1      : 0x3ffb7380  
A2      : 0x3ffb73a0  A3      : 0x8f9da8cf  A4      : 0x00000000  A5      : 0x00000000  
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x4fa27e6b  A9      : 0x3ffb7350  
A10     : 0x00000000  A11     : 0x00000000  A12     : 0xfffffc02  A13     : 0x000003fe  
A14     : 0x7ff00000  A15     : 0x00000000  SAR     : 0x0000000b  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x8f9da8cf  LBEG    : 0x40084db4  LEND    : 0x40084dc1  LCOUNT  : 0x00000000  

Backtrace:0x400d682e:0x3ffb7380 0x400d652d:0x3ffb73a0 0x400d5b26:0x3ffb73d0 0x400d61ee:0x3ffb7400 0x400d62ea:0x3ffb7430 0x4008b045:0x3ffb7450


ELF file SHA256: e99dcf446f06f884

Rebooting...

Is this related to watchdog?

My yieldIfNecessary() method is never actually yielding - it only has a delay every 2 seconds and I'm sure the device is rebooting before it even reaches 2 seconds. That 2000ms is from the ESP Arduino source though.. Is there perhaps something I need to configure to set the watchdog timeout?

Code: Select all

void yieldIfNecessary(void){
    static uint64_t lastYield = 0;
    uint64_t now = millis();
    if((now - lastYield) > 2000) {
        printf("PDS> yielding..\n");
        lastYield = now;
        vTaskDelay(5); //delay 1 RTOS tick
    }
}

SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

Re: app_main() vs loop()

Postby SparkyNZ » Sat Jan 13, 2024 8:45 pm

Actually.. I've changed the code in the updateDisplay() method so it returns immediately after printing some debug - and its no longer crashing. There must be something else in that updateDisplay() code that's causing the crash.

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: app_main() vs loop()

Postby MicroController » Mon Jan 15, 2024 9:06 am

May be helpful: https://docs.espressif.com/projects/esp ... -backtrace
And/or "addr2line", c.f. https://docs.espressif.com/projects/esp ... s-decoding

In your case, the EXCVADDR indicates an attempted access to some 'random' location (0x8f9da8cf).
This pointer corruption may happen for any number of reasons, but one thing to check is that your tasks have enough stack space.

SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

Re: app_main() vs loop()

Postby SparkyNZ » Mon Jan 15, 2024 11:30 am

MicroController wrote:
Mon Jan 15, 2024 9:06 am
May be helpful: https://docs.espressif.com/projects/esp ... -backtrace
And/or "addr2line", c.f. https://docs.espressif.com/projects/esp ... s-decoding

In your case, the EXCVADDR indicates an attempted access to some 'random' location (0x8f9da8cf).
This pointer corruption may happen for any number of reasons, but one thing to check is that your tasks have enough stack space.
It was actually caused by a bad implementation of a random() function - my fault. The code was assuming that rand() returned a positive value but I'd get the odd negative value back.. hence negative array index and boom. Thankfully its all working fine now. Lesson learned on that one.

Who is online

Users browsing this forum: No registered users and 109 guests