MCCI Arduino LoRaWAN Library

repository·master·Indexed 43 days ago

https://github.com/mcci-catena/arduino-lorawan

A high-level interface and structured wrapper for the arduino-lmic library, designed for LoRaWAN communication on Arduino-compatible hardware. It supports various networks including The Things Network, Actility, Helium, ChirpStack, and others. The library provides frameworks for low-power programming, atomic non-volatile storage management, and decoupling application logic from hardware details. It targets 32-bit processors (ARM, XTENSA) and SX1276-based LoRa radios.

Tokens
8.4K
Snippets
28
Records
43
Agent score
55%

What's inside MCCI Arduino LoRaWAN

  1. Overview of MCCI Arduino LoRaWAN Library

    master

    The arduino-lorawan library is a structured wrapper for the arduino-lmic library, designed to facilitate sending sensor data over LoRaWAN networks like The Things Network.

    It aims to:

    1. Separate network maintenance code from application logic.
    2. Decouple sensor application logic from specific device hardware details.
    3. Provide a framework for low-power programming.
    4. Offer a framework for managing non-volatile storage (like FRAM) atomically.

    Target Hardware Requirements:

    • 32-bit processor (e.g., ARM, XTENSA).
    • SX1276-based LoRa radio.
    • Arduino run-time environment.

    Reference Targets:

    • SAMD21G: Adafruit Feather M0 LoRa (and MCCI Catena 4450, 4460, 4470).
    • STM32L0: Murata CMWX1ZZABZ-078 (used in MCCI Catena 4610, 4612, 4801, 4617, 4618, 4630).
  2. Required libraries for arduino-lorawan

    master

    To use this library, you must ensure the following dependencies are installed in your Arduino environment:

    LibraryRequired VersionRecommended VersionNotes
    arduino-lmic3.99.0-24.0.0Earlier versions may cause compilation errors via asserts.
    Catena-mcciadk0.1.10.2.2Provides miscellaneous definitions.
  3. Implement the Arduino_LoRaWAN class for custom hardware

    master

    The library is designed to be used by subclassing one of its provider classes (e.g., Arduino_LoRaWAN_ttn) and overriding virtual methods. This allows you to provide implementations for provisioning and session persistence.

    To use the library in a standalone pattern (e.g., for The Things Network V3):

    1. Define a class that inherits from Arduino_LoRaWAN_ttn.
    2. Override GetOtaaProvisioningInfo to provide DevEUI, AppEUI, and AppKey.
    3. (Optional) Override NetSaveSessionInfo, NetSaveSessionState, and NetGetSessionState to implement persistent storage for session data.
    4. Instantiate your class, call .begin() in setup(), and call .loop() in loop().
    #include <Arduino_LoRaWAN_ttn.h>
    
    class cMyLoRaWAN : public Arduino_LoRaWAN_ttn {
    public:
        cMyLoRaWAN() {};
    
    protected:
        virtual bool GetOtaaProvisioningInfo(Arduino_LoRaWAN::OtaaProvisioningInfo*) override;
        virtual void NetSaveSessionInfo(const SessionInfo &Info, const uint8_t *pExtraInfo, size_t nExtraInfo) override;
        virtual void NetSaveSessionState(const SessionState &State) override;
        virtual bool NetGetSessionState(SessionState &State) override;
    };
    
    cMyLoRaWAN myLoRaWAN {};
    
    void setup() {
        myLoRaWAN.begin();
    }
    
    void loop() {
        myLoRaWAN.loop();
    }
    
    // Implementation example
    bool cMyLoRaWAN::GetOtaaProvisioningInfo(OtaaProvisioningInfo *pInfo) {
        // Fill pInfo with DevEUI, AppEUI, AppKey and return true
        return false;
    }
  4. Supply a custom pin-map to begin()

    master

    If your board is not natively supported by the library, you can provide a custom lmic_pinmap structure to the begin() method. This is useful for testing or for boards not yet integrated into the library's default configurations.

    const cMyLoRaWAN::lmic_pinmap myPinMap = {
         .nss = 8,
         .rxtx = cMyLoRaWAN::lmic_pinmap::LMIC_UNUSED_PIN,
         .rst = 4,
         .dio = { 3, 6, cMyLoRaWAN::lmic_pinmap::LMIC_UNUSED_PIN },
         .rxtx_rx_active = 0,
         .rssi_cal = 0,
         .spi_freq = 8000000,
    };
    
    void setup() {
        myLoRaWAN.begin(myPinMap);
    }
  5. Use the Arduino_LoRaWAN_Generic class

    master

    The Arduino_LoRaWAN_Generic class is a convenience wrapper that automatically selects the correct regional implementation (e.g., Arduino_LoRaWAN_Generic_eu868, Arduino_LoRaWAN_Generic_us915, etc.) based on the Arduino_LoRaWAN_REGION_TAG defined during compilation.

    This class inherits from Arduino_LoRaWAN_Generic_base, which provides the core NetworkID and network name functionality. The specific regional logic for joining the network (NetJoin) and initializing the region (NetBeginRegionInit) is handled by the underlying regional subclass.

    // The specific class instantiated depends on your CFG_xxx build flag
    Arduino_LoRaWAN_Generic network;
  6. Configure LoRaWAN provisioning (ABP vs OTAA)

    master

    The library supports two provisioning styles via the ProvisioningStyle enum:

    • kOTAA: Over-the-Air Activation (standard).
    • kABP: Activation by Personalization (static keys).
    • kNone: No provisioning.

    When implementing a custom network class derived from Arduino_LoRaWAN, you should override the following protected methods to handle persistent storage of credentials:

    • GetProvisioningStyle(): Returns the current style.
    • GetAbpProvisioningInfo(AbpProvisioningInfo *pInfo): Fills pInfo with keys/addresses if using ABP.
    • GetOtaaProvisioningInfo(OtaaProvisioningInfo *pInfo): Fills pInfo with AppKey, DevEUI, and AppEUI if using OTAA.
    • NetSaveSessionInfo(...): Called after a successful join to save session keys.
    • GetSavedSessionInfo(...): Used to retrieve previously saved session keys to avoid re-joining.
  7. Understand SessionState and SessionInfo

    master

    The library uses two main structures to manage the LoRaWAN session state:

    1. SessionInfo: Contains the core identity and keys (NetID, DevAddr, NwkSKey, AppSKey). This is typically what you save after a successful OTAA join to allow the device to resume a session without re-joining.
    2. SessionState: A more comprehensive, opaque blob that includes operational parameters like LinkDR (Data Rate), FCntUp/FCntDown (Frame Counters), Rx2Frequency, and Country code.

    Best Practice: Clients should treat SessionState as an opaque blob and store it in non-volatile memory (NVM) after each EV_TXCOMPLETE event to ensure the device can recover from power cycles without losing synchronization with the network.

  8. Configure LoRaWAN network selection at compile-time

    master

    You must define exactly one of the following symbols to select your target network. The value of the symbol must be 1.

    SymbolNetworkSupported Regions
    ARDUINO_LMIC_CFG_NETWORK_TTNThe Things NetworkEU868, US915 (subband 1), AU915 (subband 1), AS923, AS923 Japan, KR920, IN866
    ARDUINO_LMIC_CFG_NETWORK_ACTILITYActilityEU868, US915, AU915, AS923, AS923 Japan, KR920, IN866
    ARDUINO_LMIC_CFG_NETWORK_HELIUMHeliumUS915 (subband 1)
    ARDUINO_LMIC_CFG_NETWORK_MACHINEQmachineQUS915
    ARDUINO_LMIC_CFG_NETWORK_SENETSenetUS915
    ARDUINO_LMIC_CFG_NETWORK_SENRASenraIN866
    ARDUINO_LMIC_CFG_NETWORK_SWISSCOMSwisscomEU868
    ARDUINO_LMIC_CFG_NETWORK_CHIRPSTACKChirpStack.ioEU868, US915, AU915, AS923, AS923 Japan, KR920, IN866
    ARDUINO_LMIC_CFG_NETWORK_GENERICGenericEU868, US915, AU915, AS923, AS923 Japan, KR920, IN866
  9. Configure Join Subband Selection via ARDUINO_LMIC_CFG_SUBBAND

    master

    In regions like US915, AU915, and CN470, networks often use a specific subset of 8 channels called a subband. You can configure this using the ARDUINO_LMIC_CFG_SUBBAND symbol.

    Configuration Options:

    • Set to -1 (Recommended): No subband choice is made; all channels are used for joining. The network will typically provide the correct channel mask via the JoinAccept message or subsequent uplinks.
    • Set to a non-negative integer: The value is multiplied by 8 to determine the base channel. Channels [SUBBAND * 8] through [SUBBAND * 8 + 7] are used for joining.

    Valid Values:

    • US915 / AU915: Any value between 0 and 7 inclusive.
    • CN470: Any value between 0 and 11 inclusive.

    Note: Setting a subband that does not match your network will prevent communication. Networks like The Things Network and Helium pre-define subbands, so ARDUINO_LMIC_CFG_SUBBAND is ignored for them.

  10. Initialize the Arduino_LoRaWAN library

    master

    To start using the library, you must call begin(). This function initializes the underlying LMIC stack. You can pass a pointer to an Arduino_LMIC::HalPinmap_t to specify the hardware pin mapping for the LoRa radio.

    Note: The constructor does not perform initialization; begin() is mandatory.

    // Example initialization
    Arduino_LoRaWAN lora;
    // ... define pinmap ...
    if (!lora.begin(&pinmap)) {
        // Handle error
    }
  11. Use Arduino_LoRaWAN::cEventLog to track library events

    master

    The cEventLog class provides a mechanism for capturing and processing LoRaWAN library events. To use it, you must call setup() during your Arduino setup() phase and loop() during your Arduino loop() phase.

    Events are captured using logEvent(), which creates a log entry containing arbitrary data and a callback function. The callback is triggered when loop() is called, allowing you to process or print the event details.

    #include <Arduino_LoRaWAN.h>
    
    // Define a callback function for logging
    void myLogCallback(const Arduino_LoRaWAN::EventNode_t *event) {
        // Access event data using the EventNode_t methods
        uint32_t timestamp = event->getTime();
        void* clientData = event->getClientData();
        uintptr_t val1 = event->getData(0);
        // ... process event
    }
    
    void setup() {
        // ... other LoRaWAN setup
        Arduino_LoRaWAN::cEventLog().setup();
    }
    
    void loop() {
        // ... other LoRaWAN logic
        Arduino_LoRaWAN::cEventLog().loop();
    }
  12. Send a buffer via SendBuffer

    master

    Sends a message from pBuffer. You can optionally provide a callback function pDoneFn that will be triggered when the message has been transmitted or abandoned.

    typedef void Arduino_LoRaWAN::SendBufferCbFn(
        void *pClientData,
        bool fSuccess
        );
    
    bool Arduino_LoRaWAN::SendBuffer(
        const uint8_t *pBuffer,
        size_t nBuffer,
        SendBufferCbFn *pDoneFn = nullptr,
        void *pClientData = nullptr,
        bool fConfirmed = false,
        uint8_t port = 1
        );