diff --git a/features/lorawan/LoRaWANStack.cpp b/features/lorawan/LoRaWANStack.cpp index 64e8b007d7..b6bcd4b142 100644 --- a/features/lorawan/LoRaWANStack.cpp +++ b/features/lorawan/LoRaWANStack.cpp @@ -435,7 +435,7 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t& device_cla if (device_class == CLASS_B) { return LORAWAN_STATUS_UNSUPPORTED; } - _loramac.set_device_class(device_class); + _loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::handle_ack_expiry_for_class_c)); return LORAWAN_STATUS_OK; } @@ -564,7 +564,7 @@ void LoRaWANStack::process_transmission(void) _ctrl_flags |= TX_DONE_FLAG; // In Class C, reception timeout never happens, so we handle the state - // progression here + // progression for TX_DONE in UNCONFIRMED case here if (_loramac.get_device_class() == CLASS_C) { _loramac.post_process_mcps_req(); state_controller(DEVICE_STATE_STATUS_CHECK); @@ -573,6 +573,14 @@ void LoRaWANStack::process_transmission(void) } } +void LoRaWANStack::handle_ack_expiry_for_class_c(void) +{ + _ctrl_flags &= ~TX_DONE_FLAG; + _ctrl_flags |= TX_ONGOING_FLAG; + tr_error("Retries exhausted for Class C device"); + state_controller(DEVICE_STATE_STATUS_CHECK); +} + void LoRaWANStack::process_reception(const uint8_t* const payload, uint16_t size, int16_t rssi, int8_t snr) { diff --git a/features/lorawan/LoRaWANStack.h b/features/lorawan/LoRaWANStack.h index 3d627b9c3b..a92f63ae07 100644 --- a/features/lorawan/LoRaWANStack.h +++ b/features/lorawan/LoRaWANStack.h @@ -522,6 +522,8 @@ private: void make_tx_metadata_available(void); void make_rx_metadata_available(void); + void handle_ack_expiry_for_class_c(void); + private: LoRaMac _loramac; radio_events_t radio_events; diff --git a/features/lorawan/lorastack/mac/LoRaMac.cpp b/features/lorawan/lorastack/mac/LoRaMac.cpp index 497e49bbc0..55fe23f56b 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.cpp +++ b/features/lorawan/lorastack/mac/LoRaMac.cpp @@ -641,8 +641,10 @@ void LoRaMac::handle_data_frame(const uint8_t* const payload, _mcps_indication.buffer_size = size - ptr_pos; } - check_to_disable_ack_timeout(_params.is_node_ack_requested, _device_class, _mcps_confirmation.ack_received, - _params.ack_timeout_retry_counter, _params.max_ack_timeout_retries ); + // only stop act timer, if the ack is actuall recieved + if (_mcps_confirmation.ack_received) { + _lora_time.stop(_params.timers.ack_timeout_timer); + } } void LoRaMac::set_batterylevel_callback(mbed::Callback battery_level) @@ -775,7 +777,7 @@ bool LoRaMac::continue_joining_process() bool LoRaMac::continue_sending_process() { - if (_params.ack_timeout_retry_counter >= _params.max_ack_timeout_retries) { + if (_params.ack_timeout_retry_counter > _params.max_ack_timeout_retries) { _mac_commands.clear_command_buffer(); _params.adr_ack_counter++; return false; @@ -892,38 +894,20 @@ void LoRaMac::open_rx2_window() } } -void LoRaMac::check_to_disable_ack_timeout(bool node_ack_requested, - device_class_t dev_class, - bool ack_received, - uint8_t ack_timeout_retries_counter, - uint8_t ack_timeout_retries) -{ - // There are three cases where we need to stop the AckTimeoutTimer: - if( node_ack_requested == false ) { - if( dev_class == CLASS_C ) { - // FIRST CASE - // We have performed an unconfirmed uplink in class c mode - // and have received a downlink in RX1 or RX2. - _lora_time.stop(_params.timers.ack_timeout_timer); - } - } else { - if( ack_received == 1 ) { - // SECOND CASE - // We received an ACK for previously sent confirmable message - _lora_time.stop(_params.timers.ack_timeout_timer); - } else { - // THIRD CASE - // Max number of retries exceeded for confirmable message - if( ack_timeout_retries_counter > ack_timeout_retries ) { - _lora_time.stop(_params.timers.ack_timeout_timer); - } - } - } -} - void LoRaMac::on_ack_timeout_timer_event(void) { Lock lock(*this); + + if (_params.ack_timeout_retry_counter > _params.max_ack_timeout_retries) { + if (get_device_class() == CLASS_C) { + // no need to use EventQueue as LoRaWANStack and LoRaMac are always + // in same context + _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR; + _ack_expiry_handler_for_class_c.call(); + } + return; + } + tr_debug("ACK_TIMEOUT Elapses, Retrying ..."); _lora_time.stop(_params.timers.ack_timeout_timer); @@ -1318,11 +1302,14 @@ device_class_t LoRaMac::get_device_class() const return _device_class; } -void LoRaMac::set_device_class(const device_class_t& device_class) +void LoRaMac::set_device_class(const device_class_t& device_class, + mbed::Callbackack_expiry_handler) { _device_class = device_class; + _ack_expiry_handler_for_class_c = ack_expiry_handler; if (CLASS_A == _device_class) { + tr_debug("Changing device class to -> CLASS_A"); _lora_phy.put_radio_to_sleep(); } else if (CLASS_C == _device_class) { _params.is_node_ack_requested = false; @@ -1334,8 +1321,11 @@ void LoRaMac::set_device_class(const device_class_t& device_class) &_params.rx_window2_config); } if (CLASS_C == _device_class) { + tr_debug("Changing device class to -> CLASS_C"); open_rx2_window(); } + + } void LoRaMac::setup_link_check_request() diff --git a/features/lorawan/lorastack/mac/LoRaMac.h b/features/lorawan/lorastack/mac/LoRaMac.h index 5c510f3c97..b6c0a18594 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.h +++ b/features/lorawan/lorastack/mac/LoRaMac.h @@ -351,8 +351,10 @@ public: /** * @brief set_device_class Sets active device class. * @param device_class Device class to use. + * @param ack_expiry_handler callback function to inform about ack expiry */ - void set_device_class(const device_class_t& device_class); + void set_device_class(const device_class_t& device_class, + mbed::Callbackack_expiry_handler); /** * @brief opens a continuous RX2 window for Class C devices @@ -642,6 +644,13 @@ private: */ events::EventQueue *_ev_queue; + /** + * Class C doesn't timeout in RX2 window as it is a continuous window. + * We use this callback to inform the LoRaWANStack controller that the + * system cannot do more retries. + */ + mbed::Callback _ack_expiry_handler_for_class_c; + /** * Structure to hold MCPS indication data. */