LoRaWAN MAC layer implementation

Dependents:   LoRaWAN-demo-72_tjm LoRaWAN-demo-72_jlc LoRaWAN-demo-elmo frdm_LoRa_Connect_Woodstream_Demo_tjm ... more

LoRAWAN-lib is a port of the GitHub LoRaMac-node LoRaWAN MAC layer implementation.

This library depends on the SX1276Lib or SX1272Lib radio drivers depending on the used mbed component shield.

This library depends also on some cryptographic helper functions as well as helper functions for the timers management. These can be found on the example projects under the system directory.

The example projects are:

  1. LoRaWAN-demo-72
  2. LoRaWAN-demo-76
  3. LoRaWAN-demo-NAMote72

The LoRaWAN specification specifies different ISM bands operating parameters. These are all implemented under the LoRaMac-board.h file.

In order to select which band to use, please change line 24 of board.h file provided on the examples projects as follows:


EU868

board.h

#define USE_BAND_868


US915

board.h

#define USE_BAND_915


US915 - Hybrid

board.h

#define USE_BAND_915_HYBRID


CN780

board.h

#define USE_BAND_780


EU433

board.h

#define USE_BAND_433

Files at this revision

API Documentation at this revision

Comitter:
mluis
Date:
Tue Oct 20 13:21:26 2015 +0000
Child:
1:91e4e6c60d1e
Commit message:
Library creation synchronized with GitHub LoRaMac-node v3.4 (https://github.com/Lora-net/LoRaMac-node)

Changed in this revision

LoRaMac-board.h Show annotated file Show diff for this revision Revisions of this file
LoRaMac.cpp Show annotated file Show diff for this revision Revisions of this file
LoRaMac.h Show annotated file Show diff for this revision Revisions of this file
LoRaMacCrypto.cpp Show annotated file Show diff for this revision Revisions of this file
LoRaMacCrypto.h Show annotated file Show diff for this revision Revisions of this file
crypto/aes.cpp Show annotated file Show diff for this revision Revisions of this file
crypto/aes.h Show annotated file Show diff for this revision Revisions of this file
crypto/cmac.cpp Show annotated file Show diff for this revision Revisions of this file
crypto/cmac.h Show annotated file Show diff for this revision Revisions of this file
radio/SX1276Lib.lib Show annotated file Show diff for this revision Revisions of this file
system/timer.cpp Show annotated file Show diff for this revision Revisions of this file
system/timer.h Show annotated file Show diff for this revision Revisions of this file
system/utilities.cpp Show annotated file Show diff for this revision Revisions of this file
system/utilities.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMac-board.h	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,406 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: LoRa MAC layer board dependent definitions
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#ifndef __LORAMAC_BOARD_H__
+#define __LORAMAC_BOARD_H__
+
+#define USE_BAND_868
+
+/*!
+ * Returns individual channel mask
+ *
+ * \param[IN] channelIndex Channel index 1 based
+ * \retval channelMask
+ */
+#define LC( channelIndex )            ( uint16_t )( 1 << ( channelIndex - 1 ) )
+
+#if defined( USE_BAND_433 )
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define LORA_MAX_NB_CHANNELS                        16
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MIN_DATARATE                        DR_0
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MAX_DATARATE                        DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define LORAMAC_DEFAULT_DATARATE                    DR_0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MIN_TX_POWER                        TX_POWER_M5_DBM
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MAX_TX_POWER                        TX_POWER_10_DBM
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_10_DBM
+
+/*!
+ * LoRaMac TxPower definition
+ */
+#define TX_POWER_10_DBM                             0
+#define TX_POWER_07_DBM                             1
+#define TX_POWER_04_DBM                             2
+#define TX_POWER_01_DBM                             3
+#define TX_POWER_M2_DBM                             4
+#define TX_POWER_M5_DBM                             5
+
+/*!
+ * LoRaMac datarates definition
+ */
+#define DR_0                                        0  // SF12 - BW125
+#define DR_1                                        1  // SF11 - BW125
+#define DR_2                                        2  // SF10 - BW125
+#define DR_3                                        3  // SF9  - BW125
+#define DR_4                                        4  // SF8  - BW125
+#define DR_5                                        5  // SF7  - BW125
+#define DR_6                                        6  // SF7  - BW250
+#define DR_7                                        7  // FSK
+
+/*!
+ * Second reception window channel definition.
+ */
+// Channel = { Frequency [Hz], Datarate }
+#define RX_WND_2_CHANNEL                                  { 434665000, DR_0 }
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define LORA_MAX_NB_BANDS                           1
+
+// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+#define BAND0              { 100, TX_POWER_10_DBM, 0,  0 } //  1.0 %
+
+/*!
+ * LoRaMac default channels
+ */
+// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+#define LC1                { 433175000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC2                { 433375000, { ( ( DR_7 << 4 ) | DR_0 ) }, 0 }
+#define LC3                { 433575000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+#elif defined( USE_BAND_780 )
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define LORA_MAX_NB_CHANNELS                        16
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MIN_DATARATE                        DR_0
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MAX_DATARATE                        DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define LORAMAC_DEFAULT_DATARATE                    DR_0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MIN_TX_POWER                        TX_POWER_M5_DBM
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MAX_TX_POWER                        TX_POWER_10_DBM
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_10_DBM
+
+/*!
+ * LoRaMac TxPower definition
+ */
+#define TX_POWER_10_DBM                             0
+#define TX_POWER_07_DBM                             1
+#define TX_POWER_04_DBM                             2
+#define TX_POWER_01_DBM                             3
+#define TX_POWER_M2_DBM                             4
+#define TX_POWER_M5_DBM                             5
+
+/*!
+ * LoRaMac datarates definition
+ */
+#define DR_0                                        0  // SF12 - BW125
+#define DR_1                                        1  // SF11 - BW125
+#define DR_2                                        2  // SF10 - BW125
+#define DR_3                                        3  // SF9  - BW125
+#define DR_4                                        4  // SF8  - BW125
+#define DR_5                                        5  // SF7  - BW125
+#define DR_6                                        6  // SF7  - BW250
+#define DR_7                                        7  // FSK
+
+/*!
+ * Second reception window channel definition.
+ */
+// Channel = { Frequency [Hz], Datarate }
+#define RX_WND_2_CHANNEL                                  { 786000000, DR_0 }
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define LORA_MAX_NB_BANDS                           1
+
+// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+#define BAND0              { 100, TX_POWER_10_DBM, 0,  0 } //  1.0 %
+
+/*!
+ * LoRaMac default channels
+ */
+// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+#define LC1                { 779500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC2                { 779700000, { ( ( DR_7 << 4 ) | DR_0 ) }, 0 }
+#define LC3                { 779900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+
+#elif defined( USE_BAND_868 )
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define LORA_MAX_NB_CHANNELS                        16
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MIN_DATARATE                        DR_0
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MAX_DATARATE                        DR_7
+
+/*!
+ * Default datarate used by the node
+ */
+#define LORAMAC_DEFAULT_DATARATE                    DR_0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MIN_TX_POWER                        TX_POWER_02_DBM
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MAX_TX_POWER                        TX_POWER_20_DBM
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_14_DBM
+
+/*!
+ * LoRaMac TxPower definition
+ */
+#define TX_POWER_20_DBM                             0
+#define TX_POWER_14_DBM                             1
+#define TX_POWER_11_DBM                             2
+#define TX_POWER_08_DBM                             3
+#define TX_POWER_05_DBM                             4
+#define TX_POWER_02_DBM                             5
+
+/*!
+ * LoRaMac datarates definition
+ */
+#define DR_0                                        0  // SF12 - BW125
+#define DR_1                                        1  // SF11 - BW125
+#define DR_2                                        2  // SF10 - BW125
+#define DR_3                                        3  // SF9  - BW125
+#define DR_4                                        4  // SF8  - BW125
+#define DR_5                                        5  // SF7  - BW125
+#define DR_6                                        6  // SF7  - BW250
+#define DR_7                                        7  // FSK
+
+/*!
+ * Second reception window channel definition.
+ */
+// Channel = { Frequency [Hz], Datarate }
+#define RX_WND_2_CHANNEL                                  { 869525000, DR_0 }
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define LORA_MAX_NB_BANDS                           5
+
+/*!
+ * LoRaMac EU868 default bands
+ */
+typedef enum
+{
+    BAND_G1_0,
+    BAND_G1_1,
+    BAND_G1_2,
+    BAND_G1_3,
+    BAND_G1_4,
+}BandId_t;
+
+// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+#define BAND0              { 100 , TX_POWER_14_DBM, 0,  0 } //  1.0 %
+#define BAND1              { 100 , TX_POWER_14_DBM, 0,  0 } //  1.0 %
+#define BAND2              { 1000, TX_POWER_14_DBM, 0,  0 } //  0.1 %
+#define BAND3              { 10  , TX_POWER_14_DBM, 0,  0 } // 10.0 %
+#define BAND4              { 100 , TX_POWER_14_DBM, 0,  0 } //  1.0 %
+
+/*!
+ * LoRaMac default channels
+ */
+// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+#define LC1                { 868100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+#define LC2                { 868300000, { ( ( DR_6 << 4 ) | DR_0 ) }, 1 }
+#define LC3                { 868500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 }
+#define LC4                { 867100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC5                { 867300000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC6                { 867500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC7                { 867700000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC8                { 867900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }
+#define LC9                { 868800000, { ( ( DR_7 << 4 ) | DR_7 ) }, 2 }
+
+#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+
+/*!
+ * LoRaMac maximum number of channels
+ */
+#define LORA_MAX_NB_CHANNELS                        72
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MIN_DATARATE                        DR_0
+
+/*!
+ * Minimal datarate that can be used by the node
+ */
+#define LORAMAC_MAX_DATARATE                        DR_4
+
+/*!
+ * Default datarate used by the node
+ */
+#define LORAMAC_DEFAULT_DATARATE                    DR_0
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MIN_TX_POWER                        TX_POWER_10_DBM
+
+/*!
+ * Minimal Tx output power that can be used by the node
+ */
+#define LORAMAC_MAX_TX_POWER                        TX_POWER_30_DBM
+
+/*!
+ * Default Tx output power used by the node
+ */
+#define LORAMAC_DEFAULT_TX_POWER                    TX_POWER_20_DBM
+
+/*!
+ * LoRaMac TxPower definition
+ */
+#define TX_POWER_30_DBM                             0
+#define TX_POWER_28_DBM                             1
+#define TX_POWER_26_DBM                             2
+#define TX_POWER_24_DBM                             3
+#define TX_POWER_22_DBM                             4
+#define TX_POWER_20_DBM                             5
+#define TX_POWER_18_DBM                             6
+#define TX_POWER_16_DBM                             7
+#define TX_POWER_14_DBM                             8
+#define TX_POWER_12_DBM                             9
+#define TX_POWER_10_DBM                             10
+
+/*!
+ * LoRaMac datarates definition
+ */
+#define DR_0                                        0  // SF10 - BW125 |
+#define DR_1                                        1  // SF9  - BW125 |
+#define DR_2                                        2  // SF8  - BW125 +-> Up link
+#define DR_3                                        3  // SF7  - BW125 |
+#define DR_4                                        4  // SF8  - BW500 |
+#define DR_5                                        5  // RFU
+#define DR_6                                        6  // RFU
+#define DR_7                                        7  // RFU
+#define DR_8                                        8  // SF12 - BW500 |
+#define DR_9                                        9  // SF11 - BW500 |
+#define DR_10                                       10 // SF10 - BW500 |
+#define DR_11                                       11 // SF9  - BW500 |
+#define DR_12                                       12 // SF8  - BW500 +-> Down link
+#define DR_13                                       13 // SF7  - BW500 |
+#define DR_14                                       14 // RFU          |
+#define DR_15                                       15 // RFU          |
+
+/*!
+ * Second reception window channel definition.
+ */
+// Channel = { Frequency [Hz], Datarate }
+#define RX_WND_2_CHANNEL                                  { 923300000, DR_8 }
+
+/*!
+ * LoRaMac maximum number of bands
+ */
+#define LORA_MAX_NB_BANDS                           1
+
+// Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff }
+#define BAND0              { 1, TX_POWER_20_DBM, 0,  0 } //  100.0 %
+
+/*!
+ * LoRaMac default channels
+ */
+// Channel = { Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band }
+/*
+ * US band channels are initialized using a loop in LoRaMacInit function
+ * \code
+ * // 125 kHz channels
+ * for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ )
+ * {
+ *     Channels[i].Frequency = 902.3e6 + i * 200e3;
+ *     Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
+ *     Channels[i].Band = 0;
+ * }
+ * // 500 kHz channels 
+ * for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ )
+ * {
+ *     Channels[i].Frequency = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6;
+ *     Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
+ *     Channels[i].Band = 0;
+ * }
+ * \endcode
+ */
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+
+#endif // __LORAMAC_BOARD_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMac.cpp	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,2702 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: LoRa MAC layer implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#include "mbed.h"
+#include "board.h"
+#include "utilities.h"
+#include "sx1276-hal.h"
+
+#include "LoRaMacCrypto.h"
+#include "LoRaMac.h"
+
+/*!
+ * Maximum PHY layer payload size
+ */
+#define LORAMAC_PHY_MAXPAYLOAD                      250
+
+/*!
+ * Device IEEE EUI
+ */
+static uint8_t *LoRaMacDevEui;
+
+/*!
+ * Application IEEE EUI
+ */
+static uint8_t *LoRaMacAppEui;
+
+/*!
+ * AES encryption/decryption cipher application key
+ */
+static uint8_t *LoRaMacAppKey;
+
+/*!
+ * AES encryption/decryption cipher network session key
+ */
+static uint8_t LoRaMacNwkSKey[] =
+{
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*!
+ * AES encryption/decryption cipher application session key
+ */
+static uint8_t LoRaMacAppSKey[] =
+{
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*!
+ * Device nonce is a random value extracted by issuing a sequence of RSSI
+ * measurements
+ */
+static uint16_t LoRaMacDevNonce;
+
+/*!
+ * Network ID ( 3 bytes )
+ */
+static uint32_t LoRaMacNetID;
+
+/*!
+ * Mote Address
+ */
+static uint32_t LoRaMacDevAddr;
+
+/*!
+ * Mutlicast channels linked list
+ */
+static MulticastParams_t *MulticastChannels = NULL;
+
+/*!
+ * Actual device class
+ */
+static DeviceClass_t LoRaMacDeviceClass;
+
+/*!
+ * Indicates if the node is connected to a private or public network
+ */
+static bool PublicNetwork;
+
+/*!
+ * Indicates if the node supports repeaters
+ */
+static bool RepeaterSupport;
+
+/*!
+ * Buffer containing the data to be sent or received.
+ */
+static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD];
+
+/*!
+ * Length of packet in LoRaMacBuffer
+ */
+static uint16_t LoRaMacBufferPktLen = 0;
+
+/*!
+ * Buffer containing the upper layer data.
+ */
+static uint8_t LoRaMacPayload[LORAMAC_PHY_MAXPAYLOAD];
+static uint8_t LoRaMacRxPayload[LORAMAC_PHY_MAXPAYLOAD];
+
+/*!
+ * LoRaMAC frame counter. Each time a packet is sent the counter is incremented.
+ * Only the 16 LSB bits are sent
+ */
+static uint32_t UpLinkCounter = 1;
+
+/*!
+ * LoRaMAC frame counter. Each time a packet is received the counter is incremented.
+ * Only the 16 LSB bits are received
+ */
+static uint32_t DownLinkCounter = 0;
+
+/*!
+ * IsPacketCounterFixed enables the MIC field tests by fixing the
+ * UpLinkCounter value
+ */
+static bool IsUpLinkCounterFixed = false;
+
+/*!
+ * Used for test purposes. Disables the opening of the reception windows.
+ */
+static bool IsRxWindowsEnabled = true;
+
+/*!
+ * Indicates if the MAC layer has already joined a network.
+ */
+static bool IsLoRaMacNetworkJoined = false;
+
+/*!
+ * LoRaMac ADR control status
+ */
+static bool AdrCtrlOn = false;
+
+/*!
+ * Counts the number of missed ADR acknowledgements
+ */
+static uint32_t AdrAckCounter = 0;
+
+/*!
+ * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates
+ * if the nodes needs to manage the server acknowledgement.
+ */
+static bool NodeAckRequested = false;
+
+/*!
+ * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates
+ * if the ACK bit must be set for the next transmission
+ */
+static bool SrvAckRequested = false;
+
+/*!
+ * Indicates if the MAC layer wants to send MAC commands
+ */
+static bool MacCommandsInNextTx = false;
+
+/*!
+ * Contains the current MacCommandsBuffer index
+ */
+static uint8_t MacCommandsBufferIndex = 0;
+
+/*!
+ * Buffer containing the MAC layer commands
+ */
+static uint8_t MacCommandsBuffer[15];
+
+#if defined( USE_BAND_433 )
+/*!
+ * Data rates table definition
+ */
+const uint8_t Datarates[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarate[] = { 59, 59, 59, 123, 250, 250, 250, 250 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarateRepeater[] = { 59, 59, 59, 123, 230, 230, 230, 230 };
+
+/*!
+ * Tx output powers table definition
+ */
+const int8_t TxPowers[]    = { 20, 14, 11,  8,  5,  2 };
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[LORA_MAX_NB_BANDS] =
+{
+    BAND0,
+};
+
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
+{
+    LC1,
+    LC2,
+    LC3,
+};
+#elif defined( USE_BAND_780 )
+/*!
+ * Data rates table definition
+ */
+const uint8_t Datarates[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarate[] = { 59, 59, 59, 123, 250, 250, 250, 250 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarateRepeater[] = { 59, 59, 59, 123, 230, 230, 230, 230 };
+
+/*!
+ * Tx output powers table definition
+ */
+const int8_t TxPowers[]    = { 20, 14, 11,  8,  5,  2 };
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[LORA_MAX_NB_BANDS] =
+{
+    BAND0,
+};
+
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
+{
+    LC1,
+    LC2,
+    LC3,
+};
+#elif defined( USE_BAND_868 )
+/*!
+ * Data rates table definition
+ */
+const uint8_t Datarates[]  = { 12, 11, 10,  9,  8,  7,  7, 50 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarate[] = { 51, 51, 51, 115, 242, 242, 242, 242 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarateRepeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 };
+
+/*!
+ * Tx output powers table definition
+ */
+const int8_t TxPowers[]    = { 20, 14, 11,  8,  5,  2 };
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[LORA_MAX_NB_BANDS] =
+{
+    BAND0,
+    BAND1,
+    BAND2,
+    BAND3,
+    BAND4,
+};
+
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS] =
+{
+    LC1,
+    LC2,
+    LC3,
+    LC4,
+    LC5,
+    LC6,
+    LC7,
+    LC8,
+    LC9,
+};
+#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+/*!
+ * Data rates table definition
+ */
+const uint8_t Datarates[]  = { 10, 9, 8,  7,  8,  0,  0, 0, 12, 11, 10, 9, 8, 7, 0, 0 };
+
+/*!
+ * Up/Down link data rates offset definition
+ */
+const int8_t datarateOffsets[16][4] = 
+{
+    { DR_10, DR_9 , DR_8 , DR_8  }, // DR_0
+    { DR_11, DR_10, DR_9 , DR_8  }, // DR_1
+    { DR_12, DR_11, DR_10, DR_9  }, // DR_2
+    { DR_13, DR_12, DR_11, DR_10 }, // DR_3
+    { DR_13, DR_13, DR_12, DR_11 }, // DR_4
+    { 0xFF , 0xFF , 0xFF , 0xFF  },
+    { 0xFF , 0xFF , 0xFF , 0xFF  },
+    { 0xFF , 0xFF , 0xFF , 0xFF  },
+    { DR_8 , DR_8 , DR_8 , DR_8  },
+    { DR_9 , DR_8 , DR_8 , DR_8  },
+    { DR_10, DR_9 , DR_8 , DR_8  },
+    { DR_11, DR_10, DR_9 , DR_8  },
+    { DR_12, DR_11, DR_10, DR_9  },
+    { DR_13, DR_12, DR_11, DR_10 },
+    { 0xFF , 0xFF , 0xFF , 0xFF  },
+    { 0xFF , 0xFF , 0xFF , 0xFF  },
+};
+
+/*!
+ * Maximum payload with respect to the datarate index. Cannot operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarate[] = { 11, 53, 129, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
+
+/*!
+ * Maximum payload with respect to the datarate index. Can operate with repeater.
+ */
+const uint8_t MaxPayloadOfDatarateRepeater[] = { 11, 53, 129, 242, 242, 0, 0, 0, 33, 103, 222, 222, 222, 222, 0, 0 };
+
+/*!
+ * Tx output powers table definition
+ */
+const int8_t TxPowers[]    = { 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 };
+
+/*!
+ * LoRaMac bands
+ */
+static Band_t Bands[LORA_MAX_NB_BANDS] =
+{
+    BAND0,
+};
+
+/*!
+ * LoRaMAC channels
+ */
+static ChannelParams_t Channels[LORA_MAX_NB_CHANNELS];
+
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+
+/*!
+ * LoRaMAC 2nd reception window settings
+ */
+static Rx2ChannelParams_t Rx2Channel = RX_WND_2_CHANNEL;
+
+/*!
+ * Datarate offset between uplink and downlink on first window
+ */
+static uint8_t Rx1DrOffset = 0;
+
+/*!
+ * Mask indicating which channels are enabled
+ */
+static uint16_t ChannelsMask[6];
+
+/*!
+ * Channels Tx output power
+ */
+static int8_t ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER;
+
+/*!
+ * Channels datarate
+ */
+static int8_t ChannelsDatarate = LORAMAC_DEFAULT_DATARATE;
+
+/*!
+ * Channels default datarate
+ */
+static int8_t ChannelsDefaultDatarate = LORAMAC_DEFAULT_DATARATE;
+
+/*!
+ * Number of uplink messages repetitions [1:15] (unconfirmed messages only)
+ */
+static uint8_t ChannelsNbRep = 1;
+
+/*!
+ * Uplink messages repetitions counter
+ */
+static uint8_t ChannelsNbRepCounter = 0;
+
+/*!
+ * Maximum duty cycle
+ * \remark Possibility to shutdown the device.
+ */
+static uint8_t MaxDCycle = 0;
+
+/*!
+ * Agregated duty cycle management
+ */
+static uint16_t AggregatedDCycle;
+static TimerTime_t AggregatedLastTxDoneTime;
+static TimerTime_t AggregatedTimeOff;
+
+/*!
+ * Enables/Disables duty cycle management (Test only)
+ */
+static bool DutyCycleOn;
+
+/*!
+ * Current channel index
+ */
+static uint8_t Channel;
+
+/*!
+ * LoRaMac internal states
+ */
+enum LoRaMacState_e
+{
+    MAC_IDLE          = 0x00000000,
+    MAC_TX_RUNNING    = 0x00000001,
+    MAC_RX            = 0x00000002,
+    MAC_ACK_REQ       = 0x00000004,
+    MAC_ACK_RETRY     = 0x00000008,
+    MAC_CHANNEL_CHECK = 0x00000010,
+};
+
+/*!
+ * LoRaMac internal state
+ */
+uint32_t LoRaMacState = MAC_IDLE;
+
+/*!
+ * LoRaMac timer used to check the LoRaMacState (runs every second)
+ */
+static TimerEvent_t MacStateCheckTimer;
+
+/*!
+ * LoRaMac upper layer event functions
+ */
+static LoRaMacEvent_t *LoRaMacEvents;
+
+/*!
+ * LoRaMac notification event flags
+ */
+LoRaMacEventFlags_t LoRaMacEventFlags;
+
+/*!
+ * LoRaMac notification event info
+ */
+LoRaMacEventInfo_t LoRaMacEventInfo;
+
+/*!
+ * LoRaMac channel check timer
+ */
+static TimerEvent_t ChannelCheckTimer;
+
+/*!
+ * LoRaMac duty cycle delayed Tx timer
+ */
+static TimerEvent_t TxDelayedTimer;
+
+/*!
+ * LoRaMac reception windows timers
+ */
+static TimerEvent_t RxWindowTimer1;
+static TimerEvent_t RxWindowTimer2;
+
+/*!
+ * LoRaMac reception windows delay from end of Tx
+ */
+static uint32_t ReceiveDelay1;
+static uint32_t ReceiveDelay2;
+static uint32_t JoinAcceptDelay1;
+static uint32_t JoinAcceptDelay2;
+
+/*!
+ * LoRaMac reception windows delay
+ * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME
+ *         join frame  : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME
+ */
+static uint32_t RxWindow1Delay;
+static uint32_t RxWindow2Delay;
+
+/*!
+ * LoRaMac maximum time a reception window stays open
+ */
+static uint32_t MaxRxWindow;
+
+/*!
+ * Acknowledge timeout timer. Used for packet retransmissions.
+ */
+static TimerEvent_t AckTimeoutTimer;
+
+/*!
+ * Number of trials to get a frame acknowledged
+ */
+static uint8_t AckTimeoutRetries = 1;
+
+/*!
+ * Number of trials to get a frame acknowledged
+ */
+static uint8_t AckTimeoutRetriesCounter = 1;
+
+/*!
+ * Indicates if the AckTimeout timer has expired or not
+ */
+static bool AckTimeoutRetry = false;
+
+/*!
+ * Last transmission time on air
+ */
+TimerTime_t TxTimeOnAir = 0;
+
+/*!
+ * Function to be executed on Radio Tx Done event
+ */
+static void OnRadioTxDone( void );
+
+/*!
+ * Function to be executed on Radio Rx Done event
+ */
+static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
+
+/*!
+ * Function executed on Radio Tx Timeout event
+ */
+static void OnRadioTxTimeout( void );
+
+/*!
+ * Function executed on Radio Rx error event
+ */
+static void OnRadioRxError( void );
+
+/*!
+ * Function executed on Radio Rx Timeout event
+ */
+static void OnRadioRxTimeout( void );
+
+/*!
+ * Function executed on Resend Frame timer event.
+ */
+static void OnMacStateCheckTimerEvent( void );
+
+/*!
+ * Function executed on duty cycle delayed Tx  timer event
+ */
+static void OnTxDelayedTimerEvent( void );
+
+/*!
+ * Function executed on channel check timer event
+ */
+static void OnChannelCheckTimerEvent( void );
+
+/*!
+ * Function executed on first Rx window timer event
+ */
+static void OnRxWindow1TimerEvent( void );
+
+/*!
+ * Function executed on second Rx window timer event
+ */
+static void OnRxWindow2TimerEvent( void );
+
+/*!
+ * Function executed on AckTimeout timer event
+ */
+static void OnAckTimeoutTimerEvent( void );
+
+/*!
+ * Radio events function pointer
+ */
+//static RadioEvents_t RadioEvents;
+SX1276MB1xAS Radio( OnRadioTxDone, OnRadioTxTimeout, OnRadioRxDone, OnRadioRxTimeout, OnRadioRxError, NULL, NULL );
+
+/*!
+ * \brief Validates if the payload fits into the frame, taking the datarate
+ *        into account.
+ *
+ * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0
+ *
+ * \param lenN Length of the application payload. The length depends on the
+ *             datarate and is region specific
+ *
+ * \param datarate Current datarate
+ *
+ * \retval [false: payload does not fit into the frame, true: payload fits into
+ *          the frame]
+ */
+static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate );
+
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+/*!
+ * \brief Counts the number of enabled 125 kHz channels in the channel mask.
+ *        This function can only be applied to US915 band.
+ *
+ * \param channelsMask Pointer to the first element of the channel mask
+ *
+ * \retval Number of enabled channels in the channel mask
+ */
+static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask );
+#endif
+
+/*!
+ * \brief Limits the Tx power according to the number of enabled channels
+ *
+ * \retval Returns the maximum valid tx power
+ */
+static int8_t LimitTxPower( int8_t txPower );
+
+/*!
+ * Searches and set the next random available channel
+ *
+ * \retval status  Function status [0: OK, 1: Unable to find a free channel]
+ */
+static uint8_t LoRaMacSetNextChannel( void )
+{
+    uint8_t i = 0;
+    uint8_t j = 0;
+    uint8_t k = 0;
+    uint8_t nbEnabledChannels = 0;
+    uint8_t enabledChannels[LORA_MAX_NB_CHANNELS];
+    TimerTime_t curTime = TimerGetCurrentTime( );
+
+    memset( enabledChannels, 0, LORA_MAX_NB_CHANNELS );
+
+    // Update Aggregated duty cycle
+    if( AggregatedTimeOff < ( curTime - AggregatedLastTxDoneTime ) )
+    {
+        AggregatedTimeOff = 0;
+    }
+
+    // Update bands Time OFF
+    TimerTime_t minTime = ( TimerTime_t )( -1 );
+    for( i = 0; i < LORA_MAX_NB_BANDS; i++ )
+    {
+        if( DutyCycleOn == true )
+        {
+            if( Bands[i].TimeOff < ( curTime - Bands[i].LastTxDoneTime ) )
+            {
+                Bands[i].TimeOff = 0;
+            }
+            if( Bands[i].TimeOff != 0 )
+            {
+                minTime = MIN( Bands[i].TimeOff, minTime );
+            }
+        }
+        else
+        {
+            minTime = 0;
+            Bands[i].TimeOff = 0;
+        }
+    }
+
+    // Search how many channels are enabled
+    for( i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ )
+    {
+        for( j = 0; j < 16; j++ )
+        {
+            if( ( ChannelsMask[k] & ( 1 << j ) ) != 0 )
+            {
+                if( Channels[i + j].Frequency == 0 )
+                { // Check if the channel is enabled
+                    continue;
+                }
+                if( ( ( Channels[i + j].DrRange.Fields.Min <= ChannelsDatarate ) &&
+                      ( ChannelsDatarate <= Channels[i + j].DrRange.Fields.Max ) ) == false )
+                { // Check if the current channel selection supports the given datarate
+                    continue;
+                }
+                if( Bands[Channels[i + j].Band].TimeOff > 0 )
+                { // Check if the band is available for transmission
+                    continue;
+                }
+                if( AggregatedTimeOff > 0 )
+                { // Check if there is time available for transmission
+                    continue;
+                }
+                enabledChannels[nbEnabledChannels++] = i + j;
+            }
+        }
+    }
+    if( nbEnabledChannels > 0 )
+    {
+        Channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
+        LoRaMacState &= ~MAC_CHANNEL_CHECK;
+        TimerStop( &ChannelCheckTimer );
+        return 0;
+    }
+    // No free channel found. 
+    // Check again
+    if( ( LoRaMacState & MAC_CHANNEL_CHECK ) == 0 )
+    {
+        TimerSetValue( &ChannelCheckTimer, minTime );
+        TimerStart( &ChannelCheckTimer );
+        LoRaMacState |= MAC_CHANNEL_CHECK;
+    }
+    return 1;
+}
+
+/*
+ * TODO: Add documentation
+ */
+void OnChannelCheckTimerEvent( void )
+{
+    TimerStop( &ChannelCheckTimer );
+    
+    LoRaMacState &= ~MAC_CHANNEL_CHECK;
+    if( LoRaMacSetNextChannel( ) == 0 )
+    {
+        if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING )
+        {
+           LoRaMacSendFrameOnChannel( Channels[Channel] );
+        }
+    }
+}
+
+/*!
+ * Adds a new MAC command to be sent.
+ *
+ * \Remark MAC layer internal function
+ *
+ * \param [in] cmd MAC command to be added
+ *                 [MOTE_MAC_LINK_CHECK_REQ,
+ *                  MOTE_MAC_LINK_ADR_ANS,
+ *                  MOTE_MAC_DUTY_CYCLE_ANS,
+ *                  MOTE_MAC_RX2_PARAM_SET_ANS,
+ *                  MOTE_MAC_DEV_STATUS_ANS
+ *                  MOTE_MAC_NEW_CHANNEL_ANS]
+ * \param [in] p1  1st parameter ( optional depends on the command )
+ * \param [in] p2  2nd parameter ( optional depends on the command )
+ *
+ * \retval status  Function status [0: OK, 1: Unknown command, 2: Buffer full]
+ */
+static uint8_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 )
+{
+    MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
+    switch( cmd )
+    {
+        case MOTE_MAC_LINK_CHECK_REQ:
+            // No payload for this command
+            break;
+        case MOTE_MAC_LINK_ADR_ANS:
+            // Margin
+            MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+            break;
+        case MOTE_MAC_DUTY_CYCLE_ANS:
+            // No payload for this answer
+            break;
+        case MOTE_MAC_RX_PARAM_SETUP_ANS:
+            // Status: Datarate ACK, Channel ACK
+            MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+            break;
+        case MOTE_MAC_DEV_STATUS_ANS:
+            // 1st byte Battery
+            // 2nd byte Margin
+            MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+            MacCommandsBuffer[MacCommandsBufferIndex++] = p2;
+            break;
+        case MOTE_MAC_NEW_CHANNEL_ANS:
+            // Status: Datarate range OK, Channel frequency OK
+            MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
+            break;
+        case MOTE_MAC_RX_TIMING_SETUP_ANS:
+            // No payload for this answer
+            break;
+        default:
+            return 1;
+    }
+    if( MacCommandsBufferIndex <= 15 )
+    {
+        MacCommandsInNextTx = true;
+        return 0;
+    }
+    else
+    {
+        return 2;
+    }
+}
+
+// TODO: Add Documentation
+static void LoRaMacNotify( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info )
+{
+    if( ( LoRaMacEvents != NULL ) && ( LoRaMacEvents->MacEvent != NULL ) )
+    {
+        LoRaMacEvents->MacEvent( flags, info );
+    }
+    flags->Value = 0;
+}
+
+typedef uint8_t ( *GetBatteryLevel )( );
+GetBatteryLevel LoRaMacGetBatteryLevel;
+
+void LoRaMacInit( LoRaMacEvent_t *events, uint8_t ( *getBatteryLevel )( ) )
+{
+    LoRaMacEvents = events;
+
+    LoRaMacEventFlags.Value = 0;
+    
+    LoRaMacEventInfo.TxAckReceived = false;
+    LoRaMacEventInfo.TxNbRetries = 0;
+    LoRaMacEventInfo.TxDatarate = 7;
+    LoRaMacEventInfo.RxPort = 1;
+    LoRaMacEventInfo.RxBuffer = NULL;
+    LoRaMacEventInfo.RxBufferSize = 0;
+    LoRaMacEventInfo.RxRssi = 0;
+    LoRaMacEventInfo.RxSnr = 0;
+    LoRaMacEventInfo.Energy = 0;
+    LoRaMacEventInfo.DemodMargin = 0;
+    LoRaMacEventInfo.NbGateways = 0;
+    LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+   
+    LoRaMacGetBatteryLevel = getBatteryLevel;
+    
+    LoRaMacDeviceClass = CLASS_A;
+    
+    UpLinkCounter = 1;
+    DownLinkCounter = 0;
+    
+    IsLoRaMacNetworkJoined = false;
+    LoRaMacState = MAC_IDLE;
+
+#if defined( USE_BAND_433 )
+    ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+#elif defined( USE_BAND_780 )
+    ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+#elif defined( USE_BAND_868 )
+    ChannelsMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
+#elif defined( USE_BAND_915 )
+    ChannelsMask[0] = 0xFFFF;
+    ChannelsMask[1] = 0xFFFF;
+    ChannelsMask[2] = 0xFFFF;
+    ChannelsMask[3] = 0xFFFF;
+    ChannelsMask[4] = 0x00FF;
+    ChannelsMask[5] = 0x0000;
+#elif defined( USE_BAND_915_HYBRID )
+    ChannelsMask[0] = 0x00FF;
+    ChannelsMask[1] = 0x0000;
+    ChannelsMask[2] = 0x0000;
+    ChannelsMask[3] = 0x0000;
+    ChannelsMask[4] = 0x0001;
+    ChannelsMask[5] = 0x0000;
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+    // 125 kHz channels
+    for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ )
+    {
+        Channels[i].Frequency = 902.3e6 + i * 200e3;
+        Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0;
+        Channels[i].Band = 0;
+    }
+    // 500 kHz channels
+    for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ )
+    {
+        Channels[i].Frequency = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6;
+        Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4;
+        Channels[i].Band = 0;
+    }
+#endif
+
+    ChannelsTxPower = LORAMAC_DEFAULT_TX_POWER;
+    ChannelsDefaultDatarate = ChannelsDatarate = LORAMAC_DEFAULT_DATARATE;
+    ChannelsNbRep = 1;
+    ChannelsNbRepCounter = 0;
+    
+    MaxDCycle = 0;
+    AggregatedDCycle = 1;
+    AggregatedLastTxDoneTime = 0;
+    AggregatedTimeOff = 0;
+
+#if defined( USE_BAND_433 )
+    DutyCycleOn = false;
+#elif defined( USE_BAND_780 )
+    DutyCycleOn = false;
+#elif defined( USE_BAND_868 )
+    DutyCycleOn = true;
+#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+    DutyCycleOn = false;
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+
+    MaxRxWindow = MAX_RX_WINDOW;
+    ReceiveDelay1 = RECEIVE_DELAY1;
+    ReceiveDelay2 = RECEIVE_DELAY2;
+    JoinAcceptDelay1 = JOIN_ACCEPT_DELAY1;
+    JoinAcceptDelay2 = JOIN_ACCEPT_DELAY2;
+
+    TimerInit( &MacStateCheckTimer, OnMacStateCheckTimerEvent );
+    TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT );
+
+    TimerInit( &ChannelCheckTimer, OnChannelCheckTimerEvent );
+    TimerInit( &TxDelayedTimer, OnTxDelayedTimerEvent );
+    TimerInit( &RxWindowTimer1, OnRxWindow1TimerEvent );
+    TimerInit( &RxWindowTimer2, OnRxWindow2TimerEvent );
+    TimerInit( &AckTimeoutTimer, OnAckTimeoutTimerEvent );
+
+    // Random seed initialization
+    srand( Radio.Random( ) );
+
+    // Initialize channel index.
+    Channel = LORA_MAX_NB_CHANNELS;
+
+    PublicNetwork = true;
+    LoRaMacSetPublicNetwork( PublicNetwork );
+    Radio.Sleep( );
+}
+
+void LoRaMacSetAdrOn( bool enable )
+{
+    AdrCtrlOn = enable;
+}
+
+void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey )
+{
+    LoRaMacNetID = netID;
+    LoRaMacDevAddr = devAddr;
+    LoRaMacMemCpy( nwkSKey, LoRaMacNwkSKey, 16 );
+    LoRaMacMemCpy( appSKey, LoRaMacAppSKey, 16 );
+    
+    IsLoRaMacNetworkJoined = true;
+}
+
+void LoRaMacMulticastChannelAdd( MulticastParams_t *channelParam )
+{
+    // Reset downlink counter
+    channelParam->DownLinkCounter = 0;
+    
+    if( MulticastChannels == NULL )
+    {
+        MulticastChannels = channelParam;
+    }
+    else
+    {
+        MulticastParams_t *cur = MulticastChannels;
+        while( cur->Next != NULL )
+        {
+            cur = cur->Next;
+        }
+        cur->Next = channelParam;
+    }
+}
+
+void LoRaMacMulticastChannelRemove( MulticastParams_t *channelParam )
+{
+    MulticastParams_t *cur = NULL;
+    
+    // Remove the front element
+    if( MulticastChannels == channelParam )
+    {
+        if( MulticastChannels != NULL )
+        {
+            cur = MulticastChannels;
+            MulticastChannels = MulticastChannels->Next;
+            cur->Next = NULL;
+            // Last node in the list
+            if( cur == MulticastChannels )
+            {
+                MulticastChannels = NULL;
+            }
+        }
+        return;
+    }
+    
+    // Remove last element
+    if( channelParam->Next == NULL )
+    {
+        if( MulticastChannels != NULL )
+        {
+            cur = MulticastChannels;
+            MulticastParams_t *last = NULL;
+            while( cur->Next != NULL )
+            {
+                last = cur;
+                cur = cur->Next;
+            }
+            if( last != NULL )
+            {
+                last->Next = NULL;
+            }
+            // Last node in the list
+            if( cur == last )
+            {
+                MulticastChannels = NULL;
+            }
+        }
+        return;
+    }
+    
+    // Remove a middle element
+    cur = MulticastChannels;
+    while( cur != NULL )
+    {
+        if( cur->Next == channelParam )
+        {
+            break;
+        }
+        cur = cur->Next;
+    }
+    if( cur != NULL )
+    {
+        MulticastParams_t *tmp = cur ->Next;
+        cur->Next = tmp->Next;
+        tmp->Next = NULL;
+    }
+}
+
+uint8_t LoRaMacJoinReq( uint8_t *devEui, uint8_t *appEui, uint8_t *appKey )
+{
+    LoRaMacHeader_t macHdr;
+
+    LoRaMacDevEui = devEui;
+    LoRaMacAppEui = appEui;
+    LoRaMacAppKey = appKey;
+    
+    macHdr.Value = 0;
+    macHdr.Bits.MType        = FRAME_TYPE_JOIN_REQ;
+    
+    IsLoRaMacNetworkJoined = false;
+    
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+    static uint8_t drSwitch = 0;
+    
+    if( drSwitch == 0 )
+    {
+        ChannelsDatarate = DR_0;
+    }
+    else
+    {
+        ChannelsDatarate = DR_4;
+    }
+    drSwitch = ( drSwitch + 1 ) % 2;
+#endif
+    return LoRaMacSend( &macHdr, NULL, 0, NULL, 0 );
+}
+
+uint8_t LoRaMacLinkCheckReq( void )
+{
+    return AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 );
+}
+
+uint8_t LoRaMacSendFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
+{
+    LoRaMacHeader_t macHdr;
+
+    macHdr.Value = 0;
+
+    macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP;
+    return LoRaMacSend( &macHdr, NULL, fPort, fBuffer, fBufferSize );
+}
+
+uint8_t LoRaMacSendConfirmedFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize, uint8_t retries )
+{
+    LoRaMacHeader_t macHdr;
+
+    if( AdrCtrlOn == false )
+    {
+        ChannelsDatarate = ChannelsDefaultDatarate;
+    }
+    AckTimeoutRetries = retries;
+    AckTimeoutRetriesCounter = 1;
+    
+    macHdr.Value = 0;
+
+    macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
+    return LoRaMacSend( &macHdr, NULL, fPort, fBuffer, fBufferSize );
+}
+
+uint8_t LoRaMacSend( LoRaMacHeader_t *macHdr, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
+{
+    LoRaMacFrameCtrl_t fCtrl;
+
+    fCtrl.Value = 0;
+
+    fCtrl.Bits.FOptsLen      = 0;
+    fCtrl.Bits.FPending      = 0;
+    fCtrl.Bits.Ack           = false;
+    fCtrl.Bits.AdrAckReq     = false;
+    fCtrl.Bits.Adr           = AdrCtrlOn;
+
+    if( LoRaMacSetNextChannel( ) == 0 )
+    {
+        return LoRaMacSendOnChannel( Channels[Channel], macHdr, &fCtrl, fOpts, fPort, fBuffer, fBufferSize );
+    }
+    return 5;
+}
+
+uint8_t LoRaMacPrepareFrame( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
+{
+    uint16_t i;
+    uint8_t pktHeaderLen = 0;
+    uint32_t mic = 0;
+    
+    LoRaMacBufferPktLen = 0;
+    
+    NodeAckRequested = false;
+    
+    if( fBuffer == NULL )
+    {
+        fBufferSize = 0;
+    }
+    else
+    {
+        if( ValidatePayloadLength( fBufferSize, ChannelsDatarate ) == false )
+        {
+            return 3;
+        }
+    }
+
+    LoRaMacBuffer[pktHeaderLen++] = macHdr->Value;
+
+    switch( macHdr->Bits.MType )
+    {
+        case FRAME_TYPE_JOIN_REQ:            
+            RxWindow1Delay = JoinAcceptDelay1 - RADIO_WAKEUP_TIME;
+            RxWindow2Delay = JoinAcceptDelay2 - RADIO_WAKEUP_TIME;
+
+            LoRaMacBufferPktLen = pktHeaderLen;
+        
+            LoRaMacMemCpy( LoRaMacAppEui, LoRaMacBuffer + LoRaMacBufferPktLen, 8 );
+            LoRaMacBufferPktLen += 8;
+            LoRaMacMemCpy( LoRaMacDevEui, LoRaMacBuffer + LoRaMacBufferPktLen, 8 );
+            LoRaMacBufferPktLen += 8;
+
+            LoRaMacDevNonce = Radio.Random( );
+            
+            LoRaMacBuffer[LoRaMacBufferPktLen++] = LoRaMacDevNonce & 0xFF;
+            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( LoRaMacDevNonce >> 8 ) & 0xFF;
+
+            LoRaMacJoinComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen & 0xFF, LoRaMacAppKey, &mic );
+            
+            LoRaMacBuffer[LoRaMacBufferPktLen++] = mic & 0xFF;
+            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 8 ) & 0xFF;
+            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 16 ) & 0xFF;
+            LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 24 ) & 0xFF;
+            
+            break;
+        case FRAME_TYPE_DATA_CONFIRMED_UP:
+            NodeAckRequested = true;
+            //Intentional falltrough
+        case FRAME_TYPE_DATA_UNCONFIRMED_UP:
+            if( IsLoRaMacNetworkJoined == false )
+            {
+                return 2; // No network has been joined yet
+            }
+            
+            RxWindow1Delay = ReceiveDelay1 - RADIO_WAKEUP_TIME;
+            RxWindow2Delay = ReceiveDelay2 - RADIO_WAKEUP_TIME;
+
+            if( fOpts == NULL )
+            {
+                fCtrl->Bits.FOptsLen = 0;
+            }
+
+            if( SrvAckRequested == true )
+            {
+                SrvAckRequested = false;
+                fCtrl->Bits.Ack = 1;
+            }
+            
+            if( fCtrl->Bits.Adr == true )
+            {
+                if( ChannelsDatarate == LORAMAC_MIN_DATARATE )
+                {
+                    AdrAckCounter = 0;
+                    fCtrl->Bits.AdrAckReq = false;
+                }
+                else
+                {
+                    if( AdrAckCounter > ADR_ACK_LIMIT )
+                    {
+                        fCtrl->Bits.AdrAckReq = true;
+                    }
+                    else
+                    {
+                        fCtrl->Bits.AdrAckReq = false;
+                    }
+                    if( AdrAckCounter > ( ADR_ACK_LIMIT + ADR_ACK_DELAY ) )
+                    {
+                        AdrAckCounter = 0;
+#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+                        if( ChannelsDatarate > LORAMAC_MIN_DATARATE )
+                        {
+                            ChannelsDatarate--;
+                        }
+                        else
+                        {
+                            // Re-enable default channels LC1, LC2, LC3
+                            ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
+                        }
+#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+                        if( ( ChannelsDatarate > LORAMAC_MIN_DATARATE ) && ( ChannelsDatarate == DR_8 ) )
+                        {
+                            ChannelsDatarate = DR_4;
+                        }
+                        if( ChannelsDatarate > LORAMAC_MIN_DATARATE )
+                        {
+                            ChannelsDatarate--;
+                        }
+                        else
+                        {
+#if defined( USE_BAND_915 )
+                            // Re-enable default channels
+                            ChannelsMask[0] = 0xFFFF;
+                            ChannelsMask[1] = 0xFFFF;
+                            ChannelsMask[2] = 0xFFFF;
+                            ChannelsMask[3] = 0xFFFF;
+                            ChannelsMask[4] = 0x00FF;
+                            ChannelsMask[5] = 0x0000;
+#else // defined( USE_BAND_915_HYBRID )
+                            // Re-enable default channels
+                            ChannelsMask[0] = 0x00FF;
+                            ChannelsMask[1] = 0x0000;
+                            ChannelsMask[2] = 0x0000;
+                            ChannelsMask[3] = 0x0000;
+                            ChannelsMask[4] = 0x0001;
+                            ChannelsMask[5] = 0x0000;
+#endif
+                        }
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+                    }
+                }
+            }
+            
+            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF;
+            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF;
+            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF;
+            LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF;
+
+            LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value;
+
+            LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF;
+            LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF;
+
+            if( fOpts != NULL )
+            {
+                for( i = 0; i < fCtrl->Bits.FOptsLen; i++ )
+                {
+                    LoRaMacBuffer[pktHeaderLen++] = fOpts[i];
+                }
+            }
+            if( ( MacCommandsBufferIndex + fCtrl->Bits.FOptsLen ) <= 15 )
+            {
+                if( MacCommandsInNextTx == true )
+                {
+                    fCtrl->Bits.FOptsLen += MacCommandsBufferIndex;
+                    
+                    // Update FCtrl field with new value of OptionsLength
+                    LoRaMacBuffer[0x05] = fCtrl->Value;
+                    for( i = 0; i < MacCommandsBufferIndex; i++ )
+                    {
+                        LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i];
+                    }
+                }
+                MacCommandsInNextTx = false;
+                MacCommandsBufferIndex = 0;
+            }
+            
+            if( ( pktHeaderLen + fBufferSize ) > LORAMAC_PHY_MAXPAYLOAD )
+            {
+                return 3;
+            }
+
+            if( fBuffer != NULL )
+            {
+                LoRaMacBuffer[pktHeaderLen++] = fPort;
+                
+                if( fPort == 0 )
+                {
+                    LoRaMacPayloadEncrypt( ( uint8_t* )fBuffer, fBufferSize, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload );
+                }
+                else
+                {
+                    LoRaMacPayloadEncrypt( ( uint8_t* )fBuffer, fBufferSize, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, LoRaMacPayload );
+                }
+                LoRaMacMemCpy( LoRaMacPayload, LoRaMacBuffer + pktHeaderLen, fBufferSize );
+            }
+            LoRaMacBufferPktLen = pktHeaderLen + fBufferSize;
+
+            LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic );
+
+            if( ( LoRaMacBufferPktLen + LORAMAC_MFR_LEN ) > LORAMAC_PHY_MAXPAYLOAD )
+            {
+                return 3;
+            }
+            LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF;
+            LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF;
+            LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF;
+            LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF;
+            
+            LoRaMacBufferPktLen += LORAMAC_MFR_LEN;
+            break;
+        default:
+            return 4;
+    }
+
+    return 0;
+}
+
+uint8_t LoRaMacSendFrameOnChannel( ChannelParams_t channel )
+{
+    LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+    LoRaMacEventInfo.TxDatarate = ChannelsDatarate;
+
+    ChannelsTxPower = LimitTxPower( ChannelsTxPower );
+
+    Radio.SetChannel( channel.Frequency );
+    Radio.SetMaxPayloadLength( MODEM_LORA, LoRaMacBufferPktLen );
+
+#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+    if( ChannelsDatarate == DR_7 )
+    { // High Speed FSK channel
+        Radio.SetTxConfig( MODEM_FSK, TxPowers[ChannelsTxPower], 25e3, 0, Datarates[ChannelsDatarate] * 1e3, 0, 5, false, true, 0, 0, false, 3e6 );
+        TxTimeOnAir = Radio.TimeOnAir( MODEM_FSK, LoRaMacBufferPktLen );
+    }
+    else if( ChannelsDatarate == DR_6 )
+    { // High speed LoRa channel
+        Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 1, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 );
+        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
+    }
+    else
+    { // Normal LoRa channel
+        Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 0, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 );
+        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
+    }
+#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+    if( ChannelsDatarate >= DR_4 )
+    { // High speed LoRa channel BW500 kHz
+        Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 2, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 );
+        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
+    }
+    else
+    { // Normal LoRa channel
+        Radio.SetTxConfig( MODEM_LORA, TxPowers[ChannelsTxPower], 0, 0, Datarates[ChannelsDatarate], 1, 8, false, true, 0, 0, false, 3e6 );
+        TxTimeOnAir = Radio.TimeOnAir( MODEM_LORA, LoRaMacBufferPktLen );
+    }
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+
+    if( MaxDCycle == 255 )
+    {
+        return 6;
+    }
+    if( MaxDCycle == 0 )
+    {
+        AggregatedTimeOff = 0;
+    }
+
+    LoRaMacState |= MAC_TX_RUNNING;
+    // Starts the MAC layer status check timer
+    TimerStart( &MacStateCheckTimer );
+    
+    if( MAX( Bands[channel.Band].TimeOff, AggregatedTimeOff ) > ( TimerGetCurrentTime( ) ) )
+    {
+        // Schedule transmission
+        TimerSetValue( &TxDelayedTimer, MAX( Bands[channel.Band].TimeOff, AggregatedTimeOff ) );
+        TimerStart( &TxDelayedTimer );
+    }
+    else
+    {
+        // Send now
+        Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen );
+    }
+    return 0;
+}
+
+
+void OnTxDelayedTimerEvent( void )
+{
+    TimerStop( &TxDelayedTimer );
+    Radio.Send( LoRaMacBuffer, LoRaMacBufferPktLen );
+}
+
+uint8_t LoRaMacSendOnChannel( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize )
+{
+    uint8_t status = 0;
+    
+    if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING )
+    {
+        return 1; // MAC is busy transmitting a previous frame
+    }
+
+    status = LoRaMacPrepareFrame( channel, macHdr, fCtrl, fOpts, fPort, fBuffer, fBufferSize );
+    if( status != 0 )
+    {
+        return status;
+    }
+
+    LoRaMacEventInfo.TxNbRetries = 0;
+    LoRaMacEventInfo.TxAckReceived = false;
+
+    return LoRaMacSendFrameOnChannel( channel );
+}
+
+static void LoRaMacProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize )
+{
+    while( macIndex < commandsSize )
+    {
+        // Decode Frame MAC commands
+        switch( payload[macIndex++] )
+        {
+            case SRV_MAC_LINK_CHECK_ANS:
+                LoRaMacEventFlags.Bits.LinkCheck = 1;
+                LoRaMacEventInfo.DemodMargin = payload[macIndex++];
+                LoRaMacEventInfo.NbGateways = payload[macIndex++];
+                break;
+            case SRV_MAC_LINK_ADR_REQ:
+                {
+                    uint8_t status = 0x07;
+                    uint16_t chMask;
+                    int8_t txPower = 0;
+                    int8_t datarate = 0;
+                    uint8_t nbRep = 0;
+                    uint8_t chMaskCntl = 0;
+                    uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 };
+                    
+                    // Initialize local copy of the channels mask array
+                    for( uint8_t i = 0; i < 6; i++ )
+                    {
+                        channelsMask[i] = ChannelsMask[i];
+                    }
+                    datarate = payload[macIndex++];
+                    txPower = datarate & 0x0F;
+                    datarate = ( datarate >> 4 ) & 0x0F;
+
+                    if( ( AdrCtrlOn == false ) && 
+                        ( ( ChannelsDatarate != datarate ) || ( ChannelsTxPower != txPower ) ) )
+                    { // ADR disabled don't handle ADR requests if server tries to change datarate or txpower
+                        // Answer the server with fail status
+                        // Power ACK     = 0
+                        // Data rate ACK = 0
+                        // Channel mask  = 0
+                        AddMacCommand( MOTE_MAC_LINK_ADR_ANS, 0, 0 );
+                        break;
+                    }
+                    chMask = payload[macIndex++];
+                    chMask |= payload[macIndex++] << 8;
+
+                    nbRep = payload[macIndex++];
+                    chMaskCntl = ( nbRep >> 4 ) & 0x07;
+                    nbRep &= 0x0F;
+                    if( nbRep == 0 )
+                    {
+                        nbRep = 1;
+                    }
+#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+                    if( ( chMaskCntl == 0 ) && ( chMask == 0 ) )
+                    {
+                        status &= 0xFE; // Channel mask KO
+                    }
+                    else if( ( chMaskCntl >= 1 ) && ( chMaskCntl <= 5 ) )
+                    {
+                        // RFU
+                        status &= 0xFE; // Channel mask KO
+                    }
+                    else
+                    {
+                        for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS; i++ )
+                        {
+                            if( chMaskCntl == 6 )
+                            {
+                                if( Channels[i].Frequency != 0 )
+                                {
+                                    chMask |= 1 << i;
+                                }
+                            }
+                            else
+                            {
+                                if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                                    ( Channels[i].Frequency == 0 ) )
+                                {// Trying to enable an undefined channel
+                                    status &= 0xFE; // Channel mask KO
+                                }
+                            }
+                        }
+                        channelsMask[0] = chMask;
+                    }
+#elif defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+                    if( chMaskCntl == 6 )
+                    {
+                        // Enable all 125 kHz channels
+                        for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ )
+                        {
+                            for( uint8_t j = 0; j < 16; j++ )
+                            {
+                                if( Channels[i + j].Frequency != 0 )
+                                {
+                                    channelsMask[k] |= 1 << j;
+                                }
+                            }
+                        }
+                    }
+                    else if( chMaskCntl == 7 )
+                    {
+                        // Disable all 125 kHz channels
+                        channelsMask[0] = 0x0000;
+                        channelsMask[1] = 0x0000;
+                        channelsMask[2] = 0x0000;
+                        channelsMask[3] = 0x0000;
+                    }
+                    else if( chMaskCntl == 5 )
+                    {
+                        // RFU
+                        status &= 0xFE; // Channel mask KO
+                    }
+                    else
+                    {
+                        for( uint8_t i = 0; i < 16; i++ )
+                        {
+                            if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
+                                ( Channels[chMaskCntl * 16 + i].Frequency == 0 ) )
+                            {// Trying to enable an undefined channel
+                                status &= 0xFE; // Channel mask KO
+                            }
+                        }
+                        channelsMask[chMaskCntl] = chMask;
+                        
+                        if( CountNbEnabled125kHzChannels( channelsMask ) < 6 )
+                        {
+                            status &= 0xFE; // Channel mask KO
+                        }
+                    }
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+                    if( ( ( datarate < LORAMAC_MIN_DATARATE ) ||
+                          ( datarate > LORAMAC_MAX_DATARATE ) ) == true )
+                    {
+                        status &= 0xFD; // Datarate KO
+                    }
+
+                    //
+                    // Remark MaxTxPower = 0 and MinTxPower = 5
+                    //
+                    if( ( ( LORAMAC_MAX_TX_POWER <= txPower ) &&
+                          ( txPower <= LORAMAC_MIN_TX_POWER ) ) == false )
+                    {
+                        status &= 0xFB; // TxPower KO
+                    }
+                    if( ( status & 0x07 ) == 0x07 )
+                    {
+                        ChannelsDatarate = datarate;
+                        ChannelsTxPower = txPower;
+#if defined( USE_BAND_915_HYBRID )
+                        ChannelsMask[0] = channelsMask[0] & 0x00FF;
+                        ChannelsMask[1] = channelsMask[1] & 0x0000;
+                        ChannelsMask[2] = channelsMask[2] & 0x0000;
+                        ChannelsMask[3] = channelsMask[3] & 0x0000;
+                        ChannelsMask[4] = channelsMask[4] & 0x0001;
+                        ChannelsMask[5] = channelsMask[5] & 0x0000;
+#else
+                        ChannelsMask[0] = channelsMask[0];
+                        ChannelsMask[1] = channelsMask[1];
+                        ChannelsMask[2] = channelsMask[2];
+                        ChannelsMask[3] = channelsMask[3];
+                        ChannelsMask[4] = channelsMask[4];
+                        ChannelsMask[5] = channelsMask[5];
+#endif
+                        ChannelsNbRep = nbRep;
+                    }
+                    AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 );
+                }
+                break;
+            case SRV_MAC_DUTY_CYCLE_REQ:
+                MaxDCycle = payload[macIndex++];
+                AggregatedDCycle = 1 << MaxDCycle;
+                AddMacCommand( MOTE_MAC_DUTY_CYCLE_ANS, 0, 0 );
+                break;
+            case SRV_MAC_RX_PARAM_SETUP_REQ:
+                {
+                    uint8_t status = 0x07;
+                    int8_t datarate = 0;
+                    int8_t drOffset = 0;
+                    uint32_t freq = 0;
+                
+                    drOffset = payload[macIndex++];
+                    datarate = drOffset & 0x0F;
+                    drOffset = ( drOffset >> 4 ) & 0x0F;
+                    
+                    freq = ( uint32_t )payload[macIndex++];
+                    freq |= ( uint32_t )payload[macIndex++] << 8;
+                    freq |= ( uint32_t )payload[macIndex++] << 16;
+                    freq *= 100;
+                    
+                    if( Radio.CheckRfFrequency( freq ) == false )
+                    {
+                        status &= 0xFE; // Channel frequency KO
+                    }
+                    
+                    if( ( ( datarate < LORAMAC_MIN_DATARATE ) ||
+                          ( datarate > LORAMAC_MAX_DATARATE ) ) == true )
+                    {
+                        status &= 0xFD; // Datarate KO
+                    }
+
+                    if( ( ( drOffset < 0 ) || ( drOffset > 5 ) ) == true )
+                    {
+                        status &= 0xFB; // Rx1DrOffset range KO
+                    }
+                    
+                    if( ( status & 0x07 ) == 0x07 )
+                    {
+                        Rx2Channel.Datarate = datarate;
+                        Rx2Channel.Frequency = freq;
+                        Rx1DrOffset = drOffset;
+                    }
+                    AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 );
+                }
+                break;
+            case SRV_MAC_DEV_STATUS_REQ:
+                AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, LoRaMacGetBatteryLevel( ), LoRaMacEventInfo.RxSnr );
+                break;
+            case SRV_MAC_NEW_CHANNEL_REQ:
+                {
+                    uint8_t status = 0x03;
+                    int8_t channelIndex = 0;
+                    ChannelParams_t chParam;
+                    
+                    channelIndex = payload[macIndex++];
+                    chParam.Frequency = ( uint32_t )payload[macIndex++];
+                    chParam.Frequency |= ( uint32_t )payload[macIndex++] << 8;
+                    chParam.Frequency |= ( uint32_t )payload[macIndex++] << 16;
+                    chParam.Frequency *= 100;
+                    chParam.DrRange.Value = payload[macIndex++];
+                    
+                    if( ( channelIndex < 3 ) || ( channelIndex > LORA_MAX_NB_CHANNELS ) )
+                    {
+                        status &= 0xFE; // Channel frequency KO
+                    }
+                
+                    if( Radio.CheckRfFrequency( chParam.Frequency ) == false )
+                    {
+                        status &= 0xFE; // Channel frequency KO
+                    }
+
+                    if( ( chParam.DrRange.Fields.Min > chParam.DrRange.Fields.Max ) ||
+                        ( ( ( LORAMAC_MIN_DATARATE <= chParam.DrRange.Fields.Min ) &&
+                            ( chParam.DrRange.Fields.Min <= LORAMAC_MAX_DATARATE ) ) == false ) ||
+                        ( ( ( LORAMAC_MIN_DATARATE <= chParam.DrRange.Fields.Max ) &&
+                            ( chParam.DrRange.Fields.Max <= LORAMAC_MAX_DATARATE ) ) == false ) )
+                    {
+                        status &= 0xFD; // Datarate range KO
+                    }
+                    if( ( status & 0x03 ) == 0x03 )
+                    {
+                        LoRaMacSetChannel( channelIndex, chParam );
+                    }
+                    AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 );
+                }
+                break;
+            case SRV_MAC_RX_TIMING_SETUP_REQ:
+                {
+                    uint8_t delay = payload[macIndex++] & 0x0F;
+                    
+                    if( delay == 0 )
+                    {
+                        delay++;
+                    }
+                    ReceiveDelay1 = delay * 1e6;
+                    ReceiveDelay2 = ReceiveDelay1 + 1e6;
+                    AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 );
+                }
+                break;
+            default:
+                // Unknown command. ABORT MAC commands processing
+                return;
+        }
+    }
+}
+
+/*!
+ * Function to be executed on Tx Done event
+ */
+static void OnRadioTxDone( void )
+{
+    TimerTime_t curTime = TimerGetCurrentTime( );
+    if( LoRaMacDeviceClass != CLASS_C )
+    {
+        Radio.Sleep( );
+    }
+    else
+    {
+        OnRxWindow2TimerEvent( );
+    }
+
+    // Update Band Time OFF
+    Bands[Channels[Channel].Band].LastTxDoneTime = curTime;
+    if( DutyCycleOn == true )
+    {
+        Bands[Channels[Channel].Band].TimeOff = TxTimeOnAir * Bands[Channels[Channel].Band].DCycle - TxTimeOnAir;
+    }
+    else
+    {
+        Bands[Channels[Channel].Band].TimeOff = 0;
+    }
+    // Update Agregated Time OFF
+    AggregatedLastTxDoneTime = curTime;
+    AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir );
+
+    if( IsRxWindowsEnabled == true )
+    {
+        TimerSetValue( &RxWindowTimer1, RxWindow1Delay );
+        TimerStart( &RxWindowTimer1 );
+        if( LoRaMacDeviceClass != CLASS_C )
+        {
+            TimerSetValue( &RxWindowTimer2, RxWindow2Delay );
+            TimerStart( &RxWindowTimer2 );
+        }
+    }
+    else
+    {
+        LoRaMacEventFlags.Bits.Tx = 1;
+        LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+    }
+    
+    if( NodeAckRequested == false )
+    {
+        ChannelsNbRepCounter++;
+    }
+}
+
+/*!
+ * Function to be executed on Rx Done event
+ */
+static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
+{
+    LoRaMacHeader_t macHdr;
+    LoRaMacFrameCtrl_t fCtrl;
+
+    uint8_t pktHeaderLen = 0;
+    uint32_t address = 0;
+    uint16_t sequenceCounter = 0;
+    int32_t sequence = 0;
+    uint8_t appPayloadStartIndex = 0;
+    uint8_t port = 0xFF;
+    uint8_t frameLen = 0;
+    uint32_t mic = 0;
+    uint32_t micRx = 0;
+    
+    MulticastParams_t *curMulticastParams = NULL;
+    uint8_t *nwkSKey = LoRaMacNwkSKey;
+    uint8_t *appSKey = LoRaMacAppSKey;
+    uint32_t downLinkCounter = 0;
+    
+    bool isMicOk = false;
+
+    if( LoRaMacDeviceClass != CLASS_C )
+    {
+        Radio.Sleep( );
+    }
+    else
+    {
+        if( LoRaMacEventFlags.Bits.RxSlot == 0 )
+        {
+            OnRxWindow2TimerEvent( );
+        }
+    }
+    TimerStop( &RxWindowTimer2 );
+
+    macHdr.Value = payload[pktHeaderLen++];
+    
+    switch( macHdr.Bits.MType )
+    {
+        case FRAME_TYPE_JOIN_ACCEPT:
+            if( IsLoRaMacNetworkJoined == true )
+            {
+                break;
+            }
+            LoRaMacJoinDecrypt( payload + 1, size - 1, LoRaMacAppKey, LoRaMacRxPayload + 1 );
+
+            LoRaMacRxPayload[0] = macHdr.Value;
+
+            LoRaMacJoinComputeMic( LoRaMacRxPayload, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic );
+            
+            micRx |= ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN];
+            micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 1] << 8 );
+            micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 );
+            micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 );
+            
+            if( micRx == mic )
+            {
+                LoRaMacEventFlags.Bits.Rx = 1;
+                LoRaMacEventInfo.RxSnr = snr;
+                LoRaMacEventInfo.RxRssi = rssi;
+
+                LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey );
+
+                LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4];
+                LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 );
+                LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 );
+                
+                LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7];
+                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 );
+                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 );
+                LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 );
+                
+                // DLSettings
+                Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07;
+                Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F;
+                
+                // RxDelay
+                ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F );
+                if( ReceiveDelay1 == 0 )
+                {
+                    ReceiveDelay1 = 1;
+                }
+                ReceiveDelay1 *= 1e6;
+                ReceiveDelay2 = ReceiveDelay1 + 1e6;
+
+#if !( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
+                //CFList
+                if( ( size - 1 ) > 16 )
+                {
+                    ChannelParams_t param;
+                    param.DrRange.Value = ( DR_5 << 4 ) | DR_0;
+
+                    for( uint8_t i = 3, j = 0; i < ( 5 + 3 ); i++, j += 3 )
+                    {
+                        param.Frequency = ( ( uint32_t )LoRaMacRxPayload[13 + j] | ( ( uint32_t )LoRaMacRxPayload[14 + j] << 8 ) | ( ( uint32_t )LoRaMacRxPayload[15 + j] << 16 ) ) * 100;
+                        LoRaMacSetChannel( i, param );
+                    }
+                }
+#endif
+                LoRaMacEventFlags.Bits.JoinAccept = 1;
+                IsLoRaMacNetworkJoined = true;
+                ChannelsDatarate = ChannelsDefaultDatarate;
+                LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+            }
+            else
+            {
+                LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
+            }
+
+            LoRaMacEventFlags.Bits.Tx = 1;
+            break;
+        case FRAME_TYPE_DATA_CONFIRMED_DOWN:
+        case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
+            {
+                address = payload[pktHeaderLen++];
+                address |= ( (uint32_t)payload[pktHeaderLen++] << 8 );
+                address |= ( (uint32_t)payload[pktHeaderLen++] << 16 );
+                address |= ( (uint32_t)payload[pktHeaderLen++] << 24 );
+
+                if( address != LoRaMacDevAddr )
+                {
+                    curMulticastParams = MulticastChannels;
+                    while( curMulticastParams != NULL )
+                    {
+                        if( address == curMulticastParams->Address )
+                        {
+                            LoRaMacEventFlags.Bits.Multicast = 1;
+                            nwkSKey = curMulticastParams->NwkSKey;
+                            appSKey = curMulticastParams->AppSKey;
+                            downLinkCounter = curMulticastParams->DownLinkCounter;
+                            break;
+                        }
+                        curMulticastParams = curMulticastParams->Next;
+                    }
+                    if( LoRaMacEventFlags.Bits.Multicast == 0 )
+                    {
+                        // We are not the destination of this frame.
+                        LoRaMacEventFlags.Bits.Tx = 1;
+                        LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL;
+                        LoRaMacState &= ~MAC_TX_RUNNING;
+                        return;
+                    }
+                }
+                else
+                {
+                    LoRaMacEventFlags.Bits.Multicast = 0;
+                    nwkSKey = LoRaMacNwkSKey;
+                    appSKey = LoRaMacAppSKey;
+                    downLinkCounter = DownLinkCounter;
+                }
+                
+                if( LoRaMacDeviceClass != CLASS_A )
+                {
+                    LoRaMacState |= MAC_RX;
+                    // Starts the MAC layer status check timer
+                    TimerStart( &MacStateCheckTimer );
+                }
+                fCtrl.Value = payload[pktHeaderLen++];
+                
+                sequenceCounter |= ( uint32_t )payload[pktHeaderLen++];
+                sequenceCounter |= ( uint32_t )payload[pktHeaderLen++] << 8;
+
+                appPayloadStartIndex = 8 + fCtrl.Bits.FOptsLen;
+
+                micRx |= ( uint32_t )payload[size - LORAMAC_MFR_LEN];
+                micRx |= ( (uint32_t)payload[size - LORAMAC_MFR_LEN + 1] << 8 );
+                micRx |= ( (uint32_t)payload[size - LORAMAC_MFR_LEN + 2] << 16 );
+                micRx |= ( (uint32_t)payload[size - LORAMAC_MFR_LEN + 3] << 24 );
+
+                sequence = ( int32_t )sequenceCounter - ( int32_t )( downLinkCounter & 0xFFFF );
+                if( sequence < 0 )
+                {
+                    // sequence reset or roll over happened
+                    downLinkCounter = ( downLinkCounter & 0xFFFF0000 ) | ( sequenceCounter + ( uint32_t )0x10000 );
+                    LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic );
+                    if( micRx == mic )
+                    {
+                        isMicOk = true;
+                    }
+                    else
+                    {
+                        isMicOk = false;
+                        // sequence reset
+                        if( LoRaMacEventFlags.Bits.Multicast == 1 )
+                        {
+                            curMulticastParams->DownLinkCounter = downLinkCounter = sequenceCounter;
+                        }
+                        else
+                        {
+                            DownLinkCounter = downLinkCounter = sequenceCounter;
+                        }
+                        LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic );
+                    }
+                }
+                else
+                {
+                    downLinkCounter = ( downLinkCounter & 0xFFFF0000 ) | sequenceCounter;
+                    LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic );
+                }
+
+                if( ( isMicOk == true ) ||
+                    ( micRx == mic ) )
+                {
+                    LoRaMacEventFlags.Bits.Rx = 1;
+                    LoRaMacEventInfo.RxSnr = snr;
+                    LoRaMacEventInfo.RxRssi = rssi;
+                    LoRaMacEventInfo.RxBufferSize = 0;
+                    AdrAckCounter = 0;
+                    if( LoRaMacEventFlags.Bits.Multicast == 1 )
+                    {
+                        curMulticastParams->DownLinkCounter = downLinkCounter;
+                    }
+                    else
+                    {
+                        DownLinkCounter = downLinkCounter;
+                    }
+
+                    if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN )
+                    {
+                        SrvAckRequested = true;
+                    }
+                    else
+                    {
+                        SrvAckRequested = false;
+                    }
+                    // Check if the frame is an acknowledgement
+                    if( fCtrl.Bits.Ack == 1 )
+                    {
+                        LoRaMacEventInfo.TxAckReceived = true;
+
+                        // Stop the AckTimeout timer as no more retransmissions 
+                        // are needed.
+                        TimerStop( &AckTimeoutTimer );
+                    }
+                    else
+                    {
+                        LoRaMacEventInfo.TxAckReceived = false;
+                        if( AckTimeoutRetriesCounter > AckTimeoutRetries )
+                        {
+                            // Stop the AckTimeout timer as no more retransmissions 
+                            // are needed.
+                            TimerStop( &AckTimeoutTimer );
+                        }
+                    }
+                    
+                    if( fCtrl.Bits.FOptsLen > 0 )
+                    {
+                        // Decode Options field MAC commands
+                        LoRaMacProcessMacCommands( payload, 8, appPayloadStartIndex );
+                    }
+                    
+                    if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 )
+                    {
+                        port = payload[appPayloadStartIndex++];
+                        frameLen = ( size - 4 ) - appPayloadStartIndex;
+                        
+                        if( port == 0 )
+                        {
+                            LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
+                                                   frameLen,
+                                                   nwkSKey,
+                                                   address,
+                                                   DOWN_LINK,
+                                                   downLinkCounter,
+                                                   LoRaMacRxPayload );
+                            
+                            // Decode frame payload MAC commands
+                            LoRaMacProcessMacCommands( LoRaMacRxPayload, 0, frameLen );
+                        }
+                        else
+                        {
+                            LoRaMacPayloadDecrypt( payload + appPayloadStartIndex,
+                                                   frameLen,
+                                                   appSKey,
+                                                   address,
+                                                   DOWN_LINK,
+                                                   downLinkCounter,
+                                                   LoRaMacRxPayload );
+
+                            LoRaMacEventFlags.Bits.RxData = 1;
+                            LoRaMacEventInfo.RxPort = port;
+                            LoRaMacEventInfo.RxBuffer = LoRaMacRxPayload;
+                            LoRaMacEventInfo.RxBufferSize = frameLen;
+                        }
+                    }
+
+                    LoRaMacEventFlags.Bits.Tx = 1;
+                    LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+                }
+                else
+                {
+                    LoRaMacEventInfo.TxAckReceived = false;
+                    
+                    LoRaMacEventFlags.Bits.Tx = 1;
+                    LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
+                    LoRaMacState &= ~MAC_TX_RUNNING;
+                }
+            }
+            break;
+        case FRAME_TYPE_PROPRIETARY:
+            //Intentional falltrough
+        default:
+            LoRaMacEventFlags.Bits.Tx = 1;
+            LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
+            LoRaMacState &= ~MAC_TX_RUNNING;
+            break;
+    }
+}
+
+/*!
+ * Function executed on Radio Tx Timeout event
+ */
+static void OnRadioTxTimeout( void )
+{
+    if( LoRaMacDeviceClass != CLASS_C )
+    {
+        Radio.Sleep( );
+    }
+    else
+    {
+        OnRxWindow2TimerEvent( );
+    }
+    
+    LoRaMacEventFlags.Bits.Tx = 1;
+    LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
+}
+
+/*!
+ * Function executed on Radio Rx Timeout event
+ */
+static void OnRadioRxTimeout( void )
+{
+    if( LoRaMacDeviceClass != CLASS_C )
+    {
+        Radio.Sleep( );
+    }
+    if( LoRaMacEventFlags.Bits.RxSlot == 1 )
+    {
+        LoRaMacEventFlags.Bits.Tx = 1;
+        LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT;
+    }
+}
+
+/*!
+ * Function executed on Radio Rx Error event
+ */
+static void OnRadioRxError( void )
+{
+    if( LoRaMacDeviceClass != CLASS_C )
+    {
+        Radio.Sleep( );
+    }
+    if( LoRaMacEventFlags.Bits.RxSlot == 1 )
+    {
+        LoRaMacEventFlags.Bits.Tx = 1;
+        LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR;
+    }
+}
+
+/*!
+ * Initializes and opens the reception window
+ *
+ * \param [IN] freq window channel frequency
+ * \param [IN] datarate window channel datarate
+ * \param [IN] bandwidth window channel bandwidth
+ * \param [IN] timeout window channel timeout
+ */
+void LoRaMacRxWindowSetup( uint32_t freq, int8_t datarate, uint32_t bandwidth, uint16_t timeout, bool rxContinuous )
+{
+    if( Radio.GetStatus( ) == IDLE )
+    {
+        Radio.SetChannel( freq );
+        if( datarate == DR_7 )
+        {
+            Radio.SetRxConfig( MODEM_FSK, 50e3, Datarates[datarate] * 1e3, 0, 83.333e3, 5, 0, false, 0, true, 0, 0, false, rxContinuous );
+        }
+        else
+        {
+            Radio.SetRxConfig( MODEM_LORA, bandwidth, Datarates[datarate], 1, 0, 8, timeout, false, 0, false, 0, 0, true, rxContinuous );
+        }
+        if( rxContinuous == false )
+        {
+            Radio.Rx( MaxRxWindow );
+        }
+        else
+        {
+            Radio.Rx( 0 ); // Continuous mode
+        }
+    }
+}
+
+/*!
+ * Function executed on first Rx window timer event
+ */
+static void OnRxWindow1TimerEvent( void )
+{
+    uint16_t symbTimeout = 5; // DR_2, DR_1, DR_0
+    int8_t datarate = 0;
+    uint32_t bandwidth = 0; // LoRa 125 kHz
+
+    TimerStop( &RxWindowTimer1 );
+    LoRaMacEventFlags.Bits.RxSlot = 0;
+
+#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+    datarate = ChannelsDatarate - Rx1DrOffset;
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+
+    // For higher datarates, we increase the number of symbols generating a Rx Timeout
+    if( datarate >= DR_3 )
+    { // DR_6, DR_5, DR_4, DR_3
+        symbTimeout = 8;
+    }
+    if( datarate == DR_6 )
+    {// LoRa 250 kHz
+        bandwidth  = 1;
+    }
+    LoRaMacRxWindowSetup( Channels[Channel].Frequency, datarate, bandwidth, symbTimeout, false );
+#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
+    datarate = datarateOffsets[ChannelsDatarate][Rx1DrOffset];
+    if( datarate < 0 )
+    {
+        datarate = DR_0;
+    }
+    // For higher datarates, we increase the number of symbols generating a Rx Timeout
+    if( datarate > DR_0 )
+    { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13
+        symbTimeout = 8;
+    }
+    if( datarate >= DR_4 )
+    {// LoRa 500 kHz
+        bandwidth  = 2;
+    }
+    //LoRaMacRxWindowSetup( Channels[Channel].Frequency, datarate, bandwidth, symbTimeout, false );
+    LoRaMacRxWindowSetup( 923.3e6 + ( Channel % 8 ) * 600e3, datarate, bandwidth, symbTimeout, false );
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+}
+
+/*!
+ * Function executed on second Rx window timer event
+ */
+static void OnRxWindow2TimerEvent( void )
+{
+    uint16_t symbTimeout = 5; // DR_2, DR_1, DR_0
+    uint32_t bandwidth = 0; // LoRa 125 kHz
+
+    TimerStop( &RxWindowTimer2 );
+    LoRaMacEventFlags.Bits.RxSlot = 1;
+
+    if( NodeAckRequested == true )
+    {
+        TimerSetValue( &AckTimeoutTimer, ACK_TIMEOUT + randr( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND ) );
+        TimerStart( &AckTimeoutTimer );
+    }
+
+#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+    // For higher datarates, we increase the number of symbols generating a Rx Timeout
+    if( Rx2Channel.Datarate >= DR_3 )
+    { // DR_6, DR_5, DR_4, DR_3
+        symbTimeout = 8;
+    }
+    if( Rx2Channel.Datarate == DR_6 )
+    {// LoRa 250 kHz
+        bandwidth  = 1;
+    }
+#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
+    // For higher datarates, we increase the number of symbols generating a Rx Timeout
+    if( Rx2Channel.Datarate > DR_0 )
+    { // DR_1, DR_2, DR_3, DR_4, DR_8, DR_9, DR_10, DR_11, DR_12, DR_13
+        symbTimeout = 8;
+    }
+    if( Rx2Channel.Datarate >= DR_4 )
+    {// LoRa 500 kHz
+        bandwidth  = 2;
+    }
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+    if( LoRaMacDeviceClass != CLASS_C )
+    {
+        LoRaMacRxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, false );
+    }
+    else
+    {
+        LoRaMacRxWindowSetup( Rx2Channel.Frequency, Rx2Channel.Datarate, bandwidth, symbTimeout, true );
+    }
+}
+
+/*!
+ * Function executed on MacStateCheck timer event
+ */
+static void OnMacStateCheckTimerEvent( void )
+{
+    TimerStop( &MacStateCheckTimer );
+
+    if( LoRaMacEventFlags.Bits.Tx == 1 )
+    {
+        if( NodeAckRequested == false )
+        {
+            if( LoRaMacEventFlags.Bits.JoinAccept == true )
+            {
+                // Join messages aren't repeated automatically
+                ChannelsNbRepCounter = ChannelsNbRep;
+                UpLinkCounter = 0;
+            }
+            if( ChannelsNbRepCounter >= ChannelsNbRep )
+            {
+                ChannelsNbRepCounter = 0;
+
+                LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+
+                AdrAckCounter++;
+                if( IsUpLinkCounterFixed == false )
+                {
+                    UpLinkCounter++;
+                }
+
+                LoRaMacState &= ~MAC_TX_RUNNING;
+            }
+            else
+            {
+                LoRaMacEventFlags.Bits.Tx = 0;
+                // Sends the same frame again
+                if( LoRaMacSetNextChannel( ) == 0 )
+                {
+                    LoRaMacSendFrameOnChannel( Channels[Channel] );
+                }
+            }
+        }
+
+        if( LoRaMacEventFlags.Bits.Rx == 1 )
+        {
+            if( ( LoRaMacEventInfo.TxAckReceived == true ) || ( AckTimeoutRetriesCounter > AckTimeoutRetries ) )
+            {
+                AckTimeoutRetry = false;
+                if( IsUpLinkCounterFixed == false )
+                {
+                    UpLinkCounter++;
+                }
+                LoRaMacEventInfo.TxNbRetries = AckTimeoutRetriesCounter;
+                
+                LoRaMacState &= ~MAC_TX_RUNNING;
+            }
+        }
+        
+        if( ( AckTimeoutRetry == true ) && ( ( LoRaMacState & MAC_CHANNEL_CHECK ) == 0 ) )
+        {
+            AckTimeoutRetry = false;
+            if( ( AckTimeoutRetriesCounter < AckTimeoutRetries ) && ( AckTimeoutRetriesCounter <= MAX_ACK_RETRIES ) )
+            {
+                AckTimeoutRetriesCounter++;
+                
+                if( ( AckTimeoutRetriesCounter % 2 ) == 1 )
+                {
+                    ChannelsDatarate = MAX( ChannelsDatarate - 1, LORAMAC_MIN_DATARATE );
+                }
+                LoRaMacEventFlags.Bits.Tx = 0;
+                // Sends the same frame again
+                if( LoRaMacSetNextChannel( ) == 0 )
+                {
+                    LoRaMacSendFrameOnChannel( Channels[Channel] );
+                }
+            }
+            else
+            {
+#if defined( USE_BAND_433 ) || defined( USE_BAND_780 ) || defined( USE_BAND_868 )
+                // Re-enable default channels LC1, LC2, LC3
+                ChannelsMask[0] = ChannelsMask[0] | ( LC( 1 ) + LC( 2 ) + LC( 3 ) );
+#elif defined( USE_BAND_915 )
+                // Re-enable default channels
+                ChannelsMask[0] = 0xFFFF;
+                ChannelsMask[1] = 0xFFFF;
+                ChannelsMask[2] = 0xFFFF;
+                ChannelsMask[3] = 0xFFFF;
+                ChannelsMask[4] = 0x00FF;
+                ChannelsMask[5] = 0x0000;
+#elif defined( USE_BAND_915_HYBRID )
+                // Re-enable default channels
+                ChannelsMask[0] = 0x00FF;
+                ChannelsMask[1] = 0x0000;
+                ChannelsMask[2] = 0x0000;
+                ChannelsMask[3] = 0x0000;
+                ChannelsMask[4] = 0x0001;
+                ChannelsMask[5] = 0x0000;
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+                LoRaMacState &= ~MAC_TX_RUNNING;
+                
+                LoRaMacEventInfo.TxAckReceived = false;
+                LoRaMacEventInfo.TxNbRetries = AckTimeoutRetriesCounter;
+                if( IsUpLinkCounterFixed == false )
+                {
+                    UpLinkCounter++;
+                }
+                LoRaMacEventInfo.Status = LORAMAC_EVENT_INFO_STATUS_OK;
+            }
+        }
+    }
+    // Handle reception for Class B and Class C
+    if( ( LoRaMacState & MAC_RX ) == MAC_RX )
+    {
+        LoRaMacState &= ~MAC_RX;
+    }
+    if( LoRaMacState == MAC_IDLE )
+    {
+        LoRaMacNotify( &LoRaMacEventFlags, &LoRaMacEventInfo );
+    }
+    else
+    {
+        // Operation not finished restart timer
+        TimerStart( &MacStateCheckTimer );
+    }
+}
+
+static void OnAckTimeoutTimerEvent( void )
+{
+    TimerStop( &AckTimeoutTimer );
+
+    AckTimeoutRetry = true;
+    LoRaMacState &= ~MAC_ACK_REQ;
+}
+
+/*!
+ * ============================================================================
+ * = LoRaMac utility functions                                                =
+ * ============================================================================
+ */
+static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate )
+{
+    bool payloadSizeOk = false;
+    uint8_t maxN = 0;
+
+    // Get the maximum payload length
+    if( RepeaterSupport == true )
+    {
+        maxN = MaxPayloadOfDatarateRepeater[datarate];
+    }
+    else
+    {
+        maxN = MaxPayloadOfDatarate[datarate];
+    }
+
+    // Validation of the application payload size
+    if( lenN <= maxN )
+    {
+        payloadSizeOk = true;
+    }
+
+    return payloadSizeOk;
+}
+
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+static uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask )
+{
+    uint8_t nb125kHzChannels = 0;
+
+    for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ )
+    {
+        for( uint8_t j = 0; j < 16; j++ )
+        {// Verify if the channel is active
+            if( ( channelsMask[k] & ( 1 << j ) ) == ( 1 << j ) )
+            {
+                nb125kHzChannels++;
+            }
+        }
+    }
+
+    return nb125kHzChannels;
+}
+#endif
+
+static int8_t LimitTxPower( int8_t txPower )
+{
+    int8_t resultTxPower = txPower;
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+    if( ( ChannelsDatarate == DR_4 ) ||
+        ( ( ChannelsDatarate >= DR_8 ) && ( ChannelsDatarate <= DR_13 ) ) )
+    {// Limit tx power to max 26dBm
+        resultTxPower =  MAX( txPower, TX_POWER_26_DBM );
+    }
+    else
+    {
+        if( CountNbEnabled125kHzChannels( ChannelsMask ) < 50 )
+        {// Limit tx power to max 21dBm
+            resultTxPower = MAX( txPower, TX_POWER_20_DBM );
+        }
+    }
+#endif
+    return resultTxPower;
+}
+
+void LoRaMacChannelRemove( uint8_t id )
+{
+    if( ( LoRaMacState & MAC_TX_RUNNING ) == MAC_TX_RUNNING )
+    {
+        return;
+    }
+#if ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
+    if( id < 64 )
+    {
+        if( CountNbEnabled125kHzChannels( ChannelsMask ) <= 6 )
+        {
+            return;
+        }
+    }
+#else
+    if( id < 3 )
+    {
+        return;
+    }
+#endif
+
+    uint8_t index = 0;
+    index = id / 16;
+
+    if( ( index > 4 ) || ( id >= LORA_MAX_NB_CHANNELS ) )
+    {
+        return;
+    }
+
+    // Deactivate channel
+    ChannelsMask[index] &= ~( 1 << ( id % 16 ) );
+
+    return;
+}
+
+/*!
+ * ============================================================================
+ * = LoRaMac setup functions                                                  =
+ * ============================================================================
+ */
+void LoRaMacSetDeviceClass( DeviceClass_t deviceClass )
+{
+    LoRaMacDeviceClass = deviceClass;
+}
+
+void LoRaMacSetPublicNetwork( bool enable )
+{
+    PublicNetwork = enable;
+    Radio.SetModem( MODEM_LORA );
+    if( PublicNetwork == true )
+    {
+        // Change LoRa modem SyncWord
+        Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD );
+    }
+    else
+    {
+        // Change LoRa modem SyncWord
+        Radio.Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD );
+    }
+}
+
+void LoRaMacSetChannel( uint8_t id, ChannelParams_t params )
+{
+    params.Band = 0;
+    Channels[id] = params;
+    // Activate the newly created channel
+    if( id < 16 )
+    {
+        ChannelsMask[0] |= 1 << id;
+    }
+    else if( id < 32 )
+    {
+        ChannelsMask[1] |= 1 << ( id - 16 );
+    }
+    else if( id < 48 )
+    {
+        ChannelsMask[2] |= 1 << ( id - 32 );
+    }
+    else if( id < 64 )
+    {
+        ChannelsMask[3] |= 1 << ( id - 48 );
+    }
+    else if( id < 72 )
+    {
+        ChannelsMask[4] |= 1 << ( id - 64 );
+    }
+    else
+    {
+        // Don't activate the channel
+    }
+#if defined( USE_BAND_433 ) || defined( USE_BAND_780 )
+    Channels[id].Band = 0; // 1% duty cycle on EU433 and CN780 bands
+#elif defined( USE_BAND_868 )
+    if( ( Channels[id].Frequency >= 865000000 ) && ( Channels[id].Frequency <= 868000000 ) )
+    {
+        if( Channels[id].Band != BAND_G1_0 )
+        {
+            Channels[id].Band = BAND_G1_0;
+        }
+    }
+    else if( ( Channels[id].Frequency > 868000000 ) && ( Channels[id].Frequency <= 868600000 ) )
+    {
+        if( Channels[id].Band != BAND_G1_1 )
+        {
+            Channels[id].Band = BAND_G1_1;
+        }
+    }
+    else if( ( Channels[id].Frequency >= 868700000 ) && ( Channels[id].Frequency <= 869200000 ) )
+    {
+        if( Channels[id].Band != BAND_G1_2 )
+        {
+            Channels[id].Band = BAND_G1_2;
+        }
+    }
+    else if( ( Channels[id].Frequency >= 869400000 ) && ( Channels[id].Frequency <= 869650000 ) )
+    {
+        if( Channels[id].Band != BAND_G1_3 )
+        {
+            Channels[id].Band = BAND_G1_3;
+        }
+    }
+    else if( ( Channels[id].Frequency >= 869700000 ) && ( Channels[id].Frequency <= 870000000 ) )
+    {
+        if( Channels[id].Band != BAND_G1_4 )
+        {
+            Channels[id].Band = BAND_G1_4;
+        }
+    }
+    else
+    {
+        Channels[id].Frequency = 0;
+        Channels[id].DrRange.Value = 0;
+    }
+#elif ( defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID ) )
+    Channels[id].Band = 0; // No duty cycle on US915 band
+#else
+    #error "Please define a frequency band in the compiler options."
+#endif
+    // Check if it is a valid channel
+    if( Channels[id].Frequency == 0 )
+    {
+        LoRaMacChannelRemove( id );
+    }
+}
+
+void LoRaMacSetRx2Channel( Rx2ChannelParams_t param )
+{
+    Rx2Channel = param;
+}
+
+void LoRaMacSetChannelsTxPower( int8_t txPower )
+{
+    if( ( txPower >= LORAMAC_MAX_TX_POWER ) &&
+        ( txPower <= LORAMAC_MIN_TX_POWER ) )
+    {
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+        int8_t txPwr = LimitTxPower( txPower );
+        if( txPwr == txPower )
+        {
+            ChannelsTxPower = txPower;
+        }
+#else
+        ChannelsTxPower = txPower;
+#endif
+    }
+}
+
+void LoRaMacSetChannelsDatarate( int8_t datarate )
+{
+    ChannelsDefaultDatarate = ChannelsDatarate = datarate;
+}
+
+void LoRaMacSetChannelsMask( uint16_t *mask )
+{
+#if defined( USE_BAND_915 ) || defined( USE_BAND_915_HYBRID )
+    if( ( CountNbEnabled125kHzChannels( mask ) < 6 ) &&
+        ( CountNbEnabled125kHzChannels( mask ) > 0 ) )
+    {
+
+    }
+    else
+    {
+        LoRaMacMemCpy( (uint8_t* ) mask,
+                       ( uint8_t* ) ChannelsMask, 10 );
+    }
+#else
+    if( ( mask[0] & 0x0007 ) != 0x0007 )
+    {
+    }
+    else
+    {
+        LoRaMacMemCpy( ( uint8_t* ) mask,
+                       ( uint8_t* ) ChannelsMask, 2 );
+    }
+#endif
+}
+
+void LoRaMacSetChannelsNbRep( uint8_t nbRep )
+{
+    if( nbRep < 1 )
+    {
+        nbRep = 1;
+    }
+    if( nbRep > 15 )
+    {
+        nbRep = 15;
+    }
+    ChannelsNbRep = nbRep;
+}
+
+void LoRaMacSetMaxRxWindow( uint32_t delay )
+{
+    MaxRxWindow = delay;
+}
+
+void LoRaMacSetReceiveDelay1( uint32_t delay )
+{
+    ReceiveDelay1 = delay;
+}
+
+void LoRaMacSetReceiveDelay2( uint32_t delay )
+{
+    ReceiveDelay2 = delay;
+}
+
+void LoRaMacSetJoinAcceptDelay1( uint32_t delay )
+{
+    JoinAcceptDelay1 = delay;
+}
+
+void LoRaMacSetJoinAcceptDelay2( uint32_t delay )
+{
+    JoinAcceptDelay2 = delay;
+}
+
+uint32_t LoRaMacGetUpLinkCounter( void )
+{
+    return UpLinkCounter;
+}
+
+uint32_t LoRaMacGetDownLinkCounter( void )
+{
+    return DownLinkCounter;
+}
+
+/*!
+ * ============================================================================
+ * = LoRaMac test functions                                                   =
+ * ============================================================================
+ */
+void LoRaMacTestSetDutyCycleOn( bool enable )
+{
+    DutyCycleOn = enable;
+}
+
+void LoRaMacTestRxWindowsOn( bool enable )
+{
+    IsRxWindowsEnabled = enable;
+}
+
+void LoRaMacTestSetMic( uint16_t upLinkCounter )
+{
+    UpLinkCounter = upLinkCounter;
+    IsUpLinkCounterFixed = true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMac.h	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,581 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: LoRa MAC layer implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#ifndef __LORAMAC_H__
+#define __LORAMAC_H__
+
+// Includes board dependent definitions such as channels frequencies
+#include "LoRaMac-board.h"
+
+/*!
+ * Beacon interval in us
+ */
+#define BEACON_INTERVAL                             128000000
+
+/*!
+ * Class A&B receive delay in us
+ */
+#define RECEIVE_DELAY1                              1000000
+#define RECEIVE_DELAY2                              2000000
+
+/*!
+ * Join accept receive delay in us
+ */
+#define JOIN_ACCEPT_DELAY1                          5000000
+#define JOIN_ACCEPT_DELAY2                          6000000
+
+/*!
+ * Class A&B maximum receive window delay in us
+ */
+#define MAX_RX_WINDOW                               3000000
+
+/*!
+ * Maximum allowed gap for the FCNT field
+ */
+#define MAX_FCNT_GAP                                16384
+
+/*!
+ * ADR acknowledgement counter limit
+ */
+#define ADR_ACK_LIMIT                               64
+
+/*!
+ * Number of ADR acknowledgement requests before returning to default datarate
+ */
+#define ADR_ACK_DELAY                               32
+
+/*!
+ * Number of seconds after the start of the second reception window without
+ * receiving an acknowledge.
+ * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND )
+ */
+#define ACK_TIMEOUT                                 2000000
+
+/*!
+ * Random number of seconds after the start of the second reception window without
+ * receiving an acknowledge
+ * AckTimeout = ACK_TIMEOUT + Random( -ACK_TIMEOUT_RND, ACK_TIMEOUT_RND )
+ */
+#define ACK_TIMEOUT_RND                             1000000
+
+/*!
+ * Check the Mac layer state every MAC_STATE_CHECK_TIMEOUT
+ */
+#define MAC_STATE_CHECK_TIMEOUT                     1000000
+
+/*!
+ * Maximum number of times the MAC layer tries to get an acknowledge.
+ */
+#define MAX_ACK_RETRIES                             8
+
+/*!
+ * RSSI free threshold
+ */
+#define RSSI_FREE_TH                                ( int8_t )( -90 ) // [dBm]
+
+/*! 
+ * Frame direction definition
+ */
+#define UP_LINK                                     0
+#define DOWN_LINK                                   1
+
+/*!
+ * Sets the length of the LoRaMAC footer field.
+ * Mainly indicates the MIC field length
+ */
+#define LORAMAC_MFR_LEN                             4
+
+/*!
+ * Syncword for Private LoRa networks
+ */
+#define LORA_MAC_PRIVATE_SYNCWORD                   0x12
+
+/*!
+ * Syncword for Public LoRa networks
+ */
+#define LORA_MAC_PUBLIC_SYNCWORD                    0x34
+
+/*!
+ * LoRaWAN devices classes definition
+ */
+typedef enum
+{
+    CLASS_A,
+    CLASS_B,
+    CLASS_C,
+}DeviceClass_t;
+
+/*!
+ * LoRaMAC channels parameters definition
+ */
+typedef union
+{
+    int8_t Value;
+    struct
+    {
+        int8_t Min : 4;
+        int8_t Max : 4;
+    }Fields;
+}DrRange_t;
+
+typedef struct
+{
+    uint16_t DCycle;
+    int8_t TxMaxPower;
+    uint64_t LastTxDoneTime;
+    uint64_t TimeOff;
+}Band_t;
+
+typedef struct
+{
+    uint32_t Frequency; // Hz
+    DrRange_t DrRange;  // Max datarate [0: SF12, 1: SF11, 2: SF10, 3: SF9, 4: SF8, 5: SF7, 6: SF7, 7: FSK]
+                        // Min datarate [0: SF12, 1: SF11, 2: SF10, 3: SF9, 4: SF8, 5: SF7, 6: SF7, 7: FSK]
+    uint8_t Band;       // Band index
+}ChannelParams_t;
+
+typedef struct
+{
+    uint32_t Frequency; // Hz
+    uint8_t  Datarate;  // [0: SF12, 1: SF11, 2: SF10, 3: SF9, 4: SF8, 5: SF7, 6: SF7, 7: FSK]
+}Rx2ChannelParams_t;
+
+typedef struct MulticastParams_s
+{
+    uint32_t Address;
+    uint8_t NwkSKey[16];
+    uint8_t AppSKey[16];
+    uint32_t DownLinkCounter;
+    struct MulticastParams_s *Next;
+}MulticastParams_t;
+
+/*!
+ * LoRaMAC frame types
+ */
+typedef enum
+{
+    FRAME_TYPE_JOIN_REQ              = 0x00,
+    FRAME_TYPE_JOIN_ACCEPT           = 0x01,
+    FRAME_TYPE_DATA_UNCONFIRMED_UP   = 0x02,
+    FRAME_TYPE_DATA_UNCONFIRMED_DOWN = 0x03,
+    FRAME_TYPE_DATA_CONFIRMED_UP     = 0x04,
+    FRAME_TYPE_DATA_CONFIRMED_DOWN   = 0x05,
+    FRAME_TYPE_RFU                   = 0x06,
+    FRAME_TYPE_PROPRIETARY           = 0x07,
+}LoRaMacFrameType_t;
+
+/*!
+ * LoRaMAC mote MAC commands
+ */
+typedef enum
+{
+    MOTE_MAC_LINK_CHECK_REQ          = 0x02,
+    MOTE_MAC_LINK_ADR_ANS            = 0x03,
+    MOTE_MAC_DUTY_CYCLE_ANS          = 0x04,
+    MOTE_MAC_RX_PARAM_SETUP_ANS      = 0x05,
+    MOTE_MAC_DEV_STATUS_ANS          = 0x06,
+    MOTE_MAC_NEW_CHANNEL_ANS         = 0x07,
+    MOTE_MAC_RX_TIMING_SETUP_ANS     = 0x08,
+}LoRaMacMoteCmd_t;
+
+/*!
+ * LoRaMAC server MAC commands
+ */
+typedef enum
+{
+    SRV_MAC_LINK_CHECK_ANS           = 0x02,
+    SRV_MAC_LINK_ADR_REQ             = 0x03,
+    SRV_MAC_DUTY_CYCLE_REQ           = 0x04,
+    SRV_MAC_RX_PARAM_SETUP_REQ       = 0x05,
+    SRV_MAC_DEV_STATUS_REQ           = 0x06,
+    SRV_MAC_NEW_CHANNEL_REQ          = 0x07,
+    SRV_MAC_RX_TIMING_SETUP_REQ      = 0x08,
+}LoRaMacSrvCmd_t;
+
+/*!
+ * LoRaMAC Battery level indicator
+ */
+typedef enum
+{
+    BAT_LEVEL_EXT_SRC                = 0x00,
+    BAT_LEVEL_EMPTY                  = 0x01,
+    BAT_LEVEL_FULL                   = 0xFE,
+    BAT_LEVEL_NO_MEASURE             = 0xFF,
+}LoRaMacBatteryLevel_t;
+
+/*!
+ * LoRaMAC header field definition
+ */
+typedef union
+{
+    uint8_t Value;
+    struct
+    {
+        uint8_t Major           : 2;
+        uint8_t RFU             : 3;
+        uint8_t MType           : 3;
+    }Bits;
+}LoRaMacHeader_t;
+
+/*!
+ * LoRaMAC frame header field definition
+ */
+typedef union
+{
+    uint8_t Value;
+    struct
+    {
+        uint8_t FOptsLen        : 4;
+        uint8_t FPending        : 1;
+        uint8_t Ack             : 1;
+        uint8_t AdrAckReq       : 1;
+        uint8_t Adr             : 1;
+    }Bits;
+}LoRaMacFrameCtrl_t;
+
+/*!
+ * LoRaMAC event flags
+ */
+typedef union
+{
+    uint8_t Value;
+    struct
+    {
+        uint8_t Tx              : 1;
+        uint8_t Rx              : 1;
+        uint8_t RxData          : 1;
+        uint8_t Multicast       : 1;
+        uint8_t RxSlot          : 2;
+        uint8_t LinkCheck       : 1;
+        uint8_t JoinAccept      : 1;
+    }Bits;
+}LoRaMacEventFlags_t;
+
+typedef enum
+{
+    LORAMAC_EVENT_INFO_STATUS_OK = 0,
+    LORAMAC_EVENT_INFO_STATUS_ERROR,
+    LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT,
+    LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT,
+    LORAMAC_EVENT_INFO_STATUS_RX2_ERROR,
+    LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL,
+    LORAMAC_EVENT_INFO_STATUS_DOWNLINK_FAIL,
+    LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL,
+    LORAMAC_EVENT_INFO_STATUS_MIC_FAIL,
+}LoRaMacEventInfoStatus_t;
+
+/*!
+ * LoRaMAC event information
+ */
+typedef struct
+{
+    LoRaMacEventInfoStatus_t Status;
+    bool TxAckReceived;
+    uint8_t TxNbRetries;
+    uint8_t TxDatarate;
+    uint8_t RxPort;
+    uint8_t *RxBuffer;
+    uint8_t RxBufferSize;
+    int16_t RxRssi;
+    uint8_t RxSnr;
+    uint16_t Energy;
+    uint8_t DemodMargin;
+    uint8_t NbGateways;
+}LoRaMacEventInfo_t;
+
+/*!
+ * LoRaMAC events structure
+ * Used to notify upper layers of MAC events
+ */
+typedef struct sLoRaMacEvent
+{
+    /*!
+     * MAC layer event callback prototype.
+     *
+     * \param [IN] flags Bit field indicating the MAC events occurred
+     * \param [IN] info  Details about MAC events occurred
+     */
+    void ( *MacEvent )( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info );
+}LoRaMacEvent_t;
+
+/*!
+ * LoRaMAC layer initialization
+ *
+ * \param [IN] events          Pointer to a structure defining the LoRaMAC
+ *                             callback functions.
+ * \param [IN] getBatteryLevel Function callback to get the current
+ *                             battery level
+ */
+void LoRaMacInit( LoRaMacEvent_t *events, uint8_t ( *getBatteryLevel )( ) );
+
+/*!
+ * Enables/Disables the ADR (Adaptive Data Rate)
+ * 
+ * \param [IN] enable [true: ADR ON, false: ADR OFF]
+ */
+void LoRaMacSetAdrOn( bool enable );
+
+/*!
+ * Initializes the network IDs. Device address, 
+ * network session AES128 key and application session AES128 key.
+ *
+ * \remark To be only used when Over-the-Air activation isn't used.
+ *
+ * \param [IN] netID   24 bits network identifier 
+ *                     ( provided by network operator )
+ * \param [IN] devAddr 32 bits device address on the network 
+ *                     (must be unique to the network)
+ * \param [IN] nwkSKey Pointer to the network session AES128 key array
+ *                     ( 16 bytes )
+ * \param [IN] appSKey Pointer to the application session AES128 key array
+ *                     ( 16 bytes )
+ */
+void LoRaMacInitNwkIds( uint32_t netID, uint32_t devAddr, uint8_t *nwkSKey, uint8_t *appSKey );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacMulticastChannelAdd( MulticastParams_t *channelParam );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacMulticastChannelRemove( MulticastParams_t *channelParam );
+
+/*!
+ * Initiates the Over-the-Air activation 
+ * 
+ * \param [IN] devEui Pointer to the device EUI array ( 8 bytes )
+ * \param [IN] appEui Pointer to the application EUI array ( 8 bytes )
+ * \param [IN] appKey Pointer to the application AES128 key array ( 16 bytes )
+ *
+ * \retval status [0: OK, 1: Tx error, 2: Already joined a network]
+ */
+uint8_t LoRaMacJoinReq( uint8_t *devEui, uint8_t *appEui, uint8_t *appKey );
+
+/*!
+ * Sends a LinkCheckReq MAC command on the next uplink frame
+ *
+ * \retval status Function status [0: OK, 1: Busy]
+ */
+uint8_t LoRaMacLinkCheckReq( void );
+
+/*!
+ * LoRaMAC layer send frame
+ *
+ * \param [IN] fPort       MAC payload port (must be > 0)
+ * \param [IN] fBuffer     MAC data buffer to be sent
+ * \param [IN] fBufferSize MAC data buffer size
+ *
+ * \retval status          [0: OK, 1: Busy, 2: No network joined,
+ *                          3: Length or port error, 4: Unknown MAC command
+ *                          5: Unable to find a free channel
+ *                          6: Device switched off]
+ */
+uint8_t LoRaMacSendFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize );
+
+/*!
+ * LoRaMAC layer send frame
+ *
+ * \param [IN] fPort       MAC payload port (must be > 0)
+ * \param [IN] fBuffer     MAC data buffer to be sent
+ * \param [IN] fBufferSize MAC data buffer size
+ * \param [IN] fBufferSize MAC data buffer size
+ * \param [IN] nbRetries   Number of retries to receive the acknowledgement
+ *
+ * \retval status          [0: OK, 1: Busy, 2: No network joined,
+ *                          3: Length or port error, 4: Unknown MAC command
+ *                          5: Unable to find a free channel
+ *                          6: Device switched off]
+ */
+uint8_t LoRaMacSendConfirmedFrame( uint8_t fPort, void *fBuffer, uint16_t fBufferSize, uint8_t nbRetries );
+
+/*!
+ * ============================================================================
+ * = LoRaMac test functions                                                   =
+ * ============================================================================
+ */
+
+/*!
+ * LoRaMAC layer generic send frame
+ *
+ * \param [IN] macHdr      MAC header field
+ * \param [IN] fOpts       MAC commands buffer
+ * \param [IN] fPort       MAC payload port
+ * \param [IN] fBuffer     MAC data buffer to be sent
+ * \param [IN] fBufferSize MAC data buffer size
+ * \retval status          [0: OK, 1: Busy, 2: No network joined,
+ *                          3: Length or port error, 4: Unknown MAC command
+ *                          5: Unable to find a free channel
+ *                          6: Device switched off]
+ */
+uint8_t LoRaMacSend( LoRaMacHeader_t *macHdr, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize );
+
+/*!
+ * LoRaMAC layer frame buffer initialization.
+ *
+ * \param [IN] channel     Channel parameters
+ * \param [IN] macHdr      MAC header field
+ * \param [IN] fCtrl       MAC frame control field
+ * \param [IN] fOpts       MAC commands buffer
+ * \param [IN] fPort       MAC payload port
+ * \param [IN] fBuffer     MAC data buffer to be sent
+ * \param [IN] fBufferSize MAC data buffer size
+ * \retval status          [0: OK, 1: N/A, 2: No network joined,
+ *                          3: Length or port error, 4: Unknown MAC command]
+ */
+uint8_t LoRaMacPrepareFrame( ChannelParams_t channel,LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize );
+
+/*!
+ * LoRaMAC layer prepared frame buffer transmission with channel specification
+ *
+ * \remark LoRaMacPrepareFrame must be called at least once before calling this
+ *         function.
+ *
+ * \param [IN] channel     Channel parameters
+ * \retval status          [0: OK, 1: Busy]
+ */
+uint8_t LoRaMacSendFrameOnChannel( ChannelParams_t channel );
+
+/*!
+ * LoRaMAC layer generic send frame with channel specification
+ *
+ * \param [IN] channel     Channel parameters
+ * \param [IN] macHdr      MAC header field
+ * \param [IN] fCtrl       MAC frame control field
+ * \param [IN] fOpts       MAC commands buffer
+ * \param [IN] fPort       MAC payload port
+ * \param [IN] fBuffer     MAC data buffer to be sent
+ * \param [IN] fBufferSize MAC data buffer size
+ * \retval status          [0: OK, 1: Busy, 2: No network joined,
+ *                          3: Length or port error, 4: Unknown MAC command]
+ */
+uint8_t LoRaMacSendOnChannel( ChannelParams_t channel, LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t *fOpts, uint8_t fPort, void *fBuffer, uint16_t fBufferSize );
+
+/*!
+ * ============================================================================
+ * = LoRaMac setup functions                                                  =
+ * ============================================================================
+ */
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetDeviceClass( DeviceClass_t deviceClass );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetPublicNetwork( bool enable );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetChannel( uint8_t id, ChannelParams_t params );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetRx2Channel( Rx2ChannelParams_t param );
+
+/*!
+ * Sets channels tx output power
+ *
+ * \param [IN] txPower [TX_POWER_20_DBM, TX_POWER_14_DBM,
+                        TX_POWER_11_DBM, TX_POWER_08_DBM,
+                        TX_POWER_05_DBM, TX_POWER_02_DBM]
+ */
+void LoRaMacSetChannelsTxPower( int8_t txPower );
+
+/*!
+ * Sets channels datarate
+ *
+ * \param [IN] datarate eu868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7]
+ *                      us915 - [DR_0, DR_1, DR_2, DR_3, DR_4]
+ */
+void LoRaMacSetChannelsDatarate( int8_t datarate );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetChannelsMask( uint16_t *mask );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetChannelsNbRep( uint8_t nbRep );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetMaxRxWindow( uint32_t delay );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetReceiveDelay1( uint32_t delay );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetReceiveDelay2( uint32_t delay );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetJoinAcceptDelay1( uint32_t delay );
+
+/*
+ * TODO: Add documentation
+ */
+void LoRaMacSetJoinAcceptDelay2( uint32_t delay );
+
+/*
+ * TODO: Add documentation
+ */
+uint32_t LoRaMacGetUpLinkCounter( void );
+
+/*
+ * TODO: Add documentation
+ */
+uint32_t LoRaMacGetDownLinkCounter( void );
+
+/*
+ * ============================================================================
+ * = LoRaMac test functions                                                   =
+ * ============================================================================
+ */
+
+/*!
+ * Disables/Enables the duty cycle enforcement (EU868)
+ *
+ * \param   [IN] enable - Enabled or disables the duty cycle
+ */
+void LoRaMacTestSetDutyCycleOn( bool enable );
+
+/*!
+ * Disables/Enables the reception windows opening
+ *
+ * \param [IN] enable [true: enable, false: disable]
+ */
+void LoRaMacTestRxWindowsOn( bool enable );
+
+/*!
+ * Enables the MIC field test
+ *
+ * \param [IN] upLinkCounter Fixed Tx packet counter value
+ */
+void LoRaMacTestSetMic( uint16_t upLinkCounter );
+
+#endif // __LORAMAC_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMacCrypto.cpp	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,197 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: LoRa MAC layer implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#include "mbed.h"
+#include "board.h"
+#include "utilities.h"
+
+#include "aes.h"
+#include "cmac.h"
+
+#include "LoRaMacCrypto.h"
+
+/*!
+ * CMAC/AES Message Integrity Code (MIC) Block B0 size
+ */
+#define LORAMAC_MIC_BLOCK_B0_SIZE                   16
+
+/*!
+ * MIC field computation initial data
+ */
+static uint8_t MicBlockB0[] = { 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+                              };
+
+/*!
+ * Contains the computed MIC field.
+ *
+ * \remark Only the 4 first bytes are used
+ */
+static uint8_t Mic[16];
+
+/*!
+ * Encryption aBlock and sBlock
+ */
+static uint8_t aBlock[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+                          };
+static uint8_t sBlock[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+                          };
+
+/*!
+ * AES computation context variable
+ */
+static aes_context AesContext;
+                   
+/*!
+ * CMAC computation context variable
+ */
+static AES_CMAC_CTX AesCmacCtx[1];
+
+/*!
+ * \brief Computes the LoRaMAC frame MIC field  
+ *
+ * \param [IN]  buffer          Data buffer
+ * \param [IN]  size            Data buffer size
+ * \param [IN]  key             AES key to be used
+ * \param [IN]  address         Frame address
+ * \param [IN]  dir             Frame direction [0: uplink, 1: downlink]
+ * \param [IN]  sequenceCounter Frame sequence counter
+ * \param [OUT] mic Computed MIC field
+ */
+void LoRaMacComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint32_t *mic )
+{
+    MicBlockB0[5] = dir;
+    
+    MicBlockB0[6] = ( address ) & 0xFF;
+    MicBlockB0[7] = ( address >> 8 ) & 0xFF;
+    MicBlockB0[8] = ( address >> 16 ) & 0xFF;
+    MicBlockB0[9] = ( address >> 24 ) & 0xFF;
+
+    MicBlockB0[10] = ( sequenceCounter ) & 0xFF;
+    MicBlockB0[11] = ( sequenceCounter >> 8 ) & 0xFF;
+    MicBlockB0[12] = ( sequenceCounter >> 16 ) & 0xFF;
+    MicBlockB0[13] = ( sequenceCounter >> 24 ) & 0xFF;
+
+    MicBlockB0[15] = size & 0xFF;
+
+    AES_CMAC_Init( AesCmacCtx );
+
+    AES_CMAC_SetKey( AesCmacCtx, key );
+
+    AES_CMAC_Update( AesCmacCtx, MicBlockB0, LORAMAC_MIC_BLOCK_B0_SIZE );
+    
+    AES_CMAC_Update( AesCmacCtx, buffer, size & 0xFF );
+    
+    AES_CMAC_Final( Mic, AesCmacCtx );
+    
+    *mic = ( uint32_t )( ( uint32_t )Mic[3] << 24 | ( uint32_t )Mic[2] << 16 | ( uint32_t )Mic[1] << 8 | ( uint32_t )Mic[0] );
+}
+
+void LoRaMacPayloadEncrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *encBuffer )
+{
+    uint16_t i;
+    uint8_t bufferIndex = 0;
+    uint16_t ctr = 1;
+
+    memset1( AesContext.ksch, '\0', 240 );
+    aes_set_key( key, 16, &AesContext );
+
+    aBlock[5] = dir;
+
+    aBlock[6] = ( address ) & 0xFF;
+    aBlock[7] = ( address >> 8 ) & 0xFF;
+    aBlock[8] = ( address >> 16 ) & 0xFF;
+    aBlock[9] = ( address >> 24 ) & 0xFF;
+
+    aBlock[10] = ( sequenceCounter ) & 0xFF;
+    aBlock[11] = ( sequenceCounter >> 8 ) & 0xFF;
+    aBlock[12] = ( sequenceCounter >> 16 ) & 0xFF;
+    aBlock[13] = ( sequenceCounter >> 24 ) & 0xFF;
+
+    while( size >= 16 )
+    {
+        aBlock[15] = ( ( ctr ) & 0xFF );
+        ctr++;
+        aes_encrypt( aBlock, sBlock, &AesContext );
+        for( i = 0; i < 16; i++ )
+        {
+            encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i];
+        }
+        size -= 16;
+        bufferIndex += 16;
+    }
+
+    if( size > 0 )
+    {
+        aBlock[15] = ( ( ctr ) & 0xFF );
+        aes_encrypt( aBlock, sBlock, &AesContext );
+        for( i = 0; i < size; i++ )
+        {
+            encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i];
+        }
+    }
+}
+
+void LoRaMacPayloadDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *decBuffer )
+{
+    LoRaMacPayloadEncrypt( buffer, size, key, address, dir, sequenceCounter, decBuffer );
+}
+
+void LoRaMacJoinComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t *mic )
+{
+    AES_CMAC_Init( AesCmacCtx );
+
+    AES_CMAC_SetKey( AesCmacCtx, key );
+
+    AES_CMAC_Update( AesCmacCtx, buffer, size & 0xFF );
+
+    AES_CMAC_Final( Mic, AesCmacCtx );
+
+    *mic = ( uint32_t )( ( uint32_t )Mic[3] << 24 | ( uint32_t )Mic[2] << 16 | ( uint32_t )Mic[1] << 8 | ( uint32_t )Mic[0] );
+}
+
+void LoRaMacJoinDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint8_t *decBuffer )
+{
+    memset1( AesContext.ksch, '\0', 240 );
+    aes_set_key( key, 16, &AesContext );
+    aes_encrypt( buffer, decBuffer, &AesContext );
+    // Check if optional CFList is included
+    if( size >= 16 )
+    {
+        aes_encrypt( buffer + 16, decBuffer + 16, &AesContext );
+    }
+}
+
+void LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey )
+{
+    uint8_t nonce[16];
+    uint8_t *pDevNonce = ( uint8_t * )&devNonce;
+    
+    memset1( AesContext.ksch, '\0', 240 );
+    aes_set_key( key, 16, &AesContext );
+
+    memset1( nonce, 0, sizeof( nonce ) );
+    nonce[0] = 0x01;
+    LoRaMacMemCpy( appNonce, nonce + 1, 6 );
+    LoRaMacMemCpy( pDevNonce, nonce + 7, 2 );
+    aes_encrypt( nonce, nwkSKey, &AesContext );
+
+    memset1( nonce, 0, sizeof( nonce ) );
+    nonce[0] = 0x02;
+    LoRaMacMemCpy( appNonce, nonce + 1, 6 );
+    LoRaMacMemCpy( pDevNonce, nonce + 7, 2 );
+    aes_encrypt( nonce, appSKey, &AesContext );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LoRaMacCrypto.h	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,99 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: LoRa MAC layer implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#ifndef __LORAMAC_CRYPTO_H__
+#define __LORAMAC_CRYPTO_H__
+
+/*!
+ * Copies size elements of src array to dst array
+ * 
+ * \remark STM32 Standard memcpy function only works on pointers that are aligned
+ *
+ * \param [IN]  src  Source array
+ * \param [OUT] dst  Destination array
+ * \param [IN]  size Number of bytes to be copied
+ */
+#define LoRaMacMemCpy( src, dst, size ) memcpy1( dst, src, size )
+
+/*!
+ * Computes the LoRaMAC frame MIC field  
+ *
+ * \param [IN]  buffer          Data buffer
+ * \param [IN]  size            Data buffer size
+ * \param [IN]  key             AES key to be used
+ * \param [IN]  address         Frame address
+ * \param [IN]  dir             Frame direction [0: uplink, 1: downlink]
+ * \param [IN]  sequenceCounter Frame sequence counter
+ * \param [OUT] mic             Computed MIC field
+ */
+void LoRaMacComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint32_t *mic );
+
+/*!
+ * Computes the LoRaMAC payload encryption 
+ *
+ * \param [IN]  buffer          Data buffer
+ * \param [IN]  size            Data buffer size
+ * \param [IN]  key             AES key to be used
+ * \param [IN]  address         Frame address
+ * \param [IN]  dir             Frame direction [0: uplink, 1: downlink]
+ * \param [IN]  sequenceCounter Frame sequence counter
+ * \param [OUT] encBuffer       Encrypted buffer
+ */
+void LoRaMacPayloadEncrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *encBuffer );
+
+/*!
+ * Computes the LoRaMAC payload decryption 
+ *
+ * \param [IN]  buffer          Data buffer
+ * \param [IN]  size            Data buffer size
+ * \param [IN]  key             AES key to be used
+ * \param [IN]  address         Frame address
+ * \param [IN]  dir             Frame direction [0: uplink, 1: downlink]
+ * \param [IN]  sequenceCounter Frame sequence counter
+ * \param [OUT] decBuffer       Decrypted buffer
+ */
+void LoRaMacPayloadDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *decBuffer );
+
+/*!
+ * Computes the LoRaMAC Join Request frame MIC field  
+ *
+ * \param [IN]  buffer          Data buffer
+ * \param [IN]  size            Data buffer size
+ * \param [IN]  key             AES key to be used
+ * \param [OUT] mic             Computed MIC field
+ */
+void LoRaMacJoinComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t *mic );
+
+/*!
+ * Computes the LoRaMAC join frame decryption 
+ *
+ * \param [IN]  buffer          Data buffer
+ * \param [IN]  size            Data buffer size
+ * \param [IN]  key             AES key to be used
+ * \param [OUT] decBuffer       Decrypted buffer
+ */
+void LoRaMacJoinDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint8_t *decBuffer );
+
+/*!
+ * Computes the LoRaMAC join frame decryption 
+ *
+ * \param [IN]  key             AES key to be used
+ * \param [IN]  appNonce        Application nonce
+ * \param [IN]  devNonce        Device nonce
+ * \param [OUT] nwkSKey         Network session key
+ * \param [OUT] appSKey         Application session key
+ */
+void LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey );
+
+#endif // __LORAMAC_CRYPTO_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/aes.cpp	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,935 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The redistribution and use of this software (with or without changes)
+ is allowed without the payment of fees or royalties provided that:
+
+  1. source code distributions include the above copyright notice, this
+     list of conditions and the following disclaimer;
+
+  2. binary distributions include the above copyright notice, this list
+     of conditions and the following disclaimer in their documentation;
+
+  3. the name of the copyright holder is not used to endorse products
+     built using this software without specific written permission.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 09/09/2006
+
+ This is an AES implementation that uses only 8-bit byte operations on the
+ cipher state (there are options to use 32-bit types if available).
+
+ The combination of mix columns and byte substitution used here is based on
+ that developed by Karl Malbrain. His contribution is acknowledged.
+ */
+
+/* define if you have a fast memcpy function on your system */
+#if 0
+#  define HAVE_MEMCPY
+#  include <string.h>
+#  if defined( _MSC_VER )
+#    include <intrin.h>
+#    pragma intrinsic( memcpy )
+#  endif
+#endif
+
+
+#include "mbed.h"
+
+/* define if you have fast 32-bit types on your system */
+#if 1
+#  define HAVE_UINT_32T
+#endif
+
+/* define if you don't want any tables */
+#if 1
+#  define USE_TABLES
+#endif
+
+/*  On Intel Core 2 duo VERSION_1 is faster */
+
+/* alternative versions (test for performance on your system) */
+#if 1
+#  define VERSION_1
+#endif
+
+#include "aes.h"
+
+#if defined( HAVE_UINT_32T )
+  typedef unsigned long uint_32t;
+#endif
+
+/* functions for finite field multiplication in the AES Galois field    */
+
+#define WPOLY   0x011b
+#define BPOLY     0x1b
+#define DPOLY   0x008d
+
+#define f1(x)   (x)
+#define f2(x)   ((x << 1) ^ (((x >> 7) & 1) * WPOLY))
+#define f4(x)   ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY))
+#define f8(x)   ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \
+                          ^ (((x >> 5) & 4) * WPOLY))
+#define d2(x)   (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0))
+
+#define f3(x)   (f2(x) ^ x)
+#define f9(x)   (f8(x) ^ x)
+#define fb(x)   (f8(x) ^ f2(x) ^ x)
+#define fd(x)   (f8(x) ^ f4(x) ^ x)
+#define fe(x)   (f8(x) ^ f4(x) ^ f2(x))
+
+#if defined( USE_TABLES )
+
+#define sb_data(w) {    /* S Box data values */                            \
+    w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\
+    w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\
+    w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\
+    w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\
+    w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\
+    w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\
+    w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\
+    w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\
+    w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\
+    w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\
+    w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\
+    w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\
+    w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\
+    w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\
+    w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\
+    w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\
+    w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\
+    w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\
+    w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\
+    w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\
+    w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\
+    w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\
+    w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\
+    w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\
+    w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\
+    w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\
+    w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\
+    w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\
+    w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\
+    w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\
+    w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\
+    w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) }
+
+#define isb_data(w) {   /* inverse S Box data values */                    \
+    w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\
+    w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\
+    w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\
+    w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\
+    w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\
+    w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\
+    w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\
+    w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\
+    w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\
+    w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\
+    w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\
+    w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\
+    w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\
+    w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\
+    w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\
+    w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\
+    w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\
+    w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\
+    w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\
+    w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\
+    w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\
+    w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\
+    w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\
+    w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\
+    w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\
+    w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\
+    w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\
+    w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\
+    w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\
+    w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\
+    w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\
+    w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) }
+
+#define mm_data(w) {    /* basic data for forming finite field tables */   \
+    w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\
+    w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\
+    w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\
+    w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\
+    w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\
+    w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\
+    w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\
+    w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\
+    w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\
+    w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\
+    w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\
+    w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\
+    w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\
+    w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\
+    w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\
+    w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\
+    w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\
+    w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\
+    w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\
+    w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\
+    w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\
+    w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\
+    w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\
+    w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\
+    w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\
+    w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\
+    w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\
+    w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\
+    w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\
+    w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\
+    w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\
+    w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) }
+
+static const uint_8t sbox[256]  =  sb_data(f1);
+
+#if defined( AES_DEC_PREKEYED )
+static const uint_8t isbox[256] = isb_data(f1);
+#endif
+
+static const uint_8t gfm2_sbox[256] = sb_data(f2);
+static const uint_8t gfm3_sbox[256] = sb_data(f3);
+
+#if defined( AES_DEC_PREKEYED )
+static const uint_8t gfmul_9[256] = mm_data(f9);
+static const uint_8t gfmul_b[256] = mm_data(fb);
+static const uint_8t gfmul_d[256] = mm_data(fd);
+static const uint_8t gfmul_e[256] = mm_data(fe);
+#endif
+
+#define s_box(x)     sbox[(x)]
+#if defined( AES_DEC_PREKEYED )
+#define is_box(x)    isbox[(x)]
+#endif
+#define gfm2_sb(x)   gfm2_sbox[(x)]
+#define gfm3_sb(x)   gfm3_sbox[(x)]
+#if defined( AES_DEC_PREKEYED )
+#define gfm_9(x)     gfmul_9[(x)]
+#define gfm_b(x)     gfmul_b[(x)]
+#define gfm_d(x)     gfmul_d[(x)]
+#define gfm_e(x)     gfmul_e[(x)]
+#endif
+#else
+
+/* this is the high bit of x right shifted by 1 */
+/* position. Since the starting polynomial has  */
+/* 9 bits (0x11b), this right shift keeps the   */
+/* values of all top bits within a byte         */
+
+static uint_8t hibit(const uint_8t x)
+{   uint_8t r = (uint_8t)((x >> 1) | (x >> 2));
+
+    r |= (r >> 2);
+    r |= (r >> 4);
+    return (r + 1) >> 1;
+}
+
+/* return the inverse of the finite field element x */
+
+static uint_8t gf_inv(const uint_8t x)
+{   uint_8t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
+
+    if(x < 2) 
+        return x;
+
+    for( ; ; )
+    {
+        if(n1)
+            while(n2 >= n1)             /* divide polynomial p2 by p1    */
+            {
+                n2 /= n1;               /* shift smaller polynomial left */ 
+                p2 ^= (p1 * n2) & 0xff; /* and remove from larger one    */
+                v2 ^= (v1 * n2);        /* shift accumulated value and   */ 
+                n2 = hibit(p2);         /* add into result               */
+            }
+        else
+            return v1;
+
+        if(n2)                          /* repeat with values swapped    */ 
+            while(n1 >= n2)
+            {
+                n1 /= n2; 
+                p1 ^= p2 * n1; 
+                v1 ^= v2 * n1; 
+                n1 = hibit(p1);
+            }
+        else
+            return v2;
+    }
+}
+
+/* The forward and inverse affine transformations used in the S-box */
+uint_8t fwd_affine(const uint_8t x)
+{   
+#if defined( HAVE_UINT_32T )
+    uint_32t w = x;
+    w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4);
+    return 0x63 ^ ((w ^ (w >> 8)) & 0xff);
+#else
+    return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) 
+                    ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4);
+#endif
+}
+
+uint_8t inv_affine(const uint_8t x)
+{
+#if defined( HAVE_UINT_32T )
+    uint_32t w = x;
+    w = (w << 1) ^ (w << 3) ^ (w << 6);
+    return 0x05 ^ ((w ^ (w >> 8)) & 0xff);
+#else
+    return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) 
+                ^ (x >> 7) ^ (x >> 5) ^ (x >> 2);
+#endif
+}
+
+#define s_box(x)   fwd_affine(gf_inv(x))
+#define is_box(x)  gf_inv(inv_affine(x))
+#define gfm2_sb(x) f2(s_box(x))
+#define gfm3_sb(x) f3(s_box(x))
+#define gfm_9(x)   f9(x)
+#define gfm_b(x)   fb(x)
+#define gfm_d(x)   fd(x)
+#define gfm_e(x)   fe(x)
+
+#endif
+
+#if defined( HAVE_MEMCPY )
+#  define block_copy_nn(d, s, l)    memcpy(d, s, l)
+#  define block_copy(d, s)          memcpy(d, s, N_BLOCK)
+#else
+#  define block_copy_nn(d, s, l)    copy_block_nn(d, s, l)
+#  define block_copy(d, s)          copy_block(d, s)
+#endif
+
+static void copy_block( void *d, const void *s )
+{
+#if defined( HAVE_UINT_32T )
+    ((uint_32t*)d)[ 0] = ((uint_32t*)s)[ 0];
+    ((uint_32t*)d)[ 1] = ((uint_32t*)s)[ 1];
+    ((uint_32t*)d)[ 2] = ((uint_32t*)s)[ 2];
+    ((uint_32t*)d)[ 3] = ((uint_32t*)s)[ 3];
+#else
+    ((uint_8t*)d)[ 0] = ((uint_8t*)s)[ 0];
+    ((uint_8t*)d)[ 1] = ((uint_8t*)s)[ 1];
+    ((uint_8t*)d)[ 2] = ((uint_8t*)s)[ 2];
+    ((uint_8t*)d)[ 3] = ((uint_8t*)s)[ 3];
+    ((uint_8t*)d)[ 4] = ((uint_8t*)s)[ 4];
+    ((uint_8t*)d)[ 5] = ((uint_8t*)s)[ 5];
+    ((uint_8t*)d)[ 6] = ((uint_8t*)s)[ 6];
+    ((uint_8t*)d)[ 7] = ((uint_8t*)s)[ 7];
+    ((uint_8t*)d)[ 8] = ((uint_8t*)s)[ 8];
+    ((uint_8t*)d)[ 9] = ((uint_8t*)s)[ 9];
+    ((uint_8t*)d)[10] = ((uint_8t*)s)[10];
+    ((uint_8t*)d)[11] = ((uint_8t*)s)[11];
+    ((uint_8t*)d)[12] = ((uint_8t*)s)[12];
+    ((uint_8t*)d)[13] = ((uint_8t*)s)[13];
+    ((uint_8t*)d)[14] = ((uint_8t*)s)[14];
+    ((uint_8t*)d)[15] = ((uint_8t*)s)[15];
+#endif
+}
+
+static void copy_block_nn( uint_8t * d, const uint_8t *s, uint_8t nn )
+{
+    while( nn-- )
+        //*((uint_8t*)d)++ = *((uint_8t*)s)++;
+        *d++ = *s++;
+}
+
+static void xor_block( void *d, const void *s )
+{
+#if defined( HAVE_UINT_32T )
+    ((uint_32t*)d)[ 0] ^= ((uint_32t*)s)[ 0];
+    ((uint_32t*)d)[ 1] ^= ((uint_32t*)s)[ 1];
+    ((uint_32t*)d)[ 2] ^= ((uint_32t*)s)[ 2];
+    ((uint_32t*)d)[ 3] ^= ((uint_32t*)s)[ 3];
+#else
+    ((uint_8t*)d)[ 0] ^= ((uint_8t*)s)[ 0];
+    ((uint_8t*)d)[ 1] ^= ((uint_8t*)s)[ 1];
+    ((uint_8t*)d)[ 2] ^= ((uint_8t*)s)[ 2];
+    ((uint_8t*)d)[ 3] ^= ((uint_8t*)s)[ 3];
+    ((uint_8t*)d)[ 4] ^= ((uint_8t*)s)[ 4];
+    ((uint_8t*)d)[ 5] ^= ((uint_8t*)s)[ 5];
+    ((uint_8t*)d)[ 6] ^= ((uint_8t*)s)[ 6];
+    ((uint_8t*)d)[ 7] ^= ((uint_8t*)s)[ 7];
+    ((uint_8t*)d)[ 8] ^= ((uint_8t*)s)[ 8];
+    ((uint_8t*)d)[ 9] ^= ((uint_8t*)s)[ 9];
+    ((uint_8t*)d)[10] ^= ((uint_8t*)s)[10];
+    ((uint_8t*)d)[11] ^= ((uint_8t*)s)[11];
+    ((uint_8t*)d)[12] ^= ((uint_8t*)s)[12];
+    ((uint_8t*)d)[13] ^= ((uint_8t*)s)[13];
+    ((uint_8t*)d)[14] ^= ((uint_8t*)s)[14];
+    ((uint_8t*)d)[15] ^= ((uint_8t*)s)[15];
+#endif
+}
+
+static void copy_and_key( void *d, const void *s, const void *k )
+{
+#if defined( HAVE_UINT_32T )
+    ((uint_32t*)d)[ 0] = ((uint_32t*)s)[ 0] ^ ((uint_32t*)k)[ 0];
+    ((uint_32t*)d)[ 1] = ((uint_32t*)s)[ 1] ^ ((uint_32t*)k)[ 1];
+    ((uint_32t*)d)[ 2] = ((uint_32t*)s)[ 2] ^ ((uint_32t*)k)[ 2];
+    ((uint_32t*)d)[ 3] = ((uint_32t*)s)[ 3] ^ ((uint_32t*)k)[ 3];
+#elif 1
+    ((uint_8t*)d)[ 0] = ((uint_8t*)s)[ 0] ^ ((uint_8t*)k)[ 0];
+    ((uint_8t*)d)[ 1] = ((uint_8t*)s)[ 1] ^ ((uint_8t*)k)[ 1];
+    ((uint_8t*)d)[ 2] = ((uint_8t*)s)[ 2] ^ ((uint_8t*)k)[ 2];
+    ((uint_8t*)d)[ 3] = ((uint_8t*)s)[ 3] ^ ((uint_8t*)k)[ 3];
+    ((uint_8t*)d)[ 4] = ((uint_8t*)s)[ 4] ^ ((uint_8t*)k)[ 4];
+    ((uint_8t*)d)[ 5] = ((uint_8t*)s)[ 5] ^ ((uint_8t*)k)[ 5];
+    ((uint_8t*)d)[ 6] = ((uint_8t*)s)[ 6] ^ ((uint_8t*)k)[ 6];
+    ((uint_8t*)d)[ 7] = ((uint_8t*)s)[ 7] ^ ((uint_8t*)k)[ 7];
+    ((uint_8t*)d)[ 8] = ((uint_8t*)s)[ 8] ^ ((uint_8t*)k)[ 8];
+    ((uint_8t*)d)[ 9] = ((uint_8t*)s)[ 9] ^ ((uint_8t*)k)[ 9];
+    ((uint_8t*)d)[10] = ((uint_8t*)s)[10] ^ ((uint_8t*)k)[10];
+    ((uint_8t*)d)[11] = ((uint_8t*)s)[11] ^ ((uint_8t*)k)[11];
+    ((uint_8t*)d)[12] = ((uint_8t*)s)[12] ^ ((uint_8t*)k)[12];
+    ((uint_8t*)d)[13] = ((uint_8t*)s)[13] ^ ((uint_8t*)k)[13];
+    ((uint_8t*)d)[14] = ((uint_8t*)s)[14] ^ ((uint_8t*)k)[14];
+    ((uint_8t*)d)[15] = ((uint_8t*)s)[15] ^ ((uint_8t*)k)[15];
+#else
+    block_copy(d, s);
+    xor_block(d, k);
+#endif
+}
+
+static void add_round_key( uint_8t d[N_BLOCK], const uint_8t k[N_BLOCK] )
+{
+    xor_block(d, k);
+}
+
+static void shift_sub_rows( uint_8t st[N_BLOCK] )
+{   uint_8t tt;
+
+    st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]);
+    st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]);
+
+    tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]);
+    st[ 9] = s_box(st[13]); st[13] = s_box( tt );
+
+    tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt );
+    tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt );
+
+    tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]);
+    st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt );
+}
+
+#if defined( AES_DEC_PREKEYED )
+
+static void inv_shift_sub_rows( uint_8t st[N_BLOCK] )
+{   uint_8t tt;
+
+    st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]);
+    st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]);
+
+    tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]);
+    st[ 5] = is_box(st[1]); st[ 1] = is_box( tt );
+
+    tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt );
+    tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt );
+
+    tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]);
+    st[11] = is_box(st[15]); st[15] = is_box( tt );
+}
+
+#endif
+
+#if defined( VERSION_1 )
+  static void mix_sub_columns( uint_8t dt[N_BLOCK] )
+  { uint_8t st[N_BLOCK];
+    block_copy(st, dt);
+#else
+  static void mix_sub_columns( uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK] )
+  {
+#endif
+    dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]);
+    dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]);
+    dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]);
+    dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]);
+
+    dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]);
+    dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]);
+    dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]);
+    dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]);
+
+    dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]);
+    dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]);
+    dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]);
+    dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]);
+
+    dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]);
+    dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]);
+    dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]);
+    dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]);
+  }
+
+#if defined( AES_DEC_PREKEYED )
+
+#if defined( VERSION_1 )
+  static void inv_mix_sub_columns( uint_8t dt[N_BLOCK] )
+  { uint_8t st[N_BLOCK];
+    block_copy(st, dt);
+#else
+  static void inv_mix_sub_columns( uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK] )
+  {
+#endif
+    dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3]));
+    dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3]));
+    dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3]));
+    dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3]));
+
+    dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7]));
+    dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7]));
+    dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7]));
+    dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7]));
+
+    dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11]));
+    dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11]));
+    dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11]));
+    dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11]));
+
+    dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15]));
+    dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15]));
+    dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15]));
+    dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15]));
+  }
+
+#endif
+
+#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED )
+
+/*  Set the cipher key for the pre-keyed version */
+
+return_type aes_set_key( const unsigned char key[], length_type keylen, aes_context ctx[1] )
+{
+    uint_8t cc, rc, hi;
+
+    switch( keylen )
+    {
+    case 16:
+    case 24:
+    case 32:
+        break;
+    default: 
+        ctx->rnd = 0; 
+        return ( uint_8t )-1;
+    }
+    block_copy_nn(ctx->ksch, key, keylen);
+    hi = (keylen + 28) << 2;
+    ctx->rnd = (hi >> 4) - 1;
+    for( cc = keylen, rc = 1; cc < hi; cc += 4 )
+    {   uint_8t tt, t0, t1, t2, t3;
+
+        t0 = ctx->ksch[cc - 4];
+        t1 = ctx->ksch[cc - 3];
+        t2 = ctx->ksch[cc - 2];
+        t3 = ctx->ksch[cc - 1];
+        if( cc % keylen == 0 )
+        {
+            tt = t0;
+            t0 = s_box(t1) ^ rc;
+            t1 = s_box(t2);
+            t2 = s_box(t3);
+            t3 = s_box(tt);
+            rc = f2(rc);
+        }
+        else if( keylen > 24 && cc % keylen == 16 )
+        {
+            t0 = s_box(t0);
+            t1 = s_box(t1);
+            t2 = s_box(t2);
+            t3 = s_box(t3);
+        }
+        tt = cc - keylen;
+        ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0;
+        ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1;
+        ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2;
+        ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3;
+    }
+    return 0;
+}
+
+#endif
+
+#if defined( AES_ENC_PREKEYED )
+
+/*  Encrypt a single block of 16 bytes */
+
+return_type aes_encrypt( const unsigned char in[N_BLOCK], unsigned char  out[N_BLOCK], const aes_context ctx[1] )
+{
+    if( ctx->rnd )
+    {
+        uint_8t s1[N_BLOCK], r;
+        copy_and_key( s1, in, ctx->ksch );
+
+        for( r = 1 ; r < ctx->rnd ; ++r )
+#if defined( VERSION_1 )
+        {
+            mix_sub_columns( s1 );
+            add_round_key( s1, ctx->ksch + r * N_BLOCK);
+        }
+#else
+        {   uint_8t s2[N_BLOCK];
+            mix_sub_columns( s2, s1 );
+            copy_and_key( s1, s2, ctx->ksch + r * N_BLOCK);
+        }
+#endif
+        shift_sub_rows( s1 );
+        copy_and_key( out, s1, ctx->ksch + r * N_BLOCK );
+    }
+    else
+        return ( uint_8t )-1;
+    return 0;
+}
+
+/* CBC encrypt a number of blocks (input and return an IV) */
+
+return_type aes_cbc_encrypt( const unsigned char *in, unsigned char *out,
+                         int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1] )
+{
+
+    while(n_block--)
+    {
+        xor_block(iv, in);
+        if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS)
+            return EXIT_FAILURE;
+        //memcpy(out, iv, N_BLOCK);
+        block_copy(out, iv);
+        in += N_BLOCK;
+        out += N_BLOCK;
+    }
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined( AES_DEC_PREKEYED )
+
+/*  Decrypt a single block of 16 bytes */
+
+return_type aes_decrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1] )
+{
+    if( ctx->rnd )
+    {
+        uint_8t s1[N_BLOCK], r;
+        copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK );
+        inv_shift_sub_rows( s1 );
+
+        for( r = ctx->rnd ; --r ; )
+#if defined( VERSION_1 )
+        {
+            add_round_key( s1, ctx->ksch + r * N_BLOCK );
+            inv_mix_sub_columns( s1 );
+        }
+#else
+        {   uint_8t s2[N_BLOCK];
+            copy_and_key( s2, s1, ctx->ksch + r * N_BLOCK );
+            inv_mix_sub_columns( s1, s2 );
+        }
+#endif
+        copy_and_key( out, s1, ctx->ksch );
+    }
+    else
+        return -1;
+    return 0;
+}
+
+/* CBC decrypt a number of blocks (input and return an IV) */
+
+return_type aes_cbc_decrypt( const unsigned char *in, unsigned char *out,
+                         int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1] )
+{   
+    while(n_block--)
+    {   uint_8t tmp[N_BLOCK];
+        
+        //memcpy(tmp, in, N_BLOCK);
+        block_copy(tmp, in);
+        if(aes_decrypt(in, out, ctx) != EXIT_SUCCESS)
+            return EXIT_FAILURE;
+        xor_block(out, iv);
+        //memcpy(iv, tmp, N_BLOCK);
+        block_copy(iv, tmp);
+        in += N_BLOCK;
+        out += N_BLOCK;
+    }
+    return EXIT_SUCCESS;
+}
+
+#endif
+
+#if defined( AES_ENC_128_OTFK )
+
+/*  The 'on the fly' encryption key update for for 128 bit keys */
+
+static void update_encrypt_key_128( uint_8t k[N_BLOCK], uint_8t *rc )
+{   uint_8t cc;
+
+    k[0] ^= s_box(k[13]) ^ *rc;
+    k[1] ^= s_box(k[14]);
+    k[2] ^= s_box(k[15]);
+    k[3] ^= s_box(k[12]);
+    *rc = f2( *rc );
+
+    for(cc = 4; cc < 16; cc += 4 )
+    {
+        k[cc + 0] ^= k[cc - 4];
+        k[cc + 1] ^= k[cc - 3];
+        k[cc + 2] ^= k[cc - 2];
+        k[cc + 3] ^= k[cc - 1];
+    }
+}
+
+/*  Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
+
+void aes_encrypt_128( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK],
+                     const unsigned char key[N_BLOCK], unsigned char o_key[N_BLOCK] )
+{   uint_8t s1[N_BLOCK], r, rc = 1;
+
+    if(o_key != key)
+        block_copy( o_key, key );
+    copy_and_key( s1, in, o_key );
+
+    for( r = 1 ; r < 10 ; ++r )
+#if defined( VERSION_1 )
+    {
+        mix_sub_columns( s1 );
+        update_encrypt_key_128( o_key, &rc );
+        add_round_key( s1, o_key );
+    }
+#else
+    {   uint_8t s2[N_BLOCK];
+        mix_sub_columns( s2, s1 );
+        update_encrypt_key_128( o_key, &rc );
+        copy_and_key( s1, s2, o_key );
+    }
+#endif
+
+    shift_sub_rows( s1 );
+    update_encrypt_key_128( o_key, &rc );
+    copy_and_key( out, s1, o_key );
+}
+
+#endif
+
+#if defined( AES_DEC_128_OTFK )
+
+/*  The 'on the fly' decryption key update for for 128 bit keys */
+
+static void update_decrypt_key_128( uint_8t k[N_BLOCK], uint_8t *rc )
+{   uint_8t cc;
+
+    for( cc = 12; cc > 0; cc -= 4 )
+    {
+        k[cc + 0] ^= k[cc - 4];
+        k[cc + 1] ^= k[cc - 3];
+        k[cc + 2] ^= k[cc - 2];
+        k[cc + 3] ^= k[cc - 1];
+    }
+    *rc = d2(*rc);
+    k[0] ^= s_box(k[13]) ^ *rc;
+    k[1] ^= s_box(k[14]);
+    k[2] ^= s_box(k[15]);
+    k[3] ^= s_box(k[12]);
+}
+
+/*  Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */
+
+void aes_decrypt_128( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK],
+                      const unsigned char key[N_BLOCK], unsigned char o_key[N_BLOCK] )
+{
+    uint_8t s1[N_BLOCK], r, rc = 0x6c;
+    if(o_key != key)
+        block_copy( o_key, key );
+
+    copy_and_key( s1, in, o_key );
+    inv_shift_sub_rows( s1 );
+
+    for( r = 10 ; --r ; )
+#if defined( VERSION_1 )
+    {
+        update_decrypt_key_128( o_key, &rc );
+        add_round_key( s1, o_key );
+        inv_mix_sub_columns( s1 );
+    }
+#else
+    {   uint_8t s2[N_BLOCK];
+        update_decrypt_key_128( o_key, &rc );
+        copy_and_key( s2, s1, o_key );
+        inv_mix_sub_columns( s1, s2 );
+    }
+#endif
+    update_decrypt_key_128( o_key, &rc );
+    copy_and_key( out, s1, o_key );
+}
+
+#endif
+
+#if defined( AES_ENC_256_OTFK )
+
+/*  The 'on the fly' encryption key update for for 256 bit keys */
+
+static void update_encrypt_key_256( uint_8t k[2 * N_BLOCK], uint_8t *rc )
+{   uint_8t cc;
+
+    k[0] ^= s_box(k[29]) ^ *rc;
+    k[1] ^= s_box(k[30]);
+    k[2] ^= s_box(k[31]);
+    k[3] ^= s_box(k[28]);
+    *rc = f2( *rc );
+
+    for(cc = 4; cc < 16; cc += 4)
+    {
+        k[cc + 0] ^= k[cc - 4];
+        k[cc + 1] ^= k[cc - 3];
+        k[cc + 2] ^= k[cc - 2];
+        k[cc + 3] ^= k[cc - 1];
+    }
+
+    k[16] ^= s_box(k[12]);
+    k[17] ^= s_box(k[13]);
+    k[18] ^= s_box(k[14]);
+    k[19] ^= s_box(k[15]);
+
+    for( cc = 20; cc < 32; cc += 4 )
+    {
+        k[cc + 0] ^= k[cc - 4];
+        k[cc + 1] ^= k[cc - 3];
+        k[cc + 2] ^= k[cc - 2];
+        k[cc + 3] ^= k[cc - 1];
+    }
+}
+
+/*  Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */
+
+void aes_encrypt_256( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK],
+                      const unsigned char key[2 * N_BLOCK], unsigned char o_key[2 * N_BLOCK] )
+{
+    uint_8t s1[N_BLOCK], r, rc = 1;
+    if(o_key != key)
+    {
+        block_copy( o_key, key );
+        block_copy( o_key + 16, key + 16 );
+    }
+    copy_and_key( s1, in, o_key );
+
+    for( r = 1 ; r < 14 ; ++r )
+#if defined( VERSION_1 )
+    {
+        mix_sub_columns(s1);
+        if( r & 1 )
+            add_round_key( s1, o_key + 16 );
+        else
+        {
+            update_encrypt_key_256( o_key, &rc );
+            add_round_key( s1, o_key );
+        }
+    }
+#else
+    {   uint_8t s2[N_BLOCK];
+        mix_sub_columns( s2, s1 );
+        if( r & 1 )
+            copy_and_key( s1, s2, o_key + 16 );
+        else
+        {
+            update_encrypt_key_256( o_key, &rc );
+            copy_and_key( s1, s2, o_key );
+        }
+    }
+#endif
+
+    shift_sub_rows( s1 );
+    update_encrypt_key_256( o_key, &rc );
+    copy_and_key( out, s1, o_key );
+}
+
+#endif
+
+#if defined( AES_DEC_256_OTFK )
+
+/*  The 'on the fly' encryption key update for for 256 bit keys */
+
+static void update_decrypt_key_256( uint_8t k[2 * N_BLOCK], uint_8t *rc )
+{   uint_8t cc;
+
+    for(cc = 28; cc > 16; cc -= 4)
+    {
+        k[cc + 0] ^= k[cc - 4];
+        k[cc + 1] ^= k[cc - 3];
+        k[cc + 2] ^= k[cc - 2];
+        k[cc + 3] ^= k[cc - 1];
+    }
+
+    k[16] ^= s_box(k[12]);
+    k[17] ^= s_box(k[13]);
+    k[18] ^= s_box(k[14]);
+    k[19] ^= s_box(k[15]);
+
+    for(cc = 12; cc > 0; cc -= 4)
+    {
+        k[cc + 0] ^= k[cc - 4];
+        k[cc + 1] ^= k[cc - 3];
+        k[cc + 2] ^= k[cc - 2];
+        k[cc + 3] ^= k[cc - 1];
+    }
+
+    *rc = d2(*rc);
+    k[0] ^= s_box(k[29]) ^ *rc;
+    k[1] ^= s_box(k[30]);
+    k[2] ^= s_box(k[31]);
+    k[3] ^= s_box(k[28]);
+}
+
+/*  Decrypt a single block of 16 bytes with 'on the fly'
+    256 bit keying
+*/
+void aes_decrypt_256( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK],
+                      const unsigned char key[2 * N_BLOCK], unsigned char o_key[2 * N_BLOCK] )
+{
+    uint_8t s1[N_BLOCK], r, rc = 0x80;
+
+    if(o_key != key)
+    {
+        block_copy( o_key, key );
+        block_copy( o_key + 16, key + 16 );
+    }
+
+    copy_and_key( s1, in, o_key );
+    inv_shift_sub_rows( s1 );
+
+    for( r = 14 ; --r ; )
+#if defined( VERSION_1 )
+    {
+        if( ( r & 1 ) )
+        {
+            update_decrypt_key_256( o_key, &rc );
+            add_round_key( s1, o_key + 16 );
+        }
+        else
+            add_round_key( s1, o_key );
+        inv_mix_sub_columns( s1 );
+    }
+#else
+    {   uint_8t s2[N_BLOCK];
+        if( ( r & 1 ) )
+        {
+            update_decrypt_key_256( o_key, &rc );
+            copy_and_key( s2, s1, o_key + 16 );
+        }
+        else
+            copy_and_key( s2, s1, o_key );
+        inv_mix_sub_columns( s1, s2 );
+    }
+#endif
+    copy_and_key( out, s1, o_key );
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/aes.h	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,162 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The redistribution and use of this software (with or without changes)
+ is allowed without the payment of fees or royalties provided that:
+
+  1. source code distributions include the above copyright notice, this
+     list of conditions and the following disclaimer;
+
+  2. binary distributions include the above copyright notice, this list
+     of conditions and the following disclaimer in their documentation;
+
+  3. the name of the copyright holder is not used to endorse products
+     built using this software without specific written permission.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 09/09/2006
+
+ This is an AES implementation that uses only 8-bit byte operations on the
+ cipher state.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#if 1
+#  define AES_ENC_PREKEYED  /* AES encryption with a precomputed key schedule  */
+#endif
+#if 0
+#  define AES_DEC_PREKEYED  /* AES decryption with a precomputed key schedule  */
+#endif
+#if 0
+#  define AES_ENC_128_OTFK  /* AES encryption with 'on the fly' 128 bit keying */
+#endif
+#if 0
+#  define AES_DEC_128_OTFK  /* AES decryption with 'on the fly' 128 bit keying */
+#endif
+#if 0
+#  define AES_ENC_256_OTFK  /* AES encryption with 'on the fly' 256 bit keying */
+#endif
+#if 0
+#  define AES_DEC_256_OTFK  /* AES decryption with 'on the fly' 256 bit keying */
+#endif
+
+#define N_ROW                   4
+#define N_COL                   4
+#define N_BLOCK   (N_ROW * N_COL)
+#define N_MAX_ROUNDS           14
+
+typedef unsigned char uint_8t;
+
+typedef uint_8t return_type;
+
+/*  Warning: The key length for 256 bit keys overflows a byte
+    (see comment below)
+*/
+
+typedef uint_8t length_type;
+
+typedef struct
+{   uint_8t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK];
+    uint_8t rnd;
+} aes_context;
+
+/*  The following calls are for a precomputed key schedule
+
+    NOTE: If the length_type used for the key length is an
+    unsigned 8-bit character, a key length of 256 bits must
+    be entered as a length in bytes (valid inputs are hence
+    128, 192, 16, 24 and 32).
+*/
+
+#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED )
+
+return_type aes_set_key( const unsigned char key[],
+                         length_type keylen,
+                         aes_context ctx[1] );
+#endif
+
+#if defined( AES_ENC_PREKEYED )
+
+return_type aes_encrypt( const unsigned char in[N_BLOCK],
+                         unsigned char out[N_BLOCK],
+                         const aes_context ctx[1] );
+
+return_type aes_cbc_encrypt( const unsigned char *in,
+                         unsigned char *out,
+                         int n_block,
+                         unsigned char iv[N_BLOCK],
+                         const aes_context ctx[1] );
+#endif
+
+#if defined( AES_DEC_PREKEYED )
+
+return_type aes_decrypt( const unsigned char in[N_BLOCK],
+                         unsigned char out[N_BLOCK],
+                         const aes_context ctx[1] );
+
+return_type aes_cbc_decrypt( const unsigned char *in,
+                         unsigned char *out,
+                         int n_block,
+                         unsigned char iv[N_BLOCK],
+                         const aes_context ctx[1] );
+#endif
+
+/*  The following calls are for 'on the fly' keying.  In this case the
+    encryption and decryption keys are different.
+
+    The encryption subroutines take a key in an array of bytes in
+    key[L] where L is 16, 24 or 32 bytes for key lengths of 128,
+    192, and 256 bits respectively.  They then encrypts the input
+    data, in[] with this key and put the reult in the output array
+    out[].  In addition, the second key array, o_key[L], is used
+    to output the key that is needed by the decryption subroutine
+    to reverse the encryption operation.  The two key arrays can
+    be the same array but in this case the original key will be
+    overwritten.
+
+    In the same way, the decryption subroutines output keys that
+    can be used to reverse their effect when used for encryption.
+
+    Only 128 and 256 bit keys are supported in these 'on the fly'
+    modes.
+*/
+
+#if defined( AES_ENC_128_OTFK )
+void aes_encrypt_128( const unsigned char in[N_BLOCK],
+                      unsigned char out[N_BLOCK],
+                      const unsigned char key[N_BLOCK],
+                      uint_8t o_key[N_BLOCK] );
+#endif
+
+#if defined( AES_DEC_128_OTFK )
+void aes_decrypt_128( const unsigned char in[N_BLOCK],
+                      unsigned char out[N_BLOCK],
+                      const unsigned char key[N_BLOCK],
+                      unsigned char o_key[N_BLOCK] );
+#endif
+
+#if defined( AES_ENC_256_OTFK )
+void aes_encrypt_256( const unsigned char in[N_BLOCK],
+                      unsigned char out[N_BLOCK],
+                      const unsigned char key[2 * N_BLOCK],
+                      unsigned char o_key[2 * N_BLOCK] );
+#endif
+
+#if defined( AES_DEC_256_OTFK )
+void aes_decrypt_256( const unsigned char in[N_BLOCK],
+                      unsigned char out[N_BLOCK],
+                      const unsigned char key[2 * N_BLOCK],
+                      unsigned char o_key[2 * N_BLOCK] );
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/cmac.cpp	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,176 @@
+/**************************************************************************
+Copyright (C) 2009 Lander Casado, Philippas Tsigas
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files 
+(the "Software"), to deal with the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, 
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions: 
+
+Redistributions of source code must retain the above copyright notice, 
+this list of conditions and the following disclaimers. Redistributions in
+binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimers in the documentation and/or 
+other materials provided with the distribution.
+
+In no event shall the authors or copyright holders be liable for any special,
+incidental, indirect or consequential damages of any kind, or any damages 
+whatsoever resulting from loss of use, data or profits, whether or not 
+advised of the possibility of damage, and on any theory of liability, 
+arising out of or in connection with the use or performance of this software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+DEALINGS WITH THE SOFTWARE
+
+*****************************************************************************/
+//#include <sys/param.h>
+//#include <sys/systm.h> 
+#include "mbed.h"
+#include "aes.h"
+#include "cmac.h"
+#include "utilities.h"
+
+#define LSHIFT(v, r) do {                                       \
+  int i;                                                  \
+           for (i = 0; i < 15; i++)                                \
+                    (r)[i] = (v)[i] << 1 | (v)[i + 1] >> 7;         \
+            (r)[15] = (v)[15] << 1;                                 \
+    } while (0)
+    
+#define XOR(v, r) do {                                          \
+            int i;                                                  \
+            for (i = 0; i < 16; i++)     \
+        {   \
+                    (r)[i] = (r)[i] ^ (v)[i]; \
+        }                          \
+    } while (0) \
+
+
+//#define MIN(a,b) (((a)<(b))?(a):(b))
+
+/*
+void memcpy1( u_int8_t *dst, const u_int8_t *src, u_int size );
+void memset1( u_int8_t *dst, u_int8_t value, u_int size );
+*/
+
+/*
+static void memcpy1( uint_8t * d, const uint_8t *s, uint_8t nn )
+{
+    while( nn-- )
+        // *((uint_8t*)d)++ = *((uint_8t*)s)++;
+        *d++ = *s++;
+}
+
+static void memset1( uint_8t * d, uint_8t a, uint_8t nn )
+{
+    while( nn-- )
+        // *((uint_8t*)d)++ = *((uint_8t*)s)++;
+        *d++ = a;
+}
+*/
+
+void AES_CMAC_Init(AES_CMAC_CTX *ctx)
+{
+            memset1(ctx->X, 0, sizeof ctx->X);
+            ctx->M_n = 0;
+        memset1(ctx->rijndael.ksch, '\0', 240);
+}
+    
+void AES_CMAC_SetKey(AES_CMAC_CTX *ctx, const u_int8_t key[AES_CMAC_KEY_LENGTH])
+{
+           //rijndael_set_key_enc_only(&ctx->rijndael, key, 128);
+       aes_set_key( key, AES_CMAC_KEY_LENGTH, &ctx->rijndael);
+}
+    
+void AES_CMAC_Update(AES_CMAC_CTX *ctx, const u_int8_t *data, u_int len)
+{
+            u_int mlen;
+        unsigned char in[16];
+    
+            if (ctx->M_n > 0) {
+                  mlen = MIN(16 - ctx->M_n, len);
+                    memcpy1(ctx->M_last + ctx->M_n, ( uint8_t* )data, mlen);
+                    ctx->M_n += mlen;
+                    if (ctx->M_n < 16 || len == mlen)
+                            return;
+                   XOR(ctx->M_last, ctx->X);
+                    //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X);
+            aes_encrypt( ctx->X, ctx->X, &ctx->rijndael);
+                    data += mlen;
+                    len -= mlen;
+            }
+            while (len > 16) {      /* not last block */
+         
+                    XOR(data, ctx->X);
+                    //rijndael_encrypt(&ctx->rijndael, ctx->X, ctx->X);
+
+                    memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten
+            aes_encrypt( in, in, &ctx->rijndael);
+                    memcpy1(&ctx->X[0], in, 16);
+
+                    data += 16;
+                    len -= 16;
+            }
+            /* potential last block, save it */
+            memcpy1(ctx->M_last, ( uint8_t* )data, len);
+            ctx->M_n = len;
+}
+   
+void AES_CMAC_Final(u_int8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX *ctx)
+{
+            u_int8_t K[16];
+        unsigned char in[16];
+            /* generate subkey K1 */
+            memset1(K, '\0', 16);
+
+            //rijndael_encrypt(&ctx->rijndael, K, K);
+
+            aes_encrypt( K, K, &ctx->rijndael);
+
+            if (K[0] & 0x80) {
+                    LSHIFT(K, K);
+                   K[15] ^= 0x87;
+            } else
+                    LSHIFT(K, K);
+
+       
+            if (ctx->M_n == 16) {
+                    /* last block was a complete block */
+                    XOR(K, ctx->M_last);
+
+           } else {
+                   /* generate subkey K2 */
+                  if (K[0] & 0x80) {
+                          LSHIFT(K, K);
+                          K[15] ^= 0x87;
+                  } else
+                           LSHIFT(K, K);
+
+                   /* padding(M_last) */
+                   ctx->M_last[ctx->M_n] = 0x80;
+                   while (++ctx->M_n < 16)
+                         ctx->M_last[ctx->M_n] = 0;
+   
+                  XOR(K, ctx->M_last);
+          
+           
+           }
+           XOR(ctx->M_last, ctx->X);
+      
+           //rijndael_encrypt(&ctx->rijndael, ctx->X, digest);
+    
+       memcpy1(in, &ctx->X[0], 16); //Bestela ez du ondo iten
+       aes_encrypt(in, digest, &ctx->rijndael);
+           memset1(K, 0, sizeof K);
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/cmac.h	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,66 @@
+/**************************************************************************
+Copyright (C) 2009 Lander Casado, Philippas Tsigas
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files 
+(the "Software"), to deal with the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, 
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions: 
+
+Redistributions of source code must retain the above copyright notice, 
+this list of conditions and the following disclaimers. Redistributions in
+binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimers in the documentation and/or 
+other materials provided with the distribution.
+
+In no event shall the authors or copyright holders be liable for any special,
+incidental, indirect or consequential damages of any kind, or any damages 
+whatsoever resulting from loss of use, data or profits, whether or not 
+advised of the possibility of damage, and on any theory of liability, 
+arising out of or in connection with the use or performance of this software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+DEALINGS WITH THE SOFTWARE
+
+*****************************************************************************/
+
+#ifndef _CMAC_H_
+#define _CMAC_H_
+
+#include "aes.h" 
+  
+#define AES_CMAC_KEY_LENGTH     16
+#define AES_CMAC_DIGEST_LENGTH  16
+ 
+
+typedef unsigned char  u_int8_t;
+typedef unsigned int u_int;
+typedef struct _AES_CMAC_CTX {
+            aes_context    rijndael;
+            u_int8_t        X[16];
+            u_int8_t        M_last[16];
+            u_int           M_n;
+    } AES_CMAC_CTX;
+   
+//#include <sys/cdefs.h>
+    
+//__BEGIN_DECLS
+void     AES_CMAC_Init(AES_CMAC_CTX * ctx);
+void     AES_CMAC_SetKey(AES_CMAC_CTX * ctx, const u_int8_t key[AES_CMAC_KEY_LENGTH]);
+void     AES_CMAC_Update(AES_CMAC_CTX * ctx, const u_int8_t * data, u_int len);
+          //          __attribute__((__bounded__(__string__,2,3)));
+void     AES_CMAC_Final(u_int8_t digest[AES_CMAC_DIGEST_LENGTH], AES_CMAC_CTX  * ctx);
+            //     __attribute__((__bounded__(__minbytes__,1,AES_CMAC_DIGEST_LENGTH)));
+//__END_DECLS
+
+#endif /* _CMAC_H_ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/radio/SX1276Lib.lib	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/teams/Semtech/code/SX1276Lib/#e05596ba4166
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system/timer.cpp	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,62 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: Timer objects and scheduling management
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#include "board.h"
+
+Timer TimeCounter;
+Ticker LoadTimeCounter;
+
+volatile uint32_t CurrentTime = 0;
+
+void TimerResetTimeCounter( void )
+{
+    CurrentTime = CurrentTime + TimeCounter.read_us( );
+    TimeCounter.reset( );
+    TimeCounter.start( );
+}
+
+void TimerTimeCounterInit( void )
+{
+    TimeCounter.start( );
+    LoadTimeCounter.attach( &TimerResetTimeCounter, 10 );
+}
+
+TimerTime_t TimerGetCurrentTime( void )
+{
+    CurrentTime += TimeCounter.read_us( );
+    TimeCounter.reset( );
+    TimeCounter.start( );
+    return ( ( TimerTime_t )CurrentTime );
+}
+
+void TimerInit( TimerEvent_t *obj, void ( *callback )( void ) )
+{
+    obj->value = 0;
+    obj->Callback = callback;
+}
+
+void TimerStart( TimerEvent_t *obj )
+{
+    obj->Timer.attach_us( obj->Callback, obj->value );
+}
+
+void TimerStop( TimerEvent_t *obj )
+{
+    obj->Timer.detach( );
+}
+
+void TimerSetValue( TimerEvent_t *obj, uint32_t value )
+{
+    obj->value = value;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system/timer.h	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,92 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: Timer objects and scheduling management
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+#include "mbed.h"
+
+/*!
+ * \brief Timer object description
+ */
+typedef struct TimerEvent_s
+{
+    uint32_t value;
+    void ( *Callback )( void );
+    Ticker Timer;
+}TimerEvent_t;
+
+/*!
+ * \brief Timer time variable definition
+ */
+#ifndef TimerTime_t
+typedef uint32_t TimerTime_t;
+#endif
+
+/*!
+ * \brief Inializes the timer used to get current time.
+ *
+ * \remark Current time corresponds to the time since system startup
+ */
+void TimerTimeCounterInit( void );
+
+/*!
+ * \brief Initializes the timer object
+ *
+ * \remark TimerSetValue function must be called before starting the timer.
+ *         this function initializes timestamp and reload value at 0.
+ *
+ * \param [IN] obj          Structure containing the timer object parameters
+ * \param [IN] callback     Function callback called at the end of the timeout
+ */
+void TimerInit( TimerEvent_t *obj, void ( *callback )( void ) );
+
+/*!
+ * \brief Starts and adds the timer object to the list of timer events
+ *
+ * \param [IN] obj Structure containing the timer object parameters
+ */
+void TimerStart( TimerEvent_t *obj );
+
+/*!
+ * \brief Stops and removes the timer object from the list of timer events
+ *
+ * \param [IN] obj Structure containing the timer object parameters
+ */
+void TimerStop( TimerEvent_t *obj );
+
+/*!
+ * \brief Resets the timer object
+ *
+ * \param [IN] obj Structure containing the timer object parameters
+ */
+void TimerReset( TimerEvent_t *obj );
+
+/*!
+ * \brief Set timer new timeout value
+ *
+ * \param [IN] obj   Structure containing the timer object parameters
+ * \param [IN] value New timer timeout value
+ */
+void TimerSetValue( TimerEvent_t *obj, uint32_t value );
+
+/*!
+ * \brief Read the current time
+ *
+ * \retval time returns current time
+ */
+TimerTime_t TimerGetCurrentTime( void );
+
+
+#endif // __TIMER_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system/utilities.cpp	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,76 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: Helper functions implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include "board.h"
+#include "utilities.h"
+
+/*!
+ * Redefinition of rand() and srand() standard C functions.
+ * These functions are redefined in order to get the same behavior across
+ * different compiler toolchains implementations.
+ */
+// Standard random functions redefinition start
+#define RAND_LOCAL_MAX 2147483647
+
+static unsigned long next = 1;
+
+int rand1( void )
+{
+    return ( ( next = next * 1103515245 + 12345 ) % RAND_LOCAL_MAX );
+}
+
+void srand1( unsigned int seed )
+{
+    next = seed;
+}
+// Standard random functions redefinition end
+
+int32_t randr( int32_t min, int32_t max )
+{
+    return ( int32_t )rand1( ) % ( max - min + 1 ) + min;
+}
+
+void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size )
+{
+    while( size-- )
+    {
+        *dst++ = *src++;
+    }
+}
+
+void memset1( uint8_t *dst, uint8_t value, uint16_t size )
+{
+    while( size-- )
+    {
+        *dst++ = value;
+    }
+}
+
+int8_t Nibble2HexChar( uint8_t a )
+{
+    if( a < 10 )
+    {
+        return '0' + a;
+    }
+    else if( a < 16 )
+    {
+        return 'A' + ( a - 10 );
+    }
+    else
+    {
+        return '?';
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/system/utilities.h	Tue Oct 20 13:21:26 2015 +0000
@@ -0,0 +1,83 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+    (C)2013 Semtech
+
+Description: Helper functions implementation
+
+License: Revised BSD License, see LICENSE.TXT file include in the project
+
+Maintainer: Miguel Luis and Gregory Cristian
+*/
+#ifndef __UTILITIES_H__
+#define __UTILITIES_H__
+
+/*!
+ * \brief Returns the minimum value betwen a and b
+ *
+ * \param [IN] a 1st value
+ * \param [IN] b 2nd value
+ * \retval minValue Minimum value
+ */
+#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
+
+/*!
+ * \brief Returns the maximum value betwen a and b
+ *
+ * \param [IN] a 1st value
+ * \param [IN] b 2nd value
+ * \retval maxValue Maximum value
+ */
+#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )
+
+/*!
+ * \brief Returns 2 raised to the power of n
+ *
+ * \param [IN] n power value
+ * \retval result of raising 2 to the power n
+ */
+#define POW2( n ) ( 1 << n )
+
+/*!
+ * \brief Computes a random number between min and max
+ *
+ * \param [IN] min range minimum value
+ * \param [IN] max range maximum value
+ * \retval random random value in range min..max
+ */
+int32_t randr( int32_t min, int32_t max );
+
+/*!
+ * \brief Copies size elements of src array to dst array
+ * 
+ * \remark STM32 Standard memcpy function only works on pointers that are aligned
+ *
+ * \param [OUT] dst  Destination array
+ * \param [IN]  src  Source array
+ * \param [IN]  size Number of bytes to be copied
+ */
+void memcpy1( uint8_t *dst, const uint8_t *src, uint16_t size );
+
+/*!
+ * \brief Set size elements of dst array with value 
+ * 
+ * \remark STM32 Standard memset function only works on pointers that are aligned
+ *
+ * \param [OUT] dst   Destination array
+ * \param [IN]  value Default value
+ * \param [IN]  size  Number of bytes to be copied
+ */
+void memset1( uint8_t *dst, uint8_t value, uint16_t size );
+
+/*!
+ * \brief Converts a nibble to an hexadecimal character
+ * 
+ * \param [IN] a   Nibble to be converted
+ * \retval hexChar Converted hexadecimal character
+ */
+int8_t Nibble2HexChar( uint8_t a );
+
+#endif // __UTILITIES_H__