ESP32 - FreeRTOS - and running code on both cores

Hi all!

I have an ESP32 connected to an adafruit fona 3g cell module, a touchscreen LCD, and a few sensors. I communicate with the fona via SMS messages. Since SMS messages can take a second or so to send and receive, rather than writing non blocking code for the SMS part of my project, I decided to execute it on core0 of the ESP32. Everything else is run on core1 - GUI, LCD update, and sensor monitoring. I appear to have a problem when I want the code on core1 to call a function that is part of the core0 code. And I'm not understanding it...

Basically, when the code on core1 encounters an error, it should send a SMS message and all SMS activity is handled on core0. The problem is, when core1 has an error and calls a function that is related to core0, that function on core0 won't execute correctly. It appears that I can't call a function meant to run on core0 from core1 and vice-versa. Why is that?

I have coded around the problem by using global flags, and each core has access to those flags, but those cores can't access each other's functions?

I would appreciate any help in understanding how to work with both cores working together!

Randy

It stands to reason that we'd need to see your code.

You need to learn about the core and system architecture. There are a few key concepts you need to understand. You can start with Wikipedia if you like more details and you will need the datasheet. I will just point out a few things could be an issue for your case.

Address space: The processor cores might not have access to the same addresses or only for a limited space.

Virtual addressing: Usually only found in high end processors e.g. your PC. This allows fully address independent code. The physical address space is hidden from the code. Embedded processor usually do not have this feature because it has many drawbacks for instance execution time become unpredictable. So, when the code is compiled and linked for one core it cannot run on the other because both cores have the code at different addresses.

wrong forum?

Klaus_K:
You need to learn about the core and system architecture. There are a few key concepts you need to understand.

You are correct, I need to learn more, that's why I'm posting here. A helpful and very active community we have here, and some have knowledge of what I am looking to learn.

Juraj:
wrong forum?

Please direct me to the correct forum I should try.

gfvalvo:
It stands to reason that we'd need to see your code.

Of course you need to see the code, but it's quite long (1500+) lines and uses custom libraries, which is why I didn't post it.
Here is a condensed pseudo code version which should explain the problem:

void setup()
{
  initialize all sensors
  initialize fona
  define fona task
}

void fona task()
{
  // code runs on core0
  while (true)
  {
     if (SMS received)
     {
       SMShandler()
      }
   }
}

void loop()
{
  // running on core1
  read data from sensors
  if (sensor failed)
  {
    sendSMSerror()
  }
}

void SMShandler()
{
  get & parse SMS

  if (SMS command = stat)
  {
    sendSMSstatus()
  }
  else if (SMS command = set)
  {
    changeSettings()
  }
}

void sendSMSstatus()
{
  create statusMsg
  sendSMS(statusMsg)
}

void changeSettings()
{
  create responseMsg
  sendSMS(responseMsg)
}

void sendSMSerror()
{
  create errorMSG
  sendSMS(errorMSG)
}

void sendSMS(MSG)
{
  actually send the MSG via fona device
}

So the fona task runs on core0. It looks for a new SMS msg and processes it, then sends a reply via SMS
The code in loop(), running on core1, when it detects a problem with a sensor, wants to send a SMS via the sensSMSerror() function. Calling the sendSMSerror() function from core1 fails. Why is that?

If I modify the above code to this:

boolean sensorError = false

void setup()
{
  initialize all sensors
  initialize fona
  define fona task
}

void fona task()
{
  // code runs on core0
  while (true)
  {
     if (SMS received)
     {
       SMShandler()
      }
     if (sensorError = true)
     {
       sendSMSerror()
     }

   }
}

void loop()
{
  // running on core1
  read data from sensors
  if (sensor failed)
  {
    sensorError = true
  }
}

void SMShandler()
{
  get & parse SMS

  if (SMS command = stat)
  {
    sendSMSstatus()
  }
  else if (SMS command = set)
  {
    changeSettings()
  }
}

void sendSMSstatus()
{
  create statusMsg
  sendSMS(statusMsg)
}

void changeSettings()
{
  create responseMsg
  sendSMS(responseMsg)
}

void sendSMSerror()
{
  create errorMSG
  sendSMS(errorMSG)
}

void sendSMS(MSG)
{
  actually send the MSG via fona device
}

Yet, when I make those changes, looking for a flag in the fona task, running on core0, then calling the sendSMSerror() function, the code works and the SMS is sent.

What I am failing to understand is why when sendSMSerror() is called from core1, it doesn't work. When sendSMSerror() is called from core0, it works. I have two loops, one running on each core, but the rest of the code, like sendSMSerror(), isn't assigned to any core, so isn't it available to both cores?

Clearly, there is something about the dual core architecture that I don't understand or know...

Thanks for any help,
Randy

Most Arduino libraries ─ most likely including the Fona library you're using ─ are not designed for multi-threaded environments. Calls to member functions will not be thread-safe, and the library might use hardware resources like UARTs that can't just be shared between multiple threads/cores without precautions.

This means that you'll either have to perform all communication with your SMS module in one task or protect everything with mutexes.

“Calling the sendSMSerror() function from core1 fails” is not a useful description of the error. Please post the exact error messages and the decoded stack traces.

When sharing flags or any other data between tasks/threads, you have to prevent data races by making them atomic. A standard “bool” flag cannot be accessed by two different tasks, you have to use std::atomic, for example.

You can read the rules of the language here Memory model - cppreference.com, but to really understand it, you'll need a book or a “tutorial” that explains race conditions, mutexes, deadlock, atomicity, synchronization. I don't have any specific recommendations, though.

Pieter

PieterP:
Most Arduino libraries ─ most likely including the Fona library you're using ─ are not designed for multi-threaded environments. Calls to member functions will not be thread-safe, and the library might use hardware resources like UARTs that can't just be shared between multiple threads/cores without precautions.

Thank you @PieterP for your help.
I was working on the code and testing it and was coming to the conclusion that ports aren't shared with both cores.
I talk to the fona via a UART on core0, talk to the lcd via spi on core1. core0 can't access the lcd spi and core1 can't access the UART used by core0.
Thank you for your information and I will be spending some time reading up on the subjects you posted!
Randy

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