Integrate a reTerminal E1002 into Home Assistant with ESPHome

0
(0)

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:

  1. In Home Assistant, go to Settings > Add-on Store.
  2. Search for ESPHome in the add-on list.
  3. Click on ESPHome Device Builder and then Install.
  4. Wait until the installation completes.
  5. Once installed, click Start to launch the add-on.
  6. 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.

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: 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.

First configuration of the reTerminal E1002 displaying the famous “Hello World!”

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:

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

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:

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

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.

Displaying data from the internal sensor and an external sensor

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.

YAML
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.

Display of temperature, humidity and battery level of an external sensor and the internal sensor of the reTerminal E1002

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?