Files
zorruno-homeassistant/esphome/esp-espnow_test_receiver.yaml
T
2026-07-11 17:55:33 +12:00

267 lines
8.5 KiB
YAML

#:########################################################################################:#
# ESP-NOW Test Receiver
#:########################################################################################:#
#
# DEVICE:
# ESP32 DevKit V1 / ESP-WROOM-32
#
# VERSION:
# 1.0 - 2026-07-11
# - Initial ESP-NOW receiver test build.
# - Receives broadcast packets from ESP32-S3-Zero transmitter.
# - Publishes last sender MAC, payload, RSSI, received count and duplicate count.
# - Creates a short Home Assistant event pulse for each unique press_id.
#
# NOTES:
# - This device is the powered receiver.
# - The transmitter should send the same packet 2-3 times for reliability.
# - This receiver de-duplicates repeated packets using the full payload string.
# - Expected test payload format from transmitter:
# REMOTE_A:BUTTON_1:123
#
#:########################################################################################:#
substitutions:
device_name: espnow_test_receiver
friendly_name: "ESPNow Test Receiver"
area: "Test Bench"
ota_pass: !secret ota_password
api_key: !secret api_encryption_key
#:########################################################################################:#
# PACKAGES: Included Common Packages
# https://esphome.io/components/packages.html
#:########################################################################################:#
packages:
#### WIFI, Network (DHCP/IPV6 etc), Fallback AP, Safemode ####
common_wifi: !include
file: common/network_common_dhcp.yaml
vars:
local_device_name: "${device_name}"
local_ota_pass: "${ota_pass}"
#### HOME ASSISTANT API (choose encryption or no encryption options) ####
common_api: !include
file: common/api_common.yaml
#file: common/api_common_noencryption.yaml
vars:
local_api_key: "${api_key}"
#### MQTT ####
#common_mqtt: !include
# file: common/mqtt_common.yaml
# vars:
# local_device_name: "${device_name}"
#### WEB PORTAL ####
#common_webportal: !include common/webportal_common.yaml
common_webportal: !include common/webportal_common_nopasswd.yaml
#### SNTP (Only use if you want/need accurate timeclocks) ####
#common_sntp: !include common/sntp_common.yaml
#### DIAGNOSTICS Sensors ####
diag_basic: !include common/include_basic_diag_sensors.yaml
diag_more: !include common/include_more_diag_sensors_nomqtt.yaml
#diag_debug: !include common/include_debug_diag_sensors.yaml
#diag_resetcount: !include common/include_resetcount_diag_sensors.yaml
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
area: ${area}
esp32:
board: esp32dev
framework:
type: arduino
#:########################################################################################:#
# ESP-NOW Receiver
#:########################################################################################:#
#
# Do not set channel here while using Wi-Fi.
# The receiver's ESP-NOW channel follows the connected Wi-Fi channel.
#
espnow:
auto_add_peer: true
on_broadcast:
- then:
- lambda: |-
char src_mac[18];
snprintf(src_mac, sizeof(src_mac), "%02X:%02X:%02X:%02X:%02X:%02X",
info.src_addr[0], info.src_addr[1], info.src_addr[2],
info.src_addr[3], info.src_addr[4], info.src_addr[5]);
std::string payload(reinterpret_cast<const char *>(data), size);
int rssi = info.rx_ctrl ? info.rx_ctrl->rssi : 0;
id(last_sender_mac).publish_state(src_mac);
id(last_payload).publish_state(payload);
id(last_rssi).publish_state(rssi);
id(received_count) += 1;
id(received_count_sensor).publish_state(id(received_count));
ESP_LOGI("espnow_rx", "ESP-NOW broadcast from %s, RSSI %d dBm, payload: %s",
src_mac, rssi, payload.c_str());
// Test payload format:
// REMOTE_A:BUTTON_1:123
//
// For now, use the whole payload as the duplicate key.
// This means the 2nd and 3rd repeated sends from one button press are ignored.
if (payload == id(last_unique_payload)) {
id(duplicate_count) += 1;
id(duplicate_count_sensor).publish_state(id(duplicate_count));
id(packet_is_unique) = false;
ESP_LOGD("espnow_rx", "Duplicate ESP-NOW payload ignored: %s", payload.c_str());
return;
}
id(last_unique_payload) = payload;
id(packet_is_unique) = true;
// Split simple colon-delimited payload for HA visibility.
size_t first_colon = payload.find(':');
size_t second_colon = payload.find(':', first_colon + 1);
if (first_colon != std::string::npos && second_colon != std::string::npos) {
std::string remote_id = payload.substr(0, first_colon);
std::string button_id = payload.substr(first_colon + 1, second_colon - first_colon - 1);
std::string press_id = payload.substr(second_colon + 1);
id(last_remote_id).publish_state(remote_id);
id(last_button_id).publish_state(button_id);
id(last_press_id).publish_state(press_id);
ESP_LOGI("espnow_rx", "Unique press: remote=%s button=%s press_id=%s",
remote_id.c_str(), button_id.c_str(), press_id.c_str());
} else {
id(last_remote_id).publish_state("unknown");
id(last_button_id).publish_state("unknown");
id(last_press_id).publish_state("unknown");
ESP_LOGW("espnow_rx", "Payload did not match expected format: %s", payload.c_str());
}
- if:
condition:
lambda: |-
return id(packet_is_unique);
then:
- binary_sensor.template.publish:
id: espnow_unique_press_event
state: ON
- script.execute: clear_unique_press_event
#:########################################################################################:#
# Globals
#:########################################################################################:#
globals:
- id: received_count
type: int
restore_value: true
initial_value: '0'
- id: duplicate_count
type: int
restore_value: true
initial_value: '0'
- id: last_unique_payload
type: std::string
restore_value: true
initial_value: '""'
- id: packet_is_unique
type: bool
restore_value: false
initial_value: 'false'
#:########################################################################################:#
# Scripts
#:########################################################################################:#
script:
- id: clear_unique_press_event
mode: restart
then:
- delay: 500ms
- binary_sensor.template.publish:
id: espnow_unique_press_event
state: OFF
#:########################################################################################:#
# Home Assistant Entities
#:########################################################################################:#
text_sensor:
- platform: template
name: "Last Sender MAC"
id: last_sender_mac
icon: mdi:access-point-network
- platform: template
name: "Last Payload"
id: last_payload
icon: mdi:message-text
- platform: template
name: "Last Remote ID"
id: last_remote_id
icon: mdi:remote
- platform: template
name: "Last Button ID"
id: last_button_id
icon: mdi:gesture-tap-button
- platform: template
name: "Last Press ID"
id: last_press_id
icon: mdi:identifier
sensor:
- platform: template
name: "Last RSSI"
id: last_rssi
unit_of_measurement: "dBm"
accuracy_decimals: 0
icon: mdi:wifi-strength-2
- platform: template
name: "Received Count"
id: received_count_sensor
accuracy_decimals: 0
icon: mdi:counter
- platform: template
name: "Duplicate Count"
id: duplicate_count_sensor
accuracy_decimals: 0
icon: mdi:counter
binary_sensor:
- platform: template
name: "Unique Press Event"
id: espnow_unique_press_event
icon: mdi:gesture-tap-button
button:
- platform: template
name: "Reset ESP-NOW Test Counts"
icon: mdi:counter
on_press:
then:
- lambda: |-
id(received_count) = 0;
id(duplicate_count) = 0;
id(last_unique_payload) = "";
id(received_count_sensor).publish_state(0);
id(duplicate_count_sensor).publish_state(0);