270 lines
8.7 KiB
YAML
270 lines
8.7 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"
|
|
|
|
# Passwords & Secrets (Unfortunately, you can't use substitutions inside secret names)
|
|
# Re-using your existing bulb flasher secrets (adjust if you prefer per-device secrets)
|
|
api_key: !secret esp-api_key
|
|
ota_pass: !secret esp-ota_pass
|
|
#static_ip_address: !secret esp-bedrm1arlecbulb_ip
|
|
|
|
#:########################################################################################:#
|
|
# 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); |