Go Beyond the Basics
You can use your Pico W to build complex apps with user authentication, database integration, email and more, by integrating with some of Anvil’s advanced features:
Table of Contents
- Authenticate users: Use Anvil’s built in user authentication to control access to your Pico W code.
- Async code: Use
uasyncio
with Uplink functions. - HTTP endpoints: Serve HTTP requests from your Pico W.
- Run code on connection: Run code after your device connects to your Anvil app, or each time it reconnects.
- Work Around Micropython’s Limitations: Use Anvil’s Server Modules, with their full Python environments, from your Pico W code.
User authentication
Anvil has built in user authentication supporting email/password, Google, Facebook, and even enterprise SAML authentication. You can use this authentication to restrict calls on your Pico W to only permit authorised users.
To do this, add the Users Service to your app, then add require_user=True
to any @anvil.pico.callable
definition. For example:
@anvil.pico.callable(require_user=True)
def toggle_led():
# This call will only run if invoked by logged-in user
led.toggle()
For an example of this, see the Morse code transmitter tutorial.
Require a specific user
You can also require that only a specific user may be logged in to call your function - calls from other users will be rejected. For example:
@anvil.pico.callable(require_user="ian@anvil.works")
def toggle_led():
# This call will only run if invoked by ian@anvil.works.
led.toggle()
Get the logged-in user
You can get the email address of the currently logged-in user by calling await anvil.pico.get_user_email()
. This function returns None
if nobody is logged in.
Async
If you want to use async code (uasyncio
) in your function, then use @anvil.pico.callable_async
instead of @anvil.pico.callable
.
For example:
import uasyncio as a
@anvil.pico.callable_async
async def blink_led():
for i in range(20):
led.toggle()
await a.sleep_ms(50)
HTTP endpoints
If you want to give your Pico W an HTTP API, you can! You’ll need to set up a Server Module with an HTTP endpoint that calls the relevant function on your Pico W.
For example, here’s an HTTP endpoint that calls toggle_led()
from the Pico W:
# In a Server Module:
@anvil.server.http_endpoint("/blink")
def http_blink(**params):
anvil.server.call("toggle_led")
And here is how the function is defined on your Pico W code:
# On your Pico W:
@anvil.pico.callable
def toggle_led():
led.toggle()
Run code at startup
If you want to run code as soon as the Pico W connects to Anvil, then pass a task as an argument to anvil.pico.connect()
. For example:
# Define a task to run after the Anvil Uplink has connected
async def flash_led_for(seconds):
for i in range(seconds):
led.toggle()
await a.sleep_ms(100)
led.toggle()
await a.sleep(1)
# Connect the Anvil Uplink. In Micropython, this call will block forever.
anvil.pico.connect(UPLINK_KEY, flash_led_for(30))
Run code on every connection
If you want to run code each time the Pico reconnects to Anvil – rather than just once – specify on_every_connect=
instead. This is useful if, for example, you’re using the Client Uplink and want to authenticate when your device connects.
anvil.users
module, as it isn’t available in Micropython. This is further explained in the Work Around Micropython’s Limitations section below.Example: Authenticating a device at login
USERNAME="someone@example.com"
PASSWORD="1234"
async def do_login():
await anvil.pico.call("authenticate_device", USERNAME, PASSWORD)
anvil.pico.connect(UPLINK_KEY, on_every_connect=do_login())
And then in a Server Module in your Anvil app:
import anvil.users
@anvil.server.callable
def authenticate_device(username, password):
# Called each time a device connects
anvil.users.login_with_email(username, password)
Work Around Micropython’s Limitations
Micropython on the Pico W is quite a limited environment, with few libraries and only a few kilobytes of RAM. Consequently, your Pico W code can’t directly access Anvil’s Data Tables, the Email Service, use Portable Classes, or define HTTP endpoints.
However, Anvil’s Server Modules can do all of those things, because they run in a proper Python environment! So when you want to do something that doesn’t fit on the Pico, define a Server Function to do it, then use anvil.pico.call()
to invoke it from your Pico.
For example, you could write a Server Function that adds a row to a Data Table called “Visitors”, with “name” (string) and “when” (date/time) columns:
from datetime import datetime
@anvil.server.callable
def record_visitor(name):
app_tables.visitors.add_row(name=name, when=datetime.now())
And you can call it from your Pico W:
async def register_visitor(name):
await anvil.pico.call("record_visitor", name)