Ultrasonic sensor with poor wifi (and MQTT)

Hi
First time posting here - but i have been very actively reading and learning from others.
I have a few esphome projects - from automatic door opening linear actuators (for hot cupboards with water coolers) to integrations with texecom alarms

I have a problem I have been scratching my head over for weeks!
I have a ultrasonic sensor attached to an ESP8266 suspended above a bin. My intention is that i can tell when the bin men collect this bin (from a period of 30 seconds or so). I would then (ideally) want to get an alert “bin collected”… or better if i haven’t seen this collection by a cut off time (lets say 1630 on bin collection day) i would be nofified.

I started with a straight forward approach, ultrasonic sensor triggering a binary sensor when a range is hit. I added smoothing and averaging for robustness - following other examples.

I have one main issue… the esp8266 is quite a way from the house. i have mounted an external access point and done the best i can. in good weather signal sits at -75db in bad it just popps over -80db - meaning the device loses connection.

i think i can work around this by: disabling the API and maybe logging the bin presence via MQTT. i need this to somehow be fault tolerant… so ideally even if it gets one message through an hour - if that message could say if the bin was collected that would be fine.

i was thinking i would mesasge a timestamp… maybe repeatedly and allow some to fail. the two issues with that are 1) i can’t use the time directive without the API enabled. 2) even with API disabled - it still seems to fail.

Any help much appreciated

code below doesn’t do MQTT publish (as time is broken) but i have tried this too and the thing still seems to change to unavailable -even on the MQTT derived sensor.

substitutions:
  device_id: "bin-sensor-left"
  device_name: "Bin Sensor Left"
  distance_update_time: 2s
  wifi_update_time: 30s
  heartbeat_interval: 5s

esp8266:
  board: d1_mini

logger:
  level: INFO

#api:

ota:
  safe_mode: true
  num_attempts: 5

wifi:  
  reboot_timeout: 5h
  use_address: 192.168.0.33
  networks:
  - ssid: !secret outdoor_wifi_ssid
    password: !secret outdoor_wifi_password
    priority: 0.8
    manual_ip:
      static_ip: 192.168.0.33
      gateway: 192.168.0.1
      subnet: 255.255.254.0
      dns1: 192.168.0.1
      dns2: 1.1.1.1

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Fallback ${device_id}" 
    password: "secret password"

captive_portal:


web_server:
  port: 80
  auth:
    username: !secret esphome_web_username
    password: !secret esphome_web_password



globals:
  #distance from sensor to top of bin
 - id: bin_height
   type: float
   initial_value: '0.8'

#doesn't work when API disabled
time:
  - platform: homeassistant
    id: homeassistant_time

sensor:
  - platform: wifi_signal
    name: "${device_name} Wifi Signal"
    id: wifisignal
    update_interval: ${wifi_update_time}
    internal: true
    
  - platform: uptime
    id: upti
    name: "${device_name} Uptime"
    update_interval: ${heartbeat_interval}
    internal: true

  
  - platform: template
    name: $device_name Last Collected
    device_class: timestamp
    id: last_collected
    icon: mdi:calendar-clock

  - platform: ultrasonic
    trigger_pin: D6
    echo_pin: D5
    id: distance
    name: "${device_name} Distance"
    update_interval: ${distance_update_time}
    timeout: 3m
    pulse_time: 13us
    accuracy_decimals: 1
    unit_of_measurement: "m"
    icon: "mdi:ruler"
    state_class: measurement
    filters:
      - filter_out: nan 
      - median:
          window_size: 7
          send_every: 4
          send_first_at: 3
      - delta: 0.2

    
      
binary_sensor:
  - platform: template
    id: bin_presence
    device_class: presence
    icon: "mdi:trash-can"
    name: "${device_name} Presence"
    lambda: |-
      if (id(distance).state < id(bin_height)) {
        // Presence is detected
        return true;
      } else {
        // No presence detected.
        return false;
      }
    on_release:
      then:
        - sensor.template.publish:
            id: last_collected
            state: !lambda 'return id(homeassistant_time).now().timestamp;'
        - text_sensor.template.publish:
            id: last_collected_as_text
            state: !lambda |-
                char str[17];
                time_t currTime = id(homeassistant_time).now().timestamp;
                strftime(str, sizeof(str), "%Y-%m-%d %H:%M", localtime(&currTime));
                return  { str };
    filters:
      - delayed_on_off: 10s

    
status_led:
  pin: 
    number: GPIO2
    inverted: false
    
mqtt:
   topic_prefix: homeassistant
   discovery: true
   broker: my ip
   port: 1883
   username: mqtt_bin_sensor
   password: SOMEPASSWORD

Did you consider adding an antenna to the esp?

The wemos mini pro has an antenna connector.

I have ordered this - and yes i did think as a possible solution… but part of me wants to know how to code for an esp8266 that could lose signal

That is like saying “how do I drive a car with no petrol in the tank?”

I agree with getting an antenna and all that.

I work with large-scale, fault-tolerant, etc. systems. There are a few things one could do, depending on needs, caoabilities and assumptions. In your case, perhaps use MQTT. It’s possible to know when the connection has been made and based on that you can publish a message. I’m reading your requirement as “eventual consistency” (i.e. I want to know immediately, but if not, as soon as possible).

The less efficient way would be to have an automation on the ESP that publishes a message say every minute, regardless of whether it failed or succeeded.

In your case you might also want to save the collection time to memory. Not sure if there’s a chance your ESP might power cycle before it had the chance to send (on a really blue Monday, e.g.).

Thanks - i think i’m working along the same lines. if i go with the MQTT route - passing the timestamp is my next problem. I won’t know when the esp last powercycled - are there any other ways of getting time other than using the API?

Look here: Automations and Templates — ESPHome.

Make a global that’s saved to flash (not RTC, since it won’t survive a reboot, but note the note on cycles and flash longevity).