Wirepas SDK
evaluation_app/app.c
/* Copyright 2020 Wirepas Ltd. All Rights Reserved.
*
* See file LICENSE.txt for full license details.
*
*/
/*
* @file app.c
* @brief This file is a helloworld app enabling wirepas mesh network
* capabilities experiments
* (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"
#define DEBUG_LOG_MODULE_NAME "EVAL_APP"
#define DEBUG_LOG_MAX_LEVEL LVL_NOLOG
#include "debug_log.h"
#define COARSE_TO_MS(delay) (1000u * (delay) >> 7)
#define PERIODIC_MSG_DATA_PATTERN_LEN (8u)
#define DEFAULT_PERIOD_S (10u)
#define DEFAULT_PERIOD_MS (DEFAULT_PERIOD_S*1000u)
#define CUSTOM_PERIOD_TYPE 0xC3
#define PERIODIC_WORK_EXECUTION_TIME_US (250u)
#define BUTTON_PRESSED_STATE (2u)
#define TASK_EXEC_TIME_US_SEND_BUTTON_PRESSED_MSG (250u)
typedef struct __attribute__((packed))
{
uint32_t interval;
/* uint16_t custom_data; */
} app_config_t;
#define PERIODIC_MSG_PERIOD_SET_MIN_VAL_MS (2000ul)
#define PERIODIC_MSG_PERIOD_SET_MAX_VAL_MS (1200000ul)
#define MAX_LED_PER_BOARD (4u)
#define DATA_EP (1u)
static uint16_t m_filter_id;
typedef enum
{
MSG_ID_PERIODIC_MSG = 0,
MSG_ID_BUTTON_EVENT_MSG = 1,
MSG_ID_ECHO_RESPONSE_MSG = 2,
MSG_ID_LED_GET_STATE_RESPONSE_MSG = 3,
MSG_ID_PERIODIC_MSG_PERIOD_SET_MSG = 128,
MSG_ID_LED_SET_STATE_MSG = 129,
MSG_ID_LED_GET_STATE_MSG = 130,
MSG_ID_ECHO_COMMAND_MSG = 131,
} message_id_e;
typedef enum
{
LED_STATE_OFF = 0,
LED_STATE_ON = 1
} led_state_e;
typedef struct __attribute__((packed))
{
uint32_t counter_value;
uint8_t data_pattern[PERIODIC_MSG_DATA_PATTERN_LEN];
} payload_periodic_t;
typedef struct __attribute__((packed))
{
uint32_t new_period_ms;
} payload_periodic_set_t;
typedef struct __attribute__((packed))
{
uint8_t button_id;
uint8_t button_state;
} payload_button_event_t;
typedef struct __attribute__((packed))
{
uint32_t travel_time_ms;
} payload_response_echo_t;
typedef struct __attribute__((packed))
{
uint8_t led_id;
uint8_t led_state;
} payload_led_state_set_t;
typedef struct __attribute__((packed))
{
uint8_t led_id;
} payload_led_state_get_t;
typedef struct __attribute__((packed))
{
uint8_t led_id;
uint8_t led_state;
} payload_response_led_state_get_t;
typedef struct __attribute__((packed))
{
uint8_t id;
union
{
payload_periodic_t periodic;
payload_periodic_set_t periodic_set_period;
payload_button_event_t button_event;
payload_response_echo_t resp_echo;
payload_led_state_set_t led_state_set;
payload_led_state_get_t led_state_get;
payload_response_led_state_get_t resp_led_state_get;
} payload;
} msg_t;
static const uint8_t m_periodic_data_pattern[PERIODIC_MSG_DATA_PATTERN_LEN] =
{0x0A,0x0B,0x0B,0x0A,0x0A,0x0C,0x0D,0x0C};
static volatile uint8_t m_button_pressed_id;
static uint32_t m_period_ms;
static led_state_e m_table_led_state[MAX_LED_PER_BOARD] = {0};
static app_lib_data_receive_res_e unicast_broadcast_data_received_cb(
const shared_data_item_t * item,
const app_lib_data_received_t * data);
static shared_data_item_t unicast_packets_filter =
{
.cb = unicast_broadcast_data_received_cb,
.filter = {
/* Filtering by source endpoint. */
.src_endpoint = DATA_EP,
/* Filtering by destination endpoint. */
.dest_endpoint = DATA_EP,
.multicast_cb = NULL
}
};
static shared_data_item_t broadcast_packets_filter =
{
.cb = unicast_broadcast_data_received_cb,
.filter = {
/* Filtering by source endpoint. */
.src_endpoint = DATA_EP,
/* Filtering by destination endpoint. */
.dest_endpoint = DATA_EP,
.multicast_cb = NULL
}
};
static app_lib_data_send_res_e send_uplink_msg(message_id_e id,
uint8_t * payload)
{
msg_t msg; /* Create uplink message structure. */
size_t msg_byte_size = sizeof(msg.id); /* Message to send byte size. */
msg.id = (uint8_t)id;
switch (msg.id)
{
case MSG_ID_PERIODIC_MSG:
memcpy(&msg.payload.periodic,
(payload_periodic_t *)payload,
sizeof(payload_periodic_t));
msg_byte_size += sizeof(payload_periodic_t);
break;
case MSG_ID_BUTTON_EVENT_MSG:
memcpy(&msg.payload.button_event,
(payload_button_event_t *)payload,
sizeof(payload_button_event_t));
msg_byte_size += sizeof(payload_button_event_t);
break;
case MSG_ID_ECHO_RESPONSE_MSG:
memcpy(&msg.payload.resp_echo,
(payload_response_echo_t *)payload,
sizeof(payload_response_echo_t));
msg_byte_size += sizeof(payload_response_echo_t);
break;
case MSG_ID_LED_GET_STATE_RESPONSE_MSG:
memcpy(&msg.payload.resp_led_state_get,
(payload_response_led_state_get_t *)payload,
sizeof(payload_response_led_state_get_t));
msg_byte_size += sizeof(payload_response_led_state_get_t);
break;
default:
/* Invalid message ID given : send only invalid msg ID. */
break;
}
/* Create a data packet 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);
}
static uint32_t task_send_periodic_msg(void)
{
static uint32_t counter_value = 0;
payload_periodic_t payload; /* Message payload data. */
payload.counter_value = counter_value;
memcpy(payload.data_pattern,
m_periodic_data_pattern,
sizeof(m_periodic_data_pattern));
/* Send message. */
if (send_uplink_msg(MSG_ID_PERIODIC_MSG,
(uint8_t *)&payload) != APP_LIB_DATA_SEND_RES_SUCCESS)
{
/*
* Message was not accepted for sending.
* Error handling can be performed here.
*/
}
/* Increment value to send. */
counter_value++;
/*
* Inform the stack that this function should be called again in
* m_period_ms milliseconds. By returning APP_SCHEDULER_STOP_TASK,
* the scheduler will remove the task.
*/
return m_period_ms;
}
static uint32_t task_send_button_pressed_msg(void)
{
payload_button_event_t payload; /* Message payload data. */
/* Prepare payload data field. */
payload.button_id = m_button_pressed_id;
payload.button_state = (uint8_t)BUTTON_PRESSED_STATE;
/* Send message. */
if (send_uplink_msg(MSG_ID_BUTTON_EVENT_MSG,
(uint8_t *)&payload) != APP_LIB_DATA_SEND_RES_SUCCESS)
{
/*
* Message was not accepted for sending.
* Error handling can be performed here.
*/
}
}
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;
/*
* 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);
}
static void send_echo_response_msg(uint32_t delay)
{
/* Message payload data. */
payload_response_echo_t payload =
{
.travel_time_ms = 0
};
/* Prevent overflow. In case of error "travel_time_ms" value is nil. */
if (delay <= UINT32_MAX/1000u)
{
/*
* Downlink propagation time is expressed in 1/128th second.
* Have to convert it into millisecond.
*/
payload.travel_time_ms = COARSE_TO_MS(delay);
}
/* Send message. */
if (send_uplink_msg(MSG_ID_ECHO_RESPONSE_MSG,
(uint8_t *)&payload) != APP_LIB_DATA_SEND_RES_SUCCESS)
{
/*
* Message was not accepted for sending.
* Error handling can be performed here.
*/
}
}
static void set_periodic_msg_period(uint32_t new_period_ms)
{
/*
* Check new period value is in a valid range
* and update period accordingly.
*/
if ((new_period_ms >= PERIODIC_MSG_PERIOD_SET_MIN_VAL_MS) &&
(new_period_ms <= PERIODIC_MSG_PERIOD_SET_MAX_VAL_MS))
{
m_period_ms = new_period_ms;
/* Reschedule task to apply new period value. */
App_Scheduler_addTask_execTime(task_send_periodic_msg,
PERIODIC_WORK_EXECUTION_TIME_US);
}
else
{
/* Invalid period value do not change anything. */
}
}
static void set_led_state(uint8_t led_id, uint8_t led_state)
{
/* Check if requested LED is available on the board. */
uint8_t leds_num = Led_getNumber();
if ((led_id < MAX_LED_PER_BOARD) && (led_id < leds_num) && (leds_num > 0))
{
/*
* Valid LED ID requested:
* execute command if valid LED state received.
*/
if (led_state == LED_STATE_OFF)
{
Led_set(led_id,false); /* Switch off LED. */
m_table_led_state[led_id] = LED_STATE_OFF;
}
else if (led_state == LED_STATE_ON)
{
Led_set(led_id,true); /* Switch on LED. */
m_table_led_state[led_id] = LED_STATE_ON;
}
else
{
/* Invalid LED state received : do nothing. */
}
}
}
static void send_led_state(uint8_t led_id)
{
payload_response_led_state_get_t payload; /* Message payload data. */
/*
* Check if requested LED is available on the board
* and that led state table is big enough to hold all led's status.
*/
uint8_t leds_num = Led_getNumber();
if ((led_id < MAX_LED_PER_BOARD) && (led_id < leds_num) && (leds_num > 0))
{
/* Prepare payload data. */
payload.led_id = led_id;
payload.led_state = (uint8_t)m_table_led_state[led_id];
/* Send message. */
if (send_uplink_msg(MSG_ID_LED_GET_STATE_RESPONSE_MSG,
(uint8_t *)&payload) != APP_LIB_DATA_SEND_RES_SUCCESS)
{
/*
* Message was not accepted for sending.
* Error handling can be performed here.
*/
}
}
}
static app_lib_data_receive_res_e unicast_broadcast_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;
if ((msg_size < sizeof(msg.id)))
{
/* Data is not for this application. */
}
msg_size -= sizeof(msg.id);
/* Process incoming message according to message ID. */
switch (msg.id)
{
/*
* First check received message length match expected one.
* If not the case do not change anything else execute action.
*/
case MSG_ID_ECHO_COMMAND_MSG:
if (msg_size == 0)
{
send_echo_response_msg(data->delay);
}
break;
case MSG_ID_PERIODIC_MSG_PERIOD_SET_MSG:
if (msg_size == sizeof(payload_periodic_set_t))
{
set_periodic_msg_period(
msg.payload.periodic_set_period.new_period_ms);
}
break;
case MSG_ID_LED_SET_STATE_MSG:
if (msg_size == sizeof(payload_led_state_set_t))
{
set_led_state(msg.payload.led_state_set.led_id,
msg.payload.led_state_set.led_state);
}
break;
case MSG_ID_LED_GET_STATE_MSG:
if (msg_size == sizeof(payload_led_state_get_t))
{
send_led_state(msg.payload.led_state_get.led_id);
}
break;
default: /* Unknown message ID : do nothing. */
break;
}
/* Data handled successfully. */
}
static void appConfigPeriodReceivedCb(uint16_t type,
uint8_t length,
uint8_t * value_p)
{
app_config_t * config;
if (type != CUSTOM_PERIOD_TYPE)
{
/* It should never happen as we registered only this type with this cb. */
LOG(LVL_ERROR, "Wrong app config type");
return;
}
if (length != sizeof(app_config_t))
{
/* Wrong size. */
LOG(LVL_ERROR, "Wrong app config size");
return;
}
config = (app_config_t *) value_p;
"New app configuration interval_s=%d",
config->interval);
/* Set new periodic data transfer interval. */
set_periodic_msg_period(config->interval*1000);
}
void App_init(const app_global_functions_t * functions)
{
(void) functions;
LOG(LVL_INFO, "Evaluation App example start");
shared_app_config_filter_t app_config_period_filter;
/* Prepare the app_config filter for measurement rate. */
app_config_period_filter.type = CUSTOM_PERIOD_TYPE;
app_config_period_filter.cb = appConfigPeriodReceivedCb;
Shared_Appconfig_addFilter(&app_config_period_filter, &m_filter_id);
LOG(LVL_INFO, "Filter added for static period with id=%d\n", m_filter_id);
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 (i.e low-energy or low-latency with autorole)
* Default is low-energy.
*/
#ifdef ENABLE_LOW_LATENCY_MODE
lib_settings->setNodeRole(APP_LIB_SETTINGS_ROLE_AUTOROLE_LL);
#endif
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 a periodic task to be called after DEFAULT_PERIOD_MS. */
m_period_ms = DEFAULT_PERIOD_MS;
App_Scheduler_addTask_execTime(task_send_periodic_msg,
PERIODIC_WORK_EXECUTION_TIME_US);
/* Set unicast & broadcast received messages callback. */
Shared_Data_addDataReceivedCb(&unicast_packets_filter);
Shared_Data_addDataReceivedCb(&broadcast_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
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.
shared_app_config_filter_t::cb
shared_app_config_received_cb_f cb
Definition: shared_appconfig.h:73
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
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_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
Shared_Appconfig_addFilter
shared_app_config_res_e Shared_Appconfig_addFilter(shared_app_config_filter_t *filter, uint16_t *filter_id)
Add a new app config type filter to the list. If the item is already in the list it is only updated.
debug_log.h
APP_LIB_DATA_QOS_HIGH
@ APP_LIB_DATA_QOS_HIGH
Definition: wms_data.h:87
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
shared_appconfig.h
app_lib_data_received_t
Struct passed to data reception callback functions.
Definition: wms_data.h:279
app_lib_data_to_send_t::flags
uint8_t flags
Definition: wms_data.h:338
APP_ADDR_ANYSINK
@ APP_ADDR_ANYSINK
Definition: wms_app.h:236
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
APP_LIB_DATA_RECEIVE_RES_NOT_FOR_APP
@ APP_LIB_DATA_RECEIVE_RES_NOT_FOR_APP
Definition: wms_data.h:191
shared_app_config_filter_t
Structure holding all parameters for app config type filtering.
Definition: shared_appconfig.h:64
APP_LIB_DATA_SEND_RES_SUCCESS
@ APP_LIB_DATA_SEND_RES_SUCCESS
Definition: wms_data.h:139
Led_getNumber
uint8_t Led_getNumber(void)
Get number of leds available.
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.
SHARED_DATA_NET_MODE_UNICAST
@ SHARED_DATA_NET_MODE_UNICAST
Definition: shared_data.h:47
app_lib_data_to_send_t::bytes
const uint8_t * bytes
Definition: wms_data.h:326
BUTTON_PRESSED
@ BUTTON_PRESSED
Definition: button.h:22
SHARED_DATA_NET_MODE_BROADCAST
@ SHARED_DATA_NET_MODE_BROADCAST
Definition: shared_data.h:49
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
shared_app_config_filter_t::type
uint16_t type
Definition: shared_appconfig.h:68
app_lib_data_received_t::delay
uint32_t delay
Definition: wms_data.h:288
APP_LIB_SETTINGS_ROLE_AUTOROLE_LL
@ APP_LIB_SETTINGS_ROLE_AUTOROLE_LL
Definition: wms_settings.h:83
api.h
button_event_e
button_event_e
Different events for a button.
Definition: button.h:21