How to add Dragino Lora/gps HAT to V3

see dragino.toml file below

# dragino.toml
#
# The [TTN] section here contains only default starting values/keys for which are used to create cache.json, if it doesn't exist,
# when the code first runs.
#
# After joining TTN, cache.json is updated with the TTN supplied devaddr and keys etc.
#
#
# if auth_mode="OTAA":-
# If cache.json does not exist the code will try to send a JOIN_REQUEST only if devaddr = [0x00, 0x00,x00,x00]
# However, if cache.json exists the code will use the values therein and not try to send a JOIN_REQUEST
#
# if auth_mode="ABP":-
# you must provide all the TTN keys etc in the [TTN.ABP] section


[GPSD]
	threaded=true		# non-blocking operation get_gps() will return last cached valid reading
	threadLoopDelay=0.5	# number of seconds to check GPS for a valid reading

[TTN]
	# uplink frequency is randomly selected
	# warning MOST values may be modified by downlink MAC commands
	# from the server and will be cached
	
	mac_cache="cache.json"
	device_class="A"			# class B & C not yet supported

	frequency_plan = "EU_863_870_TTN"


	# radio settings
	max_power = 0x0F  			# can be changed by MAC command linkADR
	output_power = 0x0E			# current level
	sync_word = 0x34
	rx_crc = true
	
	# if the device does not recieve a msg in RX1 or RX2 then
	# send the same message again
	# TTN ignores repeated messages
	
	join_retries = 3
	join_timeout = 10	# time to wait after tx before next retry

	# initial data rate setting
	# MAC commands may change this
	
	data_rate = 3
	
	# RX1 is normally the same as the uplink settings
	# but can be changed by MAC commands
	rx1_delay=5
	rx1_DR=3
	
	
	# In EU RX2 is normally a fixed frequency & datarate
	# but can be modified by MAC commands
	rx2_DR=3
	rx2_frequency=869.525
	rx2_delay=1				# follows from end of rx1_delay
	
	rx_window=1				# duration of receive windows
	
	# these are updated on each transaction and cached by MAChandler
	fCntUp=0				# initial uplink message frame counters
	fCntDn=0				# set by TTN server

	auth_mode = "OTAA"

	# if not using ABP or OTAA you can omit the unused section
    # keys are MSB first order
	
	[TTN.OTAA]
		# received session key values are stored by MAChandler
		# enter your OTAA keys here
		deveui = [0xe4,0x5f,0x01,0xff,0xff,0x86,0x7c,0x40]
		appeui = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
		appkey = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01]
		devaddr= [0x00, 0x00, 0x00, 0x00] # not joined
	[TTN.ABP]
		# enter your device ABP key values below
		devaddr = [0x00,0x00,0x00,0x00]
		nwskey = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
		appskey = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
		appkey = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
		appeui = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] 
		deveui = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]

# frequency plans
# max of 16 channels supported in some regions.
# read the LoRaWAN Regional Parameters Specification if you want to create
# your own frequency plan - provided it works in a similar way
# the US902-928 plan uses 71 uplink channels but only 8 downlink channels

[EU_863_870_TTN]

	max_channels=8
	max_dr_offset=5 # see rx1_DR_offset_table
	max_dr_index=7	# see data_rates list
	
	# ref LoRa Alliance specification Physical Layer para 7.1
	#
    # In UK join frequencies are a restricted selection of lora_tx_freqs
	lora_join_freqs=[868.1,868.3,868.5]
	lora_tx_freqs = [868.1,868.3,868.5,867.1,867.3,867.5,868.7,867.9]
    # in UK the RX1 frequency is the same as the TX freq but that
	# may not be the case in your frequency plan
	lora_rx1_freqs=[868.1,868.3,868.5,867.1,867.3,867.5,868.7,867.9]

	TXPower=[20,14,11,8,5,2] # 6->15 are RFU

	# all the possible bandwidths however only 125 & 250 are used. See data_rates
	# the radio method set_bw(index) uses the position of the value (0..9)
	# TOML doesn't like mixed int and float
	bandwidths=[7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0] # kHz
	
	# DR0 to DR6 (sf,bw) where bw is 0-9 (see bandwidths above)
	data_rates=[[12,7],[11,7],[10,7],[9,7],[8,7],[7,7],[7,8]]

	sf_range=[7,12]

	duty_cycle_range=[0.1,1.0]	# min/max all floats
	
	# ETSI sub bands
	# These sub bands have different maxEIRP and Duty Cycles though TTN only uses
	# the first entry
	# (minFreq,maxFreq,maxDC)
	duty_cycle_table=[[863.0,868.0,1.0],[868.0,868.6,1.0],[868.7,869.2,0.1],[869.4,869.65,10.0],[869.7,870.0,1.0]]
	
	# rows range DR0 to DR7
	# columns are offsets 0..5
	DR_offset_table=[
		[0,0,0,0,0,0],
		[1,0,0,0,0,0],
		[2,1,0,0,0,0],
		[3,2,1,0,0,0],
		[4,3,2,1,0,0],
		[5,4,3,2,1,0],
		[6,5,4,3,2,1],
		[7,6,5,4,3,2]
	]
	
	# EIRP db settings
	maxEIRP=[8,10,12,13,14,16,18,20,21,24,26,27,29,30,33,36]

[AU_915_928_FSB_2]

	max_channels=8
	max_dr_offset=5 # see rx1_DR_offset_table
	max_dr_index=14	# see data_rates list

	# ref LoRa Alliance specification Physical Layer para 7.1
	#
    # In UK join frequencies are a restricted selection of lora_tx_freqs
	lora_join_freqs = [916.8,917.0,917.2,917.4,917.6,917.8,918.0,918.2]
	lora_tx_freqs = [916.8,917.0,917.2,917.4,917.6,917.8,918.0,918.2]

    # in UK the RX1 frequency is the same as the TX freq but that
	# may not be the case in your frequency plan
	lora_rx1_freqs=[923.3,923.9,924.5,925.1,925.7,926.3,926.9,927.5]

	TXPower=[20,14,11,8,5,2] # 6->15 are RFU

	# all the possible bandwidths however only 125 & 250 are used. See data_rates
	# the radio method set_bw(index) uses the position of the value (0..9)
	# TOML doesn't like mixed int and float
	bandwidths=[7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0] # kHz

	# DR0 to DR6 (sf,bw) where bw is 0-9 (see bandwidths above)
	data_rates=[
			[12,7],  # ch 0
			[11,7],
			[10,7],
			[9,7],
			[8,7],
			[7,7],
			[8,9],
			[8,9,],	#rfu
			[12,9],
			[11,9],
			[10,9],
			[9,9],  # ch11 RX2/RX1
			[8,9],
			[7,9],
			[7,9],   #rfu
			[7,9],   #rfu
			]

	sf_range=[7,12]

	# no limit in Australia
	duty_cycle_range=[0.0,100.0]	# min/max all floats, the code does not enforce

	# ETSI sub bands
	# These sub bands have different maxEIRP and Duty Cycles though TTN only uses
	# the first entry
	# in AUSTRALIA there is no duty cycle limit
	# (minFreq,maxFreq,maxDC)
	duty_cycle_table=[[916.8,918.2,100.0]] # the code checks but does not enforce

	# rows range DR0 to DR7
	# columns are offsets 0..5
	DR_offset_table=[
	   [8,8,8,8,8,8],
	   [9,8,8,8,8,8],
	   [10,9,8,8,8,8],
	   [11,10,9,8,8,8],
	   [12,11,10,9,8,8],
	   [13,12,11,10,9,8],
	   [13,13,12,11,10,9],
	   [9,8,8,8,8,8]
	]


	# EIRP db settings
	maxEIRP=[8,10,12,13,14,16,18,20,21,24,26,27,29,30,33,36]

Is anybody using a decent replacement for the Dragino lora/gps hat for the pi? It is no longer available to buy so looking for something new.

I made one with just an RFM95 on a breadboard HAT - no GPS - it works. Check board_config.py for CS & DIOx pins. The DTOverlay determines the SPI pins IIRC

The wiring is simple, it’s just SPI but they chose BCOM pin 2 for the CS.

I made a KiCAD pcb design (not actual PCB) for a dragino with GPS. This will give you the pins.

Without GPS you only need RST,MOSI,MISO,SCK,DIO0,DIO1,DIO2,NSS

1 Like

Not in Aus but Raspberry Pi LoRa/GPS HAT - RobotShop is expecting 868 versions in on 12th Sept. So ought to be available. They are being made by seedstudio.

I thought the sx127x would run at any frequency 100-1000mhz so I don’t know why they are sold as 868 maybe its just marketting.

1 Like

RF components on the output side usually - 868 & 915 are generally close enough for a non-optimal board.

Uputronics have a board for both full size & zero formats:

Totally recommended.

1 Like

I might buy one…next month…to investigate it.

I am seeing a reoccurrence of this issue again. Last time I changed the dragino board and the problem resolved itself. I have now changed out two dragino boards and the problem still occurs running the ./test.py command. Of course it could be two faulty boards but these were both new and working ok before suddenly stopped with this error.

any ideas much appreciated!!

pi@unit003:~/water-quality-lorawan-dragino-pi/dragino $ ./test.py
Mode <- SLEEP
Mode <- FSK_STDBY
Traceback (most recent call last):
  File "./test.py", line 20, in <module>
    D = Dragino("dragino.toml", logging_level=logLevel)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/dragino.py", line 85, in __init__
    super(Dragino, self).__init__() # LoRa init
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 100, in __init__
    self.rx_chain_calibration(calibration_freq)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 854, in rx_chain_calibration
    self.set_mode(op_mode_bkup)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 221, in set_mode
    sys.stderr.write("Mode <- %s\n" % MODE.lookup[mode])
KeyError: 0

I replied to this at least two weeks ago on a PM reply to your PM but I have not heard back. Have you fixed it.

For the benefit of other readers:-

Somewhere in your version of the code you are trying to put the RFM9x to sleep using mode 0 (Key Error: 0), which isn’t a LoRa sleep mode listed in Constants.py. It should be 0x81 (i.e. MODE.SLEEP is defined in Constants.py as 0x81 not 0x00).

MODE.lookup[mode])

Would then return a string and not fall over.

sorry I never saw your dm reply.

The workaround I used was to swap out the dragino board and this worked for a couple of days before the problem reappeared. I then swapped out the board for the orginal one and this worked again (and is still working).

I looked at the code in LoRa.py and see this section with MODE.lookup[mode]) but I dont see this defined as 0x00.

In Constant.py the MODE.SLEEP is defined as 0X80

So I suppose I cannot find what you are saying needs to be changed, what am I doing wrong?

    def set_mode(self, mode):
        """ Set the mode
        :param mode: Set the mode. Use constants.MODE class
        :return:    New mode
        """
        # the mode is backed up in self.mode
        if mode == self.mode:
            return mode
        if self.verbose:
            sys.stderr.write("Mode <- %s\n" % MODE.lookup[mode])
        self.mode = mode
        return self.spi.xfer([REG.LORA.OP_MODE | 0x80, mode])[1]

When writing to the SX127x mode register (0x01)

0x80 = Sleep
0x81 = STDBY (Idle)

This should mask the problem but it would be better to know where the code is getting a mode value of 0 from when it should be 0x80

sys.stderr.write("Mode <- %s\n" % MODE.lookup[mode | 0x80])

or we could try raising an exception which should give a traceback showing where the value of mode was set to zero.

 def set_mode(self, mode):
        """ Set the mode
        :param mode: Set the mode. Use constants.MODE class
        :return:    New mode
        """
        # the mode is backed up in self.mode
        if mode == self.mode:
            return mode

      if mode==0:
            raise Exception("Invalid mode value")

        if self.verbose:
            sys.stderr.write("Mode <- %s\n" % MODE.lookup[mode])
        self.mode = mode
        return self.spi.xfer([REG.LORA.OP_MODE | 0x80, mode])[1]

I find it hard to believe the Dragino RFM95’s are developing faults.

I will add raising an exception and let you know what happens. I have three units now and all have behaved themselves since the last board swap. Thanks very much for your help here.

Yes do let me know. I’m wondering if its an uninitialised value.

I’m working on conversion to CircuitPython and have LoRaWAN running on Pico+rfm95 talking to TTN. I’m doing testing and documenting it. If the problem is buried in the code it would affect the new setup too.

A dumb question but is it possible to send a remote boot command over lorawan to the raspberry pi end device which has the dragino board installed?

Not dumb. One of my projects was configured to reboot if a certain downlink message was received.
You could use port numbers.
There are a number of ways to do it. If you use systemd you can configure your task to auto start if it stops. Then send a downlink which tells your task to exit.

1 Like

hi
The KeyError: 0 problem returned and I have implemented the change in LoRa.py to raise the ecception. I am not sure if the output sheds any light on the issue

The code you provided I had to modify as it raised an indent issue(which I thought was strange ) when run. My LoRa.py is below.

Below is the output after running the test.py.

pi@unit002B:~/water-quality-lorawan-dragino-pi/dragino $ python3 test.py
Mode <- SLEEP
Mode <- FSK_STDBY
Traceback (most recent call last):
  File "test.py", line 20, in <module>
    D = Dragino("dragino.toml", logging_level=logLevel)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/dragino.py", line 85, in __init__
    super(Dragino, self).__init__() # LoRa init
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 100, in __init__
    self.rx_chain_calibration(calibration_freq)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 860, in rx_chain_calibration
    self.set_mode(op_mode_bkup)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 223, in set_mode
    raise Exception("Invalid mode value")
Exception: Invalid mode value
pi@unit002B:~/water-quality-lorawan-dragino-pi/dragino $

LoRa.py

    def set_mode(self, mode):
        """ Set the mode
        :param mode: Set the mode. Use constants.MODE class
        :return:    New mode
        """
        # the mode is backed up in self.mode
        if mode == self.mode:
            return mode

        if mode==0:
            raise Exception("Invalid mode value")

        if self.verbose:
            sys.stderr.write("Mode <- %s\n" % MODE.lookup[mode])
        self.mode = mode
        return self.spi.xfer([REG.LORA.OP_MODE | 0x80, mode])[1]

I’m wondering if it is a race condition in rx_chain_calibration() which stores the current mode then attempts to restore it with

self.set_mode(op_mode_bkup)

Mode 0x00 is FSK_SLEEP which is used by rx_chain_calibration().

Your code, below, will always raise an exception when FSK_SLEEP is attempted so remove both lines.

       if mode==0:
            raise Exception("Invalid mode value")

instead, lets put a valid entry in constants.py

@add_lookup
class MODE:
    SLEEP    = 0x80
    STDBY    = 0x81
    FSTX     = 0x82
    TX       = 0x83
    FSRX     = 0x84
    RXCONT   = 0x85
    RXSINGLE = 0x86
    CAD      = 0x87
    FSK_STDBY= 0x01     # needed for calibration

at the end add FSK_SLEEP=0x00

Now the code will print <–FSK_SLEEP instead of crashing.

Could you try that please.

Brian

Hi Brian
this is the error message I now receive after making the changes.

pi@unit002B:~/water-quality-lorawan-dragino-pi/dragino $ python3 test.py
Mode <- SLEEP
Mode <- FSK_STDBY
Mode <- FSK_SLEEP
Traceback (most recent call last):
  File "test.py", line 20, in <module>
    D = Dragino("dragino.toml", logging_level=logLevel)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/dragino.py", line 85, in __init__
    super(Dragino, self).__init__() # LoRa init
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 100, in __init__
    self.rx_chain_calibration(calibration_freq)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 856, in rx_chain_calibration
    self.set_freq(freq_bkup)
  File "/home/pi/water-quality-lorawan-dragino-pi/dragino/dragino/SX127x/LoRa.py", line 282, in set_freq
    assert self.mode == MODE.SLEEP or self.mode == MODE.STDBY or self.mode == MODE.FSK_STDBY
AssertionError

change the assert to

 assert self.mode == MODE.SLEEP or self.mode == MODE.STDBY or self.mode == MODE.FSK_STDBY or self.mode==MODE.FSK_SLEEP

It’s been a long while since I developed that code.

Brian