How to get a device to work stable at the limit of wifi range (w or w/o wifi)?

I made chicken coop door controller for my parents, and I attempted to make it as self-sufficient as possible as I knew that wifi may be an issue. I put all the automations within esphome instead of HA in the hopes that if it couldn’t phone home it would still work as programmed. I added some API and Wifi timeout settings based on my understanding of what was needed to get it to work without wifi. I thought I had success as the door controller worked as expected when not connected to HA/Esphome/Wifi (in my testing). However, as is the case with most projects, it didn’t go as expected. When I got the door to the coop location and plugged it in I had very strange results, the screen would blink on and off (not programmed) and the buttons wouldn’t work. After a while it began to work, but that is because it connected to wifi, albiet very weakly (~-80db). It stayed connected and worked for a few days and then disconnected. In its disconnected state the door did not work as programmed. I’m wondering if the weird operation is due to it being at the limit of wifi and it’s connecting/disconnecting “quickly”, not allowing it to get to the normal program cycle. Any troubleshooting tips, suggestions or insight would be appreciated. If I can’t get it to work in it’s location with wifi, my last effort will be to disable wifi, however I would like for it to try now and again to connect. Following is the esphome yaml, which is somewhat long, but I assume the relevant area to my issue is in the beginning.

esphome:
  name: coop-controller
  friendly_name: coop_controller
  on_boot:
    then:
      # read the RTC time once when the system boots
      ds1307.read_time:

rp2040:
  board: rpipicow
  framework:
    # Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged
    platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git

# Enable logging
logger:
  #level: INFO
# Enable Home Assistant API
api:
  reboot_timeout: 240h
  encryption:
    key: "secret"

ota:
  password: "secret"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot in case wifi connection fails
  ap:
    ssid: "Coop-Controller Fallback Hotspot"
    password: "secret"
  
  reboot_timeout: 24h #default is 15min
#########################################################################
i2c:
  id: bus_a
  sda: GPIO20
  scl: GPIO21
  scan: true
  
time:
  - platform: ds1307
    id: rtc_time
    i2c_id: bus_a
    address: 0x68
    # repeated synchronization is not necessary unless the external RTC
    # is much more accurate than the internal clock
    update_interval: never
  - platform: homeassistant
    id: esptime
    # instead try to synchronize via network repeatedly ...
    on_time_sync:
      then:
        # ... and update the RTC when the synchronization was successful
        ds1307.write_time:

font:
  - file: "fonts/roboto/Roboto-Regular.ttf"
    id: font1
    size: 12
  - file: "fonts/roboto/Roboto-Regular.ttf"
    id: font2
    size: 20

image:
  - file: "open.png"
    id: open_image
  - file: "closed.png"
    id: closed_image
  - file: "opening.png"
    id: opening_image
  - file: "closing.png"
    id: closing_image

display:
  - platform: ssd1306_i2c
    id: coop_display
    i2c_id: bus_a
    model: "SSD1306 128x64"
    address: 0x3C
    on_page_change:
      - if:
          condition:
            display.is_displaying_page: page1         
          then:
            sensor.template.publish:
              id: display_sensor
              state: 1
      - if:
          condition:
            display.is_displaying_page: page2         
          then:
            sensor.template.publish:
              id: display_sensor
              state: 2
      - if:
          condition:
            display.is_displaying_page: page3         
          then:
            sensor.template.publish:
              id: display_sensor
              state: 3
      - if:
          condition:
            display.is_displaying_page: page4         
          then:
            sensor.template.publish:
              id: display_sensor
              state: 4
      - if:
          condition:
            display.is_displaying_page: page5         
          then:
            sensor.template.publish:
              id: display_sensor
              state: 5
      - if:
          condition:
            display.is_displaying_page: page6         
          then:
            sensor.template.publish:
              id: display_sensor
              state: 6
    pages:
    - id: page1
      lambda: |-
        it.image(0, 0, id(open_image));
    - id: page2
      lambda: |-
        it.image(0, 0, id(closed_image));
    - id: page3
      lambda: |-
        it.image(0, 0, id(opening_image));
    - id: page4
      lambda: |-
        it.image(0, 0, id(closing_image));

    - id: page5
      lambda: |-
          it.print(0, 0, id(font1), "Time:");
          it.strftime(32, 0, id(font1), "%H:%M", id(esptime).now());
          it.printf(128, 0, id(font1), TextAlign::TOP_RIGHT,"Wifi: %.0fdb", id(wifi_signal_db).state);
          it.printf(0, 17, id(font1), "Temp: %.1f°F", id(coop_temperature).state);
          it.print(0, 33, id(font1), "Open Time: ");
          it.printf(128, 33, id(font1), TextAlign::TOP_RIGHT, "%s", id(next_coop_door_open).state.c_str());
          it.print(0, 49, id(font1), "Close Time:");
          it.printf(128, 49, id(font1), TextAlign::TOP_RIGHT, "%s", id(next_coop_door_close).state.c_str());
          
    - id: page6
      lambda: |-   
          it.print(0, 0, id(font1), "");

dallas:
  pin: GPIO22

sun:
  latitude: 45° #actual location changed for posting
  longitude: -85° #actual location changed for posting
  id: coop_sun

  on_sunrise:
    - elevation: -2°
      then:
        - logger.log: Good morning Birds - Open Door!
        - cover.open: coop_door
    - then:
        - logger.log: Hello Sun!
  on_sunset:
    - then:
        - logger.log: Goodbye Sun!
    - elevation: -5°
      then:
        - logger.log: Good Night Birds - Door Close!
        - cover.close: coop_door

text_sensor:
  - platform: sun
    name: Next Sunrise
    type: sunrise
  - platform: sun
    name: Next Sunset
    type: sunset
  - platform: sun
    name: Next Coop Door Open
    id: next_coop_door_open
    type: sunrise
    elevation: -2°
  - platform: sun
    name: Next Coop Door Close
    id: next_coop_door_close
    type: sunset
    elevation: -5°

cover:
  - platform: endstop
    device_class: door
    name: "Coop Door"
    id: coop_door
    open_action:
      - display.page.show: page3
      - switch.turn_on: green_led_coop
      - switch.turn_off: motor_reverse_pin
      - switch.turn_on: motor_forward_pin
    open_duration: 10s
    open_endstop: coop_upr_limit_switch
 
    close_action:
      - display.page.show: page4
      - switch.turn_on: green_led_coop
      - switch.turn_on: motor_reverse_pin
      - switch.turn_off: motor_forward_pin
    close_duration: 10s
    close_endstop: coop_lwr_limit_switch

    stop_action:
      - switch.turn_off: motor_forward_pin
      - switch.turn_off: motor_reverse_pin
      - switch.turn_off: green_led_coop
      - display.page.show: page5
      - delay: 10s  
      - display.page.show: page6

    max_duration: 15s

sensor:
  - platform: wifi_signal 
    name: "WiFi Signal dB"
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"
  - platform: internal_temperature
    name: "Internal Temperature"
      ##Dallas DS18B20
  - platform: dallas
    address: 0x270217b085efff28 
    name: "Coop Temperature"
    id: coop_temperature
    filters:
      - lambda: return x * (9.0/5.0) + 32.0;
    unit_of_measurement: "°F"
    ##Rotary Encoder
  - platform: rotary_encoder
    name: "Coop Motor Encoder"
    restore_mode: ALWAYS_ZERO
    pin_a: 
      number: GPIO10
      mode:
        input: true
        pullup: true
    pin_b: 
      number: GPIO9
      mode:
        input: true
        pullup: true
     ##sun sensor
  - platform: sun
    name: Sun Elevation
    type: elevation
  - platform: sun
    name: Sun Azimuth
    type: azimuth
  - platform: template
    name: "Display Sensor"
    id: display_sensor
  

switch:
  - platform: gpio
    pin: GPIO32
    name: "Onboard LED"
    on_turn_on:
      - display.page.show: page5
    on_turn_off:
      - display.page.show: page6
  - platform: gpio
    pin: GPIO15
    name: "Green LED"
    id: green_led_coop
  - platform: gpio
    pin: GPIO12
    name: "Red LED"
    id: red_led_coop
    ## Setup for motor control pins
  - platform: gpio
    id: motor_forward_pin
    pin: GPIO18
  - platform: gpio
    id: motor_reverse_pin
    pin: GPIO19
    ##following is so HA can manually control the same as physical buttons
  - platform: template
    name: "Manual Door UP"
    turn_on_action:
      - script.execute: manual_door_open
    turn_off_action:
      - script.execute: manual_door_stop
  - platform: template
    name: "Manual Door Down"
    turn_on_action:
      - script.execute: manual_door_close
    turn_off_action:
      - script.execute: manual_door_stop

binary_sensor:
    ### setup buttons to control door manually
  - platform: gpio
    pin: 
      number: GPIO13
      inverted: true
      mode:
        input: true
        pullup: true
    name: "Button - Door Down"
    filters:
      - delayed_on: 200ms 
    on_press:
      - script.execute: manual_door_close_local
    on_release:
      - script.execute: manual_door_stop
  - platform: gpio
    pin: 
      number: GPIO14
      inverted: true
      mode:
        input: true
        pullup: true
    name: "Button - Door Up"
    filters:
      - delayed_on: 200ms 
    on_press:
      - script.execute: manual_door_open_local
    on_release:
      - script.execute: manual_door_stop   
  - platform: gpio
    name: "Upr Limit Switch"
    id: coop_upr_limit_switch
    pin: 
      number: GPIO16
      inverted: true
      mode:
        input: true
    filters:
      - delayed_off: 10ms  
    on_press:
      - switch.turn_on: red_led_coop
      - delay: 5s  
      - switch.turn_off: red_led_coop

  - platform: gpio
    name: "Lwr Limit Switch"
    id: coop_lwr_limit_switch
    pin: 
      number: GPIO17
      inverted: true
      mode:
        input: true
    filters:
      - delayed_off: 10ms  
      - delayed_on: 2s  #allow for the autolock to engage
    on_press:
      - switch.turn_on: red_led_coop
      - delay: 6s  
      - switch.turn_off: red_led_coop 

  - platform: gpio
    name: "Main Door Switch"
    id: main_door_switch
    device_class: door
    pin: 
      number: GPIO6
      inverted: false
      mode:
        input: true
    filters:
      - delayed_off: 10ms  
    on_press:
      - display.page.show: page5
      - delay: 10min
      - display.page.show: page6
    on_release:
      - display.page.show: page5
      - delay: 5min
      - display.page.show: page6


#Scripts to be called so both physical button and HA can manually control door
#Made separate scripts for physical button vs HA "button" for delays
script:
  - id: manual_door_open
    mode: restart
    then:
      - display.page.show: page3
      - switch.turn_on: green_led_coop
      - switch.turn_on: motor_forward_pin
      - switch.turn_off: motor_reverse_pin
      - delay: 5s  
      - switch.turn_off: motor_forward_pin
      - switch.turn_off: motor_reverse_pin
      - switch.turn_off: green_led_coop
      - display.page.show: page5
      - delay: 10s  
      - display.page.show: page6
  - id: manual_door_close
    mode: restart
    then:
      - display.page.show: page4
      - switch.turn_on: green_led_coop
      - switch.turn_off: motor_forward_pin
      - switch.turn_on: motor_reverse_pin
      - delay: 5s  
      - switch.turn_off: motor_forward_pin
      - switch.turn_off: motor_reverse_pin
      - switch.turn_off: green_led_coop
      - display.page.show: page5
      - delay: 10s  
      - display.page.show: page6
  - id: manual_door_stop
    mode: restart
    then:
      - switch.turn_off: motor_forward_pin
      - switch.turn_off: motor_reverse_pin
      - switch.turn_off: green_led_coop
      - display.page.show: page5
      - delay: 10s  
      - display.page.show: page6
  - id: manual_door_open_local
    mode: restart
    then:
      - display.page.show: page3
      - switch.turn_on: green_led_coop
      - switch.turn_on: motor_forward_pin
      - switch.turn_off: motor_reverse_pin
      - delay: 15s  
      - switch.turn_off: motor_forward_pin
      - switch.turn_off: motor_reverse_pin
      - switch.turn_off: green_led_coop
      - display.page.show: page5
      - delay: 10s  
      - display.page.show: page6
  - id: manual_door_close_local
    mode: restart
    then:
      - display.page.show: page4
      - switch.turn_on: green_led_coop
      - switch.turn_off: motor_forward_pin
      - switch.turn_on: motor_reverse_pin
      - delay: 15s  
      - switch.turn_off: motor_forward_pin
      - switch.turn_off: motor_reverse_pin   
      - switch.turn_off: green_led_coop 
      - display.page.show: page5
      - delay: 10s  
      - display.page.show: page6

Try disabling the reboot altogether:

api:
  reboot_timeout: 0s
  encryption:
    key: "secret"

I’m thinking you might be having issues with your time keeping initialization and the automatic reboots.

On boot you trigger read_time from the RTC, then on_time_sync you potentially jump into trying to sync the time with Home Assistant.

I’m guessing on a reboot where wifi doesn’t come up, it gets stuck trying to make that happen (I’ve had some problems with timesync with ESPHome when it’s not available).

What you could do to see what’s going on is setup a separate wifi network which you can turn off, connect the device to that while indoors, and then just switch it off and see what happens while having the logging running over serial.

1 Like

Have you (could you) attach an antenna to your rp2040?

Bit like this.

I guess you could turn wifi on/off on an interval:

You should be able to confirm this using the relevant trigger/automation etc.

Thanks for the info. I will try removing it. I had added it because when I first noticed the issue I only had the wifi timeout and after digging a little I found a post where someone suggested adding it to the api. That being said after adding it, it did not solve the issue.

Thanks for the suggestion. I had not thought about the time sync being the issue. Do you have a suggestion on how I could rework it so that it trys to sync at some point but moves on if it can’t?

The one thing I noticed when it was in it’s “weird” mode and the screen would blink on and off is the time was displaying on the screen, but the other sensors were “nan” so I wasn’t sure how it was getting to the display instructions but not the other sensors.

Thanks for the info. Unfortunately, it is not possible to add an external antenna to the rp pico W, however I did order some ESP32’s with an external antenna as my ultimate back-up plan.

You should be able to confirm this using the relevant trigger/automation etc.

Not sure where to start with this. Any ideas? Besides connecting directly to it to try to see the log, I wasn’t sure if there were any other options.

I forgot it is “remotely located” ;(

There are the api and wifi connect triggers. Maybe you could do something like track the number of connects in a global and display it. Or time of last connect. Not the best for debugging though. Just a thought.

Edit: Is HA at the premises? Perhaps you could try using sntp time rather than ha time. I think sntp time only needs internet not HA, so maybe that could help reduce connectivity dependacies a little?

Also, have you considered that it could be a power supply issue? Try using the best quality cables and power supply you can. Like from your phone.

This had crossed my mind and I checked the voltages and everything seems very stable, but maybe I will have to look into it more (power source). I have a 12V power supply that feeds the motor/H-bridge and a LM2596 Voltage regulator taking the 12V input and outputting 5V, which is used to power the rpi pico.

Then that’s probably not the cause of your issues, but the WiFi seems to be.
If you want to test it out, extend the 12v wires & LM2596 to somewhere with (more) stable WiFi. If the esp behaves normally, you know it’s 100% a WiFi issue.

Don’t remove it. That will set it to the default reboot timeout. Set it to 0 to disable it.