lora — LoRa modem driver#

The lora module provides a driver for the Murata CMWX1ZZABZ LoRa modem on the Arduino Portenta Vision Shield. It exposes a high-level Lora class that wraps the AT command set used by the modem firmware (including the Arduino MKRWAN ARD-078 firmware) so an application can join a LoRaWAN network and send/receive packets.

Example:

import lora

modem = lora.Lora(band=lora.BAND_EU868, debug=True)
print("Device EUI:", modem.get_device_eui())
if modem.join_OTAA("0000000000000000", "00000000000000000000000000000000"):
    modem.send_data(b"hello", confirmed=True)

Constants#

Activation modes#

lora.MODE_ABP: int#

Activation By Personalization mode.

lora.MODE_OTAA: int#

Over-The-Air Activation mode.

RF output modes#

lora.RF_MODE_RFO: int#

Use the modem’s RFO (low-power) RF output.

lora.RF_MODE_PABOOST: int#

Use the modem’s PA_BOOST (high-power) RF output.

Bands#

lora.BAND_AS923: int#

AS923 (Asia 923 MHz) regional band.

lora.BAND_AU915: int#

AU915 (Australia 915 MHz) regional band.

lora.BAND_EU868: int#

EU868 (Europe 868 MHz) regional band.

lora.BAND_KR920: int#

KR920 (Korea 920 MHz) regional band.

lora.BAND_IN865: int#

IN865 (India 865 MHz) regional band.

lora.BAND_US915: int#

US915 (United States 915 MHz) regional band.

lora.BAND_US915_HYBRID: int#

US915 hybrid (sub-band) regional plan.

Device classes#

lora.CLASS_A: str#

LoRaWAN end-device Class A.

lora.CLASS_B: str#

LoRaWAN end-device Class B.

lora.CLASS_C: str#

LoRaWAN end-device Class C.

Exceptions#

exception lora.LoraError#

Base exception raised for any error returned by the modem or the driver.

exception lora.LoraErrorTimeout#

Raised when the modem does not respond within the configured timeout (the receive buffer is empty).

exception lora.LoraErrorParam#

Raised on a +ERR_PARAM response when an AT command was issued with an invalid parameter.

exception lora.LoraErrorBusy#

Raised on a +ERR_BUSY response when the modem is busy processing a previous command.

exception lora.LoraErrorOverflow#

Raised on a +ERR_PARAM_OVERFLOW response when a parameter exceeds the maximum allowed length.

exception lora.LoraErrorNoNetwork#

Raised on a +ERR_NO_NETWORK response when the modem has not joined a network.

exception lora.LoraErrorRX#

Raised on a +ERR_RX response when an error occurs while receiving a downlink.

exception lora.LoraErrorUnknown#

Raised on a +ERR_UNKNOWN response or when the modem reports an undocumented error.

Classes#

class lora.Lora(uart: machine.UART | None = None, rst_pin: machine.Pin | None = None, boot_pin: machine.Pin | None = None, band: int = BAND_EU868, poll_ms: int = 300000, debug: bool = False)#

Construct a new modem driver. The constructor initializes (or auto-creates) the UART and reset/boot pins, hardware-resets the module, performs autobaud synchronization, reboots the module, queries its firmware version, and configures the requested regional band.

Parameters:
  • uart – Pre-configured machine.UART instance used to talk to the modem. If None, the driver opens UART(8, 19200) with 8N2 framing (the Portenta Vision Shield default).

  • rst_pinmachine.Pin driving the modem’s reset line. If None, "PC6" is configured as a push-pull output.

  • boot_pinmachine.Pin driving the modem’s boot-select line. If None, "PG7" is configured as a push-pull output pulled low.

  • band – Regional band to configure. One of the BAND_* constants.

  • poll_ms – Interval in milliseconds between automatic empty uplinks triggered by poll() to keep the downlink window open.

  • debug – When True, all UART traffic is printed via print().

LoraErrors: dict#

Mapping from modem error response strings (e.g. "+ERR_BUSY") to the corresponding exception class. Used internally by handle_error().

init_modem() None#

Lazy-initialize self.uart, self.rst_pin and self.boot_pin to their Portenta Vision Shield defaults if they are not already set. Called from the constructor; not normally invoked by user code.

debug_print(data: str) None#

Print data if debug was enabled at construction time, otherwise do nothing.

is_arduino_firmware() bool#

Return True if the modem is running the Arduino MKRWAN ARD-078 firmware (detected from the cached fw_version string).

configure_class(_class: str) None#

Configure the LoRaWAN device class.

Parameters:

_class – One of CLASS_A, CLASS_B, CLASS_C.

configure_band(band: int) bool#

Configure the regional band and, on Arduino firmware with BAND_EU868, enable the ETSI duty-cycle limiter. Returns True on success.

Parameters:

band – One of the BAND_* constants.

set_baudrate(baudrate: int) None#

Change the modem’s UART baud rate (AT+UART).

Parameters:

baudrate – New baud rate, in bits per second.

set_autobaud(timeout: int = 10000) bool#

Send empty AT commands until the modem responds with +OK or until timeout milliseconds elapse. Returns True if synchronization succeeded.

Parameters:

timeout – Maximum time to spend trying, in milliseconds.

get_fw_version() str#

Query the modem device string (AT+DEV?) and firmware version (AT+VER?) and return them as a single space-separated string.

get_device_eui() str#

Return the modem’s 64-bit Device EUI as a hex string.

factory_default() None#

Restore factory defaults via AT+FACNEW.

restart() None#

Re-synchronize the baud rate, reboot the modem, re-read the firmware version, and re-apply the configured regional band. Raises LoraError on failure.

set_rf_power(mode: int, power: int) None#

Configure the modem RF output stage (AT+RFPOWER).

Parameters:
set_port(port: int) None#

Configure the LoRaWAN application port (1..223) used by subsequent send_data() calls.

Parameters:

port – LoRaWAN FPort.

set_public_network(enable: bool) None#

Enable or disable the public-network sync word.

Parameters:

enableTrue for the public LoRaWAN sync word, False for the private one.

sleep(enable: bool) None#

Put the modem into low-power sleep (True) or wake it up (False).

format(hexMode: bool) None#

Select the data format used over the UART for payload bytes.

Parameters:

hexModeTrue for ASCII-hex payload format, False for raw binary.

set_datarate(dr: int) None#

Set the LoRaWAN data rate index (AT+DR).

Parameters:

dr – Region-specific data rate index.

get_datarate() int#

Return the current LoRaWAN data rate index.

set_adr(adr: bool) None#

Enable or disable Adaptive Data Rate.

Parameters:

adrTrue to enable ADR, False to disable it.

get_adr() int#

Return the current ADR setting (1 if enabled, 0 otherwise).

get_devaddr() str#

Return the current 32-bit DevAddr as a hex string.

get_nwk_skey() str#

Return the current Network Session Key as a hex string.

get_appskey() str#

Return the current Application Session Key as a hex string.

get_rx2dr() int#

Return the data rate index used for the RX2 receive window.

set_rx2dr(dr: int) None#

Set the data rate index used for the RX2 receive window.

Parameters:

dr – Region-specific data rate index.

get_ex2freq() int#

Return the frequency, in Hz, used for the RX2 receive window.

set_rx2freq(freq: int) None#

Set the frequency used for the RX2 receive window.

Parameters:

freq – Frequency in Hz.

set_fcu(fcu: int) None#

Set the uplink frame counter (AT+FCU).

Parameters:

fcu – New uplink frame counter value.

get_fcu() int#

Return the current uplink frame counter.

set_fcd(fcd: int) None#

Set the downlink frame counter (AT+FCD).

Parameters:

fcd – New downlink frame counter value.

get_fcd() int#

Return the current downlink frame counter.

change_mode(mode: int) None#

Switch the activation mode.

Parameters:

mode – One of MODE_ABP, MODE_OTAA.

join(timeout_ms: int) bool#

Issue an AT+JOIN and wait for the join-accept event. Returns True if the modem reports +EVENT=1,1 (join succeeded) within timeout_ms.

Parameters:

timeout_ms – Maximum time to wait for both the +ACK and the subsequent join event, in milliseconds.

get_join_status() bool#

Return True if the modem is currently joined to a network.

get_max_size() int#

Return the maximum LoRaWAN payload size, in bytes, for the current data rate. On Arduino firmware this is fixed at 64.

poll() None#

If more than poll_ms milliseconds have passed since the last call, send an empty confirmed uplink to flush pending downlinks. Intended to be called frequently from the application’s main loop.

send_data(buff: bytes, confirmed: bool = True) bool#

Transmit a LoRaWAN uplink. Raises LoraError if buff is larger than get_max_size().

Parameters:
  • buff – Payload bytes to transmit.

  • confirmed – When True, send a confirmed uplink (+CTX) and wait for a network-server +ACK. When False, send an unconfirmed uplink (+UTX).

Returns:

True if the modem accepted the packet (and, for confirmed uplinks, the network acknowledged it), False otherwise.

receive_data(timeout: int = 1000) dict | None#

Wait for a downlink. Returns None if no +RECV event was received within timeout milliseconds, otherwise a dict {"port": str, "data": str} containing the FPort and the payload bytes.

Parameters:

timeout – Maximum time to wait, in milliseconds.

receive(delimiter: str | list | None = None, max_bytes: int | None = None, timeout: int = 1000) str#

Low-level UART read. Reads characters from the modem until a delimiter is matched, max_bytes characters have been read, or timeout milliseconds elapse, then returns the accumulated string with any trailing \r stripped.

Parameters:
  • delimiter – Either a single character to match at the end of the buffer, a multi-character string to match against the full trimmed buffer, or a list of such strings.

  • max_bytes – If set, return as soon as exactly this many bytes have been read.

  • timeout – Overall read timeout, in milliseconds.

available() int#

Return the number of bytes currently available in the modem’s UART receive buffer.

join_OTAA(appEui: str, appKey: str, devEui: str = None, timeout: int = 60000) bool#

Switch the modem to OTAA mode, program the supplied keys and EUIs, then attempt to join the network. Returns True if the join succeeded.

Parameters:
  • appEui – 64-bit Application/Join EUI as a hex string.

  • appKey – 128-bit Application Key as a hex string.

  • devEui – Optional 64-bit Device EUI as a hex string. If None, the factory-programmed Device EUI is used.

  • timeout – Join timeout, in milliseconds.

join_ABP(nwkId: int, devAddr: str, nwkSKey: str, appSKey: str, timeout: int = 60000) bool#

Switch the modem to ABP mode, program the supplied addresses and keys, then attempt to join. Returns the result of get_join_status().

Parameters:
  • nwkId – Network identifier (currently ignored by the firmware).

  • devAddr – 32-bit Device Address as a hex string.

  • nwkSKey – 128-bit Network Session Key as a hex string.

  • appSKey – 128-bit Application Session Key as a hex string.

  • timeout – Join timeout, in milliseconds.

handle_error(command: str, data: str) None#

Inspect a modem response and raise the matching LoraError subclass if it represents an error. Does nothing for non-error responses.

Parameters:
  • command – The AT command (without the AT prefix) that produced data.

  • data – The response string returned by the modem.

send_command(cmd: str, *args, delimiter: str = '\\r', data: bytes = None, timeout: int = 1000, raise_error: bool = True) str#

Build an AT command from cmd and args, optionally append a raw data payload, transmit it, and return the modem’s response. For query commands ending in ?, only the value portion (the substring after =) is returned.

Parameters:
  • cmd – AT command suffix (e.g. "+JOIN", "+DR="); the "AT" prefix and trailing \r are added automatically.

  • args – Additional arguments concatenated to cmd after string conversion.

  • delimiter – Delimiter forwarded to receive().

  • data – Optional raw bytes written immediately after the AT command (used for binary uplink payloads).

  • timeout – Response timeout, in milliseconds.

  • raise_error – When True, error responses are converted into LoraError exceptions; when False, the raw response is returned to the caller.