IoT with LoRaWAN, Pycom, The Things Network & Node-RED

Updated 18 March 2022

Note: With the rollout of TTNv3, the "Setup" section of this guide no longer works - but we're keeping it here for posterity. Let us know on our forums if you need help getting started with the new method.

In our two previous Pycom articles we covered Building a LoRaWAN Nano Gateway to The Things Network, and creating Your First LoRaWAN Node on The Things Network. In these articles we used the mighty powerful Pycom LoPy (part of the Pycom range) to build two nodes of a LoRaWAN network connected to The Things Network. This gave us full use of LoRaWAN's features including its incredible range and frugal power consumption. But we didn't close the loop; we couldn't take information from one device, make a decision based on that information, then tell other devices what to do. For this ability to coordinate devices we need a different kind of tool, one that "plugs in" to TTN, a tool like Node-RED.

Node-RED calls itself "flow-based programming for the Internet of Things". If you've never heard of Node-RED, jump to their home page and watch an introductory video. The method of connecting blocks into a graphical flow diagram looks simple, but it is POWERFUL! Connecting to our LoRaWAN devices via The Things Network is only one of many, many features. Want to control your devices based on the weather forecast? Sure. Want to track the location of something using a mapping site? Yep. There are no limits!

IoT Changes Everything

As developers and experimenters with IoT, there's a fundamental shift in how devices work. We used to write code on tiny computers to do something useful. If things changed, we usually had to go get the device, change the program and deploy it again.

Using IoT, we leave the control logic up to a platform like Node-RED. A sensor device only has one job to do: measure and report. An actuator device only has one job: make something happen. Now if the system changes, we can go to our control platform (Node-RED) and change how the system works. The devices don't have to be reprogrammed!

The Farmer Scenario

In this tutorial we're going to take what we've developed, one gateway and one node, and build an end-to-end application. Here's an example of how this simple scenario can do some useful work:

A farmer has cattle in a distant paddock, say 10km away. There's no water available to the animals so the farmer installs a trough and fills it from a pump and pipe. The pump runs off a battery and solar panels but has no communications.

In the days before IoT, the farmer rigged the pump to run 10 minutes a day to fill the trough. If anything were to go wrong the cattle would have no source of water so the farmer had to check the trough daily. Usually this was a big waste of time. If the cattle weren't in that paddock, the trough overflowed.

The farmer hears about LoRaWAN and decides to give it a go. She installs a battery powered Trough Monitor and gets an alert on her phone if the monitor detects a low water level. This is great! No wasted trips to the back paddock! The farmer then wonders how to make the pump run only when it needs to. Another LoRaWAN device is installed at the pump and now the system is under closed-loop control. Low water starts the pump. If the water level doesn't rise, the farmer is alerted to go fix the problem.

This is the power of the Internet of Things.

Understanding All the Moving Parts

The Farmer Scenario diagram

The Farmer Scenario Diagram

Our simplified "Farmer Scenario" has these parts:

  • A LoPy acting as both a sensor (water level measurement) and an actuator (pump controller) and having only LoRa communications
  • Another Pycom LoPy acting as a LoRaWAN gateway, connecting LoRa radio messages to The Things Network via the Internet
  • The Things Network (web site) as the endpoint for all our LoRaWAN communications. A message coming from any LoRa device will emerge from TTN. A message going back to any LoRa device must be sent through TTN.
  • Node-RED running on any computer connected to the Internet for integration of information from all sources, decision making, and sending control messages out to various locations including our LoRa devices.

If we assume we already have the LoRaWAN Nano Gateway and LoRaWAN Node built from the prior tutorials, as well as The Things Network set up with a Gateway, an Application and a Device, then we need to add:

  • Changes to the Node code to simulate water level and a pump
    NOTE: There's no logic to decide under what conditions to start/stop the pump. Node-RED has control.
  • Changes to The Things Network Application to convert LoRaWAN's byte data to/from JSON*
  • Installing Node-RED on a computer
  • Adding The Things Network Node-RED Application Nodes to Node-RED
  • Building a flow on Node-RED to control our application

* JSON stands for JavaScript Object Notation. It's a standard way to send and receive information as text, eg: { "humidity": 12, "temperature": 6, "led" : 1 }

New Node Code

Our new code makes our node simulate monitoring a water level and having run/stop control of a water pump. This code replaces the node_abp.py code from Your First LoRaWAN Node on The Things Network.

from network import LoRa
import struct
import socket
import pycom
import time

LORA_FREQUENCY = 916800000
LORA_NODE_DR = 5

COLOUR_WHITE = 0xFFFFFF
COLOUR_BLACK = 0x000000
COLOUR_RED   = 0xFF0000
COLOUR_GREEN = 0x00FF00
COLOUR_BLUE  = 0x0000FF

pycom.heartbeat(False)
pycom.rgbled(COLOUR_BLACK)

dev_addr = struct.unpack(">l", binascii.unhexlify('AAAAAAAA'))[0]
nwk_swkey = binascii.unhexlify('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB')
app_swkey = binascii.unhexlify('CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC')

lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AU915)

for i in range(0, 72):
    lora.remove_channel(i)

lora.add_channel(0, frequency=LORA_FREQUENCY, dr_min=0, dr_max=5)
lora.add_channel(1, frequency=LORA_FREQUENCY, dr_min=0, dr_max=5)
lora.add_channel(2, frequency=LORA_FREQUENCY, dr_min=0, dr_max=5)

lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))

s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)

s.setsockopt(socket.SOL_LORA, socket.SO_DR, LORA_NODE_DR)

s.setblocking(False)

pump_running = False
water_level = 0
empty_level = 0
full_level = 5

def check_downlink_messages():

    global pump_running # We need to tell Python to use the variable
                        # defined outside this function

    downlink_message, port = s.recvfrom(256) # See if a downlink message arrived

    if not downlink_message: # If there was no message, get out now
        return

    if downlink_message[0]: # The first byte is non-zero
        print("Starting the pump")
        pump_running = True
    else:
        print("Stopping the pump")
        pump_running = False

def send_level():

    print('Sending water level: {}'.format(water_level))
    uplink_message = bytes([water_level])
    s.send(uplink_message)

def adjust_water_level():

    global water_level  # We need to tell Python to use the variable
                        # defined outside this function

    if pump_running:
        if water_level < full_level:    # Can't be overfilled
            water_level += 1            # Water level rises
    else:
        if water_level > empty_level:   # Can't be less than empty
            water_level -= 1            # Water level drops

while(True):

    # Code at the indent happens every 10 seconds

    adjust_water_level()

    send_level()

    for i in range (10):        # 10 x 1 second delay

        # Code at this indent happens every second

        check_downlink_messages()

        if pump_running:    # At any time the pump might be stopped
            pycom.rgbled(COLOUR_GREEN)
        else:
            pycom.rgbled(COLOUR_RED)

        time.sleep(1)           # 1 second delay

You might need to adjust these lines:

LORA_FREQUENCY = 916800000
LORA_NODE_DR = 5

Also, you'll need to insert your own security credentials at these lines:

dev_addr = struct.unpack(">l", binascii.unhexlify('AAAAAAAA'))[0]
nwk_swkey = binascii.unhexlify('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB')
app_swkey = binascii.unhexlify('CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC')

The code is as it was, up to the line s.setblocking(False). From there I've added four variables, three function definitions (def) and a revised while(True): loop. I hope the code comments are sufficient to aid understanding.

If your gateway isn't up and running, you need it now. Your node is good to go!

Changes to The Things Network

Check that your gateway is showing connected in The Things Network. With that confirmed, you should be able to jump to the Application you already have and check your device is alive and data is coming in. If not, ensure you can see data from the node appearing in REPL box for the gateway. If there's nothing there you'll need to restart your node/gateway. Once data is visible through the gateway, reset frame counters in the Overview for your device. Just waiting helps too.

Application Data at The Things Network

Application Data at The Things Network

We want to change the bytes of data coming in (payload: 00) to something more meaningful before it arrives at Node-RED, so on the Application page, click Payload Formats.

Payload Formats at The Things Network

Payload Formats at The Things Network

Here we can add some JavaScript code. decoder, converter and validator are run on uplink messages arriving from LoRaWAN devices. encoder is the reverse, it acts on downlink messages headed back to the devices. With only a few lines of code here, we can change the terse binary numbers into lovely, human-readable JSON. Here's the code for each. Paste the code over what's there and click save payload functions at the bottom of the page after each one.

function Decoder(bytes, port) {

  var decoded = {};

  decoded.water_level = bytes[0];

  return decoded;
}

function Converter(decoded, port) {

  return decoded;
}

function Validator(converted, port) {

  // Ditch the data if outside range 0-10
  if (converted.water_level > 5 || converted.water_level < 0) {
    return false;
  }

  return true;
}

function Encoder(object, port) {

  var bytes = [];

  if (object.pump_running === 0 || object.pump_running === 1) {
    bytes[0] = object.pump_running ? 1 : 0;
  }

  // There is no separate converter and validator function for this direction. Return an empty array to drop the message.
  // https://www.thethingsnetwork.org/docs/devices/uno/quick-start.html
  return bytes;
}

With that code in place, you'll see on the Data tab that the nasty byte data is being interpreted as water_level.

Application data being transformed by The Things Network

Application data being transformed by The Things Network

We're done with TTN. We'll keep it open to check on things, but there shouldn't be any more changes.

Installing Node-RED on Raspberry Pi

If you're into IoT you're likely interested in Raspberry Pi. We're going to run Node-RED on a Raspberry Pi for this article. If you're not into it, no problem, have a look for a tutorial on running Node-RED on a PC/Mac instead. There are hosted services too, like FRED, which has a free option. A Pi is perfect because it's a computer you can leave running permanently for the cost of only a few watts.

Prior to the current release of Raspbian, Node-RED was installed by default. Raspbian 9 (Stretch) lists Node-RED under Recommended Software (Raspbian menu, Preferences, Recommended Software). Once in the Recommended Software dialog, search 'node'. Check the box and hit OK to install.

Installing Node-RED on Raspbian 9

Installing Node-RED on Raspbian 9

Once installed you'll find Node-RED on the menu at: Raspbian menu, Programming, Node-RED. If you're new to Linux you'll find the result underwhelming. Just a second! The console tells us to use a web browser and go to the address given. Mine in the screenshot below instructs me to go to http://192.168.15.81:1880

Starting Node-RED

Starting Node-RED

And there you will find Node-RED!

A new Flow in Node-RED

A new Flow in Node-RED

Just before we play with Node-RED, go back to the console (text screen) that opened and note the correct way to stop it. Open a terminal and type node-red-stop. You can restart it from the terminal or the menu.

Stopping Node-RED at the terminal

Stopping Node-RED at the terminal

Setting Up Node-RED

Node-RED can play nicely with The Things Network but needs an add-in for that.

Installing the TTN add-in for Node-RED

Installing the TTN add-in for Node-RED

  1. Go to the "hamburger" menu in the upper right corner and select Settings
  2. A menu pops out from which you can select Palette on the left tabs
  3. At the top change tabs to Install
  4. In the search box type ttn
  5. In the search results, click the Install button next to node-red-contrib-ttn
  6. Click Close after installation

Building a Node-RED Flow

Having learned only a little of Node-RED myself, I can only cover enough to get this basic flow to work. So, here's a bare-metal intro!
See also: node-red-contrib-ttn documentation and quick start guide.

Building a new Flow in Node-RED

Building a new Flow in Node-RED

All the actions associated with one scenario (TTN Application?) are built into one flow. The gridded area in the middle is where we add and connect nodes to do work. The available types of nodes are listed on the left, categorised under input, output, function, social, etc. On the right we have properties of the nodes (info) as well a debug tab that we'll use like the REPL box in Atom.

The TTN Uplink node

The TTN Uplink node

You can see the input nodes have a square connector on the right side so their outputs can be linked to the inputs of other nodes. The first thing we want to do is see the water_level data from our Pycom node device. From the inputs, drag a ttn uplink node onto the flow. Double-clicking it will bring up a property sheet for it on the right.

Configuring the TTN Application in Node-RED

Configuring the TTN Application in Node-RED

The first thing we'll need to do is configure a TTN app. Configuring your application from their documentation is the authority. I created an extra TTN access key for Node-RED. Because we only have one Node device we don't need to even specify a device to get data from TTN. All of the application's data comes to this one node!

Get Water Level node properties in Node-RED

Get Water Level node properties in Node-RED

By linking a debug (output) node, we can print all the data coming in to the debug tab on the right. When you have the two nodes configured and connected, hit the Deploy button in the top right.

Debugging application data in Node-RED

Debugging application data in Node-RED

Got the basics? I hope that much worked for you.

The Farmer Scenario in Node-RED

The Farmer Scenario in Node-RED

If so, we need to add and connect the following node types per the diagram above.

  • A functionnode from the function group, with code:
    if (msg.payload.water_level === 0) {
        msg.payload.pump_running = 1
    }
    if (msg.payload.water_level === 5) {
        msg.payload.pump_running = 0
    }
    return msg
  • Two inject nodes from the input group, one with JSON: {"pump_running":1}, the other with JSON: {"pump_running":0}
  • Another debug node from the output group
  • A ttn downlink node from the output group
    Send Pump Control node in Node-RED

Checking it Over

You should have a fully functional system now. The LED on your LoPy Node should turn green when the pump is running and red when it's off. The Atom REPL attached to the Node should display:

Sending water level: 4
Stopping the pump
Sending water level: 3
Sending water level: 2
Sending water level: 1
Sending water level: 0
Sending water level: 0
Starting the pump
Sending water level: 1
Starting the pump
Sending water level: 2
Sending water level: 3
Starting the pump
Sending water level: 4
Sending water level: 5
Sending water level: 5
Stopping the pump
Sending water level: 4
Stopping the pump
Sending water level: 3
Sending water level: 2

The Gateway Pycom device should be outputting normal gateway things, like:

[ 25276.029] Pull rsp
[ 25277.004] Sent downlink packet scheduled on 1981.570, at 923.299 Mhz using SF12BW500: b'`\x17\x10\x04&\x00\xb1\x01\x02\xc0k\xbb\x01\xd8'
[ 25277.144] Pull ack
[ 25285.007] Received packet: {"rxpk": [{"data": "QBcQBCYAhwECjVSiLVI=", "time": "2018-07-05T05:24:36.713347Z", "chan":0, "tmst": 1989572330, "stat": 1, "modu": "LORA", "lsnr": 5.0, "rssi": -22, "rfch": 0, "codr": "4/5", "freq": 916.7999,"datr": "SF7BW125", "size": 14}]}
[ 25285.027] Push ack
[ 25287.151] Push ack
[ 25295.011] Received packet: {"rxpk": [{"data": "QBcQBCYAiAEChhYKw3c=", "time": "2018-07-05T05:24:46.717314Z", "chan":0, "tmst": 1999576222, "stat": 1, "modu": "LORA", "lsnr": 6.0, "rssi": -22, "rfch": 0, "codr": "4/5", "freq": 916.7999,"datr": "SF7BW125", "size": 14}]}
[ 25295.031] Push ack
[ 25302.155] Pull ack
[ 25305.016] Received packet: {"rxpk": [{"data": "QBcQBCYAiQECP77Mrok=", "time": "2018-07-05T05:24:56.719345Z", "chan":0, "tmst": 2009578108, "stat": 1, "modu": "LORA", "lsnr": 6.0, "rssi": -22, "rfch": 0, "codr": "4/5", "freq": 916.7999,"datr": "SF7BW125", "size": 14}]}
[ 25305.030] Push ack
[ 25327.143] Pull ack

The Data tab on your TTN application should be printing messages going in both directions:

Application Data in its final form on The Things Network

Application Data in its final form on The Things Network

The Debug panel in Node-RED should likewise give you a view into what's going on at that end.

Node-RED debug panel showing all data

Node-RED debug panel showing all data

And there you have it. I'm excited! Now that we can build a LoRaWAN IoT system from end-to-end I'm going to be building LoRawAN IoT networks at work and at home. I'm not kidding, this stuff is awsome!

EDIT: You might have noticed I diagrammed notifications to a smartphone but didn't implement it. I'll leave that as a challenge. :)

Have a question? Ask the Author of this guide today!

Please enter minimum 20 characters

Your comment will be posted (automatically) on our Support Forum which is publicly accessible. Don't enter private information, such as your phone number.

Expect a quick reply during business hours, many of us check-in over the weekend as well.

Comments


Loading...
Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.