Dans un tutoriel précédent, je vous expliquais comment créer un capteur de température Zigbee à base d’ESP32C6 et comment l’intégrer dans Home Assistant.
Dans ce nouveau tutoriel, je vous guide pour intégrer un écran à encre électronique (e-paper) Seeed Studio reTerminal E1002 dans Home Assistant, afin d’y afficher les données issues de ce capteur.
Nous utiliserons le framework ESPHome, qui facilite grandement l’intégration de l’écran reTerminal dans Home Assistant via une configuration simple en YAML. Cette configuration permet de décrire précisément les périphériques connectés au microcontrôleur ESP32-S3 (écran, boutons, buzzer, LED, etc.), ainsi que les actions à exécuter localement. ESPHome compile ensuite ce fichier YAML pour générer un firmware qui sera téléversé sur le reTerminal.
Présentation du reTerminal
Le reTerminal E1002 est un appareil prêt à l’emploi doté d’un boîtier métallique robuste abritant un écran e-paper couleur de 7.3″ d’une résolution de 800×480. Il est équipé d’un microcontrôleur ESP32-S3 et il intègre 3 boutons, un buzzer, une LED de status (en plus de la LED d’alimentation), un capteur de température et de pression, un microphone et un lecteur de carte MicroSD. Il est alimenté par une batterie interne de 2000 mAh offrant jusqu’à 3 mois d’autonomie.
Les caractéristiques détaillées du reTerminal E1002 ainsi que son guide de démarrage rapide sont disponibles sur le wiki de Seeed Studio.





Mise à jour du firmware du reTerminal
Il est recommandé d’installer le dernier firmware disponible dès la première mise en service de votre appareil. Après avoir créé votre compte utilisateur sur le site sensecraft.seeed.cc, connectez votre reTerminal à un port USB de votre ordinateur, puis utilisez l’outil Device Flasher pour flasher le firmware le plus récent.

Dans ce tutoriel, SenseCraft HMI ne sera pas utilisé : il n’est donc pas nécessaire de suivre la procédure affichée sur l’écran du reTerminal, ni de se connecter au point d’accès Wi-Fi qu’il diffuse. L’objectif ici est de faire fonctionner le reTerminal avec Home Assistant, en le configurant directement via le framework ESPHome.
Configuration du reTerminal avec ESPHome
Installer le module complémentaire ESPHome Device Builder sur votre serveur Home Assistant puis le démarrer.

Dans le module ESPHome Builder, ajoutez le reTerminal comme nouvel appareil de type ESP32-S3. Étant donné que le reTerminal n’est pas encore configuré pour rejoindre le réseau Wi-Fi et que Home Assistant fonctionne en HTTP (et non HTTPS), le téléversement direct du firmware n’est pas possible depuis ESPHome Builder. Utilisez alors l’option Manual Download pour récupérer le firmware compilé au format Factory format (Previously Modern).




Note: l’installation des outils et la compilation du firmware peuvent être relativement longues si Home Assistant est hébergé sur un Raspberry Pi, notamment sur les modèles moins puissants. Il n’est pas rare d’attendre plusieurs minutes pour chaque compilation.
Téléversez ensuite ce fichier .bin sur le reTerminal à l’aide de l’outil en ligne ESPHome. Après ce premier flashage, le reTerminal rejoindra votre réseau Wi-Fi. Par la suite, il sera possible d’effectuer les futures mises à jour du firmware directement depuis Home Assistant via le Wi-Fi, sans nécessiter de connexion filaire.



Hello World!
Nous allons maintenant créer un exemple pour vous permettre de comprendre le principe de ESPHome et la configuration du reTerminal. Dans cet exemple, nous allons simplement afficher le message Hello World! sur l’écran du reTerminal.
Dans le module ESPHome Builder, cliquer sur Edit afin de pouvoir modifier la configuration YAML pour que le reTerminal affiche notre message.


L’écran du reTerminal E1002 de Seeed Studio est une dalle E Ink® Spectra™ 6 couleur de 7,3 pouces avec une résolution de 800×480 pixels. Comme vous pouvez le voir sur le schéma du reTerminal, l’ESP32-S3 communique avec l’écran au travers d’un bus SPI via les broches suivantes :
- SPI Clock (CLK) : GPIO 7
- Master In Slave Out (MISO) : GPIO8 (non utilisé par l’écran mais uniquement par le lecteur de carte microSD qui est sur le même bus SPI)
- Master Out Slave In (MOSI) : GPIO9
- Chip Select (CS) : GPIO10
- Data/Command (DC) : GPIO11
- Reset (RST) : GPIO12
Voici la configuration qui affiche le message « Hello Worlds! » en bleu, avec la police Google Inter 700 et une taille de 36 points. Vous pouvez utiliser cet exemple en copiant le code ci-dessous et en le collant juste après la ligne captive_portal dans votre fichier YAML.
captive_portal:
external_components:
- source:
type: git
url: https://github.com/lublak/esphome
ref: dev
components: [ waveshare_epaper ]
# define font to display words
font:
- file: "gfonts://Inter@700"
id: myFont
size: 36
# define SPI interface
spi:
clk_pin: GPIO7
mosi_pin: GPIO9
display:
- platform: waveshare_epaper
id: epaper_display
model: 7.30in-e
cs_pin: GPIO10
dc_pin: GPIO11
reset_pin:
number: GPIO12
inverted: false
busy_pin:
number: GPIO13
inverted: true
update_interval: 300s
lambda: |-
const auto BLUE = Color(0, 0, 255, 0);
it.print(0, 0, id(myFont), BLUE, "Hello World!");
Note : sur un écran e-paper, il est essentiel de rafraîchir régulièrement l’affichage afin d’éviter les effets de rémanence caractéristiques de cette technologie. Dans la configuration ci-dessus, l’affichage est rafraîchi toutes les 300 secondes (5 minutes) via la configuration update_interval: 300s
Il suffit ensuite de cliquer sur Install et de choisir l’option Wirelessly pour téléverser le firmware correspondant à cette configuration YAML dans le reTerminal via votre réseau Wi-Fi (qui a été configuré lors du premier téléversement via ESPHome).

Après compilation et téléversement, le reTerminal affiche bien le message Hello World! en bleu sur son écran.

Capteur de température et d’humidité interne au reTerminal
Le reTerminal possède un capteur interne de témperature et d’humidité STH40.
L’ESP32-S3 communique avec ce capteur au travers d’un bus I2C. Son adresse I2C est 0x44, et le bus I2C utilise les broches suivantes :
- Serial Data (SDA) : GPIO19
- Serial Clock (SCL) : GPIO20
Voici la configuration YAML qui permet de récupérer la température et l’humidité depuis ce capteur et de l’afficher sur l’écran, à copier juste après la ligne captive_portal dans votre fichier YAML :
captive_portal:
external_components:
- source:
type: git
url: https://github.com/lublak/esphome
ref: dev
components: [ waveshare_epaper ]
# define I2C interface
i2c:
sda: GPIO19
scl: GPIO20
scan: false
# temperature and humidity sensor
sensor:
- platform: sht4x
temperature:
name: "Temperature"
id: sht4x_temperature
humidity:
name: "Humidity"
id: sht4x_humidity
address: 0x44
update_interval: 60s
# define font to display words
font:
- file: "gfonts://Inter@700"
id: myFont
size: 36
# define SPI interface
spi:
clk_pin: GPIO7
mosi_pin: GPIO9
display:
- platform: waveshare_epaper
id: epaper_display
model: 7.30in-e
cs_pin: GPIO10
dc_pin: GPIO11
reset_pin:
number: GPIO12
inverted: false
busy_pin:
number: GPIO13
inverted: true
update_interval: 300s
lambda: |-
const auto BLUE = Color(0, 0, 255, 0);
it.printf(10, 10, id(myFont), BLUE, "Temperature: %.1f°C", id(sht4x_temperature).state);
it.printf(10, 40, id(myFont), BLUE, "Humidity: %.1f%%", id(sht4x_humidity).state);
Le reTerminal affichera sur son écran la température et l’humidité mesurées par son capteur interne.

Grâce à ESPHome, il est possible d’afficher les données issues du reTerminal dans Home Assistant.



Affichage des données d’un capteur externe sur l’écran du reTerminal
Il est parfaitement possible d’afficher n’importe quelle entité de Home Assistant sur l’écran du reTerminal. Comme mentionné au début de ce tutoriel, nous allons afficher sur l’écran du reTerminal, la température et l’humidité mesurée par le capteur Zigbee externe à base d’ESP32-C6 que j’ai conçu dans mon tutoriel précédent.
La température et l’humidité mesurées par ce capteur externe correspondent aux entité d’ID suivantes dans Home Assistant :
- sensor.tutoduino_esp32c6tempsensor_temperature
- sensor.tutoduino_esp32c6tempsensor_humidite

Voici le code à copier juste après la ligne captive_portal dans votre fichier YAML :
captive_portal:
external_components:
- source:
type: git
url: https://github.com/lublak/esphome
ref: dev
components: [ waveshare_epaper ]
# define I2C interface
i2c:
sda: GPIO19
scl: GPIO20
scan: false
sensor:
# internal temperature and humidity sensor
- platform: sht4x
temperature:
name: "Temperature"
id: sht4x_temperature
humidity:
name: "Humidity"
id: sht4x_humidity
address: 0x44
update_interval: 60s
# external EP32C6 sensor
- platform: homeassistant
id: tutoduino_esp32c6tempsensor_temperature
entity_id: sensor.tutoduino_esp32c6tempsensor_temperature
- platform: homeassistant
id: tutoduino_esp32c6tempsensor_humidite
entity_id: sensor.tutoduino_esp32c6tempsensor_humidite
# define font to display words
font:
- file: "gfonts://Inter@700"
id: myFont
size: 36
- file: "gfonts://Inter@700"
id: myFontLarge
size: 50
- file: "gfonts://Inter@700"
id: myFontVeryLarge
size: 70
# define SPI interface
spi:
clk_pin: GPIO7
mosi_pin: GPIO9
display:
- platform: waveshare_epaper
id: epaper_display
model: 7.30in-e
cs_pin: GPIO10
dc_pin: GPIO11
reset_pin:
number: GPIO12
inverted: false
busy_pin:
number: GPIO13
inverted: true
update_interval: 300s
lambda: |-
const auto BLUE = Color(0, 0, 255, 0);
const auto GREEN = Color(0, 255, 0, 0);
const auto BLACK = Color(0, 0, 0, 0);
it.line(400, 0, 400, 480, BLACK);
it.printf(130, 15, id(myFont), BLACK, "INDOOR");
it.printf(510, 15, id(myFont), BLACK, "OUTDOOR");
if (isnan(id(sht4x_temperature).state)) {
it.printf(90, 120, id(myFontVeryLarge), BLUE, "--.-°C");
} else {
it.printf(90, 120, id(myFontVeryLarge), BLUE, "%.1f°C", id(sht4x_temperature).state);
}
if (isnan(id(sht4x_humidity).state)) {
it.printf(120, 250, id(myFontLarge), BLUE, "--.-%%");
} else {
it.printf(120, 250, id(myFontLarge), BLUE, "%.1f%%", id(sht4x_humidity).state);
}
if (isnan(id(tutoduino_esp32c6tempsensor_temperature).state)) {
it.printf(500, 120, id(myFontVeryLarge), GREEN, "--.-°C");
} else {
it.printf(500, 120, id(myFontVeryLarge), GREEN, "%.1f°C", id(tutoduino_esp32c6tempsensor_temperature).state);
}
if (isnan(id(tutoduino_esp32c6tempsensor_humidite).state)) {
it.printf(540, 250, id(myFontLarge), GREEN, "--.-%%");
} else {
it.printf(540, 250, id(myFontLarge), GREEN, "%.1f%%", id(tutoduino_esp32c6tempsensor_humidite).state);
}
Cette configuration affiche les données du capteur interne au reTerminal dans la partie INDOOR et les données issues du capteur externe dans la partie OUTDOOR.

Affichage de la charge des batteries et ajout d’icônes
Mon capteur externe à base d’ESP32-C6 et le reTerminal étant tous les deux alimentés par batterie, il est utile de pouvoir afficher leur niveau sur l’écran.
Concernant le niveau de la batterie du capteur externe, elle est simplement récupérée via l’entité Home Assistant sensor.tutoduino_esp32c6tempsensor_batterie.
Pour récupérer le niveau de batterie du reTerminal, il faut activer la mesure de la tension de la batterie en configurant le GPIO21 (VBAT ENABLE). La lecture s’effectue ensuite sur le GPIO1 (VBAT ADC), qui est relié en interne à la batterie via un circuit de mesure. En lisant la valeur analogique (tension) sur ce GPIO1, il est possible d’obtenir une estimation précise du niveau de charge de la batterie.
Pour ajouter les icônes de thermomètre, d’hygromètre et de batterie, il faut créer le répertoire fonts dans le répertoire esphome (avec l’extension File editor) et y ajouter le fichier de font suivant : MaterialDesignIconsDesktop.ttf

Voici la configuration qui permet de récupérer le niveau des batteries et d’afficher les icônes.
esphome:
name: reterminal
friendly_name: reTerminal
on_boot:
priority: 600
then:
- output.turn_on: bsp_battery_enable
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "XXXX"
ota:
- platform: esphome
password: "XXXX"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Reterminal Fallback Hotspot"
password: "XXXX"
captive_portal:
output:
- platform: gpio
pin: GPIO21
id: bsp_battery_enable
external_components:
- source:
type: git
url: https://github.com/lublak/esphome
ref: dev
components: [ waveshare_epaper ]
# define I2C interface
i2c:
sda: GPIO19
scl: GPIO20
scan: false
sensor:
# internal temperature and humidity sensor
- platform: sht4x
temperature:
name: "Temperature"
id: sht4x_temperature
humidity:
name: "Humidity"
id: sht4x_humidity
address: 0x44
update_interval: 300s
# external EP32C6 sensor
- platform: homeassistant
id: tutoduino_esp32c6tempsensor_temperature
entity_id: sensor.tutoduino_esp32c6tempsensor_temperature
- platform: homeassistant
id: tutoduino_esp32c6tempsensor_humidite
entity_id: sensor.tutoduino_esp32c6tempsensor_humidite
- platform: homeassistant
id: tutoduino_esp32c6tempsensor_batterie
entity_id: sensor.tutoduino_esp32c6tempsensor_batterie
- platform: adc
pin: GPIO1
name: "Battery Voltage"
id: battery_voltage
update_interval: 300s
attenuation: 12db
filters:
- multiply: 2.0 # Voltage divider compensation
- platform: template
name: "Battery Level"
id: battery_level
unit_of_measurement: "%"
icon: "mdi:battery"
device_class: battery
state_class: measurement
lambda: 'return id(battery_voltage).state;'
update_interval: 300s
filters:
- calibrate_linear:
- 4.15 -> 100.0
- 3.96 -> 90.0
- 3.91 -> 80.0
- 3.85 -> 70.0
- 3.80 -> 60.0
- 3.75 -> 50.0
- 3.68 -> 40.0
- 3.58 -> 30.0
- 3.49 -> 20.0
- 3.41 -> 10.0
- 3.30 -> 5.0
- 3.27 -> 0.0
- clamp:
min_value: 0
max_value: 100
# define font to display words
font:
- file: "gfonts://Inter@700"
id: mySmallFont
size: 15
- file: "gfonts://Inter@700"
id: myFont
size: 36
- file: "gfonts://Inter@700"
id: myFontLarge
size: 50
- file: "gfonts://Inter@700"
id: myFontVeryLarge
size: 70
- file: 'fonts/MaterialDesignIconsDesktop.ttf'
id: font_mdi_large
size: 50
glyphs:
- "\U000F050F" # thermometer
- "\U000F058E" # humidity
- file: 'fonts/MaterialDesignIconsDesktop.ttf'
id: font_bat_icon
size: 50
glyphs:
- "\U000F0079" # mdi-battery
# define SPI interface
spi:
clk_pin: GPIO7
mosi_pin: GPIO9
display:
- platform: waveshare_epaper
id: epaper_display
model: 7.30in-e
cs_pin: GPIO10
dc_pin: GPIO11
reset_pin:
number: GPIO12
inverted: false
busy_pin:
number: GPIO13
inverted: true
update_interval: 10min
lambda: |-
const auto BLUE = Color(0, 0, 255, 0);
const auto GREEN = Color(0, 255, 0, 0);
const auto BLACK = Color(0, 0, 0, 0);
const auto RED = Color(255, 0, 0, 0);
it.line(400, 0, 400, 480, BLACK);
it.printf(130, 15, id(myFont), BLACK, "INDOOR");
it.printf(510, 15, id(myFont), BLACK, "OUTDOOR");
it.printf(60, 165, id(font_mdi_large), BLACK, TextAlign::CENTER, "\U000F050F");
if (isnan(id(sht4x_temperature).state)) {
it.printf(90, 120, id(myFontVeryLarge), BLUE, "--.-°C");
} else {
it.printf(90, 120, id(myFontVeryLarge), BLUE, "%.1f°C", id(sht4x_temperature).state);
}
it.printf(60, 283, id(font_mdi_large), BLACK, TextAlign::CENTER, "\U000F058E");
if (isnan(id(sht4x_humidity).state)) {
it.printf(90, 250, id(myFontLarge), BLUE, "--.-%%");
} else {
it.printf(90, 250, id(myFontLarge), BLUE, "%.1f%%", id(sht4x_humidity).state);
}
it.printf(60, 390, id(font_bat_icon), BLACK, TextAlign::CENTER, "\U000F0079");
if (isnan(id(battery_level).state)) {
it.printf(90, 360, id(myFontLarge), BLUE, "--%%");
} else {
it.printf(90, 360, id(myFontLarge), BLUE, "%2.0f%%", id(battery_level).state);
}
it.printf(470, 165 , id(font_mdi_large), BLACK, TextAlign::CENTER, "\U000F050F");
if (isnan(id(tutoduino_esp32c6tempsensor_temperature).state)) {
it.printf(500, 120, id(myFontVeryLarge), BLUE, "--.-°C");
} else {
it.printf(500, 120, id(myFontVeryLarge), BLUE, "%.1f°C", id(tutoduino_esp32c6tempsensor_temperature).state);
}
it.printf(470, 283, id(font_mdi_large), BLACK, TextAlign::CENTER, "\U000F058E");
if (isnan(id(tutoduino_esp32c6tempsensor_humidite).state)) {
it.printf(500, 250, id(myFontLarge), BLUE, "--.-%%");
} else {
it.printf(500, 250, id(myFontLarge), BLUE, "%.1f%%", id(tutoduino_esp32c6tempsensor_humidite).state);
}
it.printf(470, 390, id(font_bat_icon), BLACK, TextAlign::CENTER, "\U000F0079");
if (isnan(id(tutoduino_esp32c6tempsensor_batterie).state)) {
it.printf(500, 360, id(myFontLarge), BLUE, "--%%");
} else {
it.printf(500, 360, id(myFontLarge), BLUE, "%2.0f%%", id(tutoduino_esp32c6tempsensor_batterie).state);
}
Voici le rendu des icones sur l’écran du reTerminal E1002, mais n’hésitez pas à les adapter à vos besoins.
