ESP32 CYD avec ESP-NOW: recevoir des données

ESP32 CYD avec ESP-NOW: recevoir des données

Dans ce projet, vous apprendrez à utiliser le protocole de communication ESP-Now avec la carte CYD ESP32 (affichage jaune bon marché) pour recevoir et afficher les données de plusieurs tableaux d’expéditeurs ESP32. Deux cartes ESP32 liront chacune les données du capteur d’un BME280 et les verront à la carte CYD. La carte CYD, agissant en tant que récepteur ESP-Now, affichera les données reçues dans des tables distinctes – une pour chaque expéditeur – chacune indiquée sur un onglet différent à l’écran.

ESP32 CYD avec ESP-NOW: recevoir et afficher les données de plusieurs planches

Nouveau à l’affichage jaune bon marché ESP32? Commencez ici: commencer avec ESP32 Board Display Yellow Display – CYD (ESP32-2432S028R).

Aperçu du projet

Voici un aperçu du fonctionnement de ce projet.

ESP32 CYD Board reçoit les données de plusieurs planches ESP32 via ESP-Now - Présentation du projet
  • Dans ce projet, le conseil d’administration de CYD ESP32 agira comme un récepteur ESP-Now qui reçoit des données d’autres cartes ESP32.
  • Les autres cartes ESP32 enverront périodiquement les données du capteur (BME280) à la carte CYD, via ESP-Now.
  • La carte CYD affiche deux onglets, chacun pour chaque carte. Chaque onglet contient un tableau avec les données reçues des autres cartes.
  • Les tableaux sont mis à jour dès que le CYD reçoit de nouvelles données.

Nouveau protocole de communication ESP-Now? Commencez ici: commencez avec ESP-Now (ESP32 avec Arduino IDE).

Condition préalable

Avant de continuer, assurez-vous de suivre les prochaines conditions préalables. Vous devez suivre toutes les étapes, sinon, votre projet ne fonctionnera pas.

1) 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!

1752168666 134 ESP32 CYD avec ESP NOW recevoir des donnees

2) Installez les planches ESP32 dans Arduino IDE

Logo Arduino Ide 2

Nous programmerons l’ESP32 en utilisant Arduino IDE. Assurez-vous que les planches ESP32 sont installées. Suivez le prochain tutoriel:

3) Se familiariser avec l’affichage jaune bon marché ESP32

Le conseil d’aménagement ESP32-2432S028R est devenu connu dans la communauté des créateurs sous le nom de «affichage jaune bon marché» ou CYD pour faire court. Ce tableau de développement, dont la puce principale est un module ESP32-WORMO-32, est livrée avec un écran LCD tactile TFT de 2,8 pouces, une interface de carte microSD, une LED RVB et tous les circuits requis pour programmer et appliquer la puissance à la carte.

ESP32 Board Cyd Affichage jaune pas cher ESP32-2432S028R Front

Si c’est la première fois que vous utilisez l’affichage jaune bon marché ESP32, assurez-vous de suivre notre guide de démarrage:

4) Installez les bibliothèques TFT et LVGL

LVGL (bibliothèque graphique léger et polyvalente) est une bibliothèque graphique gratuite et open source qui fournit une large gamme d’éléments graphiques faciles à utiliser pour vos projets de microcontrôleur qui nécessitent une interface utilisateur graphique (GUI).

LVGL NOUVEAU LOGO

Suivez le prochain tutoriel pour installer et configurer les bibliothèques requises pour utiliser LVGL pour l’affichage jaune bon marché ESP32 à l’aide d’Arduino IDE.

5) Installez les bibliothèques BME280

Pour ce projet, nous utiliserons la bibliothèque Adafruit BME280 pour obtenir des données du BME280 sur les autres cartes ESP32. Dans le Arduino IDE, allez dans Sketch> Inclure la bibliothèque> Gérer les bibliothèques. Recherchez la bibliothèque Adafruit BME280 dans la zone de recherche et installez la bibliothèque. Installez également toutes les dépendances qui ne sont actuellement pas installées (généralement les bibliothèques Adafruit Bus et les bibliothèques de capteurs unifiées Adafruit).

Installation de la bibliothèque de capteurs Adafruit BME280 Arduino IDE

Obtenir l’adresse MAC du tableau cyd

Tout d’abord, vous devez connaître l’adresse MAC de la carte CYD afin que les cartes des expéditeurs puissent lui envoyer des messages.

ESP32 Board Cyd Affichage jaune pas cher ESP32-2432S028R Front

Téléchargez le code suivant sur la carte CYD.

/*
  Rui Santos & Sara Santos - Raspberryme.com
  Complete project details at https://Raspberryme.com/get-change-esp32-esp8266-mac-address-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 

void readMacAddress(){
  uint8_t baseMac[6];
  esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);
  if (ret == ESP_OK) {
    Serial.printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
                  baseMac[0], baseMac[1], baseMac[2],
                  baseMac[3], baseMac[4], baseMac[5]);
  } else {
    Serial.println("Failed to read MAC address");
  }
}

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

  WiFi.mode(WIFI_STA);
  WiFi.STA.begin();

  Serial.print("[DEFAULT] ESP32 Board MAC Address: ");
  readMacAddress();
}
 
void loop(){

}

Afficher le code brut

Après le téléchargement, ouvrez le moniteur série à un taux de bauds de 115200. Appuyez sur le bouton intégré. L’adresse MAC de la carte sera imprimée dans le moniteur série.

Obtenir l'adresse MAC de la carte CYD ESP32 sur le moniteur en série

Préparer les tableaux des expéditeurs

Pour ce tutoriel, nous enverrons des données au CYD à partir de deux cartes différentes. Vous pouvez modifier ce projet pour afficher les données à partir de plus de cartes.

Chaque carte sera identifiée par un ID (un nombre que nous attribuerons à chaque carte):

  • Id = 1 pour la carte1
  • Id = 2 pour la carte2
Deux planches ESP32 avec un capteur BME280

Câblage du capteur BME280

Chaque carte des expéditeurs enverra des données environnementales à partir d’un capteur BME280. Tirez un capteur BME280 à chacune de vos planches. Nous utiliserons les broches I2C par défaut ESP32.

En savoir plus sur I2C avec la communication ESP32: ESP32 I2C: Définissez des épingles, plusieurs interfaces de bus et périphériques (Arduino IDE).

ESP32 BME280 Température Température Humidité Pression de câblage Circuit

Vous ne connaissez pas le BME280 avec l’ESP32? Lisez ce tutoriel: ESP32 avec le capteur BME280 en utilisant Arduino IDE (pression, température, humidité).

Si vous utilisez un modèle de carte différent, les broches I2C par défaut sont différentes.

Code de l’expéditeur ESP32 ESP-NOW

Le code suivant lit les données du capteur BME280 et les envoie via ESP-Now à la carte CYD. N’oubliez pas de modifier le code avec l’adresse MAC de votre carte CYD. De plus, n’oubliez pas de modifier l’ID de la carte pour chacun de vos conseils.

/*********
  Rui Santos & Sara Santos - Raspberryme.com
  Complete project details at https://Raspberryme.com/esp32-cyd-esp-now-receive-data/
  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 

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 1

Adafruit_BME280 bme; 

// REPLACE WITH YOUR ESP RECEIVER'S MAC ADDRESS
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// Structure to send data, must match the receiver structure
typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

// Create a struct_message called myData
struct_message myData;

unsigned long previousMillis = 0;   // Stores last time temperature was published
const long interval = 10000;        // Interval at which to publish sensor readings

unsigned int readingId = 0;

void initBME() {
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

float readTemperature() {
  float t = bme.readTemperature();
  return t;
}

float readHumidity() {
  float h = bme.readHumidity();
  return h;
}

esp_now_peer_info_t peerInfo;

// Callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  char macStr[18];
  Serial.print("Packet to: ");
  // Copies the receiver mac address to a string
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print(macStr);
  Serial.print(" send status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  Serial.begin(115200);
  initBME(); 

  WiFi.mode(WIFI_STA);
 
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  esp_now_register_send_cb(OnDataSent);
   
  // Register peer
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  // Copy receiver's MAC address to peerInfo
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;
    // Set values to send
    myData.id = BOARD_ID;
    myData.temp = readTemperature();
    myData.hum = readHumidity();
    myData.readingId = readingId++;
     
    // Send data and check for errors
    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    Serial.print("Sending data (Reading ID: ");
    Serial.print(myData.readingId);
    Serial.print(", Temp: ");
    Serial.print(myData.temp);
    Serial.print("C, Hum: ");
    Serial.print(myData.hum);
    Serial.print("%): ");
    Serial.println(result == ESP_OK ? "Sent" : "Failed to send");
  }
}

Afficher le code brut

Comment fonctionne le code?

Jetons un coup d’œil sur le fonctionnement du code. Si vous êtes nouveau sur ESP-NOW, nous vous recommandons de démarrer avec ce guide ESP-Now pour mieux comprendre comment les choses fonctionnent.

Y compris les bibliothèques

Commencez à inclure les bibliothèques requises pour ce projet. Le ESP_NOW et Wifi sont tenus d’utiliser la communication ESP-Now et le Adafruit_bme280 et Adafruit_sensor sont nécessaires pour s’interfacer avec le capteur BME280.

#include 
#include 
#include 
#include 

Définissez votre identifiant de carte

Donnez une pièce d’identité unique à votre planche afin que le récepteur puisse facilement l’identifier. Ici, nous numérotions simplement les planches, mais vous pouvez utiliser une méthode différente, comme leur donner un nom, ou simplement les identifier par l’adresse MAC.

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 2

Assurez-vous de donner une pièce d’identité différente à chaque planche.

Instance BME280

Créer une instance du Adafruit_bme280 bibliothèque appelée BME qui sera utilisé pour s’interfacer avec le capteur.

Adafruit_BME280 bme; 

L’adresse MAC de la carte du récepteur

Insérez l’adresse MAC de votre carte réceptrice (la carte CYD). Par exemple, mon adresse MAC Cyd Board est 24: DC: C3: 49: 6A: 14. Je dois l’ajouter au code dans le format suivant.

// REPLACE WITH YOUR ESP RECEIVER'S MAC ADDRESS
uint8_t broadcastAddress[] = {0x24, 0xDC, 0xC3, 0x49, 0x6A, 0x14};

Création d’une structure de données

Créer une nouvelle structure appelée struct_message qui contiendra toutes les données que vous souhaitez envoyer. Le identifiant est l’identifiant de la planche, le température et humidité correspondent aux données que nous obtenons du capteur BME280 et au en lecture est juste un numéro pour garder une trace du nombre de lectures envoyées.

typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

Ensuite, nous créons une nouvelle variable de structure appelée mydata basé sur le struct_message structure.

struct_message myData;

Initialiser et obtenir des données du capteur BME280

Nous créons plusieurs fonctions liées au capteur BME280.

Le initbme () Initialise le capteur BME280 sur les broches I2C par défaut de l’ESP32.

void initBME() {
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

Le ReadTemperature () La fonction renvoie la valeur de température actuelle en tant que flotteur.

float readTemperature() {
  float t = bme.readTemperature();
  return t;
}

Le Readhumidity () La fonction renvoie la valeur d’humidité actuelle, également comme un flotteur.

float readHumidity() {
  float h = bme.readHumidity();
  return h;
}

Variable d’information par les pairs

Créer une nouvelle variable globale de type esp_now_peer_info_t appelé peerinfo Cela sera utilisé plus tard pour enregistrer des données sur les pairs ESP-Now.

esp_now_peer_info_t peerInfo;

Ondatasent ()

Le ondatasent () est une fonction de rappel qui sera appelée lorsque nous envoyons un message via ESP-Now. Cette fonction imprimera l’adresse MAC de l’expéditeur et si le message a été livré avec succès ou non.

// Callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  char macStr[18];
  Serial.print("Packet to: ");
  // Copies the receiver mac address to a string
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print(macStr);
  Serial.print(" send status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

installation()

Dans le installation()Initialisez le moniteur série et le capteur BME280.

Serial.begin(115200);
initBME();

Vous devez également initialiser l’interface Wi-Fi pour pouvoir utiliser ESP-Now.

WiFi.mode(WIFI_STA);

Initialiser le protocole ESP-Now.

if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}

Enregistrez une fonction de rappel qui s’exécutera lorsque nous envoyons des données via ESP-Now.

esp_now_register_send_cb(OnDataSent);

Enregistrez le pair ESP-Now (la carte du récepteur).

// Register peer
peerInfo.channel = 0;  
peerInfo.encrypt = false;
// Copy receiver's MAC address to peerInfo
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
  Serial.println("Failed to add peer");
  return;
}

Dans cet exemple, nous ne crypterons pas les messages. Si vous souhaitez en savoir plus sur le chiffrement des messages ESP-NOW, vous pouvez lire ce tutoriel: ESP32: ESP-Now Crypted Messages.

boucle()

Enfin, dans le boucle()toutes les 10 secondes (réglé précédemment sur le intervalle variable), nous obtenons de nouvelles données et définissons le mydata Structure Valeurs à ces nouvelles données.

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;
    // Set values to send
    myData.id = BOARD_ID;
    myData.temp = readTemperature();
    myData.hum = readHumidity();
    myData.readingId = readingId++;

En fin de compte, nous utilisons le ESP_NOW_SEND () fonction pour envoyer les données au pair.

// Send data and check for errors
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));

Nous imprimons les lectures du capteur et les informations sur le processus d’envoi dans le moniteur série.

esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
Serial.print("Sending data (Reading ID: ");
Serial.print(myData.readingId);
Serial.print(", Temp: ");
Serial.print(myData.temp);
Serial.print("C, Hum: ");
Serial.print(myData.hum);
Serial.print("%): ");
Serial.println(result == ESP_OK ? "Sent" : "Failed to send");

Tester le code de l’expéditeur

Après avoir téléchargé le code sur la carte, ouvrez le moniteur série à un taux en bauds de 115200.

Moniteur en série pour la carte d'expéditeur ESP32 ESP-Now - montrant le message a été envoyé, mais la livraison a échoué

Vous verrez que la carte commencera à envoyer des données via ESP-Now. Pour le moment, la livraison échoue parce que nous n’avons pas encore préparé le récepteur – c’est ce que nous allons faire ensuite.

N’oubliez pas de télécharger ce code sur chaque carte et modifiez l’ID de la carte.

ESP32 CYD ESP-Now Receiver Board

Le conseil d’administration du CYD agira comme un récepteur ESP-Now qui obtiendra les données des deux autres conseils.

ESP32 CYD Board affichant des données sur un tableau - Données reçues d'autres ESP32 via ESP-NOW
ESP32 CYD Board affichant des données sur un tableau - Données reçues d'autres ESP32 via ESP-NOW

Les données de chaque carte seront affichées à l’écran. Nous créerons deux onglets différents: un pour chaque carte.

/*  Rui Santos & Sara Santos - Raspberryme.com - https://Raspberryme.com/esp32-cyd-esp-now-receive-data/
    THIS EXAMPLE WAS TESTED WITH THE FOLLOWING HARDWARE:
    1) ESP32-2432S028R 2.8 inch 240×320 also known as the Cheap Yellow Display (CYD): https://makeradvisor.com/tools/cyd-cheap-yellow-display-esp32-2432s028r/
      SET UP INSTRUCTIONS: https://Raspberryme.com/cyd-lvgl/
    2) REGULAR ESP32 Dev Board + 2.8 inch 240x320 TFT Display: https://makeradvisor.com/tools/2-8-inch-ili9341-tft-240x320/ and https://makeradvisor.com/tools/esp32-dev-board-wi-fi-bluetooth/
      SET UP INSTRUCTIONS: https://Raspberryme.com/esp32-tft-lvgl/
    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.
*/

/*  Install the "lvgl" library version 9.X by kisvegabor to interface with the TFT Display - https://lvgl.io/
    *** IMPORTANT: lv_conf.h available on the internet will probably NOT work with the examples available at Raspberryme.com ***
    *** YOU MUST USE THE lv_conf.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***
    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https://Raspberryme.com/cyd-lvgl/ or https://Raspberryme.com/esp32-tft-lvgl/   */
#include 

/*  Install the "TFT_eSPI" library by Bodmer to interface with the TFT Display - https://github.com/Bodmer/TFT_eSPI
    *** IMPORTANT: User_Setup.h available on the internet will probably NOT work with the examples available at Raspberryme.com ***
    *** YOU MUST USE THE User_Setup.h FILE PROVIDED IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***
    FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https://Raspberryme.com/cyd-lvgl/ or https://Raspberryme.com/esp32-tft-lvgl/   */
#include 

// Install the "XPT2046_Touchscreen" library by Paul Stoffregen to use the Touchscreen - https://github.com/PaulStoffregen/XPT2046_Touchscreen - Note: this library doesn't require further configuration
#include 

#include 
#include 
#include 

// Touchscreen pins
#define XPT2046_IRQ 36   // T_IRQ
#define XPT2046_MOSI 32  // T_DIN
#define XPT2046_MISO 39  // T_OUT
#define XPT2046_CLK 25   // T_CLK
#define XPT2046_CS 33    // T_CS

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);

#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320

// SET VARIABLE TO 0 FOR THE TEMPERATURE DEGREES SYMBOL IN FAHRENHEIT
#define TEMP_CELSIUS 1  

// Touchscreen coordinates: (x, y) and pressure (z)
int x, y, z;

#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

// Define a queue handle
QueueHandle_t esp_now_queue;

// Structure to receive data, must match the sender structure
typedef struct {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

static lv_obj_t * table1;
static lv_obj_t * table2;

// Callback function executed when data is received
void OnDataRecv(const esp_now_recv_info *recv_info, const uint8_t *incomingData, int len) {
  // Copy incoming data to myData structure
  struct_message myData;
  memcpy(&myData, incomingData, sizeof(myData));
  xQueueSendFromISR(esp_now_queue, &myData, NULL); // Send to queue from ISR

  // Get sender's MAC address from recv_info
  const uint8_t *mac_addr = recv_info->src_addr;
  
  // Convert sender's MAC address to a string for display
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  
  // Print received data to Serial Monitor
  Serial.println("Received data:");
  Serial.print("  Sender MAC: ");
  Serial.println(macStr);
  Serial.print("  Board ID: ");
  Serial.println(myData.id);
  Serial.print("  Temperature: ");
  Serial.print(myData.temp);
  Serial.println(" °C");
  Serial.print("  Humidity: ");
  Serial.print(myData.hum);
  Serial.println(" %");
  Serial.print("  Reading ID: ");
  Serial.println(myData.readingId);
  Serial.println("-------------------");
}

// If logging is enabled, it will inform the user about what is happening in the library
void log_print(lv_log_level_t level, const char * buf) {
  LV_UNUSED(level);
  Serial.println(buf);
  Serial.flush();
}

// Get the Touchscreen data
void touchscreen_read(lv_indev_t * indev, lv_indev_data_t * data) {
  // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z)
  if(touchscreen.tirqTouched() && touchscreen.touched()) {
    // Get Touchscreen points
    TS_Point p = touchscreen.getPoint();

    // Advanced Touchscreen calibration, LEARN MORE » https://Raspberryme.com/touchscreen-calibration/
    float alpha_x, beta_x, alpha_y, beta_y, delta_x, delta_y;

    // REPLACE WITH YOUR OWN CALIBRATION VALUES » https://Raspberryme.com/touchscreen-calibration/
    alpha_x = -0.000;
    beta_x = 0.090;
    delta_x = -33.771;
    alpha_y = 0.066;
    beta_y = 0.000;
    delta_y = -14.632;

    x = alpha_y * p.x + beta_y * p.y + delta_y;
    // clamp x between 0 and SCREEN_WIDTH - 1
    x = max(0, x);
    x = min(SCREEN_WIDTH - 1, x);

    y = alpha_x * p.x + beta_x * p.y + delta_x;
    // clamp y between 0 and SCREEN_HEIGHT - 1
    y = max(0, y);
    y = min(SCREEN_HEIGHT - 1, y);

    // Basic Touchscreen calibration points with map function to the correct width and height
    //x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
    //y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);

    z = p.z;

    data->state = LV_INDEV_STATE_PRESSED;

    // Set the coordinates
    data->point.x = x;
    data->point.y = y;

    // Print Touchscreen info about X, Y and Pressure (Z) on the Serial Monitor
    Serial.print("X = ");
    Serial.print(x);
    Serial.print(" | Y = ");
    Serial.print(y);
    Serial.print(" | Pressure = ");
    Serial.print(z);
    Serial.println();
  }
  else {
    data->state = LV_INDEV_STATE_RELEASED;
  }
}

void lv_create_main_gui(void) {
  // Create a Tab view object
  lv_obj_t * tabview;
  tabview = lv_tabview_create(lv_screen_active());
  lv_tabview_set_tab_bar_size(tabview, 40);
  
  // Add 3 tabs (the tabs are page (lv_page) and can be scrolled
  lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "BOARD #1");
  lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "BOARD #2");

  table1 = lv_table_create(tab1);
  lv_table_set_cell_value(table1, 0, 0, "Temperature");
  lv_table_set_cell_value(table1, 1, 0, "Humidity");
  lv_table_set_cell_value(table1, 2, 0, "Reading ID");
  lv_table_set_cell_value(table1, 0, 1, "--");
  lv_table_set_cell_value(table1, 1, 1, "--");
  lv_table_set_cell_value(table1, 2, 1, "--");

  table2 = lv_table_create(tab2);
  lv_table_set_cell_value(table2, 0, 0, "Temperature");
  lv_table_set_cell_value(table2, 1, 0, "Humidity");
  lv_table_set_cell_value(table2, 2, 0, "Reading ID");
  lv_table_set_cell_value(table2, 0, 1, "--");
  lv_table_set_cell_value(table2, 1, 1, "--");
  lv_table_set_cell_value(table2, 2, 1, "--");

  // Center the tables
  lv_obj_center(table1);
  lv_obj_center(table2);
}

void update_table_values(struct_message *myData) {
  #if TEMP_CELSIUS
    const char degree_symbol[] = "\u00B0C";
  #else
    const char degree_symbol[] = "\u00B0F";
  #endif

  String temp_value = String(myData->temp) + degree_symbol;
  String humi_value = String(myData->hum) + "%";

  if(myData->id == 1) {
    lv_table_set_cell_value(table1, 0, 1, temp_value.c_str());
    lv_table_set_cell_value(table1, 1, 1, humi_value.c_str());
    lv_table_set_cell_value(table1, 2, 1, String(myData->readingId).c_str());
  }
  else if(myData->id == 2) {
    lv_table_set_cell_value(table2, 0, 1, temp_value.c_str());
    lv_table_set_cell_value(table2, 1, 1, humi_value.c_str());
    lv_table_set_cell_value(table2, 2, 1, String(myData->readingId).c_str());
  }
}

void setup() {
  String LVGL_Arduino = String("LVGL Library Version: ") + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
  Serial.begin(115200);
  Serial.println(LVGL_Arduino);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Initialize ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Start LVGL
  lv_init();
  // Register print function for debugging
  lv_log_register_print_cb(log_print);

  // Start the SPI for the touchscreen and init the touchscreen
  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  touchscreen.begin(touchscreenSPI);
  // Set the Touchscreen rotation in landscape mode
  // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 0: touchscreen.setRotation(0);
  touchscreen.setRotation(2);

  // Create a display object
  lv_display_t * disp;
  // Initialize the TFT display using the TFT_eSPI library
  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
  
  // Initialize an LVGL input device object (Touchscreen)
  lv_indev_t * indev = lv_indev_create();
  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
  // Set the callback function to read Touchscreen input
  lv_indev_set_read_cb(indev, touchscreen_read);

  // Function to draw the GUI
  lv_create_main_gui();

  // Register callback function to handle received data
  esp_now_register_recv_cb(OnDataRecv);
  
  esp_now_queue = xQueueCreate(10, sizeof(struct_message)); // Queue for 10 messages
  if (esp_now_queue == NULL) {
    Serial.println("Error creating queue");
    return;
  }
}

void loop() {
  lv_task_handler();  // let the GUI do its work
  lv_tick_inc(5);     // tell LVGL how much time has passed
  delay(5);           // let this time pass
  
  struct_message myData;
  if (xQueueReceive(esp_now_queue, &myData, 0) == pdTRUE) {
    // Process and display the data
    update_table_values(&myData);
  }
}

Afficher le code brut

Nous couvrons comment créer des onglets, des champs de texte, des boutons et bien plus encore dans notre Ebook LVGL pour l’ESP32. Consultez-le ici: Apprenez LVGL: Build GUIS pour les projets ESP32 (ebook).

Comment fonctionne le code?

Maintenant, examinons rapidement le fonctionnement du code ou passons à la section de démonstration.

Y compris les bibliothèques

Dans tous vos croquis LVGL, vous devez inclure le lvgl.h et le Tft_espi.h bibliothèques à afficher à l’écran.

#include 
#include 

Nous utilisons également l’écran tactile pour choisir entre les onglets. Nous devons donc inclure la bibliothèque à écran tactile.

#include 

Vous devez inclure le Wifi et ESP_NOW bibliothèques pour utiliser ESP-Now.

#include 
#include 

Dans notre code, nous utiliserons les files d’attente pour recevoir des données via ESP-Now tout en mettant à jour l’affichage simultanément.

#include 

Initialiser l’écran tactile

Les lignes suivantes définissent l’épingle à écran tactile:

#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33

Créer un tactile et écran tactile Instances:

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);

Définition de la largeur et de la hauteur de l’affichage

Vous devez définir la largeur et la hauteur de votre affichage dans tous vos croquis qui utilisent LVGL. Si vous utilisez l’écran recommandé, la taille est de 240 × 320.

#define SCREEN_WIDTH 241
#define SCREEN_HEIGHT 320

Variables à écran tactile X, Y et Z

Définissez les variables pour maintenir les coordonnées tactiles et la pression tactile.

// Touchscreen coordinates: (x, y) and pressure (z)
int x, y, z;

Création d’un tampon de dessin

Vous devez créer un tampon pour dessiner sur l’écran comme suit:

#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

Créer une file d’attente

Créer une nouvelle file d’attente appelée ESP_NOW_QUEUE que nous utiliserons plus tard pour gérer les tâches pour recevoir des messages ESP-Now et mettre à jour l’affichage.

// Define a queue handle
QueueHandle_t esp_now_queue;

Structure de données

Créez une structure de données pour recevoir les données de l’expéditeur. Cela doit être le même envoyé par la carte des expéditeurs.

// Structure to receive data, must match the sender structure
typedef struct {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

Objets LVGL pour les tables

Dans ce projet, nous créerons deux tables. Un pour afficher les données de chaque carte. Nous créons deux objets LVGL à l’échelle mondiale qui seront ensuite utilisés pour désigner les tables.

static lv_obj_t * table1;
static lv_obj_t * table2;

Fonction de rappel ONDATARECV () ESP-NOW

Le ondatarecv () La fonction s’exécutera lorsque la carte recevra de nouvelles données via ESP-Now.

// Callback function executed when data is received
void OnDataRecv(const esp_now_recv_info *recv_info, const uint8_t *incomingData, int len) {
  // Copy incoming data to myData structure
  struct_message myData;
  memcpy(&myData, incomingData, sizeof(myData));
  xQueueSendFromISR(esp_now_queue, &myData, NULL); // Send to queue from ISR

  // Get sender's MAC address from recv_info
  const uint8_t *mac_addr = recv_info->src_addr;
  
  // Convert sender's MAC address to a string for display
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  
  // Print received data to Serial Monitor
  Serial.println("Received data:");
  Serial.print("  Sender MAC: ");
  Serial.println(macStr);
  Serial.print("  Board ID: ");
  Serial.println(myData.id);
  Serial.print("  Temperature: ");
  Serial.print(myData.temp);
  Serial.println(" °C");
  Serial.print("  Humidity: ");
  Serial.print(myData.hum);
  Serial.println(" %");
  Serial.print("  Reading ID: ");
  Serial.println(myData.readingId);
  Serial.println("-------------------");
}

Dans cette fonction, nous obtenons les données reçues et les enregistrons dans le mydata variable.

// Copy incoming data to myData structure
struct_message myData;
memcpy(&myData, incomingData, sizeof(myData));

Ensuite, nous envoyons le mydata variable à la file d’attente d’une manière sûre (non bloquante).

xQueueSendFromISR(esp_now_queue, &myData, NULL); // Send to queue from ISR

Fondamentalement, la fonction xqueuendFromisr (esp_now_queue, & mydata, null) Envoie des données (mydata) à une file d’attente de Freertos (ESP_NOW_QUEUE). Ensuite, nous utiliserons ces données sur une fonction qui affiche les lectures du capteur à l’écran.

Ensuite, nous imprimons les données reçues dans le moniteur série.

Serial.println("Received data:");
Serial.print("  Sender MAC: ");
Serial.println(macStr);
Serial.print("  Board ID: ");
Serial.println(myData.id);
Serial.print("  Temperature: ");
Serial.print(myData.temp);
Serial.println(" °C");
Serial.print("  Humidity: ");
Serial.print(myData.hum);
Serial.println(" %");
Serial.print("  Reading ID: ");
Serial.println(myData.readingId);
Serial.println("-------------------");

Fonction de débogage

Pour déboguer avec la bibliothèque LVGL, vous devez utiliser le log_print () fonction. Il est défini ci-dessous. Incluez-le dans tous vos croquis avant le installation().

// If logging is enabled, it will inform the user about what is happening in the library
void log_print(lv_log_level_t level, const char * buf) {
  LV_UNUSED(level);
  Serial.println(buf);
  Serial.flush();
}

Fonction de rappel à écran tactile

Le Touch Screen_Read () La fonction de rappel sera appelée lorsque le toucher est détecté sur l’écran tactile.

// Get the Touchscreen data
void touchscreen_read(lv_indev_t * indev, lv_indev_data_t * data) {
  // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z)
  if(touchscreen.tirqTouched() && touchscreen.touched()) {
    // Get Touchscreen points
    TS_Point p = touchscreen.getPoint();


(....)
  }
}

Fonction principale LVGL – dessin de l’interface graphique

Le lv_create_main_gui () La fonction dessinera l’interface graphique sur l’écran. Cette fonction sera appelée plus tard dans le installation.

Tout d’abord, nous créons un tabview où nous ajouterons nos onglets – un onglet pour chaque carte.

// Create a Tab view object
lv_obj_t * tabview;
tabview = lv_tabview_create(lv_screen_active());
lv_tabview_set_tab_bar_size(tabview, 40);

Ensuite, nous créons les onglets que nous voulons afficher et les ajouter à la tabview.

lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "BOARD #1");
lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "BOARD #2");

Ensuite, nous créons une table appelée tableau 1 et l’ajouter à tab1.

table1 = lv_table_create(tab1);

Après cela, nous créons les cellules pour la table.

lv_table_set_cell_value(table1, 0, 0, "Temperature");
lv_table_set_cell_value(table1, 1, 0, "Humidity");
lv_table_set_cell_value(table1, 2, 0, "Reading ID");
lv_table_set_cell_value(table1, 0, 1, "--");
lv_table_set_cell_value(table1, 1, 1, "--");
lv_table_set_cell_value(table1, 2, 1, "--");

Nous suivons une procédure similaire pour l’autre tableau.

table2 = lv_table_create(tab2);
lv_table_set_cell_value(table2, 0, 0, "Temperature");
lv_table_set_cell_value(table2, 1, 0, "Humidity");
lv_table_set_cell_value(table2, 2, 0, "Reading ID");
lv_table_set_cell_value(table2, 0, 1, "--");
lv_table_set_cell_value(table2, 1, 1, "--");
lv_table_set_cell_value(table2, 2, 1, "--");

Après avoir créé les tables, nous les centrons au milieu de l’écran.

// Center the tables
lv_obj_center(table1);
lv_obj_center(table2);

Mettre à jour le tableau

Pour mettre à jour les valeurs sur la table, nous appellerons plus tard le update_table_values ​​() fonction.

void update_table_values(struct_message *myData) {

Nous vérifions d’abord l’ID du conseil d’administration pour déterminer à partir de quelle carte nous avons reçu les données, puis mettons à jour le tableau correspondant en conséquence.

if(myData->id == 1) {
  lv_table_set_cell_value(table1, 0, 1, temp_value.c_str());
  lv_table_set_cell_value(table1, 1, 1, humi_value.c_str());
  lv_table_set_cell_value(table1, 2, 1, String(myData->readingId).c_str());
}
else if(myData->id == 2) {
  lv_table_set_cell_value(table2, 0, 1, temp_value.c_str());
  lv_table_set_cell_value(table2, 1, 1, humi_value.c_str());
  lv_table_set_cell_value(table2, 2, 1, String(myData->readingId).c_str());
}

Pour en savoir plus sur la création et la gestion des tables dans LVGL, vous pouvez vérifier ce projet:

installation()

Dans le installation()incluez les lignes suivantes pour le débogage. Ceux-ci imprimeront la version de LVGL que vous utilisez. Vous devez utiliser la version 9.

String LVGL_Arduino = String("LVGL Library Version: ") + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.begin(115200);
Serial.println(LVGL_Arduino);

Initialiser ESP-now

Activez l’interface Wi-Fi et initialisez ESP-Now.

// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);

// Initialize ESP-NOW
if (esp_now_init() != ESP_OK) {
  Serial.println("Error initializing ESP-NOW");
  return;
}

Initialiser la bibliothèque LVGL

Initialiser la bibliothèque LVGL en appelant le lv_init () fonction dans le installation().

// Start LVGL
lv_init();

Enregistrer la fonction de débogage

Enregistrez votre log_print () Fonction déclarée précédemment comme une fonction associée au débogage LVGL.

// Register print function for debugging
lv_log_register_print_cb(log_print);

Démarrer l’écran tactile

Démarrez le SPI pour l’écran tactile et initialisez l’écran tactile.

touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
touchscreen.begin(touchscreenSPI);
touchscreen.setRotation(2);

Remarque: Dans certains écrans, l’écran tactile peut être à l’envers, vous devrez donc peut-être régler la rotation sur 0: TouchScreen.SetRotation (0);

Création d’un objet d’affichage

Créez l’objet d’affichage et initialisez l’affichage TFT à l’aide du Tft_espi bibliothèque.

  // Create a display object
  lv_display_t * disp;
  // Initialize the TFT display using the TFT_eSPI library
  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);

Initialisez un objet de périphérique d’entrée LVGL (écran tactile) et définissez la fonction de rappel qui sera déclenchée lorsque vous cliquez sur l’écran tactile – le Touch Screen_Read () fonction que nous avons créée précédemment.

// Initialize an LVGL input device object (Touchscreen)
lv_indev_t * indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
// Set the callback function to read Touchscreen input
lv_indev_set_read_cb(indev, touchscreen_read);

Dessiner l’interface graphique

Appeler le lv_create_main_gui () Fonction pour dessiner l’interface graphique pour votre écran tactile:

lv_create_main_gui();

Fonction de rappel ESP-NOW

Définissez la fonction à exécuter lorsque la carte reçoit des données via ESP-Now. Dans ce cas, il exécutera le Ondatarecv () fonction que nous avons déjà créée précédemment.

// Register callback function to handle received data
esp_now_register_recv_cb(OnDataRecv);

Créer une file d’attente

Le code esp_now_queue = xqueuecreate (10, sizeof (struct_message))) Crée une file d’attente Freertos pour contenir jusqu’à 10 messages de type ESP-Now struct_message pour la communication entre un ISR (la fonction de rappel ESP-Now) et une tâche.

esp_now_queue = xQueueCreate(10, sizeof(struct_message)); // Queue for 10 messages
if (esp_now_queue == NULL) {
  Serial.println("Error creating queue");
  return;
}

Mise à jour des valeurs du tableau dans la boucle

Cette ligne if (xqueUereceive (esp_now_queue, & mydata, 0) == pdtrue) vérifie si un message est disponible en ESP_NOW_QUEUE sans attendre, le stocker dans mydata en cas de succès. Si un message est reçu, il appelle Update_table_values ​​(& mydata) Pour traiter et afficher les données à l’écran.

if (xQueueReceive(esp_now_queue, &myData, 0) == pdTRUE) {
  // Process and display the data
  update_table_values(&myData);
}

Cette méthode garantit que nous ne bloquons pas la fonction de rappel ESP-Now lors de l’écriture à l’affichage.

Démonstration

Téléchargez le code sur votre carte CYD. Allez dans Tools> Board et sélectionnez ESP32> Module Dev ESP32. Ensuite, sélectionnez le port COM droit dans les outils> Port.

Allez dans Tools> Schéma de partition> Choisissez tout ce qui a plus de 1,4 Mo d’application, par exemple: «Énorme application (3 Mo no ota / 1Mb spiffs».

Sélectionnez un énorme menu d'outils Arduino IDE Scheme

Enfin, cliquez sur le bouton Télécharger.

Bouton Arduino Ide 2 Télécharger

Après quelques minutes, la carte CYD commencera à recevoir les messages des cartes des expéditeurs via ESP-Now.

ESP32 CYD Board affichant les données reçues via ESP-Now sur un tableau

Les données reçues de chaque carte seront affichées sur un tableau. Nous avons divisé l’écran en deux onglets. Chaque onglet affiche une table pour chaque carte.

Pour changer entre les onglets, vous pouvez glisser à gauche / à droite, ou appuyez sur le nom de l’onglet en haut.

ESP32 CYD Board - glisser entre les onglets

Emballage

Dans ce tutoriel, vous avez appris à utiliser ESP-Now avec la carte CYD pour recevoir des données de plusieurs planches ESP32.

De la même manière, vous pouvez également envoyer des données à plusieurs cartes ESP32. La carte CYD peut agir comme une télécommande avec des boutons qui contrôlent les autres planches sans fil.

Nous espérons que vous avez trouvé ce tutoriel utile. Si vous souhaitez en savoir plus sur la création d’interfaces utilisateur graphiques à l’aide de la bibliothèque LVGL avec l’ESP32, consultez notre dernier ebook:

Autres guides que vous pourriez aimer lire:

Pour en savoir plus sur l’ESP32, assurez-vous de jeter un œil à nos ressources:

Merci d’avoir lu.

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

Youtube video

  • ELEGOO 3PCS Carte de Développement ESP-32 Type-C, 2,4 GHz WiFi + Bluetooth
  • DIYmalls 2,8" ESP32 Écran ESP32-2432S028R résistif Tactile 240x320 TFT LCD Module ESP-32 + boîtier en Acrylique pour Arduino IDE