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

Dependencies:   LMiC SX1276Lib mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* 
00002  / _____)             _              | |
00003 ( (____  _____ ____ _| |_ _____  ____| |__
00004  \____ \| ___ |    (_   _) ___ |/ ___)  _ \
00005  _____) ) ____| | | || |_| ____( (___| | | |
00006 (______/|_____)_|_|_| \__)_____)\____)_| |_|
00007     (C)2015 Semtech
00008 
00009 Description: MBED LoRaWAN example application
00010 
00011 License: Revised BSD License, see LICENSE.TXT file include in the project
00012 
00013 Maintainer: Miguel Luis and Gregory Cristian
00014 */
00015 #include "mbed.h"
00016 
00017 #include "lmic.h"
00018 #include "debug.h"
00019 
00020 /*!
00021  * When set to 1 the application uses the Over-the-Air activation procedure
00022  * When set to 0 the application uses the Personalization activation procedure
00023  */
00024 #define OVER_THE_AIR_ACTIVATION                     0
00025 
00026 #if( OVER_THE_AIR_ACTIVATION == 0 )
00027 
00028 /*!
00029  * Defines the network ID when using personalization activation procedure
00030  */
00031 #define LORAWAN_NET_ID                              ( uint32_t )0x00000000
00032 
00033 /*!
00034  * Defines the device address when using personalization activation procedure
00035  */
00036 #define LORAWAN_DEV_ADDR                            ( uint32_t )0x12345678
00037 
00038 #endif
00039 
00040 /*!
00041  * Defines the application data transmission duty cycle
00042  */
00043 #define APP_TX_DUTYCYCLE                            5000 // 5 [s] value in ms
00044 #define APP_TX_DUTYCYCLE_RND                        1000 // 1 [s] value in ms
00045 
00046 /*!
00047  * LoRaWAN Adaptative Data Rate
00048  */
00049 #define LORAWAN_ADR_ON                              1
00050 
00051 /*!
00052  * LoRaWAN confirmed messages
00053  */
00054 #define LORAWAN_CONFIRMED_MSG_ON                    1
00055 
00056 /*!
00057  * LoRaWAN application port
00058  */
00059 #define LORAWAN_APP_PORT                            15
00060 
00061 /*!
00062  * User application data buffer size
00063  */
00064 #if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
00065 #define LORAWAN_APP_DATA_SIZE                       6
00066 
00067 #else
00068 #define LORAWAN_APP_DATA_SIZE                       1
00069 
00070 #endif
00071 
00072 //////////////////////////////////////////////////
00073 // CONFIGURATION (FOR APPLICATION CALLBACKS BELOW)
00074 //////////////////////////////////////////////////
00075 
00076 // application router ID (LSBF)
00077 static const uint8_t AppEui[8] =
00078 {
00079     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
00080 };
00081 
00082 // unique device ID (LSBF)
00083 static const u1_t DevEui[8] =
00084 {
00085     0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
00086 };
00087 
00088 // device-specific AES key (derived from device EUI)
00089 static const uint8_t DevKey[16] = 
00090 {
00091     0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
00092     0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
00093 };
00094 
00095 #if( OVER_THE_AIR_ACTIVATION == 0 )
00096 // network session key
00097 static uint8_t NwkSKey[] = 
00098 { 
00099     0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
00100     0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
00101 };
00102 
00103 // application session key
00104 static uint8_t ArtSKey[] = 
00105 { 
00106     0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
00107     0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
00108 };
00109 
00110 #endif
00111 
00112 // LEDs and Frame jobs
00113 osjob_t rxLedJob;
00114 osjob_t txLedJob;
00115 osjob_t sendFrameJob;
00116 
00117 // LED state
00118 static bool AppLedStateOn = false;
00119 
00120 //////////////////////////////////////////////////
00121 // Utility functions
00122 //////////////////////////////////////////////////
00123 /*!
00124  * \brief Computes a random number between min and max
00125  *
00126  * \param [IN] min range minimum value
00127  * \param [IN] max range maximum value
00128  * \retval random random value in range min..max
00129  */
00130 int32_t randr( int32_t min, int32_t max )
00131 {
00132     return ( int32_t )rand( ) % ( max - min + 1 ) + min;
00133 }
00134 
00135 //////////////////////////////////////////////////
00136 // APPLICATION CALLBACKS
00137 //////////////////////////////////////////////////
00138 
00139 // provide application router ID (8 bytes, LSBF)
00140 void os_getArtEui( uint8_t *buf )
00141 {
00142     memcpy( buf, AppEui, 8 );
00143 }
00144 
00145 // provide device ID (8 bytes, LSBF)
00146 void os_getDevEui( uint8_t *buf )
00147 {
00148     memcpy( buf, DevEui, 8 );
00149 }
00150 
00151 // provide device key (16 bytes)
00152 void os_getDevKey( uint8_t *buf )
00153 {
00154     memcpy( buf, DevKey, 16 );
00155 }
00156 
00157 //////////////////////////////////////////////////
00158 // MAIN - INITIALIZATION AND STARTUP
00159 //////////////////////////////////////////////////
00160 
00161 static void onRxLed( osjob_t* j )
00162 {
00163     debug_val("LED2 = ", 0 );
00164 }
00165 
00166 static void onTxLed( osjob_t* j )
00167 {
00168     debug_val("LED1 = ", 0 );
00169 }
00170 
00171 static void prepareTxFrame( void )
00172 {
00173     LMIC.frame[0] = AppLedStateOn;
00174 #if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
00175     LMIC.frame[1] = LMIC.seqnoDn >> 8;
00176     LMIC.frame[2] = LMIC.seqnoDn;
00177     LMIC.frame[3] = LMIC.rssi >> 8;
00178     LMIC.frame[4] = LMIC.rssi;
00179     LMIC.frame[5] = LMIC.snr;
00180 #endif    
00181 }
00182 
00183 void processRxFrame( void )
00184 {
00185     switch( LMIC.frame[LMIC.dataBeg - 1] ) // Check Rx port number
00186     {
00187         case 1: // The application LED can be controlled on port 1 or 2
00188         case 2:
00189             if( LMIC.dataLen == 1 )
00190             {
00191                 AppLedStateOn = LMIC.frame[LMIC.dataBeg] & 0x01;
00192                 debug_val( "LED3 = ", AppLedStateOn );
00193             }
00194             break;
00195         default:
00196             break;
00197     }
00198 }
00199 
00200 static void onSendFrame( osjob_t* j )
00201 {
00202     prepareTxFrame( );
00203     LMIC_setTxData2( LORAWAN_APP_PORT, LMIC.frame, LORAWAN_APP_DATA_SIZE, LORAWAN_CONFIRMED_MSG_ON );
00204 
00205     // Blink Tx LED
00206     debug_val( "LED1 = ", 1 );
00207     os_setTimedCallback( &txLedJob, os_getTime( ) + ms2osticks( 25 ), onTxLed );
00208 }
00209 
00210 // Initialization job
00211 static void onInit( osjob_t* j )
00212 {
00213     // reset MAC state
00214     LMIC_reset( );
00215     LMIC_setAdrMode( LORAWAN_ADR_ON );
00216 #if defined(CFG_eu868)
00217     LMIC_setDrTxpow( DR_SF12, 14 );
00218 #elif defined(CFG_us915)    
00219     LMIC_setDrTxpow( DR_SF10, 14 );
00220 #endif
00221 
00222     // start joining
00223 #if( OVER_THE_AIR_ACTIVATION != 0 )
00224     LMIC_startJoining( );
00225 #else
00226     LMIC_setSession( LORAWAN_NET_ID, LORAWAN_DEV_ADDR, NwkSKey, ArtSKey );
00227     onSendFrame( NULL );
00228 #endif
00229     // init done - onEvent( ) callback will be invoked...
00230 }
00231 
00232 int main( void )
00233 {
00234     osjob_t initjob;
00235 
00236     // initialize runtime env
00237     os_init( );
00238     // setup initial job
00239     os_setCallback( &initjob, onInit );
00240     // execute scheduled jobs and events
00241     os_runloop( );
00242     // (not reached)
00243 }
00244 
00245 //////////////////////////////////////////////////
00246 // LMIC EVENT CALLBACK
00247 //////////////////////////////////////////////////
00248 void onEvent( ev_t ev )
00249 {
00250     bool txOn = false;
00251     debug_event( ev );
00252 
00253     switch( ev ) 
00254     {
00255     // network joined, session established
00256     case EV_JOINED:
00257         debug_val( "Net ID = ", LMIC.netid );
00258         txOn = true;
00259         break;
00260     // scheduled data sent (optionally data received)
00261     case EV_TXCOMPLETE:
00262         debug_val( "Datarate = ", LMIC.datarate );
00263         // Check if we have a downlink on either Rx1 or Rx2 windows
00264         if( ( LMIC.txrxFlags & ( TXRX_DNW1 | TXRX_DNW2 ) ) != 0 )
00265         {
00266             debug_val( "LED2 = ", 1 );
00267             os_setTimedCallback( &rxLedJob, os_getTime( ) + ms2osticks( 25 ), onRxLed );
00268 
00269             if( LMIC.dataLen != 0 )
00270             { // data received in rx slot after tx
00271                 debug_buf( LMIC.frame + LMIC.dataBeg, LMIC.dataLen );
00272                 processRxFrame( );
00273             }
00274         }
00275         txOn = true;
00276         break;
00277     default:
00278         break;
00279     }
00280     if( txOn == true )
00281     {
00282         //Sends frame every APP_TX_DUTYCYCLE +/- APP_TX_DUTYCYCLE_RND random time (if not duty cycle limited)
00283         os_setTimedCallback( &sendFrameJob,
00284                              os_getTime( ) + ms2osticks( APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ) ),
00285                              onSendFrame );
00286         
00287         ////Sends frame as soon as possible (duty cylce limitations)
00288         //onSendFrame( NULL );
00289     }
00290 }