xQueueSend multiple messages on Arduino Mega 2560

cestmoi wrote on Tuesday, October 23, 2018:

Hi,

I am working on a project using freeRTOS V10.0.0. on a Arduino Mega 2560
There is some issues with the xQueueReceive function…

This code is inspired by the code example of “xQueueSend”. REF: https://www.freertos.org/a00117.html
This is the code:

#include <Arduino_FreeRTOS.h>
#include <queue.h>

struct AMessage {
  char ucMessageID;
  char ucData[20];
} xMessage;
unsigned long ulVar = 10UL;

void vATask(void);

void setup() {
  Serial.begin(115200);
  vATask();
}

void loop() {
  // Nothing
}

void vATask(void) { //void *pvParameters
  QueueHandle_t xQueue1, xQueue2;
  struct AMessage *pxMessage;
  /* Create a queue capable of containing 10 unsigned long values. */
  xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
  /* Create a queue capable of containing 10 pointers to AMessage structures.
  These should be passed by pointer as they contain a lot of data. */
  xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
  /* ... */
  xMessage.ucMessageID = 1;
  strcpy(xMessage.ucData, "Message1");
    
  if(xQueue1 != 0) {
    /* Send an unsigned long.  Wait for 10 ticks for space to become
    available if necessary. */
    if(xQueueSend(xQueue1, (void*)&ulVar, (TickType_t)10) != pdPASS ) {
    /* Failed to post the message, even after 10 ticks. */
    }
  }
  if(xQueue2 != 0) {
    /* Send a pointer to a struct AMessage object.  Don't block if the
    queue is already full. */
    pxMessage = &xMessage;
    xQueueSend(xQueue2, (void*)&pxMessage, (TickType_t)0);

    Serial.println("SEND 1");
    Serial.print("pxMessage->ucMessageID: ");
    Serial.println(pxMessage->ucMessageID, DEC);
    Serial.print("pxMessage->ucData: ");
    Serial.println(pxMessage->ucData);
    Serial.println();

    xMessage.ucMessageID = 2;
    strcpy(xMessage.ucData, "Message2");
    pxMessage = &xMessage;
    xQueueSend(xQueue2, (void*)&pxMessage, (TickType_t)0);
    Serial.println("SEND 2");
    Serial.print("pxMessage->ucMessageID: ");
    Serial.println(pxMessage->ucMessageID, DEC);
    Serial.print("pxMessage->ucData: ");
    Serial.println(pxMessage->ucData);
    Serial.println();

    xQueueReceive(xQueue2, (void*)&pxMessage, (TickType_t)0);
    Serial.println("RECEIVE 1");
    Serial.print("pxMessage->ucMessageID: ");
    Serial.println(pxMessage->ucMessageID, DEC);
    Serial.print("pxMessage->ucData: ");
    Serial.println(pxMessage->ucData);
    Serial.println();

    xQueueReceive(xQueue2, (void*)&pxMessage, (TickType_t)0);
    Serial.println("RECEIVE 2");
    Serial.print("pxMessage->ucMessageID: ");
    Serial.println(pxMessage->ucMessageID, DEC);
    Serial.print("pxMessage->ucData: ");
    Serial.println(pxMessage->ucData);
    Serial.println();
  }
}

And this is the result on the arduino serial monitor:

SEND 1
pxMessage->ucMessageID: 1
pxMessage->ucData: Message1

SEND 2
pxMessage->ucMessageID: 2
pxMessage->ucData: Message2

RECEIVE 1
pxMessage->ucMessageID: 2
pxMessage->ucData: Message2

RECEIVE 2
pxMessage->ucMessageID: 2
pxMessage->ucData: Message2

This is just a simple test code that send two messages with a ID and a char array. i want to receive this two message but I read two times the last messag sended (ID=2, message=Message2).

I want this output:

SEND 1
pxMessage->ucMessageID: 1
pxMessage->ucData: Message1

SEND 2
pxMessage->ucMessageID: 2
pxMessage->ucData: Message2

RECEIVE 1
pxMessage->ucMessageID: 1
pxMessage->ucData: Message1

RECEIVE 2
pxMessage->ucMessageID: 2
pxMessage->ucData: Message2

Thank you for your help.

rtel wrote on Tuesday, October 23, 2018:

It looks like the example code on that page is wrong, and you have
copied the error into your code. The line:

xQueueSend(xQueue2, (void*)&pxMessage, (TickType_t)0);

is taking the address of a pointer to an xMessage structure, whereas it
should take the address of the xMessage structure directly. Can you try
changing the line to:

xQueueSend(xQueue2, (void*)pxMessage, (TickType_t)0);

(remove the ‘&’). In the mean time I will review the example code,
which I see also calls xMessage AMessage, and fix as appropriate.

Let me know if this solves your problem.

rtel wrote on Tuesday, October 23, 2018:

On second look the queue was created to hold pointers. Hang on a second
or two I will try again…

rtel wrote on Tuesday, October 23, 2018:

You are seeing the expected behavour because you are queuing a pointer
to the structure.

In the first send you set the structure values to 1 and Message1, and
queue a pointer to the structure.

In the second send you set the structure values to 2 and Message2, and
queue a pointer to the structure.

In the first receive you receive a pointer to the structure. When you
dereference the pointer you read the values out of the structure. The
actually in the structure being pointed to are still 2 and Message2,
which is what is printed out.

cestmoi wrote on Monday, November 05, 2018:

Thank you Richard Barry.
I have try your answers and many others solution but nothing seems to work…

I make another example code with 10 different messages.
First, here is the code that uses a structure pointer that contains a variable and a char array:

/*Begining of Auto generated code by Atmel studio */
#include <Arduino.h>

/*End of auto generated code by Atmel studio */

#include <Arduino_FreeRTOS.h>
#include <queue.h>
//Beginning of Auto generated function prototypes by Atmel Studio
//End of Auto generated function prototypes by Atmel Studio

// CHAR ARRAY POINTER --------------------------
struct tabData {
	int messsageID = 0;
	//char* pData;
	char pData[10] = {0};
};

struct tabData *pDataSend;
struct tabData *pDataReceive;

QueueHandle_t queue;
 
void setup() {
	
	pDataSend = (struct tabData*)malloc(sizeof(struct tabData));
	//pDataSend->pData = (char*)malloc(sizeof(char)*10);
	
	pDataReceive = (struct tabData*)malloc(sizeof(struct tabData));
	//pDataReceive->pData = (char*)malloc(sizeof(char)*10);

  Serial.begin(115200);
 
  queue = xQueueCreate( 10, sizeof(tabData) );
 
  if(queue == NULL){
    Serial.println("Error creating the queue");
  }
}
 
void loop() {
 
  if(queue == NULL)return;
 
  for(int i = 0; i<10; i++){
	pDataSend->messsageID = i;
	switch(i) {
		case 0:
			strcpy(pDataSend->pData, "abcd");
			break;
		case 1:
			strcpy(pDataSend->pData, "efgh");
			break;
		case 2:
			strcpy(pDataSend->pData, "ijkl");
			break;
		case 3:
			strcpy(pDataSend->pData, "mnop");
			break;		
		case 4:
			strcpy(pDataSend->pData, "qrst");
			break;		
		case 5:
			strcpy(pDataSend->pData, "uvwx");
			break;		
		case 6:
			strcpy(pDataSend->pData, "yz12");
			break;		
		case 7:
			strcpy(pDataSend->pData, "3456");
			break;		
		case 8:
			strcpy(pDataSend->pData, "7890");
			break;
		case 9:
			strcpy(pDataSend->pData, "Test");
			break;	
		default:
			break;	
	}
	xQueueSend(queue, pDataSend, portMAX_DELAY);
  }
 
  for(int j = 0; j<10; j++){
    xQueueReceive(queue, pDataReceive, portMAX_DELAY);
    Serial.print(pDataReceive->messsageID);
    Serial.print(" | ");
	Serial.print(pDataReceive->pData);
	Serial.println();
  }
  Serial.println();
  delay(1000);
}

this version works perfectly and I have the message ID and the message on the terminal in the good order:

0 | abcd
1 | efgh
2 | ijkl
3 | mnop
4 | qrst
5 | uvwx
6 | yz12
7 | 3456
8 | 7890
9 | Test

After that, I tried to make the same program, but using a char pointer instead of the array.

/*Begining of Auto generated code by Atmel studio */
#include <Arduino.h>

/*End of auto generated code by Atmel studio */

#include <Arduino_FreeRTOS.h>
#include <queue.h>
//Beginning of Auto generated function prototypes by Atmel Studio
//End of Auto generated function prototypes by Atmel Studio

// CHAR ARRAY POINTER --------------------------
struct tabData {
	int messsageID = 0;
	char* pData;
	//char pData[10] = {0};
};

struct tabData *pDataSend;
struct tabData *pDataReceive;

QueueHandle_t queue;
 
void setup() {
	
	pDataSend = (struct tabData*)malloc(sizeof(struct tabData));
	pDataSend->pData = (char*)malloc(sizeof(char)*10);
	
	pDataReceive = (struct tabData*)malloc(sizeof(struct tabData));
	pDataReceive->pData = (char*)malloc(sizeof(char)*10);

  Serial.begin(115200);
 
  queue = xQueueCreate( 10, sizeof(tabData) );
 
  if(queue == NULL){
    Serial.println("Error creating the queue");
  }
}
 
void loop() {
 
  if(queue == NULL)return;
 
  for(int i = 0; i<10; i++){
	pDataSend->messsageID = i;
	switch(i) {
		case 0:
			strcpy(pDataSend->pData, "abcd");
			break;
		case 1:
			strcpy(pDataSend->pData, "efgh");
			break;
		case 2:
			strcpy(pDataSend->pData, "ijkl");
			break;
		case 3:
			strcpy(pDataSend->pData, "mnop");
			break;		
		case 4:
			strcpy(pDataSend->pData, "qrst");
			break;		
		case 5:
			strcpy(pDataSend->pData, "uvwx");
			break;		
		case 6:
			strcpy(pDataSend->pData, "yz12");
			break;		
		case 7:
			strcpy(pDataSend->pData, "3456");
			break;		
		case 8:
			strcpy(pDataSend->pData, "7890");
			break;
		case 9:
			strcpy(pDataSend->pData, "Test");
			break;	
		default:
			break;	
	}
	xQueueSend(queue, pDataSend, portMAX_DELAY);
  }
 
  for(int j = 0; j<10; j++){
    xQueueReceive(queue, pDataReceive, portMAX_DELAY);
    Serial.print(pDataReceive->messsageID);
    Serial.print(" | ");
	Serial.print(pDataReceive->pData);
	Serial.println();
  }
  Serial.println();
  delay(1000);
}

And this time, the teminal show this…

0 | Test
1 | Test
2 | Test
3 | Test
4 | Test
5 | Test
6 | Test
7 | Test
8 | Test
9 | Test

I don’t understand why there is the good ID but the message doesn’t change.

Sorry I do not have much experience with pointers of structures with pointers.

Thank you for your help.

richarddamon wrote on Monday, November 05, 2018:

If you think what the program is doing it is creating a single buffer with malloc to hold that data, and overwriting it with each loop of the program. The queue send copies the data in the base structure itself, but that just has a pointer to the pData buffer, so that data is reused for each structure.

You need to create a seperate buffer for each chunck of data. With the array, since that is part of the structure that is copied, you get it. With the pointer to malloced data, it isn’t, so you need to malloc a new buffer for each thing you send (and then free it after using it at the receiver).

cestmoi wrote on Thursday, November 08, 2018:

I understood my mistakes thanks to you.

There is a problem with dynamic allocation memory now… When I malloc memory out of the for loop there is the problem you just explained.
But now I make malloc memory in the for loop and free it in another loop (both loop have the same iteration number). The for loop become endless. The variable “i” is incremented at every iteration but never break.

Maybe there is a memory problem with the heap size. I actually make some rechearch for this.

I put the code modified code below.

/*Begining of Auto generated code by Atmel studio */
#include <Arduino.h>

/*End of auto generated code by Atmel studio */

#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <avr/pgmspace.h>
#include <stdlib.h>
//Beginning of Auto generated function prototypes by Atmel Studio
//End of Auto generated function prototypes by Atmel Studio

#define NUMBER_MSG	2
#define SIZE_MSG	10

// CHAR ARRAY POINTER --------------------------
struct tabData {
	int messsageID = 0;
	char* pData;
	//char pData[SIZE_MSG] = {0};
};

struct tabData *pDataSend;
struct tabData *pDataReceive;

QueueHandle_t queue;

char sendMsg[SIZE_MSG];
int sendID;
char receiveMsg[SIZE_MSG];
int receiveID;

void setup() {
	pDataSend = (struct tabData*)malloc(sizeof(struct tabData));
	pDataReceive = (struct tabData*)malloc(sizeof(struct tabData));
	//pDataSend->pData = (char*)malloc(sizeof(char)*SIZE_MSG);
	
	Serial.begin(115200);
	
	queue = xQueueCreate(NUMBER_MSG, sizeof(tabData) );
	
	if(queue == NULL){
		//Serial.println("Error creating the queue");
	}
}

void loop() {
	
	if(queue == NULL)return;
	
	for(int i = 0; i<NUMBER_MSG; i++){
		Serial.print("i = ");
		Serial.println(i);
		pDataSend->pData = (char*)malloc(sizeof(char)*SIZE_MSG);
		//free(pDataSend->pData);
		pDataSend->messsageID = i;
		switch(i) {
			case 0:
			strcpy(pDataSend->pData, "abcd");
			break;
			case 1:
			strcpy(pDataSend->pData, "efgh");
			break;
			case 2:
			strcpy(pDataSend->pData, "ijkl");
			break;
			/*case 3:
			strcpy(pDataSend->pData, "mnop");
			break;
			case 4:
			strcpy(pDataSend->pData, "qrst");
			break;
			case 5:
			strcpy(pDataSend->pData, "uvwx");
			break;
			case 6:
			strcpy(pDataSend->pData, "yz12");
			break;
			case 7:
			strcpy(pDataSend->pData, "3456");
			break;
			case 8:
			strcpy(pDataSend->pData, "7890");
			break;
			case 9:
			strcpy(pDataSend->pData, "Test");
			break;*/
			default:
			break;
		}
		sendID = pDataSend->messsageID;
		strcpy(sendMsg, pDataSend->pData);
		xQueueSend(queue, pDataSend, portMAX_DELAY);
	}
	
	for(int j = 0; j<NUMBER_MSG; j++){
		xQueueReceive(queue, pDataReceive, portMAX_DELAY);
		receiveID = pDataReceive->messsageID;
		strcpy(receiveMsg, pDataReceive->pData);

		Serial.print(pDataReceive->messsageID);
		Serial.print(" | ");
		Serial.print(pDataReceive->pData);	
		Serial.println();
		free(pDataReceive->pData);
	}
	Serial.println();
	delay(1000);
}

Thank you again

richarddamon wrote on Thursday, November 08, 2018:

First, every call to malloc should be checking for a NULL return, which indicates that you have run out of heap (what to do in this case is on reason it is generally discouraged from using malloc in embedded systems). If you do process with a NULL buffer you likely overwrite some memory, which can cause all sorts of issues.

cestmoi wrote on Thursday, November 08, 2018:

Thanks for the tip. By controlling each malloc, when the malloc is realized in the “settings” loop the memory is allocated correctly and returns a correct pointer. When memory is allocated in the main infinite loop, malloc returns a NULL pointer.

I do not understand how it is possible to allocate memory in the settings loop but not in the infinite loop.