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.
LoRaWANStack.cpp
00001 /** 00002 / _____) _ | | 00003 ( (____ _____ ____ _| |_ _____ ____| |__ 00004 \____ \| ___ | (_ _) ___ |/ ___) _ \ 00005 _____) ) ____| | | || |_| ____( (___| | | | 00006 (______/|_____)_|_|_| \__)_____)\____)_| |_| 00007 (C)2013 Semtech 00008 ___ _____ _ ___ _ _____ ___ ___ ___ ___ 00009 / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| 00010 \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| 00011 |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| 00012 embedded.connectivity.solutions=============== 00013 00014 Description: LoRaWAN stack layer that controls both MAC and PHY underneath 00015 00016 License: Revised BSD License, see LICENSE.TXT file include in the project 00017 00018 Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) 00019 00020 00021 Copyright (c) 2017, Arm Limited and affiliates. 00022 00023 SPDX-License-Identifier: BSD-3-Clause 00024 */ 00025 00026 #include <string.h> 00027 #include <stdlib.h> 00028 #include "platform/Callback.h" 00029 #include "events/EventQueue.h" 00030 #include "lorawan/LoRaWANStack.h" 00031 #if defined(FEATURE_COMMON_PAL) 00032 #include "mbed_trace.h" 00033 #define TRACE_GROUP "LSTK" 00034 #else 00035 #define tr_debug(...) (void(0)) //dummies if feature common pal is not added 00036 #define tr_info(...) (void(0)) //dummies if feature common pal is not added 00037 #define tr_error(...) (void(0)) //dummies if feature common pal is not added 00038 #define tr_warn(...) (void(0)) //dummies if feature common pal is not added 00039 #endif //defined(FEATURE_COMMON_PAL) 00040 00041 #define INVALID_PORT 0xFF 00042 #define MAX_CONFIRMED_MSG_RETRIES 255 00043 00044 using namespace mbed; 00045 using namespace events; 00046 00047 #if defined(LORAWAN_COMPLIANCE_TEST) 00048 #if (MBED_CONF_LORA_PHY == 0 || MBED_CONF_LORA_PHY == 4 || MBED_CONF_LORA_PHY == 6 || MBED_CONF_LORA_PHY == 7) 00049 #define LORAWAN_COMPLIANCE_TEST_DATA_SIZE 16 00050 #elif (MBED_CONF_LORA_PHY == 1 || MBED_CONF_LORA_PHY == 2 || MBED_CONF_LORA_PHY == 8 || MBED_CONF_LORA_PHY == 9) 00051 #define LORAWAN_COMPLIANCE_TEST_DATA_SIZE 11 00052 #else 00053 #error "Must set LoRa PHY layer parameters." 00054 #endif 00055 #endif 00056 00057 /***************************************************************************** 00058 * Private Member Functions * 00059 ****************************************************************************/ 00060 bool LoRaWANStack::is_port_valid(uint8_t port) 00061 { 00062 //Application should not use reserved and illegal port numbers. 00063 if (port >= 224 || port == 0) { 00064 return false; 00065 } else { 00066 return true; 00067 } 00068 } 00069 00070 lorawan_status_t LoRaWANStack::set_application_port(uint8_t port) 00071 { 00072 if (is_port_valid(port)) { 00073 _app_port = port; 00074 return LORAWAN_STATUS_OK; 00075 } 00076 00077 return LORAWAN_STATUS_PORT_INVALID; 00078 } 00079 00080 /***************************************************************************** 00081 * Constructor and destructor * 00082 ****************************************************************************/ 00083 LoRaWANStack::LoRaWANStack() 00084 : _loramac(), 00085 _device_current_state(DEVICE_STATE_NOT_INITIALIZED), _num_retry(1), 00086 _app_port(INVALID_PORT), _link_check_requested(false), _queue(NULL) 00087 { 00088 #ifdef MBED_CONF_LORA_APP_PORT 00089 if (is_port_valid(MBED_CONF_LORA_APP_PORT)) { 00090 _app_port = MBED_CONF_LORA_APP_PORT; 00091 } else { 00092 tr_error("User defined port in .json is illegal."); 00093 } 00094 #endif 00095 00096 memset(&_lw_session, 0, sizeof(_lw_session)); 00097 memset(&_rx_msg, 0, sizeof(_rx_msg)); 00098 00099 LoRaMacPrimitives.mcps_confirm = callback(this, &LoRaWANStack::mcps_confirm_handler); 00100 LoRaMacPrimitives.mcps_indication = callback(this, &LoRaWANStack::mcps_indication_handler); 00101 LoRaMacPrimitives.mlme_confirm = callback(this, &LoRaWANStack::mlme_confirm_handler); 00102 LoRaMacPrimitives.mlme_indication = callback(this, &LoRaWANStack::mlme_indication_handler); 00103 } 00104 00105 LoRaWANStack::~LoRaWANStack() 00106 { 00107 } 00108 00109 /***************************************************************************** 00110 * Public member functions * 00111 ****************************************************************************/ 00112 LoRaWANStack& LoRaWANStack::get_lorawan_stack() 00113 { 00114 static LoRaWANStack _lw_stack; 00115 return _lw_stack; 00116 } 00117 00118 void LoRaWANStack::bind_radio_driver(LoRaRadio& radio) 00119 { 00120 _loramac.bind_radio_driver(radio); 00121 } 00122 00123 lorawan_status_t LoRaWANStack::connect() 00124 { 00125 // connection attempt without parameters. 00126 // System tries to look for configuration in mbed_lib.json that can be 00127 // overridden by mbed_app.json. However, if none of the json files are 00128 // available (highly unlikely), we still fallback to some default parameters. 00129 // Check lorawan_data_structure for fallback defaults. 00130 00131 lorawan_connect_t connection_params; 00132 00133 //TODO: LoRaWANStack don't need to know these values, move to LoRaMac (or below) 00134 #if MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION 00135 const static uint8_t dev_eui[] = MBED_CONF_LORA_DEVICE_EUI; 00136 const static uint8_t app_eui[] = MBED_CONF_LORA_APPLICATION_EUI; 00137 const static uint8_t app_key[] = MBED_CONF_LORA_APPLICATION_KEY; 00138 00139 connection_params.connect_type = LORAWAN_CONNECTION_OTAA; 00140 connection_params.connection_u.otaa .app_eui = const_cast<uint8_t *>(app_eui); 00141 connection_params.connection_u.otaa .dev_eui = const_cast<uint8_t *>(dev_eui); 00142 connection_params.connection_u.otaa .app_key = const_cast<uint8_t *>(app_key); 00143 connection_params.connection_u.otaa .nb_trials = MBED_CONF_LORA_NB_TRIALS; 00144 00145 return join_request_by_otaa(connection_params); 00146 #else 00147 const static uint8_t nwk_skey[] = MBED_CONF_LORA_NWKSKEY; 00148 const static uint8_t app_skey[] = MBED_CONF_LORA_APPSKEY; 00149 const static uint32_t dev_addr = MBED_CONF_LORA_DEVICE_ADDRESS; 00150 const static uint32_t nwk_id = (MBED_CONF_LORA_DEVICE_ADDRESS & LORAWAN_NETWORK_ID_MASK); 00151 00152 connection_params.connect_type = LORAWAN_CONNECTION_ABP; 00153 connection_params.connection_u.abp .nwk_id = const_cast<uint8_t *>(nwk_id); 00154 connection_params.connection_u.abp .dev_addr = const_cast<uint8_t *>(dev_addr); 00155 connection_params.connection_u.abp .nwk_skey = const_cast<uint8_t *>(nwk_skey); 00156 connection_params.connection_u.abp .app_skey = const_cast<uint8_t *>(app_skey); 00157 00158 return activation_by_personalization(connection_params); 00159 #endif 00160 } 00161 00162 lorawan_status_t LoRaWANStack::connect(const lorawan_connect_t &connect) 00163 { 00164 lorawan_status_t mac_status; 00165 00166 if (connect.connect_type == LORAWAN_CONNECTION_OTAA) { 00167 mac_status = join_request_by_otaa(connect); 00168 } else if (connect.connect_type == LORAWAN_CONNECTION_ABP) { 00169 mac_status = activation_by_personalization(connect); 00170 } else { 00171 return LORAWAN_STATUS_PARAMETER_INVALID; 00172 } 00173 00174 return mac_status; 00175 } 00176 00177 lorawan_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue) 00178 { 00179 if(!queue) { 00180 return LORAWAN_STATUS_PARAMETER_INVALID; 00181 } 00182 00183 if (DEVICE_STATE_NOT_INITIALIZED != _device_current_state) 00184 { 00185 tr_debug("Initialized already"); 00186 return LORAWAN_STATUS_OK; 00187 } 00188 00189 tr_debug("Initializing MAC layer"); 00190 _queue = queue; 00191 00192 _loramac.initialize(&LoRaMacPrimitives, queue); 00193 00194 // Reset counters to zero. Will change in future with 1.1 support. 00195 _lw_session.downlink_counter = 0; 00196 _lw_session.uplink_counter = 0; 00197 00198 return lora_state_machine(DEVICE_STATE_INIT); 00199 } 00200 00201 lorawan_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count) 00202 { 00203 if (count >= MAX_CONFIRMED_MSG_RETRIES) { 00204 return LORAWAN_STATUS_PARAMETER_INVALID; 00205 } 00206 00207 _num_retry = count; 00208 00209 return LORAWAN_STATUS_OK; 00210 } 00211 00212 /*! 00213 * \brief MLME-Indication event function 00214 * 00215 * \param [IN] mlmeIndication - Pointer to the indication structure. 00216 */ 00217 void LoRaWANStack::mlme_indication_handler(loramac_mlme_indication_t *mlmeIndication) 00218 { 00219 switch( mlmeIndication->indication_type ) 00220 { 00221 case MLME_SCHEDULE_UPLINK : 00222 {// The MAC signals that we shall provide an uplink as soon as possible 00223 // TODO: Sending implementation missing and will be implemented using 00224 // another task. 00225 //OnTxNextPacketTimerEvent( ); 00226 break; 00227 } 00228 default: 00229 break; 00230 } 00231 } 00232 00233 lorawan_status_t LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs) 00234 { 00235 if (!cbs || !cbs->events) { 00236 return LORAWAN_STATUS_PARAMETER_INVALID; 00237 } 00238 00239 _callbacks.events = cbs->events; 00240 00241 if (cbs->link_check_resp) { 00242 _callbacks.link_check_resp = cbs->link_check_resp; 00243 } 00244 00245 if (cbs->battery_level) { 00246 _callbacks.battery_level = cbs->battery_level; 00247 } 00248 00249 return LORAWAN_STATUS_OK; 00250 } 00251 00252 lorawan_status_t LoRaWANStack::add_channels(const lorawan_channelplan_t &channel_plan) 00253 { 00254 if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { 00255 tr_error("Stack not initialized!"); 00256 return LORAWAN_STATUS_NOT_INITIALIZED; 00257 } 00258 00259 return _loramac.add_channel_plan(channel_plan); 00260 } 00261 00262 lorawan_status_t LoRaWANStack::drop_channel_list() 00263 { 00264 if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { 00265 tr_error("Stack not initialized!"); 00266 return LORAWAN_STATUS_NOT_INITIALIZED; 00267 } 00268 00269 return _loramac.remove_channel_plan(); 00270 } 00271 00272 lorawan_status_t LoRaWANStack::remove_a_channel(uint8_t channel_id) 00273 { 00274 if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED ) 00275 { 00276 tr_error("Stack not initialized!"); 00277 return LORAWAN_STATUS_NOT_INITIALIZED; 00278 } 00279 00280 return _loramac.remove_single_channel(channel_id); 00281 } 00282 00283 lorawan_status_t LoRaWANStack::get_enabled_channels(lorawan_channelplan_t& channel_plan) 00284 { 00285 if (_device_current_state == DEVICE_STATE_JOINING 00286 || _device_current_state == DEVICE_STATE_NOT_INITIALIZED 00287 || _device_current_state == DEVICE_STATE_INIT) 00288 { 00289 tr_error("Cannot get channel plan until Joined!"); 00290 return LORAWAN_STATUS_BUSY; 00291 } 00292 00293 return _loramac.get_channel_plan(channel_plan); 00294 } 00295 00296 lorawan_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled) 00297 { 00298 if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) 00299 { 00300 tr_error("Stack not initialized!"); 00301 return LORAWAN_STATUS_NOT_INITIALIZED; 00302 } 00303 _loramac.enable_adaptive_datarate(adr_enabled); 00304 return LORAWAN_STATUS_OK; 00305 } 00306 00307 lorawan_status_t LoRaWANStack::set_channel_data_rate(uint8_t data_rate) 00308 { 00309 if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) 00310 { 00311 tr_error("Stack not initialized!"); 00312 return LORAWAN_STATUS_NOT_INITIALIZED; 00313 } 00314 00315 return _loramac.set_channel_data_rate(data_rate); 00316 } 00317 00318 lorawan_status_t LoRaWANStack::join_request_by_otaa(const lorawan_connect_t ¶ms) 00319 { 00320 if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) 00321 { 00322 tr_error("Stack not initialized!"); 00323 return LORAWAN_STATUS_NOT_INITIALIZED; 00324 } 00325 00326 tr_debug("Initiating OTAA"); 00327 00328 // As mentioned in the comment above, in 1.0.2 spec, counters are always set 00329 // to zero for new connection. This section is common for both normal and 00330 // connection restore at this moment. Will change in future with 1.1 support. 00331 _lw_session.downlink_counter = 0; 00332 _lw_session.uplink_counter = 0; 00333 _lw_session.connection.connect_type = LORAWAN_CONNECTION_OTAA; 00334 00335 _lw_session.connection.connection_u.otaa.dev_eui = params.connection_u.otaa .dev_eui; 00336 _lw_session.connection.connection_u.otaa.app_eui = params.connection_u.otaa .app_eui; 00337 _lw_session.connection.connection_u.otaa.app_key = params.connection_u.otaa .app_key; 00338 _lw_session.connection.connection_u.otaa.nb_trials = params.connection_u.otaa .nb_trials; 00339 00340 return lora_state_machine(DEVICE_STATE_JOINING); 00341 } 00342 00343 lorawan_status_t LoRaWANStack::activation_by_personalization(const lorawan_connect_t ¶ms) 00344 { 00345 if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { 00346 tr_error("Stack not initialized!"); 00347 return LORAWAN_STATUS_NOT_INITIALIZED; 00348 } 00349 00350 tr_debug("Initiating ABP"); 00351 00352 _lw_session.connection.connect_type = LORAWAN_CONNECTION_ABP; 00353 00354 _lw_session.connection.connection_u.abp.dev_addr = params.connection_u.abp .dev_addr; 00355 _lw_session.connection.connection_u.abp.nwk_skey = params.connection_u.abp .nwk_skey; 00356 _lw_session.connection.connection_u.abp.app_skey = params.connection_u.abp .app_skey; 00357 00358 // If current state is SHUTDOWN, device may be trying to re-establish 00359 // communication. In case of ABP specification is meddled about frame counters. 00360 // It says to reset counters to zero but there is no mechanism to tell the 00361 // network server that the device was disconnected or restarted. 00362 // At the moment, this implementation does not support a non-volatile 00363 // memory storage. 00364 //_lw_session.downlink_counter; //Get from NVM 00365 //_lw_session.uplink_counter; //Get from NVM 00366 00367 tr_debug("Frame Counters. UpCnt=%lu, DownCnt=%lu", 00368 _lw_session.uplink_counter, _lw_session.downlink_counter); 00369 00370 return lora_state_machine(DEVICE_STATE_ABP_CONNECTING); 00371 } 00372 00373 int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, 00374 uint16_t length, uint8_t flags, bool null_allowed) 00375 { 00376 if (!null_allowed && !data) { 00377 return LORAWAN_STATUS_PARAMETER_INVALID; 00378 } 00379 // add a link check request with normal data, until the application 00380 // explicitly removes it. 00381 if (_link_check_requested) { 00382 set_link_check_request(); 00383 } 00384 00385 if (!_lw_session.active) { 00386 return LORAWAN_STATUS_NO_ACTIVE_SESSIONS; 00387 } 00388 00389 if(_loramac.tx_ongoing()) { 00390 return LORAWAN_STATUS_WOULD_BLOCK; 00391 } 00392 00393 #if defined(LORAWAN_COMPLIANCE_TEST) 00394 if (_compliance_test.running) { 00395 return LORAWAN_STATUS_COMPLIANCE_TEST_ON; 00396 } 00397 #endif 00398 00399 lorawan_status_t status; 00400 00401 if (_loramac.nwk_joined() == false) { 00402 return LORAWAN_STATUS_NO_NETWORK_JOINED; 00403 } 00404 00405 status = set_application_port(port); 00406 00407 if (status != LORAWAN_STATUS_OK) { 00408 tr_error("Illegal application port definition."); 00409 return status; 00410 } 00411 00412 if (flags == 0 || 00413 (flags & MSG_FLAG_MASK) == (MSG_CONFIRMED_FLAG|MSG_UNCONFIRMED_FLAG)) { 00414 tr_error("CONFIRMED and UNCONFIRMED are mutually exclusive for send()"); 00415 return LORAWAN_STATUS_PARAMETER_INVALID; 00416 } 00417 00418 int16_t len = _loramac.prepare_ongoing_tx(port, data, length, flags, _num_retry); 00419 00420 00421 status = lora_state_machine(DEVICE_STATE_SEND); 00422 00423 // send user the length of data which is scheduled now. 00424 // user should take care of the pending data. 00425 return (status == LORAWAN_STATUS_OK) ? len : (int16_t) status; 00426 } 00427 00428 int16_t LoRaWANStack::handle_rx(const uint8_t port, uint8_t* data, 00429 uint16_t length, uint8_t flags) 00430 { 00431 if (!_lw_session.active) { 00432 return LORAWAN_STATUS_NO_ACTIVE_SESSIONS; 00433 } 00434 00435 // No messages to read. 00436 if (!_rx_msg.receive_ready) { 00437 return LORAWAN_STATUS_WOULD_BLOCK; 00438 } 00439 00440 #if defined(LORAWAN_COMPLIANCE_TEST) 00441 if (_compliance_test.running) { 00442 return LORAWAN_STATUS_COMPLIANCE_TEST_ON; 00443 } 00444 #endif 00445 00446 if (data == NULL || length == 0) { 00447 return LORAWAN_STATUS_PARAMETER_INVALID; 00448 } 00449 00450 uint8_t *base_ptr = _rx_msg.msg.mcps_indication.buffer; 00451 uint16_t base_size = _rx_msg.msg.mcps_indication.buffer_size; 00452 bool read_complete = false; 00453 00454 if (_rx_msg.msg.mcps_indication.port != port) { 00455 // Nothing yet received for this particular port 00456 return LORAWAN_STATUS_WOULD_BLOCK; 00457 } 00458 00459 // check if message received is a Confirmed message and user subscribed to it or not 00460 if (_rx_msg.msg.mcps_indication.type == MCPS_CONFIRMED 00461 && ((flags & MSG_FLAG_MASK) == MSG_CONFIRMED_FLAG 00462 || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_MULTICAST 00463 || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_PROPRIETARY)) { 00464 00465 tr_debug("RX - Confirmed Message, flags=%d", flags); 00466 } 00467 00468 // check if message received is a Unconfirmed message and user subscribed to it or not 00469 if (_rx_msg.msg.mcps_indication.type == MCPS_UNCONFIRMED 00470 && ((flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_FLAG 00471 || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_MULTICAST 00472 || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_PROPRIETARY)) { 00473 tr_debug("RX - Unconfirmed Message - flags=%d", flags); 00474 } 00475 00476 // check the length of received message whether we can fit into user 00477 // buffer completely or not 00478 if (_rx_msg.msg.mcps_indication.buffer_size > length && 00479 _rx_msg.prev_read_size == 0) { 00480 // we can't fit into user buffer. Invoke counter measures 00481 _rx_msg.pending_size = _rx_msg.msg.mcps_indication.buffer_size - length; 00482 base_size = length; 00483 _rx_msg.prev_read_size = base_size; 00484 memcpy(data, base_ptr, base_size); 00485 } else if (_rx_msg.prev_read_size == 0) { 00486 _rx_msg.pending_size = 0; 00487 _rx_msg.prev_read_size = 0; 00488 memcpy(data, base_ptr, base_size); 00489 read_complete = true; 00490 } 00491 00492 // If its the pending read then we should copy only the remaining part of 00493 // the buffer. Due to checks above, in case of a pending read, this block 00494 // will be the only one to get invoked 00495 if (_rx_msg.pending_size > 0 && _rx_msg.prev_read_size > 0) { 00496 memcpy(data, base_ptr+_rx_msg.prev_read_size, base_size); 00497 } 00498 00499 // we are done handing over received buffer to user. check if there is 00500 // anything pending. If not, memset the buffer to zero and indicate 00501 // that no read is in progress 00502 if (read_complete) { 00503 memset(_rx_msg.msg.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD); 00504 _rx_msg.receive_ready = false; 00505 } 00506 00507 return base_size; 00508 } 00509 00510 void LoRaWANStack::mlme_confirm_handler(loramac_mlme_confirm_t *mlme_confirm) 00511 { 00512 switch (mlme_confirm->req_type ) { 00513 case MLME_JOIN : 00514 if (mlme_confirm->status == LORAMAC_EVENT_INFO_STATUS_OK ) { 00515 // Status is OK, node has joined the network 00516 if (lora_state_machine(DEVICE_STATE_JOINED) != LORAWAN_STATUS_OK) { 00517 tr_error("Lora state machine did not return LORAWAN_STATUS_OK"); 00518 } 00519 } else { 00520 // Join attempt failed. 00521 if (lora_state_machine(DEVICE_STATE_IDLE) != LORAWAN_STATUS_IDLE) { 00522 tr_error("Lora state machine did not return DEVICE_STATE_IDLE !"); 00523 } 00524 00525 if (_callbacks.events) { 00526 const int ret = _queue->call(_callbacks.events, JOIN_FAILURE); 00527 MBED_ASSERT(ret != 0); 00528 (void)ret; 00529 } 00530 } 00531 break; 00532 case MLME_LINK_CHECK : 00533 if (mlme_confirm->status == LORAMAC_EVENT_INFO_STATUS_OK ) { 00534 // Check DemodMargin 00535 // Check NbGateways 00536 #if defined(LORAWAN_COMPLIANCE_TEST) 00537 if (_compliance_test.running == true) { 00538 _compliance_test.link_check = true; 00539 _compliance_test.demod_margin = mlme_confirm->demod_margin ; 00540 _compliance_test.nb_gateways = mlme_confirm->nb_gateways ; 00541 } else 00542 #endif 00543 { 00544 // normal operation as oppose to compliance testing 00545 if (_callbacks.link_check_resp) { 00546 const int ret = _queue->call(_callbacks.link_check_resp, 00547 mlme_confirm->demod_margin , 00548 mlme_confirm->nb_gateways ); 00549 MBED_ASSERT(ret != 0); 00550 (void)ret; 00551 } 00552 } 00553 } 00554 break; 00555 default: 00556 break; 00557 } 00558 } 00559 00560 void LoRaWANStack::mcps_confirm_handler(loramac_mcps_confirm_t *mcps_confirm) 00561 { 00562 if (mcps_confirm->status != LORAMAC_EVENT_INFO_STATUS_OK ) { 00563 _loramac.reset_ongoing_tx(); 00564 00565 tr_error("mcps_confirm_handler: Error code = %d", mcps_confirm->status ); 00566 00567 if (mcps_confirm->status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) { 00568 if (_callbacks.events) { 00569 const int ret = _queue->call(_callbacks.events, TX_TIMEOUT); 00570 MBED_ASSERT(ret != 0); 00571 (void)ret; 00572 } 00573 return; 00574 } else if (mcps_confirm->status == LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT ) { 00575 tr_debug("Did not receive Ack"); 00576 } 00577 00578 if (_callbacks.events) { 00579 const int ret = _queue->call(_callbacks.events, TX_ERROR); 00580 MBED_ASSERT(ret != 0); 00581 (void)ret; 00582 } 00583 return; 00584 } 00585 00586 if (mcps_confirm->req_type == MCPS_CONFIRMED && 00587 mcps_confirm->ack_received ) { 00588 tr_debug("Ack received."); 00589 } 00590 00591 _lw_session.uplink_counter = mcps_confirm->ul_frame_counter ; 00592 _loramac.set_tx_ongoing(false); 00593 if (_callbacks.events) { 00594 const int ret = _queue->call(_callbacks.events, TX_DONE); 00595 MBED_ASSERT(ret != 0); 00596 (void)ret; 00597 } 00598 } 00599 00600 void LoRaWANStack::mcps_indication_handler(loramac_mcps_indication_t *mcps_indication) 00601 { 00602 if (mcps_indication->status != LORAMAC_EVENT_INFO_STATUS_OK ) { 00603 if (_callbacks.events) { 00604 const int ret = _queue->call(_callbacks.events, RX_ERROR); 00605 MBED_ASSERT(ret != 0); 00606 (void)ret; 00607 } 00608 return; 00609 } 00610 00611 switch (mcps_indication->type ) { 00612 case MCPS_UNCONFIRMED : 00613 break; 00614 case MCPS_CONFIRMED : 00615 break; 00616 case MCPS_PROPRIETARY : 00617 break; 00618 case MCPS_MULTICAST : 00619 break; 00620 default: 00621 break; 00622 } 00623 00624 //TODO: 00625 // Check Multicast 00626 // Check Port 00627 // Check Datarate 00628 // Check FramePending 00629 // Check Buffer 00630 // Check BufferSize 00631 // Check Rssi 00632 // Check Snr 00633 // Check RxSlot 00634 00635 _lw_session.downlink_counter++; 00636 00637 #if defined(LORAWAN_COMPLIANCE_TEST) 00638 if (_compliance_test.running == true) { 00639 _compliance_test.downlink_counter++; 00640 } 00641 #endif 00642 00643 if (mcps_indication->is_data_recvd == true) { 00644 switch (mcps_indication->port ) { 00645 case 224: { 00646 #if defined(LORAWAN_COMPLIANCE_TEST) 00647 tr_debug("Compliance test command received."); 00648 compliance_test_handler(mcps_indication); 00649 #else 00650 tr_info("Compliance test disabled."); 00651 #endif 00652 break; 00653 } 00654 default: { 00655 if (is_port_valid(mcps_indication->port ) == true || 00656 mcps_indication->type == MCPS_PROPRIETARY ) { 00657 00658 // Valid message arrived. 00659 _rx_msg.type = LORAMAC_RX_MCPS_INDICATION; 00660 _rx_msg.msg.mcps_indication.buffer_size = mcps_indication->buffer_size ; 00661 _rx_msg.msg.mcps_indication.port = mcps_indication->port ; 00662 _rx_msg.msg.mcps_indication.buffer = mcps_indication->buffer ; 00663 00664 // Notify application about received frame.. 00665 tr_debug("Received %d bytes", _rx_msg.msg.mcps_indication.buffer_size); 00666 _rx_msg.receive_ready = true; 00667 00668 if (_callbacks.events) { 00669 const int ret = _queue->call(_callbacks.events, RX_DONE); 00670 MBED_ASSERT(ret != 0); 00671 (void)ret; 00672 } 00673 00674 //TODO: below if clauses can be combined, 00675 // because those are calling same function with same parameters 00676 00677 // If fPending bit is set we try to generate an empty packet 00678 // with CONFIRMED flag set. We always set a CONFIRMED flag so 00679 // that we could retry a certain number of times if the uplink 00680 // failed for some reason 00681 if (_loramac.get_device_class() != CLASS_C && mcps_indication->fpending_status ) { 00682 handle_tx(mcps_indication->port , NULL, 0, MSG_CONFIRMED_FLAG, true); 00683 } 00684 00685 // Class C and node received a confirmed message so we need to 00686 // send an empty packet to acknowledge the message. 00687 // This scenario is unspecified by LoRaWAN 1.0.2 specification, 00688 // but version 1.1.0 says that network SHALL not send any new 00689 // confirmed messages until ack has been sent 00690 if (_loramac.get_device_class() == CLASS_C && mcps_indication->type == MCPS_CONFIRMED ) { 00691 handle_tx(mcps_indication->port , NULL, 0, MSG_CONFIRMED_FLAG, true); 00692 } 00693 } else { 00694 // Invalid port, ports 0, 224 and 225-255 are reserved. 00695 } 00696 break; 00697 } 00698 } 00699 } 00700 } 00701 00702 lorawan_status_t LoRaWANStack::set_link_check_request() 00703 { 00704 _link_check_requested = true; 00705 if (!_callbacks.link_check_resp) { 00706 tr_error("Must assign a callback function for link check request. "); 00707 return LORAWAN_STATUS_PARAMETER_INVALID; 00708 } 00709 00710 _loramac.setup_link_check_request(); 00711 return LORAWAN_STATUS_OK; 00712 } 00713 00714 void LoRaWANStack::remove_link_check_request() 00715 { 00716 _link_check_requested = false; 00717 } 00718 00719 lorawan_status_t LoRaWANStack::shutdown() 00720 { 00721 return lora_state_machine(DEVICE_STATE_SHUTDOWN); 00722 } 00723 00724 lorawan_status_t LoRaWANStack::set_device_class(const device_class_t & device_class) 00725 { 00726 if (device_class == CLASS_B ) { 00727 return LORAWAN_STATUS_UNSUPPORTED; 00728 } 00729 _loramac.set_device_class(device_class); 00730 return LORAWAN_STATUS_OK; 00731 } 00732 00733 lorawan_status_t LoRaWANStack::lora_state_machine(device_states_t new_state) 00734 { 00735 lorawan_status_t status = LORAWAN_STATUS_DEVICE_OFF; 00736 00737 _device_current_state = new_state; 00738 00739 switch (_device_current_state) { 00740 case DEVICE_STATE_SHUTDOWN: 00741 /* 00742 * Remove channels 00743 * Radio will be put to sleep by the APIs underneath 00744 */ 00745 drop_channel_list(); 00746 _loramac.disconnect(); 00747 00748 #if defined(LORAWAN_COMPLIANCE_TEST) 00749 _loramac.LoRaMacStopTxTimer(); 00750 #endif 00751 _loramac.set_nwk_joined(false); 00752 00753 _loramac.reset_ongoing_tx(true); 00754 00755 _rx_msg.msg.mcps_indication.buffer = NULL; 00756 _rx_msg.receive_ready = false; 00757 _rx_msg.prev_read_size = 0; 00758 _rx_msg.msg.mcps_indication.buffer_size = 0; 00759 00760 _lw_session.active = false; 00761 00762 tr_debug("LoRaWAN protocol has been shut down."); 00763 if (_callbacks.events) { 00764 const int ret = _queue->call(_callbacks.events, DISCONNECTED); 00765 MBED_ASSERT(ret != 0); 00766 (void)ret; 00767 } 00768 status = LORAWAN_STATUS_DEVICE_OFF; 00769 break; 00770 case DEVICE_STATE_NOT_INITIALIZED: 00771 status = LORAWAN_STATUS_DEVICE_OFF; 00772 break; 00773 case DEVICE_STATE_INIT: 00774 status = LORAWAN_STATUS_OK; 00775 break; 00776 case DEVICE_STATE_JOINING: 00777 if (_lw_session.connection.connect_type == LORAWAN_CONNECTION_OTAA) { 00778 tr_debug("Send Join-request.."); 00779 00780 status = _loramac.join_by_otaa(_lw_session.connection.connection_u.otaa); 00781 if (status != LORAWAN_STATUS_OK) { 00782 return status; 00783 } 00784 00785 return LORAWAN_STATUS_CONNECT_IN_PROGRESS; 00786 } else { 00787 status = LORAWAN_STATUS_PARAMETER_INVALID; 00788 } 00789 break; 00790 case DEVICE_STATE_JOINED: 00791 tr_debug("Join OK!"); 00792 00793 _lw_session.active = true; 00794 00795 if (_callbacks.events) { 00796 const int ret = _queue->call(_callbacks.events, CONNECTED); 00797 MBED_ASSERT(ret != 0); 00798 (void)ret; 00799 } 00800 status = LORAWAN_STATUS_OK; 00801 break; 00802 case DEVICE_STATE_ABP_CONNECTING: 00803 00804 _loramac.join_by_abp(_lw_session.connection.connection_u.abp); 00805 00806 tr_debug("ABP Connection OK!"); 00807 00808 status = LORAWAN_STATUS_OK; 00809 00810 _lw_session.active = true; 00811 if (_callbacks.events) { 00812 const int ret = _queue->call(_callbacks.events, CONNECTED); 00813 MBED_ASSERT(ret != 0); 00814 (void)ret; 00815 } 00816 break; 00817 case DEVICE_STATE_SEND: 00818 if (_loramac.tx_ongoing()) { 00819 status = LORAWAN_STATUS_OK; 00820 } else { 00821 _loramac.set_tx_ongoing(true); 00822 status = _loramac.send_ongoing_tx(); 00823 00824 switch (status) { 00825 case LORAWAN_STATUS_OK: 00826 tr_debug("Frame scheduled to TX.."); 00827 break; 00828 case LORAWAN_STATUS_CRYPTO_FAIL: 00829 tr_error("Crypto failed. Clearing TX buffers"); 00830 if (_callbacks.events) { 00831 const int ret = _queue->call(_callbacks.events, TX_CRYPTO_ERROR); 00832 MBED_ASSERT(ret != 0); 00833 (void)ret; 00834 } 00835 break; 00836 default: 00837 tr_error("Failure to schedule TX!"); 00838 if (_callbacks.events) { 00839 const int ret = _queue->call(_callbacks.events, TX_SCHEDULING_ERROR); 00840 MBED_ASSERT(ret != 0); 00841 (void)ret; 00842 } 00843 break; 00844 } 00845 } 00846 00847 _device_current_state = DEVICE_STATE_IDLE; 00848 break; 00849 case DEVICE_STATE_IDLE: 00850 status = LORAWAN_STATUS_IDLE; 00851 break; 00852 #if defined(LORAWAN_COMPLIANCE_TEST) 00853 case DEVICE_STATE_COMPLIANCE_TEST: 00854 tr_debug("Device is in compliance test mode."); 00855 00856 _loramac.LoRaMacSetTxTimer(5000); //ms 00857 if (_compliance_test.running == true) { 00858 send_compliance_test_frame_to_mac(); 00859 } 00860 status = LORAWAN_STATUS_COMPLIANCE_TEST_ON; 00861 break; 00862 #endif 00863 default: 00864 status = LORAWAN_STATUS_SERVICE_UNKNOWN; 00865 break; 00866 } 00867 00868 return status; 00869 } 00870 00871 #if defined(LORAWAN_COMPLIANCE_TEST) 00872 00873 lorawan_status_t LoRaWANStack::send_compliance_test_frame_to_mac() 00874 { 00875 loramac_compliance_test_req_t test_req; 00876 00877 //TODO: What if the port is not 224 ??? 00878 if (_compliance_test.app_port == 224) { 00879 // Clear any normal message stuff before compliance test. 00880 memset(&test_req, 0, sizeof(test_req)); 00881 00882 if (_compliance_test.link_check == true) { 00883 _compliance_test.link_check = false; 00884 _compliance_test.state = 1; 00885 test_req.f_buffer_size = 3; 00886 test_req.f_buffer[0] = 5; 00887 test_req.f_buffer[1] = _compliance_test.demod_margin; 00888 test_req.f_buffer[2] = _compliance_test.nb_gateways; 00889 } else { 00890 switch (_compliance_test.state) { 00891 case 4: 00892 _compliance_test.state = 1; 00893 test_req.f_buffer_size = _compliance_test.app_data_size; 00894 test_req.f_buffer[0] = _compliance_test.app_data_buffer[0]; 00895 for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, MBED_CONF_LORA_TX_MAX_SIZE); ++i) { 00896 test_req.f_buffer[i] = _compliance_test.app_data_buffer[i]; 00897 } 00898 break; 00899 case 1: 00900 test_req.f_buffer_size = 2; 00901 test_req.f_buffer[0] = _compliance_test.downlink_counter >> 8; 00902 test_req.f_buffer[1] = _compliance_test.downlink_counter; 00903 break; 00904 } 00905 } 00906 } 00907 00908 //TODO: If port is not 224, this might not work! 00909 //Is there a test case where same _tx_msg's buffer would be used, when port is not 224??? 00910 if (!_compliance_test.is_tx_confirmed) { 00911 test_req.type = MCPS_UNCONFIRMED ; 00912 test_req.fport = _compliance_test.app_port; 00913 test_req.nb_trials = 1; 00914 test_req.data_rate = _loramac.get_default_tx_datarate(); 00915 00916 tr_info("Transmit unconfirmed compliance test frame %d bytes.", test_req.f_buffer_size); 00917 00918 for (uint8_t i = 0; i < test_req.f_buffer_size; ++i) { 00919 tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)test_req.f_buffer)[i]); 00920 } 00921 } else if (_compliance_test.is_tx_confirmed) { 00922 test_req.type = MCPS_CONFIRMED ; 00923 test_req.fport = _compliance_test.app_port; 00924 test_req.nb_trials = _num_retry; 00925 test_req.data_rate = _loramac.get_default_tx_datarate(); 00926 00927 tr_info("Transmit confirmed compliance test frame %d bytes.", test_req.f_buffer_size); 00928 00929 for (uint8_t i = 0; i < test_req.f_buffer_size; ++i) { 00930 tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)test_req.f_buffer)[i]); 00931 } 00932 } else { 00933 return LORAWAN_STATUS_SERVICE_UNKNOWN; 00934 } 00935 00936 return _loramac.test_request(&test_req); 00937 } 00938 00939 void LoRaWANStack::compliance_test_handler(loramac_mcps_indication_t *mcps_indication) 00940 { 00941 if (_compliance_test.running == false) { 00942 // Check compliance test enable command (i) 00943 if ((mcps_indication->buffer_size == 4) && 00944 (mcps_indication->buffer [0] == 0x01) && 00945 (mcps_indication->buffer [1] == 0x01) && 00946 (mcps_indication->buffer [2] == 0x01) && 00947 (mcps_indication->buffer [3] == 0x01)) { 00948 _compliance_test.is_tx_confirmed = false; 00949 _compliance_test.app_port = 224; 00950 _compliance_test.app_data_size = 2; 00951 _compliance_test.downlink_counter = 0; 00952 _compliance_test.link_check = false; 00953 _compliance_test.demod_margin = 0; 00954 _compliance_test.nb_gateways = 0; 00955 _compliance_test.running = true; 00956 _compliance_test.state = 1; 00957 00958 _loramac.enable_adaptive_datarate(true); 00959 00960 #if MBED_CONF_LORA_PHY == 0 00961 _loramac.LoRaMacTestSetDutyCycleOn(false); 00962 #endif 00963 //5000ms 00964 _loramac.LoRaMacSetTxTimer(5000); 00965 00966 //TODO: Should we call lora_state_machine here instead of just setting the state? 00967 _device_current_state = DEVICE_STATE_COMPLIANCE_TEST; 00968 // lora_state_machine(DEVICE_STATE_COMPLIANCE_TEST); 00969 tr_debug("Compliance test activated."); 00970 } 00971 } else { 00972 _compliance_test.state = mcps_indication->buffer [0]; 00973 switch (_compliance_test.state) { 00974 case 0: // Check compliance test disable command (ii) 00975 _compliance_test.is_tx_confirmed = true; 00976 _compliance_test.app_port = MBED_CONF_LORA_APP_PORT; 00977 _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE; 00978 _compliance_test.downlink_counter = 0; 00979 _compliance_test.running = false; 00980 00981 _loramac.enable_adaptive_datarate(MBED_CONF_LORA_ADR_ON); 00982 00983 #if MBED_CONF_LORA_PHY == 0 00984 _loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON); 00985 #endif 00986 // Go to idle state after compliance test mode. 00987 tr_debug("Compliance test disabled."); 00988 _loramac.LoRaMacStopTxTimer(); 00989 00990 // Clear any compliance test message stuff before going back to normal operation. 00991 _loramac.reset_ongoing_tx(); 00992 lora_state_machine(DEVICE_STATE_IDLE); 00993 break; 00994 case 1: // (iii, iv) 00995 _compliance_test.app_data_size = 2; 00996 break; 00997 case 2: // Enable confirmed messages (v) 00998 _compliance_test.is_tx_confirmed = true; 00999 _compliance_test.state = 1; 01000 break; 01001 case 3: // Disable confirmed messages (vi) 01002 _compliance_test.is_tx_confirmed = false; 01003 _compliance_test.state = 1; 01004 break; 01005 case 4: // (vii) 01006 _compliance_test.app_data_size = mcps_indication->buffer_size ; 01007 01008 _compliance_test.app_data_buffer[0] = 4; 01009 for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, LORAMAC_PHY_MAXPAYLOAD); ++i) { 01010 _compliance_test.app_data_buffer[i] = mcps_indication->buffer [i] + 1; 01011 } 01012 01013 send_compliance_test_frame_to_mac(); 01014 break; 01015 case 5: // (viii) 01016 _loramac.setup_link_check_request(); 01017 break; 01018 case 6: // (ix) 01019 // Disable TestMode and revert back to normal operation 01020 _compliance_test.is_tx_confirmed = true; 01021 _compliance_test.app_port = MBED_CONF_LORA_APP_PORT; 01022 _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE; 01023 _compliance_test.downlink_counter = 0; 01024 _compliance_test.running = false; 01025 01026 _loramac.enable_adaptive_datarate(MBED_CONF_LORA_ADR_ON); 01027 01028 #if MBED_CONF_LORA_PHY == 0 01029 _loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON); 01030 #endif 01031 _loramac.join_by_otaa(_lw_session.connection.connection_u.otaa); 01032 break; 01033 case 7: // (x) 01034 if (mcps_indication->buffer_size == 3) { 01035 loramac_mlme_req_t mlme_req; 01036 mlme_req.type = MLME_TXCW ; 01037 mlme_req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer [1] << 8) | mcps_indication->buffer [2]); 01038 _loramac.mlme_request(&mlme_req); 01039 } else if (mcps_indication->buffer_size == 7) { 01040 loramac_mlme_req_t mlme_req; 01041 mlme_req.type = MLME_TXCW_1 ; 01042 mlme_req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer [1] << 8) | mcps_indication->buffer [2]); 01043 mlme_req.cw_tx_mode.frequency = (uint32_t)((mcps_indication->buffer [3] << 16) | (mcps_indication->buffer [4] << 8) 01044 | mcps_indication->buffer [5]) * 100; 01045 mlme_req.cw_tx_mode.power = mcps_indication->buffer [6]; 01046 _loramac.mlme_request(&mlme_req); 01047 } 01048 _compliance_test.state = 1; 01049 break; 01050 } 01051 } 01052 } 01053 #endif 01054
Generated on Tue Jul 12 2022 12:11:25 by
