ESP32 TFT avec LVGL : affichage des données du capteur BME280

ESP32 TFT avec LVGL : affichage des données du capteur BME280

Dans ce guide, vous apprendrez à créer une table réactive en utilisant LVGL avec un ESP32 et un écran TFT. Nous afficherons la température, l’humidité et la pression du capteur BME280, ainsi que la luminosité du LDR sur l’écran. Nous afficherons également l’horodatage associé aux lectures (date et heure). L’ESP32 sera programmé à l’aide de l’IDE Arduino.

Écran TFT ESP32, lectures du tableau BME280, LVGL Arduino

Utilisez-vous une carte CYD ? Lisez ce guide : ESP32 CYD avec LVGL – Afficher les données du capteur BME280 sur une table

Aperçu du projet

Dans ce projet, nous allons créer un tableau avec les lectures du capteur BME280 et du LDR sur un écran tactile LCD TFT ILI9341 de 2,8 pouces (240 × 320). Nous afficherons également la date et l’heure, ainsi que l’adresse IP de votre carte.

Pour obtenir une date et une heure précises pour votre fuseau horaire, nous utiliserons WorldTimeAPI. Pour obtenir l’heure de l’API, l’ESP32 doit se connecter à Internet, vous devez donc disposer d’un routeur à proximité pour que l’ESP32 puisse s’y connecter.

Si vous ne disposez pas d’une connexion Internet sur laquelle l’ESP32 fonctionnera, vous pouvez omettre la section date et heure ou utiliser un module RTC.

Nous afficherons également un bouton flottant avec l’icône d’actualisation qui, une fois cliqué, mettra à jour les valeurs du tableau.

Conditions préalables

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

1) Pièces requises

Pour ce projet, vous avez besoin des pièces suivantes :

2) Installez les cartes ESP32 dans l’IDE Arduino

Logo ArduinoIDE2

Nous allons programmer l’ESP32 à l’aide de l’IDE Arduino. Assurez-vous que les cartes ESP32 sont installées. Suivez le tutoriel suivant :

3) Familiarisez-vous avec l’écran tactile LCD TFT ILI9341

L’écran que nous utilisons dans ce guide est le 2.8. pouces TFT LCD qui est également livré avec un écran tactile. L’écran communique via le protocole de communication SPI et utilise le pilote ILI9341. L’écran tactile utilise également le protocole de communication SPI.

L’écran tactile TFT LCD est également livré avec une interface de carte SD si vous devez charger des fichiers pour votre projet spécifique. Cet écran est également disponible avec différentes tailles d’écran, mais nous utiliserons celle de 240 x 320 pixels).

Écran tactile LCD TFT ILI9341

Si c’est la première fois que vous utilisez cet écran, assurez-vous de suivre notre guide de démarrage :

4) Câblez l’écran à l’ESP32

Câblez les broches de l’écran LCD TFT et de l’écran tactile aux GPIO ESP32 selon le tableau suivant (vous devez utiliser ces broches exactes, sinon le projet ne fonctionnera pas).

Câblage de l'écran tactile TFT LCD à l'ESP32
Écran tactile LCD TFT ESP32
T_IRQ GPIO36
T_OUT GPIO39
T_DIN GPIO32
T_CS GPIO33
T_CLK GPIO25
SDO(MISO) GPIO12
DIRIGÉ GPIO21
SCK GPIO14
IDS(MOSI) GPIO13
D/C GPIO2
RÉINITIALISER FR/RESET
CS GPIO15
GND GND
VCC 5V (ou 3,3V)*

* Dans la broche VCC, vous pouvez utiliser 5V ou 3,3V selon si votre connexion J1 est ouverte ou fermée (par défaut elle est généralement ouverte comme vous pouvez le voir sur la figure ci-dessous).

VCC = 5V | J1=OPEN
VCC = 3.3V | J1=CLOSE
Écran tactile LCD TFT Connexion J1

5) Installez les bibliothèques TFT et LVGL

LVGL (Light and Versatile Graphics Library) 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ôleurs nécessitant une interface utilisateur graphique (GUI).

LVGL nouveau logo

Suivez le didacticiel suivant pour installer et configurer les bibliothèques requises pour utiliser LVGL avec l’écran tactile LCD TFT ILI9341 240 × 320 de 2,8 pouces à l’aide de l’IDE Arduino.

6) Installez les bibliothèques ArduinoJson et BME280

Pour ce projet, vous devez installer la bibliothèque ArduinoJSON pour gérer la réponse JSON lorsque vous effectuez une requête auprès de WorldTimeAPI.

Dans l’IDE Arduino, accédez à Sketch > Inclure la bibliothèque > Gérer les bibliothèques. Recherchez ArduinoJSON et installez la bibliothèque de Benoit Blanchon. Nous utilisons la version 7.0.4. Nous vous recommandons d’utiliser la même version.

1730944915 77 ESP32 TFT avec LVGL affichage des donnees du capteur BME280

Nous utiliserons la bibliothèque Adafruit BME280 pour obtenir les données du BME280. Dans l’IDE Arduino, accédez à Sketch > Inclure la bibliothèque > Gérer les bibliothèques. Recherchez la bibliothèque Adafruit BME280 dans le champ de recherche et installez la bibliothèque. Installez également toutes les dépendances actuellement non installées (généralement les bibliothèques Adafruit Bus IO et Adafruit Unified Sensor).

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

ESP32 TFT : afficher les lectures du capteur BME280 sur une table – Code Arduino

Le code suivant créera le tableau et affichera les lectures du capteur. Avant de télécharger le code sur votre carte, vous devez insérer vos informations d’identification réseau afin que l’ESP32 puisse se connecter à Internet pour obtenir l’heure. Vous devez également insérer votre fuseau horaire.

/*  Rui Santos & Sara Santos - Raspberryme.com - https://Raspberryme.com/esp32-cyd-lvgl-display-bme280-data-table/   |   https://Raspberryme.com/esp32-tft-lvgl-display-bme280-data-table/
    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 

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Specify the timezone you want to get the time for
const char* timezone = "Europe/Lisbon";

// Store date and time
String current_date;
String current_time;

// Install Adafruit Unified Sensor and Adafruit BME280 Library
#include 
#include 
#include 
#define I2C_SDA 27
#define I2C_SCL 22
#define SEALEVELPRESSURE_HPA (1013.25)
TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

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

#define LDR_PIN 34

// 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

// 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];

// 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;
  }
}

static void float_button_event_cb(lv_event_t * e) {
  update_table_values();
}

static lv_obj_t * table;
static void update_table_values(void) {
  // Get the latest temperature reading in Celsius or Fahrenheit
  #if TEMP_CELSIUS
    float bme_temp = bme.readTemperature();
    const char degree_symbol[] = "\u00B0C";
  #else
    float bme_temp = 1.8 * bme.readTemperature() + 32;
    const char degree_symbol[] = "\u00B0F";
  #endif
  
  String bme_temp_value = String(bme_temp) + degree_symbol;
  String bme_humi_value = String(bme.readHumidity()) + "%";
  String bme_press_value = String(bme.readPressure() / 100.0F) + " hPa";
  String ldr_value = String(analogRead(LDR_PIN));
  
  // Get the time from WorldTimeAPI
  get_date_and_time();
  //Serial.println("Current Date: " + current_date);
  //Serial.println("Current Time: " + current_time);

  // Fill the first column
  lv_table_set_cell_value(table, 0, 0, "Data");
  lv_table_set_cell_value(table, 1, 0, "Temperature");
  lv_table_set_cell_value(table, 2, 0, "Humidity");
  lv_table_set_cell_value(table, 3, 0, "Pressure");
  lv_table_set_cell_value(table, 4, 0, "Luminosity");
  lv_table_set_cell_value(table, 5, 0, "Date");
  lv_table_set_cell_value(table, 6, 0, "Time");
  lv_table_set_cell_value(table, 7, 0, "IP Address");

  // Fill the second column
  lv_table_set_cell_value(table, 0, 1, "Value");
  lv_table_set_cell_value(table, 1, 1, bme_temp_value.c_str());
  lv_table_set_cell_value(table, 2, 1, bme_humi_value.c_str());
  lv_table_set_cell_value(table, 3, 1, bme_press_value.c_str());
  lv_table_set_cell_value(table, 4, 1, ldr_value.c_str());
  lv_table_set_cell_value(table, 5, 1, current_date.c_str());
  lv_table_set_cell_value(table, 6, 1, current_time.c_str());
  lv_table_set_cell_value(table, 7, 1, WiFi.localIP().toString().c_str());
}

static void draw_event_cb(lv_event_t * e) {
  lv_draw_task_t * draw_task = lv_event_get_draw_task(e);
  lv_draw_dsc_base_t * base_dsc = (lv_draw_dsc_base_t*) draw_task->draw_dsc;
  // If the cells are drawn
  if(base_dsc->part == LV_PART_ITEMS) {
    uint32_t row = base_dsc->id1;
    uint32_t col = base_dsc->id2;

    // Make the texts in the first cell center aligned
    if(row == 0) {
      lv_draw_label_dsc_t * label_draw_dsc = lv_draw_task_get_label_dsc(draw_task);
      if(label_draw_dsc) {
        label_draw_dsc->align = LV_TEXT_ALIGN_CENTER;
      }
      lv_draw_fill_dsc_t * fill_draw_dsc = lv_draw_task_get_fill_dsc(draw_task);
      if(fill_draw_dsc) {
        fill_draw_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_BLUE), fill_draw_dsc->color, LV_OPA_20);
        fill_draw_dsc->opa = LV_OPA_COVER;
      }
    }
    // In the first column align the texts to the right
    else if(col == 0) {
      lv_draw_label_dsc_t * label_draw_dsc = lv_draw_task_get_label_dsc(draw_task);
      if(label_draw_dsc) {
        label_draw_dsc->align = LV_TEXT_ALIGN_RIGHT;
      }
    }

    // Make every 2nd row gray color
    if((row != 0 && row % 2) == 0) {
      lv_draw_fill_dsc_t * fill_draw_dsc = lv_draw_task_get_fill_dsc(draw_task);
      if(fill_draw_dsc) {
        fill_draw_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_GREY), fill_draw_dsc->color, LV_OPA_10);
        fill_draw_dsc->opa = LV_OPA_COVER;
      }
    }
  }
}

void lv_create_main_gui(void) {
  table = lv_table_create(lv_screen_active());

  // Inserts or updates all table values
  update_table_values();

  // Set a smaller height to the table. It will make it scrollable
  lv_obj_set_height(table, 200);
  lv_obj_center(table);

  // Add an event callback to apply some custom drawing
  lv_obj_add_event_cb(table, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
  lv_obj_add_flag(table, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);

  // Create floating button
  lv_obj_t * float_button = lv_button_create(lv_screen_active());
  lv_obj_set_size(float_button, 50, 50);
  lv_obj_add_flag(float_button, LV_OBJ_FLAG_FLOATING);
  lv_obj_align(float_button, LV_ALIGN_BOTTOM_RIGHT, -15, -15);
  lv_obj_add_event_cb(float_button, float_button_event_cb, LV_EVENT_CLICKED, NULL);
  lv_obj_set_style_radius(float_button, LV_RADIUS_CIRCLE, 0);
  lv_obj_set_style_bg_image_src(float_button, LV_SYMBOL_REFRESH, 0);
  lv_obj_set_style_text_font(float_button, lv_theme_get_font_large(float_button), 0);
  lv_obj_set_style_bg_color(float_button, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN);
}

void get_date_and_time() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;

    // Construct the API endpoint
    String url = String("http://worldtimeapi.org/api/timezone/") + timezone;

    http.begin(url);

    int httpCode = http.GET(); // Make the GET request

    if (httpCode > 0) {
      // Check for the response
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        //Serial.println("Time information:");
        //Serial.println(payload);
        
        // Parse the JSON to extract the time
        JsonDocument doc;
        DeserializationError error = deserializeJson(doc, payload);

        if (!error) {
          const char* datetime = doc["datetime"];
          //Serial.println("Datetime: " + String(datetime));
          
          // Split the datetime into date and time
          String datetimeStr = String(datetime);
          int splitIndex = datetimeStr.indexOf('T');
          current_date = datetimeStr.substring(0, splitIndex);
          current_time = datetimeStr.substring(splitIndex + 1, splitIndex + 9); // Extract time portion

        } else {
          Serial.print("deserializeJson() failed: ");
          Serial.println(error.c_str());
        }
      }
    } else {
      Serial.printf("GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end(); // Close connection
  } else {
    Serial.println("Not connected to Wi-Fi");
  }
}

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 analog read resolution
  analogReadResolution(12);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("\nConnected to Wi-Fi network with IP Address: ");
  Serial.println(WiFi.localIP());

  I2CBME.begin(I2C_SDA, I2C_SCL, 100000);
  bool status;
  // Passing a &Wire2 to set custom I2C ports
  status = bme.begin(0x76, &I2CBME);
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
  
  // 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();
}

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
}

Afficher le code brut

Comment fonctionne le code ?

Voyons comment obtenir l’heure sur Internet et afficher toutes les données sur un tableau. Vous pouvez également passer directement à la section Démonstration.

Y compris les bibliothèques

Vous devez inclure les bibliothèques lvgl, TFT_eSPI et XPT2046_Touchscreen pour communiquer et afficher du texte à l’écran.

#include 
#include 
#include 

Vous devez inclure les bibliothèques WiFi, HTTPClient et ArduinoJson pour effectuer des requêtes HTTP et gérer les données JSON.

#include 
#include 
#include 

Pour utiliser le BME280, nous devons inclure les bibliothèques suivantes.

#include 
#include 
#include 

Insérez vos informations d’identification et votre fuseau horaire

Dans les lignes suivantes, vous devez insérer vos identifiants réseau afin que l’ESP32 puisse se connecter à votre routeur.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Définissez votre fuseau horaire dans la variable timezone au début du code (liste de tous les fuseaux horaires disponibles).

const char* timezone = "Europe/Lisbon";

Déclaration d’autres variables

Nous devons créer une instance I2C sur des broches I2C personnalisées et un objet Adafruit_BME280 pour faire référence au capteur.

#define I2C_SDA 27
#define I2C_SCL 22
#define SEALEVELPRESSURE_HPA (1013.25)
TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

Notre code est prêt à afficher la température en degrés Celsius ou Fahrenheit. Pour choisir l’unité souhaitée, vous pouvez définir la valeur de la variable TEMP_CELSIUS. Il est réglé sur 1 par défaut pour afficher la température en degrés Celsius.

#define TEMP_CELSIUS 1

Si vous souhaitez plutôt afficher en degrés Fahrenheit, réglez-le sur 0.

#define TEMP_CELSIUS 0

Créez un objet table LVGL, afin que nous puissions y accéder ultérieurement dans toutes les fonctions.

static lv_obj_t * table;

installation()

Dans setup(), 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);

Connectez-vous à Internet

Pour connecter l’ESP32 à Internet, nous utilisons le code suivant.

// Connect to Wi-Fi
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.print("\nConnected to Wi-Fi network with IP Address: ");
Serial.println(WiFi.localIP());

Initialisation BME280

Les lignes suivantes initialisent le capteur sur le bus I2C créé :

I2CBME.begin(I2C_SDA, I2C_SCL, 100000);
bool status;
// Passing a &Wire2 to set custom I2C ports
status = bme.begin(0x76, &I2CBME);
if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}  

Initialiser la bibliothèque LVGL

Initialisez la bibliothèque LVGL en appelant la fonction lv_init() dans setup().

// Start LVGL
lv_init();

Fonction de débogage d’enregistrement

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

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

Préparation de l’écran tactile

Initialisez l’écran tactile.

// 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);

Calibrage de l’écran tactile

Sur la fonction touchscreen_read(), nous ajustons les points de l’écran tactile pour les calibrer avec l’affichage. Vous devez insérer les valeurs d’étalonnage sur les lignes suivantes. Consultez ce didacticiel pour obtenir les valeurs d’étalonnage de votre écran.

// 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;

Créer un objet d’affichage

Pour écrire sur l’affichage, vous devez d’abord créer un objet d’affichage. Vous devez le faire dans tous vos croquis LVGL. Les lignes suivantes créeront un objet d’affichage LVGL appelé disp avec la largeur d’écran, la hauteur d’écran et le tampon de dessin définis précédemment.

// 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);

Dessiner l’interface graphique

La bibliothèque LVGL fonctionne de manière asynchrone. Vous devez appeler la fonction pour dessiner sur l’écran dans setup(). Ensuite, tout fonctionne avec les événements et les rappels. Le code sera toujours à l’écoute des événements en arrière-plan. Lorsque quelque chose se produit, il exécutera la fonction de rappel associée à l’événement. Vous n’avez pas besoin de vérifier les événements dans la boucle().

Dans la plupart de nos exemples, la fonction qui dessinera à l’écran sera appelée lv_create_main_gui(). Ensuite, à l’intérieur de cette fonction, nous ajouterons les instructions pour construire l’interface.

// Function to draw the GUI
lv_create_main_gui();

get_date_and_time()

Nous créons deux variables globales current_date et current_time au début du code pour enregistrer la date et l’heure actuelles.

String current_date;
String current_time;

Créez une fonction appelée get_date_and_time() qui envoie une requête à WorldTimeAPI et met à jour les variables current_date et current_time avec la date et l’heure actuelles.

void get_date_and_time() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;

    // Construct the API endpoint
    String url = String("http://worldtimeapi.org/api/timezone/") + timezone;
    http.begin(url);
    int httpCode = http.GET(); // Make the GET request

    if (httpCode > 0) {
      // Check for the response
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        //Serial.println("Time information:");
        //Serial.println(payload);
        // Parse the JSON to extract the time
        JsonDocument doc;
        DeserializationError error = deserializeJson(doc, payload);
        if (!error) {
          const char* datetime = doc["datetime"];          
          // Split the datetime into date and time
          String datetime_str = String(datetime);
          int splitIndex = datetime_str.indexOf('T');
          current_date = datetime_str.substring(0, splitIndex);
          current_time = datetime_str.substring(splitIndex + 1, splitIndex + 9); // Extract time portion
          hour = current_time.substring(0, 2).toInt();
          minute = current_time.substring(3, 5).toInt();
          second = current_time.substring(6, 8).toInt();
        } else {
          Serial.print("deserializeJson() failed: ");
          Serial.println(error.c_str());
        }
      }
    } else {
      Serial.printf("GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
      sync_time_date = true;
    }
    http.end(); // Close connection
  } else {
    Serial.println("Not connected to Wi-Fi");
  }
}

Nous n’aborderons pas le fonctionnement des requêtes HTTP. Si vous souhaitez en savoir plus, vous pouvez consulter le tutoriel suivant : ESP32 HTTP GET avec Arduino IDE.

Préparation de l’interface graphique

Nous créons la table dans la fonction lv_create_main_gui().

void lv_create_main_gui(void) {

Nous pouvons créer un objet table en utilisant la fonction lv_table_create() comme suit.

table = lv_table_create(lv_screen_active());

Ensuite, nous écrivons une fonction appelée update_table_values() qui remplit le tableau avec les lectures. Nous examinerons cette fonction dans un instant.

update_table_values();

Réglez la hauteur de la table et centrez-la sur l’écran.

// Set a smaller height to the table. It will make it scrollable
lv_obj_set_height(table, 200);
lv_obj_center(table);                                                                          

Ajoutez un rappel et un indicateur à la table. Ceux-ci nous permettront de dessiner le tableau avec un style spécifique (couleurs différentes pour les en-têtes et rangées grises et blanches alternées).

// Add an event callback to apply some custom drawing
lv_obj_add_event_cb(table, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
lv_obj_add_flag(table, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);

Création du bouton flottant

Pourtant, dans la fonction lv_create_main_gui(), nous créons le bouton flottant. Il s’agit d’un bouton ordinaire, mais il est circulaire et avec un style spécifique.

// Create floating button
lv_obj_t * float_button = lv_button_create(lv_screen_active());
lv_obj_set_size(float_button, 50, 50);
lv_obj_add_flag(float_button, LV_OBJ_FLAG_FLOATING);
lv_obj_align(float_button, LV_ALIGN_BOTTOM_RIGHT, -15, -15);
lv_obj_add_event_cb(float_button, float_button_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_set_style_radius(float_button, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_image_src(float_button, LV_SYMBOL_REFRESH, 0);
lv_obj_set_style_text_font(float_button, lv_theme_get_font_large(float_button), 0);
lv_obj_set_style_bg_color(float_button, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN);

Le bouton cliqué déclenchera la fonction float_button_event_cb.

lv_obj_add_event_cb(float_button, float_button_event_cb, LV_EVENT_CLICKED, NULL);

Pour ajouter le symbole d’actualisation au bouton, nous utilisons la fonction lv_obj_set_style_bg_image_src(). Pour ajouter le symbole d’actualisation, nous devons utiliser LV_SYMBOL_REFRESH.

lv_obj_set_style_bg_image_src(float_button, LV_SYMBOL_REFRESH, 0);

Ajout de données au tableau

C’est dans update_table_values() que nous ajouterons des données à la table.

static void update_table_values(void) {

Cette fonction est appelée lorsque nous créons l’interface graphique dans la fonction lv_create_main_gui(), et lorsque vous cliquez sur le bouton de la fonction float_button_event_cb().

Tout d’abord, nous commençons par obtenir les lectures du capteur BME280 ainsi que la luminosité du LDR.

// Get the latest temperature reading in Celsius or Fahrenheit
#if TEMP_CELSIUS
  float bme_temp = bme.readTemperature();
  const char degree_symbol[] = "\u00B0C";
#else
  float bme_temp = 1.8 * bme.readTemperature() + 32;
  const char degree_symbol[] = "\u00B0F";
#endif
  
String bme_temp_value = String(bme_temp) + degree_symbol;
String bme_humi_value = String(bme.readHumidity()) + "%";
String bme_press_value = String(bme.readPressure() / 100.0F) + " hPa";
String ldr_value = String(analogRead(LDR_PIN));

Les lectures sont enregistrées sur les variables bme_temp_value, bme_humi_value, bme_pres_value et ldr_value. Nous mettons à jour les variables temporelles en appelant la fonction get_date_and_time() que nous avons vue précédemment.

// Get the time from WorldTimeAPI
get_date_and_time();

Après avoir récupéré toutes les données, nous pouvons enfin commencer à ajouter des lignes au tableau. Pour cela, nous pouvons utiliser la fonction lv_table_set_cell_value(). Cette fonction accepte comme argument le tableau auquel vous faites référence, le numéro de ligne, le numéro de colonne et les données que vous souhaitez afficher.

Les lignes suivantes remplissent la première colonne.

// Fill the first column
lv_table_set_cell_value(table, 0, 0, "Data");
lv_table_set_cell_value(table, 1, 0, "Temperature");
lv_table_set_cell_value(table, 2, 0, "Humidity");
lv_table_set_cell_value(table, 3, 0, "Pressure");
lv_table_set_cell_value(table, 4, 0, "Luminosity");
lv_table_set_cell_value(table, 5, 0, "Date");
lv_table_set_cell_value(table, 6, 0, "Time");
lv_table_set_cell_value(table, 7, 0, "IP Address");

Enfin, nous remplissons la deuxième colonne avec les valeurs des capteurs, la date et l’heure, ainsi que l’adresse IP de l’ESP32.

// Fill the second column
lv_table_set_cell_value(table, 0, 1, "Value");
lv_table_set_cell_value(table, 1, 1, bme_temp_value.c_str());
lv_table_set_cell_value(table, 2, 1, bme_humi_value.c_str());
lv_table_set_cell_value(table, 3, 1, bme_press_value.c_str());
lv_table_set_cell_value(table, 4, 1, ldr_value.c_str());
lv_table_set_cell_value(table, 5, 1, current_date.c_str());
lv_table_set_cell_value(table, 6, 1, current_time.c_str());
lv_table_set_cell_value(table, 7, 1, WiFi.localIP().toString().c_str());

boucle()

Dans la boucle (), vous pouvez ajouter toutes les autres tâches que votre ESP32 doit effectuer, comme dans n’importe quel croquis Arduino classique.

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
}

Démonstration

Téléchargez le code sur votre tableau. Accédez à Outils > Carte et sélectionnez ESP32 > Module de développement ESP32. Ensuite, sélectionnez le bon port COM dans Outils > Port.

Si vous voyez une erreur comme celle-ci : « Esquisse trop grande » pendant le processus de téléchargement, dans l’IDE Arduino, accédez à Outils > Schéma de partition > choisissez tout ce qui contient plus de 1,4 Mo d’application, par exemple : « Énorme application (3 Mo sans OTA/1 Mo). SPIFF ».

Sélectionnez le menu Outils de l'IDE Arduino pour le schéma de partitionnement d'applications énormes

Enfin, cliquez sur le bouton de téléchargement.

Bouton de téléchargement Arduino IDE 2

Après quelques secondes, les données actuelles s’afficheront à l’écran, comme indiqué dans l’image ci-dessous. Pour mettre à jour les données du tableau, il vous suffit de cliquer sur le bouton Actualiser.

Affichage TFT ESP32 Démonstration du tableau de données du capteur BME280 Actualiser les valeurs de mise à jour

Vous pouvez couvrir le LDR avec votre doigt pour voir les valeurs de luminosité changer.

Écran TFT ESP32 BME280, tableau de données de démonstration, capteur LDR

Le tableau est déroulant. Vous pouvez faire défiler vers le bas pour vérifier la date et l’heure de la dernière mise à jour.

Test de démonstration du tableau de données du capteur BME280 avec écran TFT ESP32

Dépannage

Si vous rencontrez des problèmes pour ouvrir le site Web WorldTimeAPI ou si votre requête HTTP ne parvient pas à récupérer l’heure, vous pouvez modifier la ligne suivante 252.

String url = String("http://worldtimeapi.org/api/timezone/") + timezone;

De leur nom de domaine (worldtimeapi.org) à leur adresse IP comme suit :

String url = String("http://213.188.196.246/api/timezone/") + timezone;

Conclusion

Dans ce didacticiel, vous avez appris à afficher les données du capteur dans un tableau LVGL réactif sur un TFT avec carte ESP32.

Nous espérons que vous avez trouvé ce tutoriel utile. Nous préparons d’autres guides sur ce forum, alors restez à l’écoute. 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 aimeriez lire :

Pour en savoir plus sur l’ESP32, assurez-vous de consulter nos ressources :

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

YouTube video

  • Ulegqin ESP32 Écran ESP32 Ecran Tactile Board avec WiFi Bluetooth 2,8" 240 x 320 Éc-ran ESP-32 Intelligent Module EC-ran TFT ESP-32 Tactile Connectivité IoT Haut débit Compatible avec Arduino/LVGL
  • Carte de développement ESP32 S3 avec écran tactile capacitif de 2,8 pouces 240 x 320 pixels, prise en charge XiaoZhi AI, ES3C28P Dual-Core 240 MHz microcontrôleur WiFi + BT, fourniture de tutoriels de