diff --git a/README.md b/README.md
index 706ac75..4e8a561 100755
--- a/README.md
+++ b/README.md
@@ -7,36 +7,49 @@ LoRa spread spectrum modulation.
Spread spectrum modulation has a number of intriguing features:
* High interference immunity
-* Up to 20dB link budget advantage
+* Up to 20dBm link budget advantage (for the SX1276/7/8/9)
* High Doppler shift immunity
-For examples of achieved ranges see the [references](#references) below.
+Links to some LoRa performance reports can be found in the [references](#references) section below.
# Motivation
Transceiver modules are usually interfaced with microcontroller boards such as the
-[Arduino](https://www.arduino.cc/) and there are already many fine C/C++ libraries for the SX128x family on
+[Arduino](https://www.arduino.cc/) and there are already many fine C/C++ libraries for the SX127x family on
[github](https://github.com/search?q=sx127x) and [mbed.org](https://developer.mbed.org/search/?q=sx127x).
Although C/C++ is the de facto standard for development on microcontrollers, [python](https://www.python.org)
running on a [Raspberry Pi](https://www.raspberrypi.org) is becoming a viable alternative for rapid prototyping.
+High level programming languages like python require a full-blown OS like Linux. (There are some exceptions like
+[PyMite](https://wiki.python.org/moin/PyMite) and most notably [MicroPython](https://micropython.org).)
+Using hardware capable of running Linux contradicts, to some extent, the low power specification of the SX127x family.
+Therefore it is clear that in most cases this approach is useful mostly for prototyping and technology testing.
+
+On the other hand prototyping on a full-blown OS using high level programming languages has several advantages:
+* Working prototypes can be built quickly
+* Technology testing ist faster
+* Proof of concept is easier to achieve
+* The application development phase is reached quicker
+
# Hardware
The transceiver module is a SX1276 based Modtronix [inAir9B](http://modtronix.com/inair9.html).
-It is mounted on a prototyping board to a [Raspberry Pi](https://www.raspberrypi.org) rev 2 model B.
+It is mounted on a prototyping board to a Raspberry Pi rev 2 model B.
-| board pin | RaspPi GPIO | Direction |
-|:-------------|:-----------:|:---------:|
-| inAir9B DIO0 | GPIO 21 | IN |
-| inAir9B DIO1 | GPIO 22 | IN |
-| inAir9B DIO2 | GPIO 23 | IN |
-| inAir9B DIO3 | GPIO 24 | IN |
-| LED | GPIO 25 | OUT |
+| Proto board pin | RaspPi GPIO | Direction |
+|:----------------|:-----------:|:---------:|
+| inAir9B DIO0 | GPIO 21 | IN |
+| inAir9B DIO1 | GPIO 22 | IN |
+| inAir9B DIO2 | GPIO 23 | IN |
+| inAir9B DIO3 | GPIO 24 | IN |
+| LED | GPIO 25 | OUT |
-@todo Add picture(s)
+Todo:
+- [ ] Add picture(s)
+- [ ] Wire the SX127x reset to a GPIO?
# Code Examples
@@ -71,6 +84,7 @@ class MyLoRa(LoRa):
def __init__(self, verbose=False):
super(MyLoRa, self).__init__(verbose)
+ # setup registers etc.
def on_rx_done(self):
payload = self.read_payload(nocheck=True)
@@ -85,7 +99,7 @@ class MyLoRa(LoRa):
* [spidev](https://pypi.python.org/pypi/spidev) for controlling SPI
-# API Reference
+# Class Reference
@todo
@@ -107,17 +121,40 @@ Execute `test_lora.py` to run a few unit tests.
# Contributors
-Please feel free to comment, report issues, or even contribute!
+Please feel free to comment, report issues, or contribute!
-Check out my company website [Mayer Analytics](http://mayeranalytics.com) and my private blog
-[mcmayer.net](http://mcmayer.net). Follow me on twitter [@markuscmayer](https://twitter.com/markuscmayer) and
+Contact me via my company website [Mayer Analytics](http://mayeranalytics.com) and my private blog
+[mcmayer.net](http://mcmayer.net).
+
+Follow me on twitter [@markuscmayer](https://twitter.com/markuscmayer) and
[@mayeranalytics](https://twitter.com/mayeranalytics).
-# Version
+# Version and future plans
**pySX127x** is still in the development phase. The current version is 0.1.
+95% of functions for the Sx127x LoRa capabilities are implemented. Functions will be added when necessary.
+The test coverage is rather low but we intend to change that soon.
+
+
+# References
+
+### Hardware references
+* [Semtech SX1276/77/78/79 - 137 MHz to 1020 MHz Low Power Long Range Transceiver](http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf)
+* [Modtronix inAir9](http://modtronix.com/inair9.html)
+* [Spidev Documentation](http://tightdev.net/SpiDev_Doc.pdf)
+* [Make: Tutorial: Raspberry Pi GPIO Pins and Python](http://makezine.com/projects/tutorial-raspberry-pi-gpio-pins-and-python/)
+
+### LoRa performance tests
+* [Extreme Range Links: LoRa 868 / 900MHz SX1272 LoRa module for Arduino, Raspberry Pi and Intel Galileo](https://www.cooking-hacks.com/documentation/tutorials/extreme-range-lora-sx1272-module-shield-arduino-raspberry-pi-intel-galileo/)
+* [UK LoRa versus FSK - 40km LoS (Line of Sight) test!](http://www.instructables.com/id/Introducing-LoRa-/step17/Other-region-tests/)
+
+### Spread spectrum modulation theory
+* [An Introduction to Spread Spectrum Techniques](http://www.ausairpower.net/OSR-0597.html)
+* [Theory of Spread-Spectrum Communications-A Tutorial](http://www.fer.unizg.hr/_download/repository/Theory%20of%20Spread-Spectrum%20Communications-A%20Tutorial.pdf)
+(technical paper)
+
# License
@@ -132,9 +169,4 @@ but **WITHOUT ANY WARRANTY**; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with **pySX127x**. If not, see .
-
-
-# References
-### Range Experiments
-* [Extreme Range Links: LoRa 868 / 900MHz SX1272 LoRa module for Arduino, Raspberry Pi and Intel Galileo](https://www.cooking-hacks.com/documentation/tutorials/extreme-range-lora-sx1272-module-shield-arduino-raspberry-pi-intel-galileo/)
+along with **pySX127x**. If not, see .
\ No newline at end of file
diff --git a/SX127x/LoRa.py b/SX127x/LoRa.py
index ce2499f..4b77a8a 100755
--- a/SX127x/LoRa.py
+++ b/SX127x/LoRa.py
@@ -24,10 +24,11 @@
def set_bit(v, index, bit):
""" Set the index:th bit of v to x, and return the new value.
- :param v: The integer to set the bit in
+ :param v: The integer to set the bit in
+ :type v: int
:param index: 0-based index
:param bit: bit to set (0 or 1)
- :return: Changed integer
+ :return: Changed integer
"""
mask = 1 << index
v &= ~mask
@@ -101,6 +102,8 @@ def on_payload_crc_error(self):
def on_fhss_change_channel(self):
pass
+ # Internal callbacks for GPIO.add_event_detect
+
def _dio0(self, channel):
# DIO0 00: RxDone
# DIO0 01: TxDone
@@ -148,12 +151,21 @@ def _dio3(self, channel):
else:
raise RuntimeError("unknown dio3 mapping!")
+ # All the set/get/read/write functions
+
def get_mode(self):
+ """ Get the mode
+ :return: New mode
+ """
self.mode = self.spi.xfer([REG.OP_MODE, 0])[1]
return self.mode
def set_mode(self, mode):
- # the mode is backe up in self.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:
@@ -162,22 +174,35 @@ def set_mode(self, mode):
return self.spi.xfer([REG.OP_MODE | 0x80, mode])[1]
def write_payload(self, payload):
+ """ Get FIFO ready for TX: Set FifoAddrPtr to FifoTxBaseAddr. The transceiver is put into STDBY mode.
+ :param payload: Payload to write (list)
+ :return: Written payload
+ """
self.set_mode(MODE.STDBY)
base_addr = self.get_fifo_tx_base_addr()
self.set_fifo_addr_ptr(base_addr)
return self.spi.xfer([REG.FIFO | 0x80] + payload)[1:]
def reset_ptr_rx(self):
- """ Get FIFO ready for RX: Set FifoAddrPtr to FifoRxBaseAddr """
+ """ Get FIFO ready for RX: Set FifoAddrPtr to FifoRxBaseAddr. The transceiver is put into STDBY mode. """
self.set_mode(MODE.STDBY)
base_addr = self.get_fifo_rx_base_addr()
self.set_fifo_addr_ptr(base_addr)
def rx_is_good(self):
+ """ Check the IRQ flags for RX errors
+ :return: True if no errors
+ :rtype: bool
+ """
flags = self.get_irq_flags()
return not any([flags[s] for s in ['valid_header', 'crc_error', 'rx_done', 'rx_timeout']])
def read_payload(self , nocheck = False):
+ """ Read the payload from FIFO
+ :param nocheck: If True then check rx_is_good()
+ :return: Payload
+ :rtype: list[int]
+ """
if not nocheck and not self.rx_is_good():
return None
rx_nb_bytes = self.get_rx_nb_bytes()
@@ -187,13 +212,21 @@ def read_payload(self , nocheck = False):
return payload
def get_freq(self):
- # returns freq in MHz
+ """ Get the frequency (MHz)
+ :return: Frequency in MHz
+ :rtype: float
+ """
msb, mid, lsb = self.spi.xfer([REG.FR_MSB, 0, 0, 0])[1:]
f = lsb + 256*(mid + 256*msb)
return f / 16384.
def set_freq(self, f):
- # f is a float in MHz
+ """ Set the frequency (MHz)
+ :param f: Frequency in MHz
+ "type f: float
+ :return: New register settings (3 bytes [msb, mid, lsb])
+ :rtype: list[int]
+ """
assert self.mode == MODE.SLEEP or self.mode == MODE.STDBY
i = int(f * 16384.) # choose floor
msb = i // 65536
@@ -201,7 +234,7 @@ def set_freq(self, f):
mid = i // 256
i -= mid * 256
lsb = i
- self.spi.xfer([REG.FR_MSB | 0x80, msb, mid, lsb])
+ return self.spi.xfer([REG.FR_MSB | 0x80, msb, mid, lsb])
def get_pa_config(self, convert_dBm=False):
v = self.spi.xfer([REG.PA_CONFIG, 0])[1]
@@ -218,7 +251,8 @@ def get_pa_config(self, convert_dBm=False):
)
def set_pa_config(self, pa_select=None, max_power=None, output_power=None):
- assert pa_select is None or pa_select == 0 # the inAir9 uses the RFO pin, so I don't allow to switch to PA_BOOST
+ # the inAir9 uses the RFO pin, so I don't allow to switch to PA_BOOST
+ assert pa_select is None or pa_select == 0
loc = locals()
current = self.get_pa_config()
loc = {s: current[s] if loc[s] is None else loc[s] for s in loc}
@@ -323,8 +357,9 @@ def get_irq_flags_mask(self):
cad_detected = v >> 0 & 0x01,
)
- def set_irq_flags_mask(self, rx_timeout=None, rx_done=None, crc_error=None, valid_header=None, tx_done=None,
- cad_done=None, fhss_change_ch=None, cad_detected=None):
+ def set_irq_flags_mask(self,
+ rx_timeout=None, rx_done=None, crc_error=None, valid_header=None, tx_done=None,
+ cad_done=None, fhss_change_ch=None, cad_detected=None):
loc = locals()
v = self.spi.xfer([REG.IRQ_FLAGS_MASK, 0])[1]
for i, s in enumerate(['cad_detected', 'fhss_change_ch', 'cad_done', 'tx_done', 'valid_header',
@@ -347,8 +382,9 @@ def get_irq_flags(self):
cad_detected = v >> 0 & 0x01,
)
- def set_irq_flags(self, rx_timeout=None, rx_done=None, crc_error=None, valid_header=None, tx_done=None,
- cad_done=None, fhss_change_ch=None, cad_detected=None):
+ def set_irq_flags(self,
+ rx_timeout=None, rx_done=None, crc_error=None, valid_header=None, tx_done=None,
+ cad_done=None, fhss_change_ch=None, cad_detected=None):
v = self.spi.xfer([REG.IRQ_FLAGS, 0])[1]
for i, s in enumerate(['cad_detected', 'fhss_change_ch', 'cad_done', 'tx_done', 'valid_header',
'crc_error', 'rx_done', 'rx_timeout']):
@@ -555,49 +591,133 @@ def set_sync_word(self, sync_word):
@Getter(REG.DIO_MAPPING_1)
def get_dio_mapping_1(self, mapping):
- self.dio_mapping = [mapping>>6 & 0x03, mapping>>4 & 0x03, mapping>>2 & 0x03, mapping>>0 & 0x03] + self.dio_mapping[4:6]
- return mapping
+ """ Get mapping of pins DIO0 to DIO3. Object variable dio_mapping will be set.
+ :param mapping: Register value
+ :type mapping: int
+ :return: Value of the mapping list
+ :rtype: list[int]
+ """
+ self.dio_mapping = [mapping>>6 & 0x03, mapping>>4 & 0x03, mapping>>2 & 0x03, mapping>>0 & 0x03] \
+ + self.dio_mapping[4:6]
+ return self.dio_mapping
@Setter(REG.DIO_MAPPING_1)
def set_dio_mapping_1(self, mapping):
- self.dio_mapping = [mapping>>6 & 0x03, mapping>>4 & 0x03, mapping>>2 & 0x03, mapping>>0 & 0x03] + self.dio_mapping[4:6]
+ """ Set mapping of pins DIO0 to DIO3. Object variable dio_mapping will be set.
+ :param mapping: Register value
+ :type mapping: int
+ :return: New value of the register
+ :rtype: int
+ """
+ self.dio_mapping = [mapping>>6 & 0x03, mapping>>4 & 0x03, mapping>>2 & 0x03, mapping>>0 & 0x03] \
+ + self.dio_mapping[4:6]
return mapping
@Getter(REG.DIO_MAPPING_2)
def get_dio_mapping_2(self, mapping):
+ """ Get mapping of pins DIO4 to DIO5. Object variable dio_mapping will be set.
+ :param mapping: Register value
+ :type mapping: int
+ :return: Value of the mapping list
+ :rtype: list[int]
+ """
self.dio_mapping = self.dio_mapping[0:4] + [mapping>>6 & 0x03, mapping>>4 & 0x03]
- return mapping
+ return self.dio_mapping
@Setter(REG.DIO_MAPPING_2)
def set_dio_mapping_2(self, mapping):
+ """ Set mapping of pins DIO4 to DIO5. Object variable dio_mapping will be set.
+ :param mapping: Register value
+ :type mapping: int
+ :return: New value of the register
+ :rtype: int
+ """
assert mapping & 0b00001110 == 0
self.dio_mapping = self.dio_mapping[0:4] + [mapping>>6 & 0x03, mapping>>4 & 0x03]
return mapping
+ def get_dio_mapping(self):
+ """ Utility function that returns the list of current DIO mappings. Object variable dio_mapping will be set.
+ :return: List of current DIO mappings
+ :rtype: list[int]
+ """
+ self.get_dio_mapping_1()
+ return self.get_dio_mapping_2()
+
+ def set_dio_mapping(self, mapping):
+ """ Utility function that returns the list of current DIO mappings. Object variable dio_mapping will be set.
+ :param mapping: DIO mapping list
+ :type mapping: list[int]
+ :return: New DIO mapping list
+ :rtype: list[int]
+ """
+ mapping_1 = (mapping[0] & 0x03) << 6 | (mapping[1] & 0x03) << 4 | (mapping[2] & 0x3) << 2 | mapping[3] & 0x3
+ mapping_2 = (mapping[4] & 0x03) << 6 | (mapping[5] & 0x03) << 4
+ self.set_dio_mapping_1(mapping_1)
+ return self.set_dio_mapping_2(mapping_2)
+
@Getter(REG.VERSION)
def get_version(self, version):
+ """ Version code of the chip.
+ Bits 7-4 give the full revision number; bits 3-0 give the metal mask revision number.
+ :return: Version code
+ :rtype: int
+ """
return version
@Getter(REG.TCXO)
def get_tcxo(self, tcxo):
- return tcxo
+ """ Get TCXO or XTAL input setting
+ 0 -> "XTAL": Crystal Oscillator with external Crystal
+ 1 -> "TCXO": External clipped sine TCXO AC-connected to XTA pin
+ :param tcxo: 1=TCXO or 0=XTAL input setting
+ :return: TCXO or XTAL input setting
+ :type: int (0 or 1)
+ """
+ return tcxo & 0b00010000
@Setter(REG.TCXO)
def set_tcxo(self, tcxo):
- assert tcxo & 0b11101111 == 0b00001001
- return tcxo
+ """ Make TCXO or XTAL input setting.
+ 0 -> "XTAL": Crystal Oscillator with external Crystal
+ 1 -> "TCXO": External clipped sine TCXO AC-connected to XTA pin
+ :param tcxo: 1=TCXO or 0=XTAL input setting
+ :return: new TCXO or XTAL input setting
+ """
+ return (tcxo >= 1) << 4 | 0x09 # bits 0-3 must be 0b1001
@Getter(REG.PA_DAC)
def get_pa_dac(self, pa_dac):
- return pa_dac
+ """ Enables the +20dBm option on PA_BOOST pin
+ False -> Default value
+ True -> +20dBm on PA_BOOST when OutputPower=1111
+ :return: True/False if +20dBm option on PA_BOOST on/off
+ :rtype: bool
+ """
+ pa_dac &= 0x07 # only bits 0-2
+ if pa_dac == 0x04:
+ return False
+ elif pa_dac == 0x07:
+ return True
+ else:
+ raise RuntimeError("Bad PA_DAC value %s" % hex(pa_dac))
@Setter(REG.PA_DAC)
def set_pa_dac(self, pa_dac):
- assert pa_dac & 0b1111100 == 0b00010000
- return pa_dac
+ """ Enables the +20dBm option on PA_BOOST pin
+ False -> Default value
+ True -> +20dBm on PA_BOOST when OutputPower=1111
+ :param pa_dac: 1/0 if +20dBm option on PA_BOOST on/off
+ :return: New pa_dac register value
+ :rtype: int
+ """
+ return 0x87 if pa_dac else 0x84
def dump_registers(self):
- # returns a list of [reg_addr, reg_name, reg_value] tuples
+ """ Returns a list of [reg_addr, reg_name, reg_value] tuples. Chip is put into mode SLEEP.
+ :return: List of [reg_addr, reg_name, reg_value] tuples
+ :rtype: list[tuple]
+ """
self.set_mode(MODE.SLEEP)
values = self.get_all_registers()
skip_set = set([REG.FIFO])
@@ -678,17 +798,15 @@ def __str__(self):
s += " detect_optimize %#02x\n" % self.get_detect_optimize()
s += " detection_thresh %#02x\n" % self.get_detection_threshold()
s += " sync_word %#02x\n" % self.get_sync_word()
- s += " dio_mapping_1 %#02x\n" % self.get_dio_mapping_1()
- s += " dio_mapping_2 %#02x\n" % self.get_dio_mapping_2()
- s += " tcxo %#02x\n" % self.get_tcxo()
- s += " pa_dac %#02x\n" % self.get_pa_dac()
+ s += " dio_mapping 0..5 %s\n" % self.get_dio_mapping()
+ s += " tcxo %s\n" % ['XTAL', 'TCXO'][self.get_tcxo()]
+ s += " pa_dac %s\n" % ['default', 'PA_BOOST'][self.get_pa_dac()]
s += " fifo_addr_ptr %#02x\n" % self.get_fifo_addr_ptr()
s += " fifo_tx_base_addr %#02x\n" % self.get_fifo_tx_base_addr()
s += " fifo_rx_base_addr %#02x\n" % self.get_fifo_rx_base_addr()
s += " fifo_rx_curr_addr %#02x\n" % self.get_fifo_rx_current_addr()
s += " fifo_rx_byte_addr %#02x\n" % self.get_fifo_rx_byte_addr()
s += " status %s\n" % self.get_modem_status()
- s += " dio_mapping %s\n" % self.dio_mapping
s += " version %#02x\n" % self.get_version()
return s
diff --git a/SX127x/board_config.py b/SX127x/board_config.py
index e4efe2e..4bbbd78 100755
--- a/SX127x/board_config.py
+++ b/SX127x/board_config.py
@@ -38,7 +38,7 @@ def setup():
GPIO.output(BOARD.LED, 0)
# DIOx
for gpio_pin in [BOARD.DIO0, BOARD.DIO1, BOARD.DIO2, BOARD.DIO3]:
- GPIO.setup(gpio_pin, GPIO.IN)
+ GPIO.setup(gpio_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
@staticmethod
def led_on(value=1):
diff --git a/VERSION b/VERSION
new file mode 100755
index 0000000..ceab6e1
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.1
\ No newline at end of file
diff --git a/beacon.py b/beacon.py
index c3aa72a..6bc941e 100755
--- a/beacon.py
+++ b/beacon.py
@@ -28,60 +28,61 @@
parser = LoRaArgumentParser("A simple LoRa beacon")
parser.add_argument('--single', '-S', dest='single', default=False, action="store_true", help="Single transmission")
-parser.add_argument('--wait', '-w', dest='wait', default=0, action="store", type=float, help="Waiting time between transmissions (default is 0s)")
+parser.add_argument('--wait', '-w', dest='wait', default=1, action="store", type=float, help="Waiting time between transmissions (default is 0s)")
class LoRaBeacon(LoRa):
def __init__(self, verbose=False):
super(LoRaBeacon, self).__init__(verbose)
+ self.set_mode(MODE.SLEEP)
+ self.set_dio_mapping([1,0,0,0,0,0])
def on_rx_done(self):
print "\nRxDone"
- print lora.get_irq_flags()
- print map(hex, lora.read_payload(nocheck=True))
- lora.set_mode(MODE.SLEEP)
- lora.reset_ptr_rx()
- lora.set_mode(MODE.RXCONT)
+ print self.get_irq_flags()
+ print map(hex, self.read_payload(nocheck=True))
+ self.set_mode(MODE.SLEEP)
+ self.reset_ptr_rx()
+ self.set_mode(MODE.RXCONT)
def on_tx_done(self):
print "\nTxDone"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_cad_done(self):
print "\non_CadDone";
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_rx_timeout(self):
print "\non_RxTimeout"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_valid_header(self):
print "\non_ValidHeader"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_payload_crc_error(self):
print "\non_PayloadCrcError"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_fhss_change_channel(self):
print "\non_FhssChangeChannel"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def start(self):
global args
- self.set_dio_mapping_1(0b01000000)
counter = 0
while True:
- lora.write_payload([0x0f])
+ self.write_payload([0x0f])
BOARD.led_on()
- lora.set_mode(MODE.TX)
+ self.set_mode(MODE.TX)
counter += 1
sys.stdout.flush()
sys.stdout.write("\rtx #%d" % counter)
tx_done = False
while not tx_done:
- tx_done = lora.get_irq_flags()['tx_done']
+ tx_done = self.get_irq_flags()['tx_done']
BOARD.led_off()
if args.single:
break
@@ -107,8 +108,9 @@ def start(self):
#assert(lora.get_lna()['lna_gain'] == GAIN.NOT_USED)
assert(lora.get_agc_auto_on() == 1)
-print "simple_beacon parameters:"
-print " Wait\t%f s" % args.wait
+print "Beacon config:"
+print " Wait %f s" % args.wait
+print " Single tx = %s" % args.single
print
raw_input("Press enter to start...")
diff --git a/legacy/cad.py b/legacy/cad.py
index 165dd53..4a87673 100755
--- a/legacy/cad.py
+++ b/legacy/cad.py
@@ -3,6 +3,22 @@
# beacon.py
# awaits transmissions in RXSINGLE mode
+# This file is part of pySX127x.
+#
+# pySX127x is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pySX127x is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with pySX127x. If not, see .
+
+
import sys
from time import sleep
import SX127x
diff --git a/legacy/rcv_single.py b/legacy/rcv_single.py
index 7c17791..ac46fb7 100755
--- a/legacy/rcv_single.py
+++ b/legacy/rcv_single.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7
# This file is part of pySX127x.
#
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with pySX127x. If not, see .
+
from time import sleep
from SX127x.LoRa import *
from SX127x.board_config import BOARD
diff --git a/lora_util.py b/lora_util.py
index 0e2a0d0..3130fb6 100755
--- a/lora_util.py
+++ b/lora_util.py
@@ -1,7 +1,6 @@
-#!/usr/bin/env python
-
-""" This is utility script for the LoRa. It dumps all registers. """
+#!/usr/bin/env python2.7
+""" This is a utility script for the SX127x (LoRa mode). It dumps all registers. """
# This file is part of pySX127x.
#
diff --git a/rcv_cont.py b/rcv_cont.py
index 2c03f75..f809b80 100755
--- a/rcv_cont.py
+++ b/rcv_cont.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7
""" A simple continuous receiver class. """
@@ -18,7 +18,6 @@
# along with pySX127x. If not, see .
-import sys
from time import sleep
from SX127x.LoRa import *
from SX127x.LoRaArgumentParser import LoRaArgumentParser
@@ -32,48 +31,50 @@
class LoRaRcvCont(LoRa):
def __init__(self, verbose=False):
super(LoRaRcvCont, self).__init__(verbose)
+ self.set_mode(MODE.SLEEP)
+ self.set_dio_mapping([0] * 6)
def on_rx_done(self):
BOARD.led_on()
print "\nRxDone"
- print lora.get_irq_flags()
- print map(hex, lora.read_payload(nocheck=True))
- lora.set_mode(MODE.SLEEP)
- lora.reset_ptr_rx()
+ print self.get_irq_flags()
+ print map(hex, self.read_payload(nocheck=True))
+ self.set_mode(MODE.SLEEP)
+ self.reset_ptr_rx()
BOARD.led_off()
- lora.set_mode(MODE.RXCONT)
+ self.set_mode(MODE.RXCONT)
def on_tx_done(self):
print "\nTxDone"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_cad_done(self):
print "\non_CadDone";
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_rx_timeout(self):
print "\non_RxTimeout"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_valid_header(self):
print "\non_ValidHeader"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_payload_crc_error(self):
print "\non_PayloadCrcError"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def on_fhss_change_channel(self):
print "\non_FhssChangeChannel"
- print lora.get_irq_flags()
+ print self.get_irq_flags()
def start(self):
- lora.reset_ptr_rx()
- lora.set_mode(MODE.RXCONT)
+ self.reset_ptr_rx()
+ self.set_mode(MODE.RXCONT)
while True:
sleep(.5)
- rssi_value = lora.get_rssi_value()
- status = lora.get_modem_status()
+ rssi_value = self.get_rssi_value()
+ status = self.get_modem_status()
sys.stdout.flush()
sys.stdout.write("\r%d %d %d" % (rssi_value, status['rx_ongoing'], status['modem_clear']))
diff --git a/test_lora.py b/test_lora.py
index 77217c5..b03f087 100755
--- a/test_lora.py
+++ b/test_lora.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7
""" This script runs a small number of unit tests. """
@@ -32,8 +32,13 @@ def get_reg(reg_addr):
return lora.get_register(reg_addr)
-def SaveState(reg_addr, len=1):
- """ This decorator wraps a get/set_register around the function (unittest) call. """
+def SaveState(reg_addr, n_registers=1):
+ """ This decorator wraps a get/set_register around the function (unittest) call.
+ :param reg_addr: Start of register addresses
+ :param n_registers: Number of registers to save. (Useful for MSB/LSB register pairs, etc.)
+ :return:
+ """
+
def decorator(func):
def wrapper(self):
reg_bkup = lora.get_register(reg_addr)
@@ -58,6 +63,7 @@ def test_mode(self):
lora.set_mode(m)
self.assertEqual(lora.get_mode(), m)
+ @SaveState(REG.FR_MSB, n_registers=3)
def test_set_freq(self):
freq = lora.get_freq()
for f in [433.5, 434.5, 434.0, freq]:
@@ -78,13 +84,46 @@ def test_set_low_data_rate_optim(self):
lora.set_low_data_rate_optim(False)
self.assertEqual((get_reg(REG.MODEM_CONFIG_3) & 0b1000) >> 3, 0)
- def test_set_lna_gain(self):
- bkup_lna_gain = lora.get_lna()['lna_gain']
- for target_gain in [GAIN.NOT_USED, GAIN.G1, GAIN.G2, GAIN.G6, GAIN.NOT_USED, bkup_lna_gain]:
- print target_gain
- lora.set_lna_gain(target_gain)
- actual_gain = lora.get_lna()['lna_gain']
- self.assertEqual(GAIN.lookup[actual_gain], GAIN.lookup[target_gain])
+ @SaveState(REG.DIO_MAPPING_1, 2)
+ def test_set_dio_mapping(self):
+
+ dio_mapping = [1] * 6
+ lora.set_dio_mapping(dio_mapping)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_1), 0b01010101)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_2), 0b01010000)
+ self.assertEqual(lora.get_dio_mapping(), dio_mapping)
+
+ dio_mapping = [2] * 6
+ lora.set_dio_mapping(dio_mapping)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_1), 0b10101010)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_2), 0b10100000)
+ self.assertEqual(lora.get_dio_mapping(), dio_mapping)
+
+ dio_mapping = [0] * 6
+ lora.set_dio_mapping(dio_mapping)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_1), 0b00000000)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_2), 0b00000000)
+ self.assertEqual(lora.get_dio_mapping(), dio_mapping)
+
+ dio_mapping = [0,1,2,0,1,2]
+ lora.set_dio_mapping(dio_mapping)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_1), 0b00011000)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_2), 0b01100000)
+ self.assertEqual(lora.get_dio_mapping(), dio_mapping)
+
+ dio_mapping = [1,2,0,1,2,0]
+ lora.set_dio_mapping(dio_mapping)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_1), 0b01100001)
+ self.assertEqual(get_reg(REG.DIO_MAPPING_2), 0b10000000)
+ self.assertEqual(lora.get_dio_mapping(), dio_mapping)
+
+# def test_set_lna_gain(self):
+# bkup_lna_gain = lora.get_lna()['lna_gain']
+# for target_gain in [GAIN.NOT_USED, GAIN.G1, GAIN.G2, GAIN.G6, GAIN.NOT_USED, bkup_lna_gain]:
+# print target_gain
+# lora.set_lna_gain(target_gain)
+# actual_gain = lora.get_lna()['lna_gain']
+# self.assertEqual(GAIN.lookup[actual_gain], GAIN.lookup[target_gain])
if __name__ == '__main__':
unittest.main()