Hello,
This is my first integration into Home Assistant, in a way I haven’t seen done before.
The idea is to have an ESP-01 in parallel with the doorbell chime, so that when you press the doorbell button, both get powered up. While it’s on, the ESP-01 sends a message to Home Assistant and in turn, Home Assistant sends notifications.
So far so good, but a couple of issues:
- The ESP-01 may need to be powered for longer than the amount of time people press the button.
- 12V AC? The ESP01 is 3.3V with no current protection.
So that’s what my ugly circuit board does. The 12V AC is converted into 3.3V using a bridge rectifier, a large capacitor (1000uF is what I had on hand), an AMS1117-3.3 and a smaller capacitor (33uF) to handle peak current from the ESP-01. This gives me an “energy budget” large enough to keep my ESP-01 powered for just under a second.
The next question is, what can we do with an ESP-01 powered for under a second? Certainly not WiFi, but with ESPNow (Espressif’s own communication protocol), my ESP01 can connect and send packets to another ESP8266 in under 100ms. All you need for ESPNow to work is on the “sender” to know the MAC address of the “receiver”. I haven’t tried encrypted packets yet, so I don’t know if my “energy budget” will allow for it. But with clear comms, I haven’t had any failures to communicate (yet).
OK, we have an ESP-01 that can talk to another ESP8266 or ESP32, how does that get us closer to Home Assistant?
I chose to create a serial device for my espnow_hub
(a nodemcu connected to the HA server via /dev/ttyUSB2
) and a binary sensor for the doorbell.
I haven’t really thought about what it is I want to communicate beyond toggle and push buttons, so the following leaves quite some room for improvement!
The strings sent to HA over serial are like this:
A0:20:A6:12:4B:FE|2
A0:20:A6:12:4B:FE|0
Left of the |
is the MAC address of the sender, right of it is the payload. I have 3 possible payloads:
- 0 is off
- 1 is on
- 2 is momentary on
2 is a special case: If the “receiver” sees a payload of 2 (example above), it sends it to HA over serial just like it would for a payload of 0 or 1, but then also sends a second string after 100ms which fakes a payload of 0 for “off”. This is to keep the HA template as simple as possible. I have two devices I am listening to (‘A0:20:A6:12:4B:FE’ and ‘A0:20:A6:12:4B:FF’) . One is the actual ESP-01 I use in my chime, the other one is a NodeMCU that will send a payload of 2 if I press its flash button.
# Loads default set of integrations. Do not remove.
default_config:
# Load frontend themes from the themes folder
frontend:
themes: !include_dir_merge_named themes
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
sensor:
- platform: serial
name: espnow_hub
serial_port: /dev/ttyUSB2
baudrate: 115200
binary_sensor:
- platform: template
sensors:
doorbell_sensor:
unique_id: "doorbell"
friendly_name: "Doorbell"
value_template: >
{% set d = states('sensor.espnow_hub').split('|') %}
{% if d and (d[0] == 'A0:20:A6:12:4B:FE' or d[0] == 'A0:20:A6:12:4B:FF') %}
{% if d[1] == '2' or d[1] == '1' %}
true
{% else %}
false
{% endif %}
{% endif %}
Now, let’s talk about the arduino code.
My espnow_hub
is just a nodemcu connected to the server via serial. I installed screen
on the HA server, so I could do a screen /dev/ttyUSB2 115200
and see packets being received.
Here's the PlatformIO Arduino code for the receiver
platformio.ini:
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = nodemcu
[env:esp01]
platform = espressif8266
board = esp01
framework = arduino
upload_speed = 921600
upload_port = COM32
monitor_port = COM32
upload_resetmethod = nodemcu
monitor_speed = 115200
lib_deps =
regenbogencode/ESPNowW@^1.0.2
sstaub/Ticker@^4.4.0
build_flags =
'-D RECEIVER_MAC={0xA0, 0x20, 0xA6, 0x12, 0x4B, 0xFC}'
[env:nodemcu]
platform = espressif8266
board = nodemcuv2
framework = arduino
upload_speed = 921600
monitor_speed = 115200
upload_port = COM7
monitor_port = COM7
lib_deps =
regenbogencode/ESPNowW@^1.0.2
sstaub/Ticker@^4.4.0
build_flags =
'-D RECEIVER_MAC={0xA0, 0x20, 0xA6, 0x12, 0x4B, 0xFC}'
main.cpp
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "ESPNowW.h"
#include <EasyButton.h>
//The sender needs the receiver's mac address
uint8_t receiver_mac[] = RECEIVER_MAC;
// put function declarations here:
char macStr[18];
// Arduino pin where the button is connected to.
#define BUTTON_PIN 0
// Instance of the button.
EasyButton button(BUTTON_PIN);
// Callback function to be called when the button is pressed.
void onBtn() {
digitalWrite(D0, LOW);
Serial.print(WiFi.macAddress());
Serial.println("|MACADDR");
delay(100);
digitalWrite(D0, HIGH);
}
//Here we build a nice message for Home assistant
//
//This handles a couple of different scenarios according to the first byte of payload
//MM:AA:CC:MM:AA:CC|1 and MM:AA:CC:MM:AA:CC|0 is a switch
//MM:AA:CC:MM:AA:CC|2 is a momentary switch. The receiver then fakes the MM:AA:CC:MM:AA:CC|0 message
//MM:AA:CC:MM:AA:CC|4|0|1|2|3|... 4 is a sensor and the rest comes in as uint8 HA will split and check the payload
//A bit messy? Allows ESP8266s to send single bytes payloads (especially the doorbell that isn't powered for long)
void onRecv(uint8_t *mac_addr , uint8_t *data , uint8_t data_len)
{
digitalWrite(D0, LOW);
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0] , mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.print(macStr);
for(int i = 0 ; i < data_len; i++)
{
Serial.printf("|%d" , data[i]);
}
Serial.println();
//Here we simulate the sensor going back to 0 when the (single byte) payload is 2 (defined as momentary switch)
if (data[0] == 2 && data_len == 1) {
delay(100);
Serial.print(macStr);
Serial.println("|0");
}
digitalWrite(D0, HIGH);
}
void setup() {
pinMode(D0, OUTPUT);
digitalWrite(D0, HIGH);
Serial.begin(115200);
Serial.println();
#ifdef DEBUG
Serial.print("ESP8266 Board MAC Address: ");
Serial.println(WiFi.macAddress());
Serial.println("ESP8266 RECEIVER");
#endif
// Display the mac address
onBtn();
// Initialize ESPNow
WiFi.mode(WIFI_STA);
wifi_set_macaddr(STATION_IF, &receiver_mac[0]);
WiFi.disconnect();
ESPNow.init();
ESPNow.reg_recv_cb(onRecv);
// Initialize the button.
button.begin();
// Add the callback function to be called when the button is pressed.
button.onPressed(onBtn);
}
void loop() {
// Continuously read the status of the button.
button.read();
}
The MAC address of the receiver is spoofed (much easier for testing) and is displayed on the serial port when the “flash” button is pressed on the nodemcu.
The code for the sender is equally as simple.
Here's the PlatformIO Arduino code for the sender
platformio.ini:
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp01]
platform = espressif8266
board = esp01_1m
framework = arduino
upload_speed = 921600
upload_port = COM6
monitor_port = COM6
upload_resetmethod = nodemcu
monitor_speed = 115200
lib_deps =
regenbogencode/ESPNowW@^1.0.2
sstaub/Ticker@^4.4.0
build_flags =
'-D RECEIVER_MAC={0xA0, 0x20, 0xA6, 0x12, 0x4B, 0xFC}'
;'-D DEBUG'
[env:nodemcu]
platform = espressif8266
board = nodemcuv2
framework = arduino
upload_speed = 921600
monitor_speed = 115200
upload_port = COM9
monitor_port = COM9
lib_deps =
regenbogencode/ESPNowW@^1.0.2
sstaub/Ticker@^4.4.0
build_flags =
'-D RECEIVER_MAC={0xA0, 0x20, 0xA6, 0x12, 0x4B, 0xFC}'
'-D DEBUG'
main.cpp:
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "ESPNowW.h"
//The sender needs the receiver's mac address
uint8_t receiver_mac[] = RECEIVER_MAC;
//Payload. Let's say a toggle button sends a 1 for on and a 0 for off
//A payload of 2 is for a momentary switch
//A payload of 4 indicates a sensor and the data is sent as uint* bytes
uint8_t payload = 2;
//========================================
#ifdef DEBUG
#include <EasyButton.h>
// Arduino pin where the button is connected to.
#define BUTTON_PIN 0
// Instance of the button.
EasyButton button(BUTTON_PIN);
#endif
//========================================
// Callback function to be called when the button is pressed.
void onBtn() {
ESPNow.send_message(receiver_mac , &payload , 1);
//========================================
#if defined DEBUG
digitalWrite(D0, LOW);
Serial.print("Sending payload: ");
Serial.println(payload);
delay(100);
digitalWrite(D0, HIGH);
#endif
//========================================
}
void setup() {
//========================================
#ifdef DEBUG
pinMode(D0, OUTPUT);
digitalWrite(D0, HIGH);
Serial.begin(115200);
Serial.println();
Serial.print("ESP8266 Board MAC Address: ");
Serial.println(WiFi.macAddress());
Serial.println("ESP8266 SENDER");
#endif
//========================================
// Disable WIFI to use ESPNow and add the receiver's MAC as a peer
WiFi.mode(WIFI_STA);
WiFi.disconnect();
ESPNow.init();
ESPNow.add_peer(receiver_mac);
//========================================
#ifdef DEBUG
// Initialize the button.
button.begin();
// Add the callback function to be called when the button is pressed.
button.onPressed(onBtn);
#endif
//========================================
}
void loop() {
//========================================
#ifdef DEBUG
// Continuously read the status of the button.
button.read();
#else
onBtn();
delay(1000);
#endif
//========================================
}
It’s as bare as possible in its non-debug version and will send its first payload as soon as it’s powered on, then once every second. That’s how I know my energy budget is only enough to run the code for under a second.
Once all is in place, I can see my entities:
and the automation side is really basic right now (just got into HA).
- id: '1718431786475'
alias: Doorbell Automation
description: ''
trigger:
- platform: state
entity_id:
- binary_sensor.doorbell_sensor_2
from: 'off'
to: 'on'
condition: []
action:
- service: notify.notify
metadata: {}
data:
message: Someone at the door!
data:
channel: General
importance: high
visibility: public
mode: single
That’s all I can say about my first HA project. Any comments, suggestions or improvements welcome. This is all released under the “do as you please” WTFPL public license
Sorry, I will add pictures when I’m allowed to. Totally understandable!
Cheers,
John