From b63412efa139161205adbac74bd7cccd9a303285 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie <alexandre.abadie@inria.fr> Date: Mon, 11 Sep 2017 14:55:42 +0200 Subject: [PATCH] drivers: add rn2xx3 LoRa module driver --- drivers/Makefile.dep | 8 + drivers/Makefile.include | 3 + drivers/include/rn2xx3.h | 690 +++++++++++++++++++++++ drivers/rn2xx3/Makefile | 1 + drivers/rn2xx3/include/rn2xx3_internal.h | 117 ++++ drivers/rn2xx3/include/rn2xx3_params.h | 62 ++ drivers/rn2xx3/rn2xx3.c | 309 ++++++++++ drivers/rn2xx3/rn2xx3_getset.c | 338 +++++++++++ drivers/rn2xx3/rn2xx3_internal.c | 343 +++++++++++ makefiles/pseudomodules.inc.mk | 4 + 10 files changed, 1875 insertions(+) create mode 100644 drivers/include/rn2xx3.h create mode 100644 drivers/rn2xx3/Makefile create mode 100644 drivers/rn2xx3/include/rn2xx3_internal.h create mode 100644 drivers/rn2xx3/include/rn2xx3_params.h create mode 100644 drivers/rn2xx3/rn2xx3.c create mode 100644 drivers/rn2xx3/rn2xx3_getset.c create mode 100644 drivers/rn2xx3/rn2xx3_internal.c diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 722ae9fa49..14b4a57763 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -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 diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 408210f981..124e19e7e4 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -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 diff --git a/drivers/include/rn2xx3.h b/drivers/include/rn2xx3.h new file mode 100644 index 0000000000..d30029ca29 --- /dev/null +++ b/drivers/include/rn2xx3.h @@ -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 */ +/** @} */ diff --git a/drivers/rn2xx3/Makefile b/drivers/rn2xx3/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/rn2xx3/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/rn2xx3/include/rn2xx3_internal.h b/drivers/rn2xx3/include/rn2xx3_internal.h new file mode 100644 index 0000000000..9cf27a391c --- /dev/null +++ b/drivers/rn2xx3/include/rn2xx3_internal.h @@ -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 */ +/** @} */ diff --git a/drivers/rn2xx3/include/rn2xx3_params.h b/drivers/rn2xx3/include/rn2xx3_params.h new file mode 100644 index 0000000000..c28ca1cbb3 --- /dev/null +++ b/drivers/rn2xx3/include/rn2xx3_params.h @@ -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 */ +/** @} */ diff --git a/drivers/rn2xx3/rn2xx3.c b/drivers/rn2xx3/rn2xx3.c new file mode 100644 index 0000000000..faad12900d --- /dev/null +++ b/drivers/rn2xx3/rn2xx3.c @@ -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); +} diff --git a/drivers/rn2xx3/rn2xx3_getset.c b/drivers/rn2xx3/rn2xx3_getset.c new file mode 100644 index 0000000000..1785dfa805 --- /dev/null +++ b/drivers/rn2xx3/rn2xx3_getset.c @@ -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; +} diff --git a/drivers/rn2xx3/rn2xx3_internal.c b/drivers/rn2xx3/rn2xx3_internal.c new file mode 100644 index 0000000000..7767eb0088 --- /dev/null +++ b/drivers/rn2xx3/rn2xx3_internal.c @@ -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; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 446823741a..887573a2bb 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -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_% -- GitLab