Wirepas SDK
low_latency_app/app.c
/* Copyright 2021 Wirepas Ltd. All Rights Reserved.
*
* See file LICENSE.txt for full license details.
*
*/
/*
* @file app.c
* @brief This file is an example application to demonstrate Low-Latency capabilities for a lighting usecase.
* (see README for additional information)
*/
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "api.h"
#include "led.h"
#include "button.h"
#include "shared_data.h"
#include "app_scheduler.h"
#include "app_persistent.h"
#define DEBUG_LOG_MODULE_NAME "LOWLATENCY_APP"
#define DEBUG_LOG_MAX_LEVEL LVL_NOLOG
#include "debug_log.h"
#define MAX_LED_PER_BOARD 4u
#define SWITCH_ON_COMMAND_PATTERN 0b00000001
#define DATA_EP (25u)
#define TASK_EXEC_TIME_US_SEND_BUTTON_PRESSED_MSG (250u)
#define LED_STATE_OFF 0
#define LED_STATE_ON 1
typedef enum
{
MSG_ID_SWITCH_COMMAND = 1,
MSG_ID_DOWNLINK_SET_LED_STATE = 2,
MSG_ID_MULTICAST_GROUPING = 3
} message_id_e;
static uint8_t m_button_pressed_id;
static uint32_t m_mcast_address; /* This node is part of group 1. */
typedef struct __attribute__((packed))
{
uint8_t button_id;
uint8_t action;
uint16_t counter;
} payload_switch_command_t;
typedef struct __attribute__((packed))
{
uint8_t led_id;
uint8_t brightness;
} payload_on_off_lighting_t;
typedef struct __attribute__((packed))
{
uint32_t multicast_address;
} payload_grouping_command_t;
typedef struct __attribute__((packed))
{
uint8_t id;
union
{
payload_switch_command_t switch_status;
payload_on_off_lighting_t set_brightness;
payload_grouping_command_t set_multicast_address;
} payload;
} msg_t;
static void set_led_state(uint8_t led_id, uint8_t led_state)
{
"Set led_id %d to state %d", led_id, led_state);
/* Switch the Led */
if(Led_set(led_id,led_state) == LED_RES_INVALID_ID)
{
LOG(LVL_WARNING, "Led invalid id");
}
}
static app_lib_data_send_res_e send_node_to_node_msg(message_id_e message_id, uint8_t * payload)
{
LOG(LVL_INFO, "Send message payload");
msg_t msg; /* Create node to node message structure. */
size_t msg_byte_size = sizeof (msg.id); /* Message to send byte size. */
msg.id = (uint8_t)message_id;
/* Fill the message. */
memcpy(&msg.payload.switch_status,
(payload_switch_command_t *)payload,
sizeof(payload_switch_command_t));
msg_byte_size += sizeof(payload_switch_command_t);
/* Create a packet data to send. */
app_lib_data_to_send_t data_to_send;
data_to_send.bytes = (const uint8_t *) &msg;
data_to_send.num_bytes = msg_byte_size;
data_to_send.src_endpoint = DATA_EP;
data_to_send.dest_endpoint = DATA_EP;
data_to_send.qos = APP_LIB_DATA_QOS_HIGH;
/* Send the data packet. */
return Shared_Data_sendData(&data_to_send, NULL);
}
/* Multicast group filter for Downlink packets. */
static bool filter_multicast_cb(app_addr_t received_multicast_addr)
{
LOG(LVL_INFO, "Multicast address received : 0x%x", received_multicast_addr);
return m_mcast_address == received_multicast_addr;
}
static void set_led_for_button(uint8_t button_id, uint8_t led_id)
{
/* Check button received : */
if (button_id == 0)
{
LOG(LVL_INFO, "set led %d ON", led_id);
set_led_state(led_id, LED_STATE_ON);
}
if (button_id == 1)
{
LOG(LVL_INFO, "set led %d OFF", led_id);
set_led_state(led_id, LED_STATE_OFF);
}
}
static app_lib_data_receive_res_e data_received_cb(
const shared_data_item_t * item,
{
(void) item;
msg_t *msg = (( msg_t *) data->bytes);
uint8_t msg_size = data->num_bytes;
"Message received id %d",
msg->id);
if (msg_size < sizeof (msg->id))
{
/* Data is not for this application. */
}
msg_size -= sizeof(msg->id);
switch (msg->id)
{
case MSG_ID_SWITCH_COMMAND : /* Switch command. */
LOG(LVL_INFO, "Msg Switch command received");
/* Check if received message length match expected one. */
if (msg_size == sizeof(payload_switch_command_t))
{
payload_switch_command_t *payload = & msg->payload.switch_status;
LOG (LVL_INFO, "Switch command received : button_id = %d, action = %d", payload->button_id, payload->action);
/* Check the action type. */
if (payload->action == 1) /* Button pressed. */
{
set_led_for_button(payload->button_id, payload->action);
}
else if (payload->action == 0)
{
/* Button released, no action yet. */
}
}
else
{
LOG(LVL_INFO, "Message received doesn't match the size of the expected message.");
}
break;
case MSG_ID_DOWNLINK_SET_LED_STATE: /* Brightness Downlink message. */
/*Check if received message length match expected one. */
if (msg_size == sizeof(payload_on_off_lighting_t))
{
if (msg->payload.set_brightness.brightness == 1) /* Set led ON. */
{
"Downlink brightness action %d, set Led ON",
msg->payload.set_brightness.brightness);
set_led_state(msg->payload.set_brightness.led_id, LED_STATE_ON);
}
if (msg->payload.set_brightness.brightness == 0) /* Set led OFF. */
{
"Downlink brightness action %d, set Led ON",
msg->payload.set_brightness.brightness);
set_led_state(msg->payload.set_brightness.led_id, LED_STATE_OFF);
}
}
else
{
/* Message error. */
LOG(LVL_ERROR, "Message recieved size doesn't correspond to the payload size");
}
break;
case MSG_ID_MULTICAST_GROUPING:
/*Check if received message length match expected one. */
if(msg_size == sizeof(payload_grouping_command_t))
{
"Set new Mcast address : 0x%x",
msg->payload.set_multicast_address.multicast_address);
m_mcast_address=msg->payload.set_multicast_address.multicast_address;
app_persistent_res_e res = App_Persistent_write((uint8_t *) &m_mcast_address, sizeof(m_mcast_address));
{
LOG(LVL_ERROR, "Cannot write\n");
}
}
break;
default:
break;
}
/* Data handled successfully. */
}
static shared_data_item_t alltype_packets_filter =
{
.cb = data_received_cb,
.filter = {
/* Filtering by source endpoint. */
.src_endpoint = DATA_EP,
/* Filtering by destination endpoint. */
.dest_endpoint = DATA_EP,
.multicast_cb = filter_multicast_cb
}
};
static uint32_t task_send_button_pressed_msg(void)
{
payload_switch_command_t payload; /* Message payload data. */
static uint32_t counter_value = 0;
payload.counter = counter_value;
uint8_t led_id = 1;
/* Construct switch command. */
payload.button_id = m_button_pressed_id;
payload.action = 1; /* Always do something when a button is pressed */
/* Send message. */
app_lib_data_send_res_e res = send_node_to_node_msg(MSG_ID_SWITCH_COMMAND, (uint8_t *)&payload);
{
/*
* Message was not accepted for sending.
* Error handling can be performed here.
*/
LOG(LVL_ERROR, "Message was not accepted for sending. ERROR CODE : %d", res);
}
counter_value ++;
LOG(LVL_INFO, "Button pressed id : %d", m_button_pressed_id);
/* Light On or Off current board led */
set_led_for_button(m_button_pressed_id, led_id);
}
static void button_pressed_handler(uint8_t button_id, button_event_e event)
{
(void) event;
/* Store button_id to process it in a dedicated application task. */
m_button_pressed_id = button_id;
LOG(LVL_INFO,"Button %d pressed", m_button_pressed_id);
/*
* Send "button pressed message" in a single shot application task (called
* each time button is pressed) as we are here in an IRQ context.
*/
App_Scheduler_addTask_execTime(task_send_button_pressed_msg,
TASK_EXEC_TIME_US_SEND_BUTTON_PRESSED_MSG);
}
void App_init(const app_global_functions_t * functions)
{
(void) functions;
LOG(LVL_INFO, "Starting low-latency application");
uint8_t num_buttons;
/* Basic configuration of the node with a unique node address. */
{
/*
* Could not configure the node.
* It should not happen except if one of the config value is invalid.
*/
return;
}
/*
* Set node operating mode to router low-latency
*/
lib_settings->setNodeRole(APP_LIB_SETTINGS_ROLE_HEADNODE_LL);
/* Use app persistance to read and write multicast address. */
res = App_Persistent_read( (uint8_t* ) &m_mcast_address, sizeof(m_mcast_address));
{
LOG(LVL_INFO, "App persistant first initialization\n");
m_mcast_address = 0x80000001;
App_Persistent_write((uint8_t *) &m_mcast_address, sizeof(m_mcast_address));
}
{
LOG(LVL_ERROR, "Persistent area is not initialized as it should (no area defined?)\n");
}
num_buttons = Button_get_number();
for (uint8_t button_id = 0; button_id < num_buttons; button_id++)
{
/* Register button pressed event on all user available button. */
button_pressed_handler);
}
/* Set unicast, multicast & broadcast received messages callback. */
Shared_Data_addDataReceivedCb(&alltype_packets_filter);
/*
* Start the stack.
* This is really important step, otherwise the stack will stay stopped and
* will not be part of any network. So the device will not be reachable.
* without reflashing it.
*/
lib_state->startStack();
}
LVL_INFO
#define LVL_INFO
Definition: debug_log.h:83
Led_set
led_res_e Led_set(uint8_t led_id, bool state)
Turn the given LED on or off.
app_lib_data_send_res_e
app_lib_data_send_res_e
A result code returned from lib_data->sendData().
Definition: wms_data.h:136
LVL_ERROR
#define LVL_ERROR
Definition: debug_log.h:85
APP_SCHEDULER_STOP_TASK
#define APP_SCHEDULER_STOP_TASK
Value to return from task to remove it.
Definition: app_scheduler.h:37
app_lib_data_to_send_t::tracking_id
app_lib_data_tracking_id_t tracking_id
Definition: wms_data.h:334
APP_ADDR_BROADCAST
@ APP_ADDR_BROADCAST
Send packet as broadcast to all nodes.
Definition: wms_app.h:245
button.h
Board-independent button functions.
app_lib_data_to_send_t::src_endpoint
uint8_t src_endpoint
Definition: wms_data.h:340
APP_LIB_DATA_SEND_FLAG_NONE
@ APP_LIB_DATA_SEND_FLAG_NONE
Definition: wms_data.h:96
app_lib_data_to_send_t::dest_address
app_addr_t dest_address
Definition: wms_data.h:330
Shared_Data_addDataReceivedCb
app_res_e Shared_Data_addDataReceivedCb(shared_data_item_t *item)
Add a new packet received item to the list. If the item is already in the list it is only updated.
app_lib_data_to_send_t::num_bytes
size_t num_bytes
Definition: wms_data.h:328
node_configuration.h
APP_LIB_DATA_RECEIVE_RES_HANDLED
@ APP_LIB_DATA_RECEIVE_RES_HANDLED
Definition: wms_data.h:188
App_Scheduler_addTask_execTime
app_scheduler_res_e App_Scheduler_addTask_execTime(task_cb_f cb, uint32_t delay_ms, uint32_t exec_time_us)
Add a task.
APP_LIB_DATA_NO_TRACKING_ID
#define APP_LIB_DATA_NO_TRACKING_ID
When sending data and no tracking of packet is requested, this ID may be used.
Definition: wms_data.h:60
APP_RES_OK
@ APP_RES_OK
Definition: wms_app.h:204
SHARED_DATA_NET_MODE_ALL
@ SHARED_DATA_NET_MODE_ALL
Definition: shared_data.h:53
App_Persistent_write
app_persistent_res_e App_Persistent_write(uint8_t *data, size_t len)
Write to persistent.
app_lib_data_receive_res_e
app_lib_data_receive_res_e
Return value of data reception callback.
Definition: wms_data.h:184
app_lib_data_to_send_t::dest_endpoint
uint8_t dest_endpoint
Definition: wms_data.h:342
APP_PERSISTENT_RES_INVALID_CONTENT
@ APP_PERSISTENT_RES_INVALID_CONTENT
Definition: app_persistent.h:46
APP_PERSISTENT_RES_OK
@ APP_PERSISTENT_RES_OK
Definition: app_persistent.h:38
app_scheduler.h
app_lib_data_to_send_t
A struct for lib_data->sendData().
Definition: wms_data.h:323
app_global_functions_t
List of global functions, passed to App_entrypoint()
Definition: wms_app.h:157
debug_log.h
APP_LIB_DATA_QOS_HIGH
@ APP_LIB_DATA_QOS_HIGH
Definition: wms_data.h:87
app_persistent.h
app_persistent_res_e
app_persistent_res_e
List of return code.
Definition: app_persistent.h:35
Button_register_for_event
button_res_e Button_register_for_event(uint8_t button_id, button_event_e event, on_button_event_cb cb)
Register for a button event.
led.h
Board-independent LED functions.
configureNodeFromBuildParameters
__STATIC_INLINE app_res_e configureNodeFromBuildParameters()
Wrapper on top of configureNode to get parameters from build system and hardcoded values from chip (f...
Definition: node_configuration.h:224
app_lib_data_received_t
Struct passed to data reception callback functions.
Definition: wms_data.h:279
LED_RES_INVALID_ID
@ LED_RES_INVALID_ID
Definition: led.h:26
app_addr_t
uint32_t app_addr_t
Definition: wms_app.h:228
app_lib_data_to_send_t::flags
uint8_t flags
Definition: wms_data.h:338
app_lib_data_to_send_t::qos
app_lib_data_qos_e qos
Definition: wms_data.h:336
LOG_INIT
#define LOG_INIT()
Definition: debug_log.h:66
shared_data.h
LVL_WARNING
#define LVL_WARNING
Definition: debug_log.h:84
APP_LIB_DATA_RECEIVE_RES_NOT_FOR_APP
@ APP_LIB_DATA_RECEIVE_RES_NOT_FOR_APP
Definition: wms_data.h:191
APP_LIB_DATA_SEND_RES_SUCCESS
@ APP_LIB_DATA_SEND_RES_SUCCESS
Definition: wms_data.h:139
LOG
#define LOG(level, fmt,...)
Print a log message if its severity is lower or equal to DEBUG_LOG_MAX_LEVEL.
Definition: debug_log.h:173
Shared_Data_sendData
app_lib_data_send_res_e Shared_Data_sendData(app_lib_data_to_send_t *data, app_lib_data_data_sent_cb_f sent_cb)
Send data. The packet to send is represented as a app_lib_data_to_send_t struct.
app_lib_data_to_send_t::bytes
const uint8_t * bytes
Definition: wms_data.h:326
BUTTON_PRESSED
@ BUTTON_PRESSED
Definition: button.h:22
Button_get_number
uint8_t Button_get_number(void)
Get number of buttons.
APP_SCHEDULER_SCHEDULE_ASAP
#define APP_SCHEDULER_SCHEDULE_ASAP
Value to return from task or as initial time to be executed ASAP.
Definition: app_scheduler.h:42
APP_LIB_SETTINGS_ROLE_HEADNODE_LL
@ APP_LIB_SETTINGS_ROLE_HEADNODE_LL
Definition: wms_settings.h:75
APP_PERSISTENT_RES_UNINITIALIZED
@ APP_PERSISTENT_RES_UNINITIALIZED
Definition: app_persistent.h:42
App_Persistent_read
app_persistent_res_e App_Persistent_read(uint8_t *data, size_t len)
Read from persistent.
api.h
button_event_e
button_event_e
Different events for a button.
Definition: button.h:21