Afficher sur reTerminal E1002 les données d’un capteur Home Assistant

0
(0)

Seeed Studio vient de présenter le reTerminal E1002, un appareil prêt à l’emploi doté d’un boîtier métallique robuste et d’un écran e-paper couleur de 7,3″ affichant en 800×480 pixels. Basé sur un microcontrôleur ESP32-S3, il intègre plusieurs fonctionnalités matérielles : trois boutons, un buzzer, une LED de statut (en plus de la LED d’alimentation), un capteur de température et de pression, un microphone ainsi qu’un lecteur de carte MicroSD. L’ensemble est alimenté par une batterie interne de 2000 mAh offrant jusqu’à trois mois d’autonomie.

Cet appareil est particulièrement bien adapté pour l’affichage des données issues du capteur Zigbee que j’ai développé dans mon tutoriel Créer un capteur de température Zigbee sur batterie avec un ESP32C6 et que j’ai intégré à Home Assistant.

Le reTerminal E1002 peut être configuré et personnalisé en adoptant trois approches distinctes :

C’est cette dernière approche que je vais utiliser dans cet article, car elle a l’avantage de ne pas vous imposer les limites des deux autres approches.

Configuration de l’IDE Arduino pour le reTerminal E1002

Il faut tout d’abord intégrer le support pour les microcontrôleurs ESP32 dans l’IDE Arduino, en ajoutant l’URL suivante dans le champ Additional Boards Manager URLs du menu File > Preferences :

Plaintext
https://espressif.github.io/arduino-esp32/package_esp32_index.json

Il faut ensuite installer le package ESP32, via le menu Tools > Board > Boards Manager, en recherchant “esp32” et installant le package esp32 by Espressif Systems (disponible sur github espressif).

Téléchargez ensuite la librairie GxEPD2 dans son format ZIP :

Et l’ajouter à l’IDE Arduino via le menu Sketch > Include Library > Add .ZIP Library…

Et pour finir, installer cette librairie GxEPD2 by Jean-Marc Zingg via le menu Tools > Manage libraries.

Premier programme “Hello World”

Voici le code qui affiche le célèbre “Hello World” sur l’écran du reTerminal E1002, qu’il faut installer sur l’appareil après avoir sélectionné la carte XIAO_ESP32S3 via le menu Tools > Board > ESP32 Arduino et choisi le port sur lequel le reTerminal est connecté.

C++
#include <GxEPD2_7C.h>
#include <Fonts/FreeMonoBold12pt7b.h>

// Define ePaper SPI pins
#define EPD_SCK_PIN  7
#define EPD_MOSI_PIN 9
#define EPD_CS_PIN   10
#define EPD_DC_PIN   11
#define EPD_RES_PIN  12
#define EPD_BUSY_PIN 13

#define GxEPD2_DISPLAY_CLASS GxEPD2_7C
#define GxEPD2_DRIVER_CLASS GxEPD2_730c_GDEP073E01 // 7.3'' Color driver

#define MAX_DISPLAY_BUFFER_SIZE 16000

#define MAX_HEIGHT(EPD)                                        \
    (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) \
         ? EPD::HEIGHT                                         \
         : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))

// Initialize display object
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)>
    display(GxEPD2_DRIVER_CLASS(/*CS=*/EPD_CS_PIN, /*DC=*/EPD_DC_PIN,
                                /*RST=*/EPD_RES_PIN, /*BUSY=*/EPD_BUSY_PIN));

SPIClass hspi(HSPI);

void helloWorld()
{
  display.setRotation(0);
  display.setFont(&FreeMonoBold12pt7b);
  display.setTextColor(GxEPD_RED);
  display.setFullWindow();
  display.firstPage();
  do
  {
    display.fillScreen(GxEPD_WHITE);
    display.setCursor(100, 100);
    display.print("Hello World!");
  }
  while (display.nextPage());
}
void setup()
{
  pinMode(EPD_RES_PIN, OUTPUT);
  pinMode(EPD_DC_PIN, OUTPUT);
  pinMode(EPD_CS_PIN, OUTPUT);

  // Initialize SPI
  hspi.begin(EPD_SCK_PIN, -1, EPD_MOSI_PIN, -1);
  display.epd2.selectSPI(hspi, SPISettings(2000000, MSBFIRST, SPI_MODE0));

  // Initialize display
  display.init(0);
  helloWorld();
  display.hibernate();
}

void loop() {};

Récupération d’une donnée depuis le serveur Home Assistant

L’objectif de ce tutoriel est d’afficher sur le reTerminal E1002 la température du capteur qui est disponible dans le serveur Home Assistant. Cette donnée sera récupérée via l’API RESTful du serveur Home Assistant disponible via l’URL http://ADRESSE_IP_HA:8123/api/. Cette API accepte et renvoie uniquement les objets codés en JSON. Nous utiliserons pour cela la librairie Arduinojson by Benoit Blanchon, que nous installons depuis le menu Tools > Manage Libraries de l’IDE Arduino.

Il faut récupérer, depuis la page web de Home Assistant, l’ID de l’entité que nous souhaitons afficher sur le reTerminal. Ici par exemple, la température du capteur correspond à l’entité dont l’ID est : sensor.tutoduino_esp32c6tempsensor_temperature.

ID de l’entité de Home Assistant à récupérer

Pour pouvoir récupérer des données de Home Assistant, notre programme va avoir besoin d’un jeton (Token) de Home Assistant afin de pouvoir l’interroger via son API. Il faut le faire à partir de la page web de votre serveur Home Assistant, en allant sur la page de votre profile et en cliquant sur le bouton Créer un jeton depuis le menu Sécurité.

Création d’un Jeton dans Home Assistant

Une fois le jeton créé, il suffit de l’indiquer dans votre programme sous l’IDE Arduino. Je recommande toujours de stocker vos secrets (mot de passe wifi, jeton home assistant…) dans un fichier secret.h qui sera inclus dans votre programme C. Cela évite par exemple de les faire fuiter sur Github après un copier-coller malencontreux…

Stockage du jeton Home Assistant dans un fichier secret.h dans l’IDE Arduino

Voici un exemple de programme qui va récupérer via l’API Home Assistant la température de notre capteur et l’afficher sur l’écran du reTerminal E1002. Notez que la lecture sur le serveur Home Assistant se fait toutes les 10 minutes et qu’entre deux lectures, le reTerminal se met en veille profonde afin d’économiser sa batterie. Il est cependant possible de réveiller à tout moment le reTerminal en cliquant sur son bouton vert.

C++
/**
 * @file TemperatureDisplay.ino
 * @brief Fetches temperature data from a Home Assistant server and displays it on a reTerminal E1002 e-paper screen.
 *        The device can enter deep sleep and wake up via a button press or timer.
 */

#include <WiFi.h>           // Wi-Fi library for ESP32
#include <HTTPClient.h>     // HTTP client for making requests
#include <ArduinoJson.h>    // JSON parsing library
#include <GxEPD2_7C.h>      // E-paper display library for 7-color displays
#include <Fonts/FreeMonoBold12pt7b.h>  // Custom font for the display
#include "secrets.h"       // Custom file containing sensitive data (Wi-Fi credentials, API tokens)

// --- Pin Definitions ---
// Serial1 communication pins for debugging
#define SERIAL_RX 44
#define SERIAL_TX 43
// SPI pins for e-paper display communication
#define EPD_SCK_PIN  7   // SPI Clock pin
#define EPD_MOSI_PIN 9   // MOSI (Master Out Slave In) pin
#define EPD_CS_PIN   10  // Chip Select pin
#define EPD_DC_PIN   11  // Data/Command pin
#define EPD_RES_PIN  12  // Reset pin
#define EPD_BUSY_PIN 13  // Busy pin
// Button pin definitions according to the hardware schematic
#define GREEN_BUTTON 3   // KEY0 - GPIO3, connected to the green button

// --- E-paper Display Configuration ---
// Display class for 7.3" color e-paper
#define GxEPD2_DISPLAY_CLASS GxEPD2_7C
#define GxEPD2_DRIVER_CLASS GxEPD2_730c_GDEP073E01
// Maximum display buffer size in bytes (limits memory usage)
#define MAX_DISPLAY_BUFFER_SIZE 16000
// Calculate the maximum display height based on the buffer size
#define MAX_HEIGHT(EPD) \
  (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE * 8) / EPD::WIDTH ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE * 8) / EPD::WIDTH)

// --- Device Initialization ---
// Initialize SPI interface for the display (HSPI = Host SPI)
SPIClass hspi(HSPI);
// Initialize the display object with the specified driver and buffer size
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)>
  display(GxEPD2_DRIVER_CLASS(EPD_CS_PIN, EPD_DC_PIN, EPD_RES_PIN, EPD_BUSY_PIN));

/**
 * @brief Displays the temperature on the e-paper screen.
 * @param temperature A string representing the temperature to display.
 */
void displayTemperature(const char* temperature) {
  Serial1.print("Temperature to display: ");
  Serial1.println(temperature);

  // Configure display settings
  display.setRotation(0);  // Set display orientation to default (0 degrees)
  display.setFont(&FreeMonoBold12pt7b);  // Set font to FreeMonoBold12pt7b
  display.setTextColor(GxEPD_RED);  // Set text color to red
  display.setFullWindow();  // Use the full display window for rendering

  // Start the display update process
  display.firstPage();
  do {
    display.fillScreen(GxEPD_WHITE);  // Clear the screen with white background
    display.setCursor(100, 100);  // Set cursor position to (100, 100)
    display.print("Temperature = ");
    display.print(temperature);  // Print the temperature value
    display.print(" C");  // Print the degree symbol and 'C' for Celsius
  } while (display.nextPage());  // Repeat until the display update is complete
}

/**
 * @brief Fetches the temperature from the Home Assistant server.
 * @return A dynamically allocated string containing the temperature or "Error" on failure.
 * @note The caller is responsible for freeing the returned string using free().
 */
char* getTemperature() {
  // Check if Wi-Fi is connected
  if (WiFi.status() != WL_CONNECTED) {
    Serial1.println("ERROR: Wi-Fi not connected.");
    return strdup("Error");  // Return an error message
  }

  // Initialize HTTP client and configure the request
  HTTPClient http;
  http.begin(ha_url);  // Set the target URL from secrets.h
  http.addHeader("Authorization", "Bearer " + String(ha_token));  // Add authorization header
  http.addHeader("Content-Type", "application/json");  // Set content type to JSON

  // Send a GET request to the Home Assistant API
  int httpCode = http.GET();
  if (httpCode != HTTP_CODE_OK) {
    Serial1.print("HTTP Error: ");
    Serial1.println(httpCode);
    Serial1.println(http.getString());  // Print the error response
    http.end();  // Close the connection
    return strdup("Error");  // Return an error message
  }

  // Read the server response
  String payload = http.getString();
  Serial1.println("Server response:");
  Serial1.println(payload);

  // Parse the JSON response
  DynamicJsonDocument doc(1024);  // Create a JSON document with a capacity of 1024 bytes
  DeserializationError error = deserializeJson(doc, payload);
  http.end();  // Close the HTTP connection

  if (error) {
    Serial1.print("JSON parsing error: ");
    Serial1.println(error.c_str());
    return strdup("Error");  // Return an error message if JSON parsing fails
  }

  // Check if the "state" field exists in the JSON response
  if (!doc.containsKey("state")) {
    Serial1.println("ERROR: 'state' field missing in JSON response.");
    return strdup("Error");  // Return an error message if "state" is missing
  }

  const char* state = doc["state"];  // Extract the temperature value from the JSON
  Serial1.print("Temperature = ");
  Serial1.println(state);

  // Return a dynamically allocated copy of the temperature string
  return strdup(state);
}

/**
 * @brief Prints the reason for waking up from deep sleep.
 */
void printWakeupReason() {
  esp_sleep_wakeup_cause_t wakeupReason = esp_sleep_get_wakeup_cause();
  switch (wakeupReason) {
    case ESP_SLEEP_WAKEUP_EXT0:
      Serial1.println("Wakeup caused by external signal (EXT0 - button press)");
      break;
    case ESP_SLEEP_WAKEUP_EXT1:
      Serial1.println("Wakeup caused by external signal (EXT1)");
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial1.println("Wakeup caused by timer");
      break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
      Serial1.println("Wakeup caused by touchpad");
      break;
    default:
      Serial1.printf("Wakeup not caused by deep sleep: %d\n", wakeupReason);
      break;
  }
}

/**
 * @brief Initial system setup. Runs once at startup.
 */
void setup() {
  // Initialize Serial1 for debugging
  Serial1.begin(115200, SERIAL_8N1, SERIAL_RX, SERIAL_TX);
  delay(1000);  // Wait for serial port to initialize
  Serial1.println("System starting...");
  printWakeupReason();  // Print the reason for waking up

  // Configure the green button as an input with pull-up resistor
  pinMode(GREEN_BUTTON, INPUT_PULLUP);
  // Enable wakeup from deep sleep when the button is pressed (LOW signal)
  esp_sleep_enable_ext0_wakeup((gpio_num_t)GREEN_BUTTON, LOW);

  // Configure display control pins as outputs
  pinMode(EPD_RES_PIN, OUTPUT);
  pinMode(EPD_DC_PIN, OUTPUT);
  pinMode(EPD_CS_PIN, OUTPUT);

  // Initialize SPI interface for the display
  hspi.begin(EPD_SCK_PIN, -1, EPD_MOSI_PIN, -1);
  display.epd2.selectSPI(hspi, SPISettings(2000000, MSBFIRST, SPI_MODE0));

  // Connect to Wi-Fi
  WiFi.begin(wifi_ssid, wifi_password);
  Serial1.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial1.print(".");
  }
  Serial1.println("\nConnected to Wi-Fi");

  // Initialize the e-paper display
  display.init(0);
}

/**
 * @brief Main loop. Runs repeatedly after setup().
 */
void loop() {
  // Fetch the temperature from Home Assistant
  char* temperature = getTemperature();
  if (temperature != nullptr) {
    displayTemperature(temperature);  // Display the temperature
    free(temperature);  // Free the dynamically allocated memory
  } else {
    Serial1.println("ERROR: Failed to get temperature.");
    displayTemperature("Error");  // Display an error message
  }

  // Put the display into hibernation to save power
  display.hibernate();
  delay(1000);  // Wait for 1 seconds before entering deep sleep

  // Configure deep sleep wakeup after 10 minutes
  esp_sleep_enable_timer_wakeup(10 * 60 * 1000000);  // 10 minutes in microseconds
  esp_deep_sleep_start();  // Enter deep sleep mode
}

Un programme plus complet est disponible sur mon GitHub, il permet d’afficher les prévisions météo en plus de la température remontée par le capteur sous Home Assistant.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?