Example implementation of LoraWan specification based on IBM LoraWan in C (ver. 1.5) for Elmo board. Tested only with OTA activation (requires setting AppEui/ DevKey in main.cpp).

Dependencies:   SX1272lib mbed

Committer:
WGorniak
Date:
Wed Oct 07 12:52:36 2015 +0000
Revision:
3:f6bb52cb8d60
Parent:
2:7dc3d0e3de3a
switched from mbed-src to mbed

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WGorniak 0:bb3b0e756578 1 /*
WGorniak 0:bb3b0e756578 2 / _____) _ | |
WGorniak 0:bb3b0e756578 3 ( (____ _____ ____ _| |_ _____ ____| |__
WGorniak 0:bb3b0e756578 4 \____ \| ___ | (_ _) ___ |/ ___) _ \
WGorniak 0:bb3b0e756578 5 _____) ) ____| | | || |_| ____( (___| | | |
WGorniak 0:bb3b0e756578 6 (______/|_____)_|_|_| \__)_____)\____)_| |_|
WGorniak 0:bb3b0e756578 7 (C)2015 Semtech
WGorniak 0:bb3b0e756578 8
WGorniak 0:bb3b0e756578 9 Description: MBED LoRaWAN example application
WGorniak 0:bb3b0e756578 10
WGorniak 0:bb3b0e756578 11 License: Revised BSD License, see LICENSE.TXT file include in the project
WGorniak 0:bb3b0e756578 12
mleksio 2:7dc3d0e3de3a 13 Maintainer: Espotel
WGorniak 0:bb3b0e756578 14 */
WGorniak 0:bb3b0e756578 15 #include "mbed.h"
WGorniak 0:bb3b0e756578 16
WGorniak 0:bb3b0e756578 17 #include "lmic.h"
WGorniak 0:bb3b0e756578 18 #include "debug.h"
WGorniak 0:bb3b0e756578 19
WGorniak 0:bb3b0e756578 20 /*!
WGorniak 0:bb3b0e756578 21 * When set to 1 the application uses the Over-the-Air activation procedure
WGorniak 0:bb3b0e756578 22 * When set to 0 the application uses the Personalization activation procedure
WGorniak 0:bb3b0e756578 23 */
WGorniak 0:bb3b0e756578 24 #define OVER_THE_AIR_ACTIVATION 1 //default 0
WGorniak 0:bb3b0e756578 25
WGorniak 0:bb3b0e756578 26 #if( OVER_THE_AIR_ACTIVATION == 0 )
WGorniak 0:bb3b0e756578 27
WGorniak 0:bb3b0e756578 28 /*!
WGorniak 0:bb3b0e756578 29 * Defines the network ID when using personalization activation procedure
WGorniak 0:bb3b0e756578 30 */
WGorniak 0:bb3b0e756578 31 #define LORAWAN_NET_ID ( uint32_t )0x00000000
WGorniak 0:bb3b0e756578 32
WGorniak 0:bb3b0e756578 33 /*!
WGorniak 0:bb3b0e756578 34 * Defines the device address when using personalization activation procedure
WGorniak 0:bb3b0e756578 35 */
WGorniak 0:bb3b0e756578 36 #define LORAWAN_DEV_ADDR ( uint32_t )0x12341111
WGorniak 0:bb3b0e756578 37 #endif
WGorniak 0:bb3b0e756578 38
WGorniak 0:bb3b0e756578 39 /*!
WGorniak 0:bb3b0e756578 40 * Defines the application data transmission duty cycle
WGorniak 0:bb3b0e756578 41 */
WGorniak 0:bb3b0e756578 42 #define APP_TX_DUTYCYCLE 5000 // 5 [s] value in ms
WGorniak 0:bb3b0e756578 43 #define APP_TX_DUTYCYCLE_RND 1000 // 1 [s] value in ms
WGorniak 0:bb3b0e756578 44
WGorniak 0:bb3b0e756578 45 /*!
WGorniak 0:bb3b0e756578 46 * LoRaWAN Adaptative Data Rate
WGorniak 0:bb3b0e756578 47 */
WGorniak 0:bb3b0e756578 48 #define LORAWAN_ADR_ON 1
WGorniak 0:bb3b0e756578 49
WGorniak 0:bb3b0e756578 50 /*!
WGorniak 0:bb3b0e756578 51 * LoRaWAN confirmed messages
WGorniak 0:bb3b0e756578 52 */
WGorniak 0:bb3b0e756578 53 #define LORAWAN_CONFIRMED_MSG_ON 0
WGorniak 0:bb3b0e756578 54
WGorniak 0:bb3b0e756578 55 /*!
WGorniak 0:bb3b0e756578 56 * LoRaWAN application port
WGorniak 0:bb3b0e756578 57 */
WGorniak 0:bb3b0e756578 58 #define LORAWAN_APP_PORT 1784
WGorniak 0:bb3b0e756578 59
WGorniak 0:bb3b0e756578 60 /*!
WGorniak 0:bb3b0e756578 61 * User application data buffer size
WGorniak 0:bb3b0e756578 62 */
WGorniak 0:bb3b0e756578 63 #if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
WGorniak 0:bb3b0e756578 64 #define LORAWAN_APP_DATA_SIZE 6
WGorniak 0:bb3b0e756578 65
WGorniak 0:bb3b0e756578 66 #else
WGorniak 0:bb3b0e756578 67 #define LORAWAN_APP_DATA_SIZE 1
WGorniak 0:bb3b0e756578 68
WGorniak 0:bb3b0e756578 69 #endif
WGorniak 0:bb3b0e756578 70
WGorniak 0:bb3b0e756578 71
WGorniak 0:bb3b0e756578 72 //
WGorniak 0:bb3b0e756578 73 // For sending data via button
WGorniak 0:bb3b0e756578 74 //
WGorniak 0:bb3b0e756578 75
WGorniak 0:bb3b0e756578 76 InterruptIn button(USER_BUTTON);
WGorniak 0:bb3b0e756578 77 DigitalOut led(LED1);
WGorniak 0:bb3b0e756578 78
WGorniak 0:bb3b0e756578 79 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 80 // CONFIGURATION (FOR APPLICATION CALLBACKS BELOW)
WGorniak 0:bb3b0e756578 81 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 82
WGorniak 0:bb3b0e756578 83 // application router ID (LSBF)
WGorniak 0:bb3b0e756578 84 static const uint8_t AppEui[8] =
WGorniak 0:bb3b0e756578 85 {
WGorniak 0:bb3b0e756578 86 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00
WGorniak 0:bb3b0e756578 87 };
WGorniak 0:bb3b0e756578 88
WGorniak 0:bb3b0e756578 89 // unique device ID (LSBF)
WGorniak 0:bb3b0e756578 90 static const u1_t DevEui[8] =
WGorniak 0:bb3b0e756578 91 {
WGorniak 0:bb3b0e756578 92 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x099, 0xF7
WGorniak 0:bb3b0e756578 93 };
WGorniak 0:bb3b0e756578 94
WGorniak 0:bb3b0e756578 95 // device-specific AES key (derived from device EUI)
WGorniak 0:bb3b0e756578 96 static const uint8_t DevKey[16] =
WGorniak 0:bb3b0e756578 97 {
WGorniak 0:bb3b0e756578 98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
WGorniak 0:bb3b0e756578 99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
WGorniak 0:bb3b0e756578 100 };
WGorniak 0:bb3b0e756578 101
WGorniak 0:bb3b0e756578 102
WGorniak 0:bb3b0e756578 103
WGorniak 0:bb3b0e756578 104 #if( OVER_THE_AIR_ACTIVATION == 0 )
WGorniak 0:bb3b0e756578 105 // network session key
WGorniak 0:bb3b0e756578 106 static uint8_t NwkSKey[] =
WGorniak 0:bb3b0e756578 107 {
WGorniak 0:bb3b0e756578 108 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
WGorniak 0:bb3b0e756578 109 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
WGorniak 0:bb3b0e756578 110 };
WGorniak 0:bb3b0e756578 111
WGorniak 0:bb3b0e756578 112 // application session key
WGorniak 0:bb3b0e756578 113 static uint8_t ArtSKey[] =
WGorniak 0:bb3b0e756578 114 {
WGorniak 0:bb3b0e756578 115 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6,
WGorniak 0:bb3b0e756578 116 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C
WGorniak 0:bb3b0e756578 117 };
WGorniak 0:bb3b0e756578 118
WGorniak 0:bb3b0e756578 119 #endif
WGorniak 0:bb3b0e756578 120
WGorniak 0:bb3b0e756578 121 // LEDs and Frame jobs
WGorniak 0:bb3b0e756578 122 osjob_t rxLedJob;
WGorniak 0:bb3b0e756578 123 osjob_t txLedJob;
WGorniak 0:bb3b0e756578 124 osjob_t sendFrameJob;
WGorniak 0:bb3b0e756578 125
WGorniak 0:bb3b0e756578 126 // LED state
WGorniak 0:bb3b0e756578 127 static bool AppLedStateOn = false;
WGorniak 0:bb3b0e756578 128
WGorniak 0:bb3b0e756578 129 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 130 // Utility functions
WGorniak 0:bb3b0e756578 131 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 132 /*!
WGorniak 0:bb3b0e756578 133 * \brief Computes a random number between min and max
WGorniak 0:bb3b0e756578 134 *
WGorniak 0:bb3b0e756578 135 * \param [IN] min range minimum value
WGorniak 0:bb3b0e756578 136 * \param [IN] max range maximum value
WGorniak 0:bb3b0e756578 137 * \retval random random value in range min..max
WGorniak 0:bb3b0e756578 138 */
WGorniak 0:bb3b0e756578 139 int32_t randr( int32_t min, int32_t max )
WGorniak 0:bb3b0e756578 140 {
WGorniak 0:bb3b0e756578 141 return ( int32_t )rand( ) % ( max - min + 1 ) + min;
WGorniak 0:bb3b0e756578 142 }
WGorniak 0:bb3b0e756578 143
WGorniak 0:bb3b0e756578 144 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 145 // APPLICATION CALLBACKS
WGorniak 0:bb3b0e756578 146 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 147
WGorniak 0:bb3b0e756578 148 // provide application router ID (8 bytes, LSBF)
WGorniak 0:bb3b0e756578 149 void os_getArtEui( uint8_t *buf )
WGorniak 0:bb3b0e756578 150 {
WGorniak 0:bb3b0e756578 151 memcpy( buf, AppEui, 8 );
WGorniak 0:bb3b0e756578 152 }
WGorniak 0:bb3b0e756578 153
WGorniak 0:bb3b0e756578 154 // provide device ID (8 bytes, LSBF)
WGorniak 0:bb3b0e756578 155 void os_getDevEui( uint8_t *buf )
WGorniak 0:bb3b0e756578 156 {
WGorniak 0:bb3b0e756578 157 memcpy( buf, DevEui, 8 );
WGorniak 0:bb3b0e756578 158 }
WGorniak 0:bb3b0e756578 159
WGorniak 0:bb3b0e756578 160 // provide device key (16 bytes)
WGorniak 0:bb3b0e756578 161 void os_getDevKey( uint8_t *buf )
WGorniak 0:bb3b0e756578 162 {
WGorniak 0:bb3b0e756578 163 memcpy( buf, DevKey, 16 );
WGorniak 0:bb3b0e756578 164 }
WGorniak 0:bb3b0e756578 165
WGorniak 0:bb3b0e756578 166 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 167 // MAIN - INITIALIZATION AND STARTUP
WGorniak 0:bb3b0e756578 168 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 169
WGorniak 0:bb3b0e756578 170 static void onRxLed( osjob_t* j )
WGorniak 0:bb3b0e756578 171 {
WGorniak 0:bb3b0e756578 172 debug_val((const unsigned char*)"LED2 = ", 0 );
WGorniak 0:bb3b0e756578 173 }
WGorniak 0:bb3b0e756578 174
WGorniak 0:bb3b0e756578 175 static void onTxLed( osjob_t* j )
WGorniak 0:bb3b0e756578 176 {
WGorniak 0:bb3b0e756578 177 debug_val((const unsigned char*)"LED1 = ", 0 );
WGorniak 0:bb3b0e756578 178 }
WGorniak 0:bb3b0e756578 179
WGorniak 0:bb3b0e756578 180 static void prepareTxFrame( void )
WGorniak 0:bb3b0e756578 181 {
WGorniak 0:bb3b0e756578 182 LMIC.frame[0] = AppLedStateOn;
WGorniak 0:bb3b0e756578 183 #if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
WGorniak 0:bb3b0e756578 184 LMIC.frame[1] = LMIC.seqnoDn >> 8;
WGorniak 0:bb3b0e756578 185 LMIC.frame[2] = LMIC.seqnoDn;
WGorniak 0:bb3b0e756578 186 LMIC.frame[3] = LMIC.rssi >> 8;
WGorniak 0:bb3b0e756578 187 LMIC.frame[4] = LMIC.rssi;
WGorniak 0:bb3b0e756578 188 LMIC.frame[5] = LMIC.snr;
WGorniak 0:bb3b0e756578 189 #endif
WGorniak 0:bb3b0e756578 190 }
WGorniak 0:bb3b0e756578 191
WGorniak 0:bb3b0e756578 192 void processRxFrame( void )
WGorniak 0:bb3b0e756578 193 {
WGorniak 0:bb3b0e756578 194 printf("processRxFrame\r\n");
WGorniak 0:bb3b0e756578 195 switch( LMIC.frame[LMIC.dataBeg - 1] ) // Check Rx port number
WGorniak 0:bb3b0e756578 196 {
WGorniak 0:bb3b0e756578 197 case 1: // The application LED can be controlled on port 1 or 2
WGorniak 0:bb3b0e756578 198 case 2:
WGorniak 0:bb3b0e756578 199 if( LMIC.dataLen == 1 )
WGorniak 0:bb3b0e756578 200 {
WGorniak 0:bb3b0e756578 201 AppLedStateOn = LMIC.frame[LMIC.dataBeg] & 0x01;
WGorniak 0:bb3b0e756578 202 debug_val( (const unsigned char*)"LED3 = ", AppLedStateOn );
WGorniak 0:bb3b0e756578 203 }
WGorniak 0:bb3b0e756578 204 break;
WGorniak 0:bb3b0e756578 205 default:
WGorniak 0:bb3b0e756578 206 break;
WGorniak 0:bb3b0e756578 207 }
WGorniak 0:bb3b0e756578 208 }
WGorniak 0:bb3b0e756578 209
WGorniak 0:bb3b0e756578 210 void onSendFrame( osjob_t* j )
WGorniak 0:bb3b0e756578 211 {
WGorniak 0:bb3b0e756578 212 prepareTxFrame( );
WGorniak 0:bb3b0e756578 213 LMIC_setTxData2( LORAWAN_APP_PORT, LMIC.frame, LORAWAN_APP_DATA_SIZE, LORAWAN_CONFIRMED_MSG_ON );
WGorniak 0:bb3b0e756578 214 }
WGorniak 0:bb3b0e756578 215
WGorniak 0:bb3b0e756578 216 // Initialization job
WGorniak 0:bb3b0e756578 217 static void onInit( osjob_t* j )
WGorniak 0:bb3b0e756578 218 {
WGorniak 0:bb3b0e756578 219 // reset MAC state
WGorniak 0:bb3b0e756578 220 LMIC_reset( );
WGorniak 0:bb3b0e756578 221 LMIC_setAdrMode( LORAWAN_ADR_ON );
WGorniak 0:bb3b0e756578 222 LMIC_setDrTxpow( DR_SF12, 14 );
WGorniak 0:bb3b0e756578 223
WGorniak 0:bb3b0e756578 224 // start joining
WGorniak 0:bb3b0e756578 225 #if( OVER_THE_AIR_ACTIVATION != 0 )
WGorniak 0:bb3b0e756578 226 LMIC_startJoining( );
WGorniak 0:bb3b0e756578 227 #else
WGorniak 0:bb3b0e756578 228 LMIC_setSession( LORAWAN_NET_ID, LORAWAN_DEV_ADDR, NwkSKey, ArtSKey );
WGorniak 0:bb3b0e756578 229 onSendFrame( NULL );
WGorniak 0:bb3b0e756578 230 #endif
WGorniak 0:bb3b0e756578 231 // init done - onEvent( ) callback will be invoked...
WGorniak 0:bb3b0e756578 232 }
WGorniak 0:bb3b0e756578 233
WGorniak 0:bb3b0e756578 234
WGorniak 0:bb3b0e756578 235 int main( void )
WGorniak 0:bb3b0e756578 236 {
WGorniak 0:bb3b0e756578 237 osjob_t initjob;
WGorniak 0:bb3b0e756578 238 // initialize runtime env
WGorniak 0:bb3b0e756578 239 os_init( );
WGorniak 0:bb3b0e756578 240 // setup initial job
WGorniak 0:bb3b0e756578 241 os_setCallback( &initjob, onInit );
WGorniak 0:bb3b0e756578 242
WGorniak 0:bb3b0e756578 243 // execute scheduled jobs and events
WGorniak 0:bb3b0e756578 244 os_runloop( );
WGorniak 0:bb3b0e756578 245 // (not reached)
WGorniak 0:bb3b0e756578 246 }
WGorniak 0:bb3b0e756578 247
WGorniak 0:bb3b0e756578 248 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 249 // LMIC EVENT CALLBACK
WGorniak 0:bb3b0e756578 250 //////////////////////////////////////////////////
WGorniak 0:bb3b0e756578 251 void onEvent( ev_t ev )
WGorniak 0:bb3b0e756578 252 {
WGorniak 0:bb3b0e756578 253 bool txOn = false;
WGorniak 0:bb3b0e756578 254 debug_event( ev );
WGorniak 0:bb3b0e756578 255
WGorniak 0:bb3b0e756578 256 switch( ev )
WGorniak 0:bb3b0e756578 257 {
WGorniak 0:bb3b0e756578 258 // network joined, session established
WGorniak 0:bb3b0e756578 259 case EV_JOINED:
WGorniak 0:bb3b0e756578 260 debug_val( (const unsigned char*)"Net ID = ", LMIC.netid );
WGorniak 0:bb3b0e756578 261 txOn = true;
WGorniak 0:bb3b0e756578 262 break;
WGorniak 0:bb3b0e756578 263 // scheduled data sent (optionally data received)
WGorniak 0:bb3b0e756578 264 case EV_TXCOMPLETE:
WGorniak 0:bb3b0e756578 265 debug_val( (const unsigned char*)"Datarate = ", LMIC.datarate );
WGorniak 0:bb3b0e756578 266 // Check if we have a downlink on either Rx1 or Rx2 windows
WGorniak 0:bb3b0e756578 267 if( ( LMIC.txrxFlags & ( TXRX_DNW1 | TXRX_DNW2 ) ) != 0 )
WGorniak 0:bb3b0e756578 268 {
WGorniak 0:bb3b0e756578 269 debug_val( (const unsigned char*)"LED2 = ", 1 );
WGorniak 0:bb3b0e756578 270 os_setTimedCallback( &rxLedJob, os_getTime( ) + ms2osticks( 25 ), onRxLed );
WGorniak 0:bb3b0e756578 271
WGorniak 0:bb3b0e756578 272 if( LMIC.dataLen != 0 )
WGorniak 0:bb3b0e756578 273 { // data received in rx slot after tx
WGorniak 0:bb3b0e756578 274 printf("data received in rx slot after tx: \r\n");
WGorniak 0:bb3b0e756578 275 debug_buf( LMIC.frame + LMIC.dataBeg, LMIC.dataLen );
WGorniak 0:bb3b0e756578 276 processRxFrame( );
WGorniak 0:bb3b0e756578 277 }
WGorniak 0:bb3b0e756578 278 }
WGorniak 0:bb3b0e756578 279 txOn = true;
WGorniak 0:bb3b0e756578 280 break;
WGorniak 0:bb3b0e756578 281 default:
WGorniak 0:bb3b0e756578 282 break;
WGorniak 0:bb3b0e756578 283 }
WGorniak 0:bb3b0e756578 284 if( txOn == true )
WGorniak 0:bb3b0e756578 285 {
WGorniak 0:bb3b0e756578 286 //Sends frame every APP_TX_DUTYCYCLE +/- APP_TX_DUTYCYCLE_RND random time (if not duty cycle limited)
WGorniak 0:bb3b0e756578 287 os_setTimedCallback( &sendFrameJob,
WGorniak 0:bb3b0e756578 288 os_getTime( ) + ms2osticks( APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ) ),
WGorniak 0:bb3b0e756578 289 onSendFrame );
WGorniak 0:bb3b0e756578 290 // //Sends frame as soon as possible (duty cylce limitations)
WGorniak 0:bb3b0e756578 291 // onSendFrame( NULL );
WGorniak 0:bb3b0e756578 292 }
WGorniak 0:bb3b0e756578 293 }