#:########################################################################################:# # 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" packages: network: !include common/network_common_dhcp.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(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);