ModemAsync
Module: modem_uasync
Source: modules/modem_uasync.py
Depends on: modem, uasyncio
ModemAsync extends Modem and replaces its blocking I/O layer with a uasyncio-based one. Every business method and every I/O method is a coroutine — it must be awaited to execute. Use this class whenever the modem must coexist with other concurrent tasks (timers, sensors, display updates, etc.).
ModemAsync keeps a separate singleton from Modem. Do not instantiate both in the same application — they would both try to own the nrf9151 hardware.
from modem_uasync import ModemAsyncimport uasyncio
async def main(): m = ModemAsync() await m.start() # mandatory: launches the background reader task ...
uasyncio.run(main())uasyncio constraints
Section titled “uasyncio constraints”These constraints come from MicroPython’s uasyncio event loop model and apply throughout the entire application, not just to modem calls.
1. Every coroutine must be awaited.
Without await, the call returns a coroutine object — no AT command is sent, no value is returned.
# correctresult = await m.mqtt_publish("topic", "payload")
# wrong — AT command is never sentresult = m.mqtt_publish("topic", "payload")2. await m.start() must be called before anything else.
It creates the _reader() background task that reads incoming bytes continuously. Without it, every wait_response() will time out.
3. Never use blocking sleep inside a coroutine.
time.sleep() and time.sleep_ms() freeze the entire event loop, preventing _reader() from running and causing spurious timeouts.
# correctawait uasyncio.sleep_ms(1000)
# wrong — blocks the event loopimport timetime.sleep_ms(1000)4. Long-running loops must be separate tasks.
Use uasyncio.create_task(coro()) to run a coroutine concurrently. Calling await coro() inline blocks main() until that coroutine returns.
# correct — runs concurrently with the rest of main()uasyncio.create_task(on_message(m))
# wrong — main() never advances past this lineawait on_message(m)5. recv() suspends the calling coroutine until a message arrives.
Always run it inside a dedicated task so it does not block the rest of the program.
Startup
Section titled “Startup”ModemAsync()
Section titled “ModemAsync()”Returns the ModemAsync singleton. On the first call, delegates hardware initialisation to Modem.__init__() (powers on the nRF9151, flushes the RX buffer), then sets up the async state (_cmd_event, _msg_queue, _msg_event). Subsequent calls return the same instance without re-initialising.
await m.start()Launch the background _reader() task. Must be called once before any other method.
Async low-level I/O
Section titled “Async low-level I/O”send() and read() are inherited from Modem unchanged — they write/read raw bytes synchronously and do not need await. Only wait_response() and send_cmd() are replaced by async versions.
wait_response
Section titled “wait_response”await m.wait_response(expected="OK", timeout_ms=1000)Wait for a command response without blocking the event loop. The _reader() task accumulates incoming bytes; this method waits on an internal uasyncio.Event instead of polling.
| Parameter | Type | Default | Description |
|---|---|---|---|
expected | str | "OK" | String to wait for. |
timeout_ms | int | 1000 | Timeout in milliseconds. |
Returns the accumulated response str, "ERROR", or a timeout message.
send_cmd
Section titled “send_cmd”await m.send_cmd(command, expected="OK", timeout_ms=1000, is_bool=True)Send an AT command and await the response.
| Parameter | Type | Default | Description |
|---|---|---|---|
command | str | — | AT command string — \r\n is appended automatically. |
expected | str | "OK" | Substring to look for in the response. |
timeout_ms | int | 1000 | Timeout in milliseconds. |
is_bool | bool | True | True → returns bool; False → returns raw response str. |
MQTT receive
Section titled “MQTT receive”topic, payload = await m.recv()Suspend the calling coroutine until an incoming MQTT message arrives on any subscribed topic.
Returns a (topic, payload) tuple of strings. This method is exclusive to ModemAsync — the sync Modem class has no equivalent.
Run recv() inside a dedicated task to avoid blocking the rest of the program:
async def on_message(m): while True: topic, payload = await m.recv() print(topic, payload)
uasyncio.create_task(on_message(m))Modem control
Section titled “Modem control”await m.CFUN(mode)Set the radio functionality level. Same parameters and return value as Modem.CFUN().
| Parameter | Type | Description |
|---|---|---|
mode | int | One of: 0, 1, 2, 4, 20, 21, 30, 31, 40, 41, 44. |
Returns True on success, False otherwise. Uses a 5-second timeout internally.
All methods share the same parameters and return values as their sync equivalents in Modem — refer to that page for full parameter tables.
mqtt_cfg
Section titled “mqtt_cfg”await m.mqtt_cfg(client_id, keep_alive=60, clean_session=0)get_mqtt_cfg
Section titled “get_mqtt_cfg”await m.get_mqtt_cfg()mqtt_conn
Section titled “mqtt_conn”await m.mqtt_conn(op, username, password, url, port, sec_tag=None)is_mqtt_conn
Section titled “is_mqtt_conn”await m.is_mqtt_conn()mqtt_publish
Section titled “mqtt_publish”await m.mqtt_publish(topic, msg, qos=0, retain=0)mqtt_subscribe
Section titled “mqtt_subscribe”await m.mqtt_subscribe(topic, qos=0)Certificate management
Section titled “Certificate management”Same parameters and return values as Modem.
write_certificate
Section titled “write_certificate”await m.write_certificate(sec_tag, cert_type, content, psw=None, sha256=None)list_certificate
Section titled “list_certificate”await m.list_certificate(sec_tag, cert_type=None)read_certificate
Section titled “read_certificate”await m.read_certificate(sec_tag, cert_type)delete_certificate
Section titled “delete_certificate”await m.delete_certificate(sec_tag, cert_type)Full example
Section titled “Full example”from modem_uasync import ModemAsyncimport uasyncio, ujsonfrom led import Led
l = Led()
async def on_message(m): while True: topic, payload = await m.recv() # suspends until a message arrives print(topic, payload) if "v1/devices/me/attributes" in topic: s = ujson.loads(payload) led_value = s.get("led") if led_value == 1: l.red() else: l.off()
async def main(): m = ModemAsync() await m.start() # mandatory: launches _reader()
uasyncio.create_task(on_message(m)) # run receiver concurrently
await m.mqtt_cfg("nxpico") await m.CFUN(4) await uasyncio.sleep_ms(500) # never use time.sleep_ms() here await m.CFUN(1)
while not await m.is_mqtt_conn(): await m.mqtt_conn(1, "user", "user", "broker.example.com", 1883) await uasyncio.sleep_ms(1000)
print("connected") await m.mqtt_subscribe("v1/devices/me/attributes")
while True: await m.mqtt_publish("v1/devices/me/telemetry", "{'temp': 25}") await uasyncio.sleep_ms(1000)
uasyncio.run(main())