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