Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
main.cpp
00001 /* 00002 / _____) _ | | 00003 ( (____ _____ ____ _| |_ _____ ____| |__ 00004 \____ \| ___ | (_ _) ___ |/ ___) _ \ 00005 _____) ) ____| | | || |_| ____( (___| | | | 00006 (______/|_____)_|_|_| \__)_____)\____)_| |_| 00007 (C)2015 Semtech 00008 00009 Description: LoRaMac classA device implementation 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 #include "board.h" 00017 #include "radio.h" 00018 00019 #include "LoRaMac.h" 00020 #include "Commissioning.h" 00021 #include "SerialDisplay.h" 00022 00023 /*! 00024 * Defines the application data transmission duty cycle. 5s, value in [ms]. 00025 */ 00026 #define APP_TX_DUTYCYCLE 5000 00027 00028 /*! 00029 * Defines a random delay for application data transmission duty cycle. 1s, 00030 * value in [ms]. 00031 */ 00032 #define APP_TX_DUTYCYCLE_RND 1000 00033 00034 /*! 00035 * Default datarate 00036 */ 00037 #define LORAWAN_DEFAULT_DATARATE DR_0 00038 00039 /*! 00040 * LoRaWAN confirmed messages 00041 */ 00042 #define LORAWAN_CONFIRMED_MSG_ON true 00043 00044 /*! 00045 * LoRaWAN Adaptive Data Rate 00046 * 00047 * \remark Please note that when ADR is enabled the end-device should be static 00048 */ 00049 #define LORAWAN_ADR_ON 1 00050 00051 #if defined( USE_BAND_868 ) 00052 00053 #include "LoRaMacTest.h" 00054 00055 /*! 00056 * LoRaWAN ETSI duty cycle control enable/disable 00057 * 00058 * \remark Please note that ETSI mandates duty cycled transmissions. Use only for test purposes 00059 */ 00060 #define LORAWAN_DUTYCYCLE_ON false 00061 00062 #define USE_SEMTECH_DEFAULT_CHANNEL_LINEUP 1 00063 00064 #if( USE_SEMTECH_DEFAULT_CHANNEL_LINEUP == 1 ) 00065 00066 #define LC4 { 867100000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } 00067 #define LC5 { 867300000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } 00068 #define LC6 { 867500000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } 00069 #define LC7 { 867700000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } 00070 #define LC8 { 867900000, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } 00071 #define LC9 { 868800000, { ( ( DR_7 << 4 ) | DR_7 ) }, 2 } 00072 #define LC10 { 868300000, { ( ( DR_6 << 4 ) | DR_6 ) }, 1 } 00073 00074 #endif 00075 00076 #endif 00077 00078 /*! 00079 * LoRaWAN application port 00080 */ 00081 #define LORAWAN_APP_PORT 15 00082 00083 /*! 00084 * User application data buffer size 00085 */ 00086 #define LORAWAN_APP_DATA_SIZE 41 00087 00088 00089 00090 static uint8_t DevEui[] = LORAWAN_DEVICE_EUI; 00091 static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI; 00092 static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY; 00093 00094 #if( OVER_THE_AIR_ACTIVATION == 0 ) 00095 00096 static uint8_t NwkSKey[] = LORAWAN_NWKSKEY; 00097 static uint8_t AppSKey[] = LORAWAN_APPSKEY; 00098 00099 /*! 00100 * Device address 00101 */ 00102 static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS; 00103 00104 #endif 00105 00106 /*! 00107 * Application port 00108 */ 00109 static uint8_t AppPort = LORAWAN_APP_PORT; 00110 00111 /*! 00112 * User application data size 00113 */ 00114 static uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE; 00115 00116 /*! 00117 * User application data buffer size 00118 */ 00119 #define LORAWAN_APP_DATA_MAX_SIZE 64 00120 00121 /*! 00122 * User application data 00123 */ 00124 static uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE]; 00125 00126 /*! 00127 * Indicates if the node is sending confirmed or unconfirmed messages 00128 */ 00129 static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON; 00130 00131 /*! 00132 * Defines the application data transmission duty cycle 00133 */ 00134 static uint32_t TxDutyCycleTime; 00135 00136 /*! 00137 * Timer to handle the application data transmission duty cycle 00138 */ 00139 static TimerEvent_t TxNextPacketTimer; 00140 00141 /*! 00142 * Indicates if a new packet can be sent 00143 */ 00144 static bool NextTx = true; 00145 00146 /*! 00147 * Device states 00148 */ 00149 static enum eDeviceState 00150 { 00151 DEVICE_STATE_INIT, 00152 DEVICE_STATE_JOIN, 00153 DEVICE_STATE_SEND, 00154 DEVICE_STATE_CYCLE, 00155 DEVICE_STATE_SLEEP 00156 }DeviceState; 00157 00158 /*! 00159 * LoRaWAN compliance tests support data 00160 */ 00161 struct ComplianceTest_s 00162 { 00163 bool Running; 00164 uint8_t State; 00165 bool IsTxConfirmed; 00166 uint8_t AppPort; 00167 uint8_t AppDataSize; 00168 uint8_t *AppDataBuffer; 00169 uint16_t DownLinkCounter; 00170 bool LinkCheck; 00171 uint8_t DemodMargin; 00172 uint8_t NbGateways; 00173 }ComplianceTest; 00174 00175 /* 00176 * SerialDisplay managment variables 00177 */ 00178 00179 /*! 00180 * Indicates if the MAC layer network join status has changed. 00181 */ 00182 static bool IsNetworkJoinedStatusUpdate = false; 00183 00184 /*! 00185 * Strucure containing the Uplink status 00186 */ 00187 struct sLoRaMacUplinkStatus 00188 { 00189 uint8_t Acked; 00190 int8_t Datarate; 00191 uint16_t UplinkCounter; 00192 uint8_t Port; 00193 uint8_t *Buffer; 00194 uint8_t BufferSize; 00195 }LoRaMacUplinkStatus; 00196 volatile bool UplinkStatusUpdated = false; 00197 00198 /*! 00199 * Strucure containing the Downlink status 00200 */ 00201 struct sLoRaMacDownlinkStatus 00202 { 00203 int16_t Rssi; 00204 int8_t Snr; 00205 uint16_t DownlinkCounter; 00206 bool RxData; 00207 uint8_t Port; 00208 uint8_t *Buffer; 00209 uint8_t BufferSize; 00210 }LoRaMacDownlinkStatus; 00211 volatile bool DownlinkStatusUpdated = false; 00212 00213 00214 // Application Globals 00215 DigitalOut myled(D7); 00216 00217 00218 00219 void SerialDisplayRefresh( void ) 00220 { 00221 MibRequestConfirm_t mibReq; 00222 00223 SerialDisplayInit( ); 00224 SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION ); 00225 00226 #if( OVER_THE_AIR_ACTIVATION == 0 ) 00227 SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID ); 00228 SerialDisplayUpdateDevAddr( DevAddr ); 00229 SerialDisplayUpdateKey( 12, NwkSKey ); 00230 SerialDisplayUpdateKey( 13, AppSKey ); 00231 #endif 00232 SerialDisplayUpdateEui( 5, DevEui ); 00233 SerialDisplayUpdateEui( 6, AppEui ); 00234 SerialDisplayUpdateKey( 7, AppKey ); 00235 00236 mibReq.Type = MIB_NETWORK_JOINED ; 00237 LoRaMacMibGetRequestConfirm( &mibReq ); 00238 SerialDisplayUpdateNetworkIsJoined( mibReq.Param .IsNetworkJoined ); 00239 00240 SerialDisplayUpdateAdr( LORAWAN_ADR_ON ); 00241 #if defined( USE_BAND_868 ) 00242 SerialDisplayUpdateDutyCycle( LORAWAN_DUTYCYCLE_ON ); 00243 #else 00244 SerialDisplayUpdateDutyCycle( false ); 00245 #endif 00246 SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK ); 00247 } 00248 00249 void SerialRxProcess( void ) 00250 { 00251 if( SerialDisplayReadable( ) == true ) 00252 { 00253 switch( SerialDisplayGetChar( ) ) 00254 { 00255 case 'R': 00256 case 'r': 00257 // Refresh Serial screen 00258 SerialDisplayRefresh( ); 00259 break; 00260 default: 00261 break; 00262 } 00263 } 00264 } 00265 00266 /*! 00267 * \brief Prepares the payload of the frame 00268 */ 00269 static void PrepareTxFrame( uint8_t port ) 00270 { 00271 switch( port ) 00272 { 00273 case 15: 00274 { 00275 AppData[0] = 0; 00276 if( IsTxConfirmed == true ) 00277 { 00278 AppData[1] = LoRaMacDownlinkStatus.DownlinkCounter >> 8; 00279 AppData[2] = LoRaMacDownlinkStatus.DownlinkCounter; 00280 AppData[3] = LoRaMacDownlinkStatus.Rssi >> 8; 00281 AppData[4] = LoRaMacDownlinkStatus.Rssi; 00282 AppData[5] = LoRaMacDownlinkStatus.Snr; 00283 } 00284 } 00285 break; 00286 case 224: 00287 if( ComplianceTest.LinkCheck == true ) 00288 { 00289 ComplianceTest.LinkCheck = false; 00290 AppDataSize = 3; 00291 AppData[0] = 5; 00292 AppData[1] = ComplianceTest.DemodMargin; 00293 AppData[2] = ComplianceTest.NbGateways; 00294 ComplianceTest.State = 1; 00295 } 00296 else 00297 { 00298 switch( ComplianceTest.State ) 00299 { 00300 case 4: 00301 ComplianceTest.State = 1; 00302 break; 00303 case 1: 00304 AppDataSize = 2; 00305 AppData[0] = ComplianceTest.DownLinkCounter >> 8; 00306 AppData[1] = ComplianceTest.DownLinkCounter; 00307 break; 00308 } 00309 } 00310 break; 00311 default: 00312 break; 00313 } 00314 } 00315 00316 /*! 00317 * \brief Prepares the payload of the frame 00318 * 00319 * \retval [0: frame could be send, 1: error] 00320 */ 00321 static bool SendFrame( void ) 00322 { 00323 McpsReq_t mcpsReq; 00324 LoRaMacTxInfo_t txInfo; 00325 00326 if( LoRaMacQueryTxPossible( AppDataSize, &txInfo ) != LORAMAC_STATUS_OK ) 00327 { 00328 // Send empty frame in order to flush MAC commands 00329 mcpsReq.Type = MCPS_UNCONFIRMED ; 00330 mcpsReq.Req.Unconfirmed .fBuffer = NULL; 00331 mcpsReq.Req.Unconfirmed .fBufferSize = 0; 00332 mcpsReq.Req.Unconfirmed .Datarate = LORAWAN_DEFAULT_DATARATE; 00333 00334 LoRaMacUplinkStatus.Acked = false; 00335 LoRaMacUplinkStatus.Port = 0; 00336 LoRaMacUplinkStatus.Buffer = NULL; 00337 LoRaMacUplinkStatus.BufferSize = 0; 00338 SerialDisplayUpdateFrameType( false ); 00339 } 00340 else 00341 { 00342 LoRaMacUplinkStatus.Acked = false; 00343 LoRaMacUplinkStatus.Port = AppPort; 00344 LoRaMacUplinkStatus.Buffer = AppData; 00345 LoRaMacUplinkStatus.BufferSize = AppDataSize; 00346 SerialDisplayUpdateFrameType( IsTxConfirmed ); 00347 00348 if( IsTxConfirmed == false ) 00349 { 00350 mcpsReq.Type = MCPS_UNCONFIRMED ; 00351 mcpsReq.Req.Unconfirmed .fPort = AppPort; 00352 mcpsReq.Req.Unconfirmed .fBuffer = AppData; 00353 mcpsReq.Req.Unconfirmed .fBufferSize = AppDataSize; 00354 mcpsReq.Req.Unconfirmed .Datarate = LORAWAN_DEFAULT_DATARATE; 00355 } 00356 else 00357 { 00358 mcpsReq.Type = MCPS_CONFIRMED ; 00359 mcpsReq.Req.Confirmed .fPort = AppPort; 00360 mcpsReq.Req.Confirmed .fBuffer = AppData; 00361 mcpsReq.Req.Confirmed .fBufferSize = AppDataSize; 00362 mcpsReq.Req.Confirmed .NbTrials = 8; 00363 mcpsReq.Req.Confirmed .Datarate = LORAWAN_DEFAULT_DATARATE; 00364 } 00365 } 00366 00367 if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) 00368 { 00369 return false; 00370 } 00371 return true; 00372 } 00373 00374 /*! 00375 * \brief Function executed on TxNextPacket Timeout event 00376 */ 00377 static void OnTxNextPacketTimerEvent( void ) 00378 { 00379 MibRequestConfirm_t mibReq; 00380 LoRaMacStatus_t status; 00381 00382 TimerStop( &TxNextPacketTimer ); 00383 00384 mibReq.Type = MIB_NETWORK_JOINED ; 00385 status = LoRaMacMibGetRequestConfirm( &mibReq ); 00386 00387 if( status == LORAMAC_STATUS_OK ) 00388 { 00389 if( mibReq.Param .IsNetworkJoined == true ) 00390 { 00391 DeviceState = DEVICE_STATE_SEND; 00392 NextTx = true; 00393 } 00394 else 00395 { 00396 DeviceState = DEVICE_STATE_JOIN; 00397 } 00398 } 00399 } 00400 00401 00402 /*! 00403 * \brief MCPS-Confirm event function 00404 * 00405 * \param [IN] mcpsConfirm - Pointer to the confirm structure, 00406 * containing confirm attributes. 00407 */ 00408 static void McpsConfirm( McpsConfirm_t *mcpsConfirm ) 00409 { 00410 if( mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) 00411 { 00412 switch( mcpsConfirm->McpsRequest ) 00413 { 00414 case MCPS_UNCONFIRMED : 00415 { 00416 // Check Datarate 00417 // Check TxPower 00418 break; 00419 } 00420 case MCPS_CONFIRMED : 00421 { 00422 // Check Datarate 00423 // Check TxPower 00424 // Check AckReceived 00425 // Check NbTrials 00426 LoRaMacUplinkStatus.Acked = mcpsConfirm->AckReceived ; 00427 break; 00428 } 00429 case MCPS_PROPRIETARY : 00430 { 00431 break; 00432 } 00433 default: 00434 break; 00435 } 00436 LoRaMacUplinkStatus.Datarate = mcpsConfirm->Datarate ; 00437 LoRaMacUplinkStatus.UplinkCounter = mcpsConfirm->UpLinkCounter ; 00438 00439 UplinkStatusUpdated = true; 00440 } 00441 NextTx = true; 00442 } 00443 00444 /*! 00445 * \brief MCPS-Indication event function 00446 * 00447 * \param [IN] mcpsIndication - Pointer to the indication structure, 00448 * containing indication attributes. 00449 */ 00450 static void McpsIndication( McpsIndication_t *mcpsIndication ) 00451 { 00452 if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK ) 00453 { 00454 return; 00455 } 00456 00457 switch( mcpsIndication->McpsIndication ) 00458 { 00459 case MCPS_UNCONFIRMED : 00460 { 00461 break; 00462 } 00463 case MCPS_CONFIRMED : 00464 { 00465 break; 00466 } 00467 case MCPS_PROPRIETARY : 00468 { 00469 break; 00470 } 00471 case MCPS_MULTICAST : 00472 { 00473 break; 00474 } 00475 default: 00476 break; 00477 } 00478 00479 // Check Multicast 00480 // Check Port 00481 // Check Datarate 00482 // Check FramePending 00483 // Check Buffer 00484 // Check BufferSize 00485 // Check Rssi 00486 // Check Snr 00487 // Check RxSlot 00488 LoRaMacDownlinkStatus.Rssi = mcpsIndication->Rssi ; 00489 if( mcpsIndication->Snr & 0x80 ) // The SNR sign bit is 1 00490 { 00491 // Invert and divide by 4 00492 LoRaMacDownlinkStatus.Snr = ( ( ~mcpsIndication->Snr + 1 ) & 0xFF ) >> 2; 00493 LoRaMacDownlinkStatus.Snr = -LoRaMacDownlinkStatus.Snr; 00494 } 00495 else 00496 { 00497 // Divide by 4 00498 LoRaMacDownlinkStatus.Snr = ( mcpsIndication->Snr & 0xFF ) >> 2; 00499 } 00500 LoRaMacDownlinkStatus.DownlinkCounter++; 00501 LoRaMacDownlinkStatus.RxData = mcpsIndication->RxData ; 00502 LoRaMacDownlinkStatus.Port = mcpsIndication->Port ; 00503 LoRaMacDownlinkStatus.Buffer = mcpsIndication->Buffer ; 00504 LoRaMacDownlinkStatus.BufferSize = mcpsIndication->BufferSize ; 00505 00506 if( ComplianceTest.Running == true ) 00507 { 00508 ComplianceTest.DownLinkCounter++; 00509 } 00510 00511 if( mcpsIndication->RxData == true ) 00512 { 00513 switch( mcpsIndication->Port ) 00514 { 00515 case 1: // The application LED can be controlled on port 1 or 2 00516 case 2: 00517 break; 00518 case 224: 00519 if( ComplianceTest.Running == false ) 00520 { 00521 // Check compliance test enable command (i) 00522 if( ( mcpsIndication->BufferSize == 4 ) && 00523 ( mcpsIndication->Buffer [0] == 0x01 ) && 00524 ( mcpsIndication->Buffer [1] == 0x01 ) && 00525 ( mcpsIndication->Buffer [2] == 0x01 ) && 00526 ( mcpsIndication->Buffer [3] == 0x01 ) ) 00527 { 00528 IsTxConfirmed = false; 00529 AppPort = 224; 00530 AppDataSize = 2; 00531 ComplianceTest.DownLinkCounter = 0; 00532 ComplianceTest.LinkCheck = false; 00533 ComplianceTest.DemodMargin = 0; 00534 ComplianceTest.NbGateways = 0; 00535 ComplianceTest.Running = true; 00536 ComplianceTest.State = 1; 00537 00538 MibRequestConfirm_t mibReq; 00539 mibReq.Type = MIB_ADR ; 00540 mibReq.Param .AdrEnable = true; 00541 LoRaMacMibSetRequestConfirm( &mibReq ); 00542 00543 // Limiting to just 2nd subband of 8 channels 00544 static uint16_t GatewayChannelsMask[] = {0xFF00, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000}; 00545 mibReq.Type = MIB_CHANNELS_DEFAULT_MASK ; 00546 mibReq.Param .ChannelsDefaultMask = GatewayChannelsMask; 00547 LoRaMacMibSetRequestConfirm( &mibReq ); 00548 00549 mibReq.Type = MIB_CHANNELS_MASK ; 00550 mibReq.Param .ChannelsMask = GatewayChannelsMask; 00551 LoRaMacMibSetRequestConfirm( &mibReq ); 00552 00553 00554 00555 #if defined( USE_BAND_868 ) 00556 LoRaMacTestSetDutyCycleOn( false ); 00557 #endif 00558 } 00559 } 00560 else 00561 { 00562 ComplianceTest.State = mcpsIndication->Buffer [0]; 00563 switch( ComplianceTest.State ) 00564 { 00565 case 0: // Check compliance test disable command (ii) 00566 IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON; 00567 AppPort = LORAWAN_APP_PORT; 00568 AppDataSize = LORAWAN_APP_DATA_SIZE; 00569 ComplianceTest.DownLinkCounter = 0; 00570 ComplianceTest.Running = false; 00571 00572 MibRequestConfirm_t mibReq; 00573 mibReq.Type = MIB_ADR ; 00574 mibReq.Param .AdrEnable = LORAWAN_ADR_ON; 00575 LoRaMacMibSetRequestConfirm( &mibReq ); 00576 #if defined( USE_BAND_868 ) 00577 LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); 00578 #endif 00579 break; 00580 case 1: // (iii, iv) 00581 AppDataSize = 2; 00582 break; 00583 case 2: // Enable confirmed messages (v) 00584 IsTxConfirmed = true; 00585 ComplianceTest.State = 1; 00586 break; 00587 case 3: // Disable confirmed messages (vi) 00588 IsTxConfirmed = false; 00589 ComplianceTest.State = 1; 00590 break; 00591 case 4: // (vii) 00592 AppDataSize = mcpsIndication->BufferSize ; 00593 00594 AppData[0] = 4; 00595 for( uint8_t i = 1; i < AppDataSize; i++ ) 00596 { 00597 AppData[i] = mcpsIndication->Buffer [i] + 1; 00598 } 00599 break; 00600 case 5: // (viii) 00601 { 00602 MlmeReq_t mlmeReq; 00603 mlmeReq.Type = MLME_LINK_CHECK ; 00604 LoRaMacMlmeRequest( &mlmeReq ); 00605 } 00606 break; 00607 case 6: // (ix) 00608 { 00609 MlmeReq_t mlmeReq; 00610 00611 // Disable TestMode and revert back to normal operation 00612 IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON; 00613 AppPort = LORAWAN_APP_PORT; 00614 AppDataSize = LORAWAN_APP_DATA_SIZE; 00615 ComplianceTest.DownLinkCounter = 0; 00616 ComplianceTest.Running = false; 00617 00618 MibRequestConfirm_t mibReq; 00619 mibReq.Type = MIB_ADR ; 00620 mibReq.Param .AdrEnable = LORAWAN_ADR_ON; 00621 LoRaMacMibSetRequestConfirm( &mibReq ); 00622 #if defined( USE_BAND_868 ) 00623 LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); 00624 #endif 00625 00626 mlmeReq.Type = MLME_JOIN ; 00627 00628 mlmeReq.Req.Join .DevEui = DevEui; 00629 mlmeReq.Req.Join .AppEui = AppEui; 00630 mlmeReq.Req.Join .AppKey = AppKey; 00631 mlmeReq.Req.Join .NbTrials = 3; 00632 00633 LoRaMacMlmeRequest( &mlmeReq ); 00634 DeviceState = DEVICE_STATE_SLEEP; 00635 } 00636 break; 00637 case 7: // (x) 00638 { 00639 if( mcpsIndication->BufferSize == 3 ) 00640 { 00641 MlmeReq_t mlmeReq; 00642 mlmeReq.Type = MLME_TXCW ; 00643 mlmeReq.Req.TxCw .Timeout = ( uint16_t )( ( mcpsIndication->Buffer [1] << 8 ) | mcpsIndication->Buffer [2] ); 00644 LoRaMacMlmeRequest( &mlmeReq ); 00645 } 00646 else if( mcpsIndication->BufferSize == 7 ) 00647 { 00648 MlmeReq_t mlmeReq; 00649 mlmeReq.Type = MLME_TXCW_1 ; 00650 mlmeReq.Req.TxCw .Timeout = ( uint16_t )( ( mcpsIndication->Buffer [1] << 8 ) | mcpsIndication->Buffer [2] ); 00651 mlmeReq.Req.TxCw .Frequency = ( uint32_t )( ( mcpsIndication->Buffer [3] << 16 ) | ( mcpsIndication->Buffer [4] << 8 ) | mcpsIndication->Buffer [5] ) * 100; 00652 mlmeReq.Req.TxCw .Power = mcpsIndication->Buffer [6]; 00653 LoRaMacMlmeRequest( &mlmeReq ); 00654 } 00655 ComplianceTest.State = 1; 00656 } 00657 break; 00658 default: 00659 break; 00660 } 00661 } 00662 break; 00663 default: 00664 break; 00665 } 00666 } 00667 00668 DownlinkStatusUpdated = true; 00669 } 00670 00671 /*! 00672 * \brief MLME-Confirm event function 00673 * 00674 * \param [IN] mlmeConfirm - Pointer to the confirm structure, 00675 * containing confirm attributes. 00676 */ 00677 static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm ) 00678 { 00679 switch( mlmeConfirm->MlmeRequest ) 00680 { 00681 case MLME_JOIN : 00682 { 00683 if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) 00684 { 00685 // Status is OK, node has joined the network 00686 IsNetworkJoinedStatusUpdate = true; 00687 DeviceState = DEVICE_STATE_SEND; 00688 } 00689 else 00690 { 00691 // Join was not successful. Try to join again 00692 DeviceState = DEVICE_STATE_JOIN; 00693 } 00694 break; 00695 } 00696 case MLME_LINK_CHECK : 00697 { 00698 if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK ) 00699 { 00700 // Check DemodMargin 00701 // Check NbGateways 00702 if( ComplianceTest.Running == true ) 00703 { 00704 ComplianceTest.LinkCheck = true; 00705 ComplianceTest.DemodMargin = mlmeConfirm->DemodMargin ; 00706 ComplianceTest.NbGateways = mlmeConfirm->NbGateways ; 00707 } 00708 } 00709 break; 00710 } 00711 default: 00712 break; 00713 } 00714 NextTx = true; 00715 UplinkStatusUpdated = true; 00716 } 00717 00718 void flash_builtin() { 00719 myled = 1; // turn the LED on (HIGH is the voltage level) 00720 wait(2); // wait for 2 second 00721 myled = 0; // turn the LED off by making the voltage LOW 00722 wait(1); // wait for 1 second 00723 } 00724 00725 /** 00726 * Main application entry point. 00727 */ 00728 Serial pc(SERIAL_TX, SERIAL_RX,115200); 00729 00730 int sysclk_hse_pll_patch(void) 00731 { 00732 RCC_ClkInitTypeDef RCC_ClkInitStruct; 00733 RCC_OscInitTypeDef RCC_OscInitStruct; 00734 00735 HAL_StatusTypeDef status; 00736 00737 #if defined(TARGET_NUCLEO_F411RE) 00738 /* Start HSE first */ 00739 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; 00740 RCC_OscInitStruct.HSEState = RCC_HSE_ON; 00741 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 00742 00743 00744 00745 00746 00747 00748 00749 #elif defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_NUCLEO_L152RE) 00750 /* To clear HSE_BYPASS, we need to stop HSE first */ 00751 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; 00752 RCC_OscInitStruct.HSEState = RCC_HSE_OFF; 00753 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 00754 00755 00756 00757 #else 00758 #error "!! Target not supported."; 00759 #endif 00760 status = HAL_RCC_OscConfig(&RCC_OscInitStruct); 00761 if (status != HAL_OK) { 00762 return (-1); // FAIL 00763 } 00764 00765 /* To disable PLL, change SYSCLK to HSI without PLL */ 00766 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK; 00767 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; // 16 MHz 00768 #if defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_NUCLEO_L152RE) 00769 status = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1); 00770 #elif defined(TARGET_NUCLEO_F411RE) 00771 status = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3); 00772 #else 00773 #error "!! Target not supported."; 00774 #endif 00775 if (status != HAL_OK) { 00776 return (-2); // FAIL 00777 } 00778 00779 /* Disable PLL */ 00780 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_NONE; 00781 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_OFF; 00782 status = HAL_RCC_OscConfig(&RCC_OscInitStruct); 00783 if (status != HAL_OK) { 00784 return (-3); // FAIL 00785 } 00786 00787 /* Enable HSE and activate PLL with HSE as source */ 00788 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; 00789 RCC_OscInitStruct.HSEState = RCC_HSE_ON; // External 8 MHz xtal on OSC_IN/OSC_OUT 00790 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; 00791 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; 00792 #if defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_NUCLEO_L152RE) 00793 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12; 00794 RCC_OscInitStruct.PLL.PLLDIV = RCC_PLL_DIV3; 00795 #elif defined(TARGET_NUCLEO_F411RE) 00796 RCC_OscInitStruct.PLL.PLLM = 4; /* config for 8MHz */ 00797 RCC_OscInitStruct.PLL.PLLN = 200; 00798 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; 00799 RCC_OscInitStruct.PLL.PLLQ = 8; 00800 #else 00801 #error "!! Target not supported."; 00802 #endif 00803 status = HAL_RCC_OscConfig(&RCC_OscInitStruct); 00804 if (status != HAL_OK) { 00805 return (-4); // FAIL 00806 } 00807 00808 /* Select PLL as system clock source and configure the PCLK1 and PCLK2 clocks dividers */ 00809 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); 00810 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; 00811 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; 00812 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; 00813 #if defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_NUCLEO_L152RE) 00814 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; 00815 status = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1); 00816 #elif defined(TARGET_NUCLEO_F411RE) 00817 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; 00818 status = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3); 00819 #else 00820 #error "!! Target not supported."; 00821 #endif 00822 if (status != HAL_OK) { 00823 return (-5); // FAIL 00824 } 00825 00826 /* Turn off unused clocks */ 00827 #if defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_NUCLEO_L152RE) 00828 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_MSI; 00829 RCC_OscInitStruct.MSIState = RCC_MSI_OFF; 00830 #elif defined(TARGET_NUCLEO_F411RE) 00831 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; 00832 #else 00833 #error "!! Target not supported."; 00834 #endif 00835 RCC_OscInitStruct.HSIState = RCC_HSI_OFF; 00836 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 00837 status = HAL_RCC_OscConfig(&RCC_OscInitStruct); 00838 if (status != HAL_OK) { 00839 return (-6); // FAIL 00840 } 00841 00842 return 0; // OK 00843 } 00844 00845 void ConfigureSystemClockForHSE_PLL(void) 00846 { 00847 int retVal = 0; 00848 00849 RCC_OscInitTypeDef RCC_OscInitStruct; 00850 RCC_ClkInitTypeDef RCC_ClkInitStruct; 00851 uint32_t pFLatency; 00852 00853 HAL_RCC_GetOscConfig( &RCC_OscInitStruct); 00854 HAL_RCC_GetClockConfig( &RCC_ClkInitStruct, &pFLatency); 00855 00856 printf("\r\n==> mbed-os-rev=%d.%d.%d lib-rev=%d AppBuild=%s %s <==\r\n", \ 00857 MBED_MAJOR_VERSION, MBED_MINOR_VERSION,MBED_PATCH_VERSION,\ 00858 MBED_LIBRARY_VERSION, __TIME__, __DATE__); 00859 00860 printf(">>>> Current <<<< \r\n"); 00861 printf("> SysClock= %d Hz \r\n",SystemCoreClock); 00862 #if defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_NUCLEO_L152RE) 00863 printf("> CR=%08X CFGR=%08X CSR=%08X ICSCR=%08X \r\n",\ 00864 RCC->CR, RCC->CFGR, RCC->CSR, RCC->ICSCR); 00865 00866 printf("> PLLSRC=%s PLLMUL=%u PLLDIV=%u \r\n",\ 00867 ((RCC->CFGR & (1UL<<16))?"HSE":"HSI"),\ 00868 RCC_OscInitStruct.PLL.PLLMUL, RCC_OscInitStruct.PLL.PLLDIV); 00869 00870 printf("> AHB=%d APB1=%d APB2=%d Latency=%u \r\n",\ 00871 RCC_ClkInitStruct.AHBCLKDivider, RCC_ClkInitStruct.APB1CLKDivider,\ 00872 RCC_ClkInitStruct.APB2CLKDivider,pFLatency); 00873 wait(0.1); // wait for printf to finish 00874 00875 #elif defined(TARGET_NUCLEO_F411RE) 00876 printf("> CR=%08X CFGR=%08X CSR=%08X PLLCFGR=%08X \r\n",\ 00877 RCC->CR, RCC->CFGR, RCC->CSR, RCC->PLLCFGR); 00878 00879 printf("> PLLSRC=%s PLLM=%d PLLN=%d PLLP=%d PLLQ=%d \r\n", 00880 ((RCC->PLLCFGR & (1UL<<22))?"HSE":"HSI"),\ 00881 RCC_OscInitStruct.PLL.PLLM, RCC_OscInitStruct.PLL.PLLN,\ 00882 RCC_OscInitStruct.PLL.PLLP, RCC_OscInitStruct.PLL.PLLQ); 00883 00884 printf("> AHB=%d APB1=%d APB2=%d Latency=%u \r\n",\ 00885 RCC_ClkInitStruct.AHBCLKDivider, RCC_ClkInitStruct.APB1CLKDivider,\ 00886 RCC_ClkInitStruct.APB2CLKDivider,pFLatency); 00887 wait(0.1); // wait for print to finish 00888 00889 #else 00890 #error "!! Target not supported."; 00891 #endif 00892 00893 /* setup hse */ 00894 retVal = sysclk_hse_pll_patch(); 00895 00896 printf(">>>> NEW <<<< \r\n"); 00897 printf("> SysClock= %d Hz, retVal= %d \r\n",SystemCoreClock, retVal); 00898 #if defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_NUCLEO_L152RE) 00899 printf("> CR=%08X CFGR=%08X CSR=%08X ICSCR=%08X \r\n",\ 00900 RCC->CR, RCC->CFGR, RCC->CSR, RCC->ICSCR); 00901 00902 printf("> PLLSRC=%s PLLMUL=%u PLLDIV=%u \r\n",\ 00903 ((RCC->CFGR & (1UL<<16))?"HSE":"HSI"),\ 00904 RCC_OscInitStruct.PLL.PLLMUL, RCC_OscInitStruct.PLL.PLLDIV); 00905 00906 printf("> AHB=%d APB1=%d APB2=%d Latency=%u \r\n",\ 00907 RCC_ClkInitStruct.AHBCLKDivider, RCC_ClkInitStruct.APB1CLKDivider,\ 00908 RCC_ClkInitStruct.APB2CLKDivider, pFLatency); 00909 00910 #elif defined(TARGET_NUCLEO_F411RE) 00911 printf("> CR=%08X CFGR=%08X CSR=%08X PLLCFGR=%08X \r\n",\ 00912 RCC->CR, RCC->CFGR, RCC->CSR, RCC->PLLCFGR); 00913 00914 printf("> PLLSRC=%s PLLM=%d PLLN=%d PLLP=%d PLLQ=%d \r\n",\ 00915 ((RCC->PLLCFGR & (1UL<<22))?"HSE":"HSI"),\ 00916 RCC_OscInitStruct.PLL.PLLM, RCC_OscInitStruct.PLL.PLLN,\ 00917 RCC_OscInitStruct.PLL.PLLP, RCC_OscInitStruct.PLL.PLLQ); 00918 00919 printf("> AHB=%d APB1=%d APB2=%d Latency=%u \r\n",\ 00920 RCC_ClkInitStruct.AHBCLKDivider, RCC_ClkInitStruct.APB1CLKDivider,\ 00921 RCC_ClkInitStruct.APB2CLKDivider, pFLatency); 00922 00923 #else 00924 #error "!! Target not supported."; 00925 #endif 00926 00927 if(retVal < 0) 00928 { 00929 /* indicate error i.e failed to set HSE_PLL */ 00930 while(1) 00931 { 00932 myled = 1; 00933 wait(0.2); 00934 myled = 0; 00935 wait(0.5); 00936 } 00937 } 00938 } 00939 00940 int main( void ) 00941 { 00942 ConfigureSystemClockForHSE_PLL(); 00943 00944 flash_builtin(); 00945 flash_builtin(); 00946 flash_builtin(); 00947 flash_builtin(); 00948 00949 LoRaMacPrimitives_t LoRaMacPrimitives; 00950 LoRaMacCallback_t LoRaMacCallbacks; 00951 MibRequestConfirm_t mibReq; 00952 00953 BoardInit( ); 00954 SerialDisplayInit( ); 00955 00956 SerialDisplayUpdateEui( 5, DevEui ); 00957 SerialDisplayUpdateEui( 6, AppEui ); 00958 SerialDisplayUpdateKey( 7, AppKey ); 00959 00960 #if( OVER_THE_AIR_ACTIVATION == 0 ) 00961 SerialDisplayUpdateNwkId( LORAWAN_NETWORK_ID ); 00962 SerialDisplayUpdateDevAddr( DevAddr ); 00963 SerialDisplayUpdateKey( 12, NwkSKey ); 00964 SerialDisplayUpdateKey( 13, AppSKey ); 00965 #endif 00966 00967 DeviceState = DEVICE_STATE_INIT; 00968 00969 while( 1 ) 00970 { 00971 SerialRxProcess( ); 00972 flash_builtin(); 00973 if( IsNetworkJoinedStatusUpdate == true ) 00974 { 00975 IsNetworkJoinedStatusUpdate = false; 00976 mibReq.Type = MIB_NETWORK_JOINED ; 00977 LoRaMacMibGetRequestConfirm( &mibReq ); 00978 SerialDisplayUpdateNetworkIsJoined( mibReq.Param .IsNetworkJoined ); 00979 } 00980 if( UplinkStatusUpdated == true ) 00981 { 00982 UplinkStatusUpdated = false; 00983 SerialDisplayUpdateUplink( LoRaMacUplinkStatus.Acked, LoRaMacUplinkStatus.Datarate, LoRaMacUplinkStatus.UplinkCounter, LoRaMacUplinkStatus.Port, LoRaMacUplinkStatus.Buffer, LoRaMacUplinkStatus.BufferSize ); 00984 } 00985 if( DownlinkStatusUpdated == true ) 00986 { 00987 DownlinkStatusUpdated = false; 00988 SerialDisplayUpdateDownlink( LoRaMacDownlinkStatus.RxData, LoRaMacDownlinkStatus.Rssi, LoRaMacDownlinkStatus.Snr, LoRaMacDownlinkStatus.DownlinkCounter, LoRaMacDownlinkStatus.Port, LoRaMacDownlinkStatus.Buffer, LoRaMacDownlinkStatus.BufferSize ); 00989 } 00990 00991 switch( DeviceState ) 00992 { 00993 case DEVICE_STATE_INIT: 00994 { 00995 LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm; 00996 LoRaMacPrimitives.MacMcpsIndication = McpsIndication; 00997 LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm; 00998 LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel; 00999 LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks ); 01000 01001 TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent ); 01002 01003 mibReq.Type = MIB_ADR ; 01004 mibReq.Param .AdrEnable = LORAWAN_ADR_ON; 01005 LoRaMacMibSetRequestConfirm( &mibReq ); 01006 01007 mibReq.Type = MIB_PUBLIC_NETWORK ; 01008 mibReq.Param .EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK; 01009 LoRaMacMibSetRequestConfirm( &mibReq ); 01010 01011 01012 // Limiting to just 2nd subband of 8 channels 01013 static uint16_t GatewayChannelsMask[] = {0xFF00, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000}; 01014 mibReq.Type = MIB_CHANNELS_DEFAULT_MASK ; 01015 mibReq.Param .ChannelsDefaultMask = GatewayChannelsMask; 01016 LoRaMacMibSetRequestConfirm( &mibReq ); 01017 01018 mibReq.Type = MIB_CHANNELS_MASK ; 01019 mibReq.Param .ChannelsMask = GatewayChannelsMask; 01020 LoRaMacMibSetRequestConfirm( &mibReq ); 01021 01022 01023 01024 01025 #if defined( USE_BAND_868 ) 01026 LoRaMacTestSetDutyCycleOn( LORAWAN_DUTYCYCLE_ON ); 01027 SerialDisplayUpdateDutyCycle( LORAWAN_DUTYCYCLE_ON ); 01028 01029 #if( USE_SEMTECH_DEFAULT_CHANNEL_LINEUP == 1 ) 01030 LoRaMacChannelAdd( 3, ( ChannelParams_t )LC4 ); 01031 LoRaMacChannelAdd( 4, ( ChannelParams_t )LC5 ); 01032 LoRaMacChannelAdd( 5, ( ChannelParams_t )LC6 ); 01033 LoRaMacChannelAdd( 6, ( ChannelParams_t )LC7 ); 01034 LoRaMacChannelAdd( 7, ( ChannelParams_t )LC8 ); 01035 LoRaMacChannelAdd( 8, ( ChannelParams_t )LC9 ); 01036 LoRaMacChannelAdd( 9, ( ChannelParams_t )LC10 ); 01037 01038 mibReq.Type = MIB_RX2_DEFAULT_CHANNEL ; 01039 mibReq.Param .Rx2DefaultChannel = ( Rx2ChannelParams_t ){ 869525000, DR_3 }; 01040 LoRaMacMibSetRequestConfirm( &mibReq ); 01041 01042 mibReq.Type = MIB_RX2_CHANNEL ; 01043 mibReq.Param .Rx2Channel = ( Rx2ChannelParams_t ){ 869525000, DR_3 }; 01044 LoRaMacMibSetRequestConfirm( &mibReq ); 01045 #endif 01046 01047 #endif 01048 SerialDisplayUpdateActivationMode( OVER_THE_AIR_ACTIVATION ); 01049 SerialDisplayUpdateAdr( LORAWAN_ADR_ON ); 01050 SerialDisplayUpdatePublicNetwork( LORAWAN_PUBLIC_NETWORK ); 01051 01052 LoRaMacDownlinkStatus.DownlinkCounter = 0; 01053 01054 DeviceState = DEVICE_STATE_JOIN; 01055 break; 01056 } 01057 case DEVICE_STATE_JOIN: 01058 { 01059 #if( OVER_THE_AIR_ACTIVATION != 0 ) 01060 MlmeReq_t mlmeReq; 01061 01062 mlmeReq.Type = MLME_JOIN ; 01063 01064 mlmeReq.Req.Join .DevEui = DevEui; 01065 mlmeReq.Req.Join .AppEui = AppEui; 01066 mlmeReq.Req.Join .AppKey = AppKey; 01067 01068 if( NextTx == true ) 01069 { 01070 LoRaMacMlmeRequest( &mlmeReq ); 01071 } 01072 DeviceState = DEVICE_STATE_SLEEP; 01073 #else 01074 mibReq.Type = MIB_NET_ID ; 01075 mibReq.Param .NetID = LORAWAN_NETWORK_ID; 01076 LoRaMacMibSetRequestConfirm( &mibReq ); 01077 01078 mibReq.Type = MIB_DEV_ADDR ; 01079 mibReq.Param .DevAddr = DevAddr; 01080 LoRaMacMibSetRequestConfirm( &mibReq ); 01081 01082 mibReq.Type = MIB_NWK_SKEY ; 01083 mibReq.Param .NwkSKey = NwkSKey; 01084 LoRaMacMibSetRequestConfirm( &mibReq ); 01085 01086 mibReq.Type = MIB_APP_SKEY ; 01087 mibReq.Param .AppSKey = AppSKey; 01088 LoRaMacMibSetRequestConfirm( &mibReq ); 01089 01090 mibReq.Type = MIB_NETWORK_JOINED ; 01091 mibReq.Param .IsNetworkJoined = true; 01092 LoRaMacMibSetRequestConfirm( &mibReq ); 01093 01094 DeviceState = DEVICE_STATE_SEND; 01095 #endif 01096 IsNetworkJoinedStatusUpdate = true; 01097 break; 01098 } 01099 case DEVICE_STATE_SEND: 01100 { 01101 if( NextTx == true ) 01102 { 01103 SerialDisplayUpdateUplinkAcked( false ); 01104 SerialDisplayUpdateDonwlinkRxData( false ); 01105 PrepareTxFrame( AppPort ); 01106 01107 NextTx = SendFrame( ); 01108 } 01109 if( ComplianceTest.Running == true ) 01110 { 01111 // Schedule next packet transmission 01112 TxDutyCycleTime = 5000; // 5000 ms 01113 } 01114 else 01115 { 01116 // Schedule next packet transmission 01117 TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); 01118 } 01119 DeviceState = DEVICE_STATE_CYCLE; 01120 break; 01121 } 01122 case DEVICE_STATE_CYCLE: 01123 { 01124 DeviceState = DEVICE_STATE_SLEEP; 01125 01126 // Schedule next packet transmission 01127 TimerSetValue( &TxNextPacketTimer, TxDutyCycleTime ); 01128 TimerStart( &TxNextPacketTimer ); 01129 break; 01130 } 01131 case DEVICE_STATE_SLEEP: 01132 { 01133 // Wake up through events 01134 break; 01135 } 01136 default: 01137 { 01138 DeviceState = DEVICE_STATE_INIT; 01139 break; 01140 } 01141 } 01142 } 01143 }
Generated on Wed Jul 13 2022 05:56:12 by
1.7.2