System Reset after touch pin interrupt

I’m using a TTGO T3 board with LoRa, OLED and SD card interfaces.

I’m trying to switch OLED display ON or OFF with a touch pin. A pin touch should toggle it ON or OFF and, after elapsed time, it should be turned off.

The touch pin triggers an interrupt that simply notifies a task. The task is supposed to handle the pin touch, which so far takes care of the OLED only.

But, for some reason, the device resets and I wonder why. I think about two possibilities:

  1. touch sensing keeps bouncing during too long.
  2. SPI cross interference, since touch and VSPI (SD card) use the same port.

The code is:

...
void Toque_BOTAO1 ()                    //  touch interrupt procedure
{
  digitalWrite(LED, true);              //  visual indication
  xTaskNotifyGive(vT_OLED_ON_OFF_hdl);  //  triggers ON / OFF switching
}
...
void vT_OLED_ON_OFF(void *pvParameters)
{
  TickType_t xDelayOLED = 60000 / portTICK_PERIOD_MS;   //  max OLED ON interval
  uint32_t reason;                                      //  reason for running task (time / touch)
  bool OLED_ON_OFF = true;                              //  OLED display switch

  while (true)                                          //  infinite loop
  {
    reason = ulTaskNotifyTake(pdTRUE, xDelayOLED);      //  waits for touch button or time lapse
    Serial.printf("\nTouch Btn 1: %x\n", reason);       //  serial prints reason

    if (reason) {                                       //  if touch button interrupt
      OLED_ON_OFF = !OLED_ON_OFF;                       //    > toggles OLED display
    }
    else
    {                                                   //  if elapsed time
      OLED_ON_OFF = false;                              //    > switches OLED display OFF
    }

    if (OLED_ON_OFF) {                                  //  switches display according to flag
      OLED.ssd1306_command(SSD1306_DISPLAYON);          //    > ON
    } else {
      OLED.ssd1306_command(SSD1306_DISPLAYOFF);         //    > OFF
    }

    vTaskDelay(200 / portTICK_PERIOD_MS);               //  debounce delay
    ulTaskNotifyTake(pdTRUE, 0);                        //  releases bounce
  }
}
...
void setup ()
{
...
xTaskCreatePinnedToCore(vT_OLED_ON_OFF, "OLED ON/OFF", 1000, NULL, 1, &vT_OLED_ON_OFF_hdl, 1);

touchAttachInterrupt(BOTAO1, &Toque_BOTAO1, 20);
...
}

After the first time the button is touched, the code runs its interrupt and task, then keeps running a few seconds and ends like:

ets Jun 8 2016 00:22:57

rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 188777542, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:10124
load:0x40080400,len:5828
entry 0x400806a8

What might be going on?

Thank you.
Regards

You can’t implement an ISR function with it being in IROM, must be IRAM… Where’s the IRAM_ATTR in your code?

No, in an ISR you need xTaskNotifyGiveFromISR, not the regular version of the function (from a thread).

Hm on second thought maybe the blocking is too long and that triggers the watchdog? Try checking the flag in smaller intervals. TG1WDT_SYS_RESET is the Timergroup1 Watchdog system reset after all.

Something hangs up your task, either your NotifyTake or something else.

Hello Max, good evening.

I’ve been trying your tips.

I’m using IRAM_ATTR for very often called interruptions only. I didn’t see any reason for loading such interruption in RAM since this one is supposed to be called only when someone wants to go to the circuit and watch the OLED display instead of getting info through network.

Indeed I’ve tried a simpler version of this interruption procedure. And I could check that IRAM_ATTR doesn’t make any difference. This runs fine, with or without IRAM_ATTR.

If it is really mandatory, I’ll consider using it.

IRAM_ATTR void Toque_BOTAO1()
{
  digitalWrite(LED, !digitalRead(LED));
}

Regarding the use of xTaskNotifyGive inside this interruption, I’ve tried it with vTaskNotifyGiveFromISR. Nevertheless, nothing has changed. The device is likewise reboot after a while.

IRAM_ATTR void Toque_BOTAO1()
{
  BaseType_t pxpHigherPriorityTaskWoken =  pdFALSE;
  vTaskNotifyGiveFromISR(vT_OLED_ON_OFF_hdl, &pxpHigherPriorityTaskWoken); 
}

In fact, this code has got an interruption procedure driven by a timer that makes two calls with xTaskNotifyGive and it’s been working since the beginning.

Reducing xDelayOLED to 1000, which would be useless in this application, no effect as well.

void vT_OLED_ON_OFF(void *pvParameters)

{
  TickType_t xDelayOLED = 1000 / portTICK_PERIOD_MS; 
  uint32_t reason;
  bool OLED_ON_OFF = true;

  while (true)
  {
    reason = ulTaskNotifyTake(pdTRUE, xDelayOLED);
    Serial.printf("\nTouch Btn 1: %x\n", reason);

    digitalWrite(LED, !digitalRead(LED));

    if (reason) {
      OLED_ON_OFF = !OLED_ON_OFF;
    }
    else    { 
      OLED_ON_OFF = false;
    }

    if (OLED_ON_OFF) {                                  //  switches display according to flag
      OLED.ssd1306_command(SSD1306_DISPLAYON);          //    > ON
    } else {
      OLED.ssd1306_command(SSD1306_DISPLAYOFF);         //    > OFF
    }

    vTaskDelay(200 / portTICK_PERIOD_MS);               //  debounce delay
    ulTaskNotifyTake(pdTRUE, 0);                        //  releases bounce
  }
}

At the end, I’ve tried to load it to RAM and comment out the bounce release, that must still be improved anyway.

IRAM_ATTR void vT_OLED_ON_OFF(void *pvParameters)

{
  TickType_t xDelayOLED = 1000 / portTICK_PERIOD_MS; 
  uint32_t reason;
  bool OLED_ON_OFF = true;

  while (true)
  {
    reason = ulTaskNotifyTake(pdTRUE, xDelayOLED);
    Serial.printf("\nTouch Btn 1: %x\n", reason);

    digitalWrite(LED, !digitalRead(LED));

    if (reason) {
      OLED_ON_OFF = !OLED_ON_OFF;
    }
    else    { 
      OLED_ON_OFF = false;
    }

    if (OLED_ON_OFF) {                                  //  switches display according to flag
      OLED.ssd1306_command(SSD1306_DISPLAYON);          //    > ON
    } else {
      OLED.ssd1306_command(SSD1306_DISPLAYOFF);         //    > OFF
    }

    vTaskDelay(200 / portTICK_PERIOD_MS);               //  debounce delay
    // ulTaskNotifyTake(pdTRUE, 0);                        //  releases bounce
  }
}

So far, it doesn’t work!

The issue must be somewere else and I can’t figure out!

Case you think about something else, please tell me.

Thank you.
Regards

I’m not familiar with the microcontroller here but this looks like the watch dog is timing out to me. I might be wrong though. The acronym reads to me as watchdog timer system reset.

Do you have long delays or short ones that run frequently in any of your code? Is there more code than you have posted? Do you know what the watchdog timeout period is on this device? Have you changed it etc etc.

Just thinking out loud, it might help.

Cheers,
Norm.

I tried reproducing your problem with a simple “wait for button press (on the BOOT button that every dev board should have) then light LED” with the code

#include <Arduino.h>

void waitForButtonTask(void *pvParameters);

#define Btn1_GPIO 0
#define LED1_GPIO 5

TaskHandle_t handlWaitForButtonTask;

void IRAM_ATTR Ext_INT1_ISR()
{
  BaseType_t xHigherPriorityTaskWoken;  
  vTaskNotifyGiveFromISR(handlWaitForButtonTask, &xHigherPriorityTaskWoken);
  if(xHigherPriorityTaskWoken == pdTRUE)
    portYIELD_FROM_ISR();
}

void setup()
{
  Serial.begin(115200);
  Serial.println("Start of setup");
  pinMode(LED1_GPIO, OUTPUT);
  pinMode(Btn1_GPIO, INPUT_PULLUP);
  attachInterrupt(Btn1_GPIO, Ext_INT1_ISR, RISING);
  xTaskCreatePinnedToCore(waitForButtonTask, "Button Waiter", 1000, NULL, 1, &handlWaitForButtonTask, 1);
  Serial.println("End of setup");
}

void loop()
{
  delay(2000);
  Serial.println("Looping..");
  Serial.flush();    
}

void waitForButtonTask(void *pvParameters)
{
  while (true)                                          
  {
    Serial.println("Now waiting for button press..");
    Serial.flush();    
    uint32_t reason = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    Serial.printf("\nTouch Btn: %x\n", reason);       
  }
}

And I immediately ran into

***ERROR*** A stack overflow in task Button Waiter has been detected.
abort() was called at PC 0x400851d4 on core 1

when I pushed the button. So only 1000 bytes of stack wasn’t enough for a simple waiting and printing.

As soon as I modified the task creation to use 4096 bytes

  xTaskCreatePinnedToCore(waitForButtonTask, "Button Waiter", 4096, NULL, 1, &handlWaitForButtonTask, 1);

My example worked nicely.

Now waiting for button press..
Looping..
Looping..
Looping..
Looping..

Touch Btn: 1
Now waiting for button press..
Looping..

Touch Btn: 1
Now waiting for button press..

And there were no watchdog timer resets at all.

And that is with the standard platformio.ini of

[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

Maybe that helps you.

Thank you Norman.

Yes. There’s a task that forces RTC to refresh its synchronism against NTP server every 15 minutes. This refresh also accepts external unblocking via xTaskNotifyGive in case some other point in the code identifies RTC failure meanwhile.

void vT_ajusta_RTC (void *pvParameters)

{ // Task Handle vT_ajusta_RTC_hdl
tm hora;
const TickType_t intervalo = 15 * 60000 / portTICK_PERIOD_MS; // 15 minutos
const TickType_t xDelayVerifica = 4 * 1000 / portTICK_PERIOD_MS; // 4 segundos
const TickType_t xDelayRepete = 1 * 1000 / portTICK_PERIOD_MS; // 1 segundos
while (true) {
ulTaskNotifyTake(pdTRUE, intervalo);
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2, ntpServer3);
vTaskDelay(xDelayVerifica);
RTC_ok = getLocalTime(&hora);
if (RTC_ok) {
Serial.printf(“\n\n[Core %d]: RTC disponível.\n”, xPortGetCoreID());
printLocalTime();
ulTaskNotifyTake(pdTRUE, 0);
} else {
Serial.printf(“\n\n[Core %d]: RTC NÃO DISPONÍVEL !!!\n”, xPortGetCoreID());
vTaskDelay(xDelayRepete);
xTaskNotifyGive(vT_ajusta_RTC_hdl);
}
}
}

But this works fine. It has never panic’ed the processor.

Yes. The code is now 1600 lines long and runs about 6 tasks in each core.

No. I haven’t changed that. Just using standard.

Your thoughts are wellcome.
Thank you.

I’m gonna try it right now!

Hello Max

Perfekt:

That’s the solution.

Indeed it has helped a lot. I’ve been looking for a solution during three days.

And I use to try to learn the most from your answers, such as

which is related to:

If vTaskNotifyGiveFromISR() sets this value to pdTRUE then a context switch should be requested before the interrupt is exited. How a context switch is requested from an ISR is dependent on the port - see the documentation page for the port in use.

Eventually I’ll get back to this topic for understanding this mechanism better.

Thank you very much, Max.
Regards, Ciro.

1 Like