1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 06:23:53 +01:00

Merge pull request #7505 from aabadie/driver_lorabee

drivers/rn2xx3: basic initial implementation (without netdev)
This commit is contained in:
Kaspar Schleiser 2018-01-18 15:37:08 +01:00 committed by GitHub
commit b30eca3e05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 2427 additions and 0 deletions

View File

@ -230,6 +230,14 @@ ifneq (,$(filter rgbled,$(USEMODULE)))
USEMODULE += color
endif
ifneq (,$(filter rn2%3,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_uart
USEMODULE += xtimer
USEMODULE += rn2xx3
USEMODULE += fmt
endif
ifneq (,$(filter sdcard_spi,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_spi

View File

@ -157,3 +157,6 @@ endif
ifneq (,$(filter lis3mdl,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/lis3mdl/include
endif
ifneq (,$(filter rn2%3,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/rn2xx3/include
endif

690
drivers/include/rn2xx3.h Normal file
View File

@ -0,0 +1,690 @@
/*
* Copyright (C) 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.
*/
/**
* @defgroup drivers_rn2xx3 RN2483/RN2903 LoRa module driver
* @ingroup drivers_netdev
* @brief High-level driver for the RN2483/RN2903 LoRa modules
* @{
*
* @file
* @brief High-level driver for the RN2483/RN2903 LoRa modules
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef RN2XX3_H
#define RN2XX3_H
#include <stdint.h>
#include "xtimer.h"
#include "periph/uart.h"
#include "periph/gpio.h"
#include "net/netdev.h"
#include "net/loramac.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Maximum length of an exchanged messages with commands
*/
#define RN2XX3_MAX_BUF (40U)
/**
* @brief Maximum length of an RX message
*
* LoRaMAC payload can be up to 250 bytes.
*/
#define RN2XX3_RX_MAX_BUF (250U)
/**
* @brief Maximum delay in second to receive a reply from server.
*/
#define RN2XX3_REPLY_DELAY_TIMEOUT (60U)
/**
* @brief Minimum sleep duration (in ms)
*/
#define RN2XX3_SLEEP_MIN (100U)
/**
* @brief Default sleep duration (in ms)
*/
#ifndef RN2XX3_DEFAULT_SLEEP
#define RN2XX3_DEFAULT_SLEEP (5000U)
#endif
#if defined(MODULE_RN2903)
#define RN2XX3_FREQ_BAND (915U)
#elif defined(MODULE_RN2483)
#define RN2XX3_FREQ_BAND (868U)
#else
#error "Unsupported module type, use either RN2483 or RN2903"
#endif
/**
* @brief Command responses and server replies status codes
*/
enum {
RN2XX3_OK, /**< Command is valid */
RN2XX3_DATA, /**< Command returned data */
RN2XX3_TIMEOUT, /**< Command timeout */
/* all other error codes */
RN2XX3_ERR_MAC_INIT, /**< Device mac initialization failed */
RN2XX3_ERR_INVALID_PARAM, /**< Wrong command given */
RN2XX3_ERR_NOT_JOINED, /**< Network is not joined */
RN2XX3_ERR_NO_FREE_CH, /**< All channels are busy */
RN2XX3_ERR_SILENT, /**< Device is in Silent Immediately state */
RN2XX3_ERR_FR_CNT_REJOIN_NEEDED, /**< Frame counter rolled over */
RN2XX3_ERR_BUSY, /**< MAC is not in Idle state */
RN2XX3_ERR_MAC_PAUSED, /**< MAC was paused */
RN2XX3_ERR_INVALID_DATA_LENGTH, /**< Wrong payload given */
RN2XX3_ERR_KEYS_NOT_INIT, /**< Keys not configured ("mac join" command) */
RN2XX3_ERR_SLEEP_MODE, /**< Failure because device is in sleep mode */
/* all other reply codes */
RN2XX3_REPLY_TX_MAC_OK, /**< MAC transmission successful */
RN2XX3_REPLY_TX_MAC_ERR, /**< MAC transmission failed */
RN2XX3_REPLY_TX_INVALID_DATA_LEN, /**< Application payload too large */
RN2XX3_REPLY_TX_MAC_RX, /**< Data received from server */
RN2XX3_REPLY_JOIN_ACCEPTED, /**< Join procedure successful */
RN2XX3_REPLY_JOIN_DENIED, /**< Join procedure failed */
RN2XX3_REPLY_TIMEOUT, /**< No MAC reply received from server */
RN2XX3_REPLY_OTHER, /**< Unknown reply */
};
/**
* @brief Internal states of the device
*/
enum {
RN2XX3_INT_STATE_RESET, /**< the device is being reset or initialized */
RN2XX3_INT_STATE_CMD, /**< waiting command response */
RN2XX3_INT_STATE_IDLE, /**< waiting for incoming commands */
RN2XX3_INT_STATE_SLEEP, /**< sleep mode */
RN2XX3_INT_STATE_MAC_JOIN, /**< waiting for mac join procedure reply */
RN2XX3_INT_STATE_MAC_TX, /**< waiting for mac tx reply */
RN2XX3_INT_STATE_MAC_RX_PORT, /**< waiting for mac rx port */
RN2XX3_INT_STATE_MAC_RX_MESSAGE, /**< waiting for mac rx message */
};
/**
* @brief LoRaMAC communication settings
*/
typedef struct {
uint32_t rx2_freq; /**< Center frequency used for second receive window */
loramac_dr_idx_t rx2_dr; /**< Datarate used for second receive window */
uint8_t tx_port; /**< Application TX port (between 1 and 223) */
loramac_tx_mode_t tx_mode; /**< TX mode, either confirmable of unconfirmable */
uint8_t rx_port; /**< RX port (between 1 and 223) */
} loramac_settings_t;
/**
* @brief Configuration parameters for RN2483/RN2903 devices
*/
typedef struct {
uart_t uart; /**< UART interfaced the device is connected to */
uint32_t baudrate; /**< baudrate to use */
gpio_t pin_reset; /**< GPIO pin that is connected to the STATUS pin
set to GPIO_UNDEF if not used */
} rn2xx3_params_t;
/**
* @brief RN2483/RN2903 device descriptor
*/
typedef struct {
netdev_t netdev; /**< Netdev parent struct */
/* device driver specific fields */
rn2xx3_params_t p; /**< configuration parameters */
loramac_settings_t loramac; /**< loramac communication settings */
/* values for the UART TX state machine */
char cmd_buf[RN2XX3_MAX_BUF]; /**< command to send data buffer */
mutex_t cmd_lock; /**< mutex to allow only one
* command at a time */
uint8_t int_state; /**< current state of the device */
/* buffer and synchronization for command responses */
mutex_t resp_lock; /**< mutex for waiting for command
* response */
char resp_buf[RN2XX3_MAX_BUF]; /**< command response data buffer */
uint16_t resp_size; /**< counter for received char in response */
uint8_t resp_done; /**< check if response has completed */
/* buffer for RX messages */
char rx_tmp_buf[2]; /**< Temporary RX buffer used to convert
* 2 hex characters in one byte on the
* fly. */
uint8_t rx_buf[RN2XX3_RX_MAX_BUF]; /**< RX data buffer */
uint16_t rx_size; /**< counter for received char in RX */
/* timers */
xtimer_t sleep_timer; /**< Timer used to count module sleep time */
uint32_t sleep; /**< module sleep duration */
} rn2xx3_t;
/**
* @brief Prepares the given RN2XX3 device
*
* @param[out] dev RN2XX3 device to initialize
* @param[in] params parameters for device initialization
*/
void rn2xx3_setup(rn2xx3_t *dev, const rn2xx3_params_t *params);
/**
* @brief Initializes the RN2XX3 device
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return -ENXIO if UART initialization failed
* @return RN2XX3_TIMEOUT if UART communication failed
*/
int rn2xx3_init(rn2xx3_t *dev);
/**
* @brief Restarts the RN2XX2 device
*
* After calling this function, dev->resp_buf contains the module
* name and version string.
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return RN2XX3_TIMEOUT if UART communication failed
*/
int rn2xx3_sys_reset(rn2xx3_t *dev);
/**
* @brief Performs a factory reset of the module
*
* The configuration data and user EEPPROM are reinitialized to factory default
* values and the module reboots
*
* After calling this function, dev->resp_buf contains the module name and
* version string.
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return RN2XX3_TIMEOUT if UART communication failed
*/
int rn2xx3_sys_factory_reset(rn2xx3_t *dev);
/**
* @brief Puts the RN2XX2 device in sleep mode
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return RN2XX3_TIMEOUT if UART communication failed
*/
int rn2xx3_sys_sleep(rn2xx3_t *dev);
/**
* @brief Initializes the RN2XX3 device MAC layer
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return RN2XX3_TIMEOUT if UART communication failed
*/
int rn2xx3_mac_init(rn2xx3_t *dev);
/**
* @brief Writes a command to the RN2XX3 device
*
* The module will immediately reply with a meaningful message if the command
* is valid or not.
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return RN2XX3_TIMEOUT if no response is received from the module
* @return RN2XX3_ERR_INVALID_PARAM if command is invalid
* @return RN2XX3_ERR_NOT_JOINED if network is not joined
* @return RN2XX3_ERR_NO_FREE_CH if no free channel
* @return RN2XX3_ERR_SILENT if device is in Silent state
* @return RN2XX3_ERR_FR_CNT_REJOIN_NEEDED if frame counter rolled over
* @return RN2XX3_ERR_BUSY if MAC is not in Idle state
* @return RN2XX3_ERR_INVALID_DATA_LENGTH if payload is too large
* @return RN2XX3_ERR_KEYS_NOT_INIT if keys are not configured
*/
int rn2xx3_write_cmd(rn2xx3_t *dev);
/**
* @brief Writes a command to the RN2XX3 device but don't wait for timeout
*
* The module will immediately reply with a meaningful message if the command
* is valid or not.
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return RN2XX3_ERR_INVALID_PARAM if command is invalid
* @return RN2XX3_ERR_NOT_JOINED if network is not joined
* @return RN2XX3_ERR_NO_FREE_CH if no free channel
* @return RN2XX3_ERR_SILENT if device is in Silent state
* @return RN2XX3_ERR_FR_CNT_REJOIN_NEEDED if frame counter rolled over
* @return RN2XX3_ERR_BUSY if MAC is not in Idle state
* @return RN2XX3_ERR_INVALID_DATA_LENGTH if payload is too large
* @return RN2XX3_ERR_KEYS_NOT_INIT if keys are not configured
*/
int rn2xx3_write_cmd_no_wait(rn2xx3_t *dev);
/**
* @brief Waits for a response to a command from the device
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK on success
* @return RN2XX3_TIMEOUT if no response is received from the module
* @return RN2XX3_ERR_INVALID_PARAM if command is invalid
* @return RN2XX3_ERR_NOT_JOINED if network is not joined
* @return RN2XX3_ERR_NO_FREE_CH if no free channel
* @return RN2XX3_ERR_SILENT if device is in Silent state
* @return RN2XX3_ERR_FR_CNT_REJOIN_NEEDED if frame counter rolled over
* @return RN2XX3_ERR_BUSY if MAC is not in Idle state
* @return RN2XX3_ERR_INVALID_DATA_LENGTH if payload is too large
* @return RN2XX3_ERR_KEYS_NOT_INIT if keys are not configured
*/
int rn2xx3_wait_response(rn2xx3_t *dev);
/**
* @brief Waits for a reply from the LoRaWAN network
*
* @param[in] dev LoRaBee device descriptor
* @param[in] timeout Reply wait timeout in seconds
*
* @return RN2XX3_REPLY_TIMEOUT when no MAC reply is received from server
* @return RN2XX3_REPLY_TX_MAC_OK when MAC transmission succeeded
* @return RN2XX3_REPLY_TX_MAC_ERR when MAC transmission failed
* @return RN2XX3_REPLY_TX_MAC_RX when received downlink data from server
* @return RN2XX3_REPLY_TX_INVALID_DATA_LEN when Application payload is too large
* @return RN2XX3_REPLY_JOIN_ACCEPTED when the join procedure succeded
* @return RN2XX3_REPLY_JOIN_DENIED when the join procedure failed
* @return RN2XX3_REPLY_OTHER when an unknown reply is received
*/
int rn2xx3_wait_reply(rn2xx3_t *dev, uint8_t timeout);
/**
* @brief Sends data to LoRaWAN server
*
* @param[in] dev RN2XX3 device descriptor
* @param[in] payload Payload to transmit
* @param[in] payload_len Payload length to transmit
*
* @return RN2XX3_ERR_KEYS_NOT_INIT if the loramac params are not configured
* @return RN2XX3_ERR_NO_FREE_CH if channels are busy
* @return RN2XX3_ERR_SILENT if device is in Silent state
* @return RN2XX3_ERR_BUSY if MAC layer is in idle state
* @return RN2XX3_ERR_MAC_PAUSED if MAC layed is paused
* @return RN2XX3_REPLY_TX_INVALID_DATA_LEN if payload is too large
* @return RN2XX3_REPLY_TX_MAC_ERR when MAC transmission failed
* @return RN2XX3_REPLY_TX_MAC_RX when received downlink data from server
* @return RN2XX3_REPLY_TX_MAC_OK when MAC transmission succeeded
*/
int rn2xx3_mac_tx(rn2xx3_t *dev, uint8_t *payload, uint8_t payload_len);
/**
* @brief Starts network activation procedure
*
* @param[in] dev RN2XX3 device descriptor
* @param[in] mode Activation procedure type
*
* @return RN2XX3_ERR_KEYS_NOT_INIT if the loramac params are not configured
* @return RN2XX3_ERR_NO_FREE_CH if channels are busy
* @return RN2XX3_ERR_SILENT if device is in Silent state
* @return RN2XX3_ERR_BUSY if MAC layer is in idle state
* @return RN2XX3_ERR_MAC_PAUSED if MAC layed is paused
* @return RN2XX3_REPLY_JOIN_ACCEPTED when the join procedure succeded
* @return RN2XX3_REPLY_JOIN_DENIED when the join procedure failed
*/
int rn2xx3_mac_join_network(rn2xx3_t *dev, loramac_join_mode_t mode);
/**
* @brief Saves current LoRaMAC configuration to internal EEPROM
*
* The configuration parameters saved are: frequency band, end device EUI,
* application EUI, application key, network session key, application session
* key, end device EUI and all channel parameters.
*
* @param[in] dev RN2XX3 device descriptor
*
* @return RN2XX3_OK if all is ok
* @return RN2XX3_TIMEOUT if the device didn't reply
*/
int rn2xx3_mac_save(rn2xx3_t *dev);
/* Get/Set functions definitions */
/**
* @brief Gets the rn2xx3 LoRaMAC device EUI
*
* The device EUI is an array of 8 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[out] eui The device EUI
*/
void rn2xx3_mac_get_deveui(rn2xx3_t *dev, uint8_t *eui);
/**
* @brief Sets the rn2xx3 LoRaMAC device EUI
*
* The device EUI is an array of 8 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] eui The device EUI
*/
void rn2xx3_mac_set_deveui(rn2xx3_t *dev, const uint8_t *eui);
/**
* @brief Gets the rn2xx3 LoRaMAC application EUI
*
* The application EUI is an array of 8 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[out] eui The application EUI
*/
void rn2xx3_mac_get_appeui(rn2xx3_t *dev, uint8_t *eui);
/**
* @brief Sets the rn2xx3 LoRaMAC application EUI
*
* The application key is an array of 8 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] eui The application EUI
*/
void rn2xx3_mac_set_appeui(rn2xx3_t *dev, const uint8_t *eui);
/**
* @brief Sets the rn2xx3 LoRaMAC application key
*
* The application key is an array of 16 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] key The application key
*/
void rn2xx3_mac_set_appkey(rn2xx3_t *dev, const uint8_t *key);
/**
* @brief Sets the rn2xx3 LoRaMAC application session key
*
* The application session key is an array of 16 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] key The application session key
*/
void rn2xx3_mac_set_appskey(rn2xx3_t *dev, const uint8_t *key);
/**
* @brief Sets the rn2xx3 LoRaMAC network session key
*
* The network session key is an array of 16 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] key The network session key
*/
void rn2xx3_mac_set_nwkskey(rn2xx3_t *dev, const uint8_t *key);
/**
* @brief Gets the rn2xx3 LoRaMAC device address
*
* The device address is an array of 4 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[out] addr The device address
*/
void rn2xx3_mac_get_devaddr(rn2xx3_t *dev, uint8_t *addr);
/**
* @brief Sets the rn2xx3 LoRaMAC device address
*
* The device address is an array of 4 bytes.
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] addr The device address
*/
void rn2xx3_mac_set_devaddr(rn2xx3_t *dev, const uint8_t *addr);
/**
* @brief Gets the rn2xx3 LoRaMAC TX radio power index
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The radio power index
*/
loramac_tx_pwr_idx_t rn2xx3_mac_get_tx_power(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC transmission power index
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] power The TX power index
*/
void rn2xx3_mac_set_tx_power(rn2xx3_t *dev, loramac_tx_pwr_idx_t power);
/**
* @brief Gets the rn2xx3 LoRaMAC datarate
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The datarate
*/
loramac_dr_idx_t rn2xx3_mac_get_dr(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC datarate
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] dr The datarate
*/
void rn2xx3_mac_set_dr(rn2xx3_t *dev, loramac_dr_idx_t dr);
/**
* @brief Gets the rn2xx3 LoRaMAC frequency band in operation
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The frequency band
*/
uint16_t rn2xx3_mac_get_band(rn2xx3_t *dev);
/**
* @brief Checks if the rn2xx3 LoRaMAC adaptive datarate is enabled/disabled
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return true if adaptive datarate is set,
* false otherwise
*/
bool rn2xx3_mac_get_adr(rn2xx3_t *dev);
/**
* @brief Enables/disables the rn2xx3 LoRaMAC adaptive datarate
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] adr The adaptive datarate mode
*/
void rn2xx3_mac_set_adr(rn2xx3_t *dev, bool adr);
/**
* @brief Sets the rn2xx3 battery level measured by the end device
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] battery The battery level:
* - 0 means external power,
* - 1 means low level,
* - 254 high level,
* - 255 means the battery level couldn't be
* measured by the device.
*/
void rn2xx3_mac_set_battery(rn2xx3_t *dev, uint8_t battery);
/**
* @brief Gets the rn2xx3 LoRaMAC uplink retransmission retries number
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The number of uplink retransmission retries
*/
uint8_t rn2xx3_mac_get_retx(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC uplink retransmission retries number
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] retx The number of uplink retransmission retries
*/
void rn2xx3_mac_set_retx(rn2xx3_t *dev, uint8_t retx);
/**
* @brief Sets the rn2xx3 LoRaMAC link check interval (in seconds)
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] linkchk The link check interval in seconds
*/
void rn2xx3_mac_set_linkchk_interval(rn2xx3_t *dev, uint16_t linkchk);
/**
* @brief Gets the rn2xx3 LoRaMAC interval delay before the first reception window (in ms)
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The delay in ms
*/
uint16_t rn2xx3_mac_get_rx1_delay(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC interval delay before the first reception window (in ms)
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] rx1 The delay in ms
*/
void rn2xx3_mac_set_rx1_delay(rn2xx3_t *dev, uint16_t rx1);
/**
* @brief Gets the rn2xx3 LoRaMAC interval delay before the second reception window (in ms)
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The delay in ms
*/
uint16_t rn2xx3_mac_get_rx2_delay(rn2xx3_t *dev);
/**
* @brief Checks the rn2xx3 LoRaMAC automatic reply state
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The automatic reply state
*/
bool rn2xx3_mac_get_ar(rn2xx3_t *dev);
/**
* @brief Enables/disables LoRaMAC rn2xx3 MAC automatic reply state
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] ar The automatic reply state
*/
void rn2xx3_mac_set_ar(rn2xx3_t *dev, bool ar);
/**
* @brief Gets the rn2xx3 LoRaMAC datarate used for second receive window
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The datarate during second receive window
*/
loramac_dr_idx_t rn2xx3_mac_get_rx2_dr(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC datarate used for second receive window
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] dr The datarate during second receive window
*/
void rn2xx3_mac_set_rx2_dr(rn2xx3_t *dev, loramac_dr_idx_t dr);
/**
* @brief Gets the rn2xx3 LoRaMAC frequency used during second receive window (in Hz)
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The frequency during second receive window
*/
uint32_t rn2xx3_mac_get_rx2_freq(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC frequency used during second receive window (in Hz)
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] freq The frequency during second receive window
*/
void rn2xx3_mac_set_rx2_freq(rn2xx3_t *dev, uint32_t freq);
/**
* @brief Gets the rn2xx3 LoRaMAC TX port
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The TX port
*/
uint8_t rn2xx3_mac_get_tx_port(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC TX port
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] port The TX port (from 1 to 223)
*/
void rn2xx3_mac_set_tx_port(rn2xx3_t *dev, uint8_t port);
/**
* @brief Gets the rn2xx3 LoRaMAC TX mode
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The TX mode, either confirmable or unconfirmable
*/
loramac_tx_mode_t rn2xx3_mac_get_tx_mode(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 LoRaMAC TX mode
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] mode The TX mode, either confirmable or unconfirmable
*/
void rn2xx3_mac_set_tx_mode(rn2xx3_t *dev, loramac_tx_mode_t mode);
/**
* @brief Parses the response buffer to get the LoRaWAN RX port
*
* @param[in] dev The rn2xx3 device descriptor
*
* @return The RX port (between 1 and 223)
*/
uint8_t rn2xx3_mac_get_rx_port(rn2xx3_t *dev);
/**
* @brief Sets the rn2xx3 sleep mode duration (in ms)
*
* @param[in] dev The rn2xx3 device descriptor
* @param[in] sleep The sleep mode duration (ms)
*/
void rn2xx3_sys_set_sleep_duration(rn2xx3_t *dev, uint32_t sleep);
#ifdef __cplusplus
}
#endif
#endif /* RN2XX3_H */
/** @} */

1
drivers/rn2xx3/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 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 drivers_rn2xx3
* @{
*
* @file
* @brief Internal driver definitions for the RN2483/RN2903 LoRa modules
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef RN2XX3_INTERNAL_H
#define RN2XX3_INTERNAL_H
#include <stdint.h>
#include "rn2xx3.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Convert a string of hex to an array of bytes
*
* This functions is faster than `fmt_hex_bytes` that doesn't work if it is
* used in the module UART interrupt callback.
*
* @param[in] hex The string of hex to convert
* @param[out] byte_array The resulting array of bytes
*/
void rn2xx3_hex_to_bytes(const char *hex, uint8_t *byte_array);
/**
* @brief Sets internal device state
*
* @param[in] dev The device descriptor
* @param[in] state The desired state
*/
void rn2xx3_set_internal_state(rn2xx3_t *dev, uint8_t state);
/**
* @brief Starts writing a command with the content of the device command buffer
*
* @param[in] dev The device descriptor
*/
void rn2xx3_cmd_start(rn2xx3_t *dev);
/**
* @brief Appends data to a command
*
* @param[in] dev The device descriptor
* @param[in] data The data buffer
* @param[in] data_len The data max length
*/
void rn2xx3_cmd_append(rn2xx3_t *dev, const uint8_t *data, uint8_t data_len);
/**
* @brief Finalize a command
*
* @param[in] dev The device descriptor
*
* @return RN2XX3_OK if the command succeeded
* @return RN2XX3_ERR_* otherwise
*/
int rn2xx3_cmd_finalize(rn2xx3_t *dev);
/**
* @brief Starts writing a tx command
*
* @param[in] dev The device descriptor
*/
void rn2xx3_mac_tx_start(rn2xx3_t *dev);
/**
* @brief Finalize the TX command
*
* @param[in] dev The device descriptor
*
* @return RN2XX3_OK if the command succeeded
* @return RN2XX3_REPLY_* otherwise
*/
int rn2xx3_mac_tx_finalize(rn2xx3_t *dev);
/**
* @brief Process a command immediate response
*
* @param[in] dev The device descriptor
*
* @return RN2XX3_OK if the command succeeded
* @return RN2XX3_ERR_* otherwise
*/
int rn2xx3_process_response(rn2xx3_t *dev);
/**
* @brief Process a command network reply
*
* @param[in] dev The device descriptor
*
* @return RN2XX3_OK if the command succeeded
* @return RN2XX3_REPLY_* otherwise
*/
int rn2xx3_process_reply(rn2xx3_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* RN2XX3_INTERNAL_H */
/** @} */

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 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 drivers_rn2xx3
* @{
*
* @file
* @brief Default configuration for RN2483/RN2903 devices
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*/
#ifndef RN2XX3_PARAMS_H
#define RN2XX3_PARAMS_H
#include "rn2xx3.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Set default configuration parameters for the RN2483/RN2903 driver
* @{
*/
#ifndef RN2XX3_PARAM_UART
#define RN2XX3_PARAM_UART UART_DEV(1)
#endif
#ifndef RN2XX3_PARAM_BAUDRATE
#define RN2XX3_PARAM_BAUDRATE (57600U)
#endif
#ifndef RN2XX3_PARAM_PIN_RESET
#define RN2XX3_PARAM_PIN_RESET (GPIO_UNDEF)
#endif
#ifndef RN2XX3_PARAMS
#define RN2XX3_PARAMS { .uart = RN2XX3_PARAM_UART, \
.baudrate = RN2XX3_PARAM_BAUDRATE, \
.pin_reset = RN2XX3_PARAM_PIN_RESET }
#endif
/**@}*/
/**
* @brief RN2483/RN2903 configuration
*/
static const rn2xx3_params_t rn2xx3_params[] =
{
RN2XX3_PARAMS
};
#ifdef __cplusplus
}
#endif
#endif /* RN2XX3_PARAMS_H */
/** @} */

309
drivers/rn2xx3/rn2xx3.c Normal file
View File

@ -0,0 +1,309 @@
/*
* Copyright (C) 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 drivers_rn2xx3
* @{
*
* @file
* @brief Driver implementation for the RN2483/RN2903 devices
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#include <string.h>
#include <errno.h>
#include "assert.h"
#include "xtimer.h"
#include "fmt.h"
#include "rn2xx3_params.h"
#include "rn2xx3.h"
#include "rn2xx3_internal.h"
#define ENABLE_DEBUG (0)
/* Warning: to correctly display the debug message from sleep timer callback,,
add CFLAGS+=-DTHREAD_STACKSIZE_IDLE=THREAD_STACKSIZE_DEFAULT to the build
command.
*/
#include "debug.h"
/**
* @brief Delay when resetting the device, 10ms
*/
#define RESET_DELAY (10UL * US_PER_MS)
/*
* Interrupt callbacks
*/
static void _rx_cb(void *arg, uint8_t c)
{
rn2xx3_t *dev = (rn2xx3_t *)arg;
netdev_t *netdev = (netdev_t *)dev;
/* Avoid overflow of module response buffer */
if (dev->resp_size >= RN2XX3_MAX_BUF) {
return;
}
/* The device closes the response with \r\n when done */
if (c == '\r') {
return;
}
else if (c == '\n') { /* response end delimiter */
dev->resp_buf[dev->resp_size++] = '\0';
if (dev->int_state == RN2XX3_INT_STATE_MAC_RX_MESSAGE) {
/* RX state: closing RX buffer */
dev->rx_buf[(dev->rx_size + 1) / 2] = 0;
if (netdev->event_callback) {
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
}
}
else if (dev->int_state == RN2XX3_INT_STATE_MAC_TX) {
/* still in TX state: transmission complete but no data received */
if (netdev->event_callback) {
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
}
}
dev->resp_size = 0;
dev->rx_size = 0;
dev->resp_done = 1;
mutex_unlock(&(dev->resp_lock));
}
else {
switch (dev->int_state) {
/* A successful mac TX command expects 2 kinds of replies:
* - mac_tx_ok: transmission done, no data received
* - mac_rx <port> <data>: transmission done, some data
* received on port <port>. */
case RN2XX3_INT_STATE_MAC_TX:
/* some data are available */
dev->resp_buf[dev->resp_size++] = c;
/* if module response ends with 'rx ' after 8 received chars,
the module starts receiving data */
if (dev->resp_size == 8 && dev->resp_buf[4] == 'r'
&& dev->resp_buf[5] == 'x' && dev->resp_buf[6] == ' ') {
/* next received chars correspond to the port number */
dev->int_state = RN2XX3_INT_STATE_MAC_RX_PORT;
}
break;
case RN2XX3_INT_STATE_MAC_RX_PORT:
dev->resp_buf[dev->resp_size++] = c;
if (c == ' ') {
dev->int_state = RN2XX3_INT_STATE_MAC_RX_MESSAGE;
}
break;
case RN2XX3_INT_STATE_MAC_RX_MESSAGE:
/* read and convert RX data (received in hex chars) */
if (c == ' ') {
dev->resp_buf[dev->resp_size++] = c;
break;
}
dev->rx_tmp_buf[dev->rx_size % 2] = c;
/* We convert pairs of hex character to bytes on the fly to
save space in memory */
if (dev->rx_size != 0 && dev->rx_size % 2) {
rn2xx3_hex_to_bytes(dev->rx_tmp_buf,
&dev->rx_buf[(dev->rx_size - 1) / 2]);
}
dev->rx_size++;
break;
default:
dev->resp_buf[dev->resp_size++] = c;
break;
}
}
}
static void _sleep_timer_cb(void *arg)
{
DEBUG("[rn2xx3] exit sleep\n");
rn2xx3_t *dev = (rn2xx3_t *)arg;
dev->int_state = RN2XX3_INT_STATE_IDLE;
}
/*
* Driver's "public" functions
*/
void rn2xx3_setup(rn2xx3_t *dev, const rn2xx3_params_t *params)
{
assert(dev && (params->uart < UART_NUMOF));
/* initialize device parameters */
memcpy(&dev->p, params, sizeof(rn2xx3_params_t));
/* initialize pins and perform hardware reset */
if (dev->p.pin_reset != GPIO_UNDEF) {
gpio_init(dev->p.pin_reset, GPIO_OUT);
gpio_set(dev->p.pin_reset);
}
/* UART is initialized later, since interrupts cannot be handled yet */
}
int rn2xx3_init(rn2xx3_t *dev)
{
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_RESET);
/* initialize buffers and locks*/
dev->resp_size = 0;
dev->cmd_buf[0] = 0;
/* initialize UART and GPIO pins */
if (uart_init(dev->p.uart, dev->p.baudrate, _rx_cb, dev) != UART_OK) {
DEBUG("[rn2xx3] init: error initializing UART\n");
return -ENXIO;
}
/* if reset pin is connected, do a hardware reset */
if (dev->p.pin_reset != GPIO_UNDEF) {
gpio_clear(dev->p.pin_reset);
xtimer_usleep(RESET_DELAY);
gpio_set(dev->p.pin_reset);
}
dev->sleep_timer.callback = _sleep_timer_cb;
dev->sleep_timer.arg = dev;
rn2xx3_sys_set_sleep_duration(dev, RN2XX3_DEFAULT_SLEEP);
/* sending empty command to clear uart buffer */
if (rn2xx3_write_cmd(dev) == RN2XX3_TIMEOUT) {
DEBUG("[rn2xx3] init: initialization failed\n");
return RN2XX3_TIMEOUT;
}
if (rn2xx3_mac_init(dev) != RN2XX3_OK) {
DEBUG("[rn2xx3] mac: initialization failed\n");
return RN2XX3_ERR_MAC_INIT;
}
DEBUG("[rn2xx3] init: initialization successful\n");
return RN2XX3_OK;
}
int rn2xx3_sys_reset(rn2xx3_t *dev)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "sys reset");
dev->cmd_buf[p] = 0;
int ret = rn2xx3_write_cmd(dev);
if (ret == RN2XX3_TIMEOUT || ret == RN2XX3_ERR_SLEEP_MODE) {
DEBUG("[rn2xx3] reset: failed\n");
return ret;
}
return RN2XX3_OK;
}
int rn2xx3_sys_factory_reset(rn2xx3_t *dev)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"sys factoryRESET");
dev->cmd_buf[p] = 0;
int ret = rn2xx3_write_cmd(dev);
if (ret == RN2XX3_TIMEOUT || ret == RN2XX3_ERR_SLEEP_MODE) {
DEBUG("[rn2xx3] factory reset: failed\n");
return ret;
}
return RN2XX3_OK;
}
int rn2xx3_sys_sleep(rn2xx3_t *dev)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"sys sleep %lu", (unsigned long)dev->sleep);
dev->cmd_buf[p] = 0;
if (rn2xx3_write_cmd_no_wait(dev) == RN2XX3_ERR_INVALID_PARAM) {
DEBUG("[rn2xx3] sleep: cannot put module in sleep mode\n");
return RN2XX3_ERR_INVALID_PARAM;
}
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_SLEEP);
xtimer_set(&dev->sleep_timer, dev->sleep * US_PER_MS);
return RN2XX3_OK;
}
int rn2xx3_mac_init(rn2xx3_t *dev)
{
rn2xx3_mac_set_dr(dev, LORAMAC_DEFAULT_DR);
rn2xx3_mac_set_tx_power(dev, LORAMAC_DEFAULT_TX_POWER);
rn2xx3_mac_set_tx_port(dev, LORAMAC_DEFAULT_TX_PORT);
rn2xx3_mac_set_tx_mode(dev, LORAMAC_DEFAULT_TX_MODE);
rn2xx3_mac_set_adr(dev, LORAMAC_DEFAULT_ADR);
rn2xx3_mac_set_retx(dev, LORAMAC_DEFAULT_RETX);
rn2xx3_mac_set_linkchk_interval(dev, LORAMAC_DEFAULT_LINKCHK);
rn2xx3_mac_set_rx1_delay(dev, LORAMAC_DEFAULT_RX1_DELAY);
rn2xx3_mac_set_ar(dev, LORAMAC_DEFAULT_AR);
rn2xx3_mac_set_rx2_dr(dev, LORAMAC_DEFAULT_RX2_DR);
rn2xx3_mac_set_rx2_freq(dev, LORAMAC_DEFAULT_RX2_FREQ);
return RN2XX3_OK;
}
int rn2xx3_mac_tx(rn2xx3_t *dev, uint8_t *payload, uint8_t payload_len)
{
if (dev->int_state == RN2XX3_INT_STATE_SLEEP) {
DEBUG("[rn2xx3] ABORT: device is in sleep mode\n");
return RN2XX3_ERR_SLEEP_MODE;
}
rn2xx3_mac_tx_start(dev);
for (unsigned i = 0; i < payload_len; ++i) {
rn2xx3_cmd_append(dev, &payload[i], 1);
}
int ret = rn2xx3_mac_tx_finalize(dev);
if (ret != RN2XX3_OK) {
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
DEBUG("[rn2xx3] TX command failed\n");
return ret;
}
ret = rn2xx3_wait_reply(dev, RN2XX3_REPLY_DELAY_TIMEOUT);
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
return ret;
}
int rn2xx3_mac_join_network(rn2xx3_t *dev, loramac_join_mode_t mode)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"mac join %s",
(mode == LORAMAC_JOIN_OTAA) ? "otaa" : "abp");
dev->cmd_buf[p] = 0;
int ret = rn2xx3_write_cmd(dev);
if (ret != RN2XX3_OK) {
DEBUG("[rn2xx3] join procedure command failed\n");
return ret;
}
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_MAC_JOIN);
ret = rn2xx3_wait_reply(dev,
LORAMAC_DEFAULT_JOIN_DELAY1 + \
LORAMAC_DEFAULT_JOIN_DELAY2);
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
return ret;
}
int rn2xx3_mac_save(rn2xx3_t *dev)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "mac save");
dev->cmd_buf[p] = 0;
return rn2xx3_write_cmd(dev);
}

View File

@ -0,0 +1,338 @@
/*
* Copyright (C) 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 drivers_rn2xx3
* @{
* @file
* @brief Implementation of get and set functions for RN2XX3
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
* @}
*/
#include <string.h>
#include <errno.h>
#include "assert.h"
#include "fmt.h"
#include "net/loramac.h"
#include "rn2xx3.h"
#include "rn2xx3_internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static const char *mac = "mac";
static const char *get = "get";
static const char *set = "set";
/* internal helpers */
static uint8_t _get_uint8_value(rn2xx3_t *dev, const char *command)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"%s %s %s", mac, get, command);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
return atoi(dev->resp_buf);
}
static void _set_uint8_value(rn2xx3_t *dev,
const char *command, uint8_t value)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "%s %s %s %d",
mac, set, command, value);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
}
static uint16_t _get_uint16_value(rn2xx3_t *dev, const char *command)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"%s %s %s", mac, get, command);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
return atoi(dev->resp_buf);
}
static void _set_uint16_value(rn2xx3_t *dev,
const char *command, uint16_t value)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "%s %s %s %i",
mac, set, command, value);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
}
static bool _get_bool_value(rn2xx3_t *dev, const char *command)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"%s %s %s", mac, get, command);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
return strcmp(dev->resp_buf, "on") == 0;
}
static void _set_bool_value(rn2xx3_t *dev,
const char *command, bool value)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "%s %s %s %s",
mac, set, command, value ? "on": "off");
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
}
static void _get_array_value(rn2xx3_t *dev, const char *command,
uint8_t *value)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"%s %s %s", mac, get, command);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
fmt_hex_bytes(value, dev->resp_buf);
}
static void _set_array_value(rn2xx3_t *dev, const char *command,
const uint8_t *value, uint8_t value_len)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "%s %s %s ",
mac, set, command);
dev->cmd_buf[p] = 0;
rn2xx3_cmd_start(dev);
rn2xx3_cmd_append(dev, value, value_len);
rn2xx3_cmd_finalize(dev);
}
void rn2xx3_mac_get_deveui(rn2xx3_t *dev, uint8_t *eui)
{
_get_array_value(dev, "deveui", eui);
}
void rn2xx3_mac_set_deveui(rn2xx3_t *dev, const uint8_t *eui)
{
_set_array_value(dev, "deveui", eui, LORAMAC_DEVEUI_LEN);
}
void rn2xx3_mac_get_appeui(rn2xx3_t *dev, uint8_t *eui)
{
_get_array_value(dev, "appeui", eui);
}
void rn2xx3_mac_set_appeui(rn2xx3_t *dev, const uint8_t *eui)
{
_set_array_value(dev, "appeui", eui, LORAMAC_APPEUI_LEN);
}
void rn2xx3_mac_set_appkey(rn2xx3_t *dev, const uint8_t *key)
{
_set_array_value(dev, "appkey", key, LORAMAC_APPKEY_LEN);
}
void rn2xx3_mac_set_appskey(rn2xx3_t *dev, const uint8_t *key)
{
_set_array_value(dev, "appskey", key, LORAMAC_APPSKEY_LEN);
}
void rn2xx3_mac_set_nwkskey(rn2xx3_t *dev, const uint8_t *key)
{
_set_array_value(dev, "nwkskey", key, LORAMAC_NWKSKEY_LEN);
}
void rn2xx3_mac_get_devaddr(rn2xx3_t *dev, uint8_t *addr)
{
_get_array_value(dev, "devaddr", addr);
}
void rn2xx3_mac_set_devaddr(rn2xx3_t *dev, const uint8_t *addr)
{
_set_array_value(dev, "devaddr", addr, 4);
}
loramac_tx_pwr_idx_t rn2xx3_mac_get_tx_power(rn2xx3_t *dev)
{
return _get_uint8_value(dev, "pwridx");
}
void rn2xx3_mac_set_tx_power(rn2xx3_t *dev, loramac_tx_pwr_idx_t power)
{
_set_uint8_value(dev, "pwridx", power);
}
loramac_dr_idx_t rn2xx3_mac_get_dr(rn2xx3_t *dev)
{
return _get_uint8_value(dev, "dr");
}
void rn2xx3_mac_set_dr(rn2xx3_t *dev, loramac_dr_idx_t datarate)
{
_set_uint8_value(dev, "dr", datarate);
}
uint16_t rn2xx3_mac_get_band(rn2xx3_t *dev)
{
return _get_uint16_value(dev, "band");
}
bool rn2xx3_mac_get_adr(rn2xx3_t *dev)
{
return _get_bool_value(dev, "adr");
}
void rn2xx3_mac_set_adr(rn2xx3_t *dev, bool adr)
{
_set_bool_value(dev, "adr", adr);
}
void rn2xx3_mac_set_battery(rn2xx3_t *dev, uint8_t battery)
{
_set_uint8_value(dev, "bat", battery);
}
uint8_t rn2xx3_mac_get_retx(rn2xx3_t *dev)
{
return _get_uint8_value(dev, "retx");
}
void rn2xx3_mac_set_retx(rn2xx3_t *dev, uint8_t retx)
{
_set_uint8_value(dev, "retx", retx);
}
void rn2xx3_mac_set_linkchk_interval(rn2xx3_t *dev, uint16_t linkchk)
{
_set_uint16_value(dev, "linkchk", linkchk);
}
uint16_t rn2xx3_mac_get_rx1_delay(rn2xx3_t *dev)
{
return _get_uint16_value(dev, "rxdelay1");
}
void rn2xx3_mac_set_rx1_delay(rn2xx3_t *dev, uint16_t rx1)
{
_set_uint16_value(dev, "rxdelay1", rx1);
}
uint16_t rn2xx3_mac_get_rx2_delay(rn2xx3_t *dev)
{
return _get_uint16_value(dev, "rxdelay2");
}
bool rn2xx3_mac_get_ar(rn2xx3_t *dev)
{
return _get_bool_value(dev, "ar");
}
void rn2xx3_mac_set_ar(rn2xx3_t *dev, bool ar)
{
_set_bool_value(dev, "ar", ar);
}
loramac_dr_idx_t rn2xx3_mac_get_rx2_dr(rn2xx3_t *dev)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "%s %s %s %d",
mac, get, "rx2", RN2XX3_FREQ_BAND);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
loramac_dr_idx_t dr = strtol(dev->resp_buf, NULL, 10);
return dr;
}
void rn2xx3_mac_set_rx2_dr(rn2xx3_t *dev, loramac_dr_idx_t dr)
{
dev->loramac.rx2_dr = dr;
dev->loramac.rx2_freq = rn2xx3_mac_get_rx2_freq(dev);
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"%s %s %s %d %lu",
mac, set, "rx2", dr,
(unsigned long)dev->loramac.rx2_freq);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
}
uint32_t rn2xx3_mac_get_rx2_freq(rn2xx3_t *dev)
{
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"%s %s %s %d", mac, get, "rx2", RN2XX3_FREQ_BAND);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
uint32_t freq = strtoul(dev->resp_buf + 2, NULL, 10);
return freq;
}
void rn2xx3_mac_set_rx2_freq(rn2xx3_t *dev, uint32_t freq)
{
dev->loramac.rx2_freq = freq;
dev->loramac.rx2_dr = rn2xx3_mac_get_rx2_dr(dev);
size_t p = snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1,
"%s %s %s %d %lu",
mac, set, "rx2", dev->loramac.rx2_dr,
(unsigned long)freq);
dev->cmd_buf[p] = 0;
rn2xx3_write_cmd(dev);
}
uint8_t rn2xx3_mac_get_tx_port(rn2xx3_t *dev)
{
return dev->loramac.tx_port;
}
void rn2xx3_mac_set_tx_port(rn2xx3_t *dev, uint8_t port)
{
dev->loramac.tx_port = port;
}
loramac_tx_mode_t rn2xx3_mac_get_tx_mode(rn2xx3_t *dev)
{
return dev->loramac.tx_mode;
}
void rn2xx3_mac_set_tx_mode(rn2xx3_t *dev, loramac_tx_mode_t mode)
{
dev->loramac.tx_mode = mode;
}
uint8_t rn2xx3_mac_get_rx_port(rn2xx3_t *dev)
{
return strtoul(&dev->resp_buf[6], NULL, 10);
}
void rn2xx3_sys_set_sleep_duration(rn2xx3_t *dev, uint32_t sleep)
{
if (sleep < RN2XX3_SLEEP_MIN) {
DEBUG("[rn2xx3] sleep: duration should be greater than 100 (ms)\n");
return;
}
dev->sleep = sleep;
}

View File

@ -0,0 +1,343 @@
/*
* Copyright (C) 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 drivers_rn2xx3
* @{
*
* @file
* @brief Internal driver implementation for the RN2483/RN2903 devices
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include "fmt.h"
#include "rn2xx3_internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define RESP_TIMEOUT_SEC (5U)
static const char *closing_seq = "\r\n";
static void _uart_write_str(rn2xx3_t *dev, const char *str)
{
size_t len = strlen(str);
if (len) {
uart_write(dev->p.uart, (uint8_t *)str, len);
}
}
static void isr_resp_timeout(void *arg)
{
rn2xx3_t *dev = (rn2xx3_t *)arg;
mutex_unlock(&(dev->resp_lock));
}
static bool _wait_reply(rn2xx3_t *dev, uint8_t timeout)
{
dev->resp_done = 0;
dev->resp_size = 0;
dev->resp_buf[0] = 0;
xtimer_ticks64_t sent_time = xtimer_now64();
xtimer_t resp_timer;
resp_timer.callback = isr_resp_timeout;
resp_timer.arg = dev;
xtimer_set(&resp_timer, (uint32_t)timeout * US_PER_SEC);
/* wait for results */
while ((!dev->resp_done) &&
xtimer_less(xtimer_diff32_64(xtimer_now64(), sent_time),
xtimer_ticks_from_usec(timeout * US_PER_SEC))) {
mutex_lock(&(dev->resp_lock));
}
xtimer_remove(&resp_timer);
if (dev->resp_done == 0) {
DEBUG("[rn2xx3] response timeout\n");
return true;
}
return false;
}
void rn2xx3_hex_to_bytes(const char *hex, uint8_t *byte_array)
{
const uint8_t charmap[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, /* @ABCDEFG */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* HIJKLMNO */
};
size_t len = strlen(hex);
for (uint8_t pos = 0; pos < len; pos += 2) {
uint8_t idx0 = ((uint8_t)hex[pos + 0] & 0x1F) ^ 0x10;
uint8_t idx1 = ((uint8_t)hex[pos + 1] & 0x1F) ^ 0x10;
byte_array[pos / 2] = (uint8_t)(charmap[idx0] << 4) | charmap[idx1];
};
}
void rn2xx3_set_internal_state(rn2xx3_t *dev, uint8_t state)
{
if ((dev->int_state == RN2XX3_INT_STATE_SLEEP) ||
(dev->int_state == state)) {
return;
}
if (ENABLE_DEBUG) {
printf("[rn2xx3] new state: ");
switch(state) {
case RN2XX3_INT_STATE_CMD:
puts("CMD");
break;
case RN2XX3_INT_STATE_IDLE:
puts("IDLE");
break;
case RN2XX3_INT_STATE_MAC_JOIN:
puts("JOIN");
break;
case RN2XX3_INT_STATE_MAC_RX_MESSAGE:
puts("RX MSG");
break;
case RN2XX3_INT_STATE_MAC_RX_PORT:
puts("RX PORT");
break;
case RN2XX3_INT_STATE_MAC_TX:
puts("TX");
break;
case RN2XX3_INT_STATE_RESET:
puts("RESET");
break;
case RN2XX3_INT_STATE_SLEEP:
puts("SLEEP");
break;
default:
puts("UNKNOWN");
break;
}
}
dev->int_state = state;
}
int rn2xx3_write_cmd(rn2xx3_t *dev)
{
int ret;
DEBUG("[rn2xx3] CMD: %s\n", dev->cmd_buf);
if (dev->int_state == RN2XX3_INT_STATE_SLEEP) {
DEBUG("[rn2xx3] ABORT: device is in sleep mode\n");
return RN2XX3_ERR_SLEEP_MODE;
}
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_CMD);
mutex_lock(&(dev->cmd_lock));
_uart_write_str(dev, dev->cmd_buf);
_uart_write_str(dev, closing_seq);
ret = rn2xx3_wait_response(dev);
if (ret == RN2XX3_TIMEOUT) {
mutex_unlock(&(dev->cmd_lock));
return RN2XX3_TIMEOUT;
}
mutex_unlock(&(dev->cmd_lock));
ret = rn2xx3_process_response(dev);
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
DEBUG("[rn2xx3] RET: %d, RESP: %s\n", ret, dev->resp_buf);
return ret;
}
int rn2xx3_write_cmd_no_wait(rn2xx3_t *dev)
{
DEBUG("[rn2xx3] CMD: %s\n", dev->cmd_buf);
if (dev->int_state == RN2XX3_INT_STATE_SLEEP) {
DEBUG("[rn2xx3] ABORT: device is in sleep mode\n");
return RN2XX3_ERR_SLEEP_MODE;
}
mutex_lock(&(dev->cmd_lock));
_uart_write_str(dev, dev->cmd_buf);
_uart_write_str(dev, closing_seq);
DEBUG("[rn2xx3] RET: %s\n", dev->resp_buf);
mutex_unlock(&(dev->cmd_lock));
return rn2xx3_process_response(dev);
}
int rn2xx3_wait_response(rn2xx3_t *dev)
{
if (_wait_reply(dev, RESP_TIMEOUT_SEC)) {
return RN2XX3_TIMEOUT;
}
DEBUG("[rn2xx3] RESP: %s\n", dev->resp_buf);
return RN2XX3_OK;
}
int rn2xx3_wait_reply(rn2xx3_t *dev, uint8_t timeout)
{
if (_wait_reply(dev, timeout)) {
return RN2XX3_REPLY_TIMEOUT;
}
DEBUG("[rn2xx3] REPLY: %s\n", dev->resp_buf);
return rn2xx3_process_reply(dev);
}
void rn2xx3_cmd_start(rn2xx3_t *dev)
{
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_CMD);
DEBUG("[rn2xx3] CMD: %s", dev->cmd_buf);
mutex_lock(&(dev->cmd_lock));
_uart_write_str(dev, dev->cmd_buf);
}
void rn2xx3_cmd_append(rn2xx3_t *dev, const uint8_t *payload, uint8_t payload_len)
{
char payload_str[2];
for (unsigned i = 0; i < payload_len; i++) {
fmt_byte_hex(payload_str, payload[i]);
_uart_write_str(dev, payload_str);
}
}
int rn2xx3_cmd_finalize(rn2xx3_t *dev)
{
DEBUG("\n");
_uart_write_str(dev, closing_seq);
uint8_t ret = rn2xx3_wait_response(dev);
mutex_unlock(&(dev->cmd_lock));
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_IDLE);
DEBUG("[rn2xx3] RET: %d, RESP: %s\n", ret, dev->resp_buf);
return ret;
}
void rn2xx3_mac_tx_start(rn2xx3_t *dev)
{
snprintf(dev->cmd_buf, sizeof(dev->cmd_buf) - 1, "mac tx %s %d ",
(dev->loramac.tx_mode == LORAMAC_TX_CNF) ? "cnf" : "uncnf",
dev->loramac.tx_port);
rn2xx3_cmd_start(dev);
}
int rn2xx3_mac_tx_finalize(rn2xx3_t *dev)
{
rn2xx3_cmd_finalize(dev);
rn2xx3_set_internal_state(dev, RN2XX3_INT_STATE_MAC_TX);
return rn2xx3_process_response(dev);
}
int rn2xx3_process_response(rn2xx3_t *dev)
{
uint8_t ret = RN2XX3_DATA;
if (strcmp(dev->resp_buf, "ok") == 0) {
DEBUG("[rn2xx3] command succeeded: '%s'\n", dev->cmd_buf);
ret = RN2XX3_OK;
}
else if (strcmp(dev->resp_buf, "invalid_param") == 0) {
DEBUG("[rn2xx3] invalid command: '%s'\n", dev->cmd_buf);
ret = RN2XX3_ERR_INVALID_PARAM;
}
else if (strcmp(dev->resp_buf, "not_joined") == 0) {
DEBUG("[rn2xx3] failed: network is not joined\n");
ret = RN2XX3_ERR_NOT_JOINED;
}
else if (strcmp(dev->resp_buf, "no_free_ch") == 0) {
DEBUG("[rn2xx3] failed: all channels are busy\n");
ret = RN2XX3_ERR_NO_FREE_CH;
}
else if (strcmp(dev->resp_buf, "silent") == 0) {
DEBUG("[rn2xx3] failed: all channels are busy\n");
ret = RN2XX3_ERR_SILENT;
}
else if (strcmp(dev->resp_buf, "frame_counter_err_rejoin_needed") == 0) {
DEBUG("[rn2xx3] failed: Frame counter rolled over\n");
ret = RN2XX3_ERR_FR_CNT_REJOIN_NEEDED;
}
else if (strcmp(dev->resp_buf, "busy") == 0) {
DEBUG("[rn2xx3] failed: MAC state is in an Idle state\n");
ret = RN2XX3_ERR_BUSY;
}
else if (strcmp(dev->resp_buf, "invalid_data_len") == 0) {
DEBUG("[rn2xx3] failed: payload length too large\n");
ret = RN2XX3_ERR_INVALID_DATA_LENGTH;
}
return ret;
}
int rn2xx3_process_reply(rn2xx3_t *dev)
{
uint8_t ret;
if (strcmp(dev->resp_buf, "accepted") == 0) {
DEBUG("[rn2xx3] join procedure succeeded.\n");
ret = RN2XX3_REPLY_JOIN_ACCEPTED;
}
else if (strcmp(dev->resp_buf, "denied") == 0) {
DEBUG("[rn2xx3] join procedure failed.\n");
ret = RN2XX3_REPLY_JOIN_DENIED;
}
else if (strcmp(dev->resp_buf, "mac_tx_ok") == 0) {
DEBUG("[rn2xx3] uplink transmission succeeded.\n");
ret = RN2XX3_REPLY_TX_MAC_OK;
}
else if (strncmp(dev->resp_buf, "mac_rx", 6) == 0) {
DEBUG("[rn2xx3] received downlink data from server.\n");
ret = RN2XX3_REPLY_TX_MAC_RX;
}
else if (strcmp(dev->resp_buf, "mac_err") == 0) {
DEBUG("[rn2xx3] uplink transmission failed.\n");
ret = RN2XX3_REPLY_TX_MAC_ERR;
}
else if (strcmp(dev->resp_buf, "invalid_data_len") == 0) {
DEBUG("[rn2xx3] payload length too large.\n");
ret = RN2XX3_REPLY_TX_MAC_ERR;
}
else {
DEBUG("[rn2xx3] unknown reply.\n");
ret = RN2XX3_REPLY_OTHER;
}
return ret;
}

View File

@ -98,6 +98,10 @@ PSEUDOMODULES += si7013
PSEUDOMODULES += si7020
PSEUDOMODULES += si7021
# include variants of RN2XX3 drivers as pseudo modules
PSEUDOMODULES += rn2483
PSEUDOMODULES += rn2903
# add all pseudo random number generator variants as pseudomodules
PSEUDOMODULES += prng_%

View File

@ -0,0 +1,9 @@
include ../Makefile.tests_common
DRIVER ?= rn2483
USEMODULE += $(DRIVER)
USEMODULE += shell
USEMODULE += shell_commands
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,93 @@
# About
This is a manual test application for testing RN2483/RN2903 LoRa Microchip
modules:
* [RN2483](http://ww1.microchip.com/downloads/en/DeviceDoc/50002346C.pdf)
* [RN2903](http://ww1.microchip.com/downloads/en/DeviceDoc/50002390C.pdf)
RN2483 can be used in Europe region and RN2903 in US region.
For easy testing, those modules can be found soldered on Bee-like layout. To
run this test with this kind of setup, you need to connect the following pins
of the Bee-like module to your board:
- UART RX
- UART TX
- VCC (3V3)
- GND
NOTE: when you use an Arduino Wireless Proto shield, the RN2483/RN2903 module
can potentially be connected to the same UART as RIOTs standard out. This is the
case for Arduino UNO, and most of the Nucleo 64 boards.
In this case you must redefine `UART_STDIO_DEV` to another UART interface in
the `board.h` and connect a UART-to-USB adapter to that UART.
This is not the case with Arduino-zero or some Nucleo144 boards.
# Build and flash the application
Use the `DRIVER` variable to select the right driver module between `rn2483`
and `rn2903` depending on your region.
Example in EU:
```
make DRIVER=rn2483 BOARD=arduino-zero -C tests/driver_rn2xx3 flash term
```
# Usage
For testing the LoRaBee driver, use the shell commands provided by this
test application: `sys` and `mac`:
* `sys` contains subcommands for controlling the device at a low-level
* `mac` contains subcommands for connecting to a LoRaWAN network, sending,
receiving paquets and getting/setting some LoRaMAC parameters.
The full list of available parameters for each command is described in the
following documents:
- For EU region (868MHz and 433MHZ bands):
[RN2483](http://ww1.microchip.com/downloads/en/DeviceDoc/40001784B.pdf)
- For US region (915MHZ band):
[RN2903](http://ww1.microchip.com/downloads/en/DeviceDoc/40001811A.pdf)
The radio level commands are not supported by this driver (yet).
Examples of commands:
* Reset the module:
```
> sys reset
RN2483 1.0.1 Dec 15 2015 09:38:09
```
* Put the module into sleep (5s is the default):
```
> sys sleep
```
* Set a LoRaMAC dev eui (put yours instead of the fake one given as
example here), 8 bytes in hex string representation:
```
> mac set deveui 0000000000000000
```
* Set a LoRaMAC application eui, 8 bytes in hex string representation:
```
> mac set appeui 0000000000000000
```
* Set a LoRaMAC application key, 16 bytes in hex string representation:
```
> mac set appkey 00000000000000000000000000000000
```
* Save your parameters in the module EEPROM. After that, setting deveui,
appeui, appkey is not required anymore, even after rebooting the module:
```
> mac save
```
Warning: when using **OTAA** activation, it's important to not use `mac save`
**after joining the network**. Thus, **before starting the join procedure**,
ensure a null devaddr is written to the EEPROM:
```
> mac set devaddr 00000000
> mac save
```
* Join your network using Other-the-air-activation:
```
> mac join otaa
```
* Send data `AAAA` to the LoRaWAN server:
```
> mac tx AAAA
```

450
tests/driver_rn2xx3/main.c Normal file
View File

@ -0,0 +1,450 @@
/*
* Copyright (C) 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 RN2483/RN2903 LoRa module driver
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "timex.h"
#include "shell.h"
#include "shell_commands.h"
#include "fmt.h"
#include "rn2xx3.h"
#include "rn2xx3_params.h"
#include "rn2xx3_internal.h"
static rn2xx3_t rn2xx3_dev;
static uint8_t payload[RN2XX3_MAX_BUF];
static void _print_sys_usage(void)
{
puts("Usage: sys <sleep|reset|factoryRESET>");
}
static void _print_mac_usage(void)
{
puts("Usage: mac <join|tx|set|get|save>");
}
static void _print_mac_get_usage(void)
{
puts("Usage: mac get "
"<deveui|appeui|devaddr|txport|txmode|poweridx|dr|adr|"
"band|retx|rx1|rx2|ar|rx2dr|rx2freq>");
}
static void _print_mac_set_usage(void)
{
puts("Usage: mac set "
"<deveui|appeui|appkey|devaddr|appskey|nwkskey|txport|txmode|pwridx|dr"
"|adr|bat|retx|linkchk|rx1|ar|rx2dr|rx2freq|sleep_duration> <value>");
}
int rn2xx3_sys_cmd(int argc, char **argv) {
if (argc < 2) {
_print_sys_usage();
return -1;
}
if (strcmp(argv[1], "reset") == 0) {
if (rn2xx3_sys_reset(&rn2xx3_dev) != RN2XX3_OK) {
puts("System reset failed");
return -1;
}
printf("System version: %s\n", rn2xx3_dev.resp_buf);
}
else if (strcmp(argv[1], "sleep") == 0) {
if (rn2xx3_sys_sleep(&rn2xx3_dev) != RN2XX3_OK) {
puts("Cannot put device in sleep mode");
return -1;
}
printf("Success: device is in sleep mode during %lus\n",
(unsigned long)rn2xx3_dev.sleep / MS_PER_SEC);
}
else if (strcmp(argv[1], "factoryRESET") == 0) {
if (rn2xx3_sys_factory_reset(&rn2xx3_dev) != RN2XX3_OK) {
puts("Factory reset failed");
return -1;
}
printf("System version: %s\n", rn2xx3_dev.resp_buf);
}
else {
_print_sys_usage();
return -1;
}
return 0;
}
int rn2xx3_mac_cmd(int argc, char **argv) {
if (argc < 2) {
_print_mac_usage();
return -1;
}
if (strcmp(argv[1], "join") == 0) {
if (argc != 3) {
puts("Usage: mac join <otaa|abp>");
return -1;
}
uint8_t mode;
if (strcmp(argv[2], "otaa") == 0) {
mode = LORAMAC_JOIN_OTAA;
}
else {
mode = LORAMAC_JOIN_ABP;
}
switch (rn2xx3_mac_join_network(&rn2xx3_dev, mode)) {
case RN2XX3_ERR_NO_FREE_CH:
printf("%s: all channels are busy\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_SILENT:
printf("%s: device is in silent state\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_FR_CNT_REJOIN_NEEDED:
printf("%s: frame counter rolled over\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_BUSY:
printf("%s: MAC layer not in Idle state\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_KEYS_NOT_INIT:
printf("%s: device keys not configured\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_REPLY_TIMEOUT:
puts("[ERROR]: No reply received from server");
return -1;
case RN2XX3_REPLY_JOIN_ACCEPTED:
puts("Join procedure succeeded");
break;
case RN2XX3_REPLY_JOIN_DENIED:
puts("Join procedure failed");
return -1;
default:
/* Should not happen */
break;
}
}
else if (strcmp(argv[1], "tx") == 0) {
if (argc != 3) {
puts("Usage: mac tx <payload>");
return -1;
}
switch (rn2xx3_mac_tx(&rn2xx3_dev, (uint8_t *)argv[2], strlen(argv[2]))) {
case RN2XX3_ERR_INVALID_PARAM:
printf("%s: invalid param given\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_NOT_JOINED:
printf("%s: network is not joined\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_NO_FREE_CH:
printf("%s: all channels are busy\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_SILENT:
printf("%s: device is in silent state\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_FR_CNT_REJOIN_NEEDED:
printf("%s: frame counter rolled over\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_BUSY:
printf("%s: MAC layer not in Idle state\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_ERR_KEYS_NOT_INIT:
printf("%s: device keys not configured\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_REPLY_TIMEOUT:
puts("[ERROR]: No reply received from server");
return -1;
case RN2XX3_ERR_INVALID_DATA_LENGTH:
printf("%s: payload is too large\n", rn2xx3_dev.cmd_buf);
return -1;
case RN2XX3_REPLY_TX_MAC_OK:
puts("MAC transmission succeeded");
break;
case RN2XX3_REPLY_TX_MAC_ERR:
puts("MAC transmission failed");
return -1;
case RN2XX3_REPLY_TX_INVALID_DATA_LEN:
puts("Application payload too large");
return -1;
case RN2XX3_REPLY_TX_MAC_RX:
{
puts("MAC transmission succeeded");
puts("Data received:");
printf(" -port: %d\n", rn2xx3_mac_get_rx_port(&rn2xx3_dev));
printf(" -payload len: %d\n", (uint8_t)strlen((char *)rn2xx3_dev.rx_buf));
printf(" -payload: '%s'\n", rn2xx3_dev.rx_buf);
}
break;
default:
/* Should not happen */
break;
}
}
else if (strcmp(argv[1], "save") == 0) {
if (rn2xx3_mac_save(&rn2xx3_dev) != RN2XX3_OK) {
puts("Cannot save MAC state");
}
else {
puts("MAC state saved");
}
}
else if (strcmp(argv[1], "get") == 0) {
if (argc < 3) {
_print_mac_get_usage();
return -1;
}
if (strcmp(argv[2], "deveui") == 0) {
rn2xx3_mac_get_deveui(&rn2xx3_dev, payload);
memset(rn2xx3_dev.resp_buf, 0, strlen(rn2xx3_dev.resp_buf));
fmt_bytes_hex(rn2xx3_dev.resp_buf, payload, LORAMAC_DEVEUI_LEN);
payload[LORAMAC_DEVEUI_LEN*2] = '\0';
printf("device eui: %s\n", rn2xx3_dev.resp_buf);
}
else if (strcmp(argv[2], "appeui") == 0) {
rn2xx3_mac_get_appeui(&rn2xx3_dev, payload);
memset(rn2xx3_dev.resp_buf, 0, strlen(rn2xx3_dev.resp_buf));
fmt_bytes_hex(rn2xx3_dev.resp_buf, payload, LORAMAC_APPEUI_LEN);
payload[LORAMAC_APPEUI_LEN*2] = '\0';
printf("application eui: %s\n", rn2xx3_dev.resp_buf);
}
else if (strcmp(argv[2], "devaddr") == 0) {
rn2xx3_mac_get_devaddr(&rn2xx3_dev, payload);
memset(rn2xx3_dev.resp_buf, 0, strlen(rn2xx3_dev.resp_buf));
fmt_bytes_hex(rn2xx3_dev.resp_buf, payload, LORAMAC_DEVADDR_LEN);
payload[LORAMAC_DEVADDR_LEN*2] = '\0';
printf("device addr: %s\n", rn2xx3_dev.resp_buf);
}
else if (strcmp(argv[2], "txport") == 0) {
uint8_t port = rn2xx3_mac_get_tx_port(&rn2xx3_dev);
printf("TX port: %d\n", port);
}
else if (strcmp(argv[2], "txmode") == 0) {
loramac_tx_mode_t mode = rn2xx3_mac_get_tx_mode(&rn2xx3_dev);
printf("TX mode: ");
switch (mode) {
case LORAMAC_TX_CNF:
printf("confirmable");
break;
case LORAMAC_TX_UNCNF:
printf("unconfirmable");
break;
default:
printf("not supported");
break;
}
puts("");
}
else if (strcmp(argv[2], "pwridx") == 0) {
loramac_tx_pwr_idx_t idx = rn2xx3_mac_get_tx_power(&rn2xx3_dev);
printf("pwridx: %d\n", idx);
}
else if (strcmp(argv[2], "dr") == 0) {
loramac_dr_idx_t dr = rn2xx3_mac_get_dr(&rn2xx3_dev);
printf("dr: %d\n", dr);
}
else if (strcmp(argv[2], "band") == 0) {
uint16_t band = rn2xx3_mac_get_band(&rn2xx3_dev);
printf("band: %dMHz\n", band);
}
else if (strcmp(argv[2], "adr") == 0) {
bool adr = rn2xx3_mac_get_adr(&rn2xx3_dev);
printf("adr: %s\n", adr ? "on" : "off");
}
else if (strcmp(argv[2], "retx") == 0) {
uint8_t retx = rn2xx3_mac_get_adr(&rn2xx3_dev);
printf("retx: %d\n", retx);
}
else if (strcmp(argv[2], "rx1") == 0) {
uint16_t rx1 = rn2xx3_mac_get_rx1_delay(&rn2xx3_dev);
printf("rx1: %d\n", rx1);
}
else if (strcmp(argv[2], "rx2") == 0) {
uint16_t rx2 = rn2xx3_mac_get_rx2_delay(&rn2xx3_dev);
printf("rx2: %d\n", rx2);
}
else if (strcmp(argv[2], "ar") == 0) {
bool ar = rn2xx3_mac_get_ar(&rn2xx3_dev);
printf("ar: %s\n", ar ? "on" : "off");
}
else if (strcmp(argv[2], "rx2dr") == 0) {
loramac_dr_idx_t rx2_dr = rn2xx3_mac_get_rx2_dr(&rn2xx3_dev);
printf("rx2 dr: %d\n", rx2_dr);
}
else if (strcmp(argv[2], "rx2freq") == 0) {
uint32_t rx2_freq = rn2xx3_mac_get_rx2_freq(&rn2xx3_dev);
printf("rx2 freq: %lu\n", (unsigned long)rx2_freq);
}
else {
_print_mac_get_usage();
return -1;
}
}
else if (strcmp(argv[1], "set") == 0) {
if (argc < 4) {
_print_mac_set_usage();
return -1;
}
if (strcmp(argv[2], "deveui") == 0) {
fmt_hex_bytes(payload, argv[3]);
rn2xx3_mac_set_deveui(&rn2xx3_dev, payload);
}
else if (strcmp(argv[2], "appeui") == 0) {
fmt_hex_bytes(payload, argv[3]);
rn2xx3_mac_set_appeui(&rn2xx3_dev, payload);
}
else if (strcmp(argv[2], "appkey") == 0) {
fmt_hex_bytes(payload, argv[3]);
rn2xx3_mac_set_appkey(&rn2xx3_dev, payload);
}
else if (strcmp(argv[2], "devaddr") == 0) {
fmt_hex_bytes(payload, argv[3]);
rn2xx3_mac_set_devaddr(&rn2xx3_dev, payload);
}
else if (strcmp(argv[2], "appskey") == 0) {
fmt_hex_bytes(payload, argv[3]);
rn2xx3_mac_set_appskey(&rn2xx3_dev, payload);
}
else if (strcmp(argv[2], "nwkskey") == 0) {
fmt_hex_bytes(payload, argv[3]);
rn2xx3_mac_set_nwkskey(&rn2xx3_dev, payload);
}
else if (strcmp(argv[2], "txport") == 0) {
rn2xx3_mac_set_tx_port(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "txmode") == 0) {
loramac_tx_mode_t mode;
if (strcmp(argv[2], "cnf") == 0) {
mode = LORAMAC_TX_CNF;
}
else if (strcmp(argv[2], "uncnf") == 0) {
mode = LORAMAC_TX_UNCNF;
}
else {
puts("Usage: mac set txmode <cnf|uncnf>");
return -1;
}
rn2xx3_mac_set_tx_mode(&rn2xx3_dev, mode);
}
else if (strcmp(argv[2], "pwridx") == 0) {
rn2xx3_mac_set_tx_power(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "dr") == 0) {
rn2xx3_mac_set_dr(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "adr") == 0) {
bool adr = false;
if (strcmp(argv[3], "on") == 0) {
adr = true;
}
rn2xx3_mac_set_adr(&rn2xx3_dev, adr);
}
else if (strcmp(argv[2], "bat") == 0) {
rn2xx3_mac_set_battery(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "retx") == 0) {
rn2xx3_mac_set_retx(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "linkchk") == 0) {
rn2xx3_mac_set_linkchk_interval(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "rx1") == 0) {
rn2xx3_mac_set_rx1_delay(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "ar") == 0) {
bool ar = false;
if (strcmp(argv[3], "on") == 0) {
ar = true;
}
rn2xx3_mac_set_ar(&rn2xx3_dev, ar);
}
else if (strcmp(argv[2], "rx2dr") == 0) {
rn2xx3_mac_set_rx2_dr(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "rx2freq") == 0) {
rn2xx3_mac_set_rx2_freq(&rn2xx3_dev, atoi(argv[3]));
}
else if (strcmp(argv[2], "sleep_duration") == 0) {
rn2xx3_sys_set_sleep_duration(&rn2xx3_dev, atoi(argv[3]));
}
else {
_print_mac_set_usage();
return -1;
}
}
else {
_print_mac_usage();
return -1;
}
return 0;
}
static const shell_command_t shell_commands[] = {
{ "sys", "Run RN2XX3 system commands", rn2xx3_sys_cmd },
{ "mac", "Run RN2XX3 LoRa mac layer commands", rn2xx3_mac_cmd },
{ NULL, NULL, NULL }
};
int main(void)
{
puts("RN2XX3 device driver test");
rn2xx3_setup(&rn2xx3_dev, &rn2xx3_params[0]);
if (rn2xx3_init(&rn2xx3_dev) != RN2XX3_OK) {
puts("RN2XX3 initialization failed");
return -1;
}
/* start the shell */
puts("Initialization OK, starting shell now");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}