/*! * \file main.c * * \brief LoRaMac classA device implementation * * \copyright Revised BSD License, see section \ref LICENSE. * * \code * ______ _ * / _____) _ | | * ( (____ _____ ____ _| |_ _____ ____| |__ * \____ \| ___ | (_ _) ___ |/ ___) _ \ * _____) ) ____| | | || |_| ____( (___| | | | * (______/|_____)_|_|_| \__)_____)\____)_| |_| * (C)2013-2017 Semtech * * \endcode * * \author Miguel Luis ( Semtech ) * * \author Gregory Cristian ( Semtech ) */ /*! \file classA/B-L072Z-LRWAN1/main.c */ #include #include "utilities.h" #include "board.h" #include "gpio.h" #include "LoRaMac.h" #include "Commissioning.h" #include "NvmCtxMgmt.h" #define ACTIVE_REGION LORAMAC_REGION_CN470 #ifndef ACTIVE_REGION #warning "No active region defined, LORAMAC_REGION_EU868 will be used as default." #define ACTIVE_REGION LORAMAC_REGION_EU868 #endif /*! * Defines the application data transmission duty cycle. 5s, value in [ms]. */ #define APP_TX_DUTYCYCLE 5000 /*! * Defines a random delay for application data transmission duty cycle. 1s, * value in [ms]. */ #define APP_TX_DUTYCYCLE_RND 1000 /*! * Default datarate */ #define LORAWAN_DEFAULT_DATARATE DR_0 /*! * LoRaWAN confirmed messages */ #define LORAWAN_CONFIRMED_MSG_ON false /*! * LoRaWAN Adaptive Data Rate * * \remark Please note that when ADR is enabled the end-device should be static */ #define LORAWAN_ADR_ON 1 #if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 ) #include "LoRaMacTest.h" /*! * LoRaWAN ETSI duty cycle control enable/disable * * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes */ #define LORAWAN_DUTYCYCLE_ON true #endif /*! * LoRaWAN application port */ #define LORAWAN_APP_PORT 2 #if( OVER_THE_AIR_ACTIVATION == 0 ) /*! * Device address */ static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS; #endif /*! * Application port */ static uint8_t AppPort = LORAWAN_APP_PORT; /*! * User application data size */ static uint8_t AppDataSize = 1; static uint8_t AppDataSizeBackup = 1; /*! * User application data buffer size */ #define LORAWAN_APP_DATA_MAX_SIZE 242 /*! * User application data */ static uint8_t AppDataBuffer[LORAWAN_APP_DATA_MAX_SIZE]; /*! * Indicates if the node is sending confirmed or unconfirmed messages */ static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON; /*! * Defines the application data transmission duty cycle */ static uint32_t TxDutyCycleTime; /*! * Timer to handle the application data transmission duty cycle */ static TimerEvent_t TxNextPacketTimer; /*! * Specifies the state of the application LED */ static bool AppLedStateOn = false; /*! * Timer to handle the state of LED1 */ static TimerEvent_t Led1Timer; /*! * Timer to handle the state of LED3 */ static TimerEvent_t Led3Timer; /*! * Indicates if a new packet can be sent */ static bool NextTx = true; /*! * Indicates if LoRaMacProcess call is pending. * * \warning If variable is equal to 0 then the MCU can be set in low power mode */ static uint8_t IsMacProcessPending = 0; /*! * Device states */ static enum eDeviceState { DEVICE_STATE_RESTORE, DEVICE_STATE_START, DEVICE_STATE_JOIN, DEVICE_STATE_SEND, DEVICE_STATE_CYCLE, DEVICE_STATE_SLEEP }DeviceState; /*! * LoRaWAN compliance tests support data */ struct ComplianceTest_s { bool Running; uint8_t State; bool IsTxConfirmed; uint8_t AppPort; uint8_t AppDataSize; uint8_t *AppDataBuffer; uint16_t DownLinkCounter; bool LinkCheck; uint8_t DemodMargin; uint8_t NbGateways; }ComplianceTest; /*! * */ typedef enum { LORAMAC_HANDLER_UNCONFIRMED_MSG = 0, LORAMAC_HANDLER_CONFIRMED_MSG = !LORAMAC_HANDLER_UNCONFIRMED_MSG }LoRaMacHandlerMsgTypes_t; /*! * Application data structure */ typedef struct LoRaMacHandlerAppData_s { LoRaMacHandlerMsgTypes_t MsgType; uint8_t Port; uint8_t BufferSize; uint8_t *Buffer; }LoRaMacHandlerAppData_t; LoRaMacHandlerAppData_t AppData = { .MsgType = LORAMAC_HANDLER_UNCONFIRMED_MSG, .Buffer = NULL, .BufferSize = 0, .Port = 0 }; /*! * LED GPIO pins objects */ extern Gpio_t Led1; // Tx extern Gpio_t Led3; // Rx extern Gpio_t Led4; // App /*! * MAC status strings */ const char* MacStatusStrings[] = { "OK", // LORAMAC_STATUS_OK "Busy", // LORAMAC_STATUS_BUSY "Service unknown", // LORAMAC_STATUS_SERVICE_UNKNOWN "Parameter invalid", // LORAMAC_STATUS_PARAMETER_INVALID "Frequency invalid", // LORAMAC_STATUS_FREQUENCY_INVALID "Datarate invalid", // LORAMAC_STATUS_DATARATE_INVALID "Frequency or datarate invalid", // LORAMAC_STATUS_FREQ_AND_DR_INVALID "No network joined", // LORAMAC_STATUS_NO_NETWORK_JOINED "Length error", // LORAMAC_STATUS_LENGTH_ERROR "Region not supported", // LORAMAC_STATUS_REGION_NOT_SUPPORTED "Skipped APP data", // LORAMAC_STATUS_SKIPPED_APP_DATA "Duty-cycle restricted", // LORAMAC_STATUS_DUTYCYCLE_RESTRICTED "No channel found", // LORAMAC_STATUS_NO_CHANNEL_FOUND "No free channel found", // LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND "Busy beacon reserved time", // LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME "Busy ping-slot window time", // LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME "Busy uplink collision", // LORAMAC_STATUS_BUSY_UPLINK_COLLISION "Crypto error", // LORAMAC_STATUS_CRYPTO_ERROR "FCnt handler error", // LORAMAC_STATUS_FCNT_HANDLER_ERROR "MAC command error", // LORAMAC_STATUS_MAC_COMMAD_ERROR "ClassB error", // LORAMAC_STATUS_CLASS_B_ERROR "Confirm queue error", // LORAMAC_STATUS_CONFIRM_QUEUE_ERROR "Multicast group undefined", // LORAMAC_STATUS_MC_GROUP_UNDEFINED "Unknown error", // LORAMAC_STATUS_ERROR }; /*! * MAC event info status strings. */ const char* EventInfoStatusStrings[] = { "OK", // LORAMAC_EVENT_INFO_STATUS_OK "Error", // LORAMAC_EVENT_INFO_STATUS_ERROR "Tx timeout", // LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT "Rx 1 timeout", // LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT "Rx 2 timeout", // LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT "Rx1 error", // LORAMAC_EVENT_INFO_STATUS_RX1_ERROR "Rx2 error", // LORAMAC_EVENT_INFO_STATUS_RX2_ERROR "Join failed", // LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL "Downlink repeated", // LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED "Tx DR payload size error", // LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR "Downlink too many frames loss", // LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS "Address fail", // LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL "MIC fail", // LORAMAC_EVENT_INFO_STATUS_MIC_FAIL "Multicast fail", // LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL "Beacon locked", // LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED "Beacon lost", // LORAMAC_EVENT_INFO_STATUS_BEACON_LOST "Beacon not found" // LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND }; /*! * Prints the provided buffer in HEX * * \param buffer Buffer to be printed * \param size Buffer size to be printed */ void PrintHexBuffer( uint8_t *buffer, uint8_t size ) { uint8_t newline = 0; for( uint8_t i = 0; i < size; i++ ) { if( newline != 0 ) { printf( "\n" ); newline = 0; } printf( "%02X ", buffer[i] ); if( ( ( i + 1 ) % 16 ) == 0 ) { newline = 1; } } printf( "\n" ); } /*! * Executes the network Join request */ static void JoinNetwork( void ) { LoRaMacStatus_t status; MlmeReq_t mlmeReq; mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.Datarate = LORAWAN_DEFAULT_DATARATE; // Starts the join procedure status = LoRaMacMlmeRequest( &mlmeReq ); printf( "\n###### ===== MLME-Request - MLME_JOIN ==== ######\n" ); printf( "STATUS : %s\n", MacStatusStrings[status] ); if( status == LORAMAC_STATUS_OK ) { printf( "###### ===== JOINING ==== ######\n" ); DeviceState = DEVICE_STATE_SLEEP; } else { if( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) { printf( "Next Tx in : %lu [ms]\n", mlmeReq.ReqReturn.DutyCycleWaitTime ); } DeviceState = DEVICE_STATE_CYCLE; } } /*! * \brief Prepares the payload of the frame */ static void PrepareTxFrame( uint8_t port ) { switch( port ) { case 2: { AppDataSizeBackup = AppDataSize = 1; AppDataBuffer[0] = AppLedStateOn; } break; case 224: if( ComplianceTest.LinkCheck == true ) { ComplianceTest.LinkCheck = false; AppDataSize = 3; AppDataBuffer[0] = 5; AppDataBuffer[1] = ComplianceTest.DemodMargin; AppDataBuffer[2] = ComplianceTest.NbGateways; ComplianceTest.State = 1; } else { switch( ComplianceTest.State ) { case 4: ComplianceTest.State = 1; break; case 1: AppDataSize = 2; AppDataBuffer[0] = ComplianceTest.DownLinkCounter >> 8; AppDataBuffer[1] = ComplianceTest.DownLinkCounter; break; } } break; default: break; } } /*! * \brief Prepares the payload of the frame * * \retval [0: frame could be send, 1: error] */ static bool SendFrame( void ) { McpsReq_t mcpsReq; LoRaMacTxInfo_t txInfo; if( LoRaMacQueryTxPossible( AppDataSize, &txInfo ) != LORAMAC_STATUS_OK ) { // Send empty frame in order to flush MAC commands mcpsReq.Type = MCPS_UNCONFIRMED; mcpsReq.Req.Unconfirmed.fBuffer = NULL; mcpsReq.Req.Unconfirmed.fBufferSize = 0; mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE; } else { if( IsTxConfirmed == false ) { mcpsReq.Type = MCPS_UNCONFIRMED; mcpsReq.Req.Unconfirmed.fPort = AppPort; mcpsReq.Req.Unconfirmed.fBuffer = AppDataBuffer; mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize; mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE; } else { mcpsReq.Type = MCPS_CONFIRMED; mcpsReq.Req.Confirmed.fPort = AppPort; mcpsReq.Req.Confirmed.fBuffer = AppDataBuffer; mcpsReq.Req.Confirmed.fBufferSize = AppDataSize; mcpsReq.Req.Confirmed.NbTrials = 8; mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE; } } // Update global variable AppData.MsgType = ( mcpsReq.Type == MCPS_CONFIRMED ) ? LORAMAC_HANDLER_CONFIRMED_MSG : LORAMAC_HANDLER_UNCONFIRMED_MSG; AppData.Port = mcpsReq.Req.Unconfirmed.fPort; AppData.Buffer = mcpsReq.Req.Unconfirmed.fBuffer; AppData.BufferSize = mcpsReq.Req.Unconfirmed.fBufferSize; LoRaMacStatus_t status; status = LoRaMacMcpsRequest( &mcpsReq ); printf( "\n###### ===== MCPS-Request ==== ######\n" ); printf( "STATUS : %s\n", MacStatusStrings[status] ); if( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) { printf( "Next Tx in : %lu [ms]\n", mcpsReq.ReqReturn.DutyCycleWaitTime ); } if( status == LORAMAC_STATUS_OK ) { return false; } return true; } /*! * \brief Function executed on TxNextPacket Timeout event */ static void OnTxNextPacketTimerEvent( void* context ) { MibRequestConfirm_t mibReq; LoRaMacStatus_t status; TimerStop( &TxNextPacketTimer ); mibReq.Type = MIB_NETWORK_ACTIVATION; status = LoRaMacMibGetRequestConfirm( &mibReq ); if( status == LORAMAC_STATUS_OK ) { if( mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE ) { // Network not joined yet. Try to join again JoinNetwork( ); } else { DeviceState = DEVICE_STATE_SEND; NextTx = true; } } } /*! * \brief Function executed on Led 1 Timeout event */ static void OnLed1TimerEvent( void* context ) { TimerStop( &Led1Timer ); // Switch LED 1 OFF GpioWrite( &Led1, 0 ); } /*! * \brief Function executed on Led 3 Timeout event */ static void OnLed3TimerEvent( void* context ) { TimerStop( &Led3Timer ); // Switch LED 3 OFF GpioWrite( &Led3, 0 ); } /*! * \brief MCPS-Confirm event function * * \param [IN] mcpsConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) { printf( "\n###### ===== MCPS-Confirm ==== ######\n" ); printf( "STATUS : %s\n", EventInfoStatusStrings[mcpsConfirm->Status] ); if( mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK ) { } else { switch( mcpsConfirm->McpsRequest ) { case MCPS_UNCONFIRMED: { // Check Datarate // Check TxPower break; } case MCPS_CONFIRMED: { // Check Datarate // Check TxPower // Check AckReceived // Check NbTrials break; } case MCPS_PROPRIETARY: { break; } default: break; } // Switch LED 1 ON GpioWrite( &Led1, 1 ); TimerStart( &Led1Timer ); } MibRequestConfirm_t mibGet; MibRequestConfirm_t mibReq; mibReq.Type = MIB_DEVICE_CLASS; LoRaMacMibGetRequestConfirm( &mibReq ); printf( "\n###### ===== UPLINK FRAME %lu ==== ######\n", mcpsConfirm->UpLinkCounter ); printf( "\n" ); printf( "CLASS : %c\n", "ABC"[mibReq.Param.Class] ); printf( "\n" ); printf( "TX PORT : %d\n", AppData.Port ); if( AppData.BufferSize != 0 ) { printf( "TX DATA : " ); if( AppData.MsgType == LORAMAC_HANDLER_CONFIRMED_MSG ) { printf( "CONFIRMED - %s\n", ( mcpsConfirm->AckReceived != 0 ) ? "ACK" : "NACK" ); } else { printf( "UNCONFIRMED\n" ); } PrintHexBuffer( AppData.Buffer, AppData.BufferSize ); } printf( "\n" ); printf( "DATA RATE : DR_%d\n", mcpsConfirm->Datarate ); mibGet.Type = MIB_CHANNELS; if( LoRaMacMibGetRequestConfirm( &mibGet ) == LORAMAC_STATUS_OK ) { printf( "U/L FREQ : %lu\n", mibGet.Param.ChannelList[mcpsConfirm->Channel].Frequency ); } printf( "TX POWER : %d\n", mcpsConfirm->TxPower ); mibGet.Type = MIB_CHANNELS_MASK; if( LoRaMacMibGetRequestConfirm( &mibGet ) == LORAMAC_STATUS_OK ) { printf("CHANNEL MASK: "); #if defined( REGION_AS923 ) || defined( REGION_CN779 ) || \ defined( REGION_EU868 ) || defined( REGION_IN865 ) || \ defined( REGION_KR920 ) || defined( REGION_EU433 ) || \ defined( REGION_RU864 ) for( uint8_t i = 0; i < 1; i++) #elif defined( REGION_AU915 ) || defined( REGION_US915 ) || defined( REGION_CN470 ) for( uint8_t i = 0; i < 5; i++) #else #error "Please define a region in the compiler options." #endif { printf("%04X ", mibGet.Param.ChannelsMask[i] ); } printf("\n"); } printf( "\n" ); } /*! * \brief MCPS-Indication event function * * \param [IN] mcpsIndication - Pointer to the indication structure, * containing indication attributes. */ static void McpsIndication( McpsIndication_t *mcpsIndication ) { printf( "\n###### ===== MCPS-Indication ==== ######\n" ); printf( "STATUS : %s\n", EventInfoStatusStrings[mcpsIndication->Status] ); if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK ) { return; } switch( mcpsIndication->McpsIndication ) { case MCPS_UNCONFIRMED: { break; } case MCPS_CONFIRMED: { break; } case MCPS_PROPRIETARY: { break; } case MCPS_MULTICAST: { break; } default: break; } // Check Multicast // Check Port // Check Datarate // Check FramePending if( mcpsIndication->FramePending == true ) { // The server signals that it has pending data to be sent. // We schedule an uplink as soon as possible to flush the server. OnTxNextPacketTimerEvent( NULL ); } // Check Buffer // Check BufferSize // Check Rssi // Check Snr // Check RxSlot if( ComplianceTest.Running == true ) { ComplianceTest.DownLinkCounter++; } if( mcpsIndication->RxData == true ) { switch( mcpsIndication->Port ) { case 1: // The application LED can be controlled on port 1 or 2 case 2: if( mcpsIndication->BufferSize == 1 ) { AppLedStateOn = mcpsIndication->Buffer[0] & 0x01; GpioWrite( &Led4, ( ( AppLedStateOn & 0x01 ) != 0 ) ? 1 : 0 ); } break; case 224: if( ComplianceTest.Running == false ) { // Check compliance test enable command (i) if( ( mcpsIndication->BufferSize == 4 ) && ( mcpsIndication->Buffer[0] == 0x01 ) && ( mcpsIndication->Buffer[1] == 0x01 ) && ( mcpsIndication->Buffer[2] == 0x01 ) && ( mcpsIndication->Buffer[3] == 0x01 ) ) { IsTxConfirmed = false; AppPort = 224; AppDataSizeBackup = AppDataSize; AppDataSize = 2; ComplianceTest.DownLinkCounter = 0; ComplianceTest.LinkCheck = false; ComplianceTest.DemodMargin = 0; ComplianceTest.NbGateways = 0; ComplianceTest.Running = true; ComplianceTest.State = 1; MibRequestConfirm_t mibReq; mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = true; LoRaMacMibSetRequestConfirm( &mibReq ); #if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 ) LoRaMacTestSetDutyCycleOn( false ); #endif } } else { ComplianceTest.State = mcpsIndication->Buffer[0]; switch( ComplianceTest.State ) { case 0: // Check compliance test disable command (ii) IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON; AppPort = LORAWAN_APP_PORT; AppDataSize = AppDataSizeBackup; ComplianceTest.DownLinkCounter = 0; ComplianceTest.Running = false; MibRequestConfirm_t mibReq; mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = LORAWAN_ADR_ON; LoRaMacMibSetRequestConfirm( &mibReq ); #if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 ) LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); #endif break; case 1: // (iii, iv) AppDataSize = 2; break; case 2: // Enable confirmed messages (v) IsTxConfirmed = true; ComplianceTest.State = 1; break; case 3: // Disable confirmed messages (vi) IsTxConfirmed = false; ComplianceTest.State = 1; break; case 4: // (vii) AppDataSize = mcpsIndication->BufferSize; AppDataBuffer[0] = 4; for( uint8_t i = 1; i < MIN( AppDataSize, LORAWAN_APP_DATA_MAX_SIZE ); i++ ) { AppDataBuffer[i] = mcpsIndication->Buffer[i] + 1; } break; case 5: // (viii) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_LINK_CHECK; LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq ); printf( "\n###### ===== MLME-Request - MLME_LINK_CHECK ==== ######\n" ); printf( "STATUS : %s\n", MacStatusStrings[status] ); } break; case 6: // (ix) { // Disable TestMode and revert back to normal operation IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON; AppPort = LORAWAN_APP_PORT; AppDataSize = AppDataSizeBackup; ComplianceTest.DownLinkCounter = 0; ComplianceTest.Running = false; MibRequestConfirm_t mibReq; mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = LORAWAN_ADR_ON; LoRaMacMibSetRequestConfirm( &mibReq ); #if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 ) LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); #endif JoinNetwork( ); } break; case 7: // (x) { if( mcpsIndication->BufferSize == 3 ) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_TXCW; mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq ); printf( "\n###### ===== MLME-Request - MLME_TXCW ==== ######\n" ); printf( "STATUS : %s\n", MacStatusStrings[status] ); } else if( mcpsIndication->BufferSize == 7 ) { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_TXCW_1; mlmeReq.Req.TxCw.Timeout = ( uint16_t )( ( mcpsIndication->Buffer[1] << 8 ) | mcpsIndication->Buffer[2] ); mlmeReq.Req.TxCw.Frequency = ( uint32_t )( ( mcpsIndication->Buffer[3] << 16 ) | ( mcpsIndication->Buffer[4] << 8 ) | mcpsIndication->Buffer[5] ) * 100; mlmeReq.Req.TxCw.Power = mcpsIndication->Buffer[6]; LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq ); printf( "\n###### ===== MLME-Request - MLME_TXCW1 ==== ######\n" ); printf( "STATUS : %s\n", MacStatusStrings[status] ); } ComplianceTest.State = 1; } break; case 8: // Send DeviceTimeReq { MlmeReq_t mlmeReq; mlmeReq.Type = MLME_DEVICE_TIME; LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq ); printf( "\n###### ===== MLME-Request - MLME_DEVICE_TIME ==== ######\n" ); printf( "STATUS : %s\n", MacStatusStrings[status] ); } break; default: break; } } break; default: break; } } // Switch LED 3 ON for each received downlink GpioWrite( &Led3, 1 ); TimerStart( &Led3Timer ); const char *slotStrings[] = { "1", "2", "C", "C Multicast", "B Ping-Slot", "B Multicast Ping-Slot" }; printf( "\n###### ===== DOWNLINK FRAME %lu ==== ######\n", mcpsIndication->DownLinkCounter ); printf( "RX WINDOW : %s\n", slotStrings[mcpsIndication->RxSlot] ); printf( "RX PORT : %d\n", mcpsIndication->Port ); if( mcpsIndication->BufferSize != 0 ) { printf( "RX DATA : \n" ); PrintHexBuffer( mcpsIndication->Buffer, mcpsIndication->BufferSize ); } printf( "\n" ); printf( "DATA RATE : DR_%d\n", mcpsIndication->RxDatarate ); printf( "RX RSSI : %d\n", mcpsIndication->Rssi ); printf( "RX SNR : %d\n", mcpsIndication->Snr ); printf( "\n" ); } /*! * \brief MLME-Confirm event function * * \param [IN] mlmeConfirm - Pointer to the confirm structure, * containing confirm attributes. */ static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) { printf( "\n###### ===== MLME-Confirm ==== ######\n" ); printf( "STATUS : %s\n", EventInfoStatusStrings[mlmeConfirm->Status] ); if( mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK ) { } switch( mlmeConfirm->MlmeRequest ) { case MLME_JOIN: { if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { MibRequestConfirm_t mibGet; printf( "###### ===== JOINED ==== ######\n" ); printf( "\nOTAA\n\n" ); mibGet.Type = MIB_DEV_ADDR; LoRaMacMibGetRequestConfirm( &mibGet ); printf( "DevAddr : %08lX\n", mibGet.Param.DevAddr ); printf( "\n\n" ); mibGet.Type = MIB_CHANNELS_DATARATE; LoRaMacMibGetRequestConfirm( &mibGet ); printf( "DATA RATE : DR_%d\n", mibGet.Param.ChannelsDatarate ); printf( "\n" ); // Status is OK, node has joined the network DeviceState = DEVICE_STATE_SEND; } else { // Join was not successful. Try to join again JoinNetwork( ); } break; } case MLME_LINK_CHECK: { if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) { // Check DemodMargin // Check NbGateways if( ComplianceTest.Running == true ) { ComplianceTest.LinkCheck = true; ComplianceTest.DemodMargin = mlmeConfirm->DemodMargin; ComplianceTest.NbGateways = mlmeConfirm->NbGateways; } } break; } default: break; } } /*! * \brief MLME-Indication event function * * \param [IN] mlmeIndication - Pointer to the indication structure. */ static void MlmeIndication( MlmeIndication_t *mlmeIndication ) { if( mlmeIndication->Status != LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED ) { printf( "\n###### ===== MLME-Indication ==== ######\n" ); printf( "STATUS : %s\n", EventInfoStatusStrings[mlmeIndication->Status] ); } if( mlmeIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK ) { } switch( mlmeIndication->MlmeIndication ) { case MLME_SCHEDULE_UPLINK: {// The MAC signals that we shall provide an uplink as soon as possible OnTxNextPacketTimerEvent( NULL ); break; } default: break; } } void OnMacProcessNotify( void ) { IsMacProcessPending = 1; } /** * Main application entry point. */ /** 0: Band unknown, 1: LF band, 2: HF band */ extern uint8_t sx1276_band; int lora_main( void ) { LoRaMacPrimitives_t macPrimitives; LoRaMacCallback_t macCallbacks; MibRequestConfirm_t mibReq; LoRaMacStatus_t status; uint8_t devEui[8] = { 0 }; // Automatically filed from secure-element uint8_t joinEui[8] = { 0 }; // Automatically filed from secure-element uint8_t sePin[4] = { 0 }; // Automatically filed from secure-element sx1276_band = 2; BoardInitMcu( ); BoardInitPeriph( ); macPrimitives.MacMcpsConfirm = McpsConfirm; macPrimitives.MacMcpsIndication = McpsIndication; macPrimitives.MacMlmeConfirm = MlmeConfirm; macPrimitives.MacMlmeIndication = MlmeIndication; macCallbacks.GetBatteryLevel = BoardGetBatteryLevel; macCallbacks.GetTemperatureLevel = NULL; macCallbacks.NvmContextChange = NvmCtxMgmtEvent; macCallbacks.MacProcessNotify = OnMacProcessNotify; status = LoRaMacInitialization( &macPrimitives, &macCallbacks, ACTIVE_REGION ); if ( status != LORAMAC_STATUS_OK ) { printf( "LoRaMac wasn't properly initialized, error: %s", MacStatusStrings[status] ); // Fatal error, endless loop. while ( 1 ) { } } DeviceState = DEVICE_STATE_RESTORE; printf( "###### ===== ClassA demo application v1.0.0 ==== ######\n\n" ); while( 1 ) { // Process Radio IRQ if( Radio.IrqProcess != NULL ) { Radio.IrqProcess( ); } // Processes the LoRaMac events LoRaMacProcess( ); switch( DeviceState ) { case DEVICE_STATE_RESTORE: { // Try to restore from NVM and query the mac if possible. if( NvmCtxMgmtRestore( ) == NVMCTXMGMT_STATUS_SUCCESS ) { printf( "\n###### ===== CTXS RESTORED ==== ######\n\n" ); } else { // Read secure-element DEV_EUI, JOI_EUI and SE_PIN values. mibReq.Type = MIB_DEV_EUI; LoRaMacMibGetRequestConfirm( &mibReq ); memcpy1( devEui, mibReq.Param.DevEui, 8 ); mibReq.Type = MIB_JOIN_EUI; LoRaMacMibGetRequestConfirm( &mibReq ); memcpy1( joinEui, mibReq.Param.JoinEui, 8 ); mibReq.Type = MIB_SE_PIN; LoRaMacMibGetRequestConfirm( &mibReq ); memcpy1( sePin, mibReq.Param.SePin, 4 ); #if( OVER_THE_AIR_ACTIVATION == 0 ) // Tell the MAC layer which network server version are we connecting too. mibReq.Type = MIB_ABP_LORAWAN_VERSION; mibReq.Param.AbpLrWanVersion.Value = ABP_ACTIVATION_LRWAN_VERSION; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_NET_ID; mibReq.Param.NetID = LORAWAN_NETWORK_ID; LoRaMacMibSetRequestConfirm( &mibReq ); // Choose a random device address if not already defined in Commissioning.h #if( STATIC_DEVICE_ADDRESS != 1 ) // Random seed initialization srand1( BoardGetRandomSeed( ) ); // Choose a random device address DevAddr = randr( 0, 0x01FFFFFF ); #endif mibReq.Type = MIB_DEV_ADDR; mibReq.Param.DevAddr = DevAddr; LoRaMacMibSetRequestConfirm( &mibReq ); #endif // #if( OVER_THE_AIR_ACTIVATION == 0 ) } DeviceState = DEVICE_STATE_START; break; } case DEVICE_STATE_START: { TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent ); TimerInit( &Led1Timer, OnLed1TimerEvent ); TimerSetValue( &Led1Timer, 25 ); TimerInit( &Led3Timer, OnLed3TimerEvent ); TimerSetValue( &Led3Timer, 25 ); mibReq.Type = MIB_PUBLIC_NETWORK; mibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK; LoRaMacMibSetRequestConfirm( &mibReq ); mibReq.Type = MIB_ADR; mibReq.Param.AdrEnable = LORAWAN_ADR_ON; LoRaMacMibSetRequestConfirm( &mibReq ); #if defined( REGION_EU868 ) || defined( REGION_RU864 ) || defined( REGION_CN779 ) || defined( REGION_EU433 ) LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); #endif mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; mibReq.Param.SystemMaxRxError = 20; LoRaMacMibSetRequestConfirm( &mibReq ); LoRaMacStart( ); mibReq.Type = MIB_NETWORK_ACTIVATION; status = LoRaMacMibGetRequestConfirm( &mibReq ); if( status == LORAMAC_STATUS_OK ) { if( mibReq.Param.NetworkActivation == ACTIVATION_TYPE_NONE ) { DeviceState = DEVICE_STATE_JOIN; } else { DeviceState = DEVICE_STATE_SEND; NextTx = true; } } break; } case DEVICE_STATE_JOIN: { mibReq.Type = MIB_DEV_EUI; LoRaMacMibGetRequestConfirm( &mibReq ); printf( "DevEui : %02X", mibReq.Param.DevEui[0] ); for( int i = 1; i < 8; i++ ) { printf( "-%02X", mibReq.Param.DevEui[i] ); } printf( "\n" ); mibReq.Type = MIB_JOIN_EUI; LoRaMacMibGetRequestConfirm( &mibReq ); printf( "JoinEui : %02X", mibReq.Param.JoinEui[0] ); for( int i = 1; i < 8; i++ ) { printf( "-%02X", mibReq.Param.JoinEui[i] ); } printf( "\n" ); mibReq.Type = MIB_SE_PIN; LoRaMacMibGetRequestConfirm( &mibReq ); printf( "Pin : %02X", mibReq.Param.SePin[0] ); for( int i = 1; i < 4; i++ ) { printf( "-%02X", mibReq.Param.SePin[i] ); } printf( "\n\n" ); #if( OVER_THE_AIR_ACTIVATION == 0 ) printf( "###### ===== JOINED ==== ######\n" ); printf( "\nABP\n\n" ); printf( "DevAddr : %08lX\n", DevAddr ); printf( "\n\n" ); mibReq.Type = MIB_NETWORK_ACTIVATION; mibReq.Param.NetworkActivation = ACTIVATION_TYPE_ABP; LoRaMacMibSetRequestConfirm( &mibReq ); DeviceState = DEVICE_STATE_SEND; #else JoinNetwork( ); #endif break; } case DEVICE_STATE_SEND: { if( NextTx == true ) { PrepareTxFrame( AppPort ); NextTx = SendFrame( ); } DeviceState = DEVICE_STATE_CYCLE; break; } case DEVICE_STATE_CYCLE: { DeviceState = DEVICE_STATE_SLEEP; if( ComplianceTest.Running == true ) { // Schedule next packet transmission TxDutyCycleTime = 5000; // 5000 ms } else { // Schedule next packet transmission TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); } // Schedule next packet transmission TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime ); TimerStart( &TxNextPacketTimer ); break; } case DEVICE_STATE_SLEEP: { if( NvmCtxMgmtStore( ) == NVMCTXMGMT_STATUS_SUCCESS ) { printf( "\n###### ===== CTXS STORED ==== ######\n" ); } CRITICAL_SECTION_BEGIN( ); if( IsMacProcessPending == 1 ) { // Clear flag and prevent MCU to go into low power modes. IsMacProcessPending = 0; } else { // The MCU wakes up through events BoardLowPowerHandler( ); } CRITICAL_SECTION_END( ); break; } default: { DeviceState = DEVICE_STATE_START; break; } } } }