Connect Conduit to TTN LoRaWan via Poly Packet Fwd

One of the most versatile LoRa / LoRaWAN gateways on todays market is Multitech Conduit with LoRa module. This nice blue boxes comes in 2 flavours; mLinux with everything driven from the command line and standard config files and the AEP version with a web based UI. The AEP version is intended for private LoRaWan deployments and where quick and easy configuration is required. I am not going to discuss benefits of Multitech gateway, there is another post coming where I will compare gateways from several vendors one of them being Multitech.

AEP

AEP lorawan

I will focus now on how to configure your AEP box to talk to TTN with PolyPacket Forwarder.

To begin, you need to install the Poly Packet Forwarder on your box. I suggest to download the package to another system and then use scp to move it to your Conduit:

From another system, download the latest version of ipk binary from the following link: https://github.com/kersing/packet_forwarder/tree/master/multitech-bin

Use scp to copy it to your conduit:
scp poly-packet-forwarder_2.1-r3_arm926ejste.ipk admin@<IP_OF_YOUR_CONDUIT>:/tmp/

Ssh to your Conduit or use the console access via USB port (at the front, behind the plastic cover).

Now, you can install the ipk package by executing:

opkg install poly-packet-forwarder_2.1-r3_arm926ejste.ipk

In order to keep the Lora Network/Application server functionality of the Conduit, we will not change it’s mode via the UI. We will, just modify the startup file and add few additional config files.

In order to start Poly Packet Forwarder instead of the original packet forwarder, replace /etc/init.d/lora-network-server script with the following:

#!/bin/bash
set -e

NAME=”lora-network-server”

run_dir=/var/run/lora
conf_dir=/var/config/lora
conf_file=$conf_dir/lora-network-server.conf
conf_db=$conf_dir/lora-network-server.db

angel=/sbin/angel

net_server=/opt/lora/lora-network-server
net_server_log=/var/log/lora-network-server.log
net_server_pidfile=$run_dir/$NAME.pid

pkt_fwd=/opt/lora/poly_pkt_fwd
pkt_fwd_log=/var/log/lora-pkt-fwd-1.log
pkt_fwd_pidfile=$run_dir/lora-pkt-fwd-1.pid

lora_us_id=”MTAC-LORA-915″
lora_eu_id=”MTAC-LORA-868″

read_card_info() {
# product-id of first lora card
lora_id=$(mts-io-sysfs show lora/product-id 2> /dev/null)
lora_eui=$(mts-io-sysfs show lora/eui 2> /dev/null)
# remove all colons
lora_eui_raw=${lora_eui//:/}
}

do_start() {
JSON=$(curl -m 5 -s “127.0.0.1/api?fields=loraNetwork/lora,system/capabilities/lora,system/macAddress”)
LORA_ENABLED=$( echo $JSON | jsparser -p /result/loraNetwork_lora/enabled )
LORA_CAPABLE=$( echo $JSON | jsparser -p /result/system_capabilities_lora )
LORA_PKT_FWD=$( echo $JSON | jsparser -p /result/loraNetwork_lora/packetForwarderMode –isNull || true )
if [ “$LORA_PKT_FWD” = “true” ]; then
LORA_PKT_FWD=false
else
LORA_PKT_FWD=$( echo $JSON | jsparser -p /result/loraNetwork_lora/packetForwarderMode )
fi
MAC_ADDR=$( echo $JSON | jsparser -p /result/system_macAddress )

if [ “$LORA_ENABLED” != “true” ] || [ “$LORA_CAPABLE” != “true” ]; then
exit 0
fi

if [ “$LORA_PKT_FWD” = “true” ]; then
echo -n “Starting packet forwarder mode: “i
read_card_info
GW_ID=”${lora_eui//:/}”
mkdir -p $run_dir/1

USE_GPS=$( curl -m 5 -s “127.0.0.1/api/loraNetwork/lora/packetForwarderConfig” | jsparser -p /result | grep fake_gps | wc -c )
if [ “$USE_GPS” != “0” ]; then
pkt_fwd=/opt/lora/gps_pkt_fwd
fi

curl -m 5 -s “127.0.0.1/api/loraNetwork/lora/packetForwarderConfig” | jsparser -p /result | sed “s/\(.*gateway_ID\”\s*\:\s*\”\)[^\”]*\(.*\)/\1${GW_ID}\2/g” > /var/run/lora/1/global_conf.json
# start packet forwarder
start-stop-daemon –start –background –make-pidfile \
–pidfile $pkt_fwd_pidfile –exec $angel — \
$pkt_fwd -c $run_dir/1 -l $pkt_fwd_log
renice -n -20 -p $(pgrep $(basename $pkt_fwd))
echo OK
exit 0
fi

/usr/bin/python /sbin/loraconfig.py
read_card_info

if ! [ -f $conf_file ]; then
echo “$0: $conf_file missing”
exit 1
fi

echo -n “Starting $NAME: ”
mkdir -p $run_dir/1
# start network server
start-stop-daemon –start –background –make-pidfile \
–pidfile $net_server_pidfile –exec $angel — \
$net_server -c $conf_file –lora-eui $lora_eui –lora-path $run_dir –db $conf_db \
–noconsole -l $net_server_log
sleep 2
# start packet forwarder
start-stop-daemon –start –background –make-pidfile \
–pidfile $pkt_fwd_pidfile –exec $angel — \
$pkt_fwd -c $conf_dir -l $pkt_fwd_log

renice -n -20 -p $(pgrep lora-network-se)
renice -n -20 -p $(pgrep $(basename $pkt_fwd))

echo “OK”
}

do_stop() {
echo -n “Stopping $NAME: ”
start-stop-daemon –stop –quiet –oknodo –pidfile $net_server_pidfile –retry 15
start-stop-daemon –stop –quiet –oknodo –pidfile $pkt_fwd_pidfile –retry 5
rm -f $net_server_pidfile $pkt_fwd_pidfile
echo “OK”
}

case “$1” in
“start”)
do_start
;;
“stop”)
do_stop
;;
“restart”)
## Stop the service and regardless of whether it was
## running or not, start it again.
do_stop
do_start
;;
*)
## If no parameters are given, print which are avaiable.
echo “Usage: $0 {start|stop|restart}”
exit 1
;;
esac

As I mentioned before, DO NOT MODIFY ANYTHING via your UI!!!

Now, you just need to add your local config files into /var/config/lora/ folder on the conduit:

global_conf.json

{
"SX1301_conf": {
"lorawan_public": true,
"clksrc": 0, /* radio_1 provides clock to concentrator */
"radio_0": {
"enable": true,
"type": "SX1257",
"freq": 912200000,
"rssi_offset": -166.0,
"tx_enable": true
},
"radio_1": {
"enable": true,
"type": "SX1257",
"freq": 913000000,
"rssi_offset": -166.0,
"tx_enable": false
},
"chan_multiSF_0": {
"enable": true,
"radio": 0,
"if": -300000
},
"chan_multiSF_1": {
"enable": true,
"radio": 0,
"if": -100000
},
"chan_multiSF_2": {
"enable": true,
"radio": 0,
"if": 100000
},
"chan_multiSF_3": {
"enable": true,
"radio": 0,
"if": 300000
},
"chan_multiSF_4": {
"enable": true,
"radio": 1,
"if": -300000
},
"chan_multiSF_5": {
"enable": true,
"radio": 1,
"if": -100000
},
"chan_multiSF_6": {
"enable": true,
"radio": 1,
"if": 100000
},
"chan_multiSF_7": {
"enable": true,
"radio": 1,
"if": 300000
},
"chan_Lora_std": {
"enable": true,
"radio": 0,
"if": 400000,
"bandwidth": 500000,
"spread_factor": 8
},
"chan_FSK": {
"enable": false,
"radio": 0,
"if": 100000,
"bandwidth": 250000,
"datarate": 100000,
"freq_deviation" : 25000
},
"tx_lut_0": {
/* TX gain table, index 0 */
"pa_gain": 0,
"mix_gain": 8,
"rf_power": -6,
"dig_gain": 0
},
"tx_lut_1": {
/* TX gain table, index 1 */
"pa_gain": 0,
"mix_gain": 10,
"rf_power": -3,
"dig_gain": 0
},
"tx_lut_2": {
/* TX gain table, index 2 */
"pa_gain": 0,
"mix_gain": 12,
"rf_power": 0,
"dig_gain": 0
},
"tx_lut_3": {
/* TX gain table, index 3 */
"pa_gain": 1,
"mix_gain": 8,
"rf_power": 3,
"dig_gain": 0
},
"tx_lut_4": {
/* TX gain table, index 4 */
"pa_gain": 1,
"mix_gain": 10,
"rf_power": 6,
"dig_gain": 0
},
"tx_lut_5": {
/* TX gain table, index 5 */
"pa_gain": 1,
"mix_gain": 12,
"rf_power": 10,
"dig_gain": 0
},
"tx_lut_6": {
/* TX gain table, index 6 */
"pa_gain": 1,
"mix_gain": 13,
"rf_power": 11,
"dig_gain": 0
},
"tx_lut_7": {
/* TX gain table, index 7 */
"pa_gain": 2,
"mix_gain": 9,
"rf_power": 12,
"dig_gain": 0
},
"tx_lut_8": {
/* TX gain table, index 8 */
"pa_gain": 1,
"mix_gain": 15,
"rf_power": 13,
"dig_gain": 0
},
"tx_lut_9": {
/* TX gain table, index 9 */
"pa_gain": 2,
"mix_gain": 10,
"rf_power": 14,
"dig_gain": 0
},
"tx_lut_10": {
/* TX gain table, index 10 */
"pa_gain": 2,
"mix_gain": 11,
"rf_power": 16,
"dig_gain": 0
},
"tx_lut_11": {
/* TX gain table, index 11 */
"pa_gain": 3,
"mix_gain": 10,
"rf_power": 20,
"dig_gain": 0
},
"tx_lut_12": {
/* TX gain table, index 12 */
"pa_gain": 3,
"mix_gain": 11,
"rf_power": 23,
"dig_gain": 0
},
"tx_lut_13": {
/* TX gain table, index 13 */
"pa_gain": 3,
"mix_gain": 12,
"rf_power": 24,
"dig_gain": 0
},
"tx_lut_14": {
/* TX gain table, index 14 */
"pa_gain": 3,
"mix_gain": 13,
"rf_power": 25,
"dig_gain": 0
},
"tx_lut_15": {
/* TX gain table, index 15 */
"pa_gain": 3,
"mix_gain": 15,
"rf_power": 26,
"dig_gain": 0
}
},
"gateway_conf": {
/* change with default server address/ports, or overwrite in local_conf.json */
"gateway_ID": "008000000000XXXX",
/* Devices */
"gps": true,
"beacon": false,
"monitor": false,
"upstream": true,
"downstream": true,
"ghoststream": false,
"radiostream": true,
"statusstream": true,
/* node server */
"server_address": "127.0.0.1",
"serv_port_up": 1780,
"serv_port_down": 1782,
/* node servers for poly packet server (max 4) */
"servers":
[ { "server_address": "127.0.0.1",
"serv_port_up": 1780,
"serv_port_down": 1782,
"serv_enabled": true },
{ /* "server_address": "us01-iot.semtech.com", */
"server_address": "12.13.93.141",
"serv_port_up": 1780,
"serv_port_down": 1780,
"serv_enabled": false },
{ /* "server_address": "router.us.thethings.network", */
"server_address": "router.us.thethings.network",
"serv_port_up" : 1700,
"serv_port_down" : 1700,
"serv_enabled": true },
{ /* "server_address": "ott1.iothub.ca", */
"server_address": "ott1.iothub.ca",
"serv_port_up": 1700,
"serv_port_down": 1700,
"serv_enabled": true }],
/* adjust the following parameters for your network */
"keepalive_interval": 12,
"stat_interval": 20,
"push_timeout_ms": 120,
"synch_word" : 52,
/* forward only valid packets */
"forward_crc_valid": true,
"forward_crc_error": false,
"forward_crc_disabled": true,
/* GPS configuration */
"gps_tty_path": "/dev/ttyAMA0",
"fake_gps": true,
"ref_latitude": YOUR_LAT,
"ref_longitude": YOUR_LON,
"ref_altitude": YOUR_ALT,
/* Ghost configuration */
"ghost_address": "127.0.0.1",
"ghost_port": 1918,
/* Monitor configuration */
"monitor_address": "127.0.0.1",
"monitor_port": 2008,
"ssh_path": "/usr/bin/ssh",
"ssh_port": 22,
"http_port": 80,
"ngrok_path": "/usr/bin/ngrok",
"system_calls": ["df -m","free -h","uptime","who -a","uname -a"],
/* Platform definition, put a asterix here for the system value, max 24 chars. */
"platform": "*",
/* Email of gateway operator, max 40 chars*/
"contact_email": "YOUR_EMAIL",
/* Public description of this device, max 64 chars */
"description": "YOUR_GW_DESCRIPTION"
}
}

Keep in mind, that this example is for SubBand 7 of North American flavour of Lora.
The local_conf.json will overwrite respective portion go global_conf.json with your site specific information

local_conf.json
{
/* Put there parameters that are different for each gateway (eg. pointing one gateway to a test server while the others stay in production) */
/* Settings defined in global_conf will be overwritten by those in local_conf */
"gateway_conf": {
/* you must pick a unique 64b number for each gateway (represented by an hex string) */
"gateway_ID": "008000000000XXXX",
/* Email of gateway operator, max 40 chars*/
"contact_email": "YOUR_CONTACT_EMAIL",
/* Public description of this device, max 64 chars */
"description": "DESCRIPTION_OF_YOUR_GATEWAY",
/* Enter VALID GPS coordinates below before enabling fake GPS */
"fake_gps": true,
"ref_latitude": YOUR_GPS_LAT,
"ref_longitude": YOUR_GPS_LON,
"ref_altitude": YOUR_GPS_ALT
}
}

There is one more thing, since the UI controls switching between Packet forwarder mode and Network server mode, you will have no logging for the packet forwarder. In order to enable it, you need to issue the following command from your console:
curl 127.0.0.1/api/loraNetwork -X PUT -d '{"log":{"syslog":false}}' -H "Content-Type: application/json"

At this point you can restart you lora service /etc/init.d/lora-network-server restart and your log should confirm that everything is in order:
tail -f /var/log/lora-pkt-fwd-1.log
# Invalid gps time reference (age: 1464959181 sec)
# Manual GPS coordinates: latitude ....., longitude ...., altitude 111 m
##### END #####
INFO: [down] for server router.us.thethings.network PULL_ACK received in 106 ms
INFO: [up] PUSH_ACK for server router.us.thethings.network received in 108 ms
INFO: [up] PUSH_ACK for server ott1.iothub.ca received in 35 ms
INFO: [down] for server 127.0.0.1 PULL_ACK received in 1 ms
INFO: [down] for server ott1.iothub.ca PULL_ACK received in 34 ms
INFO: [down] for server router.us.thethings.network PULL_ACK received in 105 ms
INFO: [down] for server 127.0.0.1 PULL_ACK received in 1 ms
INFO: [down] for server ott1.iothub.ca PULL_ACK received in 34 ms

As you can see, we are also connecting to the localhost, so Conduit’s network server. I used that for some tests and find it very helpful.

At this point you can start your node-red (on the Conduit) and configure it to connect to TTN to retrieve and process your packets:

ttn

ttn

node-red & lorawan

node-red & lorawan

From the node-red, processed packets are send to InfluxDB and then Grafana provides nice user interface:

SenseInAir over lorawan

SenseInAir over lorawan

Please let me know if you are interested in any write-ups on Node-Red and TTN (or other LoRaWan networks), but for now enjoy your AEP Conduit communicating flawlessly with The Things Network and LoRaWan.

 

 

 

 

11 Comments Connect Conduit to TTN LoRaWan via Poly Packet Fwd

  1. Stefan

    Great manual. When I tail the logfile it says it’s not found. Any idea how to solve that? Do I also need to install the updated network server as mentioned in an earlier TTN manual?

    Reply
    1. IotMan

      Stefan,
      Yes, you should install the update for the network server. The change described here only applies to the layer below network server…
      Did you execute
      curl 127.0.0.1/api/loraNetwork -X PUT -d ‘{“log”:{“syslog”:false}}’ -H “Content-Type: application/json” ?
      If yes, please provide the output of:
      curl 127.0.0.1/api/loraNetwork/log

      Reply
      1. Stefan B

        This is ironic because my name is also Stefan but I’m not the same person… but I had the same issue as Stefan above.
        After I Ran:
        curl 127.0.0.1/api/loraNetwork -X PUT -d ‘{“log”:{“syslog”:false}}’ -H “Content-Type: application/json”

        I Run:
        tail -f /var/log/tail -f lora-pkt-fwd-1.log

        Output:
        tail: can’t open ‘/var/log/tail’: No such file or directory
        tail: can’t open ‘lora-pkt-fwd-1.log’: No such file or directory
        tail: no files

        Output of curl 127.0.0.1/api/loraNetwork/log:
        {
        “code” : 200,
        “result” : {
        “console” : true,
        “level” : 30,
        “path” : “”,
        “syslog” : false
        },
        “status” : “success”
        }

        Any suggestions?

        Reply
  2. Mikko

    Thanks for the great guide. After every reboot however, I have to manually restart the /etc/init.d/lora-network-server or otherwise it will only connect to 127.0.0.1 . Do you have any advice?

    Reply
  3. JC Wren

    Where did the tx_lut_xx array and rssi_offset values come from? I’ve seen these in some of the European configuration files, and in one other US configuration, but I haven’t seen who decides what they should be, how they’re calculated, etc.

    Reply
    1. IotMan

      It’s as per SX1301 spec. You don’t really change that part (offsets etc). In case of North America you just change both frequencies and the rest remains the same. Those values will be provided with Conduit template, but in case of more sophisticated devices such as Kerlink, they will be generated after the calibration. Actually, technically speaking you get Kerlink pre-calibrated, but those values are unique for your model.

      Reply
  4. Peter Keegan

    IOTMAN,
    This is quite interesting. Are you suggesting that I would be able to both subscribe and publish through both the localhost ( Conduit ) and say thethingsnetwork? I need to archive the messages locally if my internet connection were to go down.

    Reply
    1. IotMan

      The Poly forwarder allows you to connect to multiple lora servers, like your localhost and ttn for example. However, your node will register with only one of those servers, so the other one will still receive packets but they will be rejected….

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *