mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
572 lines
14 KiB
C
572 lines
14 KiB
C
/*
|
|
* Copyright (C) 2016 Unwired Devices <info@unwds.com>
|
|
* 2017 Inria Chile
|
|
* 2017 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 SX127X modem driver
|
|
*
|
|
* @author Eugene P. <ep@unwds.com>
|
|
* @author José Ignacio Alamos <jose.alamos@inria.cl>
|
|
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
|
* @}
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "thread.h"
|
|
#include "shell.h"
|
|
|
|
#include "net/netdev.h"
|
|
#include "net/netdev/lora.h"
|
|
#include "net/lora.h"
|
|
|
|
#include "board.h"
|
|
|
|
#include "sx127x_internal.h"
|
|
#include "sx127x_params.h"
|
|
#include "sx127x_netdev.h"
|
|
|
|
#include "fmt.h"
|
|
|
|
#define SX127X_LORA_MSG_QUEUE (16U)
|
|
#define SX127X_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
|
|
|
|
#define MSG_TYPE_ISR (0x3456)
|
|
|
|
static char stack[SX127X_STACKSIZE];
|
|
static kernel_pid_t _recv_pid;
|
|
|
|
static char message[32];
|
|
static sx127x_t sx127x;
|
|
|
|
int lora_setup_cmd(int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 4) {
|
|
puts("usage: setup "
|
|
"<bandwidth (125, 250, 500)> "
|
|
"<spreading factor (7..12)> "
|
|
"<code rate (5..8)>");
|
|
return -1;
|
|
}
|
|
|
|
/* Check bandwidth value */
|
|
int bw = atoi(argv[1]);
|
|
uint8_t lora_bw;
|
|
|
|
switch (bw) {
|
|
case 125:
|
|
puts("setup: setting 125KHz bandwidth");
|
|
lora_bw = LORA_BW_125_KHZ;
|
|
break;
|
|
|
|
case 250:
|
|
puts("setup: setting 250KHz bandwidth");
|
|
lora_bw = LORA_BW_250_KHZ;
|
|
break;
|
|
|
|
case 500:
|
|
puts("setup: setting 500KHz bandwidth");
|
|
lora_bw = LORA_BW_500_KHZ;
|
|
break;
|
|
|
|
default:
|
|
puts("[Error] setup: invalid bandwidth value given, "
|
|
"only 125, 250 or 500 allowed.");
|
|
return -1;
|
|
}
|
|
|
|
/* Check spreading factor value */
|
|
uint8_t lora_sf = atoi(argv[2]);
|
|
|
|
if (lora_sf < 7 || lora_sf > 12) {
|
|
puts("[Error] setup: invalid spreading factor value given");
|
|
return -1;
|
|
}
|
|
|
|
/* Check coding rate value */
|
|
int cr = atoi(argv[3]);
|
|
|
|
if (cr < 5 || cr > 8) {
|
|
puts("[Error ]setup: invalid coding rate value given");
|
|
return -1;
|
|
}
|
|
uint8_t lora_cr = (uint8_t)(cr - 4);
|
|
|
|
/* Configure radio device */
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
|
|
netdev->driver->set(netdev, NETOPT_BANDWIDTH,
|
|
&lora_bw, sizeof(lora_bw));
|
|
netdev->driver->set(netdev, NETOPT_SPREADING_FACTOR,
|
|
&lora_sf, sizeof(lora_sf));
|
|
netdev->driver->set(netdev, NETOPT_CODING_RATE,
|
|
&lora_cr, sizeof(lora_cr));
|
|
|
|
puts("[Info] setup: configuration set with success");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int random_cmd(int argc, char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
uint32_t rand;
|
|
|
|
netdev->driver->get(netdev, NETOPT_RANDOM, &rand, sizeof(rand));
|
|
printf("random: number from sx127x: %u\n",
|
|
(unsigned int)rand);
|
|
|
|
/* reinit the transceiver to default values */
|
|
sx127x_init_radio_settings(&sx127x);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int register_cmd(int argc, char **argv)
|
|
{
|
|
if (argc < 2) {
|
|
puts("usage: register <get | set>");
|
|
return -1;
|
|
}
|
|
|
|
if (strstr(argv[1], "get") != NULL) {
|
|
if (argc < 3) {
|
|
puts("usage: register get <all | allinline | regnum>");
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(argv[2], "all") == 0) {
|
|
puts("- listing all registers -");
|
|
uint8_t reg = 0, data = 0;
|
|
/* Listing registers map */
|
|
puts("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F");
|
|
for (unsigned i = 0; i <= 7; i++) {
|
|
printf("0x%02X ", i << 4);
|
|
|
|
for (unsigned j = 0; j <= 15; j++, reg++) {
|
|
data = sx127x_reg_read(&sx127x, reg);
|
|
printf("%02X ", data);
|
|
}
|
|
puts("");
|
|
}
|
|
puts("-done-");
|
|
return 0;
|
|
}
|
|
else if (strcmp(argv[2], "allinline") == 0) {
|
|
puts("- listing all registers in one line -");
|
|
/* Listing registers map */
|
|
for (uint16_t reg = 0; reg < 256; reg++) {
|
|
printf("%02X ", sx127x_reg_read(&sx127x, (uint8_t)reg));
|
|
}
|
|
puts("- done -");
|
|
return 0;
|
|
}
|
|
else {
|
|
long int num = 0;
|
|
/* Register number in hex */
|
|
if (strstr(argv[2], "0x") != NULL) {
|
|
num = strtol(argv[2], NULL, 16);
|
|
}
|
|
else {
|
|
num = atoi(argv[2]);
|
|
}
|
|
|
|
if (num >= 0 && num <= 255) {
|
|
printf("[regs] 0x%02X = 0x%02X\n",
|
|
(uint8_t)num,
|
|
sx127x_reg_read(&sx127x, (uint8_t)num));
|
|
}
|
|
else {
|
|
puts("regs: invalid register number specified");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else if (strstr(argv[1], "set") != NULL) {
|
|
if (argc < 4) {
|
|
puts("usage: register set <regnum> <value>");
|
|
return -1;
|
|
}
|
|
|
|
long num, val;
|
|
|
|
/* Register number in hex */
|
|
if (strstr(argv[2], "0x") != NULL) {
|
|
num = strtol(argv[2], NULL, 16);
|
|
}
|
|
else {
|
|
num = atoi(argv[2]);
|
|
}
|
|
|
|
/* Register value in hex */
|
|
if (strstr(argv[3], "0x") != NULL) {
|
|
val = strtol(argv[3], NULL, 16);
|
|
}
|
|
else {
|
|
val = atoi(argv[3]);
|
|
}
|
|
|
|
sx127x_reg_write(&sx127x, (uint8_t)num, (uint8_t)val);
|
|
}
|
|
else {
|
|
puts("usage: register get <all | allinline | regnum>");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int send_cmd(int argc, char **argv)
|
|
{
|
|
if (argc <= 1) {
|
|
puts("usage: send <payload>");
|
|
return -1;
|
|
}
|
|
|
|
printf("sending \"%s\" payload (%" PRIuSIZE " bytes)\n",
|
|
argv[1], strlen(argv[1]) + 1);
|
|
|
|
iolist_t iolist = {
|
|
.iol_base = argv[1],
|
|
.iol_len = (strlen(argv[1]) + 1)
|
|
};
|
|
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
|
|
if (netdev->driver->send(netdev, &iolist) == -ENOTSUP) {
|
|
puts("Cannot send: radio is still transmitting");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int listen_cmd(int argc, char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
/* Switch to continuous listen mode */
|
|
const netopt_enable_t single = false;
|
|
|
|
netdev->driver->set(netdev, NETOPT_SINGLE_RECEIVE, &single, sizeof(single));
|
|
const uint32_t timeout = 0;
|
|
|
|
netdev->driver->set(netdev, NETOPT_RX_TIMEOUT, &timeout, sizeof(timeout));
|
|
|
|
/* Switch to RX state */
|
|
netopt_state_t state = NETOPT_STATE_RX;
|
|
|
|
netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(state));
|
|
|
|
printf("Listen mode set\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int syncword_cmd(int argc, char **argv)
|
|
{
|
|
if (argc < 2) {
|
|
puts("usage: syncword <get|set>");
|
|
return -1;
|
|
}
|
|
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
uint8_t syncword;
|
|
|
|
if (strstr(argv[1], "get") != NULL) {
|
|
netdev->driver->get(netdev, NETOPT_SYNCWORD, &syncword,
|
|
sizeof(syncword));
|
|
printf("Syncword: 0x%02x\n", syncword);
|
|
return 0;
|
|
}
|
|
|
|
if (strstr(argv[1], "set") != NULL) {
|
|
if (argc < 3) {
|
|
puts("usage: syncword set <syncword>");
|
|
return -1;
|
|
}
|
|
syncword = fmt_hex_byte(argv[2]);
|
|
netdev->driver->set(netdev, NETOPT_SYNCWORD, &syncword,
|
|
sizeof(syncword));
|
|
printf("Syncword set to %02x\n", syncword);
|
|
}
|
|
else {
|
|
puts("usage: syncword <get|set>");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
int channel_cmd(int argc, char **argv)
|
|
{
|
|
if (argc < 2) {
|
|
puts("usage: channel <get|set>");
|
|
return -1;
|
|
}
|
|
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
uint32_t chan;
|
|
|
|
if (strstr(argv[1], "get") != NULL) {
|
|
netdev->driver->get(netdev, NETOPT_CHANNEL_FREQUENCY, &chan,
|
|
sizeof(chan));
|
|
printf("Channel: %i\n", (int)chan);
|
|
return 0;
|
|
}
|
|
|
|
if (strstr(argv[1], "set") != NULL) {
|
|
if (argc < 3) {
|
|
puts("usage: channel set <channel>");
|
|
return -1;
|
|
}
|
|
chan = atoi(argv[2]);
|
|
netdev->driver->set(netdev, NETOPT_CHANNEL_FREQUENCY, &chan,
|
|
sizeof(chan));
|
|
printf("New channel set\n");
|
|
}
|
|
else {
|
|
puts("usage: channel <get|set>");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rx_timeout_cmd(int argc, char **argv)
|
|
{
|
|
if (argc < 2) {
|
|
puts("usage: channel <get|set>");
|
|
return -1;
|
|
}
|
|
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
uint16_t rx_timeout;
|
|
|
|
if (strstr(argv[1], "set") != NULL) {
|
|
if (argc < 3) {
|
|
puts("usage: rx_timeout set <rx_timeout>");
|
|
return -1;
|
|
}
|
|
rx_timeout = atoi(argv[2]);
|
|
netdev->driver->set(netdev, NETOPT_RX_SYMBOL_TIMEOUT, &rx_timeout,
|
|
sizeof(rx_timeout));
|
|
printf("rx_timeout set to %i\n", rx_timeout);
|
|
}
|
|
else {
|
|
puts("usage: rx_timeout set");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int reset_cmd(int argc, char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
|
|
puts("resetting sx127x...");
|
|
netopt_state_t state = NETOPT_STATE_RESET;
|
|
|
|
netdev->driver->set(netdev, NETOPT_STATE, &state, sizeof(netopt_state_t));
|
|
return 0;
|
|
}
|
|
|
|
static void _set_opt(netdev_t *netdev, netopt_t opt, bool val, char *str_help)
|
|
{
|
|
netopt_enable_t en = val ? NETOPT_ENABLE : NETOPT_DISABLE;
|
|
|
|
netdev->driver->set(netdev, opt, &en, sizeof(en));
|
|
printf("Successfully ");
|
|
if (val) {
|
|
printf("enabled ");
|
|
}
|
|
else {
|
|
printf("disabled ");
|
|
}
|
|
printf("%s\n", str_help);
|
|
}
|
|
|
|
int crc_cmd(int argc, char **argv)
|
|
{
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
|
|
if (argc < 3 || strcmp(argv[1], "set") != 0) {
|
|
printf("usage: %s set <1|0>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
int tmp = atoi(argv[2]);
|
|
|
|
_set_opt(netdev, NETOPT_INTEGRITY_CHECK, tmp, "CRC check");
|
|
return 0;
|
|
}
|
|
|
|
int implicit_cmd(int argc, char **argv)
|
|
{
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
|
|
if (argc < 3 || strcmp(argv[1], "set") != 0) {
|
|
printf("usage: %s set <1|0>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
int tmp = atoi(argv[2]);
|
|
|
|
_set_opt(netdev, NETOPT_FIXED_HEADER, tmp, "implicit header");
|
|
return 0;
|
|
}
|
|
|
|
int payload_cmd(int argc, char **argv)
|
|
{
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
|
|
if (argc < 3 || strcmp(argv[1], "set") != 0) {
|
|
printf("usage: %s set <payload length>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
uint16_t tmp = atoi(argv[2]);
|
|
|
|
netdev->driver->set(netdev, NETOPT_PDU_SIZE, &tmp, sizeof(tmp));
|
|
printf("Successfully set payload to %i\n", tmp);
|
|
return 0;
|
|
}
|
|
|
|
static const shell_command_t shell_commands[] = {
|
|
{ "setup", "Initialize LoRa modulation settings", lora_setup_cmd },
|
|
{ "implicit", "Enable implicit header", implicit_cmd },
|
|
{ "crc", "Enable CRC", crc_cmd },
|
|
{ "payload", "Set payload length (implicit header)", payload_cmd },
|
|
{ "random", "Get random number from sx127x", random_cmd },
|
|
{ "syncword", "Get/Set the syncword", syncword_cmd },
|
|
{ "rx_timeout", "Set the RX timeout", rx_timeout_cmd },
|
|
{ "channel", "Get/Set channel frequency (in Hz)", channel_cmd },
|
|
{ "register", "Get/Set value(s) of registers of sx127x", register_cmd },
|
|
{ "send", "Send raw payload string", send_cmd },
|
|
{ "listen", "Start raw payload listener", listen_cmd },
|
|
{ "reset", "Reset the sx127x device", reset_cmd },
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
static void _event_cb(netdev_t *dev, netdev_event_t event)
|
|
{
|
|
if (event == NETDEV_EVENT_ISR) {
|
|
msg_t msg;
|
|
|
|
msg.type = MSG_TYPE_ISR;
|
|
msg.content.ptr = dev;
|
|
|
|
if (msg_send(&msg, _recv_pid) <= 0) {
|
|
puts("gnrc_netdev: possibly lost interrupt.");
|
|
}
|
|
}
|
|
else {
|
|
size_t len;
|
|
netdev_lora_rx_info_t packet_info;
|
|
switch (event) {
|
|
case NETDEV_EVENT_RX_STARTED:
|
|
puts("Data reception started");
|
|
break;
|
|
|
|
case NETDEV_EVENT_RX_COMPLETE:
|
|
len = dev->driver->recv(dev, NULL, 0, 0);
|
|
dev->driver->recv(dev, message, len, &packet_info);
|
|
printf(
|
|
"{Payload: \"%s\" (%" PRIuSIZE " bytes), RSSI: %i, SNR: %i, TOA: %" PRIu32 "}\n",
|
|
message, len,
|
|
packet_info.rssi, packet_info.snr,
|
|
sx127x_get_time_on_air((const sx127x_t *)dev, len));
|
|
break;
|
|
|
|
case NETDEV_EVENT_TX_COMPLETE:
|
|
sx127x_set_sleep(&sx127x);
|
|
puts("Transmission completed");
|
|
break;
|
|
|
|
case NETDEV_EVENT_CAD_DONE:
|
|
break;
|
|
|
|
case NETDEV_EVENT_TX_TIMEOUT:
|
|
sx127x_set_sleep(&sx127x);
|
|
break;
|
|
|
|
default:
|
|
printf("Unexpected netdev event received: %d\n", event);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *_recv_thread(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
static msg_t _msg_q[SX127X_LORA_MSG_QUEUE];
|
|
|
|
msg_init_queue(_msg_q, SX127X_LORA_MSG_QUEUE);
|
|
|
|
while (1) {
|
|
msg_t msg;
|
|
msg_receive(&msg);
|
|
if (msg.type == MSG_TYPE_ISR) {
|
|
netdev_t *dev = msg.content.ptr;
|
|
dev->driver->isr(dev);
|
|
}
|
|
else {
|
|
puts("Unexpected msg type");
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
sx127x.params = sx127x_params[0];
|
|
netdev_t *netdev = &sx127x.netdev;
|
|
|
|
netdev->driver = &sx127x_driver;
|
|
|
|
netdev->event_callback = _event_cb;
|
|
|
|
if (netdev->driver->init(netdev) < 0) {
|
|
puts("Failed to initialize SX127x device, exiting");
|
|
return 1;
|
|
}
|
|
|
|
_recv_pid = thread_create(stack, sizeof(stack), THREAD_PRIORITY_MAIN - 1,
|
|
0, _recv_thread, NULL,
|
|
"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;
|
|
}
|