1

so I am having this strange problem (I guess there is something wrong with my code), I am trying to create 2 tasks on esp32 using freeRTOS, 1st task is to receive data from the Bluetooth module and put what it received in a queue, the 2nd task then reads from that queue and sends back what was originally sent by the sender (more like an echo).

the problem is that one of the 2 tasks is only running most of the time (which is sendData()) and the other task barely runs (I knew that by calling Serial.println("test") once in the while loop of the 1st task and again for the other task, for the function (task) sendData(), the Serial.println("test") was called infinitely when using it inside function sendData(), while using same line in the other function receiveData() , it was only called few times, and for all the times the code doesn't work as it was intended to be.

so anyone can tell me what's wrong with my code?

main.cpp

#include <Arduino.h>

#include "constants.h"

#include "bluetooth.h"

// utility objects
Bluetooth bluetooth;  /*this is the object throught which we can control the bluetooth*/


// task handlers
TaskHandle_t xHandle_bluetoothSendData = NULL;    /*this is the task handler for the bleutooth send data task*/
TaskHandle_t xHandle_bluetoothReceiveData = NULL; /*this is the task handler for the bleutooth receive data task*/


// Queue handlers
QueueHandle_t xQueue_bluetoothSendData = NULL;    /*this is the queue used for bluetooth send data queue task*/
QueueHandle_t xQueue_bluetoothRcvData = NULL;     /*this is the queue used for bluetooth receive data queue task*/


// task queues contents structures
typedef struct
{
  char data[BLUETOOTH_MSG_SEND_LEN];
}bluetoothSendMsg_t;

typedef struct
{
  char data[BLUETOOTH_MSG_RCV_LEN];
}bluetoothRcvMsg_t;



/**
 * @brief this is the task used for sending data from esp32
*/
void sendData(void* pvParameters)
{
  /*struct for sending data*/
  bluetoothSendMsg_t *sendMessage;

  while (1)
  {
    /*get what's in the queue*/
    if(xQueue_bluetoothSendData != NULL && xQueueReceive(xQueue_bluetoothSendData, &sendMessage, (TickType_t) 0) == pdPASS)
    {
      Serial.print(sendMessage->data);  
      bluetooth.sendData(sendMessage->data);      /*sending the message received back to receiver*/ 
    }
  }
}

/**
 * @brief this is the task used for reveiving data by esp32
*/
void receiveData(void* pvParameters)
{
  /*for sending and receiving structs*/
  bluetoothRcvMsg_t receivedMsg;
  bluetoothSendMsg_t *messageTobeSend;

  while (1)
  {

    /*receiving message form the receiver from the receiver and put it in a queue to be send back*/
    strcpy(receivedMsg.data, bluetooth.getData(BLUETOOTH_MSG_RCV_LEN, 0).c_str());

    Serial.print("test");

    /*for some reason we received an empty message*/
    if(receivedMsg.data[0] != '\0')
    {
      messageTobeSend = (bluetoothSendMsg_t*) pvPortMalloc(sizeof(bluetoothSendMsg_t)); /*make a place in the heap for this new message*/
      strcpy(messageTobeSend->data, receivedMsg.data);  /*copying received message to another struct that will be send back*/
      Serial.print(messageTobeSend->data);
      xQueueSend(xQueue_bluetoothSendData, (void*) &messageTobeSend, (TickType_t) 0); // send back to the queue , non blocking if the queue is full
      receivedMsg.data[0] = '\0';
    }

  }
  
}

void setup() {
  // put your setup code here, to run once:
  bluetooth.init(); /*initialization for the code*/

  /*for debugging purposes*/
  Serial.begin(115200); 
  while(!Serial);

  /*create needed queues for inter-task communication*/
  /*docs: https://www.freertos.org/a00116.html*/
  xQueue_bluetoothSendData = xQueueCreate(BLUETOOTH_SEND_DATA_QUEUE_LEN, sizeof(bluetoothSendMsg_t*));
  xQueue_bluetoothRcvData = xQueueCreate(BLUETOOTH_RCV_DATA_QUEUE_LEN, sizeof(bluetoothRcvMsg_t*));
  
  /*create needed tasks*/
  /*docs: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos_idf.html#_CPPv423xTaskCreatePinnedToCore14TaskFunction_tPCKcK22configSTACK_DEPTH_TYPEPCv11UBaseType_tPC12TaskHandle_tK10BaseType_t*/
  xTaskCreatePinnedToCore(sendData, "Send data to bluetooth", BLUETOOTH_STACK_SEND_DATA, nullptr, tskIDLE_PRIORITY , &xHandle_bluetoothSendData, tskNO_AFFINITY);
  xTaskCreatePinnedToCore(receiveData, "receive dat from the bluetooth", BLUETOOTH_STACK_RCV_DATA, nullptr, tskIDLE_PRIORITY , &xHandle_bluetoothReceiveData, tskNO_AFFINITY);


} 

void loop() {
}

constants.h

#ifndef CONSTANTS_H_
#define CONSTANTS_H_


// related to the bluetooth
#define BLUETOOTH_STACK_SEND_DATA       1024      // used by freeRTOS to create task for sending data
#define BLUETOOTH_MSG_SEND_LEN          5       // used by queues for sending data to the task
#define BLUETOOTH_SEND_DATA_QUEUE_LEN   10      // used by queues to create the length of the queue

#define BLUETOOTH_STACK_RCV_DATA       1024      // used by freeRTOS to create task for sending data
#define BLUETOOTH_MSG_RCV_LEN          5       // used by queues for sending data to the task
#define BLUETOOTH_RCV_DATA_QUEUE_LEN   10      // used by queues to create the length of the queue


#endif

bluetooth.h

#ifndef BLUETOOTH_H_
#define BLUETOOTH_H_

/*library link: https://github.com/espressif/arduino-esp32/tree/master/libraries/BluetoothSerial*/
#include "BluetoothSerial.h"

class Bluetooth
{
private:

    /*this is object used to access bluetooth utils*/
    BluetoothSerial SerialBT;

public:

    /*this is the name of the esp32 when being searched for*/
    const String device_name = "ESP32-Carcotech";

    /*this is the Pin number for connection with ESP for legacy devices*/
    const char *pin = "1234";

    /*constructor*/
    Bluetooth();
  
    /*destructor*/
    ~Bluetooth();

    /**
     * @brief used to receive a string from the bluetooth
     * @param dataLen: is the length of the expected data to receive from the bluetooth module
     * @param timeOutMS: is the timeOut to wait for if not all data received : put 0 to wait infinitely if didn't till recevie same dataLen
    */
    String getData(int dataLen, uint32_t timeOutMS);

    /**
     * @brief used to send data over the bluetooth module
     * @param data: this is the data to send
    */
    void sendData(String data);


    /**
     * @brief this is the initialization function, for some reason putting the intialization code in the 
     * constructor results in watchdog timer to reset the esp32 over and over and over
    */
    void init();

};

#endif

bluetooth.cpp

#include "bluetooth.h"

Bluetooth::Bluetooth()
{


}

String Bluetooth::getData(int dataLen, uint32_t timeOutMS)
{

    // this is a counter for how many data we received
    int receivedCounter = 0;

    // this is the buffer to receive in
    String data;

    // setting the timeOut
    SerialBT.setTimeout(timeOutMS);    

    // check if we should wait indefinitely or not
    if(timeOutMS == 0)
    {   

        while(SerialBT.available())
        {
            if(receivedCounter == dataLen)
                break;
            
            data += (char)SerialBT.read();
            receivedCounter++;
        }
        
    }
    else
    {

        while(SerialBT.available())
        {
            int temp = SerialBT.read();

            if(receivedCounter == dataLen || temp == -1 )
                break;
            
            data += (char)SerialBT.read();
            receivedCounter++;
        }

    }

    return data + "\0";
}

void Bluetooth::sendData(String data)
{
    SerialBT.write((const uint8_t *)data.c_str(), strlen(data.c_str()));
}

void Bluetooth::init()
{
    
    // initialization of the bluetooth
    SerialBT.begin(device_name);

    // telling the bluetooth to use that pin for connection
    SerialBT.setPin(pin);
}


Bluetooth::~Bluetooth()
{
}
abdo Salm
  • 1,678
  • 4
  • 12
  • 22

1 Answers1

1

I think that your problem comes from

  • the timeout being set to 0
  • a bad behavior of Bluetooth::getData when timeout is 0

Reading at xQueueReceive documentation:

xTicksToWait The maximum amount of time the task should block waiting for an item to receive should the queue be empty at the time of the call. Setting xTicksToWait to 0 will cause the function to return immediately if the queue is empty. The time is defined in tick periods so the constant portTICK_PERIOD_MS should be used to convert to real time if this is required.

If INCLUDE_vTaskSuspend is set to '1' then specifying the block time as portMAX_DELAY will cause the task to block indefinitely (without a timeout).

So if you want to wait indefinitely when passing 0 timeout (as guess from comment), I recommend to change you code to:

String Bluetooth::getData(int dataLen, uint32_t timeOutMS)
{
    // this is a counter for how many data we received
    int receivedCounter = 0;

    // this is the buffer to receive in
    String data;

    // check if we should wait indefinitely or not
    if(timeOutMS == 0)
    {   
        // wait forever
        SerialBT.setTimeout(portMAX_DELAY);

        while(SerialBT.available())
        {
            if(receivedCounter == dataLen)
                break;
            
            data += (char)SerialBT.read();
            receivedCounter++;
        }
    }
    else
    {
        // setting the timeOut
        SerialBT.setTimeout(timeOutMS); 

        while(SerialBT.available())
        {
            int temp = SerialBT.read();

            if(receivedCounter == dataLen || temp == -1 )
                break;
            
            data += (char)SerialBT.read();
            receivedCounter++;
        }

    }

    return data + "\0";
}
Mathieu
  • 8,840
  • 7
  • 32
  • 45
  • really appreciate it, it was one of 2 main reasons I discovered, the other reason is that I had to introduce some delay inside the `while` loops (suggested by some colleage) and it worked but neither him nor I know why we should introduce this delay inside the loops using `vTaskDelay()` – abdo Salm Mar 02 '23 at 21:41