Application example using LoRaWAN-lib MAC layer implementation

Dependencies:   mbed LoRaWAN-lib SX1276Lib

Fork of LoRaWAN-demo-76 by Semtech

Committer:
mluis
Date:
Thu Nov 26 12:59:52 2015 +0000
Revision:
1:352f608c3337
Parent:
0:92bca02df485
Child:
3:9c6f7f082151
Changed application architecture to mimic GitHub LoRaMac-node repository.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mluis 0:92bca02df485 1 /*
mluis 0:92bca02df485 2 / _____) _ | |
mluis 0:92bca02df485 3 ( (____ _____ ____ _| |_ _____ ____| |__
mluis 0:92bca02df485 4 \____ \| ___ | (_ _) ___ |/ ___) _ \
mluis 0:92bca02df485 5 _____) ) ____| | | || |_| ____( (___| | | |
mluis 0:92bca02df485 6 (______/|_____)_|_|_| \__)_____)\____)_| |_|
mluis 0:92bca02df485 7 (C)2015 Semtech
mluis 0:92bca02df485 8
mluis 0:92bca02df485 9 Description: LoRaMac classA device implementation
mluis 0:92bca02df485 10
mluis 0:92bca02df485 11 License: Revised BSD License, see LICENSE.TXT file include in the project
mluis 0:92bca02df485 12
mluis 0:92bca02df485 13 Maintainer: Miguel Luis and Gregory Cristian
mluis 0:92bca02df485 14 */
mluis 0:92bca02df485 15 #include "mbed.h"
mluis 0:92bca02df485 16 #include "board.h"
mluis 0:92bca02df485 17 #include "radio.h"
mluis 0:92bca02df485 18
mluis 0:92bca02df485 19 #include "LoRaMac.h"
mluis 0:92bca02df485 20
mluis 0:92bca02df485 21 #include "SerialDisplay.h"
mluis 0:92bca02df485 22
mluis 0:92bca02df485 23 /*!
mluis 0:92bca02df485 24 * When set to 1 the application uses the Over-the-Air activation procedure
mluis 0:92bca02df485 25 * When set to 0 the application uses the Personalization activation procedure
mluis 0:92bca02df485 26 */
mluis 0:92bca02df485 27 #define OVER_THE_AIR_ACTIVATION 1
mluis 0:92bca02df485 28
mluis 0:92bca02df485 29 /*!
mluis 0:92bca02df485 30 * Indicates if the end-device is to be connected to a private or public network
mluis 0:92bca02df485 31 */
mluis 0:92bca02df485 32 #define LORAWAN_PUBLIC_NETWORK true
mluis 0:92bca02df485 33
mluis 0:92bca02df485 34 #if( OVER_THE_AIR_ACTIVATION != 0 )
mluis 0:92bca02df485 35
mluis 0:92bca02df485 36 /*!
mluis 0:92bca02df485 37 * Join requests trials duty cycle.
mluis 0:92bca02df485 38 */
mluis 0:92bca02df485 39 #define OVER_THE_AIR_ACTIVATION_DUTYCYCLE 10000000 // 10 [s] value in us
mluis 0:92bca02df485 40
mluis 0:92bca02df485 41 /*!
mluis 0:92bca02df485 42 * Mote device IEEE EUI
mluis 0:92bca02df485 43 *
mluis 0:92bca02df485 44 * \remark must be written as a little endian value (reverse order of normal reading)
mluis 0:92bca02df485 45 */
mluis 1:352f608c3337 46 #define LORAWAN_DEVICE_EUI { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }
mluis 0:92bca02df485 47
mluis 0:92bca02df485 48 /*!
mluis 0:92bca02df485 49 * Application IEEE EUI
mluis 0:92bca02df485 50 *
mluis 0:92bca02df485 51 * \remark must be written as a little endian value (reverse order of normal reading)
mluis 0:92bca02df485 52 */
mluis 1:352f608c3337 53 #define LORAWAN_APPLICATION_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
mluis 0:92bca02df485 54
mluis 0:92bca02df485 55 /*!
mluis 0:92bca02df485 56 * AES encryption/decryption cipher application key
mluis 0:92bca02df485 57 */
mluis 1:352f608c3337 58 #define LORAWAN_APPLICATION_KEY { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01 }
mluis 0:92bca02df485 59
mluis 0:92bca02df485 60 #else
mluis 0:92bca02df485 61
mluis 0:92bca02df485 62 /*!
mluis 0:92bca02df485 63 * Current network ID
mluis 0:92bca02df485 64 */
mluis 0:92bca02df485 65 #define LORAWAN_NETWORK_ID ( uint32_t )0
mluis 0:92bca02df485 66
mluis 0:92bca02df485 67 /*!
mluis 0:92bca02df485 68 * Device address on the network
mluis 0:92bca02df485 69 *
mluis 0:92bca02df485 70 * \remark must be written as a big endian value (normal reading order)
mluis 0:92bca02df485 71 */
mluis 1:352f608c3337 72 #define LORAWAN_DEVICE_ADDRESS ( uint32_t )0xFF000001
mluis 0:92bca02df485 73
mluis 0:92bca02df485 74 /*!
mluis 0:92bca02df485 75 * AES encryption/decryption cipher network session key
mluis 0:92bca02df485 76 */
mluis 1:352f608c3337 77 #define LORAWAN_NWKSKEY { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01 }
mluis 0:92bca02df485 78
mluis 0:92bca02df485 79 /*!
mluis 0:92bca02df485 80 * AES encryption/decryption cipher application session key
mluis 0:92bca02df485 81 */
mluis 1:352f608c3337 82 #define LORAWAN_APPSKEY { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01 }
mluis 0:92bca02df485 83
mluis 0:92bca02df485 84 #endif
mluis 0:92bca02df485 85
mluis 0:92bca02df485 86 /*!
mluis 1:352f608c3337 87 * Defines the application data transmission duty cycle. 5s, value in [us].
mluis 0:92bca02df485 88 */
mluis 1:352f608c3337 89 #define APP_TX_DUTYCYCLE 5000000
mluis 1:352f608c3337 90
mluis 1:352f608c3337 91 /*!
mluis 1:352f608c3337 92 * Defines a random delay for application data transmission duty cycle. 1s,
mluis 1:352f608c3337 93 * value in [us].
mluis 1:352f608c3337 94 */
mluis 1:352f608c3337 95 #define APP_TX_DUTYCYCLE_RND 1000000
mluis 0:92bca02df485 96
mluis 0:92bca02df485 97 /*!
mluis 0:92bca02df485 98 * LoRaWAN confirmed messages
mluis 0:92bca02df485 99 */
mluis 0:92bca02df485 100 #define LORAWAN_CONFIRMED_MSG_ON true
mluis 0:92bca02df485 101
mluis 0:92bca02df485 102 /*!
mluis 0:92bca02df485 103 * LoRaWAN Adaptative Data Rate
mluis 0:92bca02df485 104 *
mluis 0:92bca02df485 105 * \remark Please note that when ADR is enabled the end-device should be static
mluis 0:92bca02df485 106 */
mluis 0:92bca02df485 107 #define LORAWAN_ADR_ON 1
mluis 0:92bca02df485 108
mluis 1:352f608c3337 109 #if defined( USE_BAND_868 )
mluis 1:352f608c3337 110
mluis 0:92bca02df485 111 /*!
mluis 0:92bca02df485 112 * LoRaWAN ETSI duty cycle control enable/disable
mluis 0:92bca02df485 113 *
mluis 0:92bca02df485 114 * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes
mluis 0:92bca02df485 115 */
mluis 0:92bca02df485 116 #define LORAWAN_DUTYCYCLE_ON false
mluis 0:92bca02df485 117
mluis 1:352f608c3337 118 #endif
mluis 1:352f608c3337 119
mluis 0:92bca02df485 120 /*!
mluis 0:92bca02df485 121 * LoRaWAN application port
mluis 0:92bca02df485 122 */
mluis 0:92bca02df485 123 #define LORAWAN_APP_PORT 15
mluis 0:92bca02df485 124
mluis 0:92bca02df485 125 /*!
mluis 0:92bca02df485 126 * User application data buffer size
mluis 0:92bca02df485 127 */
mluis 0:92bca02df485 128 #if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
mluis 0:92bca02df485 129 #define LORAWAN_APP_DATA_SIZE 6
mluis 0:92bca02df485 130
mluis 0:92bca02df485 131 #else
mluis 0:92bca02df485 132 #define LORAWAN_APP_DATA_SIZE 1
mluis 0:92bca02df485 133
mluis 0:92bca02df485 134 #endif
mluis 0:92bca02df485 135
mluis 0:92bca02df485 136 #if( OVER_THE_AIR_ACTIVATION != 0 )
mluis 0:92bca02df485 137
mluis 0:92bca02df485 138 static uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
mluis 0:92bca02df485 139 static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI;
mluis 0:92bca02df485 140 static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY;
mluis 0:92bca02df485 141
mluis 0:92bca02df485 142 #else
mluis 0:92bca02df485 143
mluis 0:92bca02df485 144 static uint8_t NwkSKey[] = LORAWAN_NWKSKEY;
mluis 0:92bca02df485 145 static uint8_t AppSKey[] = LORAWAN_APPSKEY;
mluis 0:92bca02df485 146
mluis 0:92bca02df485 147 #endif
mluis 0:92bca02df485 148
mluis 0:92bca02df485 149 /*!
mluis 0:92bca02df485 150 * Indicates if the MAC layer has already joined a network.
mluis 0:92bca02df485 151 */
mluis 0:92bca02df485 152 static bool IsNetworkJoined = false;
mluis 0:92bca02df485 153 static bool IsNetworkJoinedStatusUpdate = false;
mluis 0:92bca02df485 154
mluis 0:92bca02df485 155 /*!
mluis 1:352f608c3337 156 * Application port
mluis 1:352f608c3337 157 */
mluis 1:352f608c3337 158 static uint8_t AppPort = LORAWAN_APP_PORT;
mluis 1:352f608c3337 159
mluis 1:352f608c3337 160 /*!
mluis 1:352f608c3337 161 * User application data size
mluis 1:352f608c3337 162 */
mluis 1:352f608c3337 163 static uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE;
mluis 1:352f608c3337 164
mluis 1:352f608c3337 165 /*!
mluis 1:352f608c3337 166 * User application data buffer size
mluis 1:352f608c3337 167 */
mluis 1:352f608c3337 168 #define LORAWAN_APP_DATA_MAX_SIZE 64
mluis 1:352f608c3337 169
mluis 1:352f608c3337 170 /*!
mluis 0:92bca02df485 171 * User application data
mluis 0:92bca02df485 172 */
mluis 1:352f608c3337 173 static uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE];
mluis 1:352f608c3337 174
mluis 1:352f608c3337 175 /*!
mluis 1:352f608c3337 176 * Indicates if the node is sending confirmed or unconfirmed messages
mluis 1:352f608c3337 177 */
mluis 1:352f608c3337 178 static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
mluis 0:92bca02df485 179
mluis 0:92bca02df485 180 /*!
mluis 0:92bca02df485 181 * Defines the application data transmission duty cycle
mluis 0:92bca02df485 182 */
mluis 0:92bca02df485 183 static uint32_t TxDutyCycleTime;
mluis 0:92bca02df485 184
mluis 1:352f608c3337 185 /*!
mluis 1:352f608c3337 186 * Timer to handle the application data transmission duty cycle
mluis 1:352f608c3337 187 */
mluis 1:352f608c3337 188 static TimerEvent_t TxNextPacketTimer;
mluis 0:92bca02df485 189
mluis 0:92bca02df485 190 #if( OVER_THE_AIR_ACTIVATION != 0 )
mluis 0:92bca02df485 191
mluis 0:92bca02df485 192 /*!
mluis 0:92bca02df485 193 * Defines the join request timer
mluis 0:92bca02df485 194 */
mluis 1:352f608c3337 195 static TimerEvent_t JoinReqTimer;
mluis 0:92bca02df485 196
mluis 0:92bca02df485 197 #endif
mluis 0:92bca02df485 198
mluis 0:92bca02df485 199 /*!
mluis 0:92bca02df485 200 * Indicates if a new packet can be sent
mluis 0:92bca02df485 201 */
mluis 0:92bca02df485 202 static bool TxNextPacket = true;
mluis 0:92bca02df485 203 static bool ScheduleNextTx = false;
mluis 0:92bca02df485 204
mluis 1:352f608c3337 205 static LoRaMacCallbacks_t LoRaMacCallbacks;
mluis 0:92bca02df485 206
mluis 1:352f608c3337 207 static TimerEvent_t Led1Timer;
mluis 1:352f608c3337 208 volatile bool Led1State = false;
mluis 0:92bca02df485 209 volatile bool Led1StateChanged = false;
mluis 0:92bca02df485 210
mluis 1:352f608c3337 211 static TimerEvent_t Led2Timer;
mluis 1:352f608c3337 212 volatile bool Led2State = false;
mluis 0:92bca02df485 213 volatile bool Led2StateChanged = false;
mluis 0:92bca02df485 214
mluis 1:352f608c3337 215 static bool AppLedStateOn = false;
mluis 0:92bca02df485 216 volatile bool Led3StateChanged = false;
mluis 0:92bca02df485 217
mluis 0:92bca02df485 218 volatile bool LinkStatusUpdated = false;
mluis 0:92bca02df485 219
mluis 1:352f608c3337 220 static bool ComplianceTestOn = false;
mluis 1:352f608c3337 221 static uint8_t ComplianceTestState = 0;
mluis 1:352f608c3337 222 static uint16_t ComplianceTestDownLinkCounter = 0;
mluis 1:352f608c3337 223 static bool ComplianceTestLinkCheck = false;
mluis 1:352f608c3337 224 static uint8_t ComplianceTestDemodMargin = 0;
mluis 1:352f608c3337 225 static uint8_t ComplianceTestNbGateways = 0;
mluis 1:352f608c3337 226
mluis 0:92bca02df485 227 struct sLoRaMacUplinkStatus
mluis 0:92bca02df485 228 {
mluis 0:92bca02df485 229 uint8_t Acked;
mluis 0:92bca02df485 230 int8_t Datarate;
mluis 0:92bca02df485 231 uint16_t UplinkCounter;
mluis 0:92bca02df485 232 uint8_t Port;
mluis 0:92bca02df485 233 uint8_t *Buffer;
mluis 0:92bca02df485 234 uint8_t BufferSize;
mluis 0:92bca02df485 235 }LoRaMacUplinkStatus;
mluis 0:92bca02df485 236
mluis 0:92bca02df485 237 struct sLoRaMacDownlinkStatus
mluis 0:92bca02df485 238 {
mluis 0:92bca02df485 239 int16_t Rssi;
mluis 0:92bca02df485 240 int8_t Snr;
mluis 0:92bca02df485 241 uint16_t DownlinkCounter;
mluis 0:92bca02df485 242 bool RxData;
mluis 0:92bca02df485 243 uint8_t Port;
mluis 0:92bca02df485 244 uint8_t *Buffer;
mluis 0:92bca02df485 245 uint8_t BufferSize;
mluis 0:92bca02df485 246 }LoRaMacDownlinkStatus;
mluis 0:92bca02df485 247
mluis 0:92bca02df485 248 void SerialDisplayRefresh( void )
mluis 0:92bca02df485 249 {
mluis 0:92bca02df485 250 SerialDisplayInit( );
mluis 0:92bca02df485 251 SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION );
mluis 0:92bca02df485 252
mluis 0:92bca02df485 253 #if( OVER_THE_AIR_ACTIVATION == 0 )
mluis 0:92bca02df485 254 SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID );
mluis 0:92bca02df485 255 SerialDisplayUpdateDevAddr( LORAWAN_DEVICE_ADDRESS );
mluis 0:92bca02df485 256 SerialDisplayUpdateKey( 12, NwkSKey );
mluis 0:92bca02df485 257 SerialDisplayUpdateKey( 13, AppSKey );
mluis 0:92bca02df485 258 #else
mluis 0:92bca02df485 259 SerialDisplayUpdateEui( 5, DevEui );
mluis 0:92bca02df485 260 SerialDisplayUpdateEui( 6, AppEui );
mluis 0:92bca02df485 261 SerialDisplayUpdateKey( 7, AppKey );
mluis 0:92bca02df485 262 #endif
mluis 0:92bca02df485 263 SerialDisplayUpdateNetworkIsJoined( IsNetworkJoined );
mluis 0:92bca02df485 264
mluis 0:92bca02df485 265 SerialDisplayUpdateAdr( LORAWAN_ADR_ON );
mluis 1:352f608c3337 266 #if defined( USE_BAND_868 )
mluis 0:92bca02df485 267 SerialDisplayUpdateDutyCycle( LORAWAN_DUTYCYCLE_ON );
mluis 1:352f608c3337 268 #else
mluis 1:352f608c3337 269 SerialDisplayUpdateDutyCycle( false );
mluis 1:352f608c3337 270 #endif
mluis 0:92bca02df485 271 SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK );
mluis 0:92bca02df485 272
mluis 0:92bca02df485 273 SerialDisplayUpdateLedState( 3, AppLedStateOn );
mluis 0:92bca02df485 274 }
mluis 0:92bca02df485 275
mluis 0:92bca02df485 276 void SerialRxProcess( void )
mluis 0:92bca02df485 277 {
mluis 0:92bca02df485 278 if( SerialDisplayReadable( ) == true )
mluis 0:92bca02df485 279 {
mluis 0:92bca02df485 280 switch( SerialDisplayGetChar( ) )
mluis 0:92bca02df485 281 {
mluis 0:92bca02df485 282 case 'R':
mluis 0:92bca02df485 283 case 'r':
mluis 0:92bca02df485 284 // Refresh Serial screen
mluis 0:92bca02df485 285 SerialDisplayRefresh( );
mluis 0:92bca02df485 286 break;
mluis 0:92bca02df485 287 default:
mluis 0:92bca02df485 288 break;
mluis 0:92bca02df485 289 }
mluis 0:92bca02df485 290 }
mluis 0:92bca02df485 291 }
mluis 0:92bca02df485 292
mluis 0:92bca02df485 293 /*!
mluis 0:92bca02df485 294 * Prepares the frame buffer to be sent
mluis 0:92bca02df485 295 */
mluis 0:92bca02df485 296 static void PrepareTxFrame( uint8_t port )
mluis 0:92bca02df485 297 {
mluis 1:352f608c3337 298 switch( port )
mluis 1:352f608c3337 299 {
mluis 1:352f608c3337 300 case 15:
mluis 1:352f608c3337 301 {
mluis 1:352f608c3337 302 AppData[0] = AppLedStateOn;
mluis 1:352f608c3337 303 if( IsTxConfirmed == true )
mluis 1:352f608c3337 304 {
mluis 1:352f608c3337 305 AppData[1] = LoRaMacDownlinkStatus.DownlinkCounter >> 8;
mluis 1:352f608c3337 306 AppData[2] = LoRaMacDownlinkStatus.DownlinkCounter;
mluis 1:352f608c3337 307 AppData[3] = LoRaMacDownlinkStatus.Rssi >> 8;
mluis 1:352f608c3337 308 AppData[4] = LoRaMacDownlinkStatus.Rssi;
mluis 1:352f608c3337 309 AppData[5] = LoRaMacDownlinkStatus.Snr;
mluis 1:352f608c3337 310 }
mluis 1:352f608c3337 311 }
mluis 1:352f608c3337 312 break;
mluis 1:352f608c3337 313 case 224:
mluis 1:352f608c3337 314 if( ComplianceTestLinkCheck == true )
mluis 1:352f608c3337 315 {
mluis 1:352f608c3337 316 ComplianceTestLinkCheck = false;
mluis 1:352f608c3337 317 AppDataSize = 3;
mluis 1:352f608c3337 318 AppData[0] = 5;
mluis 1:352f608c3337 319 AppData[1] = ComplianceTestDemodMargin;
mluis 1:352f608c3337 320 AppData[2] = ComplianceTestNbGateways;
mluis 1:352f608c3337 321 ComplianceTestState = 1;
mluis 1:352f608c3337 322 }
mluis 1:352f608c3337 323 else
mluis 1:352f608c3337 324 {
mluis 1:352f608c3337 325 switch( ComplianceTestState )
mluis 1:352f608c3337 326 {
mluis 1:352f608c3337 327 case 4:
mluis 1:352f608c3337 328 ComplianceTestState = 1;
mluis 1:352f608c3337 329 break;
mluis 1:352f608c3337 330 case 1:
mluis 1:352f608c3337 331 AppDataSize = 2;
mluis 1:352f608c3337 332 AppData[0] = ComplianceTestDownLinkCounter >> 8;
mluis 1:352f608c3337 333 AppData[1] = ComplianceTestDownLinkCounter;
mluis 1:352f608c3337 334 break;
mluis 1:352f608c3337 335 }
mluis 1:352f608c3337 336 }
mluis 1:352f608c3337 337 break;
mluis 1:352f608c3337 338 }
mluis 0:92bca02df485 339 }
mluis 0:92bca02df485 340
mluis 0:92bca02df485 341 static void ProcessRxFrame( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info )
mluis 0:92bca02df485 342 {
mluis 0:92bca02df485 343 switch( info->RxPort ) // Check Rx port number
mluis 0:92bca02df485 344 {
mluis 0:92bca02df485 345 case 1: // The application LED can be controlled on port 1 or 2
mluis 0:92bca02df485 346 case 2:
mluis 0:92bca02df485 347 if( info->RxBufferSize == 1 )
mluis 0:92bca02df485 348 {
mluis 0:92bca02df485 349 AppLedStateOn = info->RxBuffer[0] & 0x01;
mluis 0:92bca02df485 350 Led3StateChanged = true;
mluis 0:92bca02df485 351 }
mluis 0:92bca02df485 352 break;
mluis 1:352f608c3337 353 case 224:
mluis 1:352f608c3337 354 if( ComplianceTestOn == false )
mluis 1:352f608c3337 355 {
mluis 1:352f608c3337 356 // Check compliance test enable command (i)
mluis 1:352f608c3337 357 if( ( info->RxBufferSize == 4 ) &&
mluis 1:352f608c3337 358 ( info->RxBuffer[0] == 0x01 ) &&
mluis 1:352f608c3337 359 ( info->RxBuffer[1] == 0x01 ) &&
mluis 1:352f608c3337 360 ( info->RxBuffer[2] == 0x01 ) &&
mluis 1:352f608c3337 361 ( info->RxBuffer[3] == 0x01 ) )
mluis 1:352f608c3337 362 {
mluis 1:352f608c3337 363 IsTxConfirmed = false;
mluis 1:352f608c3337 364 AppPort = 224;
mluis 1:352f608c3337 365 AppDataSize = 2;
mluis 1:352f608c3337 366 ComplianceTestDownLinkCounter = 0;
mluis 1:352f608c3337 367 ComplianceTestLinkCheck = false;
mluis 1:352f608c3337 368 ComplianceTestDemodMargin = 0;
mluis 1:352f608c3337 369 ComplianceTestNbGateways = 0;
mluis 1:352f608c3337 370 ComplianceTestOn = true;
mluis 1:352f608c3337 371 ComplianceTestState = 1;
mluis 1:352f608c3337 372
mluis 1:352f608c3337 373 LoRaMacSetAdrOn( true );
mluis 1:352f608c3337 374 #if defined( USE_BAND_868 )
mluis 1:352f608c3337 375 LoRaMacTestSetDutyCycleOn( false );
mluis 1:352f608c3337 376 #endif
mluis 1:352f608c3337 377 }
mluis 1:352f608c3337 378 }
mluis 1:352f608c3337 379 else
mluis 1:352f608c3337 380 {
mluis 1:352f608c3337 381 ComplianceTestState = info->RxBuffer[0];
mluis 1:352f608c3337 382 switch( ComplianceTestState )
mluis 1:352f608c3337 383 {
mluis 1:352f608c3337 384 case 0: // Check compliance test disable command (ii)
mluis 1:352f608c3337 385 IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
mluis 1:352f608c3337 386 AppPort = LORAWAN_APP_PORT;
mluis 1:352f608c3337 387 if( IsTxConfirmed == true )
mluis 1:352f608c3337 388 {
mluis 1:352f608c3337 389 AppDataSize = 6;
mluis 1:352f608c3337 390 }
mluis 1:352f608c3337 391 else
mluis 1:352f608c3337 392 {
mluis 1:352f608c3337 393 AppDataSize = 1;
mluis 1:352f608c3337 394 }
mluis 1:352f608c3337 395 ComplianceTestDownLinkCounter = 0;
mluis 1:352f608c3337 396 ComplianceTestOn = false;
mluis 1:352f608c3337 397
mluis 1:352f608c3337 398 LoRaMacSetAdrOn( LORAWAN_ADR_ON );
mluis 1:352f608c3337 399 #if defined( USE_BAND_868 )
mluis 1:352f608c3337 400 LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
mluis 1:352f608c3337 401 #endif
mluis 1:352f608c3337 402 break;
mluis 1:352f608c3337 403 case 1: // (iii, iv)
mluis 1:352f608c3337 404 AppDataSize = 2;
mluis 1:352f608c3337 405 break;
mluis 1:352f608c3337 406 case 2: // Enable confirmed messages (v)
mluis 1:352f608c3337 407 IsTxConfirmed = true;
mluis 1:352f608c3337 408 ComplianceTestState = 1;
mluis 1:352f608c3337 409 break;
mluis 1:352f608c3337 410 case 3: // Disable confirmed messages (vi)
mluis 1:352f608c3337 411 IsTxConfirmed = false;
mluis 1:352f608c3337 412 ComplianceTestState = 1;
mluis 1:352f608c3337 413 break;
mluis 1:352f608c3337 414 case 4: // (vii)
mluis 1:352f608c3337 415 AppDataSize = info->RxBufferSize;
mluis 1:352f608c3337 416
mluis 1:352f608c3337 417 AppData[0] = 4;
mluis 1:352f608c3337 418 for( uint8_t i = 1; i < AppDataSize; i++ )
mluis 1:352f608c3337 419 {
mluis 1:352f608c3337 420 AppData[i] = info->RxBuffer[i] + 1;
mluis 1:352f608c3337 421 }
mluis 1:352f608c3337 422 break;
mluis 1:352f608c3337 423 case 5: // (viii)
mluis 1:352f608c3337 424 LoRaMacLinkCheckReq( );
mluis 1:352f608c3337 425 break;
mluis 1:352f608c3337 426 default:
mluis 1:352f608c3337 427 break;
mluis 1:352f608c3337 428 }
mluis 1:352f608c3337 429 }
mluis 1:352f608c3337 430 break;
mluis 0:92bca02df485 431 default:
mluis 0:92bca02df485 432 break;
mluis 0:92bca02df485 433 }
mluis 0:92bca02df485 434 }
mluis 0:92bca02df485 435
mluis 0:92bca02df485 436 static bool SendFrame( void )
mluis 0:92bca02df485 437 {
mluis 0:92bca02df485 438 uint8_t sendFrameStatus = 0;
mluis 0:92bca02df485 439
mluis 0:92bca02df485 440 LoRaMacUplinkStatus.Acked = false;
mluis 1:352f608c3337 441 LoRaMacUplinkStatus.Port = AppPort;
mluis 0:92bca02df485 442 LoRaMacUplinkStatus.Buffer = AppData;
mluis 1:352f608c3337 443 LoRaMacUplinkStatus.BufferSize = AppDataSize;
mluis 0:92bca02df485 444
mluis 1:352f608c3337 445 SerialDisplayUpdateFrameType( IsTxConfirmed );
mluis 1:352f608c3337 446
mluis 1:352f608c3337 447 if( IsTxConfirmed == false )
mluis 1:352f608c3337 448 {
mluis 1:352f608c3337 449 sendFrameStatus = LoRaMacSendFrame( AppPort, AppData, AppDataSize );
mluis 1:352f608c3337 450 }
mluis 1:352f608c3337 451 else
mluis 1:352f608c3337 452 {
mluis 1:352f608c3337 453 sendFrameStatus = LoRaMacSendConfirmedFrame( AppPort, AppData, AppDataSize, 8 );
mluis 1:352f608c3337 454 }
mluis 1:352f608c3337 455
mluis 0:92bca02df485 456 switch( sendFrameStatus )
mluis 0:92bca02df485 457 {
mluis 0:92bca02df485 458 case 5: // NO_FREE_CHANNEL
mluis 0:92bca02df485 459 // Try again later
mluis 0:92bca02df485 460 return true;
mluis 0:92bca02df485 461 default:
mluis 0:92bca02df485 462 return false;
mluis 0:92bca02df485 463 }
mluis 0:92bca02df485 464 }
mluis 0:92bca02df485 465
mluis 0:92bca02df485 466 #if( OVER_THE_AIR_ACTIVATION != 0 )
mluis 0:92bca02df485 467
mluis 0:92bca02df485 468 /*!
mluis 0:92bca02df485 469 * \brief Function executed on JoinReq Timeout event
mluis 0:92bca02df485 470 */
mluis 0:92bca02df485 471 static void OnJoinReqTimerEvent( void )
mluis 0:92bca02df485 472 {
mluis 0:92bca02df485 473 TimerStop( &JoinReqTimer );
mluis 0:92bca02df485 474 TxNextPacket = true;
mluis 0:92bca02df485 475 }
mluis 0:92bca02df485 476
mluis 0:92bca02df485 477 #endif
mluis 0:92bca02df485 478
mluis 0:92bca02df485 479 /*!
mluis 0:92bca02df485 480 * \brief Function executed on TxNextPacket Timeout event
mluis 0:92bca02df485 481 */
mluis 0:92bca02df485 482 static void OnTxNextPacketTimerEvent( void )
mluis 0:92bca02df485 483 {
mluis 0:92bca02df485 484 TimerStop( &TxNextPacketTimer );
mluis 0:92bca02df485 485 TxNextPacket = true;
mluis 0:92bca02df485 486 }
mluis 0:92bca02df485 487
mluis 0:92bca02df485 488 /*!
mluis 0:92bca02df485 489 * \brief Function executed on Led 1 Timeout event
mluis 0:92bca02df485 490 */
mluis 0:92bca02df485 491 static void OnLed1TimerEvent( void )
mluis 0:92bca02df485 492 {
mluis 0:92bca02df485 493 TimerStop( &Led1Timer );
mluis 1:352f608c3337 494 Led1State = false;
mluis 0:92bca02df485 495 Led1StateChanged = true;
mluis 0:92bca02df485 496 }
mluis 0:92bca02df485 497
mluis 0:92bca02df485 498 /*!
mluis 0:92bca02df485 499 * \brief Function executed on Led 2 Timeout event
mluis 0:92bca02df485 500 */
mluis 0:92bca02df485 501 static void OnLed2TimerEvent( void )
mluis 0:92bca02df485 502 {
mluis 0:92bca02df485 503 TimerStop( &Led2Timer );
mluis 1:352f608c3337 504 Led2State = false;
mluis 0:92bca02df485 505 Led2StateChanged = true;
mluis 0:92bca02df485 506 }
mluis 0:92bca02df485 507
mluis 0:92bca02df485 508 /*!
mluis 0:92bca02df485 509 * \brief Function to be executed on MAC layer event
mluis 0:92bca02df485 510 */
mluis 0:92bca02df485 511 static void OnMacEvent( LoRaMacEventFlags_t *flags, LoRaMacEventInfo_t *info )
mluis 0:92bca02df485 512 {
mluis 0:92bca02df485 513 if( flags->Bits.JoinAccept == 1 )
mluis 0:92bca02df485 514 {
mluis 0:92bca02df485 515 #if( OVER_THE_AIR_ACTIVATION != 0 )
mluis 0:92bca02df485 516 TimerStop( &JoinReqTimer );
mluis 0:92bca02df485 517 #endif
mluis 0:92bca02df485 518 IsNetworkJoined = true;
mluis 0:92bca02df485 519 IsNetworkJoinedStatusUpdate = true;
mluis 0:92bca02df485 520 }
mluis 0:92bca02df485 521 else
mluis 0:92bca02df485 522 {
mluis 0:92bca02df485 523 if( flags->Bits.Tx == 1 )
mluis 0:92bca02df485 524 {
mluis 0:92bca02df485 525 }
mluis 0:92bca02df485 526
mluis 0:92bca02df485 527 if( flags->Bits.Rx == 1 )
mluis 0:92bca02df485 528 {
mluis 1:352f608c3337 529 if( ComplianceTestOn == true )
mluis 1:352f608c3337 530 {
mluis 1:352f608c3337 531 ComplianceTestDownLinkCounter++;
mluis 1:352f608c3337 532 if( flags->Bits.LinkCheck == 1 )
mluis 1:352f608c3337 533 {
mluis 1:352f608c3337 534 ComplianceTestLinkCheck = true;
mluis 1:352f608c3337 535 ComplianceTestDemodMargin = info->DemodMargin;
mluis 1:352f608c3337 536 ComplianceTestNbGateways = info->NbGateways;
mluis 1:352f608c3337 537 }
mluis 1:352f608c3337 538 }
mluis 0:92bca02df485 539 if( flags->Bits.RxData == true )
mluis 0:92bca02df485 540 {
mluis 0:92bca02df485 541 ProcessRxFrame( flags, info );
mluis 0:92bca02df485 542 }
mluis 0:92bca02df485 543
mluis 0:92bca02df485 544 LoRaMacDownlinkStatus.Rssi = info->RxRssi;
mluis 0:92bca02df485 545 if( info->RxSnr & 0x80 ) // The SNR sign bit is 1
mluis 0:92bca02df485 546 {
mluis 0:92bca02df485 547 // Invert and divide by 4
mluis 0:92bca02df485 548 LoRaMacDownlinkStatus.Snr = ( ( ~info->RxSnr + 1 ) & 0xFF ) >> 2;
mluis 0:92bca02df485 549 LoRaMacDownlinkStatus.Snr = -LoRaMacDownlinkStatus.Snr;
mluis 0:92bca02df485 550 }
mluis 0:92bca02df485 551 else
mluis 0:92bca02df485 552 {
mluis 0:92bca02df485 553 // Divide by 4
mluis 0:92bca02df485 554 LoRaMacDownlinkStatus.Snr = ( info->RxSnr & 0xFF ) >> 2;
mluis 0:92bca02df485 555 }
mluis 0:92bca02df485 556 LoRaMacDownlinkStatus.DownlinkCounter++;
mluis 0:92bca02df485 557 LoRaMacDownlinkStatus.RxData = flags->Bits.RxData;
mluis 0:92bca02df485 558 LoRaMacDownlinkStatus.Port = info->RxPort;
mluis 0:92bca02df485 559 LoRaMacDownlinkStatus.Buffer = info->RxBuffer;
mluis 0:92bca02df485 560 LoRaMacDownlinkStatus.BufferSize = info->RxBufferSize;
mluis 0:92bca02df485 561
mluis 1:352f608c3337 562 Led2State = true;
mluis 1:352f608c3337 563 Led2StateChanged = true;
mluis 0:92bca02df485 564 TimerStart( &Led2Timer );
mluis 0:92bca02df485 565 }
mluis 1:352f608c3337 566
mluis 1:352f608c3337 567 LoRaMacUplinkStatus.Acked = info->TxAckReceived;
mluis 1:352f608c3337 568 LoRaMacUplinkStatus.Datarate = info->TxDatarate;
mluis 1:352f608c3337 569 LoRaMacUplinkStatus.UplinkCounter = LoRaMacGetUpLinkCounter( ) - 1;
mluis 0:92bca02df485 570 }
mluis 1:352f608c3337 571
mluis 1:352f608c3337 572 LinkStatusUpdated = true;
mluis 0:92bca02df485 573 // Schedule a new transmission
mluis 0:92bca02df485 574 ScheduleNextTx = true;
mluis 0:92bca02df485 575 }
mluis 0:92bca02df485 576
mluis 0:92bca02df485 577 /**
mluis 0:92bca02df485 578 * Main application entry point.
mluis 0:92bca02df485 579 */
mluis 0:92bca02df485 580 int main( void )
mluis 0:92bca02df485 581 {
mluis 0:92bca02df485 582 SerialDisplayInit( );
mluis 0:92bca02df485 583
mluis 0:92bca02df485 584 #if( OVER_THE_AIR_ACTIVATION != 0 )
mluis 0:92bca02df485 585 uint8_t sendFrameStatus = 0;
mluis 0:92bca02df485 586 #endif
mluis 0:92bca02df485 587 bool trySendingFrameAgain = false;
mluis 0:92bca02df485 588
mluis 0:92bca02df485 589 BoardInit( );
mluis 0:92bca02df485 590
mluis 1:352f608c3337 591 LoRaMacCallbacks.MacEvent = OnMacEvent;
mluis 1:352f608c3337 592 LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel;
mluis 1:352f608c3337 593 LoRaMacInit( &LoRaMacCallbacks );
mluis 0:92bca02df485 594
mluis 0:92bca02df485 595 IsNetworkJoined = false;
mluis 0:92bca02df485 596
mluis 0:92bca02df485 597 SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION );
mluis 0:92bca02df485 598
mluis 0:92bca02df485 599 #if( OVER_THE_AIR_ACTIVATION == 0 )
mluis 0:92bca02df485 600 LoRaMacInitNwkIds( LORAWAN_NETWORK_ID, LORAWAN_DEVICE_ADDRESS, NwkSKey, AppSKey );
mluis 0:92bca02df485 601 IsNetworkJoined = true;
mluis 0:92bca02df485 602
mluis 0:92bca02df485 603 SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID );
mluis 0:92bca02df485 604 SerialDisplayUpdateDevAddr( LORAWAN_DEVICE_ADDRESS );
mluis 0:92bca02df485 605 SerialDisplayUpdateKey( 12, NwkSKey );
mluis 0:92bca02df485 606 SerialDisplayUpdateKey( 13, AppSKey );
mluis 0:92bca02df485 607 #else
mluis 0:92bca02df485 608 // Sends a JoinReq Command every 5 seconds until the network is joined
mluis 0:92bca02df485 609 TimerInit( &JoinReqTimer, OnJoinReqTimerEvent );
mluis 0:92bca02df485 610 TimerSetValue( &JoinReqTimer, OVER_THE_AIR_ACTIVATION_DUTYCYCLE );
mluis 0:92bca02df485 611
mluis 0:92bca02df485 612 SerialDisplayUpdateEui( 5, DevEui );
mluis 0:92bca02df485 613 SerialDisplayUpdateEui( 6, AppEui );
mluis 0:92bca02df485 614 SerialDisplayUpdateKey( 7, AppKey );
mluis 0:92bca02df485 615
mluis 0:92bca02df485 616 #endif
mluis 0:92bca02df485 617
mluis 0:92bca02df485 618 SerialDisplayUpdateNetworkIsJoined( IsNetworkJoined );
mluis 0:92bca02df485 619
mluis 0:92bca02df485 620 TxNextPacket = true;
mluis 0:92bca02df485 621 TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent );
mluis 0:92bca02df485 622
mluis 0:92bca02df485 623 TimerInit( &Led1Timer, OnLed1TimerEvent );
mluis 0:92bca02df485 624 TimerSetValue( &Led1Timer, 500000 );
mluis 0:92bca02df485 625
mluis 0:92bca02df485 626 TimerInit( &Led2Timer, OnLed2TimerEvent );
mluis 0:92bca02df485 627 TimerSetValue( &Led2Timer, 500000 );
mluis 0:92bca02df485 628
mluis 0:92bca02df485 629 LoRaMacSetAdrOn( LORAWAN_ADR_ON );
mluis 1:352f608c3337 630 #if defined( USE_BAND_868 )
mluis 0:92bca02df485 631 LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON );
mluis 1:352f608c3337 632 #endif
mluis 0:92bca02df485 633 LoRaMacSetPublicNetwork( LORAWAN_PUBLIC_NETWORK );
mluis 0:92bca02df485 634
mluis 0:92bca02df485 635 SerialDisplayUpdateAdr( LORAWAN_ADR_ON );
mluis 1:352f608c3337 636 #if defined( USE_BAND_868 )
mluis 0:92bca02df485 637 SerialDisplayUpdateDutyCycle( LORAWAN_DUTYCYCLE_ON );
mluis 1:352f608c3337 638 #else
mluis 1:352f608c3337 639 SerialDisplayUpdateDutyCycle( false );
mluis 1:352f608c3337 640 #endif
mluis 0:92bca02df485 641 SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK );
mluis 0:92bca02df485 642
mluis 0:92bca02df485 643 LoRaMacDownlinkStatus.DownlinkCounter = 0;
mluis 0:92bca02df485 644
mluis 0:92bca02df485 645 while( 1 )
mluis 0:92bca02df485 646 {
mluis 0:92bca02df485 647 while( IsNetworkJoined == false )
mluis 0:92bca02df485 648 {
mluis 0:92bca02df485 649 #if( OVER_THE_AIR_ACTIVATION != 0 )
mluis 0:92bca02df485 650 if( TxNextPacket == true )
mluis 0:92bca02df485 651 {
mluis 0:92bca02df485 652 TxNextPacket = false;
mluis 0:92bca02df485 653
mluis 0:92bca02df485 654 sendFrameStatus = LoRaMacJoinReq( DevEui, AppEui, AppKey );
mluis 0:92bca02df485 655 switch( sendFrameStatus )
mluis 0:92bca02df485 656 {
mluis 0:92bca02df485 657 case 1: // BUSY
mluis 0:92bca02df485 658 break;
mluis 0:92bca02df485 659 case 0: // OK
mluis 0:92bca02df485 660 case 2: // NO_NETWORK_JOINED
mluis 0:92bca02df485 661 case 3: // LENGTH_PORT_ERROR
mluis 0:92bca02df485 662 case 4: // MAC_CMD_ERROR
mluis 0:92bca02df485 663 case 6: // DEVICE_OFF
mluis 0:92bca02df485 664 default:
mluis 0:92bca02df485 665 // Relaunch timer for next trial
mluis 0:92bca02df485 666 TimerStart( &JoinReqTimer );
mluis 0:92bca02df485 667 break;
mluis 0:92bca02df485 668 }
mluis 0:92bca02df485 669 }
mluis 0:92bca02df485 670 SerialRxProcess( );
mluis 0:92bca02df485 671 #endif
mluis 0:92bca02df485 672 }
mluis 0:92bca02df485 673
mluis 0:92bca02df485 674 SerialRxProcess( );
mluis 0:92bca02df485 675
mluis 0:92bca02df485 676 if( IsNetworkJoinedStatusUpdate == true )
mluis 0:92bca02df485 677 {
mluis 0:92bca02df485 678 IsNetworkJoinedStatusUpdate = false;
mluis 0:92bca02df485 679 SerialDisplayUpdateNetworkIsJoined( IsNetworkJoined );
mluis 0:92bca02df485 680 }
mluis 0:92bca02df485 681 if( Led1StateChanged == true )
mluis 0:92bca02df485 682 {
mluis 0:92bca02df485 683 Led1StateChanged = false;
mluis 1:352f608c3337 684 SerialDisplayUpdateLedState( 1, Led1State );
mluis 0:92bca02df485 685 }
mluis 0:92bca02df485 686 if( Led2StateChanged == true )
mluis 0:92bca02df485 687 {
mluis 0:92bca02df485 688 Led2StateChanged = false;
mluis 1:352f608c3337 689 SerialDisplayUpdateLedState( 2, Led2State );
mluis 0:92bca02df485 690 }
mluis 0:92bca02df485 691 if( Led3StateChanged == true )
mluis 0:92bca02df485 692 {
mluis 0:92bca02df485 693 Led3StateChanged = false;
mluis 0:92bca02df485 694 SerialDisplayUpdateLedState( 3, AppLedStateOn );
mluis 0:92bca02df485 695 }
mluis 0:92bca02df485 696 if( LinkStatusUpdated == true )
mluis 0:92bca02df485 697 {
mluis 0:92bca02df485 698 LinkStatusUpdated = false;
mluis 1:352f608c3337 699 SerialDisplayUpdateLedState( 2, Led2State );
mluis 0:92bca02df485 700 SerialDisplayUpdateUplink( LoRaMacUplinkStatus.Acked, LoRaMacUplinkStatus.Datarate, LoRaMacUplinkStatus.UplinkCounter, LoRaMacUplinkStatus.Port, LoRaMacUplinkStatus.Buffer, LoRaMacUplinkStatus.BufferSize );
mluis 0:92bca02df485 701 SerialDisplayUpdateDownlink( LoRaMacDownlinkStatus.RxData, LoRaMacDownlinkStatus.Rssi, LoRaMacDownlinkStatus.Snr, LoRaMacDownlinkStatus.DownlinkCounter, LoRaMacDownlinkStatus.Port, LoRaMacDownlinkStatus.Buffer, LoRaMacDownlinkStatus.BufferSize );
mluis 0:92bca02df485 702 }
mluis 0:92bca02df485 703
mluis 0:92bca02df485 704 if( ScheduleNextTx == true )
mluis 0:92bca02df485 705 {
mluis 0:92bca02df485 706 ScheduleNextTx = false;
mluis 1:352f608c3337 707
mluis 1:352f608c3337 708 // if( ComplianceTestOn == true )
mluis 1:352f608c3337 709 // {
mluis 1:352f608c3337 710 // TxNextPacket = true;
mluis 1:352f608c3337 711 // }
mluis 1:352f608c3337 712 // else
mluis 1:352f608c3337 713 // {
mluis 1:352f608c3337 714 // Schedule next packet transmission
mluis 1:352f608c3337 715 TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
mluis 1:352f608c3337 716 TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime );
mluis 1:352f608c3337 717 TimerStart( &TxNextPacketTimer );
mluis 1:352f608c3337 718 // }
mluis 0:92bca02df485 719 }
mluis 0:92bca02df485 720
mluis 0:92bca02df485 721 if( trySendingFrameAgain == true )
mluis 0:92bca02df485 722 {
mluis 0:92bca02df485 723 trySendingFrameAgain = SendFrame( );
mluis 0:92bca02df485 724 }
mluis 0:92bca02df485 725 if( TxNextPacket == true )
mluis 0:92bca02df485 726 {
mluis 0:92bca02df485 727 TxNextPacket = false;
mluis 0:92bca02df485 728
mluis 0:92bca02df485 729 SerialDisplayUpdateDonwlinkRxData( false );
mluis 0:92bca02df485 730
mluis 1:352f608c3337 731 PrepareTxFrame( AppPort );
mluis 0:92bca02df485 732
mluis 1:352f608c3337 733 Led1State = true;
mluis 1:352f608c3337 734 SerialDisplayUpdateLedState( 1, Led1State );
mluis 0:92bca02df485 735 TimerStart( &Led1Timer );
mluis 0:92bca02df485 736
mluis 0:92bca02df485 737 trySendingFrameAgain = SendFrame( );
mluis 0:92bca02df485 738
mluis 0:92bca02df485 739 SerialDisplayUpdateUplink( LoRaMacUplinkStatus.Acked, LoRaMacUplinkStatus.Datarate, LoRaMacUplinkStatus.UplinkCounter, LoRaMacUplinkStatus.Port, LoRaMacUplinkStatus.Buffer, LoRaMacUplinkStatus.BufferSize );
mluis 0:92bca02df485 740 }
mluis 0:92bca02df485 741 }
mluis 0:92bca02df485 742 }