ttn-resin-gateway-rpi/run.py

341 lines
12 KiB
Python
Executable File

#!/usr/bin/python
"""
Author: JP Meijers
Date: 2017-02-26
Based on: https://github.com/rayozzie/ttn-resin-gateway-rpi/blob/master/run.sh
"""
import os
import os.path
import sys
import urllib2
import time
import uuid
import json
import subprocess
try:
import urlparse
except:
import urllib.parse as urlparse
try:
import RPi.GPIO as GPIO
except RuntimeError:
print("Error importing RPi.GPIO! This is probably because you need superuser privileges. You can achieve this by using 'sudo' to run your script")
GWID_PREFIX="FFFE"
if not os.path.exists("/opt/ttn-gateway/mp_pkt_fwd"):
print ("ERROR: gateway executable not found. Is it built yet?")
sys.exit(0)
if os.environ.get('HALT') != None:
print ("*** HALT asserted - exiting ***")
sys.exit(0)
# Show info about the machine we're running on
print ("*** Resin Machine Info:")
print ("*** Type: "+str(os.environ.get('RESIN_MACHINE_NAME')))
print ("*** Arch: "+str(os.environ.get('RESIN_ARCH')))
if os.environ.get("RESIN_HOST_CONFIG_core_freq")!=None:
print ("*** Core freq: "+str(os.environ.get('RESIN_HOST_CONFIG_core_freq')))
if os.environ.get("RESIN_HOST_CONFIG_dtoverlay")!=None:
print ("*** UART mode: "+str(os.environ.get('RESIN_HOST_CONFIG_dtoverlay')))
# Check if the correct environment variables are set
print ("*******************")
print ("*** Configuration:")
print ("*******************")
if os.environ.get("GW_EUI")==None:
# The FFFE should be inserted in the middle (so xxxxxxFFFExxxxxx)
my_eui = format(uuid.getnode(), '012x')
my_eui = my_eui[:6]+GWID_PREFIX+my_eui[6:]
my_eui = my_eui.upper()
else:
my_eui = os.environ.get("GW_EUI")
# Define default configs
description = os.getenv('GW_DESCRIPTION', "")
placement = ""
latitude = os.getenv('GW_REF_LATITUDE', 0)
longitude = os.getenv('GW_REF_LONGITUDE', 0)
altitude = os.getenv('GW_REF_ALTITUDE', 0)
frequency_plan_url = os.getenv('FREQ_PLAN_URL', "https://account.thethingsnetwork.org/api/v2/frequency-plans/EU_863_870")
# Fetch config from TTN if TTN is enabled
if(os.getenv('SERVER_TTN', "true")=="true"):
if os.environ.get("GW_ID")==None:
print ("ERROR: GW_ID required")
print ("See https://www.thethingsnetwork.org/docs/gateways/registration.html#via-gateway-connector")
sys.exit(0)
if os.environ.get("GW_KEY")==None:
print ("ERROR: GW_KEY required")
print ("See https://www.thethingsnetwork.org/docs/gateways/registration.html#via-gateway-connector")
sys.exit(0)
print ("*******************")
print ("*** Fetching config from TTN account server")
print ("*******************")
# Fetch the URL, if it fails try 30 seconds later again.
config_response = ""
try:
req = urllib2.Request('https://account.thethingsnetwork.org/gateways/'+os.environ.get("GW_ID"))
req.add_header('Authorization', 'Key '+os.environ.get("GW_KEY"))
response = urllib2.urlopen(req, timeout=30)
config_response = response.read()
except urllib2.URLError as err:
print ("Unable to fetch configuration from TTN. Are your GW_ID and GW_KEY correct?")
sys.exit(0)
# Parse config
ttn_config = {}
try:
ttn_config = json.loads(config_response)
except:
print ("Unable to parse configuration from TTN")
sys.exit(0)
frequency_plan = ttn_config.get('frequency_plan', "EU_863_870")
frequency_plan_url = ttn_config.get('frequency_plan_url', "https://account.thethingsnetwork.org/api/v2/frequency-plans/EU_863_870")
if "router" in ttn_config:
router = ttn_config['router'].get('mqtt_address', "mqtt://router.dev.thethings.network:1883")
router = urlparse.urlparse(router)
router = router.hostname # mp_pkt_fwd only wants the hostname, not the protocol and port
else:
router = "router.dev.thethings.network"
if "attributes" in ttn_config:
description = ttn_config['attributes'].get('description', "")
placement = ttn_config['attributes'].get('placement', "unknown")
if "antenna_location" in ttn_config:
latitude = ttn_config['antenna_location'].get('latitude', 0)
longitude = ttn_config['antenna_location'].get('longitude', 0)
altitude = ttn_config['antenna_location'].get('altitude', 0)
fallback_routers = []
if "fallback_routers" in ttn_config:
for fb_router in ttn_config["fallback_routers"]:
if "mqtt_address" in fb_router:
fallback_routers.append(fb_router["mqtt_address"])
print ("Gateway ID:\t"+os.environ.get("GW_ID"))
print ("Gateway Key:\t"+os.environ.get("GW_KEY"))
print ("Frequency plan:\t\t"+frequency_plan)
print ("Frequency plan url:\t"+frequency_plan_url)
print ("Gateway description:\t"+description)
print ("Gateway placement:\t"+placement)
print ("Router:\t\t\t"+router)
print ("")
print ("Fallback routers:")
for fb_router in fallback_routers:
print ("\t"+fb_router)
# Done fetching config from TTN
else:
print ("TTN gateway connector disabled. Not fetching config from account server.")
print ("Latitude:\t\t"+str(latitude))
print ("Longitude:\t\t"+str(longitude))
print ("Altitude:\t\t"+str(altitude))
print ("Gateway EUI:\t"+my_eui)
print ("Has hardware GPS:\t"+str(os.getenv('GW_GPS', False)))
print ("Hardware GPS port:\t"+os.getenv('GW_GPS_PORT', "/dev/ttyAMA0"))
# Retrieve global_conf
sx1301_conf = {}
try:
response = urllib2.urlopen(frequency_plan_url, timeout=30)
global_conf = response.read()
global_conf_object = json.loads(global_conf)
if('SX1301_conf' in global_conf_object):
sx1301_conf = global_conf_object['SX1301_conf']
except urllib2.URLError as err:
print ("Unable to fetch global conf from Github")
sys.exit(0)
sx1301_conf['antenna_gain'] = float(os.getenv('GW_ANTENNA_GAIN', 0))
# Build local_conf
gateway_conf = {}
gateway_conf['gateway_ID'] = my_eui
gateway_conf['contact_email'] = os.getenv('GW_CONTACT_EMAIL', "")
gateway_conf['description'] = description
gateway_conf['stat_file'] = 'loragwstat.json'
if(os.getenv('GW_LOGGER', "false")=="true"):
gateway_conf['logger'] = True
else:
gateway_conf['logger'] = False
if(os.getenv('GW_FWD_CRC_ERR', "false")=="true"):
#default is False
gateway_conf['forward_crc_error'] = True
if(os.getenv('GW_FWD_CRC_VAL', "true")=="false"):
#default is True
gateway_conf['forward_crc_valid'] = False
# Parse GW_GPS env var. It is a string, we need a boolean.
if(os.getenv('GW_GPS', "false")=="true"):
gw_gps = True
else:
gw_gps = False
# Use hardware GPS
if(gw_gps):
print ("Using real GPS")
gateway_conf['gps'] = True
gateway_conf['fake_gps'] = False
gateway_conf['gps_tty_path'] = os.getenv('GW_GPS_PORT', "/dev/ttyAMA0")
# Use fake GPS with coordinates from TTN
elif(gw_gps==False and latitude!=0 and longitude!=0):
print ("Using fake GPS")
gateway_conf['gps'] = True
gateway_conf['fake_gps'] = True
gateway_conf['ref_latitude'] = float(latitude)
gateway_conf['ref_longitude'] = float(longitude)
gateway_conf['ref_altitude'] = float(altitude)
# No GPS coordinates
else:
print ("Not sending coordinates")
gateway_conf['gps'] = False
gateway_conf['fake_gps'] = False
# Add server configuration
gateway_conf['servers'] = []
# Add TTN server
if(os.getenv('SERVER_TTN', "true")=="true"):
server = {}
server['serv_type'] = "ttn"
server['server_address'] = router
server['server_fallbacks'] = fallback_routers
server['serv_gw_id'] = os.environ.get("GW_ID")
server['serv_gw_key'] = os.environ.get("GW_KEY")
server['serv_enabled'] = True
gateway_conf['servers'].append(server)
else:
if(os.getenv('SERVER_0_ENABLED', "false")=="true"):
server = {}
if(os.getenv('SERVER_0_TYPE', "semtech")=="ttn"):
server['serv_type'] = "ttn"
server['serv_gw_id'] = os.environ.get("SERVER_0_GWID")
server['serv_gw_key'] = os.environ.get("SERVER_0_GWKEY")
server['server_address'] = os.environ.get("SERVER_0_ADDRESS")
server['serv_port_up'] = int(os.getenv("SERVER_0_PORTUP", 1700))
server['serv_port_down'] = int(os.getenv("SERVER_0_PORTDOWN", 1700))
server['serv_enabled'] = True
if(os.getenv('SERVER_0_DOWNLINK', "false")=="true"):
server['serv_down_enabled'] = True
else:
server['serv_down_enabled'] = False
gateway_conf['servers'].append(server)
# Add up to 3 additional servers
if(os.getenv('SERVER_1_ENABLED', "false")=="true"):
server = {}
if(os.getenv('SERVER_1_TYPE', "semtech")=="ttn"):
server['serv_type'] = "ttn"
server['serv_gw_id'] = os.environ.get("SERVER_1_GWID")
server['serv_gw_key'] = os.environ.get("SERVER_1_GWKEY")
server['server_address'] = os.environ.get("SERVER_1_ADDRESS")
server['serv_port_up'] = int(os.getenv("SERVER_1_PORTUP", 1700))
server['serv_port_down'] = int(os.getenv("SERVER_1_PORTDOWN", 1700))
server['serv_enabled'] = True
if(os.getenv('SERVER_1_DOWNLINK', "false")=="true"):
server['serv_down_enabled'] = True
else:
server['serv_down_enabled'] = False
gateway_conf['servers'].append(server)
if(os.getenv('SERVER_2_ENABLED', "false")=="true"):
server = {}
if(os.getenv('SERVER_2_TYPE', "semtech")=="ttn"):
server['serv_type'] = "ttn"
server['serv_gw_id'] = os.environ.get("SERVER_2_GWID")
server['serv_gw_key'] = os.environ.get("SERVER_2_GWKEY")
server['server_address'] = os.environ.get("SERVER_2_ADDRESS")
server['serv_port_up'] = int(os.getenv("SERVER_2_PORTUP", 1700))
server['serv_port_down'] = int(os.getenv("SERVER_2_PORTDOWN", 1700))
server['serv_enabled'] = True
if(os.getenv('SERVER_2_DOWNLINK', "false")=="true"):
server['serv_down_enabled'] = True
else:
server['serv_down_enabled'] = False
gateway_conf['servers'].append(server)
if(os.getenv('SERVER_3_ENABLED', "false")=="true"):
server = {}
if(os.getenv('SERVER_3_TYPE', "semtech")=="ttn"):
server['serv_type'] = "ttn"
server['serv_gw_id'] = os.environ.get("SERVER_3_GWID")
server['serv_gw_key'] = os.environ.get("SERVER_3_GWKEY")
server['server_address'] = os.environ.get("SERVER_3_ADDRESS")
server['serv_port_up'] = int(os.getenv("SERVER_3_PORTUP", 1700))
server['serv_port_down'] = int(os.getenv("SERVER_3_PORTDOWN", 1700))
server['serv_enabled'] = True
if(os.getenv('SERVER_3_DOWNLINK', "false")=="true"):
server['serv_down_enabled'] = True
else:
server['serv_down_enabled'] = False
gateway_conf['servers'].append(server)
# We merge the json objects from the global_conf and local_conf and save it to the global_conf.
# Therefore there will not be a local_conf.json file.
local_conf = {'SX1301_conf': sx1301_conf, 'gateway_conf': gateway_conf}
with open('/opt/ttn-gateway/global_conf.json', 'w') as the_file:
the_file.write(json.dumps(local_conf, indent=4))
# Endless loop to reset and restart packet forwarder
while True:
# Reset the gateway board - this only works for the Raspberry Pi.
GPIO.setmode(GPIO.BOARD) # hardware pin numbers, just like gpio -1
if (os.environ.get("GW_RESET_PIN")!=None):
try:
pin_number = int(os.environ.get("GW_RESET_PIN"))
print ("[TTN Gateway]: Resetting concentrator on pin "+str(os.environ.get("GW_RESET_PIN")))
GPIO.setup(pin_number, GPIO.OUT, initial=GPIO.LOW)
GPIO.output(pin_number, 0)
time.sleep(0.1)
GPIO.output(pin_number, 1)
time.sleep(0.1)
GPIO.output(pin_number, 0)
time.sleep(0.1)
GPIO.input(pin_number)
GPIO.cleanup(pin_number)
except ValueError:
print ("Can't interpret "+os.environ.get("GW_RESET_PIN")+" as a valid pin number.")
else:
print ("[TTN Gateway]: Resetting concentrator on default pin 22.")
GPIO.setup(22, GPIO.OUT, initial=GPIO.LOW)
GPIO.output(22, 0)
time.sleep(0.1)
GPIO.output(22, 1)
time.sleep(0.1)
GPIO.output(22, 0)
time.sleep(0.1)
GPIO.input(22)
GPIO.cleanup(22)
# Start forwarder
subprocess.call("/opt/ttn-gateway/mp_pkt_fwd -c /opt/ttn-gateway", shell=True)
time.sleep(15)