Pico Wi-Fi renewal quandary

Folks,

I have a Pico WH running a weather station, using wi-fi to connect to my home network. The Pico hosts a simple HTML page that we access for weather. On start up the Pico establishes a connection via connect.py, then the serve function uses a while true loop to wait for a request from a webpage.

The typical signal strength on the Pico is ~-70 dMm and so dropouts are common.

My quandary is about what’s best practice for renewing a wi-fi connection. Options as a I see them are:

A) Monitor the connection and if lost, renew.
B) Automatically reboot the Pico on a regular basis.

My issue with both of these is that the while true loop is always waiting for a request.

Do I simply add some “If connected else” code in the first line of the while true loop?

The serve function.

def serve(connection): 
    print("serve function running")
    while True:
        client = connection.accept()[0] # continuously waits for incoming client connection.
        request = client.recv(1024)
  
        # Define the values.
        bme280_sensor()
        Ctime = current_time()
        temp = tempC
        rh = humRH
        pres = preshPa
        dp = dewpoint()
        Pmsg = pressure_msg()
        bvolts = batt_volt()
        bperc = batt_perc()
        cpower = batt_p_status()
        bcharge = batt_c_status()
        sigs = signal_strength()
        HIc = heat_index()
        uvi = uv_index()
        uvi_risk = risk
        # Send to the page.
        html=webpage(tempC, preshPa, humRH, dp, Ctime, Pmsg, bvolts, bperc, cpower, bcharge, sigs, HIc, uvi, uvi_risk) # Gets the data from the webpage function.
        client.send(html) # sends the generated HTML back to the client.
        client.close() # closes the client connection.
                
        # Cycle the Pico LED each refresh. 
        Picoled.value(1) 
        sleep_ms(500)
        Picoled.value(0)
        sleep_ms(2000)
  

The connect program.

import network
from secrets import secrets.
import time

def do_connect(ssid=secrets['ssid'],psk=secrets['password']): # Gets the info from the secrets.py 
    wlan = network.WLAN(network.STA_IF) # Initialize the Wi-Fi interface.
    wlan.active(True) # Activate the interface.
    wlan.connect(ssid, psk) 
    print(wlan.isconnected()) # Check the connection status.
    # Wait for connect or fail
    wait = 10
    while wait > 0:
        if wlan.status() < 0 or wlan.status() >= 3:
            break
        wait -= 1
        print('waiting for connection...')
        time.sleep(1)

    # Handle connection error
    if wlan.status() != 3:
        raise RuntimeError('wifi connection failed')
    # raise is an exception event that stops the program.
    # RuntimeError is a built-in exception that is raised when an error is detected that doesn't fall into any of the other specific...
    # categories of exceptions. It generally indicates that something went wrong during the execution of the program,
    # but it doesn't provide details about what exactly failed.

    else:
        print('connected')
        ip=wlan.ifconfig()[0]
        print('network config: ', ip)
        # Get the current SSID
        ssid_name = wlan.config('essid')
        print('Connected to:', ssid_name)
        # Get the RSSI (signal strength) of the connected network
        rssi = wlan.status('rssi')
        print(f"Signal strength (RSSI): {rssi} dBm")
        return ip # Exit and return the ip address.

# Get the RSSI (signal strength) of the connected network for display on the w/station.
def signal_strength():
    wlan = network.WLAN(network.STA_IF) # Initialize the Wi-Fi interface.
    rssi = wlan.status('rssi')
    return rssi

# Get the IP address of the connected network for display on the w/station.
def ip_add():
    wlan = network.WLAN(network.STA_IF) # Initialize the Wi-Fi interface.
    ip=wlan.ifconfig()[0]
    return ip

1 Like

Hey @Mark285907,

From the look of your code you could probably address this by changing the while statement directly. wlan.isconnected() Shoud output True when connection is active so you could just change your while True: statement to while wlan.isconnected():. This would be the simplest option but won’t attempt to reconnect after the signal is lost.

I would try a format similar to this

while True:
    if wlan.isconnected():
        the rest of your function code
    else:
        do_connect()
1 Like

Based on the comment in the code

 while True:
        client = connection.accept()[0] # continuously waits for incoming client connection.
     

Seems to imply to me that once that call is made it will set there until something connections. What I dont know is if that will exit if wifi is lost ?

Can you add some form of timeout to that “wait for connection” ?

1 Like

Hi Mark

I assume you mean -70 dbm (NOT dMm, don’t have any idea what that is).
That equates to about 70µV if a 50Ω system and 87µV if 75Ω.
I know plenty of receivers that would be over the moon with that sort of signal.
Don’t know what the specs are on a Pico RF side of things but if you have multiple drop outs it must be pretty “deaf” as the saying goes.
Cheers Bob

1 Like

Mr. Robert, while I have only tinkered with the Pico, most of these have PCB antenna that are kinda poor. So while they may hear the AP nice and loud, what the send may not.

Either way I think being able to detect when wifi is not up and attempt a reconnect should happen; e.g. auto recover if the AP reboots.

1 Like

Hi Michael.
Yes if just depending on the antenna on a board there are probably several hundred outside things affecting overall performance so you will have to take what you get sort of thing.
Cheers Bob

1 Like

Thanks Michael.

Yes, it just sits there waiting for a request. Once got, it refreshes the html data, loops back to the top and waits again.

A time out is no issue but its the “check for connection/reconnect” bit prior to the “wait for request” bit I need to implement.

The code needs to “check for connection/reconnect” every loop and because a wait can be days, also do a “check for connection/reconnect” at some frequency.

I haven’t looked for this yet but I’m wondering if there’s a time based, internal interrupt I can use.

Thanks Sam.

I’ve added the wlan.isconnected() code but thinking it through, doing this in the serve/while loop is sort of redundant as the while loop loops with every request. The pico would have to lose the connection during one cycle of the loop for this to be or benefit.

I still need to implement some sort of time based check and renewal.

maybe something like
src: https://forums.raspberrypi.com/viewtopic.php?t=312815

from machine import Pin, Timer

led = Pin(25, Pin.OUT)

def tick(timer):
  led.toggle()

Timer().init(freq=2, mode=Timer.PERIODIC, callback=tick)

Mark, since your code sits in a while loop, it may just stall for some reason and not respond, making you think it’s lost connection. I’d be more inclined to run an ‘async’ server as per this: An asynchronous webserver written in MicroPython to turn an LED on/off on a Raspberry Pi Pico W · GitHub.
Dave

1 Like

The problem with this approach is that the weather station needs a continuous reliable WiFi connection. It has no knowledge that clients are having difficulty accessing it so can do nothing about it. An alternate approach is to have the weather station send the data to a well connected reliable server. The advantage is that the Pico W can trap errors. If there is a transmit failure the Pico W can try reconnecting the WiFi. If that also fails the Pico W can try re-booting. Another advantage is that the Pico W can turn on the WiFi only when needed to send the data. This saves power if the weather station is solar powered (mine is).

1 Like

Thanks Michael.
I’ve implemented a virtual timer interrupt that tests the Wi-Fi connection and sends the wifi status to the local OLED every 5 minutes. Initial testing seems to indicate the periodic period is random and certainly not 300,000 ms!

The interrupt seems not to cause any drama with the while true loop. I’ll test for a few days and if all good, I’ll modify some more to call do_connect.py if the wifi is lost.

Cheers.

def timer_test(timer): # Calls the wifi_sta function in do_connect to confirm wifi status.
    if wifi_sta() == True:
        wsta = "Wi-Fi is ON"
    else:
        wsta = "Wi-Fi is OFF"
    oled(wsta) # Send to the OLED.
                   
# Set up a timer interrupt to run the timer_test function.
timer = Timer(-1)  
timer.init(period=3000000, mode=Timer.PERIODIC, callback=timer_test)

Glad to here the is some progress.
One rule of thumb when dealing with interrupts its to keep the code as small as you can; so it exists as fast as it can.
Depending on the underlying system, often interrupts will stop something else working (pause it) save it state, then run the IRQ code, then un pause the original code point. So if something then comes in WHILE its processing the IRQ it “may” miss it; so getting in and out as fast as you can is ideal.

Also keep an eye out for any watch dog timers that may need to be cleared. It may not be an issue, but if the code runs for too long and a watchdog time goes off and resets the device, then on the plus side its reset, on the down side, it may not need to, so more lost time…
That wording is more vague then I like, but Im not sure how a lot of devices I dont use will deal with it.

So just see how your testing goes, turn off your AP and reboot it and see if it auto reconnects.

1 Like

So I’ve been running the Pico weather station all day with the code checking the Wi-Fi every 5 minutes. This it does although every now and again something is getting confused. Maybe the print command is iffy (?) where I see 3 or more identical time checks, although the quantity of checks seems correct.

The line main.py serve says Wi-Fi connected is True means the web page was refreshed, a html request received (2 of per, being the html request and favicon request) causing the serve function to loop.

MPY: soft reboot
do_connect.py says do_connect is called
do_connect.py says wifi is True
do_connect.py says connected
do_connect.py says network config:  192.168.0.26
do_connect.py says Connected to: OPTUS_B4BA10_2.4
do_connect.py says Signal strength (RSSI): -71 dBm
do_connect.py says MAC Address: 2c:cf:67:98:40:81
main.py says ------------------------------------------------------------
main.py says Make sure LiPoly battery is plugged into the board!
Addresses of online modules are: [11, 60, 119]
LC709203F_CR.py says inizializing LC709203F...
LC709203F_CR.py says LC709203F sucessfully inizialized!
do_connect.py says do_connect is called
do_connect.py says wifi is True
do_connect.py says connected
do_connect.py says network config:  192.168.0.26
do_connect.py says Connected to: OPTUS_B4BA10_2.4
do_connect.py says Signal strength (RSSI): -72 dBm
do_connect.py says MAC Address: 2c:cf:67:98:40:81
main.py says Online and waiting
main.py says serve is called from try:
main.py serve says Wi-Fi connected is True
main.py serve says Wi-Fi connected is True
main.py serve says Wi-Fi connected is True
main.py says Wi-Fi is ON at 5.2
main.py says Wi-Fi is ON at 10.2
main.py says Wi-Fi is ON at 15.2
main.py says Wi-Fi is ON at 20.2
main.py says Wi-Fi is ON at 25.2
main.py says Wi-Fi is ON at 30.2
main.py says Wi-Fi is ON at 35.2
main.py says Wi-Fi is ON at 40.2
main.py says Wi-Fi is ON at 45.2
main.py says Wi-Fi is ON at 50.2
main.py says Wi-Fi is ON at 55.2
main.py says Wi-Fi is ON at 60.2
main.py says Wi-Fi is ON at 65.2
main.py says Wi-Fi is ON at 70.2
main.py says Wi-Fi is ON at 75.2
main.py says Wi-Fi is ON at 80.2
main.py says Wi-Fi is ON at 96.6
main.py says Wi-Fi is ON at 96.6
main.py says Wi-Fi is ON at 96.6
main.py says Wi-Fi is ON at 100.2
main.py says Wi-Fi is ON at 105.2
main.py says Wi-Fi is ON at 110.2
main.py says Wi-Fi is ON at 115.2
main.py serve says Wi-Fi connected is True
main.py serve says Wi-Fi connected is True
main.py says Wi-Fi is ON at 120.2
main.py says Wi-Fi is ON at 125.2
main.py says Wi-Fi is ON at 130.2
main.py says Wi-Fi is ON at 135.2
main.py says Wi-Fi is ON at 140.2
main.py says Wi-Fi is ON at 145.2
main.py says Wi-Fi is ON at 150.2
main.py says Wi-Fi is ON at 155.2
main.py says Wi-Fi is ON at 160.2
main.py says Wi-Fi is ON at 165.2
main.py says Wi-Fi is ON at 170.2
main.py says Wi-Fi is ON at 175.2
main.py says Wi-Fi is ON at 180.2
main.py says Wi-Fi is ON at 185.2
main.py says Wi-Fi is ON at 190.2
main.py says Wi-Fi is ON at 195.2
main.py says Wi-Fi is ON at 200.2
main.py says Wi-Fi is ON at 205.2
main.py says Wi-Fi is ON at 210.2
main.py says Wi-Fi is ON at 322.9
main.py says Wi-Fi is ON at 322.9
main.py says Wi-Fi is ON at 322.9
main.py says Wi-Fi is ON at 323.0
main.py says Wi-Fi is ON at 323.0
main.py says Wi-Fi is ON at 323.0
main.py says Wi-Fi is ON at 323.0
main.py says Wi-Fi is ON at 323.0
main.py serve says Wi-Fi connected is True
main.py says Wi-Fi is ON at 325.2
main.py serve says Wi-Fi connected is True
main.py serve says Wi-Fi connected is True
main.py says Wi-Fi is ON at 330.2
main.py says Wi-Fi is ON at 335.2
main.py says Wi-Fi is ON at 340.2
main.py says Wi-Fi is ON at 345.2
main.py says Wi-Fi is ON at 350.2
main.py says Wi-Fi is ON at 355.2
main.py says Wi-Fi is ON at 360.2
main.py says Wi-Fi is ON at 365.2

Hey @Mark285907,

Looks to me like this could be the result of another part of your code being considered a higher-priority task which is forcing this function to wait until the previous process has been completed before it runs this function. The repetition is likely a build-up of print statements that are all run at once once the processor has time to prioritise that task.

What does your complete code look like now? If this is a concern it may be worthwhile identifying what part of your code is taking a bit to run and adding in a time-out to that section.

1 Like