Timothy Lovett
Published © CC BY-NC-SA

Hive Helper - Xiao ESP32C3 ePaper Home Assistant Display

Building a Xiao ESP32C3 controlled ePaper display with expansion ports for i2c and uart.

IntermediateFull instructions provided10 hours748
Hive Helper - Xiao ESP32C3 ePaper Home Assistant Display

Things used in this project

Hardware components

Seeed Studio XIAO ESP32C3
Seeed Studio XIAO ESP32C3
×1
WeAct 2.9'' Epaper Module
×1
Hive Helper PCB
×1
M3 Screw 20mm
×4
Adafruit Heat-Set Insert
×1
Grove - Multichannel Gas Sensor v2
Seeed Studio Grove - Multichannel Gas Sensor v2
×1
Grove Flame Sensor
×1

Story

Read more

Custom parts and enclosures

Hive Helper Standoff

Standoff used between PCB and epaper display
4x needed

Sketchfab still processing.

Hive Helper Spacer

Spacer used on screws to keep epaper display securely in place
4x needed

Sketchfab still processing.

Hive Helper Backing

Backing piece for Hive Helper
1x

Sketchfab still processing.

Hive Helper Face

Front face piece for Hive Helper
1x

Sketchfab still processing.

Schematics

Hive Helper Schematic

Code

Hive Helper ESPHome Yaml

YAML
Yaml file for Hive Helper's ESPHome configuration
esphome:
  name: hive-helper
  friendly_name: Hive Helper

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino

external_components:
  - source: github://wifwucite/esphome-grove-multi-gas-v2

# Enable logging
logger:

i2c:
  sda: 6
  scl: 7
  scan: true

# Enable Home Assistant API
api:
  encryption:
    key: !secret hive_helper_api_key

ota:
  - platform: esphome
    password: !secret hive_helper_ota

binary_sensor:
  - platform: gpio
    pin:
      number: 20
      inverted: true
    name: "Flame Alert"
  - platform: gpio
    pin: 9
    name: "Hive Button"
    id: hive_button
    on_press:
      then:        
        - display.page.show_next: my_display
        - component.update: my_display

text_sensor:
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: today_condition
    id: weather_condition_now
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day1_condition
    id: weather_condition_day1
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day2_condition
    id: weather_condition_day2
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day3_condition
    id: weather_condition_day3
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day4_condition
    id: weather_condition_day4
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day5_condition
    id: weather_condition_day5

sensor:
  - platform: grove_multigas_v2
    NO2:
      name: "NO2"
      id: no2_sensor
    C2H5OH:
      name: "EtOH"
      id: etoh_sensor
    VOC:
      name: "VOC"
      id: voc_sensor
    CO:
      name: "CO"
      id: co_sensor
    update_interval: 10s
  - platform: aht10
    variant: AHT20
    temperature:
      id: aht20_temperature
      name: Hive Temperature
      filters:
        - offset: -2.0
    humidity:
      id: aht20_humidity
      name: Hive Humidity
    update_interval: 60s
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: today_temperature
    id: weather_temperature_now
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: today_humidity
    id: weather_humidity_now
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: today_precipitation
    id: weather_precipitation_now
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day1_temperature
    id: weather_temperature_day1
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day1_humidity
    id: weather_humidity_day1
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day1_precipitation
    id: weather_precipitation_day1
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day2_temperature
    id: weather_temperature_day2
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day2_humidity
    id: weather_humidity_day2
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day2_precipitation
    id: weather_precipitation_day2
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day3_temperature
    id: weather_temperature_day3
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day3_humidity
    id: weather_humidity_day3
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day3_precipitation
    id: weather_precipitation_day3
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day4_temperature
    id: weather_temperature_day4
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day4_humidity
    id: weather_humidity_day4
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day4_precipitation
    id: weather_precipitation_day4
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day5_temperature
    id: weather_temperature_day5
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day5_humidity
    id: weather_humidity_day5
  - platform: homeassistant
    entity_id: sensor.daily_weather
    attribute: day5_precipitation
    id: weather_precipitation_day5
font:
  - file: "fonts/BebasNeue-Regular.ttf"
    id: font_time
    size: 20
  - file: "fonts/BebasNeue-Regular.ttf"
    id: font_mini_metric
    size: 18
  - file: "fonts/BebasNeue-Regular.ttf"
    id: font_metric
    size: 30
  # Include Material Design Icons font
  # Thanks to https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16
  # Thanks to https://community.home-assistant.io/t/use-esphome-with-e-ink-displays-to-blend-in-with-your-home-decor/435428 for reference
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi
    size: 26
    glyphs: &mdi-weather-glyphs
      - "\U000F0590" # mdi-weather-cloudy
      - "\U000F0F2F" # mdi-weather-cloudy-alert
      - "\U000F0E6E" # mdi-weather-cloudy-arrow-right
      - "\U000F0591" # mdi-weather-fog
      - "\U000F0592" # mdi-weather-hail
      - "\U000F0F30" # mdi-weather-hazy
      - "\U000F0898" # mdi-weather-hurricane
      - "\U000F0593" # mdi-weather-lightning
      - "\U000F067E" # mdi-weather-lightning-rainy
      - "\U000F0594" # mdi-weather-night
      - "\U000F0F31" # mdi-weather-night-partly-cloudy
      - "\U000F0595" # mdi-weather-partly-cloudy
      - "\U000F0F32" # mdi-weather-partly-lightning
      - "\U000F0F33" # mdi-weather-partly-rainy
      - "\U000F0F34" # mdi-weather-partly-snowy
      - "\U000F0F35" # mdi-weather-partly-snowy-rainy
      - "\U000F0596" # mdi-weather-pouring
      - "\U000F0597" # mdi-weather-rainy
      - "\U000F0598" # mdi-weather-snowy
      - "\U000F0F36" # mdi-weather-snowy-heavy
      - "\U000F067F" # mdi-weather-snowy-rainy
      - "\U000F0599" # mdi-weather-sunny
      - "\U000F0F37" # mdi-weather-sunny-alert
      - "\U000F14E4" # mdi-weather-sunny-off
      - "\U000F059A" # mdi-weather-sunset
      - "\U000F059B" # mdi-weather-sunset-down
      - "\U000F059C" # mdi-weather-sunset-up
      - "\U000F0F38" # mdi-weather-tornado
      - "\U000F059D" # mdi-weather-windy
      - "\U000F059E" # mdi-weather-windy-variant
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_mdi_large
    size: 35
    glyphs: *mdi-weather-glyphs
spi:
  clk_pin: 8
  mosi_pin: 10

time:
  - platform: homeassistant
    id: homeassistant_time

display:
  - platform: waveshare_epaper
    model: 2.90inV2
    cs_pin: 5
    dc_pin: 4
    busy_pin: 2
    reset_pin: 3
    rotation: 270
    full_update_every: 1
    update_interval: 60s
    id: my_display
    pages:
      - id: page1
        lambda: |-
          auto time_now = id(homeassistant_time).now();
          auto weekday_1 = time_now;
          weekday_1.increment_day();
          auto weekday_2 = weekday_1;
          weekday_2.increment_day();
          auto weekday_3 = weekday_2;
          weekday_3.increment_day();
          auto weekday_4 = weekday_3;
          weekday_4.increment_day();
          auto weekday_5 = weekday_4;
          weekday_5.increment_day();
          std::map<std::string, std::string> weather_icon_map
            {
              {"cloudy", "\U000F0590"},
              {"cloudy-alert", "\U000F0F2F"},
              {"cloudy-arrow-right", "\U000F0E6E"},
              {"fog", "\U000F0591"},
              {"hail", "\U000F0592"},
              {"hazy", "\U000F0F30"},
              {"hurricane", "\U000F0898"},
              {"lightning", "\U000F0593"},
              {"lightning-rainy", "\U000F067E"},
              {"night", "\U000F0594"},
              {"night-partly-cloudy", "\U000F0F31"},
              {"partlycloudy", "\U000F0595"},
              {"partly-lightning", "\U000F0F32"},
              {"partly-rainy", "\U000F0F33"},
              {"partly-snowy", "\U000F0F34"},
              {"partly-snowy-rainy", "\U000F0F35"},
              {"pouring", "\U000F0596"},
              {"rainy", "\U000F0597"},
              {"snowy", "\U000F0598"},
              {"snowy-heavy", "\U000F0F36"},
              {"snowy-rainy", "\U000F067F"},
              {"sunny", "\U000F0599"},
              {"sunny-alert", "\U000F0F37"},
              {"sunny-off", "\U000F14E4"},
              {"sunset", "\U000F059A"},
              {"sunset-down", "\U000F059B"},
              {"sunset-up", "\U000F059C"},
              {"tornado", "\U000F0F38"},
              {"windy", "\U000F059D"},
              {"windy-variant", "\U000F059E"},
            };
            it.strftime(5, 10, id(font_time), "%a %B %d, %Y - %I:%M %p", time_now);
            it.printf(240, 10, id(font_mdi_large), "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str());
            it.printf(230, 50, id(font_metric), "%.1fΒ°", id(weather_temperature_now).state);
            it.printf(230, 90, id(font_metric), "%.1f%%", id(weather_humidity_now).state);
            it.strftime(8, 42, id(font_mini_metric), "%a", weekday_1);
            it.strftime(48, 42, id(font_mini_metric), "%a", weekday_2);
            it.strftime(88, 42, id(font_mini_metric), "%a", weekday_3);
            it.strftime(128, 42, id(font_mini_metric), "%a", weekday_4);
            it.strftime(168, 42, id(font_mini_metric), "%a", weekday_5);
            it.printf(5, 60, id(font_mdi), "%s", weather_icon_map[id(weather_condition_day1).state.c_str()].c_str());
            it.printf(45, 60, id(font_mdi), "%s", weather_icon_map[id(weather_condition_day2).state.c_str()].c_str());
            it.printf(85, 60, id(font_mdi), "%s", weather_icon_map[id(weather_condition_day3).state.c_str()].c_str());
            it.printf(125, 60, id(font_mdi), "%s", weather_icon_map[id(weather_condition_day4).state.c_str()].c_str());
            it.printf(165, 60, id(font_mdi), "%s", weather_icon_map[id(weather_condition_day5).state.c_str()].c_str());
            it.printf(6, 85, id(font_mini_metric), "%.1fΒ°", id(weather_temperature_day1).state);
            it.printf(46, 85, id(font_mini_metric), "%.1fΒ°", id(weather_temperature_day2).state);
            it.printf(86, 85, id(font_mini_metric), "%.1fΒ°", id(weather_temperature_day3).state);
            it.printf(126, 85, id(font_mini_metric), "%.1fΒ°", id(weather_temperature_day4).state);
            it.printf(166, 85, id(font_mini_metric), "%.1fΒ°", id(weather_temperature_day5).state);
            it.printf(6, 105, id(font_mini_metric), "%.1f%%", id(weather_humidity_day1).state);
            it.printf(46, 105, id(font_mini_metric), "%.1f%%", id(weather_humidity_day2).state);
            it.printf(86, 105, id(font_mini_metric), "%.1f%%", id(weather_humidity_day3).state);
            it.printf(126, 105, id(font_mini_metric), "%.1f%%", id(weather_humidity_day4).state);
            it.printf(166, 105, id(font_mini_metric), "%.1f%%", id(weather_humidity_day5).state);
      - id: page2
        lambda: |- 
          auto time_now = id(homeassistant_time).now();
          it.strftime(5, 10, id(font_time), "%a %B %d, %Y - %I:%M %p", time_now);

          it.printf(30, 45, id(font_mini_metric), "EtOH");
          it.printf(20, 60, id(font_mini_metric), "%.1f ppm", id(etoh_sensor).state);
          it.printf(30, 85, id(font_mini_metric), "NO2");
          it.printf(20, 105, id(font_mini_metric), "%.1f ppm", id(no2_sensor).state);
          it.printf(90, 45, id(font_mini_metric), "VOC");
          it.printf(80, 60, id(font_mini_metric), "%.1f ppm", id(voc_sensor).state);
          it.printf(90, 85, id(font_mini_metric), "CO");
          it.printf(80, 105, id(font_mini_metric), "%.1f ppm", id(co_sensor).state);

          it.printf(230, 10, id(font_metric), "ROOM");
          it.printf(230, 50, id(font_metric), "%.1fΒ°", id(aht20_temperature).state * 1.8 + 32);
          it.printf(230, 90, id(font_metric), "%.1f%%", id(aht20_humidity).state);

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true
  use_address: hive-helper.wild

captive_portal:
    

Hive Helper Home Assistant Sensor Setup

YAML
Setup logic for Home Assistant Sensor
template:
  - trigger:
      - platform: time_pattern
        hours: /1
    action:
      - service: weather.get_forecasts
        target:
          entity_id: weather.forecast_home
        data:
          type: daily
        response_variable: daily
    sensor:
      - name: Daily Weather
        unique_id: weather_forecast_daily
        state: "{{ daily['weather.forecast_home'].forecast[0]['condition'] }}"
        attributes:
          today_condition: "{{ daily['weather.forecast_home'].forecast[0]['condition'] }}"
          today_humidity: "{{ daily['weather.forecast_home'].forecast[0]['humidity'] }}"
          today_temperature: "{{ daily['weather.forecast_home'].forecast[0]['temperature'] }}"
          today_precipitation: "{{ daily['weather.forecast_home'].forecast[0]['precipitation'] }}"
          day1_condition: "{{ daily['weather.forecast_home'].forecast[1]['condition'] }}"
          day1_humidity: "{{ daily['weather.forecast_home'].forecast[1]['humidity'] }}"
          day1_temperature: "{{ daily['weather.forecast_home'].forecast[1]['temperature'] }}"
          day1_precipitation: "{{ daily['weather.forecast_home'].forecast[1]['precipitation'] }}"
          day2_condition: "{{ daily['weather.forecast_home'].forecast[2]['condition'] }}"
          day2_humidity: "{{ daily['weather.forecast_home'].forecast[2]['humidity'] }}"
          day2_temperature: "{{ daily['weather.forecast_home'].forecast[2]['temperature'] }}"
          day2_precipitation: "{{ daily['weather.forecast_home'].forecast[2]['precipitation'] }}"
          day3_condition: "{{ daily['weather.forecast_home'].forecast[3]['condition'] }}"
          day3_humidity: "{{ daily['weather.forecast_home'].forecast[3]['humidity'] }}"
          day3_temperature: "{{ daily['weather.forecast_home'].forecast[3]['temperature'] }}"
          day3_precipitation: "{{ daily['weather.forecast_home'].forecast[3]['precipitation'] }}"
          day4_condition: "{{ daily['weather.forecast_home'].forecast[4]['condition'] }}"
          day4_humidity: "{{ daily['weather.forecast_home'].forecast[4]['humidity'] }}"
          day4_temperature: "{{ daily['weather.forecast_home'].forecast[4]['temperature'] }}"
          day4_precipitation: "{{ daily['weather.forecast_home'].forecast[4]['precipitation'] }}"
          day5_condition: "{{ daily['weather.forecast_home'].forecast[5]['condition'] }}"
          day5_humidity: "{{ daily['weather.forecast_home'].forecast[5]['humidity'] }}"
          day5_temperature: "{{ daily['weather.forecast_home'].forecast[5]['temperature'] }}"
          day5_precipitation: "{{ daily['weather.forecast_home'].forecast[5]['precipitation'] }}"

Credits

Timothy Lovett

Timothy Lovett

16 projects β€’ 12 followers
Maker. I spent over a decade working on backend systems in various languages.

Comments