ESPHome: Program ESP32 Without Code

Use ESPHome to configure ESP32 devices with YAML and integrate them into Home Assistant

Writing Arduino code is powerful, but sometimes you just want a sensor that works without debugging C++. That is where ESPHome comes in. ESPHome lets you configure ESP32 devices using simple YAML files -- describe what hardware is connected and how it should behave, and ESPHome generates the firmware for you.

In this article, you will install ESPHome, create your first device, and see it appear automatically in Home Assistant.

🔗What Is ESPHome?

ESPHome is a system that turns YAML configuration files into compiled firmware for ESP32 (and ESP8266) devices. You describe your hardware -- "I have a DHT22 on GPIO 4" -- and ESPHome generates, compiles, and flashes the firmware. No C++ needed.

graph LR
    A[YAML Config] -->|compile| B[ESPHome]
    B -->|flash USB / OTA| C[ESP32]
    C -->|auto-discovery| D[Home Assistant]

Key features:

  • YAML-based -- readable, version-controllable configuration
  • Auto-discovery -- devices appear in Home Assistant automatically
  • OTA updates -- after the first USB flash, update wirelessly over your network
  • Logs -- view real-time logs from the device in your browser
  • 1,800+ supported components -- sensors, displays, LEDs, motors, and more
  • Home Assistant native API -- direct connection, no MQTT broker needed

🔗Installing ESPHome

If you followed the Home Assistant setup guide, ESPHome is available as an add-on:

  1. Go to Settings > Add-ons > Add-on Store.
  2. Search for ESPHome.
  3. Click Install, wait for it to finish, then click Start.
  4. Enable Show in sidebar for quick access.

You now have an ESPHome dashboard accessible from Home Assistant's sidebar.

🔗Standalone (Without Home Assistant)

You can also run ESPHome standalone on your computer using pip:

pip install esphome

Then use the command line to create and flash devices:

esphome wizard my_device.yaml   # Create a new config interactively
esphome run my_device.yaml      # Compile and flash
esphome logs my_device.yaml     # View live logs

The Home Assistant add-on is more convenient, but the standalone option works well if you prefer the command line or do not have Home Assistant yet.

🔗Your First ESPHome Device

Let us create a device with a DHT22 temperature/humidity sensor and an LED connected to an ESP32.

🔗Wiring

ComponentPinESP32 PinNotes
DHT22VCC3.3VPower
DHT22GNDGNDGround
DHT22DATAGPIO 4Add a $10\,\text{k}\Omega$ pull-up resistor to 3.3V
LEDAnode (+)GPIO 2Through a $220\,\Omega$ resistor
LEDCathode (-)GND

Many ESP32 DevKit boards have a built-in LED on GPIO 2. If yours does, you can skip the external LED and just use the onboard one.

🔗Step 1: Create the Device

  1. Open the ESPHome dashboard (from the Home Assistant sidebar or at http://<HA_IP>:6052).
  2. Click New Device.
  3. Give it a name: living-room-sensor (use lowercase with hyphens, no spaces).
  4. Select ESP32 as the device type.
  5. ESPHome will generate a basic configuration. Click Install and choose Plug into this computer for the first flash.

🔗Step 2: Edit the Configuration

After the initial setup, click Edit on the device card. Replace the generated configuration with this:

esphome:
  name: living-room-sensor
  friendly_name: Living Room Sensor

esp32:
  board: esp32dev

# WiFi connection
wifi:
  ssid: "YourNetworkName"
  password: "YourPassword"

  # Fallback hotspot in case WiFi fails
  ap:
    ssid: "Living-Room-Sensor"
    password: "fallback123"

# Enable Home Assistant API
api:
  encryption:
    key: "auto-generated-key-here"

# Enable OTA updates
ota:
  - platform: esphome
    password: "auto-generated-password"

# Enable logging
logger:

# DHT22 temperature and humidity sensor
sensor:
  - platform: dht
    pin: GPIO4
    model: DHT22
    temperature:
      name: "Temperature"
      unit_of_measurement: "°C"
      accuracy_decimals: 1
    humidity:
      name: "Humidity"
      unit_of_measurement: "%"
      accuracy_decimals: 1
    update_interval: 30s

# LED as a controllable light
light:
  - platform: binary
    name: "Status LED"
    output: led_output

output:
  - platform: gpio
    id: led_output
    pin: GPIO2

Security note: The api encryption key and ota password are automatically generated when you create the device. Keep them as-is -- they secure communication between the ESP32 and Home Assistant.

🔗Step 3: Flash the Firmware

  1. Connect your ESP32 to your computer via USB.
  2. In the ESPHome dashboard, click the three-dot menu on your device and select Install.
  3. Choose Plug into this computer (for the first flash).
  4. Select the correct serial port.
  5. Wait for the compilation and upload to complete.

The first flash must be done over USB. After that, you can flash wirelessly (OTA) -- just select Wirelessly instead of Plug into this computer.

🔗Step 4: See It in Home Assistant

After flashing, the ESP32 will connect to your WiFi and announce itself to Home Assistant.

  1. In Home Assistant, go to Settings > Devices & Services.
  2. You should see a notification that a new ESPHome device was discovered.
  3. Click Configure and enter the API encryption key if prompted.
  4. The device and its entities (temperature, humidity, LED) will appear automatically.

You can now see the temperature and humidity on your dashboard, and toggle the LED from the Home Assistant interface.

🔗YAML Configuration Explained

ESPHome's YAML structure is straightforward. Every configuration file has these core sections:

# Identity
esphome:
  name: device-name          # Unique name (used as hostname)
  friendly_name: My Device   # Display name in Home Assistant

# Hardware
esp32:
  board: esp32dev             # Board type (esp32dev works for most DevKit boards)

# Network
wifi:
  ssid: "..."
  password: "..."

# Home Assistant connection
api:
  encryption:
    key: "..."

# Over-the-air updates
ota:
  - platform: esphome

# Serial logging
logger:

# Components (sensors, lights, switches, etc.)
sensor:
  - platform: ...
light:
  - platform: ...

The sensor, light, switch, and binary_sensor sections are where you define your hardware components. Each platform has its own set of options -- the ESPHome documentation lists them all.

🔗More YAML Examples

🔗Binary Sensor (Push Button)

A physical button connected between GPIO 14 and GND, exposed to Home Assistant as a binary sensor:

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO14
      mode: INPUT_PULLUP
      inverted: true
    name: "Desk Button"
    device_class: none
    filters:
      - delayed_on: 50ms    # Debounce
      - delayed_off: 50ms

The INPUT_PULLUP mode enables the ESP32's internal pull-up resistor, so you only need a button between the GPIO pin and GND -- no external resistor required. The inverted: true setting means pressing the button (pulling the pin LOW) registers as "on."

🔗Switch (Relay Control)

Control a relay module connected to GPIO 26:

switch:
  - platform: gpio
    pin: GPIO26
    name: "Garden Pump"
    icon: "mdi:water-pump"
    restore_mode: ALWAYS_OFF   # Default to off after reboot

The restore_mode option controls what happens when the ESP32 reboots. ALWAYS_OFF ensures the relay does not accidentally turn on after a power outage.

🔗PWM Light (Dimmable LED)

A dimmable LED using PWM on GPIO 16:

output:
  - platform: ledc
    pin: GPIO16
    id: pwm_led
    frequency: 1000Hz

light:
  - platform: monochromatic
    name: "Desk Lamp"
    output: pwm_led
    gamma_correct: 2.8

The ledc platform uses the ESP32's hardware LED Control (LEDC) peripheral for smooth PWM output. The gamma_correct value adjusts the brightness curve so that "50% brightness" looks like 50% brightness to your eyes (LEDs are not linear).

🔗I2C Sensor (BME280)

A BME280 environment sensor connected via I2C:

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true

sensor:
  - platform: bme280_i2c
    address: 0x76
    temperature:
      name: "Temperature"
      oversampling: 16x
    humidity:
      name: "Humidity"
    pressure:
      name: "Pressure"
    update_interval: 60s

Notice that you do not need to install libraries or write initialization code. ESPHome handles all of that -- you just specify the platform, pin, and desired settings.

🔗WiFi Signal Strength

ESPHome can also expose diagnostic information:

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    update_interval: 60s

  - platform: uptime
    name: "Uptime"

button:
  - platform: restart
    name: "Restart"

This gives you the WiFi signal strength, device uptime, and a restart button -- all visible in Home Assistant. Very useful for monitoring device health.

🔗OTA Updates

After the initial USB flash, you never need to physically access the device again. ESPHome supports Over-The-Air (OTA) updates:

  1. Edit the YAML configuration.
  2. Click Install > Wirelessly.
  3. ESPHome compiles the new firmware and uploads it to the ESP32 over WiFi.

The update process takes about 30-60 seconds. The device briefly disconnects, installs the new firmware, reboots, and reconnects to Home Assistant automatically.

Fallback hotspot: If the ESP32 cannot connect to your WiFi (wrong password, router change), it creates its own WiFi access point using the ap credentials from your config. Connect to it and navigate to 192.168.4.1 to update the WiFi settings.

🔗Viewing Logs

Real-time device logs are invaluable for debugging. You can view them in two ways:

  1. ESPHome dashboard -- click Logs on the device card. Works wirelessly.
  2. USB serial -- click Logs and select the serial port. Useful if WiFi is not working.

Logs show sensor readings, WiFi connection status, API connections, and any errors:

[14:32:15][D][dht:048]: Got Temperature=23.4°C Humidity=51.2%
[14:32:15][D][sensor:094]: 'Temperature': Sending state 23.40000 °C
[14:32:15][D][sensor:094]: 'Humidity': Sending state 51.20000 %

🔗ESPHome vs. Arduino: When to Use Which

Both ESPHome and Arduino (with MQTT) are valid ways to connect ESP32 devices to Home Assistant. Here is when to choose each:

ESPHomeArduino + MQTT
Best forStandard sensors, switches, lightsCustom logic, complex calculations
ProgrammingYAML (no code)C++
Learning curveLowModerate
Setup speedFastSlower
FlexibilityHigh (1,800+ components)Unlimited
Custom logicLimited (lambdas for simple C++)Full control
HA connectionNative API (no broker needed)MQTT (needs Mosquitto)
OTA updatesBuilt-inMust implement yourself
LoggingBuilt-in web viewerSerial or custom MQTT logging

Choose ESPHome when:

  • You want a temperature sensor, a switch, or a light with minimal effort
  • You prefer configuration over programming
  • You want automatic OTA updates and built-in diagnostics
  • You are new to ESP32 and just want things to work

Choose Arduino + MQTT when:

  • You need complex calculations or custom algorithms
  • You want to learn how things work at a lower level
  • You need features not supported by ESPHome components
  • You are connecting to platforms other than Home Assistant
  • You enjoy writing code

You can mix both. Many home automation setups use ESPHome for simple sensor nodes and Arduino-coded devices for more complex projects. Home Assistant does not care how the device communicates -- both ESPHome and MQTT devices appear as entities on the same dashboard.

🔗Lambdas: Adding Custom Code to ESPHome

ESPHome is not entirely code-free. For situations that require a bit of custom logic, you can use lambdas -- small inline C++ snippets within your YAML:

sensor:
  - platform: adc
    pin: GPIO34
    name: "Battery Voltage"
    update_interval: 60s
    attenuation: 11db
    filters:
      - multiply: 2.0    # Voltage divider ratio
      - lambda: |-
          if (x < 3.3) {
            return 0.0;   # Battery empty
          } else if (x > 4.2) {
            return 100.0; # Battery full
          }
          // Linear interpolation between 3.3V and 4.2V
          return (x - 3.3) / (4.2 - 3.3) * 100.0;
    unit_of_measurement: "%"
    accuracy_decimals: 0

Lambdas let you handle edge cases without leaving the ESPHome ecosystem. However, if your custom logic grows beyond a few lines, Arduino + MQTT is probably a better fit.

🔗Secrets Management

Avoid hardcoding WiFi passwords and API keys in your YAML files. ESPHome supports a secrets.yaml file:

secrets.yaml:

wifi_ssid: "YourNetworkName"
wifi_password: "YourPassword"
api_key: "your-encryption-key"
ota_password: "your-ota-password"

device.yaml:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

api:
  encryption:
    key: !secret api_key

This way, you can share or version-control your device configs without exposing credentials.

🔗Troubleshooting

ProblemCauseSolution
Device not discovered in HAmDNS issues or different subnetCheck that HA and ESP32 are on the same network. Try adding the device manually by IP.
Compilation failsYAML syntax errorCheck indentation. YAML is sensitive to spaces (no tabs).
Cannot flash over USBWrong serial port or driver issueTry a different USB cable (data cables, not charge-only). Install CP2102 or CH340 drivers.
OTA update failsDevice unreachableCheck WiFi signal. Move the device closer to the router during update.
Sensor reads wrong valuesWiring issue or wrong GPIODouble-check connections. Verify the GPIO number matches your board.
"Already connected" errorAnother ESPHome dashboard instanceOnly one dashboard can connect to a device at a time. Close other tabs.

🔗What is Next?

If ESPHome covers your needs, skip ahead to building a custom sensor node for a complete end-to-end project. If you prefer writing your own Arduino code and using MQTT, the next article shows you how to connect an Arduino-coded ESP32 to Home Assistant using MQTT auto-discovery.