The LoRaWAN part of this Guide is deprecated and is no longer possible! Single-channel packet forwarders no longer work after the Things Network migration to The Things Stack v3. For more information about this decision, visit:

We've sent data from our sensor devices to Adafruit IO over LoRa, but what about sending data over a different protocol - LoRaWAN

The Adafruit_TinyLoRa library allows the RFM9x to be used as a LoRaWAN device capable of communicating its data over longer distance with less power utilization. 

Feather Usage

The code below will use the adafruit_tinylora library which provides high-level access to your radio transceiver features. 

The code for sending data using a BME280 sensor, along with a unique feather identifier, to a Things Network Gateway is shown below:

# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries
# SPDX-License-Identifier: MIT

import time
import board
import busio
from digitalio import DigitalInOut
import adafruit_bme280
from adafruit_tinylora.adafruit_tinylora import TTN, TinyLoRa

# Unique feather identifier

i2c = busio.I2C(board.SCL, board.SDA)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)

# TinyLoRa/RFM9x Setup
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
# pylint: disable=c-extension-no-member
cs = DigitalInOut(board.RFM9X_CS)
irq = DigitalInOut(board.RFM9X_D0)
rst = DigitalInOut(board.RFM9X_RST)

# TTN Device Address, 4 Bytes, MSB
devaddr = bytearray([0x00, 0x00, 0x00, 0x00])

# TTN Network Key, 16 Bytes, MSB
nwkey = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])

# TTN Application Key, 16 Bytess, MSB
app = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])

ttn_config = TTN(devaddr, nwkey, app, country='US')

lora = TinyLoRa(spi, cs, irq, rst, ttn_config, channel = 6)

# bme data packet
bme_d = bytearray(7)

while True:
    # Grab sensor data
    temp_val = int(bme280.temperature * 100)
    humid_val = int(bme280.humidity * 100)

    bme_d[0] = FEATHER_ID
    # Temperature data
    bme_d[1] = (temp_val >> 8) & 0xff
    bme_d[2] = temp_val & 0xff
    # Humidity data
    bme_d[3] = (humid_val >> 8) & 0xff
    bme_d[4] = humid_val & 0xff

    print('Sending packet...')
    lora.send_data(bme_d, len(bme_d), lora.frame_counter)
    print('Packet sent!')
    lora.frame_counter += 1
    time.sleep(1 * 60)

While we can send data to The Things Network, and our gateway might notice it, it isn't registered to a device yet.

Running the Code using Mu Editor

Upon saving (ctrl/cmd +s) your code, the board will refresh. 

But where is the output? The serial monitor is hidden by default in Mu Editor.

In the Mu Editor, click the Serial button on the top icon-bar to bring up the Serial REPL.

The REPL should output the following: output:
Sending packet...
Packet sent!

We still need to check that the Raspberry Pi Gateway receives the packet from the Feather. Let's set that up next.

Pi Usage

Run the Python program by entering the following into the terminal:


Press the first button to display statistics about the Pi, such as its IP, CPU load, and available memory.

Press the third button to display the name of the gateway along with the frequency, spreading factor, and The Things Network router.

Press the second button to launch the gateway. It'll display the current status (if a packet is received or not) and update the timestamp every minute.

When a LoRa Packet is received by the gateway, the terminal will display that a packet has been received, and it'll print out useful data coming from the packet :

incoming pkt...

The display will also refresh to display that a packet has been received.

We can't read the incoming data since it's encrypted. After all, device data shouldn't be readable by a gateway operator. We'll need to check our device on The Things Network Console to read the decrypted data.

Checking the data on The Things Network Console

We want to make sure the data has been received on the other end. To do this, we'll navigate to the The Things Network Console and select the application. From the menu on the right hand side of the page, Click Data.

If everything worked correctly, you'll see the payload from your device streaming into the page, in real time. 

Neat, right? But while we received a payload, we still don't understand what it means. It's been sent to The Things Network and decoded on the client (Gateway) side, so it's not an AES-encrypted payload anymore. It's just not readable by humans.

If you're sending packets in strange formats or encodings (like we are!), The Things Network Console has a programmable data decoder to decode the packets, and assign useful labels to the data.

Copy and paste the decoder script below into the decoder's integrated text editor and click save

// TinyLoRa - BME280 and Feather ID Decoder
function Decoder(bytes, port) {
  var decoded = {};

  // Decode bytes to int
  var celciusInt = (bytes[1] << 8) | bytes[2];
  var humidInt = (bytes[3] << 8) | bytes[4];
  // Decode Feather ID
  decoded.featherID = bytes[0]
  // Decode int to float
  decoded.celcius = celciusInt / 100;
  decoded.humid = humidInt / 100;

  return decoded;

Then, click the data tab. Next to the raw payload data, you should see the decoded data for humidity and temperature, along with the unique ID of the feather.

This guide was first published on Feb 05, 2019. It was last updated on Apr 20, 2024.

This page (Using with The Things Network) was last updated on Apr 20, 2024.

Text editor powered by tinymce.