In a previous tutorial, I explained how to create a Zigbee temperature sensor based on ESP32C6 and how to integrate it into Home Assistant.
In this new tutorial, I guide you through integrating a Seeed Studio reTerminal E1002 e-paper display into Home Assistant to display data from this sensor.
We will use the ESPHome framework, which greatly simplifies integrating the reTerminal display into Home Assistant via a simple YAML configuration. This configuration allows precise description of peripherals connected to the ESP32-S3 microcontroller (display, buttons, buzzer, LED, etc.), as well as actions to be executed locally. ESPHome then compiles this YAML file to generate firmware that is uploaded to the reTerminal.
Introducing the reTerminal
The reTerminal E1002 is a ready-to-use device featuring a robust metal case housing a 7.3″ color e-paper display with a resolution of 800×480. It is powered by an ESP32-S3 microcontroller and includes 3 buttons, a buzzer, a status LED (in addition to the power LED), a temperature and pressure sensor, a microphone, and a MicroSD card reader. It is powered by an internal 2000 mAh battery providing up to 3 months of autonomy.
The detailed specifications of the reTerminal E1002 as well as its quick start guide are available on the Seeed Studio wiki.
Key features include:
- A 7.3-inch full-color ePaper display with 800×480 resolution using Advanced Color ePaper (ACeP) technology with 6 colors (including black and white).
- Powered by the Espressif ESP32-S3 microcontroller with 8MB PSRAM.
- 32MB SPI flash memory and a MicroSD card slot supporting up to 32GB (FAT32 format).
- Wireless connectivity with 2.4GHz 802.11 b/g/n Wi-Fi and Bluetooth 5.0.
- Integrated temperature and humidity sensor (SHT40), microphone, buzzer, and multiple buttons.
- A built-in 2000 mAh battery providing up to 3 months of battery life.
- Multiple LEDs including a green status LED and a red charging LED.
- USB-C port for charging and firmware updates.
- An 8-pin expansion header exposing ESP32-S3 GPIOs, I2C, UART, ADC, and power lines for additional hardware expansion.
The device comes with a power switch on the back and supports easy network setup via Wi-Fi (2.4 GHz only) through hotspot or mobile app configuration. It can connect to the SenseCraft HMI platform for no-code UI dashboard creation and also supports Home Assistant, Arduino, and ESP-IDF environments for more advanced development.
The wiki offers comprehensive instructions on getting started, powering the device on, network configuration, Bluetooth setup, dashboard creation using the SenseCraft platform, device operation, troubleshooting, and hardware expansion options.





Updating the reTerminal firmware
It is recommended to install the latest available firmware as soon as you first use your device. After creating your user account on the sensecraft.seeed.cc website, connect your reTerminal to a USB port on your computer, then use the Device Flasher tool to flash the most recent firmware.

In this tutorial, SenseCraft HMI will not be used: therefore it is not necessary to follow the procedure displayed on the reTerminal screen, nor to connect to the Wi-Fi hotspot it broadcasts. The goal here is to make the reTerminal work with Home Assistant by configuring it directly through the ESPHome framework.
Configuring reTerminal with ESPHome
To install the ESPHome Device Builder add-on on your Home Assistant server and start it, follow these steps:
- In Home Assistant, go to Settings > Add-on Store.
- Search for ESPHome in the add-on list.
- Click on ESPHome Device Builder and then Install.
- Wait until the installation completes.
- Once installed, click Start to launch the add-on.
- Optionally, open the add-on’s Web UI to begin creating and managing your ESPHome device configurations.
This add-on provides a simple web interface to build, compile, and upload ESPHome firmware to your devices directly from Home Assistant.

In the ESPHome Builder module, add the reTerminal as a new device of type ESP32-S3. Since the reTerminal is not yet configured to join the Wi-Fi network and Home Assistant is running over HTTP (not HTTPS), direct firmware upload from ESPHome Builder is not possible. Instead, use the Manual Download option to retrieve the compiled firmware in Factory format (Previously Modern).




Note: Installing tools and compiling the firmware can be relatively slow, especially if Home Assistant is hosted on a less powerful Raspberry Pi. It is not uncommon for each compilation to take several minutes.
Then upload this firmware file to the reTerminal using the ESPHome online tool. After this initial flashing, the reTerminal will connect to your Wi-Fi network. Future firmware updates can then be done directly over Wi-Fi from Home Assistant without the need for a wired connection.



Hello World!
We will now create an example to help you understand the principle of ESPHome and the configuration of the reTerminal. In this example, we will simply display the message Hello World! on the reTerminal screen.
In the ESPHome Builder module, click on Edit to modify the YAML configuration so that the reTerminal displays our message.


The reTerminal E1002 screen from Seeed Studio is a 7.3-inch color E Ink® Spectra™ 6 panel with a resolution of 800×480 pixels. As you can see on the reTerminal schematic, the ESP32-S3 communicates with the screen over an SPI bus through the following pins:
- SPI Clock (CLK): GPIO 7
- Master In Slave Out (MISO): GPIO 8 (not used by the screen but only by the microSD card reader on the same SPI bus)
- Master Out Slave In (MOSI): GPIO 9
- Chip Select (CS): GPIO 10
- Data/Command (DC): GPIO 11
- Reset (RST): GPIO 12
This SPI connection is used to drive the e-paper display effectively.
Here’s the configuration that displays the message “Hello Worlds!” in blue, with the Google Inter 700 font and a size of 36 points. You can use this example by copying the code below and pasting it just after the captive_portal line in your YAML file.
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: On an e-paper screen, it is essential to refresh the display regularly to avoid the afterglow effects characteristic of this technology. In the configuration above, the display is refreshed every 300 seconds (5 minutes) via the update_interval: 300s configuration.
Then simply click on Install and choose the Wirelessly option to upload the firmware corresponding to this YAML configuration into the reTerminal via your Wi-Fi network (which was configured during the first upload via ESPHome).

After compiling and uploading, the reTerminal displays the message Hello World! in blue on its screen.

Internal temperature and humidity sensor in the reTerminal
The reTerminal has an internal temperature and humidity sensor STH40.
The ESP32-S3 communicates with this sensor via an I2C bus. Its I2C address is 0x44, and the I2C bus uses the following pins:
- Serial Data (SDA): GPIO19
- Serial Clock (SCL): GPIO20
Here is the YAML configuration that allows you to retrieve temperature and humidity from this sensor and display it on the screen, to copy just after the captive_portal line in your YAML file:
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);
The reTerminal will display on its screen the temperature and humidity measured by its internal sensor.

Thanks to ESPHome, it is possible to display data from reTerminal in Home Assistant.



Displaying data from an external sensor on the reTerminal screen
It is perfectly possible to display any Home Assistant entity on the reTerminal screen. As mentioned at the beginning of this tutorial, we will display on the reTerminal screen the temperature and humidity measured by the external ESP32-C6-based Zigbee sensor that I designed in my previous tutorial.
The temperature and humidity measured by this external sensor correspond to the following ID entities in Home Assistant:
- sensor.tutoduino_esp32c6tempsensor_temperature
- sensor.tutoduino_esp32c6tempsensor_humidite

Here is the code to copy just after the captive_portal line in your YAML file:
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);
}
This configuration displays data from the internal sensor in the reTerminal in the INDOOR section and data from the external sensor in the OUTDOOR section.

Display battery charge and add icons
Since my ESP32-C6 based external sensor and the reTerminal are both battery powered, it is useful to be able to display their level on the screen.
Regarding the battery level of the external sensor, it is simply retrieved via the Home Assistant entity sensor.tutoduino_esp32c6tempsensor_batterie.
To retrieve the reTerminal’s battery level, you must enable battery voltage measurement by configuring GPIO21 (VBAT ENABLE). The reading is then performed on GPIO1 (VBAT ADC), which is internally connected to the battery via a measurement circuit. By reading the analog value (voltage) on this GPIO1, it is possible to obtain an accurate estimate of the battery charge level.
To add the thermometer, hygrometer and battery icons, you need to create the fonts directory in the esphome directory (with the File editor extension) and add the following font file to it: MaterialDesignIconsDesktop.ttf

Here is the configuration that allows you to retrieve the battery level and display the icons.
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);
}
Here is how the icons appear on the reTerminal E1002 screen, but feel free to adapt them to your needs.
