Created
September 21, 2025 19:16
-
-
Save boverby/e89b549c121146466a00fddaf0e3cc08 to your computer and use it in GitHub Desktop.
simple espnow example for esphome
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # min_espnow.yaml | |
| # see https://github.com/u-fire/ESPHomeComponents - capture data and auto-discovery | |
| # see https://github.com/ChuckMash/ESPythoNOW - listens to espnow from linux/python | |
| # see https://github.com/boverby/python_espnow_mqtt - combine those two | |
| # see https://community.home-assistant.io/t/consultation-on-the-usage-of-espnow-in-esphome/928005/33 | |
| # see https://ncrmnt.org/2021/12/06/optimizing-esp8266-esphome-for-battery-power-and-making-an-ice-bath-thermometer-as-well/ | |
| ## updates in < 1 sec == more battery life | |
| # python_espnow_mqtt_host command: journalctl --since "1 min ago" -f -o short-precise -u python_espnow_mqtt.service | |
| substitutions: | |
| device: min_espnow | |
| thismacaddr: 24:EC:4A:27:18:9C | |
| sensor_readings_count: 20 #### note 50 resulted in boot loop | |
| esp32: | |
| board: esp32-s3-devkitc-1 | |
| framework: | |
| type: esp-idf | |
| logger: | |
| level: DEBUG | |
| # Deep sleep component | |
| deep_sleep: | |
| run_duration: 10s # doesnt matter, will be shortcut by on_boot process | |
| sleep_duration: 300s | |
| wakeup_pin: | |
| number: GPIO7 | |
| allow_other_uses: true | |
| mode: | |
| input: true | |
| pulldown: true | |
| # ESP-NOW component | |
| espnow: | |
| auto_add_peer: true | |
| channel: 1 | |
| peers: | |
| - 00:14:d1:63:8c:3c ### linux based espnow receiver (dvr) | |
| i2c: | |
| sda: GPIO6 | |
| scl: GPIO5 | |
| globals: | |
| - id: sent | |
| type: int | |
| restore_value: no | |
| initial_value: '0' | |
| binary_sensor: | |
| - platform: gpio | |
| pin: | |
| number: GPIO7 | |
| mode: | |
| input: true | |
| pulldown: true | |
| allow_other_uses: true | |
| name: "${device} motion 07" | |
| id: ${device}_motion_07 | |
| icon: mdi:motion-sensor | |
| internal : true | |
| device_class: motion | |
| on_press: | |
| then: | |
| - logger.log: "${device} GPIO07 acted" | |
| - lambda: id(sent)++; | |
| # Sensor configuration | |
| sensor: | |
| - platform: shtcx | |
| address: 0x70 | |
| update_interval: 1s | |
| temperature: | |
| filters: | |
| - lambda: return x * (9.0/5.0) + 32.0; | |
| unit_of_measurement: "°F" | |
| state_class: "measurement" | |
| icon: mdi:thermometer | |
| name: "${device} Temperature" | |
| id: ${device}_temperature | |
| on_value: | |
| then: | |
| - lambda: id(sent)++; | |
| humidity: | |
| name: "${device} Humidity" | |
| id: ${device}_humidity | |
| state_class: "measurement" | |
| icon: mdi:water-percent | |
| filters: | |
| - filter_out: nan | |
| on_value: | |
| then: | |
| - lambda: id(sent)++; | |
| - platform: adc | |
| pin: GPIO3 | |
| name: "${device} vdd" | |
| id: "${device}_vdd" | |
| attenuation: auto | |
| state_class: "measurement" | |
| icon: mdi:flash-outline | |
| update_interval: never | |
| filters: | |
| - median: | |
| window_size: ${sensor_readings_count} | |
| send_every: ${sensor_readings_count} | |
| send_first_at: ${sensor_readings_count} | |
| - lambda: return x * 5.537 * 1.03759398 ; | |
| on_value: | |
| then: | |
| - component.update: "${device}_battery" | |
| - lambda: id(sent)++; | |
| # https://community.home-assistant.io/t/esphome-battery-level-sensor/245196/19 | |
| - platform: template | |
| name: "${device} Battery" | |
| id: ${device}_battery | |
| lambda: return id(${device}_vdd).state; | |
| accuracy_decimals: 0 | |
| unit_of_measurement: "%" | |
| device_class: battery | |
| state_class: "measurement" | |
| icon: mdi:battery-medium | |
| update_interval: never | |
| filters: | |
| - calibrate_linear: | |
| method: exact | |
| datapoints: | |
| - 0.00 -> 0.0 | |
| - 3.30 -> 1.0 | |
| - 3.39 -> 10.0 | |
| - 3.75 -> 50.0 | |
| - 4.11 -> 90.0 | |
| - 4.20 -> 100.0 | |
| - lambda: |- | |
| if (x <= 100) { | |
| return x; | |
| } else { | |
| return 100; | |
| } | |
| if (x <0) { | |
| return 0; | |
| } | |
| on_value: | |
| then: | |
| - lambda: id(sent)++; | |
| # Main automation to run on boot | |
| esphome: | |
| name: ${device} | |
| platformio_options: | |
| build_flags: -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=1 | |
| on_boot: | |
| # Manually trigger sensor updates on boot | |
| - then: | |
| - wait_until: ### required because shtcx is not pollable | |
| condition: | |
| - lambda: 'return id(${device}_temperature).has_state();' | |
| - repeat: | |
| count: ${sensor_readings_count} | |
| then: | |
| - component.update: ${device}_vdd | |
| - espnow.send: | |
| address: "00:14:d1:63:8c:3c" | |
| data: !lambda |- | |
| char buf[160]; | |
| int8_t accuracy = id(${device}_temperature)->get_accuracy_decimals(); | |
| sprintf(buf, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:", | |
| str_snake_case( App.get_name() ).c_str() , | |
| id(${device}_temperature).get_device_class().c_str(), | |
| state_class_to_string( id(${device}_temperature)->get_state_class() ) , | |
| str_snake_case( id(${device}_temperature).get_name() ).c_str() , | |
| id(${device}_temperature).get_unit_of_measurement().c_str() , | |
| value_accuracy_to_string(id(${device}_temperature).state, accuracy).c_str() , | |
| id(${device}_temperature).get_icon().c_str(), | |
| ESPHOME_VERSION, | |
| ESPHOME_BOARD, | |
| "sensor" | |
| ); | |
| std::string s = buf; | |
| return std::vector<unsigned char>( s.begin(), s.end() ); | |
| - espnow.send: | |
| address: "00:14:d1:63:8c:3c" | |
| data: !lambda |- | |
| char buf[160]; | |
| int8_t accuracy = id(${device}_humidity)->get_accuracy_decimals(); | |
| sprintf(buf, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:", | |
| str_snake_case( App.get_name() ).c_str() , | |
| id(${device}_humidity).get_device_class().c_str(), | |
| state_class_to_string( id(${device}_humidity)->get_state_class() ) , | |
| str_snake_case( id(${device}_humidity).get_name() ).c_str() , | |
| id(${device}_humidity).get_unit_of_measurement().c_str() , | |
| value_accuracy_to_string(id(${device}_humidity).state, accuracy).c_str() , | |
| id(${device}_humidity).get_icon().c_str(), | |
| ESPHOME_VERSION, | |
| ESPHOME_BOARD, | |
| "sensor" | |
| ); | |
| std::string s = buf; | |
| return std::vector<unsigned char>( s.begin(), s.end() ); | |
| - espnow.send: | |
| address: "00:14:d1:63:8c:3c" | |
| data: !lambda |- | |
| char buf[160]; | |
| int8_t accuracy = id(${device}_vdd)->get_accuracy_decimals(); | |
| sprintf(buf, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:", | |
| str_snake_case( App.get_name() ).c_str() , | |
| id(${device}_vdd).get_device_class().c_str(), | |
| state_class_to_string( id(${device}_vdd)->get_state_class() ) , | |
| str_snake_case( id(${device}_vdd).get_name() ).c_str() , | |
| id(${device}_vdd).get_unit_of_measurement().c_str() , | |
| value_accuracy_to_string(id(${device}_vdd).state, accuracy).c_str() , | |
| id(${device}_vdd).get_icon().c_str(), | |
| ESPHOME_VERSION, | |
| ESPHOME_BOARD, | |
| "sensor" | |
| ); | |
| std::string s = buf; | |
| return std::vector<unsigned char>( s.begin(), s.end() ); | |
| on_sent: | |
| - logger.log: | |
| format: "ESPNow message vdd sent successfully" | |
| level: INFO | |
| tag: ESPNOW | |
| on_error: | |
| - logger.log: "ESPNow message vdd failed to send" | |
| - espnow.send: | |
| address: "00:14:d1:63:8c:3c" | |
| data: !lambda |- | |
| char buf[160]; | |
| int8_t accuracy = id(${device}_battery)->get_accuracy_decimals(); | |
| sprintf(buf, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:", | |
| str_snake_case( App.get_name() ).c_str() , | |
| id(${device}_battery).get_device_class().c_str(), | |
| state_class_to_string( id(${device}_battery)->get_state_class() ) , | |
| str_snake_case( id(${device}_battery).get_name() ).c_str() , | |
| id(${device}_battery).get_unit_of_measurement().c_str() , | |
| value_accuracy_to_string(id(${device}_battery).state, accuracy).c_str() , | |
| id(${device}_battery).get_icon().c_str(), | |
| ESPHOME_VERSION, | |
| ESPHOME_BOARD, | |
| "sensor" | |
| ); | |
| std::string s = buf; | |
| return std::vector<unsigned char>( s.begin(), s.end() ); | |
| on_sent: | |
| - logger.log: | |
| format: "ESPNow message battery sent successfully" | |
| level: INFO | |
| tag: ESPNOW | |
| on_error: | |
| - logger.log: "ESPNow message battery failed to send" | |
| # Ensure the device has time to transmit before sleeping | |
| # - delay: 5s | |
| - deep_sleep.enter #### shortcut to end since all work done ( save battery) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment