diff --git a/tests/pkg_semtech-loramac/Makefile b/tests/pkg_semtech-loramac/Makefile index 25ab1ecdf5..ad2f10c202 100644 --- a/tests/pkg_semtech-loramac/Makefile +++ b/tests/pkg_semtech-loramac/Makefile @@ -26,4 +26,12 @@ USEMODULE += fmt FEATURES_OPTIONAL += periph_eeprom +# Default IotLab Config to run the test +ifneq (,$(filter iotlab%,$(MAKECMDGOALS))) + IOTLAB_NODES ?= 1 + IOTLAB_TYPE ?= st-lrwan1:sx1276 + IOTLAB_SITE ?= saclay + include $(RIOTBASE)/dist/testbed-support/Makefile.iotlab +endif + include $(RIOTBASE)/Makefile.include diff --git a/tests/pkg_semtech-loramac/README.md b/tests/pkg_semtech-loramac/README.md index 1b369863f6..3da6d09fc5 100644 --- a/tests/pkg_semtech-loramac/README.md +++ b/tests/pkg_semtech-loramac/README.md @@ -206,3 +206,63 @@ The node will also print the data received: > loramac tx test Data received: This is RIOT! + +## Automatic test + +The automatic test replicates 11-lorawan release specs tests: + +- [11-lorawan](https://github.com/RIOT-OS/Release-Specs/blob/ba236c4a1d1258ab63d21b0a860d0f5a5935bbd4/11-lorawan/11-lorawan.md) + - [Task #02 - OTAA join procedure](https://github.com/RIOT-OS/Release-Specs/blob/ba236c4a1d1258ab63d21b0a860d0f5a5935bbd4/11-lorawan/11-lorawan.md#task-02---otaa-join-procedure) + - [Task #03 - ABP join procedure](https://github.com/RIOT-OS/Release-Specs/blob/ba236c4a1d1258ab63d21b0a860d0f5a5935bbd4/11-lorawan/11-lorawan.md#task-03---abp-join-procedure) + - [Task #04 - LoRaWAN device parameters persistence](https://github.com/RIOT-OS/Release-Specs/blob/master/11-lorawan/11-lorawan.md#task-04---lorawan-device-parameters-persistence) + +It is recommended to test using iotlab-nodes. The default configuration is already +set on the application Makefile. + +### Requirements + +- The tests assumes that there is a gateway in all DR distance to the device and the +device was flashed with the correct keys. The APPEUI is assumed to be the same for OTAA +and ABP. + +- The DR duty cycling time-offs values are for EU863-870 + +- To use iotlab it is required to have a valid account for the FIT IoT-LAB +(registration there is open for everyone) and the [iot-lab/cli-tools](https://github.com/iot-lab/cli-tools) need to be installed. + +- The frame counters must be reset on the LoRaWAN backend at the beginning of the +test. + +- iotlab uses TTN lorawan gateways, to run the test you will need to create an +[account](https://account.thethingsnetwork.org/), add an [application](https://www.thethingsnetwork.org/docs/applications/add.html) +and [register](https://www.thethingsnetwork.org/docs/devices/registration.html) +a device. Two devices must be registered, one configured for OTA and another +for ABP. For this test you need to take note of the Device EUI, Application EUI +& Application Key of the device registered for OTA, as well as the Device EUI, +Device Address, Network and Application session keys of the device registered +for ABP. The test assumes that both devices have the same Application EUI. + +### Usage + +1. flash device with appropriate keys and test + + $ DEVEUI_OTA=<...> DEVEUI_ABP=<...> APPEUI=<...> APPKEY=<...> DEVADDR=<...> NWKSKEY=<...> APPSKEY=<...> RX2_DR=<...> make BOARD=b-l072z-lrwan1 -C tests/pkg_semtech-loramac test + +#### With iotlab + +1. setup the iotlab experiment: + + $ make -C tests/pkg_semtech-loramac iotlab-exp + +2. flash device with the appropriate keys and test + + $ DEVEUI=<...> APPEUI=<...> APPKEY=<...> DEVADDR=<...> NWKSKEY=<...> APPSKEY=<...> RX2_DR=<...> IOTLAB_NODE=auto-ssh make -C tests/pkg_semtech-loramac flash test + +3. stop the iotlab experiment: + + $ make -C examples/lorawan/ iotlab-stop + +_note_: if you have multiple running experiments you will need to set `IOTLAB_EXP_ID` + to the appropriate experiment, when using the `iotlab-exp` you will see a: + `Waiting that experiment 175694 gets in state Running`. That number matches + the experiment id you started. \ No newline at end of file diff --git a/tests/pkg_semtech-loramac/tests/01-run.py b/tests/pkg_semtech-loramac/tests/01-run.py new file mode 100755 index 0000000000..eb284f29f3 --- /dev/null +++ b/tests/pkg_semtech-loramac/tests/01-run.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019 Inria +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import os +import sys +import time + +from testrunner import run + +# It's assumed that the same APPEUI is used for abp and otaa +DEVEUI_ABP = os.getenv('DEVEUI_ABP') +DEVEUI_OTA = os.getenv('DEVEUI_OTA') +APPEUI = os.getenv('APPEUI') +APPKEY = os.getenv('APPKEY') +DEVADDR = os.getenv('DEVADDR') +NWKSKEY = os.getenv('NWKSKEY') +APPSKEY = os.getenv('APPSKEY') + +# Default to things network RX2_DR +TEST_RX2_DR = os.getenv('RX2_DR', 3) + +# Theoretical duty cycling timeoff for EU863-870 +# https://www.semtech.com/uploads/documents/LoraDesignGuide_STD.pdf#page=7 +TEST_DATA_RATES = {"0": 164.6, "3": 20.6, "5": 6.2} + +# Dummy Message +MSG = "This is RIOT" + + +def _send_line_echo(child, line): + child.sendline(line) + child.expect_exact(line) + child.expect_exact(">") + + +def _send_line(child, line, expect_line): + child.sendline(line) + child.expect_exact(expect_line) + child.expect_exact(">") + + +def _reset_config(child): + # Start with a clean config + child.sendline("loramac erase") + child.expect("loramac erase") + child.expect_exact(">") + child.sendline("reboot") + child.expect_exact("reboot") + child.expect_exact("All up, running the shell now") + child.expect_exact(">") + + +def _check_eeprom(child): + # Check if eeprom is supported + child.sendline("loramac help") + child.expect(r'Usage: loramac \<([\w+\|?]+)\>') + return (len(child.match.group(1).split('|')) == 7) + + +def _reboot(child, join): + if join == "abp": + child.sendline("loramac get ul_cnt") + child.expect(r'Uplink Counter: (\d+)') + uplink_counter = int(child.match.group(1)) + child.sendline("reboot") + child.expect_exact("All up, running the shell now") + child.expect_exact(">") + if join == "abp": + _send_line_echo(child, "loramac set ul_cnt {}".format(uplink_counter)) + + +def _loramac_setup(child, join): + if join == "abp": + _send_line_echo(child, "loramac set deveui {}".format(DEVEUI_ABP)) + _send_line_echo(child, "loramac set appeui {}".format(APPEUI)) + _send_line_echo(child, "loramac set devaddr {}".format(DEVADDR)) + _send_line_echo(child, "loramac set nwkskey {}".format(NWKSKEY)) + _send_line_echo(child, "loramac set appskey {}".format(APPSKEY)) + _send_line_echo(child, "loramac set rx2_dr {}".format(TEST_RX2_DR)) + else: + _send_line_echo(child, "loramac set deveui {}".format(DEVEUI_OTA)) + _send_line_echo(child, "loramac set appeui {}".format(APPEUI)) + _send_line_echo(child, "loramac set appkey {}".format(APPKEY)) + + +def loramac_tx_test(child, join): + + _reset_config(child) + + # test all data rates + for key, time_off in TEST_DATA_RATES.items(): + # Setup keys and rx2_dr + _loramac_setup(child, join) + # Set DR and join + _send_line_echo(child, "loramac set dr {}".format(key)) + child.sendline("loramac join {}".format(join)) + child.expect_exact(["Join procedure succeeded!", + "Warning: already joined!"]) + child.expect_exact(">") + # Transmit cnf message + child.sendline("loramac tx \"{}\" cnf 123".format(MSG)) + child.expect_exact("Received ACK from network", timeout=30) + child.expect_exact("Message sent with success") + child.expect_exact(">") + # Wake-up just before time_off, fail to send + time.sleep(time_off) + # Send uncnf message with success + child.sendline("loramac tx \"{}\" uncnf 42".format(MSG)) + child.expect_exact("Message sent with success") + child.expect_exact(">") + # Reboot node + _reboot(child, join) + + +def test_task02(child): + loramac_tx_test(child, "otaa") + + +def test_task03(child): + loramac_tx_test(child, "abp") + + +def test_task04(child): + # Erase eeprom + _reset_config(child) + + # Verify start from erased state + _send_line(child, "loramac get deveui", "DEVEUI: 0000000000000000") + _send_line(child, "loramac get appeui", "APPEUI: 0000000000000000") + _send_line(child, "loramac get appkey", + "APPKEY: 00000000000000000000000000000000") + _send_line(child, "loramac get devaddr", "DEVADDR: 00000000") + _send_line(child, "loramac get nwkskey", + "NWKSKEY: 00000000000000000000000000000000") + _send_line(child, "loramac get appskey", + "APPSKEY: 00000000000000000000000000000000") + + # Save and verify otaa keys + _loramac_setup(child, "otaa") + _send_line_echo(child, "loramac save") + child.sendline("reboot") + child.expect_exact("All up, running the shell now") + child.expect_exact(">") + _send_line(child, "loramac get deveui", "DEVEUI: {}".format(DEVEUI_OTA)) + _send_line(child, "loramac get appeui", "APPEUI: {}".format(APPEUI)) + _send_line(child, "loramac get appkey", "APPKEY: {}".format(APPKEY)) + _reset_config(child) + + # Save and verify abp keys + _loramac_setup(child, "abp") + _send_line_echo(child, "loramac save") + child.sendline("reboot") + child.expect_exact("All up, running the shell now") + child.expect_exact(">") + _send_line(child, "loramac get devaddr", "DEVADDR: {}".format(DEVADDR)) + _send_line(child, "loramac get nwkskey", "NWKSKEY: {}".format(NWKSKEY)) + _send_line(child, "loramac get appskey", "APPSKEY: {}".format(APPSKEY)) + + +def testfunc(child): + + def run(func): + if child.logfile == sys.stdout: + func(child) + else: + try: + func(child) + print(".", end="", flush=True) + except Exception as e: + print("FAILED") + raise e + + run(test_task02) + run(test_task03) + if(_check_eeprom(child)): + run(test_task04) + + print("TEST PASSED") + + +if __name__ == "__main__": + sys.exit(run(testfunc))