Recipe: ESPHome + Adafruit Feather ESP32-S2 + ThinkInk E-paper display + Battery monitor

Long-time lurker etc etc. Thought I’d share a recipe to get an Adafruit Feather ESP32-S2 working with ESPHome, along with the ThinkInk E-paper display.

Hardware-wise, it’s a nice straightforward way to get started with E-Paper (or is it EPaper? epaper?).

As a bonus, I wrote a basic driver for the max17048 battery fuel gauge which is integrated on many Adafruit Feathers.

Here’s the end result:

The ThinkInk display isn’t officially supported by ESPHome, but it appears to be similar enough to a WaveShare display that it works alright.

The max17048 battery monitor works, though its accuracy leaves something to be desired.

Basic steps

  1. Buy the parts
  2. Solder pin headers to the Feather
  3. Plug it all together
  4. Flash it with ESPHome using the .yaml file below
  5. Profit
  6. Add the device to Home Assistant if you’d like

Parts List

This is all stuff off of Adafruit:

ESPHome .yaml file

# Handy docs:
# the basics of using a qt py with ESPHome: https://jamesachambers.com/home-assistant-tiny-wifi-button-guide-ft-adafruit-qt-py/
# ESP32-S2 Feather pinout: https://learn.adafruit.com/adafruit-esp32-s2-feather/pinouts
# E-ink feather pinout: https://learn.adafruit.com/adafruit-2-9-eink-display-breakouts-and-featherwings/pinouts#featherwing-connections-3102326

# Chip selects: e-ink: GPIO9 sRAM: GPIO6 SD: GPIO5
# EINK DC: D10

esphome:
  name: esp32-s2-mini-feather
  friendly_name: ESP32 S2 Mini Feather E-ink

esp32:
  board: featheresp32-s2
  framework:
    type: arduino

# Enable logging
logger:

web_server:
  port: 80

# Enable Home Assistant API
api:
  encryption:
    key: "REDACTED"

ota:
  password: "REDACTED"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

spi:
  clk_pin: GPIO36
  mosi_pin: GPIO35
  miso_pin: GPIO37
  interface: hardware

i2c:
  sda: GPIO3
  scl: GPIO4

font:
  - file:
      type: gfonts
      family: Montserrat
      weight: 600
    id: title_font
    size: 40
  - file:
      type: gfonts
      family: Montserrat
      weight: 300
    id: small_font
    size: 20

external_components:
  - source: github://Option-Zero/esphome-components
    components: [max17048]
time:
  platform: sntp
  id: current_time

display:
  - platform: waveshare_epaper
    model: 2.90in-bV3
    id: eink_display
    cs_pin: GPIO9
    dc_pin: GPIO10
    update_interval: 1min
    rotation: 90
    lambda: |-
      it.print(0, 0, id(title_font), "Current Time");
      it.strftime(0, 50, id(title_font), "%H:%M", id(current_time).now());

      it.printf(0, 100, id(small_font), "Batt: %.1f%%  %.2fV  %.0f%%/hr", id(batt_pct).state, id(batt_v).state, id(batt_discharge_rate).state);

sensor:
  - platform: max17048
    battery_voltage:
      name: Battery voltage
      id: batt_v
    battery_level:
      name: Battery level
      id: batt_pct
    rate:
      name: Battery discharge rate
      id: batt_discharge_rate


output:
  - platform: gpio
    pin: GPIO21
    id: neopixel_power_gpio
switch:
  - platform: output
    name: "Neopixel power bus"
    output: neopixel_power_gpio
    id: neopixel_power_switch
    restore_mode: ALWAYS_ON
    disabled_by_default: True
light:
  - name: "Neopixel"
    id: neopixel
    platform: neopixelbus
    type: GRB
    variant: WS2812
    pin: GPIO33
    method:
      type: esp32_rmt
      channel: 0
    num_leds: 1
    restore_mode: RESTORE_DEFAULT_ON
    default_transition_length: 100ms

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO13
      mode: INPUT_PULLUP
      inverted: True
    name: "Button A"
  - platform: gpio
    pin:
      number: GPIO12
      mode: INPUT_PULLUP
      inverted: True
    name: "Button B"
    on_press:
      - light.toggle: neopixel
  - platform: gpio
    pin:
      number: GPIO11
      mode: INPUT_PULLUP
      inverted: True
    name: "Button C"
    on_press: 
      then:
        - component.update: eink_display 

Gotchas, tips n tricks, and TODOs

Some cool / annoying stuff I encountered along the way:

Manual reset

After updating firmware, you have to manually hit the reset button or else the E-ink display may not update. I’m not sure why that is. But it’ll drive ya crazy if you forget to reset it.

Partial screen refresh

Every screen refresh is a full refresh, which is an epilepsy-triggering, 6-second-long sequence of flashes. I haven’t looked into whether a partial screen refresh is possible with this hardware.

Battery usage

Battery life is currently ~24 hours without any kind of optimization.

The max17048 tended to underreport battery percentage in my testing with this setup (it hit ~1% after 12 hours, but the clock kept running for another 12 hours). I do like how it also reports battery charge or discharge rate in %/hr though.

Trouble with initial flash of the Feather S2

When the Feather hasn’t been flashed with ESPHome, the first flash can be tricky. I was getting the “Failed to initialize. Try resetting your device or holding the BOOT button while selecting your port.” error as seen in Getting started with ESPHome and Adafruit ESP32-S2 Feather .

Luckily I was able to cold-start the device with this sequence:

  1. Build the firmware from the ESPHome add-on and download the .bin file (“new style”). Make sure you supply wi-fi login info.
  2. Connect to the device using Adafruit ESPTool
  3. Flash the .bin firmware file to the device at offset 0x0
  4. From there, you should be able to detect the device in the ESPHome add-on and use OTA updates.

Screen color

This is a 3-color display (black, white, red) but it’s currently only working with black and white. Some folks have managed to get the red working: Use ESPHome with e-ink Displays to blend in with your home decor! - #303 by skybarberom

My initial forays into this weren’t immediately successful so I figured I’d share what I have.

Extra flash, buttons n goodies

The Adafruit 2.9" Tri-Color eInk / ePaper Display FeatherWing has a MicroSD card slot and some SRAM as well. I didn’t need those for this project, but I included the Chip Select pins in the YAML.

There are also three buttons on the FeatherWing. I set those up to report to ESPHome so you can use them to trigger automations. I also set one of them to flash the neopixel on the Feather, and the other to trigger an immediate screen refresh.