LoRaWAN Class A node example based on IBM LoRa MAC in C (LMiC) implementation

Dependencies:   LMiC SX1276Lib mbed

Revision:
1:60184eda0066
Parent:
0:a2929fa6e4f0
Child:
3:ce28e3313a88
--- a/main.cpp	Thu Jan 22 13:02:55 2015 +0000
+++ b/main.cpp	Tue Mar 31 13:38:08 2015 +0000
@@ -4,15 +4,18 @@
  \____ \| ___ |    (_   _) ___ |/ ___)  _ \
  _____) ) ____| | | || |_| ____( (___| | | |
 (______/|_____)_|_|_| \__)_____)\____)_| |_|
-    (C)2013 Semtech
+    (C)2015 Semtech
 
-Description: MBED example application
+Description: MBED LoRaWAN example application
 
 License: Revised BSD License, see LICENSE.TXT file include in the project
 
 Maintainer: Miguel Luis and Gregory Cristian
 */
+#include "mbed.h"
+
 #include "lmic.h"
+#include "debug.h"
 
 /*!
  * When set to 1 the application uses the Over-the-Air activation procedure
@@ -20,16 +23,60 @@
  */
 #define OVER_THE_AIR_ACTIVATION                     0
 
-#define APP_DATA_SIZE                               1
+#if( OVER_THE_AIR_ACTIVATION == 0 )
+
+/*!
+ * Defines the network ID when using personalization activation procedure
+ */
+#define LORAWAN_NET_ID                              ( uint32_t )0x00000000
+
+/*!
+ * Defines the device address when using personalization activation procedure
+ */
+#define LORAWAN_DEV_ADDR                            ( uint32_t )0x12345678
+
+#endif
+
+/*!
+ * Defines the application data transmission duty cycle
+ */
+#define APP_TX_DUTYCYCLE                            5000 // 5 [s] value in ms
+#define APP_TX_DUTYCYCLE_RND                        1000 // 1 [s] value in ms
+
+/*!
+ * LoRaWAN Adaptative Data Rate
+ */
+#define LORAWAN_ADR_ON                              1
+
+/*!
+ * LoRaWAN confirmed messages
+ */
+#define LORAWAN_CONFIRMED_MSG_ON                    1
+
+/*!
+ * LoRaWAN application port
+ */
+#define LORAWAN_APP_PORT                            15
+
+/*!
+ * User application data buffer size
+ */
+#if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
+#define LORAWAN_APP_DATA_SIZE                       6
+
+#else
+#define LORAWAN_APP_DATA_SIZE                       1
+
+#endif
 
 //////////////////////////////////////////////////
 // CONFIGURATION (FOR APPLICATION CALLBACKS BELOW)
 //////////////////////////////////////////////////
 
 // application router ID (LSBF)
-static const u1_t AppEui[8] =
+static const uint8_t AppEui[8] =
 {
-    0xAA, 0xCC, 0x11, 0x00, 0xCC, 0xEE, 0x77, 0xEE
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
 // unique device ID (LSBF)
@@ -39,49 +86,70 @@
 };
 
 // device-specific AES key (derived from device EUI)
-static const u1_t DevKey[16] =
+static const uint8_t DevKey[16] = 
 {
-    0xAB, 0x89, 0xEF, 0xCD, 0x23, 0x01, 0x67, 0x45,
-    0x54, 0x76, 0x10, 0x32, 0xDC, 0xFE, 0x98, 0xBA
+    0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
+    0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
 };
 
+#if( OVER_THE_AIR_ACTIVATION == 0 )
+// network session key
 static uint8_t NwkSKey[] = 
 { 
     0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
     0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
 };
 
+// application session key
 static uint8_t ArtSKey[] = 
 { 
     0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
     0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
 };
 
+#endif
+
 // LEDs and Frame jobs
 osjob_t rxLedJob;
 osjob_t txLedJob;
 osjob_t sendFrameJob;
 
+// LED state
 static bool AppLedStateOn = false;
 
 //////////////////////////////////////////////////
+// Utility functions
+//////////////////////////////////////////////////
+/*!
+ * \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 )
+{
+    return ( int32_t )rand( ) % ( max - min + 1 ) + min;
+}
+
+//////////////////////////////////////////////////
 // APPLICATION CALLBACKS
 //////////////////////////////////////////////////
 
 // provide application router ID (8 bytes, LSBF)
-void os_getArtEui( u1_t* buf )
+void os_getArtEui( uint8_t *buf )
 {
     memcpy( buf, AppEui, 8 );
 }
 
 // provide device ID (8 bytes, LSBF)
-void os_getDevEui( u1_t* buf )
+void os_getDevEui( uint8_t *buf )
 {
     memcpy( buf, DevEui, 8 );
 }
 
 // provide device key (16 bytes)
-void os_getDevKey( u1_t* buf )
+void os_getDevKey( uint8_t *buf )
 {
     memcpy( buf, DevKey, 16 );
 }
@@ -90,35 +158,26 @@
 // MAIN - INITIALIZATION AND STARTUP
 //////////////////////////////////////////////////
 
-// Initialization job
-static void onInit( osjob_t* j )
-{
-    // reset MAC state
-    LMIC_reset( );
-    LMIC_setAdrMode( 1 );
-    LMIC_setDrTxpow( DR_FSK, 14 );
-    // start joining
-#if( OVER_THE_AIR_ACTIVATION != 0 )
-    LMIC_startJoining( );
-#else    
-    LMIC_startABP( 0, 0x44332211, NwkSKey, ArtSKey );
-#endif
-    // init done - onEvent() callback will be invoked...
-}
-
 static void onRxLed( osjob_t* j )
 {
-    DEBUG_VAL("LED2 = ", 1 );
+    debug_val("LED2 = ", 0 );
 }
 
 static void onTxLed( osjob_t* j )
 {
-    DEBUG_VAL("LED1 = ", 1 );
+    debug_val("LED1 = ", 0 );
 }
 
 static void prepareTxFrame( void )
 {
     LMIC.frame[0] = AppLedStateOn;
+#if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
+    LMIC.frame[1] = LMIC.seqnoDn >> 8;
+    LMIC.frame[2] = LMIC.seqnoDn;
+    LMIC.frame[3] = LMIC.rssi >> 8;
+    LMIC.frame[4] = LMIC.rssi;
+    LMIC.frame[5] = LMIC.snr;
+#endif    
 }
 
 void processRxFrame( void )
@@ -130,7 +189,7 @@
             if( LMIC.dataLen == 1 )
             {
                 AppLedStateOn = LMIC.frame[LMIC.dataBeg] & 0x01;
-                DEBUG_VAL( "LED3 = ", AppLedStateOn ? 0 : 1 );
+                debug_val( "LED3 = ", AppLedStateOn );
             }
             break;
         default:
@@ -141,10 +200,32 @@
 static void onSendFrame( osjob_t* j )
 {
     prepareTxFrame( );
-    LMIC_setTxData2( 1, LMIC.frame, APP_DATA_SIZE, 1 );
+    LMIC_setTxData2( LORAWAN_APP_PORT, LMIC.frame, LORAWAN_APP_DATA_SIZE, LORAWAN_CONFIRMED_MSG_ON );
+
+    // Blink Tx LED
+    debug_val( "LED1 = ", 1 );
+    os_setTimedCallback( &txLedJob, os_getTime( ) + ms2osticks( 25 ), onTxLed );
 }
 
-int main(void)
+// Initialization job
+static void onInit( osjob_t* j )
+{
+    // reset MAC state
+    LMIC_reset( );
+    LMIC_setAdrMode( LORAWAN_ADR_ON );
+    LMIC_setDrTxpow( DR_SF12, 14 );
+
+    // start joining
+#if( OVER_THE_AIR_ACTIVATION != 0 )
+    LMIC_startJoining( );
+#else
+    LMIC_setSession( LORAWAN_NET_ID, LORAWAN_DEV_ADDR, NwkSKey, ArtSKey );
+    onSendFrame( NULL );
+#endif
+    // init done - onEvent( ) callback will be invoked...
+}
+
+int main( void )
 {
     osjob_t initjob;
 
@@ -163,27 +244,27 @@
 void onEvent( ev_t ev )
 {
     bool txOn = false;
-
-    DEBUG_EVENT( ev );
+    debug_event( ev );
 
     switch( ev ) 
     {
     // network joined, session established
     case EV_JOINED:
-        DEBUG_VAL( "Net ID = ", LMIC.netid );
+        debug_val( "Net ID = ", LMIC.netid );
         txOn = true;
         break;
     // scheduled data sent (optionally data received)
     case EV_TXCOMPLETE:
+        debug_val( "Datarate = ", LMIC.datarate );
         // Check if we have a downlink on either Rx1 or Rx2 windows
-        if( ( LMIC.txrxFlags & ( TXRX_DNW1 | TXRX_DNW2 ) )!= 0 )
+        if( ( LMIC.txrxFlags & ( TXRX_DNW1 | TXRX_DNW2 ) ) != 0 )
         {
-            DEBUG_VAL( "LED2 = ", 0 );
-            os_setTimedCallback( &rxLedJob, os_getTime( ) + ms2osticks( 15 ), onRxLed );
+            debug_val( "LED2 = ", 1 );
+            os_setTimedCallback( &rxLedJob, os_getTime( ) + ms2osticks( 25 ), onRxLed );
 
-            if( LMIC.dataLen != 0 ) 
+            if( LMIC.dataLen != 0 )
             { // data received in rx slot after tx
-                DEBUG_BUF( LMIC.frame + LMIC.dataBeg, LMIC.dataLen );
+                debug_buf( LMIC.frame + LMIC.dataBeg, LMIC.dataLen );
                 processRxFrame( );
             }
         }
@@ -194,11 +275,12 @@
     }
     if( txOn == true )
     {
-        //os_setTimedCallback( &sendFrameJob, os_getTime( ) + sec2osticks( 5 ), onSendFrame );
-        onSendFrame( NULL );
+        //Sends frame every APP_TX_DUTYCYCLE +/- APP_TX_DUTYCYCLE_RND random time (if not duty cycle limited)
+        os_setTimedCallback( &sendFrameJob,
+                             os_getTime( ) + ms2osticks( APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ) ),
+                             onSendFrame );
         
-        // Blink Tx LED
-        DEBUG_VAL( "LED1 = ", 0 );
-        os_setTimedCallback( &txLedJob, os_getTime( ) + ms2osticks( 25 ), onTxLed );
+        ////Sends frame as soon as possible (duty cylce limitations)
+        //onSendFrame( NULL );
     }
 }