From c8e343a59004100ce1f3c83b8643d28f67fd9ad4 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Thu, 11 Mar 2021 17:29:36 +0100 Subject: [PATCH] tests/driver_sx126x: add test application --- tests/driver_sx126x/Makefile | 10 + tests/driver_sx126x/Makefile.ci | 11 + tests/driver_sx126x/README.md | 45 ++++ tests/driver_sx126x/app.config.test | 7 + tests/driver_sx126x/main.c | 350 ++++++++++++++++++++++++++++ 5 files changed, 423 insertions(+) create mode 100644 tests/driver_sx126x/Makefile create mode 100644 tests/driver_sx126x/Makefile.ci create mode 100644 tests/driver_sx126x/README.md create mode 100644 tests/driver_sx126x/app.config.test create mode 100644 tests/driver_sx126x/main.c diff --git a/tests/driver_sx126x/Makefile b/tests/driver_sx126x/Makefile new file mode 100644 index 0000000000..68af3a584c --- /dev/null +++ b/tests/driver_sx126x/Makefile @@ -0,0 +1,10 @@ +include ../Makefile.tests_common + +# other values supported are sx1262, sx1268 and llcc68 +LORA_DRIVER ?= sx1261 +USEMODULE += $(LORA_DRIVER) + +USEMODULE += shell +USEMODULE += shell_commands + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_sx126x/Makefile.ci b/tests/driver_sx126x/Makefile.ci new file mode 100644 index 0000000000..ecd48b4a74 --- /dev/null +++ b/tests/driver_sx126x/Makefile.ci @@ -0,0 +1,11 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + nucleo-l011k4 \ + stm32f030f4-demo \ + samd10-xmini \ + waspmote-pro \ + # diff --git a/tests/driver_sx126x/README.md b/tests/driver_sx126x/README.md new file mode 100644 index 0000000000..fea2c71319 --- /dev/null +++ b/tests/driver_sx126x/README.md @@ -0,0 +1,45 @@ +sx126x/llcc68 LoRa driver +================== + +This is a manual test application for the SX1261/2/8 and LLCC68 LoRa radio driver. + +Usage +===== + +This application adds a shell command to control basic features radio device: + +``` +main(): This is RIOT! (Version: 2021.04-devel) +Initialization successful - starting the shell now +> help +help +Command Description +--------------------------------------- +sx126x Control the SX126X radio +reboot Reboot the node +version Prints current RIOT_VERSION +pm interact with layered PM subsystem +> sx126x +sx126x +Usage: sx126x +``` + +The `get` and `set` subcommands allows for getting/setting the current +frequency channel (freq) and lora modulation parameters (bw, sf, cr). + +To put the device in listen mode, use the `rx` subcommand: + +``` +> sx126x rx start +sx126x rx start +Listen mode started +``` + +To send a message, use the `tx` subcommand: + +``` +> sx126x tx "This is RIOT!" +sx126x tx "This is RIOT!" +sending "This is RIOT!" payload (14 bytes) +> Transmission completed +``` diff --git a/tests/driver_sx126x/app.config.test b/tests/driver_sx126x/app.config.test new file mode 100644 index 0000000000..f6daeadf2b --- /dev/null +++ b/tests/driver_sx126x/app.config.test @@ -0,0 +1,7 @@ +# this file enables modules defined in Kconfig. Do not use this file for +# application configuration. This is only needed during migration. +CONFIG_MODULE_SX126X=y +CONFIG_PACKAGE_DRIVER_SX126X=y + +CONFIG_MODULE_SHELL=y +CONFIG_MODULE_SHELL_COMMANDS=y diff --git a/tests/driver_sx126x/main.c b/tests/driver_sx126x/main.c new file mode 100644 index 0000000000..313c95b4ed --- /dev/null +++ b/tests/driver_sx126x/main.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2021 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the sx126x/llcc68 radio driver + * + * @author Alexandre Abadie + * + * @} + */ + +#include +#include +#include +#include + +#include "msg.h" +#include "thread.h" +#include "shell.h" + +#include "net/lora.h" +#include "net/netdev.h" +#include "net/netdev/lora.h" + +#include "sx126x.h" +#include "sx126x_params.h" +#include "sx126x_netdev.h" + +#define SX126X_MSG_QUEUE (8U) +#define SX126X_STACKSIZE (THREAD_STACKSIZE_DEFAULT) +#define SX126X_MSG_TYPE_ISR (0x3456) +#define SX126X_MAX_PAYLOAD_LEN (128U) + +static char stack[SX126X_STACKSIZE]; +static kernel_pid_t _recv_pid; + +static char message[SX126X_MAX_PAYLOAD_LEN]; + +static sx126x_t sx126x; + +static void _event_cb(netdev_t *dev, netdev_event_t event) +{ + if (event == NETDEV_EVENT_ISR) { + msg_t msg; + msg.type = SX126X_MSG_TYPE_ISR; + if (msg_send(&msg, _recv_pid) <= 0) { + puts("sx126x_netdev: possibly lost interrupt."); + } + } + else { + switch (event) { + case NETDEV_EVENT_RX_STARTED: + puts("Data reception started"); + break; + + case NETDEV_EVENT_RX_COMPLETE: + { + size_t len = dev->driver->recv(dev, NULL, 0, 0); + netdev_lora_rx_info_t packet_info; + dev->driver->recv(dev, message, len, &packet_info); + printf( + "Received: \"%s\" (%d bytes) - [RSSI: %i, SNR: %i, TOA: %" PRIu32 "ms]\n", + message, (int)len, + packet_info.rssi, (int)packet_info.snr, + sx126x_get_lora_time_on_air_in_ms(&sx126x.pkt_params, &sx126x.mod_params) + ); + netopt_state_t state = NETOPT_STATE_RX; + dev->driver->set(dev, NETOPT_STATE, &state, sizeof(state)); + } + break; + + case NETDEV_EVENT_TX_COMPLETE: + puts("Transmission completed"); + break; + + case NETDEV_EVENT_TX_TIMEOUT: + puts("Transmission timeout"); + break; + + default: + printf("Unexpected netdev event received: %d\n", event); + break; + } + } +} + +void *_recv_thread(void *arg) +{ + netdev_t *netdev = (netdev_t *)arg; + + static msg_t _msg_queue[SX126X_MSG_QUEUE]; + + msg_init_queue(_msg_queue, SX126X_MSG_QUEUE); + + while (1) { + msg_t msg; + msg_receive(&msg); + if (msg.type == SX126X_MSG_TYPE_ISR) { + netdev->driver->isr(netdev); + } + else { + puts("Unexpected msg type"); + } + } +} + +static void _get_usage(const char *cmd) +{ + printf("Usage: %s get \n", cmd); +} + +static int sx126x_get_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc == 2) { + _get_usage(argv[0]); + return -1; + } + + if (!strcmp("type", argv[2])) { + uint16_t type; + netdev->driver->get(netdev, NETOPT_DEVICE_TYPE, &type, sizeof(uint16_t)); + printf("Device type: %s\n", (type == NETDEV_TYPE_LORA) ? "lora" : "fsk"); + } + else if (!strcmp("freq", argv[2])) { + uint32_t freq; + netdev->driver->get(netdev, NETOPT_CHANNEL_FREQUENCY, &freq, sizeof(uint32_t)); + printf("Frequency: %" PRIu32 "Hz\n", freq); + } + else if (!strcmp("bw", argv[2])) { + uint8_t bw; + netdev->driver->get(netdev, NETOPT_BANDWIDTH, &bw, sizeof(uint8_t)); + uint16_t bw_val = 0; + switch (bw) { + case LORA_BW_125_KHZ: + bw_val = 125; + break; + case LORA_BW_250_KHZ: + bw_val = 250; + break; + case LORA_BW_500_KHZ: + bw_val = 500; + break; + default: + break; + } + printf("Bandwidth: %ukHz\n", bw_val); + } + else if (!strcmp("sf", argv[2])) { + uint8_t sf; + netdev->driver->get(netdev, NETOPT_SPREADING_FACTOR, &sf, sizeof(uint8_t)); + printf("Spreading factor: %d\n", sf); + } + else if (!strcmp("cr", argv[2])) { + uint8_t cr; + netdev->driver->get(netdev, NETOPT_CODING_RATE, &cr, sizeof(uint8_t)); + printf("Coding rate: %d\n", cr); + } + else if (!strcmp("random", argv[2])) { + uint32_t rand; + netdev->driver->get(netdev, NETOPT_RANDOM, &rand, sizeof(uint32_t)); + printf("random number: %" PRIu32 "\n", rand); + } + else { + _get_usage(argv[0]); + return -1; + } + + return 0; +} + +static void _set_usage(const char *cmd) +{ + printf("Usage: %s set \n", cmd); +} + +static int sx126x_set_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc != 4) { + _set_usage(argv[0]); + return -1; + } + + int ret = 0; + + if (!strcmp("freq", argv[2])) { + uint32_t freq = atoi(argv[3]); + ret = netdev->driver->set(netdev, NETOPT_CHANNEL_FREQUENCY, &freq, sizeof(uint32_t)); + } + else if (!strcmp("bw", argv[2])) { + uint8_t bw; + if (!strcmp("125", argv[3])) { + bw = LORA_BW_125_KHZ; + } + else if (!strcmp("250", argv[3])) { + bw = LORA_BW_250_KHZ; + } + else if (!strcmp("500", argv[3])) { + bw = LORA_BW_500_KHZ; + } + else { + puts("invalid bandwidth, use 125, 250 or 500"); + return -1; + } + ret = netdev->driver->set(netdev, NETOPT_BANDWIDTH, &bw, sizeof(uint8_t)); + } + else if (!strcmp("sf", argv[2])) { + uint8_t sf = atoi(argv[3]); + ret = netdev->driver->set(netdev, NETOPT_SPREADING_FACTOR, &sf, sizeof(uint8_t)); + } + else if (!strcmp("cr", argv[2])) { + uint8_t cr = atoi(argv[3]); + ret = netdev->driver->set(netdev, NETOPT_CODING_RATE, &cr, sizeof(uint8_t)); + } + else { + _set_usage(argv[0]); + return -1; + } + + if (ret < 0) { + printf("cannot set %s\n", argv[2]); + return ret; + } + + printf("%s set\n", argv[2]); + return 0; +} + +static void _rx_usage(const char *cmd) +{ + printf("Usage: %s rx \n", cmd); +} + +static int sx126x_rx_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc == 2) { + _rx_usage(argv[0]); + return -1; + } + + if (!strcmp("start", argv[2])) { + /* Switch to RX state */ + netopt_state_t state = NETOPT_STATE_IDLE; + netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(state)); + printf("Listen mode started\n"); + } + else if (!strcmp("stop", argv[2])) { + /* Switch to RX state */ + netopt_state_t state = NETOPT_STATE_STANDBY; + netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(state)); + printf("Listen mode stopped\n"); + } + else { + _rx_usage(argv[0]); + return -1; + } + + return 0; +} + +static int sx126x_tx_cmd(netdev_t *netdev, int argc, char **argv) +{ + if (argc == 2) { + printf("Usage: %s tx \n", argv[0]); + return -1; + } + + printf("sending \"%s\" payload (%u bytes)\n", + argv[2], (unsigned)strlen(argv[2]) + 1); + iolist_t iolist = { + .iol_base = argv[2], + .iol_len = (strlen(argv[2]) + 1) + }; + + if (netdev->driver->send(netdev, &iolist) == -ENOTSUP) { + puts("Cannot send: radio is still transmitting"); + return -1; + } + + return 0; +} + +int sx126x_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + netdev_t *netdev = (netdev_t *)&sx126x; + + if (!strcmp("get", argv[1])) { + return sx126x_get_cmd(netdev, argc, argv); + } + else if (!strcmp("set", argv[1])) { + return sx126x_set_cmd(netdev, argc, argv); + } + else if (!strcmp("rx", argv[1])) { + return sx126x_rx_cmd(netdev, argc, argv); + } + else if (!strcmp("tx", argv[1])) { + return sx126x_tx_cmd(netdev, argc, argv); + } + + return 0; +} + +static const shell_command_t shell_commands[] = { + { "sx126x", "Control the SX126X radio", sx126x_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + sx126x_setup(&sx126x, &sx126x_params[0], 0); + netdev_t *netdev = (netdev_t *)&sx126x; + + netdev->driver = &sx126x_driver; + + if (netdev->driver->init(netdev) < 0) { + puts("Failed to initialize SX126X device, exiting"); + return 1; + } + + netdev->event_callback = _event_cb; + + _recv_pid = thread_create(stack, sizeof(stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, _recv_thread, netdev, + "recv_thread"); + + if (_recv_pid <= KERNEL_PID_UNDEF) { + puts("Creation of receiver thread failed"); + return 1; + } + + /* start the shell */ + puts("Initialization successful - starting the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + return 0; +}