Task Crashes on task termination

I am doing some HTTP and HTTPS GET requests and I find that with one site that I am accessing that the code hangs while negotiating the Secure connection regardless of setting the timeouts. I have tried several libraries for doing this without success so I tried to put the code in a task and then check completion or kill the task. The problem is that the code works fine outside of the task but the task crashes when trying to return with an illegal instruction panic. At first this looks like it was occcuring in the String library so I rewrote the code to not use any Strings but it still crashed and implicated the string library so I think the traceback is junk and not really indicating the failure. The entry point I am calling is near the bottom => String HTTPget()

Here is the code with the tasking commented out. If I switch it to use the task it will crash. The code is a little cluttered with debug prints but I commented them all out to get the bare minimum. Any ideas on what I am doing wrong?

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "ArduinoTimer.h"

struct TaskParameters {
  const char* param1;
  const char* param2;
  const char* param3;
  uint16_t    param4;
  int         param5;
  int         param6;
  HTTPClient* param7;                                                                           // Add an additional parameter for the client instance

};
String payload = "";                                                                            // response string from GET

void taskHTTPget(void* parameters) {
  TaskParameters* taskParams = reinterpret_cast<TaskParameters*>(parameters);
  const char* callerName = taskParams->param1;
  const char* request = taskParams->param2;
  const char* server = taskParams->param3;
  uint16_t    port = taskParams->param4;
  int         connectionTimeout = taskParams->param5;
  int         responseTimeout = taskParams->param6;
  HTTPClient* http = taskParams->param7;                                                        // Add an additional parameter for the client instance

  //logDebug("HTTPget debugWDT");
//  HTTPClient http;
  payload = "";                                                                                 // response string from GET
  String Request = String(server) + ":" + String(port) + String(request);

  //Serial.println(Request);                                                                      // debug print
  //Serial.print(" http->begin ");
  unsigned long httpStart = millis();                                                           // time how long the GET takes

  http->setConnectTimeout(connectionTimeout);                                                    // set times out to fix issues with NINA timeout
  http->begin(Request);
  //logDebug("HTTPget debugWDT http->begin");
  http->setTimeout(responseTimeout);                                                             // set timeout on GET
  //logDebug("HTTPget debugWDT http->setTimeout");

  int httpCode = http->GET();                                                                    // start connection and send HTTP header
  //logDebug("HTTPget debugWDT http->GET complete");
  //Serial.printf("GET %s Finished %dms ", Request.c_str(), millis() - httpStart);

  if (httpCode > 0) {                                                                           // httpCode will be negative on error
    //Serial.printf("[HTTP] GET... code: %7d   \n", httpCode);                                    // HTTP header has been send and Server response header has been handled
    if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_ACCEPTED) {                           // file found at server
      //Serial.println("HTTPget called by  " + String(callerName) + " " + Request + " " + "OK");  // GET OK
      //logError("HTTPget called by  " + String(callerName) + " " + Request + " " + "OK");
      payload = http->getString();                                                               // Get the JSON String
      //Serial.println(payload);                                                                  // Print the JSON String
    } else {
      //Serial.printf("HTTPget called by %s %s failed code=%d, error: %s \n", callerName, Request.c_str(), httpCode, http->errorToString(httpCode).c_str());
      //logError("HTTPget called by  " + String(callerName) + " " + Request + " " + http->errorToString(httpCode));
    }
  } else {
    //Serial.printf("HTTPget called by %s [HTTP] GET... failed code=%d, error: %s \n", callerName, httpCode, http->errorToString(httpCode).c_str());
    //logError("HTTPget called by  " + String(callerName) + " " + Request + " " + http->errorToString(httpCode));
  }
  http->end();                                                                                     // close the connection
  //Serial.println("EXIT taskHTTPget");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
String HTTPget(const char* callerName, const char* request, const char* server, uint16_t port, int connectionTimeout, int responseTimeout) {
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ArduinoTimer getTimer;
  TaskParameters taskParams;
  TaskHandle_t taskHandle;
    
  HTTPClient client;                                                                                                          // Create the WiFiClient instance 
  taskParams.param1 = callerName;
  taskParams.param2 = request;
  taskParams.param3 = server;
  taskParams.param4 = port;
  taskParams.param5 = connectionTimeout;
  taskParams.param6 = responseTimeout + 200;
  taskParams.param7 = &client;

  // Rest of the code remains the same

  // Pass the client instance to the task parameters
  taskParams.param7 = &client;
  payload = "";

  Serial.printf("HTTPSget called by %s %s%s port %d  %d\n", callerName, server, request, port, connectionTimeout, responseTimeout);

  getTimer.Reset();

  // esp_task_wdt_init(responseTimeout, true);                                                                              // Initialize the watchdog timer

//    BaseType_t taskCreationResult = xTaskCreate(                                                                            // Create the task and pass the parameters
//      taskHTTPget,          // Task function
//      "taskHTTPget",        // Task name (for debugging)
//      5000,                 // Stack size (in words) 20,000 bytes
//      &taskParams,          // Task parameters
//      1,                    // Task priority
//      &taskHandle           // Task handle (optional)
//    );
//    if (taskCreationResult == pdPASS) Serial.println("Task Creation Succeeded");
//    else Serial.println("Task Creation Failed");
    
  taskHTTPget(&taskParams);
//  while (payload.isEmpty()  && !getTimer.TimePassed_Milliseconds(responseTimeout))  {
//    Serial.print(".");  
//    esp_task_wdt_reset();                                                  // feed the dog
//    waitMilliseconds(100);
//  }
  Serial.printf("exit HTTPget payload == "".isEmpty= %d getTimer.TimePassed_Milliseconds(responseTimeout)=%d getTimer.EllapsedMilliseconds()=%d\n payload=%s\n", payload == "", 
                getTimer.TimePassed_Milliseconds(responseTimeout), getTimer.EllapsedMilliseconds(),payload.c_str());
//    if (uxTaskGetNumberOfTasks() > 0 && eTaskGetState(taskHandle) != eDeleted) vTaskDelete(taskHandle);                 // Check if the task is still running

  return String(payload);
}

That code does not compile.

I agree. It is a function from a larger program that some of the includes are in the main. I tried a few things like placing a return in the first line of taskHTTPget(&taskParams); and the task crashes. If I enable all the debug prints I can see that it crashes on return. If I pass the arguments using globals it still crashes.

That makes it exceedingly difficult to recreate the problem and provide help. Please post a small, complete code that compiles and demonstrates the problem at hand.

Try add vTaskDelete at the end of the task

These should be char arrays :

const char* param1;
const char* param2;
const char* param3;

And use strcpy instead of this :

taskParams.param1 = callerName;
taskParams.param2 = request;
taskParams.param3 = server;

This should be static or global :

TaskParameters taskParams;

Thanks. That is something obvious that I missed in the several iterations. I simplified the code and made a main and setup and it compiles and runs. The task consists only of a return. I can add code before the return and it will run but the exit crashes.

#include <Arduino.h>
#include <M5Stack.h>
#include <HTTPClient.h>

void setup() {
  // put your setup code here, to run once:
  M5.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:
  String GETresult = HTTPget("GetUranus", "/Reporting/Device", "http://192.168.1.15", 32000, 2000, 5000);              // Get the uniqueKey
}
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "ArduinoTimer.h"

String payload = "";                                                                            // response string from GET

void taskHTTPget(void* /*parameters*/) {
 return;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
String HTTPget(const char* callerName, const char* request, const char* server, uint16_t port, int connectionTimeout, int responseTimeout) {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ArduinoTimer getTimer;

  TaskHandle_t taskHandle;

  payload = "";

  Serial.printf("HTTPSget called by %s %s%s port %d  %d\n", callerName, server, request, port, connectionTimeout, responseTimeout);



  BaseType_t taskCreationResult = xTaskCreate(                                                                            // Create the task and pass the parameters
                                    taskHTTPget,          // Task function
                                    "taskHTTPget",        // Task name (for debugging)
                                    5000,                 // Stack size (in words) 20,000 bytes
                                    NULL,//&taskParams,          // Task parameters
                                    1,                    // Task priority
                                    &taskHandle           // Task handle (optional)
                                  );
 
  return String("");
}

The code crashes with the exception:
Decoding stack results 0x400fc938: taskHTTPget(void)* at Z:\Arduino 2022\TestTask/TestTask.ino line 20

You haven't bothered to tell us what kind of board you're using. Is it ESP32? If so, a FreeRTOS task should NEVER return. For a simple example, see:

I apologize. The target is an M5STACK FIRE ESP32.

I removed all the extraneous libraries and the code crashes on the M5STACK FIRE but not on the M5STICK-C.

#include <Arduino.h>
#include <M5Stack.h>


void setup() {
  // put your setup code here, to run once:
  M5.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:
  String GETresult = HTTPget("GetUranus", "/Reporting/Device", "http://192.168.1.15", 32000, 2000, 5000);              // Get the uniqueKey
}


String payload = "";                                                                            // response string from GET

void taskHTTPget(void* /*parameters*/) {
Serial.println("taskHTTPget");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
String HTTPget(const char* callerName, const char* request, const char* server, uint16_t port, int connectionTimeout, int responseTimeout) {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  TaskHandle_t taskHandle = NULL;

  payload = "";

  Serial.printf("HTTPSget called by %s %s%s port %d  %d\n", callerName, server, request, port, connectionTimeout, responseTimeout);



  BaseType_t taskCreationResult = xTaskCreate(                                                                            // Create the task and pass the parameters
                                    taskHTTPget,          // Task function
                                    "taskHTTPget",        // Task name (for debugging)
                                    5000,                 // Stack size (in words) 20,000 bytes
                                    NULL,//&taskParams,          // Task parameters
                                    1,                    // Task priority
                                    &taskHandle           // Task handle (optional)
                                  );
 
  return String("");
}

Did you try ?

AGAIN .... NEVER, EVER, EVER let a FreeRTOS task return!!!

Either kill it per @guix's suggestion or suspend it by pending on a notification, queue, etc.

Also, this silly comment formatting you have around the function name is annoying and distracting. Get rid of it:

I didn't try the recommendation of calling vTaskDelete in the task. What do I use for the task handle?

I passed the Taskhandle as a global and did the delete and this seems to work. Thanks for the help!

Or

void taskHTTPget(void* /*parameters*/) {
  Serial.println("taskHTTPget");
  vTaskDelete( NULL );
}

(read the documentation)

The code as structured is rather a nonsensical way to use a FreeRTOS task.

I am open to suggestions. The problem I am trying to solve is that the HTTPclient and all the other HTTP clients e.g. WiFiClient can get hung despite the setting of a timeout on the connect and the get. This causes a watchdog bite which disconnects the ESP from the socket to the ASCOM driver which requires a manual intervention to reconnect it. I tried the non-blocking GET but I found only one library handles both HTTPS and HTTP and I was having problems with it. I am fine waiting a couple of seconds for the result and that was a simple modification to the tens of thousands of lines of code in this Sketch. The Sketch queries WeatherFlow for my local weather station data, Openweather to get cloud cover data, a local Skyquality meter to get sky brightness, and a telescope mount to get Az/El data so I don't close the observatory roof on the telescope.

Thanks
Kurt

I'd have to see the full code in order to make any useful suggestions.

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