Introduction

Home Assistant has revolutionized the way we think about home automation, providing an open-source platform that integrates seamlessly with thousands of devices and services. But what happens when you want to monitor something that doesn't have a pre-built integration? This is where custom sensors come in.

In this comprehensive guide, I'll walk you through the process of building custom sensors for Home Assistant using Python, ESP32 microcontrollers, and MicroPython. By the end, you'll have the knowledge to extend your smart home's capabilities beyond off-the-shelf solutions.

Prerequisites

Before we dive into building custom sensors, make sure you have the following:

Hardware Requirements

  • ESP32 development board (ESP8266 works too, but with fewer features)
  • DHT22 or BME280 temperature/humidity sensor
  • Breadboard and jumper wires
  • MicroUSB cable for programming
  • Optional: 3D printed enclosure for permanent installation

Software Requirements

  • Home Assistant running on your network
  • MQTT broker (I recommend Mosquitto, which can be installed as a Home Assistant add-on)
  • Thonny IDE or esptool for flashing MicroPython
  • MicroPython firmware for ESP32

If you're new to some of these concepts, don't worry. I'll provide step-by-step instructions to get everything set up.

Hardware Setup

Let's start by connecting our temperature and humidity sensor to the ESP32. For this tutorial, I'll be using the DHT22 sensor, but the process is similar for BME280 or other I2C sensors.

DHT22 Connections:


DHT22 Pin | ESP32 Pin
------------------
VCC       | 3.3V
GND       | GND
DATA      | GPIO 4
                                

For a more reliable connection, add a 10kΩ pull-up resistor between the DATA and VCC pins. This helps ensure clean signal transmission.

Pro Tip

When building sensors for permanent installation, soldering the connections rather than using a breadboard will significantly improve reliability and reduce the chance of loose connections over time.

Once your hardware is connected, we need to flash MicroPython to the ESP32.

Programming with MicroPython

MicroPython is a lean and efficient implementation of Python 3 that's optimized for microcontrollers. It allows us to write clean, readable code while still maintaining good performance.

Flashing MicroPython

First, download the latest MicroPython firmware for ESP32 from the official website. Then, use esptool to flash it:


# Install esptool if you haven't already
pip install esptool

# Erase the flash memory first
esptool.py --port /dev/ttyUSB0 erase_flash

# Flash the MicroPython firmware
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-20220117-v1.18.bin
                                

Replace /dev/ttyUSB0 with the appropriate port for your system and esp32-20220117-v1.18.bin with the name of the firmware file you downloaded.

Sensor Code

Now, let's write the MicroPython code that will read from our sensor and publish the data to our MQTT broker. Create a file called main.py on your ESP32:


import time
import dht
import machine
import network
import json
from umqtt.simple import MQTTClient

# Configuration
WIFI_SSID = "YourWifiName"
WIFI_PASSWORD = "YourWifiPassword"
MQTT_BROKER = "192.168.1.xxx"  # IP address of your MQTT broker
MQTT_PORT = 1883
MQTT_USER = "your_mqtt_username"
MQTT_PASSWORD = "your_mqtt_password"
MQTT_CLIENT_ID = "esp32_sensor_1"
MQTT_TOPIC = "home/sensors/living_room"
SENSOR_PIN = 4
READING_INTERVAL = 60  # Seconds between readings

# Initialize sensor
sensor = dht.DHT22(machine.Pin(SENSOR_PIN))

# Connect to WiFi
def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('Connecting to WiFi...')
        wlan.connect(WIFI_SSID, WIFI_PASSWORD)
        while not wlan.isconnected():
            pass
    print('Network configuration:', wlan.ifconfig())

# Read sensor data
def read_sensor():
    try:
        sensor.measure()
        temp = sensor.temperature()
        hum = sensor.humidity()
        return {
            'temperature': round(temp, 1),
            'humidity': round(hum, 1),
            'timestamp': time.time()
        }
    except Exception as e:
        print('Error reading sensor:', e)
        return None

# Publish to MQTT
def publish_reading(data):
    if data is None:
        return
    
    try:
        client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT, 
                            MQTT_USER, MQTT_PASSWORD)
        client.connect()
        
        # Convert data to JSON string
        json_data = json.dumps(data)
        client.publish(MQTT_TOPIC, json_data, retain=True)
        
        # Also publish to individual topics for easier Home Assistant integration
        client.publish(f"{MQTT_TOPIC}/temperature", str(data['temperature']), retain=True)
        client.publish(f"{MQTT_TOPIC}/humidity", str(data['humidity']), retain=True)
        
        client.disconnect()
        print('Published:', json_data)
    except Exception as e:
        print('Error publishing to MQTT:', e)

# Main loop
def main():
    connect_wifi()
    
    while True:
        data = read_sensor()
        publish_reading(data)
        time.sleep(READING_INTERVAL)

# Start the program
if __name__ == "__main__":
    main()
                                

This code does the following:

  1. Connects to your Wi-Fi network
  2. Reads temperature and humidity from the DHT22 sensor
  3. Formats the data as JSON
  4. Publishes it to your MQTT broker
  5. Waits for the specified interval before reading again

Important Security Note

In a production environment, never hardcode credentials in your code. Instead, use a separate config file or environment variables.

MQTT Integration

MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol that's perfect for IoT applications. It follows a publish-subscribe model where our sensors publish data to specific topics, and Home Assistant subscribes to those topics to receive the data.

MQTT Broker Setup

If you haven't already set up an MQTT broker, the easiest way is to install the Mosquitto add-on in Home Assistant:

  1. Go to your Home Assistant dashboard
  2. Navigate to SettingsAdd-onsAdd-on Store
  3. Search for "Mosquitto broker" and install it
  4. Configure a username and password in the add-on config
  5. Start the add-on

Make note of your Home Assistant's IP address, as this will be the MQTT broker address you'll use in your ESP32 code.

MQTT Topic Structure

Organizing your MQTT topics in a logical hierarchy makes it easier to manage multiple sensors. I recommend a structure like this:


home/sensors/[location]/temperature
home/sensors/[location]/humidity
                                

For example, home/sensors/living_room/temperature would contain the current temperature reading from the living room sensor.

Home Assistant Configuration

Now that our ESP32 is publishing data to MQTT, we need to configure Home Assistant to use this data.

MQTT Integration

First, make sure the MQTT integration is enabled in Home Assistant:

  1. Go to SettingsDevices & Services
  2. Click Add Integration and search for "MQTT"
  3. Enter the broker details (typically localhost:1883 if using the Mosquitto add-on)
  4. Enter the username and password you configured for the MQTT broker

Sensor Configuration

Next, we'll create sensors in Home Assistant that subscribe to our MQTT topics. Add the following to your configuration.yaml file:


# MQTT Sensors
sensor:
  - platform: mqtt
    name: "Living Room Temperature"
    state_topic: "home/sensors/living_room/temperature"
    unit_of_measurement: "°C"
    device_class: temperature
    value_template: "{{ value | float }}"
    
  - platform: mqtt
    name: "Living Room Humidity"
    state_topic: "home/sensors/living_room/humidity"
    unit_of_measurement: "%"
    device_class: humidity
    value_template: "{{ value | float }}"
                                

After saving the configuration, restart Home Assistant to apply the changes.

Creating Dashboard Cards

Finally, let's add these sensors to our Home Assistant dashboard:

  1. Go to Overview and edit your dashboard
  2. Click + Add Card
  3. Select Entities card type
  4. Add both your temperature and humidity sensors
  5. Customize the card title and appearance as desired

For a more visual representation, you might prefer using a Gauge card for temperature and humidity individually.

Advanced Features and Customizations

Once you have the basic sensor setup working, there are several ways to enhance and extend your system:

Adding Battery Monitoring

If your sensor is battery-powered, you can add battery level monitoring:


# Add to your main.py
ADC_PIN = 35  # GPIO pin connected to battery monitoring circuit
BATTERY_MAX = 4.2  # Maximum battery voltage
BATTERY_MIN = 3.0  # Minimum battery voltage

def read_battery_level():
    # ESP32 ADC reference voltage is 3.3V and resolution is 12-bit (0-4095)
    adc = machine.ADC(machine.Pin(ADC_PIN))
    adc.atten(machine.ADC.ATTN_11DB)  # Full range: 0-3.3V
    
    # Read 10 samples and average
    readings = [adc.read() for _ in range(10)]
    avg_reading = sum(readings) / len(readings)
    
    # Convert ADC reading to voltage
    voltage = avg_reading / 4095 * 3.3
    
    # If using a voltage divider, apply the conversion factor
    # voltage = voltage * 2  # example for a 1:1 voltage divider
    
    # Calculate percentage
    percentage = 100 * (voltage - BATTERY_MIN) / (BATTERY_MAX - BATTERY_MIN)
    percentage = max(0, min(100, percentage))  # Clamp between 0-100%
    
    return round(percentage, 1)
                                

Then publish this value to MQTT along with your other sensor readings.

Deep Sleep for Battery Saving

For battery-powered sensors, implementing deep sleep significantly extends battery life:


import esp32

# Instead of time.sleep() in the main loop
def main():
    connect_wifi()
    
    while True:
        data = read_sensor()
        data['battery'] = read_battery_level()
        publish_reading(data)
        
        # Go to deep sleep instead of using time.sleep
        print(f"Going to deep sleep for {READING_INTERVAL} seconds")
        esp32.wake_on_timer(READING_INTERVAL * 1000)  # Time in milliseconds
        esp32.deep_sleep()
                                

Multiple Sensors on One ESP32

You can connect multiple sensors to a single ESP32 by assigning different GPIO pins and creating additional sensor objects in your code.

OTA Updates

For sensors placed in hard-to-reach locations, implementing Over-The-Air (OTA) updates is invaluable. MicroPython has libraries for this, but it's beyond the scope of this tutorial.

Troubleshooting Common Issues

Sensor Not Showing in Home Assistant

Possible causes:

  • MQTT topics in Home Assistant don't match what the ESP32 is publishing
  • ESP32 not connected to WiFi or MQTT broker
  • MQTT broker credentials incorrect

Solution: Check the MQTT logs in Home Assistant to see if messages are being received. Use an MQTT client like MQTT Explorer to monitor topics and messages directly.

Inconsistent or Incorrect Readings

Possible causes:

  • Sensor placement near heat sources or in direct sunlight
  • Poor wiring connections
  • Sensor quality issues

Solution: Place sensors away from heat sources, ensure all connections are secure, and consider using a higher-quality sensor like BME280 instead of DHT22 for more accurate readings.

ESP32 Crashes or Resets Frequently

Possible causes:

  • Power supply issues
  • Memory leaks in code
  • Hardware issues

Solution: Use a stable power supply, implement error handling and watchdog timers in your code, and check for hardware defects.

Conclusion

Building custom sensors for Home Assistant opens up endless possibilities for home automation and monitoring. Whether you're tracking environmental conditions, monitoring energy usage, or creating specialized sensors for unique needs, the combination of ESP32, MicroPython, MQTT, and Home Assistant provides a powerful, flexible foundation.

The approach outlined in this tutorial is just the beginning. As you become more comfortable with these technologies, you can expand your system to include more sensors, more complex data processing, and integrations with other systems.

Remember, the key to reliable sensor networks is thoughtful planning, clean code, and careful attention to power management and connectivity issues.

Next Steps

Ready to take your custom sensors to the next level? Here are some advanced topics to explore:

  • Creating compact, weatherproof enclosures using 3D printing
  • Implementing mesh networks for wider coverage with ESP-NOW or similar protocols
  • Adding local data storage for offline operation during network outages
  • Creating advanced automations based on sensor data patterns

Happy building! If you create any interesting sensors using this guide, I'd love to hear about them.