FreeRTOS-based app freezes after making call to Notecard API (can't lock semaphore?)

I’m working on an app where I have an MCU (currently STM32 Nucleo-U083RC) connected via I2C to a Notecard. At present the app is very simple, being a minor modification to the STM “FreeRTOS_Semaphore_LowPower” sample. There’s a main thread which is a loop which waits on a binary semaphore. There’s an ISR which releases the semaphore at which point the main thread takes an action (flashes the user LED on the Nucleo board) and then goes back to sleep. So far, this works as expected. However, if I extend the main thread action so that it makes a Notecard API request (currently card.version) then the application freezes. I have determined that this is something (somehow) to do with the binary semaphore used to control the main loop. At the top of the loop, the code has “osSemaphoreAcquire(BinarySemaphoreHandle, osWaitForever)”, and - if and only if the previous cycle of the main loop has made the Notecard request - this call never returns, and so the code is frozen. Immediately before the call to osSemaphoreAcquire, the code executes “osSemaphoreGetCount (BinarySemaphoreHandle)”. If I run the code with the Notecard request commented out, and inspect the result of this call, it is always zero, which is what I would expect. However, if I run the code including the Notecard API call then this value is always non-zero (in fact always seems to be 339). By the way, the Notecard API request does succeed.

I’m pretty new to FreeRTOS, and indeed to the Blues world. I’d be very grateful if anyone has any insight into how to correct this situation. Doing a search in the forum, I saw that @bsatrom mentioned back in August 2023 that there might be some examples for FreeRTOS on the way, but I haven’t managed to locate anything…

Hey @darkerenergy - thanks for reaching out! I believe that I’ve seen something similar in the past, and in the end it turned out to be an issue with FreeRTOS on M0/M0+ based STM32 MCUs. Specifically, if you have a look at the limitations section, you’ll find this:-

On Cortex-M0 and Cortex-M0+, all IT are disabled between xTaskCreate() and vTaskStartScheduler(). So it is not possible to use IT inbetween, like Serial.print() … This is the reason why, in example “frLiyLayland”, between xTaskCreate() and vTaskStartScheduler(), we use direct printf(), which will access directly USART without interrupt.

The Nucleo-U083RC has an ARM Cortex M0+ core so this checks out. You can confirm if this is the case on your end by trying the same example on another MCU (e.g. you can use the Swan which has a Cortex M4).

We have several examples and applications running FreeRTOS successfully, and I can check on when official support will be provided. In the meantime, if FreeRTOS isn’t essential for your project, I’d recommend checking the other firmware libraries that we support. I personally recommend using our Arduino library for the quickest way to get started. If you specifically need an RTOS, I suggest trying out the Zephyr SDK. We offer many examples for both in our app accelerators and hackster projects.

Thanks
Youssif

Hi @Youssif - many thanks for the prompt response. My apologies, as I’m still struggling with an ecosystem which is all new to me. I’m not sure that I fully understand all the implications of the limitations you mention. Unfortunately, I’m working within the constraints of trying to graft Notecard (specifically LoRa) functionality into a client’s existing project, which is already committed to the STM M0+ MCU (specifically U083) due to extreme low-power operating conditions, and also to FreeRTOS/CMSISv2 (they have existing middleware components such as ModBUS client/server library). Of course, if the bottom line is that there’s no way to achieve the intermixing of M0+ / FreeRTOS / Notecard-over-I2C then I will need to go back to my client and explain the situation. However, before I do that, I’d like to make sure that I am confident that it simply can’t be done, so any guidance from yourself and the community is very welcome.
Cheers,
Chris

Hey @darkerenergy - I agree, let’s first ensure that the issue is indeed related to the M0/M0+ limitation and explore if there’s a possible workaround. To do this, I’ll need to take a closer look at your code. Could you please share a snippet or a minimal reproducible version that demonstrates the problem? Feel free to send it via a direct message or through a repository link.

Thanks
Youssif

Hi @Youssif we’re kind of interested in this too in future and have experienced similar issues.

So thought I would chime in here

Here’s some minimal code for our board.

#include <Arduino.h>
#include <STM32FreeRTOS.h>
#include <Notecard.h>

#define LED1_GRN PC_12
#define LED2_RED PC_13

#define tx_pin2note PB_3 // STM32 TX to Notecard
#define rx_pin2note PB_4 // STM32 RX from Notecard

#define tx_pin2debug PC_10
#define rx_pin2debug PC_11

HardwareSerial stlinkSerial(rx_pin2debug, tx_pin2debug);
HardwareSerial notecardSerial(rx_pin2note, tx_pin2note);
Notecard notecard;

void TaskBlink(void *pvParameters);

void TaskBlink(void *pvParameters) // This is a task.
{
    (void)pvParameters;

    pinMode(LED1_GRN, OUTPUT);

    for (;;) // A Task shall never return or exit.
    {
        stlinkSerial.println("Running blink");
        digitalWrite(LED1_GRN, HIGH);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        digitalWrite(LED1_GRN, LOW);
        vTaskDelay(1000 / portTICK_PERIOD_MS);

        stlinkSerial.println("Getting status");
        J *req = NoteNewRequest("card.status");
        NoteRequest(req);
    }
}

void setup()
{
    stlinkSerial.begin(115200);
    while (!stlinkSerial)
    {
        ;
    }
    stlinkSerial.print("[INFO] Program Starting: Version ");

    notecard.begin(notecardSerial);
    notecard.setDebugOutputStream(stlinkSerial);

    // Now set up two tasks to run independently.
    xTaskCreate(
        TaskBlink, (const portCHAR *)"Blink" // A name just for humans
        ,
        128 // This stack size can be checked & adjusted by reading the Stack Highwater
        ,
        NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
        ,
        NULL);

    vTaskStartScheduler();
    Serial.println("Insufficient RAM");
    while (1)
        ;
}

void loop()
{
}

Compiling with these definitions

  -DconfigUSE_CMSIS_RTOS_V2=1
  -DconfigMEMMANG_HEAP_NB=3

This seems to actually work for a while, but will eventually freeze

[INFO] {"req":"card.status","crc":"0010:5E9E92F3"}
09:19:37:602 -> [INFO] Program Starting: Version Running blink
09:19:40:494 -> Getting status
09:19:40:510 -> [INFO] {"req":"card.status","crc":"0001:5E9E92F3"}
09:19:40:678 -> [INFO] {"connected":true,"status":"{normal}","storage":2,"time":1729860400,"cell":true}
09:19:40:678 -> Running blink
09:19:42:685 -> Getting status
09:19:42:685 -> [INFO] {"req":"card.status","crc":"0002:5E9E92F3"}
09:19:42:828 -> [INFO] {"connected":true,"status":"{normal}","storage":2,"time":1729860400,"cell":true}
09:19:42:849 -> Running blink
09:19:44:855 -> Getting status
09:19:44:855 -> [INFO] {"req":"card.status","crc":"0003:5E9E92F3"}
09:19:45:019 -> [INFO] {"connected":true,"status":"{normal}","storage":2,"time":1729860400,"cell":true}

... TRIM ...

09:21:24:651 -> [INFO] {"req":"card.status","crc":"0031:5E9E92F3"}
09:21:24:794 -> [INFO] {"connected":true,"status":"{normal}","storage":2,"time":1729860400,"cell":true}
09:21:24:815 -> Running blink
09:21:26:820 -> Getting status
09:21:26:820 -> [INFO] {"req":"card.status","crc":"0032:5E9E92F3"}

Frozen past this point no more blinky :(

There’s a really good chance I am doing something fundamentally wrong here as I don’t profess to be an expert in any of this :slight_smile:

Edit:

This seems to be stable now I’ve adjusted the stack size to 192 words, I’m not sure why but after a few iterations the amount of stack needed seems to go up slightly…

Hi Youssif -
Many thanks for agreeing to take a look at this. I’ll send you a minimal reproducible version via DM in a few minutes. It is an extremely trivial modification to the standard STM32 example “FreeRTOS_Semaphore_LowPower” (for the board I’m using, which is a Nucleo-U083RC). Essentially, I just added I2C configuration for the Notecard and added the Blues source/header files, then made a “card.version” request immediately after the “blink the user LED” action in the main thread.
I did take a look at the link you provided. The only M0/M0+ limitation I see is the one you mentioned “all IT are disabled between xTaskCreate() and vTaskStartScheduler()”, and I’m not doing that, so I wouldn’t think (as a noob) that this would be affecting me…
Cheers,
Chris

@darkerenergy does the code you have resemble the code above? We had a similar issue to you but I now can’t reproduce, so I am interested to see what the minimal code you have looks like too!

Hi @alan01252 -
The code in our “minimum reproducible” main thread looks pretty similar to yours,. Here’s our main thread:

void MainThread_Entry(void argument)
{
/
USER CODE BEGIN MainThread /
uint8_t i = 0;
/
Infinite loop */
while(1)
{
currentValue = osSemaphoreGetCount (BinarySemaphoreHandle);
if (osSemaphoreAcquire(BinarySemaphoreHandle, osWaitForever) == osOK)
{
for (i=0; i<10; i++)
{
HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
App_Delay(500);
}

  J* req = NoteNewRequest("card.version");

  if (NoteRequestWithRetry(req, HUB_SET_TIMEOUT)) {
      printf("** Notecard card.version succeeded **\n\r");
  } else {
      printf("** Notecard card.version failed **\n\r");
  }
}

}

/* USER CODE END MainThread */
}

So on a pseudocode level it would I think look very similar indeed to your sample. I’m happy to make our entire repo available to you if you like - there’s nothing remotely proprietary in it.

I’m glad to hear you moved past your own issue and now can’t reproduce it! :slight_smile:

Cheers,

Chris