mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-09 22:45:11 +00:00
wireless/lora_pkt_fwd: add Semtech UDP packet forwarder and lora command
Forward the packets received by a LoRa concentrator to a LoRaWAN network server with the Semtech UDP protocol version 2, and turn the downlink requests of the server into transmissions. The server may be given as a name and is resolved with getaddrinfo(). No floating point is used anywhere. The same program provides the "lora" command, which mirrors the AT command set of the vendor gateway firmwares, plus tx, which sends a single packet and so brings a gateway up against any LoRa receiver without a network server. Nothing here names a chip: the types and the commands are the device independent gateway ones of the nuttx repository, nuttx/wireless/lpwan/lora_gw.h. The application depends on LPWAN_LORA_GW, the symbol such a driver selects, so until one is merged nothing here is compiled. Assisted-by: Claude Code 4.8 Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
This commit is contained in:
parent
2aebae7400
commit
baef9ebf6e
7 changed files with 2191 additions and 0 deletions
35
wireless/lora_pkt_fwd/CMakeLists.txt
Normal file
35
wireless/lora_pkt_fwd/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# ##############################################################################
|
||||
# apps/wireless/lora_pkt_fwd/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with this work for
|
||||
# additional information regarding copyright ownership. The ASF licenses this
|
||||
# file to you under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
# ##############################################################################
|
||||
if(CONFIG_WIRELESS_LORA_PKT_FWD)
|
||||
nuttx_add_application(
|
||||
NAME
|
||||
${CONFIG_LORA_PKT_FWD_PROGNAME}
|
||||
PRIORITY
|
||||
${CONFIG_LORA_PKT_FWD_PRIORITY}
|
||||
STACKSIZE
|
||||
${CONFIG_LORA_PKT_FWD_STACKSIZE}
|
||||
MODULE
|
||||
${CONFIG_WIRELESS_LORA_PKT_FWD}
|
||||
SRCS
|
||||
lora_main.c
|
||||
lora_pkt_fwd.c)
|
||||
endif()
|
||||
92
wireless/lora_pkt_fwd/Kconfig
Normal file
92
wireless/lora_pkt_fwd/Kconfig
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config WIRELESS_LORA_PKT_FWD
|
||||
tristate "LoRaWAN Semtech UDP packet forwarder"
|
||||
default n
|
||||
depends on LPWAN_LORA_GW && NET_UDP && NETUTILS_NETLIB
|
||||
select NETUTILS_CODECS
|
||||
select CODECS_BASE64
|
||||
select LIBC_STRERROR if DEBUG_FEATURES
|
||||
---help---
|
||||
Forwards the packets received by a LoRa concentrator to a LoRaWAN
|
||||
network server using the Semtech UDP protocol version 2, and turns
|
||||
the downlink requests of the server into transmissions.
|
||||
|
||||
Also provides the "lora" command, the equivalent of the AT command
|
||||
set of the vendor gateway firmwares.
|
||||
|
||||
if WIRELESS_LORA_PKT_FWD
|
||||
|
||||
config LORA_PKT_FWD_PROGNAME
|
||||
string "Program name"
|
||||
default "lora"
|
||||
|
||||
config LORA_PKT_FWD_PRIORITY
|
||||
int "Daemon task priority"
|
||||
default 100
|
||||
|
||||
config LORA_PKT_FWD_STACKSIZE
|
||||
int "Daemon stack size"
|
||||
default 8192
|
||||
|
||||
config LORA_PKT_FWD_VERSION
|
||||
string "Version reported by \"lora ver\""
|
||||
default "1.0.0"
|
||||
|
||||
config LORA_PKT_FWD_DEVPATH
|
||||
string "Concentrator device"
|
||||
default "/dev/lora0"
|
||||
|
||||
config LORA_PKT_FWD_SERVER
|
||||
string "Network server host name or address"
|
||||
default "192.168.15.100"
|
||||
---help---
|
||||
Host name or address of the LoRaWAN network server. A name is
|
||||
resolved with getaddrinfo(), which needs NETDB_DNSCLIENT.
|
||||
|
||||
The Things Network uses the regional cluster names, for example
|
||||
au1.cloud.thethings.network for AU915.
|
||||
|
||||
config LORA_PKT_FWD_PORT_UP
|
||||
int "Uplink UDP port"
|
||||
default 1700
|
||||
|
||||
config LORA_PKT_FWD_PORT_DOWN
|
||||
int "Downlink UDP port"
|
||||
default 1700
|
||||
|
||||
config LORA_PKT_FWD_EUI
|
||||
string "Gateway identifier"
|
||||
default "080027FFFF0C2338"
|
||||
---help---
|
||||
The 64 bit gateway identifier, as sixteen hexadecimal digits. It has
|
||||
to match the gateway registered in the network server.
|
||||
|
||||
config LORA_PKT_FWD_EUI_FROM_MAC
|
||||
bool "Derive the gateway identifier from the Ethernet address"
|
||||
default n
|
||||
---help---
|
||||
Build the identifier out of the Ethernet address of eth0 instead of
|
||||
using the configured one, inserting FFFE in the middle as usual for
|
||||
an EUI-64.
|
||||
|
||||
config LORA_PKT_FWD_TXPOWER
|
||||
int "Transmit power of \"lora tx\" in dBm"
|
||||
default 14
|
||||
---help---
|
||||
Antenna power requested for the packet sent by the "lora tx"
|
||||
command. The concentrator picks the closest entry of its gain
|
||||
table.
|
||||
|
||||
config LORA_PKT_FWD_KEEPALIVE
|
||||
int "PULL_DATA keepalive interval in seconds"
|
||||
default 5
|
||||
|
||||
config LORA_PKT_FWD_STATINTERVAL
|
||||
int "Status push interval in seconds"
|
||||
default 30
|
||||
|
||||
endif # WIRELESS_LORA_PKT_FWD
|
||||
25
wireless/lora_pkt_fwd/Make.defs
Normal file
25
wireless/lora_pkt_fwd/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/wireless/lora_pkt_fwd/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_WIRELESS_LORA_PKT_FWD),)
|
||||
CONFIGURED_APPS += $(APPDIR)/wireless/lora_pkt_fwd
|
||||
endif
|
||||
35
wireless/lora_pkt_fwd/Makefile
Normal file
35
wireless/lora_pkt_fwd/Makefile
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
############################################################################
|
||||
# apps/wireless/lora_pkt_fwd/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# LoRaWAN packet forwarder and the "lora" command
|
||||
|
||||
PROGNAME = $(CONFIG_LORA_PKT_FWD_PROGNAME)
|
||||
PRIORITY = $(CONFIG_LORA_PKT_FWD_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_LORA_PKT_FWD_STACKSIZE)
|
||||
MODULE = $(CONFIG_WIRELESS_LORA_PKT_FWD)
|
||||
|
||||
CSRCS = lora_pkt_fwd.c
|
||||
MAINSRC = lora_main.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
653
wireless/lora_pkt_fwd/lora_main.c
Normal file
653
wireless/lora_pkt_fwd/lora_main.c
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
/****************************************************************************
|
||||
* apps/wireless/lora_pkt_fwd/lora_main.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* The "lora" command, the equivalent of the AT command set of the vendor
|
||||
* gateway firmware:
|
||||
*
|
||||
* lora sys full system configuration (AT+SYS)
|
||||
* lora ver firmware version (AT+VER)
|
||||
* lora status concentrator and forwarder state
|
||||
* lora ch show or change the channel plan (AT+CH)
|
||||
* lora server show or change the server (AT+PKTFWD)
|
||||
* lora ip network configuration (AT+IP)
|
||||
* lora mac Ethernet address and gateway id (AT+MAC)
|
||||
* lora reset restart the concentrator (AT+RESET)
|
||||
* lora tx transmit one packet, for bring-up
|
||||
* lora start start the packet forwarder
|
||||
* lora stop stop the packet forwarder
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <nuttx/version.h>
|
||||
#include <nuttx/wireless/lpwan/lora_gw.h>
|
||||
|
||||
#include "netutils/netlib.h"
|
||||
|
||||
#include "lora_pkt_fwd.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define LORA_SEPARATOR \
|
||||
"---------------------------------------------------------------"
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_usage
|
||||
****************************************************************************/
|
||||
|
||||
static void lora_usage(void)
|
||||
{
|
||||
printf("lora - LoRa Gateway commands (concentrator + forwarder)\n");
|
||||
printf("Subcommands:\n");
|
||||
printf(" sys : Show full system configuration (like AT+SYS)\n");
|
||||
printf(" ver : Show firmware version (like AT+VER)\n");
|
||||
printf(" status : Show gateway status and statistics\n");
|
||||
printf(" ch : Show/set channel plan (like AT+CH)\n");
|
||||
printf(" Usage: lora ch [REGION]\n");
|
||||
printf(" server : Show/set LoRaWAN server (like AT+PKTFWD)\n");
|
||||
printf(" Usage: lora server [HOST [PORT_UP [PORT_DOWN]]]\n");
|
||||
printf(" ip : Show network configuration (like AT+IP)\n");
|
||||
printf(" mac : Show MAC address and Gateway ID (like AT+MAC)\n");
|
||||
printf(" reset : Reset concentrator and restart (like AT+RESET)\n");
|
||||
printf(" tx : Transmit one packet, for bring-up\n");
|
||||
printf(" Usage: lora tx FREQ_HZ [SF [TEXT]]\n");
|
||||
printf(" start : Start packet forwarder\n");
|
||||
printf(" stop : Stop packet forwarder\n");
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_opendev
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_opendev(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(CONFIG_LORA_PKT_FWD_DEVPATH, O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
fprintf(stderr, "lora: cannot open %s: %d\n",
|
||||
CONFIG_LORA_PKT_FWD_DEVPATH, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_getregion
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_getregion(FAR struct lora_gw_regioninfo_s *info, int index)
|
||||
{
|
||||
struct lora_gw_regionreq_s req;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = lora_opendev();
|
||||
if (fd < 0)
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
req.index = index;
|
||||
ret = ioctl(fd, WLIOC_GW_GETREGION, (unsigned long)&req);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memcpy(info, &req.info, sizeof(*info));
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_getstatus
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_getstatus(FAR struct lora_gw_status_s *status)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = lora_opendev();
|
||||
if (fd < 0)
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, WLIOC_GW_GETSTATUS, (unsigned long)status);
|
||||
close(fd);
|
||||
|
||||
return ret < 0 ? -errno : OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_printchannels
|
||||
****************************************************************************/
|
||||
|
||||
static void lora_printchannels(FAR const struct lora_gw_regioninfo_s *info,
|
||||
FAR const char *indent)
|
||||
{
|
||||
FAR const struct lora_gw_chaninfo_s *ch;
|
||||
FAR const char *type;
|
||||
char rate[24];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LORA_GW_IF_CHAIN_NB; i++)
|
||||
{
|
||||
ch = &info->channels[i];
|
||||
|
||||
switch (ch->type)
|
||||
{
|
||||
case LORA_GW_CHAN_MULTI_SF:
|
||||
type = "LORA_MULTI_SF";
|
||||
strlcpy(rate, "SF7/SF12, BW125KHz", sizeof(rate));
|
||||
break;
|
||||
|
||||
case LORA_GW_CHAN_STD:
|
||||
type = "LORA_STANDARD";
|
||||
snprintf(rate, sizeof(rate), "SF%u, BW%uKHz", ch->datarate,
|
||||
ch->bandwidth == LORA_GW_BW_500K ? 500 :
|
||||
(ch->bandwidth == LORA_GW_BW_250K ? 250 : 125));
|
||||
break;
|
||||
|
||||
case LORA_GW_CHAN_FSK:
|
||||
type = "FSK";
|
||||
rate[0] = '\0';
|
||||
break;
|
||||
|
||||
default:
|
||||
type = "OFF";
|
||||
rate[0] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ch->enable)
|
||||
{
|
||||
printf("%sCHANNEL%d: OFF (%s)\n",
|
||||
indent, i, type);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%sCHANNEL%d: %" PRIu32 ", %c, %s (%s)\n",
|
||||
indent, i, ch->freq_hz, ch->rf_chain == 0 ? 'A' : 'B',
|
||||
rate, type);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_ver
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_ver(void)
|
||||
{
|
||||
printf("Powered by NuttX & LoRa Gateway HAL\n");
|
||||
printf("VERSION: %s (NuttX %s)\n", CONFIG_LORA_PKT_FWD_VERSION,
|
||||
CONFIG_VERSION_STRING);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_ip
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_ip(void)
|
||||
{
|
||||
struct in_addr addr;
|
||||
uint8_t flags = 0;
|
||||
|
||||
netlib_getifstatus(LORA_FWD_IFNAME, &flags);
|
||||
|
||||
printf(" ETHERNET: %s\n",
|
||||
#ifdef CONFIG_NETUTILS_DHCPC
|
||||
"DHCP"
|
||||
#else
|
||||
"static"
|
||||
#endif
|
||||
);
|
||||
|
||||
printf(" LINK: %s\n",
|
||||
(flags & IFF_UP) != 0 ? "UP" : "DOWN");
|
||||
|
||||
if (netlib_get_ipv4addr(LORA_FWD_IFNAME, &addr) == OK)
|
||||
{
|
||||
printf(" IP: %s\n", inet_ntoa(addr));
|
||||
}
|
||||
|
||||
if (netlib_get_ipv4netmask(LORA_FWD_IFNAME, &addr) == OK)
|
||||
{
|
||||
printf(" NETMASK: %s\n", inet_ntoa(addr));
|
||||
}
|
||||
|
||||
if (netlib_get_dripv4addr(LORA_FWD_IFNAME, &addr) == OK)
|
||||
{
|
||||
printf(" GATEWAY: %s\n", inet_ntoa(addr));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_mac
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_mac(void)
|
||||
{
|
||||
FAR struct lora_fwd_config_s *config = lora_fwd_config();
|
||||
uint8_t mac[IFHWADDRLEN];
|
||||
|
||||
if (netlib_getmacaddr(LORA_FWD_IFNAME, mac) == OK)
|
||||
{
|
||||
printf(" MACADDR: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
printf(" GATEWAY ID: %02X%02X%02X%02X%02X%02X%02X%02X\n",
|
||||
config->eui[0], config->eui[1], config->eui[2], config->eui[3],
|
||||
config->eui[4], config->eui[5], config->eui[6], config->eui[7]);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_server
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_server(int argc, FAR char *argv[])
|
||||
{
|
||||
FAR struct lora_fwd_config_s *config = lora_fwd_config();
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
unsigned long port_up = (argc > 3) ? strtoul(argv[3], NULL, 10) : 0;
|
||||
unsigned long port_down = (argc > 4) ? strtoul(argv[4], NULL, 10) : 0;
|
||||
|
||||
lora_fwd_setserver(argv[2], port_up, port_down);
|
||||
|
||||
if (lora_fwd_isrunning())
|
||||
{
|
||||
printf("lora: restart the forwarder to use the new server\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" LORAWAN SERVER: %s\n", config->server);
|
||||
printf(" UPLINK UDP PORT: %u\n", config->port_up);
|
||||
printf("DOWNLINK UDP PORT: %u\n", config->port_down);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_ch
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_ch(int argc, FAR char *argv[])
|
||||
{
|
||||
struct lora_gw_regioninfo_s info;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
int fd = lora_opendev();
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, WLIOC_GW_SETREGION, (unsigned long)argv[2]);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "lora: cannot select %s: %d\n", argv[2], errno);
|
||||
close(fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Restart the concentrator so that the new plan takes effect */
|
||||
|
||||
if (lora_fwd_isrunning())
|
||||
{
|
||||
ioctl(fd, WLIOC_GW_RESET, 0);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
ret = lora_getregion(&info, -1);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "lora: cannot read the channel plan: %d\n", -ret);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("%s Channel Plan (%s):\n", info.name, info.desc);
|
||||
printf(" Radio A: %" PRIu32 " Hz\n", info.radio_freq[0]);
|
||||
printf(" Radio B: %" PRIu32 " Hz\n", info.radio_freq[1]);
|
||||
printf("\n");
|
||||
|
||||
lora_printchannels(&info, " ");
|
||||
|
||||
printf("\nAvailable regions:");
|
||||
for (i = 0; lora_getregion(&info, i) == OK; i++)
|
||||
{
|
||||
printf(" %s", info.name);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_status
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_status(void)
|
||||
{
|
||||
struct lora_gw_status_s status;
|
||||
struct lora_fwd_stats_s stats;
|
||||
|
||||
if (lora_getstatus(&status) < 0)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
lora_fwd_getstats(&stats);
|
||||
|
||||
printf("Concentrator: %s\n", status.started ? "RUNNING" : "STOPPED");
|
||||
printf("Pkt Forwarder: %s\n",
|
||||
lora_fwd_isrunning() ? "RUNNING" : "STOPPED");
|
||||
printf("Uptime: %" PRIu32 " s\n", stats.uptime_sec);
|
||||
printf("RX OK: %" PRIu32 "\n", status.rx_ok);
|
||||
printf("RX BAD: %" PRIu32 "\n", status.rx_bad);
|
||||
printf("RX NOCRC:%" PRIu32 "\n", status.rx_nocrc);
|
||||
printf("RX ERR: %" PRIu32 "\n", status.rx_err);
|
||||
printf("RX FWD: %" PRIu32 "\n", stats.rx_fwd);
|
||||
printf("TX OK: %" PRIu32 "\n", status.tx_ok);
|
||||
printf("TX ERR: %" PRIu32 "\n", status.tx_err);
|
||||
printf("PUSH: %" PRIu32 " sent, %" PRIu32 " acked\n",
|
||||
stats.push_sent, stats.push_ack);
|
||||
printf("PULL: %" PRIu32 " sent, %" PRIu32 " acked\n",
|
||||
stats.pull_sent, stats.pull_ack);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_sys
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_sys(void)
|
||||
{
|
||||
FAR struct lora_fwd_config_s *config = lora_fwd_config();
|
||||
struct lora_gw_regioninfo_s info;
|
||||
struct lora_gw_status_s status;
|
||||
|
||||
printf("\n");
|
||||
printf("Powered by NuttX & LoRa Gateway HAL\n");
|
||||
printf("%s\n", LORA_SEPARATOR);
|
||||
printf(" VERSION: %s (NuttX %s)\n", CONFIG_LORA_PKT_FWD_VERSION,
|
||||
CONFIG_VERSION_STRING);
|
||||
printf(" LORAWAN: Public\n");
|
||||
|
||||
lora_cmd_mac();
|
||||
|
||||
printf(" LORAWAN SERVER: %s\n", config->server);
|
||||
printf(" UPLINK UDP PORT: %u\n", config->port_up);
|
||||
printf("DOWNLINK UDP PORT: %u\n", config->port_down);
|
||||
|
||||
lora_cmd_ip();
|
||||
|
||||
if (lora_getregion(&info, -1) == OK)
|
||||
{
|
||||
printf(" REGION: %s (%s)\n", info.name, info.desc);
|
||||
lora_printchannels(&info, " ");
|
||||
}
|
||||
|
||||
printf("%s\n", LORA_SEPARATOR);
|
||||
|
||||
if (lora_getstatus(&status) == OK)
|
||||
{
|
||||
printf(" CONCENTRATOR: %s\n",
|
||||
status.started ? "Running" : "Stopped");
|
||||
}
|
||||
|
||||
printf(" PKT FWD: %s\n",
|
||||
lora_fwd_isrunning() ? "Running" : "Stopped");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_tx
|
||||
*
|
||||
* Description:
|
||||
* Transmit a single packet. This exists to bring a gateway up without a
|
||||
* network server: any LoRa receiver tuned to the same frequency, spreading
|
||||
* factor and bandwidth sees it. The polarity is the one of an uplink, not
|
||||
* the inverted one of a LoRaWAN downlink, so that a plain end device radio
|
||||
* can pick it up.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_tx(int argc, FAR char *argv[])
|
||||
{
|
||||
struct lora_gw_txpkt_s txpkt;
|
||||
FAR const char *text;
|
||||
unsigned long datarate;
|
||||
size_t len;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "lora: usage: lora tx FREQ_HZ [SF [TEXT]]\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
memset(&txpkt, 0, sizeof(txpkt));
|
||||
|
||||
datarate = (argc > 3) ? strtoul(argv[3], NULL, 10) : 7;
|
||||
text = (argc > 4) ? argv[4] : "NuttX LoRa gateway";
|
||||
|
||||
if (datarate < 6 || datarate > 12)
|
||||
{
|
||||
fprintf(stderr, "lora: spreading factor out of range\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
len = strlen(text);
|
||||
if (len > sizeof(txpkt.payload))
|
||||
{
|
||||
len = sizeof(txpkt.payload);
|
||||
}
|
||||
|
||||
txpkt.freq_hz = strtoul(argv[2], NULL, 10);
|
||||
txpkt.datarate = datarate;
|
||||
|
||||
txpkt.tx_mode = LORA_GW_TX_IMMEDIATE;
|
||||
txpkt.rf_chain = 0;
|
||||
txpkt.rf_power = CONFIG_LORA_PKT_FWD_TXPOWER;
|
||||
txpkt.modulation = LORA_GW_MOD_LORA;
|
||||
txpkt.bandwidth = LORA_GW_BW_125K;
|
||||
txpkt.coderate = WLIOC_LORA_CR_4_5;
|
||||
txpkt.preamble = 8;
|
||||
txpkt.size = len;
|
||||
|
||||
memcpy(txpkt.payload, text, len);
|
||||
|
||||
fd = lora_opendev();
|
||||
if (fd < 0)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = write(fd, &txpkt, sizeof(txpkt));
|
||||
close(fd);
|
||||
|
||||
if (ret != sizeof(txpkt))
|
||||
{
|
||||
fprintf(stderr, "lora: transmit failed: %d\n", errno);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("lora: sent %zu bytes at %" PRIu32 " Hz, SF%lu\n",
|
||||
len, txpkt.freq_hz, datarate);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lora_cmd_reset
|
||||
****************************************************************************/
|
||||
|
||||
static int lora_cmd_reset(void)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = lora_opendev();
|
||||
if (fd < 0)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, WLIOC_GW_RESET, 0);
|
||||
close(fd);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "lora: reset failed: %d\n", errno);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("lora: concentrator restarted\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: main
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, FAR char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
lora_fwd_init();
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
lora_usage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "sys") == 0)
|
||||
{
|
||||
return lora_cmd_sys();
|
||||
}
|
||||
else if (strcmp(argv[1], "ver") == 0)
|
||||
{
|
||||
return lora_cmd_ver();
|
||||
}
|
||||
else if (strcmp(argv[1], "status") == 0)
|
||||
{
|
||||
return lora_cmd_status();
|
||||
}
|
||||
else if (strcmp(argv[1], "ch") == 0)
|
||||
{
|
||||
return lora_cmd_ch(argc, argv);
|
||||
}
|
||||
else if (strcmp(argv[1], "server") == 0)
|
||||
{
|
||||
return lora_cmd_server(argc, argv);
|
||||
}
|
||||
else if (strcmp(argv[1], "ip") == 0)
|
||||
{
|
||||
return lora_cmd_ip();
|
||||
}
|
||||
else if (strcmp(argv[1], "mac") == 0)
|
||||
{
|
||||
return lora_cmd_mac();
|
||||
}
|
||||
else if (strcmp(argv[1], "reset") == 0)
|
||||
{
|
||||
return lora_cmd_reset();
|
||||
}
|
||||
else if (strcmp(argv[1], "tx") == 0)
|
||||
{
|
||||
return lora_cmd_tx(argc, argv);
|
||||
}
|
||||
else if (strcmp(argv[1], "start") == 0)
|
||||
{
|
||||
ret = lora_fwd_start();
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "lora: cannot start the forwarder: %d\n", -ret);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (strcmp(argv[1], "stop") == 0)
|
||||
{
|
||||
ret = lora_fwd_stop();
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stderr, "lora: cannot stop the forwarder: %d\n", -ret);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
fprintf(stderr, "lora: unknown subcommand '%s'\n", argv[1]);
|
||||
lora_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
1242
wireless/lora_pkt_fwd/lora_pkt_fwd.c
Normal file
1242
wireless/lora_pkt_fwd/lora_pkt_fwd.c
Normal file
File diff suppressed because it is too large
Load diff
109
wireless/lora_pkt_fwd/lora_pkt_fwd.h
Normal file
109
wireless/lora_pkt_fwd/lora_pkt_fwd.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/****************************************************************************
|
||||
* apps/wireless/lora_pkt_fwd/lora_pkt_fwd.h
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __APPS_WIRELESS_LORA_PKT_FWD_LORA_PKT_FWD_H
|
||||
#define __APPS_WIRELESS_LORA_PKT_FWD_LORA_PKT_FWD_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define LORA_FWD_HOSTLEN 64
|
||||
#define LORA_FWD_EUILEN 8
|
||||
#define LORA_FWD_IFNAME "eth0"
|
||||
|
||||
/* Semtech UDP protocol version 2 packet types. Note that the numbering is
|
||||
* not sequential: PULL_RESP comes before PULL_ACK. Swapping those two makes
|
||||
* a gateway answer INVALID_JSON to every keepalive.
|
||||
*/
|
||||
|
||||
#define LORA_PKT_PUSH_DATA 0x00
|
||||
#define LORA_PKT_PUSH_ACK 0x01
|
||||
#define LORA_PKT_PULL_DATA 0x02
|
||||
#define LORA_PKT_PULL_RESP 0x03
|
||||
#define LORA_PKT_PULL_ACK 0x04
|
||||
#define LORA_PKT_TX_ACK 0x05
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
/* Runtime configuration of the forwarder ***********************************/
|
||||
|
||||
struct lora_fwd_config_s
|
||||
{
|
||||
char server[LORA_FWD_HOSTLEN]; /* Host name or address of the server */
|
||||
uint16_t port_up;
|
||||
uint16_t port_down;
|
||||
uint8_t eui[LORA_FWD_EUILEN]; /* Gateway identifier */
|
||||
int keepalive_sec;
|
||||
int stat_sec;
|
||||
};
|
||||
|
||||
/* Counters of the forwarder itself. The radio side counters come from the
|
||||
* driver with WLIOC_GW_GETSTATUS.
|
||||
*/
|
||||
|
||||
struct lora_fwd_stats_s
|
||||
{
|
||||
uint32_t rx_fwd; /* Packets forwarded upstream */
|
||||
uint32_t rx_drop; /* Packets dropped before forwarding */
|
||||
uint32_t tx_req; /* Downlink requests received */
|
||||
uint32_t tx_ok; /* Downlinks handed to the concentrator */
|
||||
uint32_t push_sent;
|
||||
uint32_t push_ack;
|
||||
uint32_t pull_sent;
|
||||
uint32_t pull_ack;
|
||||
uint32_t uptime_sec;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/* Daemon control, lora_pkt_fwd.c *******************************************/
|
||||
|
||||
void lora_fwd_init(void);
|
||||
int lora_fwd_start(void);
|
||||
int lora_fwd_stop(void);
|
||||
bool lora_fwd_isrunning(void);
|
||||
|
||||
/* Configuration and statistics. The daemon runs as its own task and shares
|
||||
* this state with the "lora" command through the flat address space, which
|
||||
* is why the counters are only visible in a flat build.
|
||||
*/
|
||||
|
||||
FAR struct lora_fwd_config_s *lora_fwd_config(void);
|
||||
void lora_fwd_getstats(FAR struct lora_fwd_stats_s *stats);
|
||||
int lora_fwd_setserver(FAR const char *host, int port_up, int port_down);
|
||||
int lora_fwd_parse_eui(FAR const char *str, FAR uint8_t *eui);
|
||||
|
||||
#endif /* __APPS_WIRELESS_LORA_PKT_FWD_LORA_PKT_FWD_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue