Reworking callback API

Application should be able to add some optional callbacks if it needs to.
Ofcourse there is a penalty of 8-12 bytes per callback, but there can be
certain needs of the application that needs to be met for example setting
up a link check request etc.

We have introduced a structure that contains callbacks for the application use.

 - 'events' callback is mandatory, user must assign it. Because this callback brings
   state changes for the application. We cannot segregate this into individual handlers
   because of RAM penalty.
- Other calbacks (none of them are implemented yet are optional).

Example of using the API is provided with doxygen
pull/6087/head
Hasnain Virk 2017-12-21 13:37:38 +02:00 committed by Jimmy Brisson
parent e60227cf4d
commit 05e2d29238
8 changed files with 215 additions and 44 deletions

View File

@ -177,7 +177,15 @@ int16_t LoRaWANInterface::receive(uint8_t port, uint8_t* data, uint16_t length,
}
}
void LoRaWANInterface::lora_event_callback(mbed::Callback<void(lora_events_t)> event_cb)
{
stk_obj().set_lora_event_cb(event_cb);
}
lora_mac_status_t LoRaWANInterface::add_app_callbacks(lorawan_app_callbacks_t *callbacks)
{
if (!callbacks || !callbacks->events) {
// Event Callback is mandatory
return LORA_MAC_STATUS_PARAMETER_INVALID;
}
stk_obj().set_lora_callbacks(callbacks);
return LORA_MAC_STATUS_OK;
}

View File

@ -318,24 +318,75 @@ public:
virtual int16_t receive(uint8_t port, uint8_t* data, uint16_t length,
int flags);
/** Callback handler.
*
* Events that can be posted to user:
*
* CONNECTED - When the connection is complete
* DISCONNECTED - When the protocol is shut down in response to disconnect()
* TX_DONE - When a packet is sent
* TX_TIMEOUT, - When stack was unable to send packet in TX window
* TX_ERROR, - A general TX error
* TX_CRYPTO_ERROR, - If MIC fails, or any other crypto relted error
* TX_SCHEDULING_ERROR, - When stack is unable to schedule packet
* RX_DONE, - When there is something to receive
* RX_TIMEOUT, - Not yet mapped
* RX_ERROR - A general RX error
*
* @param event_cb A callback function for catching events from the stack.
*/
virtual void lora_event_callback(mbed::Callback<void(lora_events_t)> event_cb);
/** Add application callbacks to the stack.
*
* 'lorawan_app_callbacks' is a structure that holds pointers to the application
* provided methods which are needed to be called in response to certain
* requests. The structure is default constructed to set all pointers to NULL.
* So if the user does not provide the pointer, a response will not be posted.
* However, the 'lorawan_events' callback is mandatory to be provided as it
* contains essential events.
*
* Events that can be posted to user via 'lorawan_events' are:
*
* CONNECTED - When the connection is complete
* DISCONNECTED - When the protocol is shut down in response to disconnect()
* TX_DONE - When a packet is sent
* TX_TIMEOUT, - When stack was unable to send packet in TX window
* TX_ERROR, - A general TX error
* TX_CRYPTO_ERROR, - If MIC fails, or any other crypto relted error
* TX_SCHEDULING_ERROR, - When stack is unable to schedule packet
* RX_DONE, - When there is something to receive
* RX_TIMEOUT, - Not yet mapped
* RX_ERROR - A general RX error
*
* Other responses to certain standard requests are an item for the future.
* For example, a link check request could be sent whenever the device tries
* to send a message and if the network server responds with a link check resposne,
* the stack notifies the application be calling the appropriate method. For example,
* 'link_check_resp' callback could be used to collect a response for a link check
* request MAC command and the result is thus transported to the application
* via callback function provided.
*
* As can be seen from declaration, mbed::Callback<void(uint8_t, uint8_t)> *link_check_resp)
* carries two parameters. First one is Demodulation Margin and the second one
* is number of gateways involved in the path to network server.
*
* An example of using this API with a latch onto 'lorawan_events' could be:
*
* LoRaWANInterface lorawan(radio);
* lorawan_app_callbacks cbs;
* static void my_event_handler();
*
* int main()
* {
* lorawan.initialize(&queue);
* cbs.lorawan_events = mbed::callback(my_event_handler);
* lorawan.add_app_callbacks(&cbs);
* lorawan.connect();
* }
*
* static void my_event_handler(lora_events_t events)
* {
* switch(events) {
* case CONNECTED:
* //do something
* break;
* case DISCONNECTED:
* //do something
* break;
* case TX_DONE:
* //do something
* break;
* default:
* break;
* }
* }
*
* @param callbacks A pointer to the structure containing application
* callbacks.
*/
virtual lora_mac_status_t add_app_callbacks(lorawan_app_callbacks_t *callbacks);
};
#endif /* LORAWANINTERFACE_H_ */

View File

@ -483,9 +483,18 @@ void LoRaWANStack::mlme_confirm(MlmeConfirm_t *mlme_confirm)
mlme_confirm_handler(&lora_mlme_confirm);
}
void LoRaWANStack::set_lora_event_cb(mbed::Callback<void(lora_events_t)> event_cb)
void LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs)
{
_events = event_cb;
if (cbs) {
if (cbs->events) {
_callbacks.events = cbs->events;
}
if (cbs->link_check_resp) {
_callbacks.link_check_resp = cbs->link_check_resp;
}
}
}
lora_mac_status_t LoRaWANStack::add_channels(const lora_channelplan_t &channel_plan)
@ -1051,7 +1060,10 @@ void LoRaWANStack::mlme_confirm_handler(lora_mac_mlme_confirm_t *mlme_confirm)
// Join attempt failed.
set_device_state(DEVICE_STATE_IDLE);
lora_state_machine();
_queue->call(_events, JOIN_FAILURE);
if (_callbacks.events) {
_queue->call(_callbacks.events, JOIN_FAILURE);
}
}
break;
case LORA_MLME_LINK_CHECK:
@ -1132,14 +1144,18 @@ void LoRaWANStack::mcps_confirm_handler(lora_mac_mcps_confirm_t *mcps_confirm)
// If sending timed out, we have a special event for that
if (mcps_confirm->status == LORA_EVENT_INFO_STATUS_TX_TIMEOUT) {
_queue->call(_events, TX_TIMEOUT);
if (_callbacks.events) {
_queue->call(_callbacks.events, TX_TIMEOUT);
}
return;
} if (mcps_confirm->status == LORA_EVENT_INFO_STATUS_RX2_TIMEOUT) {
tr_debug("Did not receive Ack");
}
// Otherwise send a general TX_ERROR event
_queue->call(_events, TX_ERROR);
if (_callbacks.events) {
_queue->call(_callbacks.events, TX_ERROR);
}
return;
}
@ -1159,7 +1175,9 @@ void LoRaWANStack::mcps_confirm_handler(lora_mac_mcps_confirm_t *mcps_confirm)
// data rate plus frame counter.
_lw_session.uplink_counter = mcps_confirm->uplink_counter;
_tx_msg.tx_ongoing = false;
_queue->call(_events, TX_DONE);
if (_callbacks.events) {
_queue->call(_callbacks.events, TX_DONE);
}
}
/** MCPS-Indication event function
@ -1175,7 +1193,9 @@ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indi
}
if (mcps_indication->status != LORA_EVENT_INFO_STATUS_OK) {
_queue->call(_events, RX_ERROR);
if (_callbacks.events) {
_queue->call(_callbacks.events, RX_ERROR);
}
return;
}
@ -1224,7 +1244,9 @@ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indi
// This may never happen as both radio and MAC are limited
// to the size 255 bytes
tr_debug("Cannot receive more than buffer capacity!");
_queue->call(_events, RX_ERROR);
if (_callbacks.events) {
_queue->call(_callbacks.events, RX_ERROR);
}
return;
} else {
_rx_msg.type = LORAMAC_RX_MCPS_INDICATION;
@ -1238,7 +1260,9 @@ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indi
// Notify application about received frame..
tr_debug("Received %d bytes", _rx_msg.rx_message.mcps_indication.buffer_size);
_rx_msg.receive_ready = true;
_queue->call(_events, RX_DONE);
if (_callbacks.events) {
_queue->call(_callbacks.events, RX_DONE);
}
} else {
// Invalid port, ports 0, 224 and 225-255 are reserved.
}
@ -1805,7 +1829,9 @@ lora_mac_status_t LoRaWANStack::lora_state_machine()
_lw_session.active = false;
tr_debug("LoRaWAN protocol has been shut down.");
_queue->call(_events, DISCONNECTED);
if (_callbacks.events) {
_queue->call(_callbacks.events, DISCONNECTED);
}
status = LORA_MAC_STATUS_DEVICE_OFF;
break;
case DEVICE_STATE_NOT_INITIALIZED:
@ -1847,7 +1873,9 @@ lora_mac_status_t LoRaWANStack::lora_state_machine()
// Session is now active
_lw_session.active = true;
// Tell the application that we are connected
_queue->call(_events, CONNECTED);
if (_callbacks.events) {
_queue->call(_callbacks.events, CONNECTED);
}
break;
case DEVICE_STATE_ABP_CONNECTING:
/*
@ -1879,7 +1907,9 @@ lora_mac_status_t LoRaWANStack::lora_state_machine()
status = LORA_MAC_STATUS_OK;
// Session is now active
_lw_session.active = true;
_queue->call(_events, CONNECTED);
if (_callbacks.events) {
_queue->call(_callbacks.events, CONNECTED);
}
break;
case DEVICE_STATE_SEND:
// If a transmission is ongoing, don't interrupt
@ -1895,11 +1925,15 @@ lora_mac_status_t LoRaWANStack::lora_state_machine()
break;
case LORA_MAC_STATUS_CRYPTO_FAIL:
tr_error("Crypto failed. Clearing TX buffers");
_queue->call(_events, TX_CRYPTO_ERROR);
if (_callbacks.events) {
_queue->call(_callbacks.events, TX_CRYPTO_ERROR);
}
break;
default:
tr_error("Failure to schedule TX!");
_queue->call(_events, TX_SCHEDULING_ERROR);
if (_callbacks.events) {
_queue->call(_callbacks.events, TX_SCHEDULING_ERROR);
}
break;
}
}

View File

@ -65,11 +65,11 @@ public:
*/
lora_mac_status_t initialize_mac_layer(events::EventQueue *queue);
/** Sets all callbacks of the LoRaWAN interface.
/** Sets all callbacks for the application.
*
* @param *event_cb An event structure representing all possible callbacks.
* \param callbacks A pointer to the structure carrying callbacks.
*/
void set_lora_event_cb(mbed::Callback<void(lora_events_t)> event_cb);
void set_lora_callbacks(lorawan_app_callbacks_t *callbacks);
/** Adds channels to use.
*
@ -412,7 +412,7 @@ private:
compliance_test_t _compliance_test;
device_states_t _device_current_state;
mbed::Callback<void(lora_events_t)> _events;
lorawan_app_callbacks_t _callbacks;
radio_events_t *_mac_handlers;
lorawan_session_t _lw_session;
lora_mac_tx_message_t _tx_msg;

View File

@ -2136,6 +2136,7 @@ static LoRaMacStatus_t ScheduleTx( void )
// Send later - prepare timer
LoRaMacState |= LORAMAC_TX_DELAYED;
tr_debug("Next Transmission in %lu ms", dutyCycleTimeOff);
TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff );
TimerStart( &TxDelayedTimer );

View File

@ -2884,6 +2884,18 @@ typedef enum lora_events {
JOIN_FAILURE,
} lora_events_t;
typedef struct {
// Mandatory. Event Callback must be provided
mbed::Callback<void(lora_events_t)> events;
// Rest are optional
// If the user do not assign these callbacks, these callbacks would return
// null if checked with bool operator
// link_check_resp callback and other such callbacks will be maped in
// future releases of Mbed-OS
mbed::Callback<void(uint8_t, uint8_t)> link_check_resp;
}lorawan_app_callbacks_t;
typedef struct lora_channelplan {
uint8_t nb_channels; // number of channels
lora_channels_t *channels;

View File

@ -18,6 +18,20 @@
#ifndef LORARADIO_H_
#define LORARADIO_H_
/**
* Structure to hold RF controls for LoRa Radio.
* SX1276 have an extra control for the crystal (used in DOSCO-L072CZ)
*/
typedef struct {
PinName rf_switch_ctl1;
PinName rf_switch_ctl2;
PinName txctl;
PinName rxctl;
PinName ant_switch;
PinName pwr_amp_ctl;
PinName tcxo;
} rf_ctrls;
/** Radio driver internal state.
* State machine states definition.
*/

View File

@ -230,9 +230,16 @@ public:
virtual int16_t receive(uint8_t port, uint8_t* data, uint16_t length,
int flags) = 0;
/** Callback handler.
/** Add application callbacks to the stack.
*
* Events that can be posted to user:
* 'lorawan_app_callbacks_t' is a structure that holds pointers to the application
* provided methods which are needed to be called in response to certain
* requests. The structure is default constructed to set all pointers to NULL.
* So if the user does not provide the pointer, a response will not be posted.
* However, the 'lorawan_events' callback is mandatory to be provided as it
* contains essential events.
*
* Events that can be posted to user via 'lorawan_events' are:
*
* CONNECTED - When the connection is complete
* DISCONNECTED - When the protocol is shut down in response to disconnect()
@ -245,9 +252,53 @@ public:
* RX_TIMEOUT, - Not yet mapped
* RX_ERROR - A general RX error
*
* @param cb A pointer to the callback function.
* Other responses to certain standard requests are an item for the future.
* For example, a link check request could be sent whenever the device tries
* to send a message and if the network server responds with a link check resposne,
* the stack notifies the application be calling the appropriate method. For example,
* 'link_check_resp' callback could be used to collect a response for a link check
* request MAC command and the result is thus transported to the application
* via callback function provided.
*
* As can be seen from declaration, mbed::Callback<void(uint8_t, uint8_t)> *link_check_resp)
* carries two parameters. First one is Demodulation Margin and the second one
* is number of gateways involved in the path to network server.
*
* An example of using this API with a latch onto 'lorawan_events' could be:
*
* LoRaWANInterface lorawan(radio);
* lorawan_app_callbacks_t cbs;
* static void my_event_handler();
*
* int main()
* {
* lorawan.initialize();
* cbs.lorawan_events = mbed::callback(my_event_handler);
* lorawan.add_app_callbacks(&cbs);
* lorawan.connect();
* }
*
* static void my_event_handler(lora_events_t events)
* {
* switch(events) {
* case CONNECTED:
* //do something
* break;
* case DISCONNECTED:
* //do something
* break;
* case TX_DONE:
* //do something
* break;
* default:
* break;
* }
* }
*
* @param callbacks A pointer to the structure containing application
* callbacks.
*/
virtual void lora_event_callback(mbed::Callback<void(lora_events_t)> cb) = 0;
virtual lora_mac_status_t add_app_callbacks(lorawan_app_callbacks_t *callbacks) = 0;
};
#endif /* LORAWAN_BASE_H_ */