ESPHome Raspberry Pi turns a Pi 4 into a central dashboard for building, flashing, and managing firmware on ESP32 and ESP8266 devices. You define each device behaviour in a YAML file: sensors, GPIO outputs, Wi-Fi credentials, OTA settings. ESPHome ESPHome compiles and flashes the firmware directly. Once a device is flashed, all future updates go over Wi-Fi. This guide covers installing ESPHome on Bookworm, creating your first device YAML, wiring common sensors, configuring GPIO outputs, OTA updates, and Home Assistant integration.
Last tested: Raspberry Pi OS Bookworm Lite 64-bit | April 22, 2026 | Raspberry Pi 4 Model B (4GB) | ESPHome 2024.11 | ESP32-WROOM-32 + NodeMCU v3 (ESP8266)
Key Takeaways
- The first flash must be done over USB. After that, all firmware updates can be delivered over Wi-Fi via OTA as long as the device has a reachable IP address. Assign a static IP in the YAML or via router DHCP reservation to prevent OTA from failing after router reboots.
- Some ESP8266 GPIO pins affect boot mode. GPIO0 must be HIGH at boot, GPIO2 must be HIGH, and GPIO15 must be LOW. Connecting a sensor or relay to these pins without accounting for their boot state can prevent the device from starting.
- Install ESPHome with
pip install esphome --break-system-packageson Bookworm, notsudo pip3 install esphome. The latter installs into the system Python environment which Bookworm protects by default, producing a “externally managed environment” error.
ESPHome Raspberry Pi: How It Works
ESPHome is a framework for ESP32 and ESP8266 microcontrollers. You describe what a device should do in a YAML configuration file: which sensors are attached, which GPIO pins control outputs, how often to report readings, how to connect to Wi-Fi. ESPHome compiles that into C++ firmware and flashes it to the device. The Pi runs the ESPHome dashboard, a web interface on port 6052 where you manage all device configurations, trigger builds, and monitor live device logs.
After initial USB flashing, devices connect back to the Pi over Wi-Fi using the ESPHome native API. From the dashboard you can push firmware updates wirelessly, view real-time sensor readings, and check connectivity status for every device on the network.
| Feature | ESPHome | Tasmota | Arduino IDE |
|---|---|---|---|
| Configuration method | YAML files | Web UI + rules | C++ code |
| Home Assistant integration | Native API, automatic discovery | MQTT | Manual |
| OTA updates | Built-in, dashboard-managed | Built-in | Manual |
| Custom sensor support | Extensive component library | Limited | Full |
| Skill required | YAML editing | Web UI | C++ programming |
Installation on Raspberry Pi OS Bookworm
Flash Raspberry Pi OS Bookworm Lite 64-bit using Raspberry Pi Imager. In the advanced settings, set hostname, enable SSH, and configure credentials. After first boot:
sudo apt update && sudo apt full-upgrade -y
sudo apt install python3-pip python3-venv -y
Install ESPHome into a virtual environment to keep it isolated from the system Python:
python3 -m venv ~/esphome-venv
source ~/esphome-venv/bin/activate
pip install esphome
esphome version
Alternatively, install directly with the Bookworm-compatible flag:
pip install esphome --break-system-packages
esphome version
Create the config directory and start the dashboard:
mkdir -p ~/esphome/config
esphome dashboard ~/esphome/config/
Navigate to http://<pi-ip>:6052 in a browser. The dashboard loads with an option to create a new device. Set a dashboard username and password when prompted on first access.
To run the dashboard as a systemd service that starts at boot, create /etc/systemd/system/esphome-dashboard.service:
[Unit]
Description=ESPHome Dashboard
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/esphome
ExecStart=/home/pi/esphome-venv/bin/esphome dashboard config/
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now esphome-dashboard
sudo systemctl status esphome-dashboard
Expected result: systemctl status esphome-dashboard shows the service active. The dashboard is accessible at http://<pi-ip>:6052 after a reboot without manual intervention.

Creating a Device Configuration
From the dashboard, click New Device. Enter a device name (lowercase, no spaces), select the platform (ESP32 or ESP8266), and enter Wi-Fi credentials. ESPHome generates a starter YAML file. A minimal working config for a NodeMCU (ESP8266) with a DHT22 temperature and humidity sensor:
esphome:
name: livingroom-sensor
esp8266:
board: nodemcuv2
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.1.150
gateway: 192.168.1.1
subnet: 255.255.255.0
logger:
api:
encryption:
key: !secret api_encryption_key
ota:
password: !secret ota_password
sensor:
- platform: dht
pin: GPIO14
model: DHT22
temperature:
name: "Living Room Temperature"
device_class: temperature
state_class: measurement
humidity:
name: "Living Room Humidity"
device_class: humidity
state_class: measurement
update_interval: 60s
The !secret references pull values from a secrets.yaml file in the same directory, keeping credentials out of device configs:
# secrets.yaml
wifi_ssid: "YourNetworkName"
wifi_password: "YourPassword"
api_encryption_key: "base64-encoded-32-byte-key"
ota_password: "strong-ota-password"
Validate the config before flashing:
esphome config ~/esphome/config/livingroom-sensor.yaml
Expected result: esphome config returns no errors. Any YAML formatting errors, unknown component names, or missing required fields are reported with line numbers before a flash attempt is made.
NodeMCU (ESP8266) GPIO pin mapping
The NodeMCU board labels pins D0 through D8, but ESPHome uses GPIO numbers. Reference:
| Board label | GPIO number | Boot note |
|---|---|---|
| D0 | GPIO16 | No PWM, no interrupt |
| D1 | GPIO5 | Safe general use |
| D2 | GPIO4 | Safe general use |
| D3 | GPIO0 | Must be HIGH at boot |
| D4 | GPIO2 | Must be HIGH at boot, onboard LED |
| D5 | GPIO14 | Safe general use |
| D6 | GPIO12 | Safe general use |
| D7 | GPIO13 | Safe general use |
| D8 | GPIO15 | Must be LOW at boot |
Wiring Common Sensors and Outputs
DHT22 temperature and humidity sensor
Wire VCC to 3.3V, GND to GND, and DATA to GPIO14 (D5). Place a 10k pull-up resistor between VCC and DATA for reliable readings. The sensor does not work reliably without the pull-up at 3.3V. Use GPIO numbers in the YAML config, not board labels.
PIR motion sensor (HC-SR501)
The HC-SR501 output pin pushes 3.3V on some modules and 5V on others. Check yours with a multimeter before connecting it to an ESP GPIO. A 5V signal on a 3.3V GPIO will damage the ESP. Use a voltage divider or level shifter if needed. Wire VCC to 5V, GND to GND, OUT to a safe GPIO.
binary_sensor:
- platform: gpio
pin: GPIO5
name: "Motion Sensor"
device_class: motion
filters:
- delayed_on: 50ms
- delayed_off: 2s
Relay or LED output
For a relay or LED controlled via GPIO, define a switch component. The relay module draws its control signal from the GPIO pin. Never power the relay coil from the GPIO directly, use the module’s VCC/GND pins from a 5V supply:
switch:
- platform: gpio
pin: GPIO13
id: relay_output
name: "Relay Control"
Button input with action
binary_sensor:
- platform: gpio
pin:
number: GPIO0
mode: INPUT_PULLUP
inverted: true
name: "Button"
on_press:
- switch.toggle: relay_output
INPUT_PULLUP keeps the pin HIGH until the button pulls it LOW. inverted: true means press = ON. The on_press action references the relay switch by its id.
I2C sensors (CCS811, BME280, SSD1306)
I2C sensors share SDA and SCL lines. On ESP8266, SDA is typically GPIO4 (D2) and SCL is GPIO5 (D1). On ESP32, you can assign any pins. Declare the I2C bus once at the top of the config:
i2c:
sda: GPIO4
scl: GPIO5
scan: true
sensor:
- platform: bme280_i2c
temperature:
name: "BME280 Temperature"
pressure:
name: "BME280 Pressure"
humidity:
name: "BME280 Humidity"
address: 0x76
update_interval: 30s
OTA Updates and Device Management
The first flash requires a USB connection between the Pi (or your computer) and the ESP device. After the device boots and connects to Wi-Fi, all subsequent firmware updates go over the network. From the dashboard, click Install on any device and choose Wirelessly. The dashboard compiles the firmware, transfers it to the device, and the device reboots into the new firmware automatically.
From the CLI:
# First flash (USB required)
esphome run ~/esphome/config/livingroom-sensor.yaml
# Subsequent OTA updates
esphome upload ~/esphome/config/livingroom-sensor.yaml
# Monitor live logs
esphome logs ~/esphome/config/livingroom-sensor.yaml
If OTA fails, the most common causes are: the device IP changed (fix with a static IP in the YAML or a DHCP reservation on the router), the OTA password does not match the one in secrets.yaml, or the device is not reachable from the Pi. Confirm with:
ping 192.168.1.150
esphome logs ~/esphome/config/livingroom-sensor.yaml --device 192.168.1.150
Expected result: esphome logs connects and streams sensor readings every 60 seconds. You see entries like [D][dht:048]: Got Temperature=22.4°C Humidity=47.8%. A device that connects to Wi-Fi but shows no sensor readings usually has a wiring problem or wrong GPIO number.
Log verbosity
Set the log level in the YAML logger: block. DEBUG is useful during development; reduce to WARN or ERROR in production to reduce log noise and serial overhead:
logger:
level: DEBUG
Home Assistant Integration
When a device has the api: component in its YAML and Home Assistant is on the same network, Home Assistant auto-discovers the device and prompts to add it. No MQTT broker is needed for this path. The ESPHome native API is direct and encrypted. Accept the discovery notification in Home Assistant under Settings > Devices and Services > Discovered.
All entities defined in the YAML (sensors, switches, binary sensors) appear automatically in Home Assistant after pairing. Changes to YAML that add or rename entities are reflected in Home Assistant after the next OTA update and a Home Assistant page reload.
For setups where you want ESPHome devices to communicate via MQTT rather than the native API (for example, if Home Assistant is not running or you want to route messages through a Mosquitto broker), replace api: with an MQTT component:
mqtt:
broker: 192.168.1.80
username: sensor1
password: !secret mqtt_password
topic_prefix: esphome/livingroom-sensor
For the Mosquitto broker setup including TLS and ACL configuration, see Mosquitto MQTT Raspberry Pi. For Home Assistant on Pi, see Home Assistant Raspberry Pi.
Troubleshooting
Device fails to boot after flash
A device that reboots continuously after flashing usually has a GPIO conflict at boot. Review the GPIO pin mapping table. If you assigned GPIO0, GPIO2, or GPIO15 to a sensor or relay, the physical connection may be pulling that pin to the wrong state. Disconnect the sensor, reflash, and confirm the device boots cleanly before reconnecting.
esphome command not found
# If using venv
source ~/esphome-venv/bin/activate
esphome version
# If installed with --break-system-packages
which esphome
# If not found:
export PATH=$PATH:~/.local/bin
YAML validation errors
esphome config ~/esphome/config/livingroom-sensor.yaml
Run this before every flash. ESPHome reports the exact line and component causing the error. Common causes: indentation errors (YAML is whitespace-sensitive), wrong platform name (esp8266 not ESP8266), missing required fields, or referencing an id that does not exist.
No sensor readings in logs
Confirm wiring with a multimeter: VCC is at the correct voltage, GND is connected, and the data pin is connected to the GPIO number in the YAML (not the board label). For the DHT22, check the pull-up resistor is present. For I2C sensors, run scan: true in the i2c: block and check the log for detected addresses. A missing address means a wiring problem or wrong I2C address in the config.
Dashboard service not starting after reboot
journalctl -u esphome-dashboard -n 30
The most common cause is that the ExecStart path in the service file does not match the actual ESPHome binary location. If using a virtual environment, the path must point to the venv binary: /home/pi/esphome-venv/bin/esphome. Confirm with which esphome while the venv is active.
FAQ
Can ESPHome run on a Pi Zero 2 W?
Yes. The dashboard runs fine on a Pi Zero 2 W for a small number of devices. The limitation is compile time. Building firmware for an ESP32 takes 2-3 minutes on a Pi Zero 2 W versus under a minute on a Pi 4. For a network with more than 5-10 devices getting regular OTA updates, Pi 4 is the practical choice.
Does ESPHome work without Home Assistant?
Yes. ESPHome is independent of Home Assistant. You can use it with any MQTT broker, read sensor data from the native API with custom scripts, or simply log data locally. Home Assistant provides the most seamless integration but is not required.
Can I use ESP8266 and ESP32 devices together?
Yes. Each device has its own YAML config specifying its platform. The dashboard manages both simultaneously. ESP32 supports more GPIO pins, more ADC channels, Bluetooth, and faster processing. ESP8266 is smaller, cheaper, and adequate for simple sensor and relay work.
How do I add HTTPS to the ESPHome dashboard?
Put Caddy in front of the dashboard as a reverse proxy. ESPHome serves on port 6052 on the Pi. Caddy terminates TLS and proxies to it. See Caddy Reverse Proxy Raspberry Pi for the full setup. The Caddyfile entry is a single block pointing to localhost:6052.
What if a device is physically inaccessible and OTA fails?
If a bad firmware update breaks Wi-Fi connectivity and the device is physically unreachable, the only recovery option is serial flash. The ESPHome web flasher at web.esphome.io works in Chrome via WebSerial without installing anything. Download a known-good firmware binary from the dashboard (Install > Manual Download), then flash it via the web flasher using a USB cable. This recovers the device and re-enables OTA for future updates.
References
- https://esphome.io/index.html
- https://esphome.io/components/sensor/dht.html
- https://esphome.io/guides/getting_started_command_line.html
- https://esphome.io/components/ota/
About the Author
Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.
Last tested hardware: Raspberry Pi 4 Model B (4GB), ESP32-WROOM-32, NodeMCU v3 (ESP8266), DHT22, HC-SR501. Last tested OS: Raspberry Pi OS Bookworm Lite 64-bit. ESPHome 2024.11.

