DEV Community

Cover image for ESPHome and ESP32 Sensor Network: A Cloud-Free Smart Home
Mustafa ERBAY
Mustafa ERBAY

Posted on Originally published at mustafaerbay.com.tr

ESPHome and ESP32 Sensor Network: A Cloud-Free Smart Home

A room sensor should not need a vendor cloud, a mobile application, and a remote API just to report temperature. An ESP32 can perform the measurement, ESPHome can provide local integration, and Home Assistant can keep the automation inside your network.

“Local” does not automatically mean secure, resilient, or maintainable. This guide builds one BME280 temperature, humidity, and pressure node while treating network boundaries, secret management, OTA risk, validation, and recovery as parts of the same design.

What does cloud-free actually mean?

In this architecture, the ESP32 sends measurements to Home Assistant through ESPHome's local native API. The normal data path does not depend on a vendor account or an Internet-hosted broker. If the Internet connection fails while local Wi-Fi, the ESP32, and Home Assistant remain available, local measurements and automations can continue.

Dependencies still exist. The access point, address assignment, Home Assistant host, and the sensor's power supply are all part of the system. The ESP32 may continue reading its sensor while Home Assistant is offline, but dashboards, history, and Home Assistant automations will be unavailable. Document the dependency chain instead of treating “no cloud” as a complete availability model.

Start with a small, explicit architecture

The first node needs only an ESP32 development board, an I²C BME280 module, a dedicated IoT network, and a Home Assistant/ESPHome management point. The example assigns 192.168.30.41 to the node. Replace the address, gateway, and subnet with values from your own plan, and never select an address that may already be in use.

An IoT VLAN is not an ESPHome requirement, but it makes the trust boundary easier to reason about. Allow Home Assistant to reach the ESPHome native API — TCP 6053 by default — and limit OTA access to a trusted administration host or network. A fixed address may remove the need for mDNS discovery. Build firewall rules from the flows you actually use rather than copying a broad ruleset.

Use the current ESPHome configuration model

The current ESPHome ESP32 component recommends declaring the chip variant; this example targets the classic ESP32 family. It uses ESP-IDF, the default and recommended framework for supported ESP32 targets in ESPHome. If your board uses an ESP32-C3, S3, or another variant, change both the variant and pins according to that board's documentation.

GPIO21 and GPIO22 are common I²C pins on classic ESP32 development boards, but they are not universal. BME280 modules commonly appear at 0x76 or 0x77. The I²C scan during the first boot should confirm the address instead of leaving it as an assumption.

substitutions:
  node_name: living-room-climate
  friendly_name: Living Room Climate

esphome:
  name: ${node_name}
  friendly_name: ${friendly_name}

esp32:
  variant: esp32
  framework:
    type: esp-idf

logger:

api:
  encryption:
    key: !secret living_room_api_key

ota:
  - platform: esphome
    password: !secret living_room_ota_password

wifi:
  ssid: !secret iot_wifi_ssid
  password: !secret iot_wifi_password
  min_auth_mode: WPA2
  manual_ip:
    static_ip: 192.168.30.41
    gateway: 192.168.30.1
    subnet: 255.255.255.0

i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true

sensor:
  - platform: bme280_i2c
    address: 0x76
    temperature:
      name: "${friendly_name} Temperature"
    pressure:
      name: "${friendly_name} Pressure"
    humidity:
      name: "${friendly_name} Humidity"
    update_interval: 60s
Enter fullscreen mode Exit fullscreen mode

OTA is now a platform list, so do not copy the legacy single-block form into a new project. api.encryption.key expects a Base64-encoded 32-byte key. Home Assistant requests the same key when the node is added. Keep it out of articles, screenshots, and source control.

Separate secrets from configuration

ESPHome's !secret tag reads sensitive values from secrets.yaml in the configuration directory. That file must be a flat key-value mapping and must not enter version control. A repository can contain a secrets.example.yaml that documents required field names while the real values are distributed from a password manager.

The file can follow the structure below. Replace every placeholder with a unique value. Do not reuse the Wi-Fi password as the OTA password, and generate the API key independently.

iot_wifi_ssid: "YOUR-IOT-SSID"
iot_wifi_password: "REPLACE-WITH-A-LONG-UNIQUE-PASSWORD"
living_room_api_key: "REPLACE-WITH-A-32-BYTE-BASE64-KEY"
living_room_ota_password: "REPLACE-WITH-A-DIFFERENT-LONG-RANDOM-PASSWORD"
Enter fullscreen mode Exit fullscreen mode

If you choose a static address, exclude it from the DHCP pool or create a reservation for the board's MAC address. Otherwise another client can receive the same address and create a failure that looks like a sensor problem. For multiple nodes, maintain one inventory containing address, physical location, board variant, and attached sensor.

Install and validate in a deliberate order

Run esphome config first to validate YAML syntax and the component schema. Use USB for the initial firmware installation so that serial logs remain available when Wi-Fi or OTA configuration is wrong. esphome run validates, compiles, uploads, and then starts the logs.

Run these commands from the configuration directory. The serial device name varies by operating system; verify the port before writing. After a successful first USB installation, controlled updates can use the node's IP address.

esphome config living-room-climate.yaml
esphome run living-room-climate.yaml --device /dev/ttyUSB0

# After initial provisioning, from the trusted management network:
esphome run living-room-climate.yaml --device 192.168.30.41
Enter fullscreen mode Exit fullscreen mode

Confirm that the I²C scan finds 0x76. If it reports 0x77, update the configured address. If no device appears, check 3.3 V, ground, SDA, and SCL wiring and then the module data sheet before changing unrelated software options.

Understand what API encryption does not solve

ESPHome Native API encryption protects API traffic between Home Assistant and the node. The OTA password makes unauthorized uploads through the ESPHome OTA mechanism more difficult. These controls matter, but neither creates a hardware root of trust against physical capture, flash extraction, or an untrusted image in the boot chain.

Espressif Secure Boot and Flash Encryption address different threats: one authenticates software during boot, while the other protects external flash contents. They deserve evaluation for production hardware, but eFuse-related security changes can make recovery difficult. Follow Espressif's documentation for the exact target and production mode instead of applying commands copied from an unrelated board.

Scale from one sensor to a fleet

Copied YAML files begin to drift as soon as the second node arrives. Centralize shared Wi-Fi, logging, API, and sensor policy through controlled packages or templates. Keep the node name, address, API key, and calibration data device-specific. A unique API key for every node limits the effect of one exposed secret.

Do not update the entire fleet at once. Start with an accessible canary node and verify the build, network connection, sensor readings, Home Assistant discovery, and reboot behavior. Then proceed in small groups. Recording the firmware version, YAML commit, and installation time replaces guesswork with an auditable answer to “what is running on this device?”

Recovery is part of the installation

A node may fail to return after OTA because of an incorrect pin, framework change, Wi-Fi credential, or firewall rule. Retain the last working YAML and its firmware artifact. Keep physical USB access for important nodes, and test serial recovery before mounting a board somewhere difficult to reach.

Use a concrete release checklist: configuration validation passes, the I²C address is visible, readings are plausible, the Home Assistant connection is encrypted, local data continues when Internet access is removed, and the node reconnects after both the access point and Home Assistant restart. The network is not complete until these failure paths have been tested.

Conclusion

A cloud-free sensor network is more than uploading YAML to an ESP32. A local API, explicit network boundary, per-device secrets, controlled OTA process, observable first boot, and physical recovery path make the system understandable and repairable.

The first objective should be one dependable node, not dozens of sensors. Once its configuration, security decisions, and validation steps are repeatable, the network can grow with less operational risk.

Official references

Top comments (0)