PineDIO Module

The PineDIO unit. Modified to have external WiFi antenna for range.

Setting up the PineDIO LoRaWAN gateway with TTN

The PineDIO is a LoRaWAN gateway solution from Pine64. Announced on May 6th, these units have been slowly sent out to lucky early developers. It consists of a Pine64 A64-LTS SBC + an adapter hat to hold a RAK2287 module. The RAK2287 is a low-cost concentrator module that provides all of the LoRa goodness required for this unit to work.

The RAK2287 integrates an SX1301 controller with SX125x frontend chips, this is all populated on a small mini-PCIe form factor board that is then mounted to a raspberry pi hat, that sits on the Pi header on the A64 board.

Steps to setting up the PineDIO as a TTN Gateway

A word of warning before you continue, it’s generally a bad idea to run a radio device without antennas connected. It may lead to damage. I highly recommend connecting the LoRA and GPS antennas now before you forget.

1. Sign up for an account at TTN

To register your gateway device, you will need to create an account so that you can log in to the console page for your region.

2. Download, flash and start the Armbian Image on the PineDIO

The A64-LTS board uses the sopine image of Armbian. For this walkthrough, I have used the focal release. Download the image via torrent or web from the armbian website.

Once the download has been completed, I recommend using the Balena Etcher program to flash this to a uSD card. I recommend at least 8GB cards, with 16GB being “about right” for most use cases.

Once the uSD card has been written, insert it into the side of the gateway in the uSD card slot. Connect power via the small DC input plug on the side of the PineDIO.

I highly recommend using an ethernet cable for the first time setup and configuration of the gateway, when I first loaded the Armbian image WiFi was not working out of the box.

3. Login and setup basics

Once the OS has loaded, log into the A64 using either an HDMI screen and USB keyboard, or by using ssh from another computer on the same network.

The default credentials are:

  • user: root
  • password: 1234

Once you log into the device, it will prompt you through setting more secure passwords and a new user account. For this guide, I’m using the username pine. (There will be a few places later where you will need to know this).

Once you have logged into the system, we want to ensure some essentials are installed on the system:

sudo apt update && sudo apt upgrade -y # Update the OS to the latest
sudo apt install build-essential git # You should already have these, but better safe than sorry

After these have finished, I found it simplest to directly edit the configuration file to enable the hardware ports required for the radio. For reference, it is required to enable the UART 2 for the GPS and the SPI port with chip select 0.

To enable these, open the configuration file sudo nano /boot/armbianEnv.txt Then add the following two lines:

param_spidev_spi_bus=0
overlays=spi-spidev uart2

Once this is done, my file looks like this:

verbosity=1
bootlogo=false
console=both
disp_mode=1920x1080p60
overlay_prefix=sun50i-a64
rootdev=UUID=a664f698-1b0b-4ae0-b7bf-cba8fc3e5fc5
rootfstype=ext4
param_spidev_spi_bus=0
overlays=spi-spidev uart2
usbstoragequirks=0x2537:0x1066:u,0x2537:0x1068:u

Note that some of your fields (especially the rootdev) will be different. This is normal.

Once you are done, hit ctrl-o, enter and ctrl-x to save and exit. Now reboot the board to for Linux to re-initialise the drivers for the hardware, and then you are ready to start setting up the LoRaWAN code.

These drivers create the /dev/spidev0.0 and /dev/ttyS2 devices.

4. Setting up LoRaWAN

For the sx130x series devices, Semtech publish drivers for use with these as gateways such as their development kits that use the Raspberry Pi.

I have forked this repository and changed the pin configuration to suit the PineDIO and edit the temperature compensation for RSSI to use a fixed 20C rather than 0C (as there is no temp sensor in the PineDIO).

To get started with the forked code login to the PineDIO and then enter the following command to pull the modified code:

git clone https://github.com/Ralim/sx1302_hal.git

This will download the folder sx1302_hal to the PineDIO.

Next enter cd sx1302 to enter into this folder.

The sx1302 code is set up a little differently from normal and it is designed to use scp to transfer the compiled code to the target. The easiest way to do this is to just compile the code on the PineDIO, and it’s quite fast so that’s what we will do in a second.

Before setting up your device, edit the target.cfg and set the USER to be the username you created on your device. Next, for this code, I have set it up to store the code in the more logical /opt/ folder, rather than your home folder. The setup script requires the folder to already exist and be writable by the user, so running sudo mkdir -p /opt/sx1302 && sudo chown $USER:$USER /opt/sx1302 will set this up for you.

Then execute make clean all install; this trifecta command will clean all temporary files, compile all of the code and then scp them into place. It will prompt you for your password at the end of the scp command.

Once this is done, in theory, the basics are all ready to go.

Enter cd /opt/sx1302 to change to the folder that contains the compiled programs.

Next run sudo ./chip_id, this is a small test program that will read out the id of your gateway (required for the next step) and test that the communications are working.

This should produce some output that looks like the below, your EUI is the unique id for your gateway. Naturally, this will be different for every unit.

sudo ./chip_id

PineDIO reset through GPIO71...
Opening SPI communication interface
Note: chip version is 0x10 (v1.0)
INFO: using legacy timestamp
ARB: dual demodulation disabled for all SF

INFO: concentrator EUI: 0x0016XXXXXXXXXXXX

Closing SPI communication interface
PineDIO reset through GPIO71...

If you receive communications errors at this point, jump to the troubleshooting section at the bottom of this page.

Otherwise, you now have the EUI and can register your gateway on The Things Network

5. Register your gateway on The Things Network

The Things Network console should have a prompt to register your gateway, if not click the Gateways tab at the top and then there is a blue button to add a new gateway to your account.

As per the following screenshot:

  1. Gateway ID is a unique name for the gateway on your account, you cant change it later on!
  2. The Gateway EUI. Copy and paste this from the command before without the leading 0x, so for this example, it would be 0016XXXXXXXXXXXX
  3. A nice friendly name, that might be where it’s located… Or if you’re boring like me “PineDIO” works too :)
  4. I prefer to untick these to hide the location, as my gateway is at a residential address
Screenshot of the TTN setup page

TTN setup page - They may change the names here over time, but the questions are roughly the same.

Once you are through this really short page you should now be able to see the status of your gateway. If it shows you a list of gateways, click on the gateway to view more information about the device.

At the moment it’s normal to see the gateway as offline as the LoRa packet forwarder service is not running.

6. Create LoRa configuration file

To tell the generic packet forwarder how to forward the packets to The Things Network; as well as what frequencies to use there is a file called global_conf.json that contains these settings. On the website for The Things Network, there is a download button that will download their general configuration file. This is not in exactly the right format but it’s somewhat close.

First, copy my template Australian configuration file by running cp ~/sx1302_hal/packet_forwarder/global_conf.json .. Then open this file in your text editor and compare it against the one from The Things network. Ensure all of the radio frequency options and your device ID at the bottom of the file are updated to match the one from their website.

6. Test sending data through to the server

To test that your gateway can send data through to The Things Network, we can temporarily run the packet forwarder directly to see its output easily.

Run sudo ./lora_pkt_fwd and wait and see. You should see it scroll fairly quickly as it prints out the settings it parsed from the file. Then after a while, you should see the following:

PineDIO reset through GPIO71...
Opening SPI communication interface
Note: chip version is 0x10 (v1.0)
INFO: using legacy timestamp
INFO: LoRa Service modem: configuring preamble size to 8 symbols
ARB: dual demodulation disabled for all SF
INFO: [main] concentrator started, packet can now be received
INFO: concentrator EUI: 0x0016c001ff1a0c4e
WARNING: [gps] GPS out of sync, keeping previous time reference
WARNING: [gps] GPS out of sync, keeping previous time reference
INFO: [down] PULL_ACK received in 5 ms

Once you have seen the PULL_ACK this means that the TTN server has sent a response back to this unit.

Within a minute you should see the gateway show as online.

7. Setting up the forwarder as a service

Once the gateway is showing as online, you have a working gateway. The only key issue left is that currently, you would need to log into the unit after every restart and run the program again. There is a better way, as we can set up systemd to handle running this program once the device boots up, and it will restart it if it crashes out.

If the code was setup as suggested above this can be easily setup by running the following:

sudo cp ~/sx1302_hal/tools/systemd/lora_pkt_fwd.service /etc/systemd/system
sudo systemctl daemon-reload
sudo systemctl enable lora_pkt_fwd.service --now

After this is done, systemd will automatically start the service and boot, and it will have also started it right now for you. You should see the gateway come online within a minute.

If you want to see more logs or if there are issues, you can use systemctl status lora_pkt_fwd to see basic information, and the output logs will be sent to journalctl -e.

Troubleshooting

Strange failures

When I was first working on this, I was running into strange lockups, or things just not working as they should. After this started occurring, dmesg would be flooded with errors from the emmc bus.

This was resolved by using the armbian-config tool to upgrade to the 5.10.34-sunxi64 kernel release. After this point, emmc (and the uSD and WiFi) have been working stable.

chip_id fails to read from the device

  • Validate that /dev/spidev0.0 exists
    • If not, try checking that spidev is turned on in sudo armbian-config
  • Validate that you have the HAT and module connected correctly, and fully plugged in
  • Try changing to the tested kernel (see above)

No PULL_ACK from the remote server

This would indicate that the lora_pkt_forwarder code is not communicating with the TTN server. All communication is done over UDP on port 1700. Your firewall will need to allow data transfer on this port, additionally, it will need to hold the port open in its NAT table for at least 5 seconds for the response to get back through.

If you are behind a corporate firewall, they should be able to help you with getting this data through. Also checking that internet is working will help too, though often you cant get to this point without the internet :D.

What does a full session of running the lora_pkt_fwd look like

For reference here is the output of running the software on my unit.

sudo ./lora_pkt_fwd
*** Packet Forwarder ***
Version: 2.1.0
*** SX1302 HAL library version info ***
Version: 2.1.0;
***
INFO: Little endian host
INFO: found configuration file global_conf.json, parsing it
INFO: global_conf.json does contain a JSON object named SX130x_conf, parsing SX1302 parameters
INFO: com_type SPI, com_path /dev/spidev0.0, lorawan_public 1, clksrc 0, full_duplex 0
INFO: antenna_gain 0 dBi
INFO: Configuring legacy timestamp
INFO: no configuration for SX1261
INFO: Configuring Tx Gain LUT for rf_chain 0 with 16 indexes for sx1250
INFO: radio 0 enabled (type SX1250), center frequency 917200000, RSSI offset -215.399994, tx enabled 1, single input mode 0
INFO: radio 1 enabled (type SX1250), center frequency 917900000, RSSI offset -215.399994, tx enabled 0, single input mode 0
INFO: Lora multi-SF channel 0>  radio 0, IF -400000 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora multi-SF channel 1>  radio 0, IF -200000 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora multi-SF channel 2>  radio 0, IF 0 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora multi-SF channel 3>  radio 0, IF 200000 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora multi-SF channel 4>  radio 1, IF -300000 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora multi-SF channel 5>  radio 1, IF -100000 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora multi-SF channel 6>  radio 1, IF 100000 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora multi-SF channel 7>  radio 1, IF 300000 Hz, 125 kHz bw, SF 5 to 12
INFO: Lora std channel> radio 0, IF 300000 Hz, 500000 Hz bw, SF 8, Explicit header
INFO: FSK channel 8 disabled
INFO: global_conf.json does contain a JSON object named gateway_conf, parsing gateway parameters
INFO: gateway MAC address is configured to 0016C001FF1A0C4E
INFO: server hostname or IP address is configured to "au1.cloud.thethings.network"
INFO: upstream port is configured to "1700"
INFO: downstream port is configured to "1700"
INFO: downstream keep-alive interval is configured to 10 seconds
INFO: statistics display interval is configured to 30 seconds
INFO: upstream PUSH_DATA time-out is configured to 100 ms
INFO: packets received with a valid CRC will be forwarded
INFO: packets received with a CRC error will NOT be forwarded
INFO: packets received with no CRC will be forwarded
INFO: GPS serial port path is configured to "/dev/ttyS2"
INFO: Reference latitude is configured to 0.000000 deg
INFO: Reference longitude is configured to 0.000000 deg
INFO: Reference altitude is configured to 0 meters
INFO: Beaconing period is configured to 0 seconds
INFO: Beaconing signal will be emitted at 923300000 Hz
INFO: Beaconing channel number is set to 8
INFO: Beaconing channel frequency step is set to 600000Hz
INFO: Beaconing datarate is set to SF12
INFO: Beaconing modulation bandwidth is set to 500000Hz
INFO: Beaconing TX power is set to 27dBm
INFO: global_conf.json does contain a JSON object named debug_conf, parsing debug parameters
INFO: got 2 debug reference payload
INFO: reference payload ID 0 is 0xCAFE1234
INFO: reference payload ID 1 is 0xCAFE2345
INFO: setting debug log file name to loragw_hal.log
INFO: [main] TTY port /dev/ttyS2 open for GPS synchronization
PineDIO reset through GPIO71...
Opening SPI communication interface
Note: chip version is 0x10 (v1.0)
INFO: using legacy timestamp
INFO: LoRa Service modem: configuring preamble size to 8 symbols
ARB: dual demodulation disabled for all SF
INFO: [main] concentrator started, packet can now be received
INFO: concentrator EUI: 0x0016c001ff1a0c4e
WARNING: [gps] GPS out of sync, keeping previous time reference
WARNING: [gps] GPS out of sync, keeping previous time reference
INFO: [down] PULL_ACK received in 5 ms
INFO: [down] PULL_ACK received in 4 ms
INFO: [down] PULL_ACK received in 4 ms

##### 2021-07-28 10:58:13 GMT #####
### [UPSTREAM] ###
# RF packets received by concentrator: 0
# CRC_OK: 0.00%, CRC_FAIL: 0.00%, NO_CRC: 0.00%
# RF packets forwarded: 0 (0 bytes)
# PUSH_DATA datagrams sent: 0 (0 bytes)
# PUSH_DATA acknowledged: 0.00%
### [DOWNSTREAM] ###
# PULL_DATA sent: 3 (100.00% acknowledged)
# PULL_RESP(onse) datagrams received: 0 (0 bytes)
# RF packets sent to concentrator: 0 (0 bytes)
# TX errors: 0
### SX1302 Status ###
# SX1302 counter (INST): 30734866
# SX1302 counter (PPS):  29930251
# BEACON queued: 0
# BEACON sent so far: 0
# BEACON rejected: 0
### [JIT] ###
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
#--------
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
### [GPS] ###
# Valid time reference (age: 0 sec)
# GPS coordinates: latitude -34.43504, longitude 150.88349, altitude 35 m
### Concentrator temperature: 0 C ###
##### END #####

JSON up: {"stat":{"time":"2021-07-28 10:58:13 GMT","lati":-34.43504,"long":150.88349,"alti":35,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"temp":0.0}}
INFO: [up] PUSH_ACK received in 3 ms
INFO: [down] PULL_ACK received in 4 ms
INFO: [down] PULL_ACK received in 4 ms
INFO: [down] PULL_ACK received in 4 ms

##### 2021-07-28 10:58:43 GMT #####
### [UPSTREAM] ###
# RF packets received by concentrator: 0
# CRC_OK: 0.00%, CRC_FAIL: 0.00%, NO_CRC: 0.00%
# RF packets forwarded: 0 (0 bytes)
# PUSH_DATA datagrams sent: 1 (166 bytes)
# PUSH_DATA acknowledged: 100.00%
### [DOWNSTREAM] ###
# PULL_DATA sent: 3 (100.00% acknowledged)
# PULL_RESP(onse) datagrams received: 0 (0 bytes)
# RF packets sent to concentrator: 0 (0 bytes)
# TX errors: 0
### SX1302 Status ###
# SX1302 counter (INST): 60736524
# SX1302 counter (PPS):  59930246
# BEACON queued: 0
# BEACON sent so far: 0
# BEACON rejected: 0
### [JIT] ###
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
#--------
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
### [GPS] ###
# Valid time reference (age: 0 sec)
# GPS coordinates: latitude -34.43505, longitude 150.88349, altitude 37 m
### Concentrator temperature: 0 C ###
##### END #####

JSON up: {"stat":{"time":"2021-07-28 10:58:43 GMT","lati":-34.43505,"long":150.88349,"alti":37,"rxnb":0,"rxok":0,"rxfw":0,"ackr":100.0,"dwnb":0,"txnb":0,"temp":0.0}}
INFO: [up] PUSH_ACK received in 3 ms
INFO: [down] PULL_ACK received in 4 ms
INFO: [down] PULL_ACK received in 4 ms
INFO: [down] PULL_ACK received in 4 ms

##### 2021-07-28 10:59:13 GMT #####
### [UPSTREAM] ###
# RF packets received by concentrator: 0
# CRC_OK: 0.00%, CRC_FAIL: 0.00%, NO_CRC: 0.00%
# RF packets forwarded: 0 (0 bytes)
# PUSH_DATA datagrams sent: 1 (168 bytes)
# PUSH_DATA acknowledged: 100.00%
### [DOWNSTREAM] ###
# PULL_DATA sent: 3 (100.00% acknowledged)
# PULL_RESP(onse) datagrams received: 0 (0 bytes)
# RF packets sent to concentrator: 0 (0 bytes)
# TX errors: 0
### SX1302 Status ###
# SX1302 counter (INST): 90737475
# SX1302 counter (PPS):  89930241
# BEACON queued: 0
# BEACON sent so far: 0
# BEACON rejected: 0
### [JIT] ###
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
#--------
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
### [GPS] ###
# Valid time reference (age: 0 sec)
# GPS coordinates: latitude -34.43505, longitude 150.88352, altitude 37 m
### Concentrator temperature: 0 C ###
##### END #####

JSON up: {"stat":{"time":"2021-07-28 10:59:13 GMT","lati":-34.43505,"long":150.88352,"alti":37,"rxnb":0,"rxok":0,"rxfw":0,"ackr":100.0,"dwnb":0,"txnb":0,"temp":0.0}}
INFO: [up] PUSH_ACK received in 3 ms
INFO: [down] PULL_ACK received in 3 ms
INFO: [down] PULL_ACK received in 4 ms
INFO: [down] PULL_ACK received in 3 ms

##### 2021-07-28 10:59:43 GMT #####
### [UPSTREAM] ###
# RF packets received by concentrator: 0
# CRC_OK: 0.00%, CRC_FAIL: 0.00%, NO_CRC: 0.00%
# RF packets forwarded: 0 (0 bytes)
# PUSH_DATA datagrams sent: 1 (168 bytes)
# PUSH_DATA acknowledged: 100.00%
### [DOWNSTREAM] ###
# PULL_DATA sent: 2 (100.00% acknowledged)
# PULL_RESP(onse) datagrams received: 0 (0 bytes)
# RF packets sent to concentrator: 0 (0 bytes)
# TX errors: 0
### SX1302 Status ###
# SX1302 counter (INST): 439990168
# SX1302 counter (PPS):  439930180
# BEACON queued: 0
# BEACON sent so far: 0
# BEACON rejected: 0
### [JIT] ###
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
#--------
src/jitqueue.c:440:jit_print_queue(): INFO: [jit] queue is empty
### [GPS] ###
# Valid time reference (age: 1 sec)
# GPS coordinates: latitude -34.43513, longitude 150.88362, altitude 46 m
### Concentrator temperature: 0 C ###
##### END #####

INFO: End of upstream thread

INFO: End of JIT thread

INFO: End of GPS thread

INFO: End of downstream thread
INFO: GPS closed successfully
Closing SPI communication interface
INFO: concentrator stopped successfully
PineDIO reset through GPIO71...
INFO: Exiting packet forwarder program