ESP32 FREERTOS FIDESURS: Communication inter-tâches (Arduino)

ESP32 FREERTOS FIDESURS: Communication inter-tâches (Arduino)

Dans ce guide, vous apprendrez à utiliser les files d’attente de Freertos pour une communication sûre et efficace entre les tâches sur l’ESP32, en utilisant l’ide Arduino. Les files d’attente vous permettent d’échanger des données entre les tâches de manière sûre. Nous couvrirons les principes de base du fonctionnement des files d’attente et vous guiderons à travers trois exemples pratiques pour montrer comment transmettre les données entre les tâches.

ESP32 avec files d'attente de Freertos: communication inter-tâches (Arduino IDE)

Nouveau à Freertos? Commencez par ce tutoriel: ESP32 avec Freertos (Arduino IDE) – Guide démarré: Création de tâches.

Table des matières

Dans ce tutoriel, nous couvrirons les sujets suivants:

Freertos Communication entre les tâches

À Freertos, il existe différentes façons de communiquer et de synchroniser les données entre les tâches, adaptées à différentes fins.

  • Files d’attente: permettre aux tâches d’envoyer et de recevoir des données, comme les lectures de capteurs, les états de bouton ou d’autres données, comme nous le verrons dans les exemples couverts dans ce tutoriel. Ici, nous nous concentrerons sur les files d’attente de Freertos.
  • Les sémaphores sont comme des outils de signalisation dans Freertos utilisés pour coordonner les tâches. Ils sont souvent utilisés pour indiquer qu’un événement s’est produit ou une ressource est prête. Ils ne transportent pas de données, juste un décompte (ou un drapeau) pour déclencher des actions après que quelque chose s’est produit. Nous couvrirons cela dans un futur tutoriel.
  • Les mutexes (exclusions mutuelles) sont utilisées pour protéger les ressources partagées en permettant à une seule tâche d’y accéder à la fois. Cela empêche les conflits dans les opérations simultanées qui doivent accéder aux mêmes données. Contrairement aux files d’attente, les mutex se concentrent sur l’accès aux ressources plutôt que sur le transfert de données. Nous couvrirons cela dans un futur tutoriel.

Que sont les files d’attente Freertos?

Les files d’attente de Freertos sont des structures de données en filetage qui permettent aux tâches d’envoyer et de recevoir des données pour une communication synchronisée dans un environnement multitâche.

Ils garantissent que les données sont transmises en toute sécurité entre les tâches sans conflits ni perte de données.

Les files d’attente sont particulièrement utiles lorsqu’une tâche doit envoyer des données et qu’une autre tâche doit les gérer plus tard, comme une tâche de capteur envoyant des lectures à une tâche de journalisation ou pour être traitée pour s’afficher sur un écran. De cette façon, chaque tâche peut fonctionner à sa propre vitesse sans interférer avec l’autre.

Bases de file d’attente Freertos

Voici quelques concepts de base sur la création et la gestion des files d’attente sur votre code Arduino IDE pour l’ESP32:

Créer une file d’attente

Utilisez le xqueuecreate (Size, item_size) pour créer une tâche – la taille correspond au nombre d’éléments qui peuvent être sur la file d’attente et item_size est la taille des octets du tas alloué pour chaque élément de la file d’attente.

Envoi de données dans une file d’attente

Pour envoyer des données à une file d’attente, utilisez le xqueueSend () pour ajouter des données à la file d’attente. Ou utilisez xqueueSendFromisr () si l’envoi des données d’un ISR (interruption de service d’interruption).

Recevoir des données d’une file d’attente

La fonction xqueueCeceive () lit les données de la file d’attente, si disponible.


Exemple 1: Utilisation des files d’attente pour contrôler la luminosité des LED

ESP32 avec une LED et un potentiomètre

Dans cet exemple, nous créerons deux tâches. Une tâche enverra des données à une file d’attente. L’autre tâche lira les données de la file d’attente.

Nous contrôlerons la luminosité LED à l’aide d’un potentiomètre. Ceci est un exemple de base, vous avez probablement vu auparavant, pour en savoir plus sur la lecture analogique et le PWM. Dans ce cas, nous utiliserons cet exemple de base comme point de départ pour expliquer les files d’attente.

Nous allons créer deux tâches avec les noms suivants:

  • Pottask: lit la valeur du potentiomètre et l’envoie dans une file d’attente.
  • LEDBRIGHTNESTTASK: vérifie s’il y a des valeurs à recevoir de la file d’attente et ajuste la luminosité LED en conséquence.

Pièces requises

Pour ce projet, vous avez besoin des parties suivantes:

Vous pouvez utiliser les liens précédents ou aller directement à makeradvisor.com/tools pour trouver toutes les pièces de vos projets au meilleur prix!

ESP32 FREERTOS FIDESURS Communication inter taches Arduino

Câblage du circuit

Pour cet exemple, câblez une LED et un potentiomètre à votre carte ESP32. Nous connectons la LED à GPIO 2 et le potentiomètre à GPIO 15.

ESP32 connecté à une LED et à un potentiomètre

Code – Control LED luminosité (Exemple de base en file d’attente)

Le code suivant crée deux tâches. On lit les valeurs du potentiomètre et les envoie dans une file d’attente. L’autre tâche lit les valeurs de la file d’attente et contrôle la luminosité LED en conséquence.

Vous pouvez copier le code suivant sur l’Arduino IDE et le télécharger sur votre carte.

/*
  Rui Santos & Sara Santos - Raspberryme.com
  Complete project details at https://Raspberryme.com/esp32-freertos-queues-inter-task-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define POT_PIN 15 
#define LED_PIN 2   
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12  // 12-bit (0–4095)
#define QUEUE_SIZE 5

// Create an handle for the queue
QueueHandle_t potQueue = NULL;

void potTask(void *parameter) {
  for (;;) {
    uint16_t potValue = analogRead(POT_PIN);  // Read 0–4095
    xQueueSend(potQueue, &potValue, portMAX_DELAY);  // Send to queue
    Serial.printf("potTask: Sent pot value %u\n", potValue);
    vTaskDelay(100 / portTICK_PERIOD_MS);  // 100ms
  }
}

void LEDBrightnessTask(void *parameter) {
  for (;;) {
    uint16_t potValue;
    if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
      uint16_t brightness = potValue;
      ledcWrite(LED_PIN, brightness);
      Serial.printf("LEDBrightnessTask: Set brightness %u\n", brightness);
    }
  }
}

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

  // Setup PWM for LED
  ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);

  // Create queue (5 items, each uint16_t)
  potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
  if (potQueue == NULL) {
    Serial.println("Failed to create queue!");
    while (1);
  }

  // Create tasks
  xTaskCreatePinnedToCore(
    potTask,
    "potTask",
    3000,  // Task stack
    NULL,
    1,
    NULL,
    1  // Core 1
  );

  xTaskCreatePinnedToCore(
    LEDBrightnessTask,
    "LEDBrightnessTask",
    3000,
    NULL,
    1,
    NULL,
    1
  );
}

void loop() {
  // Empty
}

Afficher le code brut

Comment fonctionne le code?

Jetons un coup d’œil au code pour voir comment cela fonctionne.

Définissez les GPIO LED et Potentiomètre

Définissez d’abord les broches GPIO pour la LED et pour le potentiomètre.

#define POT_PIN 15
#define LED_PIN 2   

Propriétés PWM

Ensuite, définissez la fréquence et la résolution PWM pour ajuster la luminosité LED. Nous définissons la résolution 12 bits (la même résolution par défaut pour la lecture analogique).

#define PWM_FREQ 5000
#define PWM_RESOLUTION 12  // 12-bit (0–4095)

Si vous êtes nouveau dans PWM avec l’ESP32, consultez ce tutoriel pour en savoir plus: ESP32 PWM avec Arduino IDE (sortie analogique).

Taille de file d’attente de Freertos

Définissez la taille de la file d’attente Freertos. Il s’agit du nombre d’éléments qui peuvent retenir la file d’attente. Dans ce cas, nous le fixons à 5.

#define QUEUE_SIZE 5

Dans notre cas, il s’agit d’une bonne estimation car les éléments envoyés à la file d’attente seront presque immédiatement consommés par l’autre tâche.

Poignée de file d’attente

Créez une poignée pour la file d’attente. C’est ainsi que nous ferons référence à notre file d’attente tout au long de notre code. Nous appelons la file d’attente comme Potqueue.

// Create an handle for the queue
QueueHandle_t potQueue = NULL;

Fonction Pottask

Créez une fonction pour la tâche pontenomètre: Pottask. C’est la tâche qui se lira dans le pontenomomètre.

void potTask(void *parameter) {
  for (;;) {
    uint16_t potValue = analogRead(POT_PIN);  // Read 0–4095
    xQueueSend(potQueue, &potValue, portMAX_DELAY);  // Send to queue
    Serial.printf("potTask: Sent pot value %u\n", potValue);
    vTaskDelay(100 / portTICK_PERIOD_MS);  // 100ms
  }
}

Nous commençons par lire la valeur du potentiomètre et l’enregistrer dans un type de variable Uint16_T appelé PotValue.

uint16_t potValue = analogRead(POT_PIN);  // Read 0–4095

Pour en savoir plus sur la lecture des valeurs analogiques avec l’ESP32, consultez ce tutoriel: ESP32 ADC – Lisez les valeurs analogiques avec Arduino IDE.

Envoyer un article à la file d’attente

Ensuite, nous utiliserons la fonction XQueueSend () pour envoyer un élément à la file d’attente. Nous envoyons la PotValue à la poche comme suit.

Serial.printf("potTask: Sent pot value %u\n", potValue);

Le dernier paramètre, Portmax_delay signifie que la tâche attendra indéfiniment jusqu’à ce qu’il y ait de l’espace dans la file d’attente pour ce nouvel élément. Si vous ne voulez pas qu’il attende et que vous souhaitez revenir immense, passez 0 à la place.

vtaskdelay

Ensuite, nous attendons 100 millisecondes entre chaque lecture.

vTaskDelay(100 / portTICK_PERIOD_MS);  // 100ms

Dans les tâches de Freertos, nous devons utiliser la fonction VTaskDelay () au lieu de la fonction arduino de retard () habituelle. Il accepte comme argument le nombre de tiques à attendre. Dans le cas de l’ESP32, une tique correspond à 1ms = Porttick_period_ms.

LEDBRIGHTNESTTASK

Les lignes suivantes créent la fonction de la tâche qui contrôle la luminosité LED. Cela s’appelle LedBrightnessTask.

void LEDBrightnessTask(void *parameter) {
  for (;;) {
    uint16_t potValue;
    if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
      uint16_t brightness = potValue;
      ledcWrite(LED_PIN, brightness);
      Serial.printf("LEDBrightnessTask: Set brightness %u\n", brightness);
    }
  }
}

Nous créons une variable locale appelée PotValue qui recevra les valeurs dans la file d’attente.

uint16_t potValue;
Obtenez des valeurs de la file d’attente

Pour obtenir des valeurs de la file d’attente, nous pouvons utiliser la fonction xqueueCeceive (). Passez la poignée de la file d’attente comme argument, un pointeur vers la variable où la valeur doit être enregistrée et s’il faut attendre qu’un élément soit disponible (Portmax_delay) ou pour revenir immédiatement (passer 0).

if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
Contrôler la luminosité LED

Parce que nous contrôlons PWM avec une résolution 12 bits et en utilisant la résolution par défaut pour PWM (12 bits), la luminosité sera la même que la PotValue.

uint16_t brightness = potValue;

Enfin, nous pouvons utiliser la fonction LEDCWrite () pour ajuster la luminosité de la LED.

ledcWrite(LED_PIN, brightness);

installation()

Dans la configuration (), initialisez le moniteur en série à un taux de bauds de 115200.

Serial.begin(115200);

Configurez les propriétés PWM.

ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);
Créer la file d’attente

Créez la file d’attente réelle à l’aide de la fonction xqueUeCreate (). Cela accepte comme argument la taille de la file d’attente et la taille de chaque élément en octets. Étant donné que nous stockons les variables Uint16_T, nous passons la taille de Uint16_T, qui est de 2 octets (16 bits).

// Create queue (5 items, each uint16_t)
potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (potQueue == NULL) {
  Serial.println("Failed to create queue!");
  while (1);
}
Créer les tâches

Ensuite, créez les tâches qui gèrent le potentiomètre et contrôlez la luminosité LED. Nous attribuons les deux tâches au Core 1.

// Create tasks
xTaskCreatePinnedToCore(
  potTask,
  "potTask",
  3000,  // Task stack
  NULL,
  1,
  NULL,
  1  // Core 1
);

xTaskCreatePinnedToCore(
  LEDBrightnessTask,
  "LEDBrightnessTask",
  3000,
  NULL,
  1,
  NULL,
  1
);

Nous avons couvert comment créer des tâches dans ce tutoriel précédent: ESP32 avec Freertos (Arduino IDE) – Démarrage: Créer des tâches

boucle()

La boucle () est vide car Freertos gère les tâches.

void loop() {
  // Empty
}

Démonstration

Téléchargez le code sur votre carte. Appuyez sur le bouton INBORD RST, afin que l’ESP32 commence à exécuter le code.

Ensuite, ouvrez le moniteur en série à un taux en bauds de 115200. Vous devriez obtenir quelque chose de similaire.

ESP32 Tâches Freertos avec files d'attente - démonstration dans le moniteur en série

Faites tourner le potentiomètre et voyez la luminosité LED augmenter et diminuer en conséquence.

ESP32 Control LED luminosité avec ponteniomètre

Exemple 2: Freertos Filent – Partage des données entre trois tâches

Les données peuvent être partagées entre plusieurs tâches Freertos. Dans l’exemple précédent, nous avons transmis des données d’une tâche à une autre. Mais nous pouvons transmettre des données à autant de tâches que nous le souhaitons.

Cet exemple est une modification du projet précédent, avec l’ajout d’une troisième tâche. Cette troisième tâche sera chargée de récupérer les valeurs du potentiomètre à partir de la file d’attente et d’afficher les valeurs avec un horodatage dans le moniteur série. J’ai également supprimé toutes les impressions en série des autres tâches.

Ceci est un exemple simple qui montre comment vous pouvez utiliser les files d’attente pour partager des données entre plusieurs tâches.

Câblage du circuit

Gardez le même circuit avec une LED et un potentiomètre de l’exemple précédent.

Code: Freertos Filent – Trois tâches

Copiez le code suivant sur l’ide Arduino.

/*
  Rui Santos & Sara Santos - Raspberryme.com
  Complete project details at https://Raspberryme.com/esp32-freertos-queues-inter-task-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define POT_PIN 15 
#define LED_PIN 2   
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12  // 12-bit (0–4095)
#define QUEUE_SIZE 5

QueueHandle_t potQueue = NULL;

void SensorTask(void *parameter) {
  for (;;) {
    uint16_t potValue = analogRead(POT_PIN);  // Read 0–4095
    xQueueSend(potQueue, &potValue, portMAX_DELAY);  // Send to queue
    vTaskDelay(300 / portTICK_PERIOD_MS);  // 100ms
  }
}

void LEDBrightnessTask(void *parameter) {
  for (;;) {
    uint16_t potValue;
    if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
      uint16_t brightness = potValue;
      ledcWrite(LED_PIN, brightness);
    }
  }
}

void SerialLoggerTask(void *parameter) {
  for (;;) {
    uint16_t potValue;
    if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
      Serial.printf("SerialLoggerTask: Pot value %u at %lu ms\n", potValue, millis());
    }
  }
}

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

  // Setup PWM for LED
  ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);

  // Create queue (5 items, each uint16_t)
  potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
  if (potQueue == NULL) {
    Serial.println("Failed to create queue!");
    while (1);
  }

  // Create tasks with one parameter per line
  xTaskCreatePinnedToCore(
    SensorTask,             // Task function
    "SensorTask",           // Task name
    3000,                   // Stack size (bytes)
    NULL,                   // Task parameters
    1,                      // Priority
    NULL,                   // Task handle
    1                       // Core ID
  );

  xTaskCreatePinnedToCore(
    LEDBrightnessTask,      // Task function
    "LEDBrightnessTask",    // Task name
    3000,                   // Stack size (bytes)
    NULL,                   // Task parameters
    1,                      // Priority
    NULL,                   // Task handle
    1                       // Core ID
  );

  xTaskCreatePinnedToCore(
    SerialLoggerTask,       // Task function
    "SerialLoggerTask",     // Task name
    3000,                   // Stack size (bytes)
    NULL,                   // Task parameters
    1,                      // Priority
    NULL,                   // Task handle
    1                       // Core ID
  );
}

void loop() {
  // Empty
}

Afficher le code brut

Comment fonctionne le code?

Ce code est très similaire à l’exemple précédent, mais nous ajoutons une tâche supplémentaire. Le SerialLoggerTask ().

void SerialLoggerTask(void *parameter) {
  for (;;) {
    uint16_t potValue;
    if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
      Serial.printf("SerialLoggerTask: Pot value %u at %lu ms\n", potValue, millis());
    }
  }
}

Cette tâche obtient la potvalue de la poche.

if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {

Et puis, l’affiche dans le moniteur en série le long d’un horodatage.

Serial.printf("SerialLoggerTask: Pot value %u at %lu ms\n", potValue, millis());

Vous devez également créer cette nouvelle tâche dans la configuration ().

xTaskCreatePinnedToCore(
  SerialLoggerTask,       // Task function
  "SerialLoggerTask",     // Task name
  3000,                   // Stack size (bytes)
  NULL,                   // Task parameters
  1,                      // Priority
  NULL,                   // Task handle
  1                       // Core ID
);

Démonstration

Le résultat sera similaire à l’exemple précédent, mais maintenant vous avez le SerialLoggertask affichant les valeurs et l’horodatage dans le moniteur série.

ESP32 avec une tâche Freertos qui imprime les données du moniteur en série - démonstration

Cet exemple simple est de démontrer que vous pouvez transmettre des données à autant de tâches que vous le souhaitez avec plusieurs files d’attente.


Exemple 3: Afficher les données du capteur sur un écran (partage d’une structure de données)

Lectures du capteur d'affichage ESP32 à partir d'un capteur BME280 sur un écran OLED

Dans cet exemple, nous afficherons les lectures du capteur à partir d’un BME280 sur un écran OLED à l’aide de tâches Freertos. Le processus est divisé en deux tâches principales:

Sensortask:

  • Lit la température, l’humidité et la pression du capteur BME280
  • Envoie les données du capteur à une file d’attente

Affichage

  • Reçoit les données du capteur de la file d’attente
  • Affiche les lectures sur l’écran OLED

Le but de cet exemple est de démontrer comment partager une structure de données contenant plusieurs variables (température, humidité et pression) entre les tâches.

Pièces requises

Pour cet exemple, vous avez besoin des parties suivantes:

Câblage du circuit

Tirez le BME280 et l’affichage OLED sur les broches I2C par défaut ESP32.

Circuit ESP32 avec écran OLED et BME280 connecté aux broches I2C par défaut ESP32

Vous aimerez peut-être aussi la lecture: ESP32 I2C Communication: Set Pins, plusieurs interfaces de bus et périphériques (Arduino IDE).

BME280 ESP32
Vin 3V3
GND GND
SCL GPIO 22
SDA GPIO 21
Affichage OLED ESP32
Vin 3V3
GND GND
SCL GPIO 22
SDA GPIO 21

Code – afficher les données du capteur sur un écran à l’aide de files d’attente Freertos

Copiez le code suivant sur l’ide Arduino.

/*
  Rui Santos & Sara Santos - Raspberryme.com
  Complete project details at https://Raspberryme.com/esp32-freertos-queues-inter-task-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include 
#include 
#include 
#include 
#include 

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDRESS 0x3C

#define QUEUE_SIZE 5

Adafruit_BME280 bme;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

QueueHandle_t sensorQueue = NULL;

typedef struct {
  float temperature;
  float humidity;
  float pressure;
} SensorData;

void SensorTask(void *parameter) {
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
  for (;;) {
    SensorData data;
    data.temperature = bme.readTemperature();  
    data.humidity = bme.readHumidity();        
    data.pressure = bme.readPressure() / 100.0F;  
    xQueueSend(sensorQueue, &data, portMAX_DELAY);
    Serial.print("SensorTask: Sent Temp=");
    Serial.print(data.temperature, 1);
    Serial.print("°C, Hum=");
    Serial.print(data.humidity, 1);
    Serial.print("%, Pres=");
    Serial.print(data.pressure, 1);
    Serial.println("hPa");
    vTaskDelay(2000 / portTICK_PERIOD_MS);  // 2s
  }
}

void DisplayTask(void *parameter) {
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
    Serial.println("OLED init failed!");
    while (1);
  }
  display.clearDisplay();
  display.setTextSize(1); 
  display.setTextColor(SSD1306_WHITE);

  for (;;) {
    SensorData data;
    if (xQueueReceive(sensorQueue, &data, portMAX_DELAY)) {

      // Display on OLED
      display.clearDisplay();
      display.setCursor(0, 0);
      display.print("Temperature: ");
      display.print(data.temperature, 1);
      display.print(" ");
      display.cp437(true);
      display.write(167);
      display.println("C");
      display.setCursor(0, 20);
      display.print("Humidity: ");
      display.print(data.humidity, 1);
      display.println(" %");
      display.setCursor(0, 40);
      display.print("Pressure: ");
      display.print(data.pressure, 1);
      display.println(" hPa");
      display.display();
    }
  }
}

void setup() {
  Serial.begin(115200);
  
  // Starts I2C on the board default's  I2C pins
  Wire.begin();

  // Create queue
  sensorQueue = xQueueCreate(QUEUE_SIZE, sizeof(SensorData));
  if (sensorQueue == NULL) {
    Serial.println("Failed to create queue!");
    while (1);
  }

  // Create tasks
  xTaskCreatePinnedToCore(
    SensorTask,             // Task function
    "SensorTask",           // Task name
    4000,                   // Stack size (bytes)
    NULL,                   // Task parameters
    1,                      // Priority
    NULL,                   // Task handle
    1                       // Core ID
  );

  xTaskCreatePinnedToCore(
    DisplayTask,            // Task function
    "DisplayTask",          // Task name
    4000,                   // Stack size (bytes)
    NULL,                   // Task parameters
    1,                      // Priority
    NULL,                   // Task handle
    1                       // Core ID
  );
}

void loop() {
  // Empty
}

Afficher le code brut

Comment fonctionne le code?

À ce stade, vous devriez être familier avec la plupart du code.

Pour envoyer plusieurs variables à la file d’attente, nous créons une structure qui contiendra la température, l’humidité et la pression. Nous appelons ce type de structure Sensordata.

Alternativement, vous pouvez également utiliser des objets JSON, mais je trouve des structures plus faciles à utiliser pour ce scénario.

typedef struct {
  float temperature;
  float humidity;
  float pressure;
} SensorData;

La fonction SensortaSk est responsable de la récupération des lectures de capteurs actuelles et de l’envoi de la file d’attente. Nous créons une variable appelée données, qui est une structure de données de type sensordata (celle que nous avons créée précédemment).

SensorData data;

Ensuite, nous ajoutons des valeurs à la structure des données comme suit.

data.temperature = bme.readTemperature();  
data.humidity = bme.readHumidity();        
data.pressure = bme.readPressure() / 100.0F;  

Maintenant que nous avons rempli la structure des données avec les dernières lectures de capteurs, nous pouvons l’envoyer dans la file d’attente, tout comme nous l’avons fait dans les exemples précédents, en utilisant la fonction XQueuend ().

xQueueSend(sensorQueue, &data, portMAX_DELAY);

Dans la fonction DisplayTask, nous obtenons les données de la file d’attente et les affichons sur l’écran OLED.

void DisplayTask(void *parameter) {
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
    Serial.println("OLED init failed!");
    while (1);
  }
  display.clearDisplay();
  display.setTextSize(1); 
  display.setTextColor(SSD1306_WHITE);

  for (;;) {
    SensorData data;
    if (xQueueReceive(sensorQueue, &data, portMAX_DELAY)) {

      // Display on OLED
      display.clearDisplay();
      display.setCursor(0, 0);
      display.print("Temperature: ");
      display.print(data.temperature, 1);
      display.print(" ");
      display.cp437(true);
      display.write(167);
      display.println("C");
      display.setCursor(0, 20);
      display.print("Humidity: ");
      display.print(data.humidity, 1);
      display.println(" %");
      display.setCursor(0, 40);
      display.print("Pressure: ");
      display.print(data.pressure, 1);
      display.println(" hPa");
      display.display();
    }
  }
}

Comme dans les exemples précédents, dans la configuration (), nous créons la file d’attente et les tâches, et les assignons à un noyau ESP32.

void setup() {
  Serial.begin(115200);
  
  // Starts I2C on the board default's  I2C pins
  Wire.begin();

  // Create queue
  sensorQueue = xQueueCreate(QUEUE_SIZE, sizeof(SensorData));
  if (sensorQueue == NULL) {
    Serial.println("Failed to create queue!");
    while (1);
  }

  // Create tasks
  xTaskCreatePinnedToCore(
    SensorTask,             // Task function
    "SensorTask",           // Task name
    4000,                   // Stack size (bytes)
    NULL,                   // Task parameters
    1,                      // Priority
    NULL,                   // Task handle
    1                       // Core ID
  );

  xTaskCreatePinnedToCore(
    DisplayTask,            // Task function
    "DisplayTask",          // Task name
    4000,                   // Stack size (bytes)
    NULL,                   // Task parameters
    1,                      // Priority
    NULL,                   // Task handle
    1                       // Core ID
  );
}

La boucle () est vide car Freertos gère les tâches.

void loop() {
  // Empty
}

Démonstration

Téléchargez le code précédent sur votre carte ESP32. Après le téléchargement, appuyez sur le bouton INBORD RST afin que la carte commence à exécuter le code.

Ouvrez le moniteur en série à un taux en bauds de 115200. Toutes les deux secondes, il affichera des lectures de capteurs mis à jour.

Tâche de capteur ESP32 (Freertos) affichée sur le moniteur en série toutes les deux secondes

L’écran est mis à jour au même rythme que les nouvelles lectures sont envoyées à la file d’attente.

Affichage ESP32 BME280 LEATURES DE CAPTEURS SUR L'AFFICHAGE OLED À l'aide des files d'attente Freertos

Emballage

Dans ce tutoriel, vous avez appris à utiliser Freertos Fidoues pour transmettre des données d’une tâche à l’autre de manière sûre. Nous vous avons montré trois exemples différents qui montrent à quel point il est facile d’utiliser des files d’attente pour communiquer entre les tâches. Après avoir compris ces exemples de base, l’idée est d’appliquer ce concept à des projets plus complexes, où l’accès aux valeurs des variables simultanément pourrait être essentiel au programme.

Nous espérons que vous avez trouvé ce tutoriel utile. Nous créerons plus de guides Freertos avec l’ESP32 programmé avec Arduino IDE bientôt. Alors, restez à l’écoute.

En savoir plus sur l’ESP32 avec nos ressources:

Merci d’avoir lu.

Cette vidéo vous emmène dans l’histoire de Raspberry Pi :

YouTube video