Receiving serial input from one task and printing on serial in other task

Hi
I have tried the solution for shawn hymel freertos course on youtube the 4th memory allocation problem. where one task should be listening to the serial input and another task should print what has been received from the serial input. I have done the code as after receiving the serial input will create a dynamic memory using malloc and the serial input is stored and the pointer of the malloc call is globally declared so that it can be accessed by another task. When I tried to print from another task there is no data. I have checked after assigning the data to the pointer. below is the code

// Use only core 1 for demo purposes
#if CONFIG_FREERTOS_UNICORE
  static const BaseType_t app_cpu = 0;
#else
  static const BaseType_t app_cpu = 1;
#endif

int flag=0; 
static char *ptr=NULL;
int ind=0; //for index

// Task: Perform some mundane task
void testTask(void *parameter) {
  unsigned char bytes='0';
  while (1) {
    

      if(Serial.available())
      {
            ptr = (char*)pvPortMalloc(20 * sizeof(char));
            // One way to prevent heap overflow is to check the malloc output
            if (ptr == NULL) {
              Serial.println("Not enough heap.");
              vPortFree(NULL);
            } 
            else {
                    bytes=Serial.read();
                    if(bytes=='\n')
                    {
                      
                      Serial.println("Turning on flag");
                      //*(ptr+(ind))='\0';                     
                      /*for(int i=0;i<ind;i++)
                      {
                        Serial.println(*(ptr+i));
                      }*/
                      flag=1;
                      Serial.println("After turning flag");
                      vTaskDelay(3000 / portTICK_PERIOD_MS);         
                     }
                    else{                      
                          //*(ptr+ind)=bytes;
                          ptr[ind]=bytes;                        
                          //Serial.println(ptr[ind]);
                          Serial.println(*(ptr+ind));                    
                          ind++;                   
                        }
                    }
     }

    // Wait for a while
    vTaskDelay(100 / portTICK_PERIOD_MS);
  }
}

void printTask(void *parameter) {

  int i=0;
  while(1){
    if(flag==1)
    {
      Serial.println("In receiving task");
      Serial.println(ind);
      for(int i=0;i<ind;i++)
       {
        Serial.println(*(ptr+i));
       }

      flag=0;
      ind=0;
      vTaskDelay(100 / portTICK_PERIOD_MS);
      vPortFree(ptr);
      ptr=NULL; 
    }
    vTaskDelay(100 / portTICK_PERIOD_MS); 
   
  }
}
void setup() {

  // Configure Serial
  Serial.begin(115200);

  // Wait a moment to start (so we don't miss Serial output)
  vTaskDelay(1000 / portTICK_PERIOD_MS);
  Serial.println();
  Serial.println("---FreeRTOS Memory Demo---");

  // Start the only other task
  xTaskCreatePinnedToCore(testTask,
                          "Test Task",
                          1500,
                          NULL,
                          1,
                          NULL,
                          app_cpu);

  xTaskCreatePinnedToCore(printTask,
                          "print Task",
                          1500,
                          NULL,
                          1,
                          NULL,
                          app_cpu);
  
  // Delete "setup and loop" task
  vTaskDelete(NULL);
}

void loop() {
  // Execution should never get here
}

There are a number of problems in your code.

is wrong / undefined behavior and might even crash depending on the implementation of vPortFree. There is no check to avoid buffer overflows because you don’t verify ind exceeding the malloc’d buffer size…
When transferring a buffer from a task to another task you should use a queue to do that in a synchronized way also avoiding so called race conditions.
Polling global flags (volatile is also missing in your code) to detect events is bad habit.
A queue is designed for this purpose and the receiving task can simply wait for incoming data.
In your case you could use a queue of char* items to transfer the pointer of the malloc’d buffer. I think this is also covered in the example code of FreeRTOS - Open Source Software for Embedded Systems - FreeRTOS xQueueSend() API function descriptions
Alternatively and probably better you could use a large enough queue of char items and fill the received bytes directly into the queue.

To add to Harmut’s reply:

A stream buffer might be a better option than a queue in this case.

Also, try to make your tasks event driven - i.e. replace the arbitrary calls to vTaskDelay() with calls that block on reads from a queue or stream buffer (so the task doesn’t run unless data is available).

Thank you for the response, yeah im aware of the buffer overflow as I’m giving the input which will be much less that 20 characters and yeah i have changed the flag to volatile. what im trying to do here is just poc that, the tasks are created in the heap. And when a one task calls malloc the memory is created in heap too. The pointer to that memory is global so when another task tries to access memory it should be right. And after accessing the memory the memory is freed. The flag make sure that the second task doesn’t access the memory until its created and stored from serial input. From the replies what I can infer is that flag isn’t the right way to access the memory one should use queues or stream buffer. my main question was even though i’m storing the serial data to the memory created by malloc call. It doesnt appear when i try to access it. In the shwan hymel solution he just stored the data into an array and memcpy that data to the memory created by malloc. This is the only difference between my code and his. In mine the data is directly stored to memory and by doing that the data doesn’t appear in the second task but it appears in the first by printing in the first task. I couldn’t understand why is this happening, I don’t see if that makes a difference of using memcpy. Correct me if I’m wrong in any of the inferences.
Thank you

I’m sorry for the misconception, I have found why this isn’t working. It’s because I’m creating a new buffer i.e calling malloc, for every time I receive a serial input. Thank you for your answers.

Great that you found the problem ! However, the approach with busy polling a flag variable is flawed. Think of having more tasks in your application. The other tasks might not get processing time because the polling tasks runs all the time even though there is nothing to do. Sure, you can start setting up task priorities accordingly, but it’s by far not so good as just letting the task wait for something to do and giving up the CPU during that time. Hence usually for use cases like yours a queue or streambuffer is used to transfer and signal some data from a task to another.
Just a hint :slight_smile:

To add to @hs2’s response above. The usual argument for not changing the implementaiton from polling a flag variable is

It’s only ever going to do this, so it’s not worth the effort

and in my experience, only ever going to do this is true incredibly infrequently.

I do however take on board that you posted POC code, and maybe your actual implementation will not use such a mechanism.