Pico Webserver + Anvil Uplink

Hi,

I am using a pico webserver for easy configuration of wifi credentials. When the pico has connected to the web i want the pico to connect to anvil via uplink. I cannot get this to work as the script seems to stop when the webserver run. Do you have any ideas for improvements ?

See code from Pico below

Code Sample:

from phew import access_point, connect_to_wifi, is_connected_to_wifi, dns, server
from phew.template import render_template
import json
import machine
import os
import utime
import _thread
import anvil.pico
from machine import Pin, reset
from utime import sleep
import uasyncio
import network

AP_NAME = "pi pico"
AP_DOMAIN = "pipico.net"
AP_TEMPLATE_PATH = "ap_templates"
APP_TEMPLATE_PATH = "app_templates"
WIFI_FILE = "wifi.json"
WIFI_MAX_ATTEMPTS = 3


def machine_reset():
    utime.sleep(1)
    print("Resetting...")
    machine.reset()

def setup_mode():
    print("Entering setup mode...")

    def ap_index(request):
        if request.headers.get("host").lower() != AP_DOMAIN.lower():
            return render_template(f"{AP_TEMPLATE_PATH}/redirect.html", domain=AP_DOMAIN.lower())

        return render_template(f"{AP_TEMPLATE_PATH}/index.html")

    def ap_configure(request):
        print("Saving wifi credentials...")

        # Get the values from the form input fields
        ssid = request.form["ssid"]
        password = request.form["password"]
        email = request.form["email"]
        id = request.form["id"]

        # Create a dictionary to store the values
        wifi_data = {
            "ssid": ssid,
            "password": password,
            "email": email,
            "id": id
        }

        with open(WIFI_FILE, "w") as f:
            json.dump(wifi_data, f)
            f.close()

        # Reboot from a new thread after responding to the user.
        _thread.start_new_thread(machine_reset, ())
        return render_template(f"{AP_TEMPLATE_PATH}/configured.html", ssid=ssid)

    def ap_catch_all(request):
        if request.headers.get("host") != AP_DOMAIN:
            return render_template(f"{AP_TEMPLATE_PATH}/redirect.html", domain=AP_DOMAIN)

        return "Not found.", 404

    server.add_route("/", handler=ap_index, methods=["GET"])
    server.add_route("/configure", handler=ap_configure, methods=["POST"])
    server.set_callback(ap_catch_all)

    ap = access_point(AP_NAME)
    ip = ap.ifconfig()[0]
    dns.run_catchall(ip)

def application_mode():
    print("Entering application mode.")
    onboard_led = machine.Pin("LED", machine.Pin.OUT)

    def app_index(request):
        return render_template(f"{APP_TEMPLATE_PATH}/index.html")

    def app_toggle_led(request):
        onboard_led.toggle()
        return "OK"

    def app_get_temperature(request):
        # Not particularly reliable but uses built-in hardware.
        # Demos how to incorporate sensor data into this application.
        # The front end polls this route and displays the output.
        # Replace code here with something else for a 'real' sensor.
        # Algorithm used here is from:
        # https://www.coderdojotc.org/micropython/advanced-labs/03-internal-temperature/
        sensor_temp = machine.ADC(4)
        reading = sensor_temp.read_u16() * (3.3 / (65535))
        temperature = 27 - (reading - 0.706) / 0.001721
        return f"{round(temperature, 1)}"

    def app_reset(request):
        # Deleting the WIFI configuration file will cause the device to reboot as
        # the access point and request new configuration.
        os.remove(WIFI_FILE)
        # Reboot from a new thread after responding to the user.
        _thread.start_new_thread(machine_reset, ())
        return render_template(f"{APP_TEMPLATE_PATH}/reset.html", access_point_ssid=AP_NAME)

    def app_catch_all(request):
        return "Not found.", 404

    server.add_route("/", handler=app_index, methods=["GET"])
    server.add_route("/toggle", handler=app_toggle_led, methods=["GET"])
    server.add_route("/temperature", handler=app_get_temperature, methods=["GET"])
    server.add_route("/reset", handler=app_reset, methods=["GET"])
    # Add other routes for your application...
    server.set_callback(app_catch_all)

# Figure out which mode to start up in...
try:
    os.stat(WIFI_FILE)

    # File was found, attempt to connect to wifi...
    with open(WIFI_FILE) as f:
        wifi_current_attempt = 1
        wifi_credentials = json.load(f)

        while wifi_current_attempt < WIFI_MAX_ATTEMPTS:
            ip_address = connect_to_wifi(wifi_credentials["ssid"], wifi_credentials["password"])

            if is_connected_to_wifi():
                print(f"Connected to wifi, IP address {ip_address}")
                break
            else:
                wifi_current_attempt += 1

        if is_connected_to_wifi():
            application_mode()
        else:
            # Bad configuration, delete the credentials file, reboot
            # into setup mode to get new credentials from the user.
            print("Bad wifi connection!")
            print(wifi_credentials)
            os.remove(WIFI_FILE)
            machine_reset()

except Exception:
    # Either no wifi configuration file found, or something went wrong,
    # so go into setup mode.
    setup_mode()

print("before webserver")

def webserver():
    # Start the web server...
    server.run()

webserver()
#webserver()
print("afterwebserver")
    
# This is an example Anvil Uplink script for the Pico W.
# See https://anvil.works/pico for more information
UPLINK_KEY = "******"

#generate unique device ID from anvil app and set it here
DEVICE_ID  = "278960A602"

# We use the LED to indicate server calls and responses.
led = Pin(26, Pin.OUT)  # Create LED object from Pin 26, Set Pin 26 to output

LED_STATUS = ""

@anvil.pico.callable_async("get_led_status_"+DEVICE_ID)
async def get_led_status():
    return LED_STATUS


@anvil.pico.callable_async("useremail_"+DEVICE_ID)
async def useremail():
    email = "******"
    print(email)
    return email

@anvil.pico.callable_async("systemon_"+DEVICE_ID)
async def systemon():
    global LED_STATUS
    led.value(1)  # Set led turn on
    LED_STATUS = "ON"
    print("LED is ON")
    return "LED is ON"

@anvil.pico.callable_async("systemoff_"+DEVICE_ID)
async def systemoff():
    global LED_STATUS
    led.value(0)  # Set led turn off
    LED_STATUS = "OFF"
    print("LED is OFF")
    return "LED is OFF"

sleep(5)
# Connect the Anvil Uplink. In MicroPython, this call will block forever.
anvil.pico.connect(UPLINK_KEY)
#anvil.pico.connect(UPLINK_KEY,on_first_connect=webserver())