2017-11-27 13:17:18 +00:00
|
|
|
/**
|
|
|
|
/ _____) _ | |
|
|
|
|
( (____ _____ ____ _| |_ _____ ____| |__
|
|
|
|
\____ \| ___ | (_ _) ___ |/ ___) _ \
|
|
|
|
_____) ) ____| | | || |_| ____( (___| | | |
|
|
|
|
(______/|_____)_|_|_| \__)_____)\____)_| |_|
|
|
|
|
(C)2013 Semtech
|
|
|
|
___ _____ _ ___ _ _____ ___ ___ ___ ___
|
|
|
|
/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
|
|
|
|
\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
|
|
|
|
|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
|
|
|
|
embedded.connectivity.solutions===============
|
|
|
|
|
|
|
|
Description: LoRaWAN stack layer that controls both MAC and PHY underneath
|
|
|
|
|
|
|
|
License: Revised BSD License, see LICENSE.TXT file include in the project
|
|
|
|
|
|
|
|
Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (c) 2017, Arm Limited and affiliates.
|
|
|
|
|
|
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "platform/Callback.h"
|
2017-12-11 10:20:11 +00:00
|
|
|
#include "events/EventQueue.h"
|
2018-04-10 11:04:18 +00:00
|
|
|
|
|
|
|
#include "LoRaWANStack.h"
|
2018-05-15 11:29:45 +00:00
|
|
|
|
|
|
|
#include "mbed-trace/mbed_trace.h"
|
2017-11-27 13:17:18 +00:00
|
|
|
#define TRACE_GROUP "LSTK"
|
|
|
|
|
|
|
|
#define INVALID_PORT 0xFF
|
|
|
|
#define MAX_CONFIRMED_MSG_RETRIES 255
|
2018-05-03 15:49:14 +00:00
|
|
|
/**
|
|
|
|
* Control flags for transient states
|
|
|
|
*/
|
|
|
|
#define IDLE_FLAG 0x00000000
|
|
|
|
#define TX_ONGOING_FLAG 0x00000001
|
|
|
|
#define MSG_RECVD_FLAG 0x00000002
|
|
|
|
#define CONNECTED_FLAG 0x00000004
|
|
|
|
#define USING_OTAA_FLAG 0x00000008
|
|
|
|
#define TX_DONE_FLAG 0x00000010
|
2017-11-27 13:17:18 +00:00
|
|
|
|
|
|
|
using namespace mbed;
|
2017-12-11 10:20:11 +00:00
|
|
|
using namespace events;
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-01-05 10:13:48 +00:00
|
|
|
#if defined(LORAWAN_COMPLIANCE_TEST)
|
|
|
|
#if (MBED_CONF_LORA_PHY == 0 || MBED_CONF_LORA_PHY == 4 || MBED_CONF_LORA_PHY == 6 || MBED_CONF_LORA_PHY == 7)
|
|
|
|
#define LORAWAN_COMPLIANCE_TEST_DATA_SIZE 16
|
|
|
|
#elif (MBED_CONF_LORA_PHY == 1 || MBED_CONF_LORA_PHY == 2 || MBED_CONF_LORA_PHY == 8 || MBED_CONF_LORA_PHY == 9)
|
|
|
|
#define LORAWAN_COMPLIANCE_TEST_DATA_SIZE 11
|
|
|
|
#else
|
|
|
|
#error "Must set LoRa PHY layer parameters."
|
|
|
|
#endif
|
2017-11-27 13:17:18 +00:00
|
|
|
#endif
|
|
|
|
|
2018-05-18 12:27:23 +00:00
|
|
|
/**
|
|
|
|
* Bit mask for message flags
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MSG_FLAG_MASK 0x0F
|
|
|
|
|
2017-11-27 13:17:18 +00:00
|
|
|
/*****************************************************************************
|
2018-05-03 15:49:14 +00:00
|
|
|
* Constructor *
|
2017-11-27 13:17:18 +00:00
|
|
|
****************************************************************************/
|
|
|
|
LoRaWANStack::LoRaWANStack()
|
2018-03-12 11:30:00 +00:00
|
|
|
: _loramac(),
|
2018-05-03 15:49:14 +00:00
|
|
|
_device_current_state(DEVICE_STATE_NOT_INITIALIZED),
|
|
|
|
_lw_session(),
|
|
|
|
_tx_msg(),
|
|
|
|
_rx_msg(),
|
2018-05-15 11:59:55 +00:00
|
|
|
_tx_metadata(),
|
2018-05-15 12:07:02 +00:00
|
|
|
_rx_metadata(),
|
2018-05-03 15:49:14 +00:00
|
|
|
_num_retry(1),
|
|
|
|
_ctrl_flags(IDLE_FLAG),
|
|
|
|
_app_port(INVALID_PORT),
|
|
|
|
_link_check_requested(false),
|
|
|
|
_automatic_uplink_ongoing(false),
|
|
|
|
_ready_for_rx(true),
|
|
|
|
_queue(NULL)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-15 11:59:55 +00:00
|
|
|
_tx_metadata.stale = true;
|
2018-05-15 12:07:02 +00:00
|
|
|
_rx_metadata.stale = true;
|
2018-05-15 11:59:55 +00:00
|
|
|
|
2017-11-27 13:17:18 +00:00
|
|
|
#ifdef MBED_CONF_LORA_APP_PORT
|
|
|
|
if (is_port_valid(MBED_CONF_LORA_APP_PORT)) {
|
|
|
|
_app_port = MBED_CONF_LORA_APP_PORT;
|
|
|
|
} else {
|
|
|
|
tr_error("User defined port in .json is illegal.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
2018-05-03 15:49:14 +00:00
|
|
|
* Public Methods *
|
2017-11-27 13:17:18 +00:00
|
|
|
****************************************************************************/
|
2018-03-12 11:30:00 +00:00
|
|
|
void LoRaWANStack::bind_radio_driver(LoRaRadio& radio)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
radio_events.tx_done = mbed::callback(this, &LoRaWANStack::tx_interrupt_handler);
|
|
|
|
radio_events.rx_done = mbed::callback(this, &LoRaWANStack::rx_interrupt_handler);
|
|
|
|
radio_events.rx_error = mbed::callback(this, &LoRaWANStack::rx_error_interrupt_handler);
|
|
|
|
radio_events.tx_timeout = mbed::callback(this, &LoRaWANStack::tx_timeout_interrupt_handler);
|
|
|
|
radio_events.rx_timeout = mbed::callback(this, &LoRaWANStack::rx_timeout_interrupt_handler);
|
|
|
|
|
2018-03-15 08:43:31 +00:00
|
|
|
_loramac.bind_radio_driver(radio);
|
2018-05-03 15:49:14 +00:00
|
|
|
|
|
|
|
radio.lock();
|
|
|
|
radio.init_radio(&radio_events);
|
|
|
|
radio.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
lorawan_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue)
|
|
|
|
{
|
|
|
|
if(!queue) {
|
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
tr_debug("Initializing MAC layer");
|
|
|
|
_queue = queue;
|
|
|
|
|
|
|
|
return state_controller(DEVICE_STATE_IDLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
lorawan_status_t LoRaWANStack::set_lora_callbacks(const lorawan_app_callbacks_t *callbacks)
|
|
|
|
{
|
|
|
|
if (!callbacks || !callbacks->events) {
|
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
_callbacks.events = callbacks->events;
|
|
|
|
|
|
|
|
if (callbacks->link_check_resp) {
|
|
|
|
_callbacks.link_check_resp = callbacks->link_check_resp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (callbacks->battery_level) {
|
|
|
|
_callbacks.battery_level = callbacks->battery_level;
|
2018-05-11 06:50:24 +00:00
|
|
|
_loramac.set_batterylevel_callback(callbacks->battery_level);
|
2018-05-03 15:49:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return LORAWAN_STATUS_OK;
|
2018-03-12 11:30:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lorawan_status_t LoRaWANStack::connect()
|
|
|
|
{
|
2018-04-09 08:08:29 +00:00
|
|
|
if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) {
|
|
|
|
tr_error("Stack not initialized!");
|
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
|
|
|
}
|
|
|
|
|
|
|
|
lorawan_status_t status = _loramac.prepare_join(NULL, MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION);
|
|
|
|
|
|
|
|
if (LORAWAN_STATUS_OK != status) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
return handle_connect(MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION);
|
2018-03-12 11:30:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lorawan_status_t LoRaWANStack::connect(const lorawan_connect_t &connect)
|
|
|
|
{
|
2018-04-09 08:08:29 +00:00
|
|
|
if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) {
|
|
|
|
tr_error("Stack not initialized!");
|
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
|
|
|
}
|
2018-03-12 11:30:00 +00:00
|
|
|
|
2018-04-09 08:08:29 +00:00
|
|
|
if (!(connect.connect_type == LORAWAN_CONNECTION_OTAA) &&
|
|
|
|
!(connect.connect_type == LORAWAN_CONNECTION_ABP)) {
|
2018-03-12 11:30:00 +00:00
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
|
|
|
}
|
2018-04-09 08:08:29 +00:00
|
|
|
bool is_otaa = (connect.connect_type == LORAWAN_CONNECTION_OTAA);
|
2018-03-12 11:30:00 +00:00
|
|
|
|
2018-04-09 08:08:29 +00:00
|
|
|
lorawan_status_t status = _loramac.prepare_join(&connect, is_otaa);
|
|
|
|
|
|
|
|
if (LORAWAN_STATUS_OK != status) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
return handle_connect(is_otaa);
|
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::add_channels(const lorawan_channelplan_t &channel_plan)
|
2017-12-15 10:30:40 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) {
|
|
|
|
tr_error("Stack not initialized!");
|
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
2017-12-21 11:37:38 +00:00
|
|
|
}
|
2018-03-12 11:30:00 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
return _loramac.add_channel_plan(channel_plan);
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::remove_a_channel(uint8_t channel_id)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
|
|
|
if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) {
|
|
|
|
tr_error("Stack not initialized!");
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
return _loramac.remove_single_channel(channel_id);
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 14:39:31 +00:00
|
|
|
lorawan_status_t LoRaWANStack::drop_channel_list()
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-01-11 11:55:08 +00:00
|
|
|
if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) {
|
2017-11-27 13:17:18 +00:00
|
|
|
tr_error("Stack not initialized!");
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 14:23:33 +00:00
|
|
|
return _loramac.remove_channel_plan();
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::get_enabled_channels(lorawan_channelplan_t& channel_plan)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) {
|
2017-11-27 13:17:18 +00:00
|
|
|
tr_error("Stack not initialized!");
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
return _loramac.get_channel_plan(channel_plan);
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
if (count >= MAX_CONFIRMED_MSG_RETRIES) {
|
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
_num_retry = count;
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-03-16 13:40:02 +00:00
|
|
|
return LORAWAN_STATUS_OK;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 14:39:31 +00:00
|
|
|
lorawan_status_t LoRaWANStack::set_channel_data_rate(uint8_t data_rate)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
|
|
|
if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state)
|
|
|
|
{
|
|
|
|
tr_error("Stack not initialized!");
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 11:12:50 +00:00
|
|
|
return _loramac.set_channel_data_rate(data_rate);
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-04-09 12:11:08 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled)
|
2018-04-09 12:11:08 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED)
|
|
|
|
{
|
|
|
|
tr_error("Stack not initialized!");
|
|
|
|
return LORAWAN_STATUS_NOT_INITIALIZED;
|
2018-04-09 12:11:08 +00:00
|
|
|
}
|
2018-05-03 15:49:14 +00:00
|
|
|
_loramac.enable_adaptive_datarate(adr_enabled);
|
|
|
|
return LORAWAN_STATUS_OK;
|
2018-04-09 12:11:08 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 12:22:15 +00:00
|
|
|
lorawan_status_t LoRaWANStack::stop_sending(void)
|
|
|
|
{
|
2018-05-16 10:12:44 +00:00
|
|
|
if (_loramac.clear_tx_pipe() == LORAWAN_STATUS_OK) {
|
|
|
|
if (_device_current_state == DEVICE_STATE_SENDING) {
|
|
|
|
_ctrl_flags &= ~TX_DONE_FLAG;
|
|
|
|
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
|
|
|
_loramac.set_tx_ongoing(false);
|
|
|
|
_device_current_state = DEVICE_STATE_IDLE;
|
|
|
|
return LORAWAN_STATUS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return LORAWAN_STATUS_BUSY;
|
2018-05-15 12:22:15 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
int16_t LoRaWANStack::handle_tx(const uint8_t port, const uint8_t* data,
|
2018-04-09 12:11:08 +00:00
|
|
|
uint16_t length, uint8_t flags,
|
|
|
|
bool null_allowed, bool allow_port_0)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-03-15 08:43:31 +00:00
|
|
|
if (!null_allowed && !data) {
|
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
|
|
|
}
|
|
|
|
// add a link check request with normal data, until the application
|
|
|
|
// explicitly removes it.
|
2018-03-12 11:30:00 +00:00
|
|
|
if (_link_check_requested) {
|
|
|
|
set_link_check_request();
|
|
|
|
}
|
|
|
|
|
2017-11-27 13:17:18 +00:00
|
|
|
if (!_lw_session.active) {
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_NO_ACTIVE_SESSIONS;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-03-15 08:43:31 +00:00
|
|
|
if(_loramac.tx_ongoing()) {
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_WOULD_BLOCK;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 10:13:48 +00:00
|
|
|
#if defined(LORAWAN_COMPLIANCE_TEST)
|
2017-11-27 13:17:18 +00:00
|
|
|
if (_compliance_test.running) {
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_COMPLIANCE_TEST_ON;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
2018-01-05 10:13:48 +00:00
|
|
|
#endif
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-01-12 14:39:31 +00:00
|
|
|
lorawan_status_t status;
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-03-16 11:12:50 +00:00
|
|
|
if (_loramac.nwk_joined() == false) {
|
|
|
|
return LORAWAN_STATUS_NO_NETWORK_JOINED;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-04-09 12:11:08 +00:00
|
|
|
status = set_application_port(port, allow_port_0);
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-01-12 14:39:31 +00:00
|
|
|
if (status != LORAWAN_STATUS_OK) {
|
2017-11-27 13:17:18 +00:00
|
|
|
tr_error("Illegal application port definition.");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2018-05-18 12:27:23 +00:00
|
|
|
// All the flags mutually exclusive. In addition to that MSG_MULTICAST_FLAG cannot be
|
|
|
|
// used for uplink.
|
|
|
|
switch (flags & MSG_FLAG_MASK) {
|
|
|
|
case MSG_UNCONFIRMED_FLAG:
|
|
|
|
case MSG_CONFIRMED_FLAG:
|
|
|
|
case MSG_PROPRIETARY_FLAG:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
tr_error("Invalid send flags");
|
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-03-15 08:43:31 +00:00
|
|
|
int16_t len = _loramac.prepare_ongoing_tx(port, data, length, flags, _num_retry);
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
status = state_controller(DEVICE_STATE_SCHEDULING);
|
2017-11-27 13:17:18 +00:00
|
|
|
|
|
|
|
// send user the length of data which is scheduled now.
|
|
|
|
// user should take care of the pending data.
|
2018-03-15 08:43:31 +00:00
|
|
|
return (status == LORAWAN_STATUS_OK) ? len : (int16_t) status;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-04-10 10:04:23 +00:00
|
|
|
int16_t LoRaWANStack::handle_rx(uint8_t* data, uint16_t length, uint8_t& port, int& flags, bool validate_params)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
|
|
|
if (!_lw_session.active) {
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_NO_ACTIVE_SESSIONS;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// No messages to read.
|
|
|
|
if (!_rx_msg.receive_ready) {
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_WOULD_BLOCK;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 10:13:48 +00:00
|
|
|
#if defined(LORAWAN_COMPLIANCE_TEST)
|
2017-11-27 13:17:18 +00:00
|
|
|
if (_compliance_test.running) {
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_COMPLIANCE_TEST_ON;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
2018-01-05 10:13:48 +00:00
|
|
|
#endif
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-03-12 11:30:00 +00:00
|
|
|
if (data == NULL || length == 0) {
|
2018-01-12 14:39:31 +00:00
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-04-10 10:04:23 +00:00
|
|
|
int received_flags = convert_to_msg_flag(_rx_msg.msg.mcps_indication.type);
|
|
|
|
if (validate_params) {
|
|
|
|
// Check received message port and flags match with the ones requested by user
|
|
|
|
received_flags &= MSG_FLAG_MASK;
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-04-10 10:04:23 +00:00
|
|
|
if (_rx_msg.msg.mcps_indication.port != port || !(flags & received_flags)) {
|
|
|
|
return LORAWAN_STATUS_WOULD_BLOCK;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-04-10 10:04:23 +00:00
|
|
|
// Report values back to user
|
|
|
|
port = _rx_msg.msg.mcps_indication.port;
|
|
|
|
flags = received_flags;
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-04-10 10:04:23 +00:00
|
|
|
const uint8_t *base_ptr = _rx_msg.msg.mcps_indication.buffer;
|
|
|
|
uint16_t base_size = _rx_msg.msg.mcps_indication.buffer_size;
|
|
|
|
bool read_complete = false;
|
2017-11-27 13:17:18 +00:00
|
|
|
|
|
|
|
// check the length of received message whether we can fit into user
|
|
|
|
// buffer completely or not
|
2018-01-12 14:39:31 +00:00
|
|
|
if (_rx_msg.msg.mcps_indication.buffer_size > length &&
|
2018-04-10 10:04:23 +00:00
|
|
|
_rx_msg.prev_read_size == 0) {
|
2017-11-27 13:17:18 +00:00
|
|
|
// we can't fit into user buffer. Invoke counter measures
|
2018-01-12 14:39:31 +00:00
|
|
|
_rx_msg.pending_size = _rx_msg.msg.mcps_indication.buffer_size - length;
|
2017-11-27 13:17:18 +00:00
|
|
|
base_size = length;
|
|
|
|
_rx_msg.prev_read_size = base_size;
|
|
|
|
memcpy(data, base_ptr, base_size);
|
|
|
|
} else if (_rx_msg.prev_read_size == 0) {
|
|
|
|
_rx_msg.pending_size = 0;
|
|
|
|
_rx_msg.prev_read_size = 0;
|
|
|
|
memcpy(data, base_ptr, base_size);
|
|
|
|
read_complete = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If its the pending read then we should copy only the remaining part of
|
|
|
|
// the buffer. Due to checks above, in case of a pending read, this block
|
|
|
|
// will be the only one to get invoked
|
|
|
|
if (_rx_msg.pending_size > 0 && _rx_msg.prev_read_size > 0) {
|
|
|
|
memcpy(data, base_ptr+_rx_msg.prev_read_size, base_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// we are done handing over received buffer to user. check if there is
|
|
|
|
// anything pending. If not, memset the buffer to zero and indicate
|
|
|
|
// that no read is in progress
|
|
|
|
if (read_complete) {
|
2018-05-03 15:49:14 +00:00
|
|
|
_rx_msg.msg.mcps_indication.buffer = NULL;
|
|
|
|
_rx_msg.msg.mcps_indication.buffer_size = 0;
|
2017-11-27 13:17:18 +00:00
|
|
|
_rx_msg.receive_ready = false;
|
|
|
|
}
|
|
|
|
|
2017-12-14 13:20:27 +00:00
|
|
|
return base_size;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::set_link_check_request()
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
_link_check_requested = true;
|
|
|
|
if (!_callbacks.link_check_resp) {
|
|
|
|
tr_error("Must assign a callback function for link check request. ");
|
|
|
|
return LORAWAN_STATUS_PARAMETER_INVALID;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
2018-05-03 15:49:14 +00:00
|
|
|
|
|
|
|
_loramac.setup_link_check_request();
|
|
|
|
return LORAWAN_STATUS_OK;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::remove_link_check_request()
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
_link_check_requested = false;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::shutdown()
|
|
|
|
{
|
|
|
|
return state_controller(DEVICE_STATE_SHUTDOWN);
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::set_device_class(const device_class_t& device_class)
|
|
|
|
{
|
|
|
|
if (device_class == CLASS_B) {
|
|
|
|
return LORAWAN_STATUS_UNSUPPORTED;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
2018-05-16 09:59:19 +00:00
|
|
|
_loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::handle_ack_expiry_for_class_c));
|
2018-05-03 15:49:14 +00:00
|
|
|
return LORAWAN_STATUS_OK;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-16 10:12:44 +00:00
|
|
|
lorawan_status_t LoRaWANStack::acquire_tx_metadata(lorawan_tx_metadata &tx_metadata)
|
2018-05-15 11:59:55 +00:00
|
|
|
{
|
|
|
|
if (!_tx_metadata.stale) {
|
|
|
|
tx_metadata = _tx_metadata;
|
|
|
|
_tx_metadata.stale = true;
|
|
|
|
return LORAWAN_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LORAWAN_STATUS_METADATA_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-05-16 10:12:44 +00:00
|
|
|
lorawan_status_t LoRaWANStack::acquire_rx_metadata(lorawan_rx_metadata &metadata)
|
2018-05-15 12:07:02 +00:00
|
|
|
{
|
|
|
|
if (!_rx_metadata.stale) {
|
|
|
|
metadata = _rx_metadata;
|
|
|
|
_rx_metadata.stale = true;
|
|
|
|
return LORAWAN_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LORAWAN_STATUS_METADATA_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-05-15 12:18:13 +00:00
|
|
|
lorawan_status_t LoRaWANStack::acquire_backoff_metadata(int& backoff)
|
|
|
|
{
|
|
|
|
int id = _loramac.get_backoff_timer_event_id();
|
|
|
|
|
|
|
|
if (_loramac.get_backoff_timer_event_id() > 0) {
|
|
|
|
backoff = _queue->time_left(id);
|
|
|
|
return LORAWAN_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
backoff = -1;
|
|
|
|
return LORAWAN_STATUS_METADATA_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* Interrupt handlers *
|
|
|
|
****************************************************************************/
|
|
|
|
void LoRaWANStack::tx_interrupt_handler(void)
|
|
|
|
{
|
|
|
|
const int ret = _queue->call(this, &LoRaWANStack::process_transmission);
|
|
|
|
MBED_ASSERT(ret != 0);
|
|
|
|
(void)ret;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::rx_interrupt_handler(const uint8_t *payload, uint16_t size,
|
|
|
|
int16_t rssi, int8_t snr)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
if (!_ready_for_rx || size > sizeof _rx_payload) {
|
2017-11-27 13:17:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
_ready_for_rx = false;
|
|
|
|
memcpy(_rx_payload, payload, size);
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
const uint8_t *ptr = _rx_payload;
|
|
|
|
const int ret = _queue->call(this, &LoRaWANStack::process_reception,
|
|
|
|
ptr, size, rssi, snr);
|
|
|
|
MBED_ASSERT(ret != 0);
|
|
|
|
(void)ret;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::rx_error_interrupt_handler(void)
|
|
|
|
{
|
|
|
|
const int ret = _queue->call(this, &LoRaWANStack::process_reception_timeout,
|
|
|
|
false);
|
|
|
|
MBED_ASSERT(ret != 0);
|
|
|
|
(void)ret;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::tx_timeout_interrupt_handler(void)
|
|
|
|
{
|
|
|
|
const int ret = _queue->call(this, &LoRaWANStack::process_transmission_timeout);
|
|
|
|
MBED_ASSERT(ret != 0);
|
|
|
|
(void)ret;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::rx_timeout_interrupt_handler(void)
|
|
|
|
{
|
|
|
|
const int ret = _queue->call(this, &LoRaWANStack::process_reception_timeout,
|
|
|
|
true);
|
|
|
|
MBED_ASSERT(ret != 0);
|
|
|
|
(void)ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Processors for deferred interrupts *
|
|
|
|
****************************************************************************/
|
|
|
|
void LoRaWANStack::process_transmission_timeout()
|
|
|
|
{
|
|
|
|
// this is a fatal error and should not happen
|
|
|
|
tr_debug("TX Timeout");
|
|
|
|
_loramac.on_radio_tx_timeout();
|
2018-05-15 12:22:15 +00:00
|
|
|
_ctrl_flags |= TX_ONGOING_FLAG;
|
|
|
|
_ctrl_flags &= ~TX_DONE_FLAG;
|
2018-05-03 15:49:14 +00:00
|
|
|
state_controller(DEVICE_STATE_STATUS_CHECK);
|
|
|
|
state_machine_run_to_completion();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_transmission(void)
|
|
|
|
{
|
|
|
|
tr_debug("Transmission completed");
|
2018-05-16 09:43:57 +00:00
|
|
|
_loramac.on_radio_tx_done();
|
2018-05-03 15:49:14 +00:00
|
|
|
|
2018-05-15 11:59:55 +00:00
|
|
|
make_tx_metadata_available();
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_device_current_state == DEVICE_STATE_JOINING) {
|
|
|
|
_device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT;
|
2018-04-06 09:36:20 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_device_current_state == DEVICE_STATE_SENDING) {
|
|
|
|
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
|
|
|
_ctrl_flags |= TX_ONGOING_FLAG;
|
|
|
|
_ctrl_flags &= ~TX_DONE_FLAG;
|
2018-05-15 12:30:43 +00:00
|
|
|
tr_debug("Awaiting ACK");
|
2018-05-03 15:49:14 +00:00
|
|
|
_device_current_state = DEVICE_STATE_AWAITING_ACK;
|
|
|
|
return;
|
2018-04-06 09:36:20 +00:00
|
|
|
}
|
2018-05-03 15:49:14 +00:00
|
|
|
|
|
|
|
// Class A unconfirmed message sent, TX_DONE event will be sent to
|
|
|
|
// application when RX2 windows is elapsed, i.e., in process_reception_timeout()
|
|
|
|
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
|
|
|
_ctrl_flags |= TX_DONE_FLAG;
|
|
|
|
|
|
|
|
// In Class C, reception timeout never happens, so we handle the state
|
2018-05-16 09:59:19 +00:00
|
|
|
// progression for TX_DONE in UNCONFIRMED case here
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_loramac.get_device_class() == CLASS_C) {
|
|
|
|
_loramac.post_process_mcps_req();
|
|
|
|
state_controller(DEVICE_STATE_STATUS_CHECK);
|
|
|
|
state_machine_run_to_completion();
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-16 09:59:19 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::process_reception(const uint8_t* const payload, uint16_t size,
|
|
|
|
int16_t rssi, int8_t snr)
|
2017-12-15 10:30:40 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
_device_current_state = DEVICE_STATE_RECEIVING;
|
|
|
|
_ctrl_flags &= ~MSG_RECVD_FLAG;
|
|
|
|
|
|
|
|
_loramac.on_radio_rx_done(payload, size, rssi, snr);
|
|
|
|
|
2018-05-15 12:07:02 +00:00
|
|
|
make_rx_metadata_available();
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_loramac.get_mlme_confirmation()->pending) {
|
|
|
|
_loramac.post_process_mlme_request();
|
|
|
|
mlme_confirm_handler();
|
2017-12-15 10:30:40 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_loramac.nwk_joined()) {
|
2018-05-14 10:32:47 +00:00
|
|
|
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
2018-05-03 15:49:14 +00:00
|
|
|
// if ack was not received, we will try retransmission after
|
|
|
|
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
|
2018-05-15 12:22:15 +00:00
|
|
|
// if ack was received. Otherwise, following method will be called in
|
|
|
|
// LoRaMac.cpp, on_ack_timeout_timer_event().
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_loramac.get_mcps_indication()->is_ack_recvd) {
|
|
|
|
tr_debug("Ack=OK, NbTrials=%d", _loramac.get_mcps_confirmation()->nb_retries);
|
|
|
|
_loramac.post_process_mcps_req();
|
|
|
|
_ctrl_flags |= TX_DONE_FLAG;
|
2018-05-15 12:22:15 +00:00
|
|
|
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
2018-05-03 15:49:14 +00:00
|
|
|
state_controller(DEVICE_STATE_STATUS_CHECK);
|
2018-05-15 12:22:15 +00:00
|
|
|
} else {
|
|
|
|
if (!_loramac.continue_sending_process()) {
|
|
|
|
tr_error("Retries exhausted for Class A device");
|
|
|
|
_ctrl_flags &= ~TX_DONE_FLAG;
|
|
|
|
_ctrl_flags |= TX_ONGOING_FLAG;
|
|
|
|
state_controller(DEVICE_STATE_STATUS_CHECK);
|
|
|
|
}
|
2018-05-03 15:49:14 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-05-18 12:27:23 +00:00
|
|
|
// handle UNCONFIRMED, PROPRIETARY case here, RX slots were turned off due to
|
|
|
|
// valid packet reception, so we generate a TX_DONE event
|
2018-05-03 15:49:14 +00:00
|
|
|
_loramac.post_process_mcps_req();
|
|
|
|
_ctrl_flags |= TX_DONE_FLAG;
|
|
|
|
state_controller(DEVICE_STATE_STATUS_CHECK);
|
|
|
|
}
|
2018-03-12 11:30:00 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
// handle any pending MCPS indication
|
|
|
|
if (_loramac.get_mcps_indication()->pending) {
|
|
|
|
_loramac.post_process_mcps_ind();
|
|
|
|
_ctrl_flags |= MSG_RECVD_FLAG;
|
|
|
|
state_controller(DEVICE_STATE_STATUS_CHECK);
|
|
|
|
}
|
|
|
|
|
|
|
|
// change the state only if a TX cycle completes for Class A
|
|
|
|
// For class C it's not needed as it will already be in receiving
|
|
|
|
// state, no matter if the TX cycle completed or not.
|
|
|
|
if (!(_ctrl_flags & TX_ONGOING_FLAG)) {
|
|
|
|
// we are done here, update the state
|
|
|
|
state_machine_run_to_completion();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_loramac.get_mlme_indication()->pending) {
|
|
|
|
tr_debug("MLME Indication pending");
|
|
|
|
_loramac.post_process_mlme_ind();
|
|
|
|
tr_debug("Automatic uplink requested");
|
|
|
|
mlme_indication_handler();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ready_for_rx = true;
|
2017-12-15 10:30:40 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::process_reception_timeout(bool is_timeout)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
// when is_timeout == false, a CRC error took place in the received frame
|
|
|
|
// we treat that erroneous frame as no frame received at all, hence handle
|
|
|
|
// it exactly as we would handle timeout
|
|
|
|
rx_slot_t slot = _loramac.on_radio_rx_timeout(is_timeout);
|
|
|
|
|
|
|
|
if (slot == RX_SLOT_WIN_2 && !_loramac.nwk_joined()) {
|
|
|
|
state_controller(DEVICE_STATE_JOINING);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* LoRaWAN Specification 1.0.2. Section 3.3.6
|
|
|
|
* Main point:
|
|
|
|
* We indicate successful transmission
|
|
|
|
* of UNCONFIRMED message after RX windows are done with.
|
|
|
|
* For a CONFIRMED message, it means that we have not received
|
|
|
|
* ack (actually nothing was received), and we should retransmit if we can.
|
2018-05-15 12:22:15 +00:00
|
|
|
*
|
|
|
|
* NOTE: This code block doesn't get hit for Class C as in Class C, RX2 timeout
|
|
|
|
* never occurs.
|
2018-05-03 15:49:14 +00:00
|
|
|
*/
|
|
|
|
if (slot == RX_SLOT_WIN_2) {
|
|
|
|
_loramac.post_process_mcps_req();
|
|
|
|
|
2018-05-15 12:22:15 +00:00
|
|
|
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
|
|
|
if (_loramac.continue_sending_process()) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
tr_error("Retries exhausted for Class A device");
|
|
|
|
}
|
2018-05-03 15:49:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
state_controller(DEVICE_STATE_STATUS_CHECK);
|
|
|
|
state_machine_run_to_completion();
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* Private methods *
|
|
|
|
****************************************************************************/
|
2018-05-15 11:59:55 +00:00
|
|
|
void LoRaWANStack::make_tx_metadata_available(void)
|
|
|
|
{
|
|
|
|
_tx_metadata.stale = false;
|
|
|
|
_tx_metadata.channel = _loramac.get_mcps_confirmation()->channel;
|
|
|
|
_tx_metadata.data_rate = _loramac.get_mcps_confirmation()->data_rate;
|
|
|
|
_tx_metadata.tx_power = _loramac.get_mcps_confirmation()->tx_power;
|
|
|
|
_tx_metadata.tx_toa = _loramac.get_mcps_confirmation()->tx_toa;
|
|
|
|
_tx_metadata.nb_retries = _loramac.get_mcps_confirmation()->nb_retries;
|
|
|
|
}
|
|
|
|
|
2018-05-15 12:07:02 +00:00
|
|
|
void LoRaWANStack::make_rx_metadata_available(void)
|
|
|
|
{
|
|
|
|
_rx_metadata.stale = false;
|
|
|
|
_rx_metadata.rx_datarate = _loramac.get_mcps_indication()->rx_datarate;
|
|
|
|
_rx_metadata.rssi = _loramac.get_mcps_indication()->rssi;
|
|
|
|
_rx_metadata.snr = _loramac.get_mcps_indication()->snr;
|
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
bool LoRaWANStack::is_port_valid(const uint8_t port, bool allow_port_0)
|
2018-02-20 13:48:08 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
//Application should not use reserved and illegal port numbers.
|
|
|
|
if (port == 0) {
|
|
|
|
return allow_port_0;
|
|
|
|
} else {
|
|
|
|
return true;
|
2018-03-16 11:12:50 +00:00
|
|
|
}
|
2018-02-20 13:48:08 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::set_application_port(const uint8_t port, bool allow_port_0)
|
2017-11-27 13:17:18 +00:00
|
|
|
{
|
2018-05-03 15:49:14 +00:00
|
|
|
if (is_port_valid(port, allow_port_0)) {
|
|
|
|
_app_port = port;
|
|
|
|
return LORAWAN_STATUS_OK;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
return LORAWAN_STATUS_PORT_INVALID;
|
|
|
|
}
|
2018-03-12 11:30:00 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::state_machine_run_to_completion()
|
|
|
|
{
|
|
|
|
if (_loramac.get_device_class() == CLASS_C) {
|
|
|
|
_device_current_state = DEVICE_STATE_RECEIVING;
|
|
|
|
return;
|
|
|
|
}
|
2018-01-26 12:33:26 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
_device_current_state = DEVICE_STATE_IDLE;
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::send_event_to_application(const lorawan_event_t event) const
|
|
|
|
{
|
|
|
|
if (_callbacks.events) {
|
|
|
|
const int ret = _queue->call(_callbacks.events, event);
|
|
|
|
MBED_ASSERT(ret != 0);
|
|
|
|
(void)ret;
|
|
|
|
}
|
|
|
|
}
|
2018-03-15 08:43:31 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::send_automatic_uplink_message(const uint8_t port)
|
|
|
|
{
|
|
|
|
const int16_t ret = handle_tx(port, NULL, 0, MSG_CONFIRMED_FLAG, true, true);
|
|
|
|
if (ret < 0) {
|
|
|
|
send_event_to_application(AUTOMATIC_UPLINK_ERROR);
|
|
|
|
}
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
int LoRaWANStack::convert_to_msg_flag(const mcps_type_t type)
|
|
|
|
{
|
|
|
|
int msg_flag = MSG_UNCONFIRMED_FLAG;
|
|
|
|
switch (type) {
|
|
|
|
case MCPS_UNCONFIRMED:
|
|
|
|
msg_flag = MSG_UNCONFIRMED_FLAG;
|
|
|
|
break;
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
case MCPS_CONFIRMED:
|
|
|
|
msg_flag = MSG_CONFIRMED_FLAG;
|
2017-11-27 13:17:18 +00:00
|
|
|
break;
|
2018-05-03 15:49:14 +00:00
|
|
|
|
|
|
|
case MCPS_MULTICAST:
|
|
|
|
msg_flag = MSG_MULTICAST_FLAG;
|
2017-11-27 13:17:18 +00:00
|
|
|
break;
|
2018-05-03 15:49:14 +00:00
|
|
|
|
|
|
|
case MCPS_PROPRIETARY:
|
|
|
|
msg_flag = MSG_PROPRIETARY_FLAG;
|
2017-11-27 13:17:18 +00:00
|
|
|
break;
|
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
default:
|
|
|
|
tr_error("Unknown message type!");
|
|
|
|
MBED_ASSERT(0);
|
|
|
|
}
|
2018-03-16 11:12:50 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
return msg_flag;
|
|
|
|
}
|
2018-03-16 11:12:50 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
lorawan_status_t LoRaWANStack::handle_connect(bool is_otaa)
|
|
|
|
{
|
|
|
|
if (is_otaa) {
|
|
|
|
tr_debug("Initiating OTAA");
|
2018-03-16 11:12:50 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
// In 1.0.2 spec, counters are always set to zero for new connection.
|
|
|
|
// This section is common for both normal and
|
|
|
|
// connection restore at this moment. Will change in future with 1.1 support.
|
|
|
|
_lw_session.downlink_counter = 0;
|
|
|
|
_lw_session.uplink_counter = 0;
|
|
|
|
_ctrl_flags |= USING_OTAA_FLAG;
|
|
|
|
} else {
|
|
|
|
// If current state is SHUTDOWN, device may be trying to re-establish
|
|
|
|
// communication. In case of ABP specification is meddled about frame counters.
|
|
|
|
// It says to reset counters to zero but there is no mechanism to tell the
|
|
|
|
// network server that the device was disconnected or restarted.
|
|
|
|
// At the moment, this implementation does not support a non-volatile
|
|
|
|
// memory storage.
|
|
|
|
//_lw_session.downlink_counter; //Get from NVM
|
|
|
|
//_lw_session.uplink_counter; //Get from NVM
|
|
|
|
|
|
|
|
tr_debug("Initiating ABP");
|
|
|
|
tr_debug("Frame Counters. UpCnt=%lu, DownCnt=%lu",
|
|
|
|
_lw_session.uplink_counter, _lw_session.downlink_counter);
|
|
|
|
_ctrl_flags &= ~USING_OTAA_FLAG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return state_controller(DEVICE_STATE_CONNECTING);
|
|
|
|
}
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::mlme_indication_handler()
|
|
|
|
{
|
|
|
|
if (_loramac.get_mlme_indication()->indication_type == MLME_SCHEDULE_UPLINK) {
|
|
|
|
// The MAC signals that we shall provide an uplink as soon as possible
|
|
|
|
#if MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE
|
|
|
|
_automatic_uplink_ongoing = true;
|
|
|
|
tr_debug("mlme indication: sending empty uplink to port 0 to acknowledge MAC commands...");
|
|
|
|
send_automatic_uplink_message(0);
|
|
|
|
#else
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
send_event_to_application(UPLINK_REQUIRED);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2018-03-16 11:12:50 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
tr_error("Unknown MLME Indication type.");
|
|
|
|
}
|
2018-03-16 11:12:50 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::mlme_confirm_handler()
|
|
|
|
{
|
|
|
|
if (_loramac.get_mlme_confirmation()->req_type == MLME_LINK_CHECK) {
|
|
|
|
if (_loramac.get_mlme_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) {
|
|
|
|
#if defined(LORAWAN_COMPLIANCE_TEST)
|
|
|
|
if (_compliance_test.running == true) {
|
|
|
|
_compliance_test.link_check = true;
|
|
|
|
_compliance_test.demod_margin = _loramac.get_mlme_confirmation()->demod_margin;
|
|
|
|
_compliance_test.nb_gateways = _loramac.get_mlme_confirmation()->nb_gateways;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (_callbacks.link_check_resp) {
|
|
|
|
const int ret = _queue->call(_callbacks.link_check_resp,
|
|
|
|
_loramac.get_mlme_confirmation()->demod_margin,
|
|
|
|
_loramac.get_mlme_confirmation()->nb_gateways);
|
|
|
|
MBED_ASSERT(ret != 0);
|
|
|
|
(void)ret;
|
2017-11-27 13:17:18 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-03 15:49:14 +00:00
|
|
|
}
|
|
|
|
} else if (_loramac.get_mlme_confirmation()->req_type == MLME_JOIN) {
|
|
|
|
if (_loramac.get_mlme_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) {
|
|
|
|
state_controller(DEVICE_STATE_CONNECTED);
|
|
|
|
} else {
|
|
|
|
tr_error("Joining error: %d", _loramac.get_mlme_confirmation()->status);
|
|
|
|
_device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT;
|
|
|
|
state_controller(DEVICE_STATE_JOINING);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::mcps_confirm_handler()
|
|
|
|
{
|
|
|
|
// success case
|
|
|
|
if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) {
|
|
|
|
_lw_session.uplink_counter = _loramac.get_mcps_confirmation()->ul_frame_counter;
|
|
|
|
send_event_to_application(TX_DONE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// failure case
|
|
|
|
tr_error("mcps_confirmation: Error code = %d", _loramac.get_mcps_confirmation()->status);
|
|
|
|
|
|
|
|
if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) {
|
|
|
|
send_event_to_application(TX_TIMEOUT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if no ack was received, send TX_ERROR
|
|
|
|
send_event_to_application(TX_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::mcps_indication_handler()
|
|
|
|
{
|
|
|
|
const loramac_mcps_indication_t *mcps_indication = _loramac.get_mcps_indication();
|
|
|
|
if (mcps_indication->status != LORAMAC_EVENT_INFO_STATUS_OK) {
|
|
|
|
tr_error("RX_ERROR: mcps_indication status = %d", mcps_indication->status);
|
|
|
|
send_event_to_application(RX_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_lw_session.downlink_counter = mcps_indication->dl_frame_counter;
|
2018-03-16 11:12:50 +00:00
|
|
|
|
2018-01-05 10:13:48 +00:00
|
|
|
#if defined(LORAWAN_COMPLIANCE_TEST)
|
2018-05-03 15:49:14 +00:00
|
|
|
if (_compliance_test.running == true) {
|
|
|
|
_compliance_test.downlink_counter++;
|
|
|
|
}
|
|
|
|
#endif
|
2017-11-27 13:17:18 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
if (mcps_indication->port == 224) {
|
|
|
|
#if defined(LORAWAN_COMPLIANCE_TEST)
|
|
|
|
tr_debug("Compliance test command received.");
|
|
|
|
compliance_test_handler(mcps_indication);
|
|
|
|
#else
|
|
|
|
tr_info("Compliance test disabled.");
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
if (mcps_indication->is_data_recvd) {
|
|
|
|
// Valid message arrived.
|
|
|
|
_rx_msg.type = LORAMAC_RX_MCPS_INDICATION;
|
|
|
|
_rx_msg.msg.mcps_indication.buffer_size = mcps_indication->buffer_size;
|
|
|
|
_rx_msg.msg.mcps_indication.port = mcps_indication->port;
|
|
|
|
_rx_msg.msg.mcps_indication.buffer = mcps_indication->buffer;
|
|
|
|
_rx_msg.msg.mcps_indication.type = mcps_indication->type;
|
|
|
|
|
|
|
|
// Notify application about received frame..
|
|
|
|
tr_debug("Packet Received %d bytes",
|
|
|
|
_rx_msg.msg.mcps_indication.buffer_size);
|
|
|
|
_rx_msg.receive_ready = true;
|
|
|
|
send_event_to_application(RX_DONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If fPending bit is set we try to generate an empty packet
|
|
|
|
* with CONFIRMED flag set. We always set a CONFIRMED flag so
|
|
|
|
* that we could retry a certain number of times if the uplink
|
|
|
|
* failed for some reason
|
|
|
|
* or
|
|
|
|
* Class C and node received a confirmed message so we need to
|
|
|
|
* send an empty packet to acknowledge the message.
|
|
|
|
* This scenario is unspecified by LoRaWAN 1.0.2 specification,
|
|
|
|
* but version 1.1.0 says that network SHALL not send any new
|
|
|
|
* confirmed messages until ack has been sent
|
|
|
|
*/
|
|
|
|
if ((_loramac.get_device_class() != CLASS_C && mcps_indication->fpending_status)
|
|
|
|
||
|
|
|
|
(_loramac.get_device_class() == CLASS_C && mcps_indication->type == MCPS_CONFIRMED)) {
|
|
|
|
#if (MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE)
|
|
|
|
tr_debug("Sending empty uplink message...");
|
|
|
|
_automatic_uplink_ongoing = true;
|
|
|
|
send_automatic_uplink_message(mcps_indication->port);
|
|
|
|
#else
|
|
|
|
send_event_to_application(UPLINK_REQUIRED);
|
2018-01-05 10:13:48 +00:00
|
|
|
#endif
|
2018-05-03 15:49:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lorawan_status_t LoRaWANStack::state_controller(device_states_t new_state)
|
|
|
|
{
|
|
|
|
lorawan_status_t status = LORAWAN_STATUS_OK;
|
|
|
|
|
|
|
|
switch (new_state) {
|
|
|
|
case DEVICE_STATE_IDLE:
|
|
|
|
process_idle_state(status);
|
|
|
|
break;
|
|
|
|
case DEVICE_STATE_CONNECTING:
|
|
|
|
process_connecting_state(status);
|
|
|
|
break;
|
|
|
|
case DEVICE_STATE_JOINING:
|
|
|
|
process_joining_state(status);
|
|
|
|
break;
|
|
|
|
case DEVICE_STATE_CONNECTED:
|
|
|
|
process_connected_state();
|
|
|
|
break;
|
|
|
|
case DEVICE_STATE_SCHEDULING:
|
|
|
|
process_scheduling_state(status);
|
|
|
|
break;
|
|
|
|
case DEVICE_STATE_STATUS_CHECK:
|
|
|
|
process_status_check_state();
|
|
|
|
break;
|
|
|
|
case DEVICE_STATE_SHUTDOWN:
|
|
|
|
process_shutdown_state(status);
|
|
|
|
break;
|
2017-11-27 13:17:18 +00:00
|
|
|
default:
|
2018-05-03 15:49:14 +00:00
|
|
|
tr_debug("state_controller: Unknown state!");
|
2018-01-12 14:39:31 +00:00
|
|
|
status = LORAWAN_STATUS_SERVICE_UNKNOWN;
|
2017-11-27 13:17:18 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
2018-03-05 13:16:08 +00:00
|
|
|
|
2018-05-03 15:49:14 +00:00
|
|
|
void LoRaWANStack::process_shutdown_state(lorawan_status_t& op_status)
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Remove channels
|
|
|
|
* Radio will be put to sleep by the APIs underneath
|
|
|
|
*/
|
|
|
|
drop_channel_list();
|
|
|
|
_loramac.disconnect();
|
|
|
|
_lw_session.active = false;
|
|
|
|
_device_current_state = DEVICE_STATE_SHUTDOWN;
|
|
|
|
op_status = LORAWAN_STATUS_DEVICE_OFF;
|
|
|
|
_ctrl_flags &= ~CONNECTED_FLAG;
|
|
|
|
send_event_to_application(DISCONNECTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_status_check_state()
|
|
|
|
{
|
|
|
|
if (_device_current_state == DEVICE_STATE_SENDING ||
|
|
|
|
_device_current_state == DEVICE_STATE_AWAITING_ACK) {
|
|
|
|
// this happens after RX2 slot is exhausted
|
|
|
|
// we may or may not have a successful UNCONFIRMED transmission
|
|
|
|
// here. In CONFIRMED case this block is invoked only
|
|
|
|
// when the MAX number of retries are exhausted, i.e., only error
|
2018-05-15 12:22:15 +00:00
|
|
|
// case will fall here. Moreover, it will happen for Class A only.
|
2018-05-03 15:49:14 +00:00
|
|
|
_ctrl_flags &= ~TX_DONE_FLAG;
|
|
|
|
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
|
|
|
_loramac.set_tx_ongoing(false);
|
|
|
|
_loramac.reset_ongoing_tx();
|
|
|
|
mcps_confirm_handler();
|
|
|
|
|
|
|
|
} else if (_device_current_state == DEVICE_STATE_RECEIVING) {
|
|
|
|
|
2018-05-15 12:22:15 +00:00
|
|
|
if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & TX_ONGOING_FLAG)) {
|
2018-05-03 15:49:14 +00:00
|
|
|
// for CONFIRMED case, ack validity is already checked
|
2018-05-15 12:22:15 +00:00
|
|
|
// If it was a successful transmission, TX_ONGOING_FLAG will not be set.
|
|
|
|
// If it was indeed set, that means the device was in Class C mode and
|
|
|
|
// CONFIRMED transmission was in place and the ack retries maxed out.
|
2018-05-03 15:49:14 +00:00
|
|
|
_ctrl_flags &= ~TX_DONE_FLAG;
|
|
|
|
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
|
|
|
_loramac.set_tx_ongoing(false);
|
|
|
|
_loramac.reset_ongoing_tx();
|
|
|
|
// if an automatic uplink is ongoing, we should not send a TX_DONE
|
|
|
|
// event to application
|
|
|
|
if (_automatic_uplink_ongoing) {
|
|
|
|
_automatic_uplink_ongoing = false;
|
|
|
|
} else {
|
|
|
|
mcps_confirm_handler();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle any received data and send event accordingly
|
|
|
|
if (_ctrl_flags & MSG_RECVD_FLAG) {
|
|
|
|
_ctrl_flags &= ~MSG_RECVD_FLAG;
|
|
|
|
mcps_indication_handler();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_scheduling_state(lorawan_status_t& op_status)
|
|
|
|
{
|
|
|
|
if (_device_current_state != DEVICE_STATE_IDLE) {
|
|
|
|
if (_device_current_state != DEVICE_STATE_RECEIVING
|
|
|
|
&& _loramac.get_device_class() != CLASS_C) {
|
|
|
|
op_status = LORAWAN_STATUS_BUSY;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
op_status = _loramac.send_ongoing_tx();
|
|
|
|
if (op_status == LORAWAN_STATUS_OK) {
|
|
|
|
_ctrl_flags |= TX_ONGOING_FLAG;
|
|
|
|
_ctrl_flags &= ~TX_DONE_FLAG;
|
|
|
|
_loramac.set_tx_ongoing(true);
|
|
|
|
_device_current_state = DEVICE_STATE_SENDING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_joining_state(lorawan_status_t& op_status)
|
|
|
|
{
|
|
|
|
if (_device_current_state == DEVICE_STATE_CONNECTING) {
|
|
|
|
_device_current_state = DEVICE_STATE_JOINING;
|
|
|
|
tr_debug("Sending Join Request ...");
|
|
|
|
op_status = _loramac.join(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_device_current_state == DEVICE_STATE_AWAITING_JOIN_ACCEPT) {
|
|
|
|
_device_current_state = DEVICE_STATE_JOINING;
|
|
|
|
// retry join
|
|
|
|
bool can_continue = _loramac.continue_joining_process();
|
|
|
|
|
|
|
|
if (!can_continue) {
|
|
|
|
send_event_to_application(JOIN_FAILURE);
|
|
|
|
_device_current_state = DEVICE_STATE_IDLE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_connected_state()
|
|
|
|
{
|
|
|
|
if (_ctrl_flags & USING_OTAA_FLAG) {
|
|
|
|
tr_debug("OTAA Connection OK!");
|
|
|
|
}
|
|
|
|
|
|
|
|
_lw_session.active = true;
|
|
|
|
send_event_to_application(CONNECTED);
|
|
|
|
_ctrl_flags |= CONNECTED_FLAG;
|
|
|
|
|
|
|
|
_device_current_state = DEVICE_STATE_IDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_connecting_state(lorawan_status_t& op_status)
|
|
|
|
{
|
|
|
|
if (_device_current_state != DEVICE_STATE_IDLE
|
|
|
|
&&
|
|
|
|
_device_current_state != DEVICE_STATE_SHUTDOWN) {
|
|
|
|
op_status = LORAWAN_STATUS_BUSY;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_ctrl_flags & CONNECTED_FLAG) {
|
|
|
|
tr_debug("Already connected");
|
|
|
|
op_status = LORAWAN_STATUS_OK;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_device_current_state = DEVICE_STATE_CONNECTING;
|
|
|
|
|
|
|
|
if (_ctrl_flags & USING_OTAA_FLAG) {
|
|
|
|
process_joining_state(op_status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
op_status = _loramac.join(false);
|
|
|
|
tr_debug("ABP connection OK.");
|
|
|
|
process_connected_state();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_idle_state(lorawan_status_t& op_status)
|
|
|
|
{
|
|
|
|
if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) {
|
|
|
|
_device_current_state = DEVICE_STATE_IDLE;
|
|
|
|
process_uninitialized_state(op_status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_device_current_state = DEVICE_STATE_IDLE;
|
|
|
|
op_status = LORAWAN_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoRaWANStack::process_uninitialized_state(lorawan_status_t& op_status)
|
|
|
|
{
|
|
|
|
op_status = _loramac.initialize(_queue);
|
|
|
|
|
|
|
|
if (op_status == LORAWAN_STATUS_OK) {
|
|
|
|
_device_current_state = DEVICE_STATE_IDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 13:16:08 +00:00
|
|
|
#if defined(LORAWAN_COMPLIANCE_TEST)
|
2018-03-15 08:43:31 +00:00
|
|
|
|
|
|
|
lorawan_status_t LoRaWANStack::send_compliance_test_frame_to_mac()
|
2018-03-05 13:16:08 +00:00
|
|
|
{
|
2018-03-21 12:39:18 +00:00
|
|
|
loramac_compliance_test_req_t test_req;
|
2018-03-15 08:43:31 +00:00
|
|
|
|
|
|
|
//TODO: What if the port is not 224 ???
|
|
|
|
if (_compliance_test.app_port == 224) {
|
2018-03-05 13:16:08 +00:00
|
|
|
// Clear any normal message stuff before compliance test.
|
2018-03-21 12:39:18 +00:00
|
|
|
memset(&test_req, 0, sizeof(test_req));
|
2018-03-05 13:16:08 +00:00
|
|
|
|
|
|
|
if (_compliance_test.link_check == true) {
|
|
|
|
_compliance_test.link_check = false;
|
|
|
|
_compliance_test.state = 1;
|
2018-03-21 12:39:18 +00:00
|
|
|
test_req.f_buffer_size = 3;
|
|
|
|
test_req.f_buffer[0] = 5;
|
|
|
|
test_req.f_buffer[1] = _compliance_test.demod_margin;
|
|
|
|
test_req.f_buffer[2] = _compliance_test.nb_gateways;
|
2018-03-05 13:16:08 +00:00
|
|
|
} else {
|
|
|
|
switch (_compliance_test.state) {
|
|
|
|
case 4:
|
|
|
|
_compliance_test.state = 1;
|
2018-03-21 12:39:18 +00:00
|
|
|
test_req.f_buffer_size = _compliance_test.app_data_size;
|
|
|
|
test_req.f_buffer[0] = _compliance_test.app_data_buffer[0];
|
2018-03-05 13:16:08 +00:00
|
|
|
for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, MBED_CONF_LORA_TX_MAX_SIZE); ++i) {
|
2018-03-21 12:39:18 +00:00
|
|
|
test_req.f_buffer[i] = _compliance_test.app_data_buffer[i];
|
2018-03-05 13:16:08 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
2018-03-21 12:39:18 +00:00
|
|
|
test_req.f_buffer_size = 2;
|
|
|
|
test_req.f_buffer[0] = _compliance_test.downlink_counter >> 8;
|
|
|
|
test_req.f_buffer[1] = _compliance_test.downlink_counter;
|
2018-03-05 13:16:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-15 08:43:31 +00:00
|
|
|
//TODO: If port is not 224, this might not work!
|
|
|
|
//Is there a test case where same _tx_msg's buffer would be used, when port is not 224???
|
2018-03-05 13:16:08 +00:00
|
|
|
if (!_compliance_test.is_tx_confirmed) {
|
2018-03-21 12:39:18 +00:00
|
|
|
test_req.type = MCPS_UNCONFIRMED;
|
|
|
|
test_req.fport = _compliance_test.app_port;
|
|
|
|
test_req.nb_trials = 1;
|
|
|
|
test_req.data_rate = _loramac.get_default_tx_datarate();
|
2018-03-05 13:16:08 +00:00
|
|
|
|
2018-03-21 12:39:18 +00:00
|
|
|
tr_info("Transmit unconfirmed compliance test frame %d bytes.", test_req.f_buffer_size);
|
2018-03-05 13:16:08 +00:00
|
|
|
|
2018-03-21 12:39:18 +00:00
|
|
|
for (uint8_t i = 0; i < test_req.f_buffer_size; ++i) {
|
|
|
|
tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)test_req.f_buffer)[i]);
|
2018-03-05 13:16:08 +00:00
|
|
|
}
|
|
|
|
} else if (_compliance_test.is_tx_confirmed) {
|
2018-03-21 12:39:18 +00:00
|
|
|
test_req.type = MCPS_CONFIRMED;
|
|
|
|
test_req.fport = _compliance_test.app_port;
|
|
|
|
test_req.nb_trials = _num_retry;
|
|
|
|
test_req.data_rate = _loramac.get_default_tx_datarate();
|
2018-03-05 13:16:08 +00:00
|
|
|
|
2018-03-21 12:39:18 +00:00
|
|
|
tr_info("Transmit confirmed compliance test frame %d bytes.", test_req.f_buffer_size);
|
2018-03-05 13:16:08 +00:00
|
|
|
|
2018-03-21 12:39:18 +00:00
|
|
|
for (uint8_t i = 0; i < test_req.f_buffer_size; ++i) {
|
|
|
|
tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)test_req.f_buffer)[i]);
|
2018-03-05 13:16:08 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return LORAWAN_STATUS_SERVICE_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
2018-03-21 12:39:18 +00:00
|
|
|
return _loramac.test_request(&test_req);
|
2018-03-16 11:12:50 +00:00
|
|
|
}
|
|
|
|
|
2018-03-15 08:43:31 +00:00
|
|
|
void LoRaWANStack::compliance_test_handler(loramac_mcps_indication_t *mcps_indication)
|
|
|
|
{
|
|
|
|
if (_compliance_test.running == false) {
|
|
|
|
// Check compliance test enable command (i)
|
|
|
|
if ((mcps_indication->buffer_size == 4) &&
|
|
|
|
(mcps_indication->buffer[0] == 0x01) &&
|
|
|
|
(mcps_indication->buffer[1] == 0x01) &&
|
|
|
|
(mcps_indication->buffer[2] == 0x01) &&
|
|
|
|
(mcps_indication->buffer[3] == 0x01)) {
|
|
|
|
_compliance_test.is_tx_confirmed = false;
|
|
|
|
_compliance_test.app_port = 224;
|
|
|
|
_compliance_test.app_data_size = 2;
|
|
|
|
_compliance_test.downlink_counter = 0;
|
|
|
|
_compliance_test.link_check = false;
|
|
|
|
_compliance_test.demod_margin = 0;
|
|
|
|
_compliance_test.nb_gateways = 0;
|
|
|
|
_compliance_test.running = true;
|
|
|
|
_compliance_test.state = 1;
|
|
|
|
|
2018-03-16 11:12:50 +00:00
|
|
|
_loramac.enable_adaptive_datarate(true);
|
2018-03-15 08:43:31 +00:00
|
|
|
|
|
|
|
#if MBED_CONF_LORA_PHY == 0
|
|
|
|
_loramac.LoRaMacTestSetDutyCycleOn(false);
|
|
|
|
#endif
|
|
|
|
//5000ms
|
|
|
|
_loramac.LoRaMacSetTxTimer(5000);
|
|
|
|
|
|
|
|
//TODO: Should we call lora_state_machine here instead of just setting the state?
|
|
|
|
_device_current_state = DEVICE_STATE_COMPLIANCE_TEST;
|
|
|
|
// lora_state_machine(DEVICE_STATE_COMPLIANCE_TEST);
|
|
|
|
tr_debug("Compliance test activated.");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_compliance_test.state = mcps_indication->buffer[0];
|
|
|
|
switch (_compliance_test.state) {
|
|
|
|
case 0: // Check compliance test disable command (ii)
|
|
|
|
_compliance_test.is_tx_confirmed = true;
|
|
|
|
_compliance_test.app_port = MBED_CONF_LORA_APP_PORT;
|
|
|
|
_compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE;
|
|
|
|
_compliance_test.downlink_counter = 0;
|
|
|
|
_compliance_test.running = false;
|
|
|
|
|
2018-03-16 11:12:50 +00:00
|
|
|
_loramac.enable_adaptive_datarate(MBED_CONF_LORA_ADR_ON);
|
|
|
|
|
2018-03-15 08:43:31 +00:00
|
|
|
#if MBED_CONF_LORA_PHY == 0
|
|
|
|
_loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON);
|
|
|
|
#endif
|
|
|
|
// Go to idle state after compliance test mode.
|
|
|
|
tr_debug("Compliance test disabled.");
|
|
|
|
_loramac.LoRaMacStopTxTimer();
|
|
|
|
|
|
|
|
// Clear any compliance test message stuff before going back to normal operation.
|
|
|
|
_loramac.reset_ongoing_tx();
|
|
|
|
lora_state_machine(DEVICE_STATE_IDLE);
|
|
|
|
break;
|
|
|
|
case 1: // (iii, iv)
|
|
|
|
_compliance_test.app_data_size = 2;
|
|
|
|
break;
|
|
|
|
case 2: // Enable confirmed messages (v)
|
|
|
|
_compliance_test.is_tx_confirmed = true;
|
|
|
|
_compliance_test.state = 1;
|
|
|
|
break;
|
|
|
|
case 3: // Disable confirmed messages (vi)
|
|
|
|
_compliance_test.is_tx_confirmed = false;
|
|
|
|
_compliance_test.state = 1;
|
|
|
|
break;
|
|
|
|
case 4: // (vii)
|
|
|
|
_compliance_test.app_data_size = mcps_indication->buffer_size;
|
|
|
|
|
|
|
|
_compliance_test.app_data_buffer[0] = 4;
|
|
|
|
for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, LORAMAC_PHY_MAXPAYLOAD); ++i) {
|
|
|
|
_compliance_test.app_data_buffer[i] = mcps_indication->buffer[i] + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
send_compliance_test_frame_to_mac();
|
|
|
|
break;
|
|
|
|
case 5: // (viii)
|
2018-03-16 11:12:50 +00:00
|
|
|
_loramac.setup_link_check_request();
|
2018-03-15 08:43:31 +00:00
|
|
|
break;
|
|
|
|
case 6: // (ix)
|
|
|
|
// Disable TestMode and revert back to normal operation
|
|
|
|
_compliance_test.is_tx_confirmed = true;
|
|
|
|
_compliance_test.app_port = MBED_CONF_LORA_APP_PORT;
|
|
|
|
_compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE;
|
|
|
|
_compliance_test.downlink_counter = 0;
|
|
|
|
_compliance_test.running = false;
|
|
|
|
|
2018-03-16 11:12:50 +00:00
|
|
|
_loramac.enable_adaptive_datarate(MBED_CONF_LORA_ADR_ON);
|
|
|
|
|
2018-03-15 08:43:31 +00:00
|
|
|
#if MBED_CONF_LORA_PHY == 0
|
|
|
|
_loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON);
|
|
|
|
#endif
|
2018-04-09 08:08:29 +00:00
|
|
|
_loramac.join(true);
|
2018-03-15 08:43:31 +00:00
|
|
|
break;
|
|
|
|
case 7: // (x)
|
|
|
|
if (mcps_indication->buffer_size == 3) {
|
|
|
|
loramac_mlme_req_t mlme_req;
|
|
|
|
mlme_req.type = MLME_TXCW;
|
2018-03-16 11:12:50 +00:00
|
|
|
mlme_req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]);
|
2018-03-15 08:43:31 +00:00
|
|
|
_loramac.mlme_request(&mlme_req);
|
|
|
|
} else if (mcps_indication->buffer_size == 7) {
|
|
|
|
loramac_mlme_req_t mlme_req;
|
|
|
|
mlme_req.type = MLME_TXCW_1;
|
2018-03-16 11:12:50 +00:00
|
|
|
mlme_req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]);
|
|
|
|
mlme_req.cw_tx_mode.frequency = (uint32_t)((mcps_indication->buffer[3] << 16) | (mcps_indication->buffer[4] << 8)
|
2018-03-15 08:43:31 +00:00
|
|
|
| mcps_indication->buffer[5]) * 100;
|
2018-03-16 11:12:50 +00:00
|
|
|
mlme_req.cw_tx_mode.power = mcps_indication->buffer[6];
|
2018-03-15 08:43:31 +00:00
|
|
|
_loramac.mlme_request(&mlme_req);
|
|
|
|
}
|
|
|
|
_compliance_test.state = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-05 13:16:08 +00:00
|
|
|
#endif
|
|
|
|
|