TRMNL 7.5” (OG) DIY Kit Battery voltage status not working

Good Day all,

Does anybody have an idea what I’m doing wrong? I’m trying to measure the battery voltage from out ESPHome, but the voltage I get is only 0.00000 V.
The TRMNL 7.5” (OG) DIY Kit has a XIAO-ESP32S3-Plus.

I also asked this question on the Seedstation forum, but not many people had an idea of what I can do.

I followed the example script from this page and take out what i only need from my understanding.

this is output from ESPHome:

INFO ESPHome 2025.10.3
INFO Reading configuration /config/esphome/einkdisplay.yaml...
INFO Detected timezone 'Europe/Amsterdam'
INFO Starting log output from einkdisplay.local using esphome API
INFO Successfully resolved einkdisplay.local in 0.005s
WARNING Can't connect to ESPHome API for einkdisplay.local: Error connecting to [AddrInfo(family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, proto=6, sockaddr=IPv4Sockaddr(address='192.168.1.109', port=6053))]: [Errno 113] Connect call failed ('192.168.1.109', 6053) (SocketAPIError)
INFO Trying to connect to einkdisplay.local in the background
INFO Successfully connected to einkdisplay @ 192.168.1.109 in 0.013s
INFO Successful handshake with einkdisplay @ 192.168.1.109 in 0.108s
[22:35:37.299][I][app:185]: ESPHome version 2025.10.3 compiled on Oct 24 2025, 22:13:53
[22:35:37.299][C][wifi:679]: WiFi:
[22:35:37.299][C][wifi:458]:   Local MAC: D0:CF:00:00:ED:24
[22:35:37.305][C][wifi:465]:   IP Address: 192.168.1.109
[22:35:37.320][C][wifi:469]:   SSID: [redacted]
[22:35:37.320][C][wifi:469]:   BSSID: [redacted]
[22:35:37.320][C][wifi:469]:   Hostname: 'einkdisplay'
[22:35:37.320][C][wifi:469]:   Signal strength: -73 dB ▂▄▆█
[22:35:37.320][C][wifi:469]:   Channel: 11
[22:35:37.320][C][wifi:469]:   Subnet: 255.255.255.0
[22:35:37.320][C][wifi:469]:   Gateway: 192.168.1.1
[22:35:37.320][C][wifi:469]:   DNS1: 192.168.1.1
[22:35:37.320][C][wifi:469]:   DNS2: 0.0.0.0
[22:35:37.320][C][logger:261]: Logger:
[22:35:37.320][C][logger:261]:   Max Level: DEBUG
[22:35:37.320][C][logger:261]:   Initial Level: DEBUG
[22:35:37.320][C][logger:267]:   Log Baud Rate: 115200
[22:35:37.320][C][logger:267]:   Hardware UART: USB_SERIAL_JTAG
[22:35:37.320][C][logger:274]:   Task Log Buffer Size: 768
[22:35:37.334][C][spi:067]: SPI bus:
[22:35:37.334][C][spi:068]:   CLK Pin: GPIO7
[22:35:37.334][C][spi:069]:   SDI Pin: 
[22:35:37.334][C][spi:070]:   SDO Pin: GPIO9
[22:35:37.340][C][spi:075]:   Using HW SPI: SPI
[22:35:37.379][C][uptime.sensor:015]: Uptime Sensor 'Uptime'
[22:35:37.379][C][uptime.sensor:015]:   State Class: 'total_increasing'
[22:35:37.379][C][uptime.sensor:015]:   Unit of Measurement: 's'
[22:35:37.379][C][uptime.sensor:015]:   Accuracy Decimals: 0
[22:35:37.380][C][uptime.sensor:025]:   Device Class: 'duration'
[22:35:37.384][C][uptime.sensor:029]:   Icon: 'mdi:timer-outline'
[22:35:37.385][C][uptime.sensor:033]:   Type: Seconds
[22:35:37.399][C][template.sensor:015]: Template Sensor 'Display Last Update'
[22:35:37.399][C][template.sensor:015]:   State Class: ''
[22:35:37.399][C][template.sensor:015]:   Unit of Measurement: ''
[22:35:37.399][C][template.sensor:015]:   Accuracy Decimals: 1
[22:35:37.400][C][template.sensor:025]:   Device Class: 'timestamp'
[22:35:37.400][C][template.sensor:362]:   Update Interval: 60.0s
[22:35:37.414][C][template.sensor:015]: Template Sensor 'Display Refresh Count'
[22:35:37.414][C][template.sensor:015]:   State Class: 'total_increasing'
[22:35:37.414][C][template.sensor:015]:   Unit of Measurement: 'Refreshes'
[22:35:37.414][C][template.sensor:015]:   Accuracy Decimals: 0
[22:35:37.415][C][template.sensor:362]:   Update Interval: 60.0s
[22:35:37.449][C][homeassistant.time:010]: Home Assistant Time:
[22:35:37.449][C][homeassistant.time:010]:   Timezone: 'CET-1CEST,M3.5.0,M10.5.0/3'
[22:35:37.455][C][adc.esp32:015]: ADC Sensor 'Battery Voltage'
[22:35:37.455][C][adc.esp32:015]:   State Class: 'measurement'
[22:35:37.455][C][adc.esp32:015]:   Unit of Measurement: 'V'
[22:35:37.455][C][adc.esp32:015]:   Accuracy Decimals: 2
[22:35:37.461][C][adc.esp32:025]:   Device Class: 'voltage'
[22:35:37.461][C][adc.esp32:122]:   Pin: GPIO1
[22:35:37.464][C][adc.esp32:123]:   Channel:       0
[22:35:37.464][C][adc.esp32:123]:   Unit:          ADC1
[22:35:37.464][C][adc.esp32:123]:   Attenuation:   12 dB
[22:35:37.464][C][adc.esp32:123]:   Samples:       1
[22:35:37.464][C][adc.esp32:123]:   Sampling mode: average
[22:35:37.468][C][adc.esp32:133]:   Setup Status:
[22:35:37.468][C][adc.esp32:133]:     Handle Init:  OK
[22:

This .yaml i created:

esphome:
  name: einkdisplay
  friendly_name: EinkDisplay

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "KZfSEtCJbvI2000000000000000000e0LUu9Ih1b2BU="

ota:
  - platform: esphome
    password: "b62c4d1a00000000000000007b4e464d364"

globals:
  - id: wifi_status
    type: int
    restore_value: no
    initial_value: "0"
  - id: recorded_display_refresh
    type: int
    restore_value: yes
    initial_value: '0'
    
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  on_connect:
    then:
      - lambda: |-
          id(wifi_status) = 1;
  on_disconnect:
    then:
      - lambda: |-
          id(wifi_status) = 0;

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Einkdisplay Fallback Hotspot"
    password: "OReBfmKer3EX"

captive_portal:

# Here is deep sleep part
deep_sleep:
  id: deep_sleep_1
  run_duration: 1min  # Device wake up and run 60s (enough to pull data and update)
  sleep_duration: 60min  # deep sleep for 120m

http_request:
  verify_ssl: false
  timeout: 10s
  watchdog_timeout: 15s

online_image:
  - id: dashboard_image
    format: bmp
    type: BINARY
    buffer_size: 50000
    url: http://192.168.1.113:10000/dashboard-display?viewport=800x480&eink=2&invert&format=bmp #change this link to your screenshot link
    update_interval: 25s
    on_download_finished:
      - delay: 0ms
      - component.update: main_display

spi:
  clk_pin: GPIO7
  mosi_pin: GPIO9

display:
  - platform: waveshare_epaper
    id: main_display
    model: 7.50inv2
    cs_pin: GPIO44
    dc_pin: GPIO10
    reset_pin: GPIO38
    busy_pin: 
      number: GPIO4
      inverted: true
    update_interval: never
    lambda: |-
      it.image(0, 0, id(dashboard_image));
      
time:
  - platform: homeassistant
    id: homeassistant_time
          
sensor:

  - platform: adc
    pin: GPIO1
    name: "Battery Voltage"
    id: battery_voltage
    update_interval: 60s
    attenuation: 12db
    filters:
      - multiply: 2.0
  
  - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
    name: "WiFi Signal dB"
    id: wifi_signal_db
    update_interval: 120s
    entity_category: "diagnostic"

  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    id: wifi_signal_percent
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "Signal %"
    entity_category: "diagnostic"

  - platform: uptime
    name: Uptime

  - platform: internal_temperature
    name: "Internal Temperature"

  - platform: template
    name: "Display Last Update"
    device_class: timestamp
    entity_category: "diagnostic"
    id: display_last_update
    lambda: 'return id(homeassistant_time).now().timestamp;'

  - platform: template
    name: "Display Refresh Count"
    accuracy_decimals: 0
    unit_of_measurement: "Refreshes"
    state_class: "total_increasing"
    entity_category: "diagnostic"
    lambda: 'return id(recorded_display_refresh) += 1;'

Hi, my configuration looks like this with working battery information.


esphome:
  name: trmnl
  friendly_name: TRMNL
  on_boot:
    priority: 600
    then:
      - output.turn_on: bsp_battery_enable
      - delay: 200ms
      - component.update: battery_voltage
      - component.update: battery_level

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

psram:
  mode: octal
  speed: 80MHz

# Enable logging
logger:

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

ota:
  - platform: esphome
    password: "xxx"

globals:
  - id: wifi_status
    type: int
    restore_value: no
    initial_value: "0"
  - id: recorded_display_refresh
    type: int
    restore_value: yes
    initial_value: '0'
    
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  on_connect:
    then:
      - lambda: |-
          id(wifi_status) = 1;
  on_disconnect:
    then:
      - lambda: |-
          id(wifi_status) = 0;

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "TRMNL Fallback Hotspot"
    password: "xxx"

captive_portal:

deep_sleep:
  id: deep_sleep_1
  run_duration: 1min  # Device wake up and run 60s (enough to pull data and update)
  sleep_duration: 1min  # deep sleep for 30m

http_request:
  verify_ssl: false
  timeout: 10s
  watchdog_timeout: 15s

online_image:
  - id: dashboard_image
    format: PNG
    type: BINARY
    buffer_size: 50000
    url: http://192.168.1.100:10000/dashboard-trmnl?viewport=800x480&format=png&theme=Graphite+E-ink+Light&zoom=0.8&lang=de&invert=
    update_interval: 30s
    on_download_finished:
      - delay: 0ms
      - component.update: main_display

spi:
  clk_pin: GPIO7
  mosi_pin: GPIO9

display:
  - platform: waveshare_epaper
    id: main_display
    model: 7.50inv2
    cs_pin: GPIO44
    dc_pin: GPIO10
    reset_pin: GPIO38
    busy_pin: 
      number: GPIO4
      inverted: true
    update_interval: never
    lambda: |-
      it.image(0, 0, id(dashboard_image));

time:
  - platform: homeassistant
    id: homeassistant_time
          
sensor:
  - platform: adc
    pin: GPIO1
    name: "Voltage"
    id: battery_voltage
    device_class: voltage
    unit_of_measurement: "V"
    state_class: measurement
    entity_category: "diagnostic"
    update_interval: 60s
    attenuation: 12db
    filters:
      - multiply: 2.0

  - platform: template
    name: "Battery"
    id: battery_level
    device_class: battery
    unit_of_measurement: "%"
    state_class: measurement
    entity_category: "diagnostic"
    lambda: 'return id(battery_voltage).state;'
    update_interval: 60s
    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

  - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
    name: "WiFi Signal dB"
    id: wifi_signal_db
    update_interval: 120s
    entity_category: "diagnostic"

  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_signal_db
    name: "WiFi Signal Percent"
    id: wifi_signal_percent
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"

  - platform: uptime
    name: Uptime

  - platform: internal_temperature
    name: "Internal Temperature"

  - platform: template
    name: "Display Last Update"
    device_class: timestamp
    entity_category: "diagnostic"
    id: display_last_update
    lambda: 'return id(homeassistant_time).now().timestamp;'

  - platform: template
    name: "Display Refresh Counts"
    accuracy_decimals: 0
    unit_of_measurement: "Refreshes"
    state_class: "total_increasing"
    entity_category: "diagnostic"
    lambda: 'return id(recorded_display_refresh) += 1;'

output:
  - platform: gpio
    pin: GPIO6
    id: bsp_battery_enable

Thank you for the solution. I have tested it and it works.
I appreciate that you took the time to help me, I only noticed late that you had replied.

I see that you also set the PSRAM parameter. Is that required to load PNG files?
In my configuration, I changed it to BMP format because the PNG file was too large to load.

Yes, I read somewhere that it’s needed for PNG.

You should enable PSRAM in any case. Why to struggle with 0.5MB ram if your board actually has extra 8MB?

It indeed wasn’t very smart to struggle with 0.5 MB, but I managed to get it working using BMP file types and it worked fine until today. Yesterday I updated Home Assistant, and something changed in the theme, which caused the image to no longer display correctly. Now that I know how to use 8 MB, I’ve switched to PNG file types and choice other theme for the page