Smart Devices with ESPHome

When I started automating my aquarium in June 2022, my goal was simple: monitor temperature, control the lighting, and manage the water pump intelligently. Initially, I considered a traditional Arduino setup using VSCode with the PlatformIO extension. But even this small project required hundreds of lines of code, juggling libraries, headers, and constant recompilation.

Then I discovered ESPHome. What would have taken hundreds of lines of Arduino code could now be done in under 100 lines, with clear YAML configuration and almost no boilerplate.

ESPHome doesn’t require Home Assistant. While it integrates seamlessly with Home Assistant, you can use ESPHome devices completely standalone. All sensors and switches work out of the box, and you can update firmware via USB or OTA. If you prefer, ESPHome can also communicate over MQTT to other platforms like Node-RED or OpenHAB. Home Assistant makes management easier, but it’s not mandatory – you can get started and fully control your devices without it.

The ESPHome Advantage

ESPHome makes IoT device automation simple, maintainable, and robust:

  • Readable YAML configuration: Devices, sensors, and automations are declared clearly and intuitively.
  • Initial USB setup: First firmware upload is done via USB on a Windows PC, no drivers required, with Home Assistant open in the browser.
  • OTA updates: After initial setup, firmware updates are deployed wirelessly.
  • Live log debugging: Monitor device logs wirelessly or via USB, making troubleshooting fast and flexible.
  • Failover Wi-Fi: ESPHome remembers multiple networks and reconnects automatically.
  • Integration-ready: Works seamlessly with Home Assistant and MQTT.

For my aquarium, this meant I could configure temperature thresholds, automate LED lighting schedules, and control pumps in minutes rather than hours. Every change was instantly testable, without worrying about compilation errors.

Step-by-Step Setup

  1. Connect ESP to USB and open Home Assistant in a browser.
  2. Create YAML configuration for sensors, switches, and Wi-Fi.
  3. Initial upload via USB to flash firmware.
  4. Enable OTA updates for future wireless firmware changes.
  5. Monitor live logs either wirelessly or via USB.

This workflow minimizes hardware hassle and accelerates development.

Real-World Example: Aquarium Project

To allow independent control of multiple 230 V devices – Daylight, Nightlight, Air Pump, and Filter – I installed a 4-channel relay purchased from EZ Delivery into an OBO enclosure from OBI.

For those interested in the hardware details, including wiring diagrams, GPIO assignments, and photos of the setup, all relevant information is still available in the archived GitHub repository: https://github.com/filipnet/aquarium. This archive provides a thorough reference for anyone looking to replicate or understand the physical setup of the project.

Comparison

The difference between the two approaches is clear:

  • Arduino Code: ~420 lines, ~12,000 characters
  • ESPHome YAML: ~80 lines, ~1,200 characters

Maintaining the Arduino project was time-consuming and often felt like a waste of time. ESPHome achieves the same functionality with far less code, easier maintenance, and faster iteration. For this reason, I’m no longer continuing the Arduino version on GitHub.

Breaking Down ESPHome Configurations into Segments

To make ESPHome more approachable and easier to understand, we’ll break the configuration into individual segments. Each YAML snippet represents a specific part of your device setup, such as Wi-Fi, sensors, or switches.

These segments are designed to be modular: you can follow along step by step, and in the end, combine them into a single, complete configuration file. This approach helps you understand each component separately before seeing how they work together in the full setup.

By the time you finish all the segments, you’ll have a complete ESPHome YAML file ready to deploy to your device, with a clear understanding of each part.

Click here: If you are already familiar with ESPHome and want the full code in one block.
esphome:
  name: esp8266-d1261d
  friendly_name: esp8266-d1261d
  on_boot:
    priority: -10
    then:
      - logger.log: "Waiting 60s before activating sensors..."
      - delay: 60s
      - component.update: ds18b20_sensor
      - component.update: bme280_sensor

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "1234567abcdefg1234567abcdefg12"

ota:
  - platform: esphome
    password: "abcdefg1234567abcdefg1234567ab"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp8266-D1261D Fallback Hotspot"
    password: "1234567abcdefg1234567abcdefg12"

captive_portal:

time:
  - platform: sntp
    id: time_ntp
    timezone: Europe/Berlin
    servers: 192.168.1.1

mqtt:
  broker: "mqtt.example.com"
  username: "esp8266-d1261d"
  password: "abcdefg1234567abcdefg1234567ab"

switch:
  - platform: gpio
    pin: D5
    name: "Aquarium Nightlight"
    id: relay_1
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:weather-night"

  - platform: gpio
    pin: D6
    name: "Aquarium Daylight"
    id: relay_2
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:weather-sunny"

  - platform: gpio
    pin: D3
    name: "Aquarium Airpump"
    id: relay_3
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:air-filter"

  - platform: gpio
    pin: D0
    name: "Aquarium Filter"
    id: relay_4F
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:filter"

# BME280 I²C Sensor for ambient
i2c:
  sda: D2
  scl: D1
  scan: true

# 1-Wire Bus for DS18B20
one_wire:
  - platform: gpio
    pin: D7
    id: one_wire_bus

sensor:
  - platform: wifi_signal
    name: "Wi-Fi Signal Strength"
    icon: "mdi:wifi"
    update_interval: 60s

  - platform: uptime
    name: "System Uptime"
    icon: "mdi:clock-outline"
    update_interval: 30s

  - platform: dallas_temp
    one_wire_id: one_wire_bus
    name: "Aquarium Water Temperature"
    id: ds18b20_sensor
    update_interval: 60s

  - platform: bme280_i2c
    address: 0x76
    id: bme280_sensor
    update_interval: 30s
    temperature:
      id: bme280_temperature
      name: "Ambient Air Temperature"
      oversampling: 16x
      icon: "mdi:thermometer"
    pressure:
      id: bme280_pressure
      name: "Ambient Air Pressure"
      icon: "mdi:gauge"
    humidity:
      id: bme280_humidity
      name: "Ambient Air Humidity"
      icon: "mdi:water-percent"
  • Note: Any sensitive data, such as passwords or API keys, have been pseudonymized (replaced with placeholders) for security and privacy reasons.
  • Note on Hostnames: I prefer using generic hostnames for IoT devices rather than task-specific names like aquarium_sensor. Devices often get repurposed over time, and keeping a flexible, neutral hostname makes it easier to reassign them without confusion or conflicts in the network.

ESPHome Basic Setup with Optional Sensor Delay

Note: When initially setting up the device via the Home Assistant frontend, some values (like encryption keys, passwords, or other generated settings) are automatically created. Do not change these, or the device may fail to connect or update.

esphome:
  name: esp8266-d1261d
  friendly_name: esp8266-d1261d
  on_boot:
    priority: -10
    then:
      - logger.log: "Waiting 60s before activating sensors..."
      - delay: 60s
      - component.update: ds18b20_sensor
      - component.update: bme280_sensor

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "1234567abcdefg1234567abcdefg12"

Segments explained:

  • esphome: → Sets the device name and friendly name.
  • on_boot:Optional: waits 60s after boot before activating sensors to prevent boot-time errors.
  • esp8266: → Specifies the board type (D1 Mini).
  • logger: → Enables logging for debugging.
  • api: → Connects the device to Home Assistant with encryption.
  • on_boot:Optional (the blue marked parts): waits 60s after boot before activating sensors to prevent boot-time errors

Over-the-Air Updates (OTA)

ota:
  - platform: esphome
    password: "abcdefg1234567abcdefg1234567ab"

Segment explained:

  • ota: → Enables over-the-air updates, so you can upload new firmware wirelessly.
  • password: → Protects the OTA process

Wi-Fi Configuration with Fallback Hotspot

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp8266-D1261D Fallback Hotspot"
    password: "1234567abcdefg1234567abcdefg12"

captive_portal:

Segment explained:

  • ssid & password → Connects the device to your main Wi-Fi.
  • use_address → Assigns a fixed IP so the backend (Home Assistant) can reliably access the ESPHome device.
    • Note: If you initially use DHCP, the device will get an IP automatically; you can then update use_address with the assigned IP for consistent access.
  • ap: → Configures a fallback hotspot (captive portal) that activates if the main Wi-Fi connection fails.

Time Synchronization (NTP)

time:
  - platform: sntp
    id: time_ntp
    timezone: Europe/Berlin
    servers: 192.168.1.1

Segment explained:

  • platform: sntp → Uses the Simple Network Time Protocol to keep the ESPHome device clock accurate.
  • id: time_ntp → Identifier for this time component; can be referenced by other components if needed.
  • timezone → Sets the device’s timezone (here, Europe/Berlin).
  • servers → Specifies the NTP server to sync with; can be local (like a Home Assistant instance) or public NTP servers.

Tip: Having accurate time is important for logs, automations, and scheduled tasks.

Optional: MQTT Integration

mqtt:
  broker: "mqtt.example.com"
  username: "esp8266-d1261d"
  password: "abcdefg1234567abcdefg1234567ab"

Segment explained:

  • broker → The address of your MQTT server.
  • username & password → Credentials to connect with values.
  • Optional: You only need this if you want the ESPHome device to communicate over MQTT instead of (or in addition to) the Home Assistant API.

Note: Up to this point, the code can be reused for almost any other ESPHome project – you only need to adjust the relevant variables. Everything that follows covers sensors and project-specific details.

Relay Control (Switches)

switch:
  - platform: gpio
    pin: D5
    name: "Aquarium Nightlight"
    id: relay_1
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:weather-night"

  - platform: gpio
    pin: D6
    name: "Aquarium Daylight"
    id: relay_2
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:weather-sunny"

  - platform: gpio
    pin: D3
    name: "Aquarium Airpump"
    id: relay_3
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:air-filter"

  - platform: gpio
    pin: D0
    name: "Aquarium Filter"
    id: relay_4F
    restore_mode: RESTORE_DEFAULT_OFF
    icon: "mdi:filter"

Segment explained:

  • platform: gpio → Uses GPIO pins to control physical relays.
  • pin: → The GPIO pin connected to each relay.
  • name: → Friendly name for Home Assistant interface.
  • id: → Internal identifier for automations or scripts.
  • restore_mode: RESTORE_DEFAULT_OFF → Ensures the relay is off on boot by default.
  • icon: → Optional icon for UI display.

Tip: Even though the example uses aquarium-related names, consider using generic names for IoT devices so they can be repurposed easily later.

Sensor Setup

# BME280 I²C Sensor for ambient
i2c:
  sda: D2
  scl: D1
  scan: true

# 1-Wire Bus for DS18B20
one_wire:
  - platform: gpio
    pin: D7
    id: one_wire_bus

sensor:
  - platform: wifi_signal
    name: "Wi-Fi Signal Strength"
    icon: "mdi:wifi"
    update_interval: 60s

  - platform: uptime
    name: "System Uptime"
    icon: "mdi:clock-outline"
    update_interval: 30s

  - platform: dallas_temp
    one_wire_id: one_wire_bus
    name: "Aquarium Water Temperature"
    id: ds18b20_sensor
    update_interval: 60s

  - platform: bme280_i2c
    address: 0x76
    id: bme280_sensor
    update_interval: 30s
    temperature:
      id: bme280_temperature
      name: "Ambient Air Temperature"
      oversampling: 16x
      icon: "mdi:thermometer"
    pressure:
      id: bme280_pressure
      name: "Ambient Air Pressure"
      icon: "mdi:gauge"
    humidity:
      id: bme280_humidity
      name: "Ambient Air Humidity"
      icon: "mdi:water-percent"

Segment explained:

  • i2c: → Defines the I²C bus for sensors like BME280; must be declared before the sensor itself.
  • one_wire: → Defines the 1-Wire bus for DS18B20 temperature sensors; also must be declared first.
  • sensor: → Declares all sensors connected to the device.
    • wifi_signal → Monitors Wi-Fi strength.
    • uptime → Tracks system uptime.
    • dallas_temp → Reads water temperature from DS18B20 sensors on the defined 1-Wire bus.
    • bme280_i2c → Measures ambient temperature, humidity, and pressure via the BME280 on the I²C bus.

Tip: Always define the bus (i2c or one_wire) before the sensors that use it, otherwise ESPHome will throw an error.

Aquarium Scheduler (Home Assistant)

In Home Assistant, you can schedule aquarium devices like lights and the air pump. For example, the daylight relay turns on in the morning and off in the evening, while the air pump cycles throughout the day. These automations are easy to adjust via the UI and can interact with sensor data for smarter control, all without modifying the ESPHome YAML.

This is what the scheduler configuration looks like. You can work in a simple mode, using just basic on and off commands, or create a full schedule (as shown here) to plan the entire day.

In Home Assistant, I utilize the Scheduler Component. This custom integration allows me to create time-based schedules for controlling devices such as aquarium lights and pumps. Paired with the Scheduler Card, it provides an intuitive interface within the Lovelace dashboard, enabling me to manage and adjust schedules effortlessly.

Click here: For example, this is how I configured the day and night light with the scheduler.

EntityActionStartStop
Daylight6:30
Daylighton6:3016:30
Daylightoff16:30
Nightlight5:00
Nightlighton5:006:35
Nightlightoff6:3515:25
Nightlighton15:2521:00
Nightlightoff21:00

Lessons Learned

This is how it looks in Home Assistant for me. I can monitor the water temperature, the status of the lights, filter, and air pump, and control them directly. Below, you can see the schedulers I’ve set up.

When converting my ESP8266 aquarium controller code to ESPHome YAML, I ran into several challenges:

  • YAML indentation issues: Even a single misplaced space could break the configuration, especially with nested components like the BME280 sub-sensors.
  • Boot failures with connected sensors: The ESP wouldn’t start if the I²C BME280 or 1-Wire DS18B20 sensors were connected.
  • Relay restore mode conflicts: Setting a relay to restore_default_on caused startup problems due to duplicate GPIO initialization on boot.
  • Startup delays for sensors: Attempting to introduce a delay before reading sensor values didn’t work as expected in ESPHome.
  • In ESPHome, all component IDs are case-sensitive, so DS18B20_sensor and ds18b20_sensor are treated as different; always match the ID exactly as defined to avoid configuration errors.
  • Interestingly, ESPHome won’t start with a faulty power supply (3.4 V instead of 5 V), whereas the Arduino code still runs; replacing the power supply fixed the ESPHome startup issue.
  • Keep a backup of your YAML before changes.
  • Name sensors and switches clearly for Home Assistant dashboards.

After completing the configuration, I flashed a new D1 Mini and replaced the old one on the board. Once the new board was installed, everything worked perfectly. ESPHome turned my modest aquarium automation project into a reliable, easy-to-manage smart device, reducing complexity while enhancing functionality.

I’m glad I initially started with C++ and PlatformIO, which helped me develop my programming skills, but everything has its time. With YAML and ESPHome, I now spend less time wrestling with code and more time exploring other exciting IT topics.


Useful Resources