Part 3 : Add another app controlling the alarm
Our alarm is ready to work with any other app. We can now develop a new app that provides other functions for our bike alarm. To keep it simple, we will add a mono-stable push button board to the network, and create a new app that monitors this button to activate or deactivate the alarm app we created on the last part of this tutorial.
This app is really similar to the alarm app, so if you understood everything from the beginning of this tutorial, this part will be easy. We will call this application "start_control".
To start the app, we follow the same steps that the previous ones:
- App folder creation (we call it start_controller).
- .c and .h files creation in the folder.
- Service creation by writing the init and loop functions.
- Detection event management with initialization of the button to return a state value each 10 ms.
- Loop conditional execution if
control_app
isPLAY
andLuos_IsNodeDetected()
istrue
.
Your start_controller.c
file should look like this:
service_t *app;
volatile control_t control_app;
void StartController_Init(void)
{
revision_t revision = {.major = 1, .minor = 0, .build = 0};
// Create App
app = Luos_CreateService(StartController_MsgHandler, START_CONTROLLER_APP, "start_control", revision);
Luos_Detect(app);
// By default this app running
control_app.flux = PLAY;
}
void StartController_Loop(void)
{
// Check if the app is detected
if ((Luos_IsNodeDetected()) & (control_app.flux == PLAY))
{
// Do app things
}
}
static void StartController_MsgHandler(service_t *service, msg_t *msg)
{
if (msg->header.cmd == CONTROL)
{
control_app.unmap = msg->data[0];
return;
}
if (msg->header.cmd == END_DETECTION)
{
// Init other services
}
}
As you can see, we created a new type called START_CONTROLLER_APP. We added this app to the structure we already made for the first one:
typedef enum
{
ALARM_CONTROLLER_APP = LUOS_LAST_TYPE,
START_CONTROLLER_APP
} alarm_t;
Those custom types are product level information. Following the Luos project code organization, you should create the alarm_t
enum into the product_config.h
.
Manage button updates
This app needs to get the button's information to do things. Let us add an auto-update on the END_DETECTION
event and manage button information reception.
We choose to search for the button service using its alias, instead of using its type like we did in the 'alarm_controller' app. This way, you can choose which button do what depending on its alias, and manage as many buttons you want on your bike for future new features.
void StartController_MsgHandler(service_t *service, msg_t *msg)
{
if (msg->header.cmd == CONTROL)
{
control_app.unmap = msg->data[0];
return;
}
if (msg->header.cmd == END_DETECTION)
{
search_result_t result;
RTFilter_Alias(RTFilter_Reset(&result), "lock");
if (result.result_nbr > 0)
{
msg_t msg;
msg.header.target = result.result_table[0]->id;
msg.header.target_mode = IDACK;
// Setup auto update each UPDATE_PERIOD_MS on button
// This value is resetted on all service at each detection
// It's important to setting it each time.
time_luos_t time = TimeOD_TimeFrom_ms(UPDATE_PERIOD_MS);
TimeOD_TimeToMsg(&time, &msg);
msg.header.cmd = UPDATE_PUB;
while (Luos_SendMsg(app, &msg) != SUCCEED)
{
Luos_Loop();
}
end_detection = 0;
return;
}
}
}
We can now receive a data each 10 ms, that is used to control the alarm from a button service called "lock". In the reception callback, we have to deal differently whether the data comes from a button or not, because buttons are (most of the time) mono-stable. We need to detect a push event in order to control the alarm, and with a mono-stable button a push gives 0->1->0. In this app, we want to switch the buttons state instead of going back to the default state (0) each time, so we have to get the 0->1->0 event to switch the state of a variable.
If the sender is not a button, we can take the state without any filtering.
In rx_start_controller_cb
you can add:
if (msg->header.cmd == IO_STATE)
{
if (control_app.flux == PLAY)
{
search_result_t result;
RTFilter_ID(RTFilter_Reset(&result), msg->header.source);
if (result.result_table[0]->type == STATE_TYPE)
{
// This is the button reply we have to filter it to manage monostability
if ((!last_btn_state) & (last_btn_state != msg->data[0]))
{
lock = (!lock);
state_switch++;
}
}
else
{
// This is an already filtered information
if ((lock != msg->data[0]))
{
lock = msg->data[0];
state_switch++;
}
}
last_btn_state = msg->data[0];
}
return;
}
We now have all we need to shut down the alarm app at every button push, and turn the LED green to display the alarm state in the loop:
void StartController_Loop(void)
{
search_result_t result;
// Check if the app is detected
if (Luos_IsNodeDetected())
{
// ********** non blocking button management ************
if (state_switch & (control_app.flux == PLAY))
{
msg_t msg;
msg.header.target_mode = IDACK;
// Share the lock state with the alarm_control app
RTFilter_Alias(RTFilter_Reset(&result), "alarm_control");
if (result.result_nbr > 0)
{
// we have an alarm_controller App control it
control_t alarm_control;
if (lock)
{
// Bike is locked, alarm need to run.
alarm_control.flux = PLAY;
}
else
{
// Bike is unlocked alarm should be sutted down.
alarm_control.flux = STOP;
}
// send message
msg.header.target = result.result_table[0]->id;
ControlOD_ControlToMsg(&alarm_control, &msg);
Luos_SendMsg(app, &msg);
}
// The button state switch, change the led consequently
state_switch = 0;
RTFilter_Type(RTFilter_Reset(&result), COLOR_TYPE);
if (result.result_nbr > 0)
{
// We have an alarm, we can set its color
color_t color;
color.r = 0;
color.g = 0;
color.b = 0;
if (!lock)
{
color.g = LIGHT_INTENSITY;
}
msg.header.target = result.result_table[0]->id;
IlluminanceOD_ColorToMsg(&color, &msg);
Luos_SendMsg(app, &msg);
}
}
}
}
Now to finish this app, we have to put it into a project. We will put this service in the button board, so we duplicate the button project, add the start_controller folder in the build path, and call the init and loop functions in the main function.
We are ready to test!