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
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.
- You can even see
- 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
-edconfig.h
). - Config validation and autocompletion, thanks to the defined schema.
- Secret management using
- 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 (1, 2). 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 * * * *
).