Tenki Hari (Weather Needles, from ja: 天気針)

A minimal weather forecast display gadget – and an experiment in building a battery-powered IoT device with ESPHome.

Intro: How I Learned to Stop Worrying and Love ESPHome

ESP32-C3 Super Mini

Building home IoT gadgets with embedded programming is fun. However, keeping track of devices scattered around the house ("What was that hostname?"), managing over-the-air software updates, and integrating with Home Assistant... not so much.

That's where ESPHome comes in. Think of it as Ansible or Kubernetes for ESP microcontrollers (beware, there's a lot of explicit YAML contents ahead). It's somewhat similar to Tasmota, but in my experience, Tasmota is more focused on being an alternative firmware for ESP-based IoT devices.
ESPHome, while it can certainly be used that way, really shines when it comes to customization.

Here's what I like about ESPHome:

  • Multiple flashing methods supported: USB, Web Serial!, OTA, via Web UI, and even a full-featured CLI.
    • You can even see ESP_LOGD() logs over the air.
  • YAML-based device definitions: (Yes, it's a paved road to YAML HELL. Keep it simple, stupid!)
    • Secret management using include YAMLs (about as good as a .gitignore-ed config.h).
    • Config validation and autocompletion, thanks to the defined schema.
  • Simple device management dashboard:: Again, CLI is supported, meaning it works headless.
  • Automatic Home Assistant integration: Devices are auto-detected with HA entities.
  • Extensive peripheral/external device support: e.g., SPI/I2C sensors, etc.

For example, add this to your device's YAML and flash:

switch:
  - platform: gpio
    name: "Charge Port 1"
    pin: GPIO4
    restore_mode: RESTORE_DEFAULT_ON

Voila! Before you can even finish sipping your coffee, a switch entity appears in your Home Assistant instance.

Sure, it takes away some of the coding fun. However, you can still inject C++ snippets using the lambda: syntax. Black magic? Maybe. ESPHome is also can be considered as a wrapper that generates Arduino or esp-idf based firmware from your YAML definitions.

Installation (Quick Overview)

The official ESPHome Getting Started guide covers installation, but in my case, it was as simple as adding 10 lines to my Home Assistant docker-compose.yaml:

  esphome:
    container_name: esphome
    image: ghcr.io/esphome/esphome:2024.12.4
    volumes:
      - ./esphome/config:/config
      - /etc/localtime:/etc/localtime:ro
    restart: always
    privileged: true
    network_mode: host # For mDNS
    env_file:
      - esphome.env # This contains `USERNAME` and `PASSWORD`

Then, just navigated to http://localhost:6052, clicked NEW DEVICE, and followed the wizard. ESPHome creates a <device-name>.yaml file to your local filesystem, which you can edit via the dashboard and update via Install > Wirelessly.

With this setup, you also need to register the device with Home Assistant for the first time. When the device is discovered in "Integrations," copy and paste the API key from ESPHome.

One quick note: for initial flashing via the dashboard (using Web Serial), you'll need localhost or https://. SSH port forwarding (-L 6052:localhost:6052) to your home lab server, or you can set up HTTPS with Caddy or Tailscale. Alternatively, use the ESPHome Flasher or the CLI (docker compose exec esphome).

Building a Battery-Powered Device with ESPHome

I've built several battery-powered devices in the past, primarily using Arduino (12). Could I build something similar with ESPHome? (Spoiler: Yes!)

Here's the goal:

  • An ESP32-C3 powered by 4 x NiMH AA batteries, lasting for at least 6 months.
  • Wakes up hourly, retrieves the weather forecast for a few hours ahead, and moves a servo to indicate the conditions.
  • Wakes up at specific times (not just periodically), like 5 minutes past the hour, every hour (5 * * * *).
... Read more »