Skip to main content

Develop a connected bike alarm

Confirmed

What you will learn:

In this tutorial series, we are going to show you how to make your own Luos app using a fully modular bike alarm as an example.

Board
MCU
Service
Network
Distributed System

Part 1 : Make a basic alarm

The purpose of the bike alarm is to create an alarm that constantly measures a bike's movements while activated, and raise an alarm if the bike is moving.

To make it work, we will use an IMU board to measure the bike's motions and, for now, an RGB LED board to display an alarm using a red blinky LED. For this tutorial, we are going to use L0 board examples available in Luos examples. We are going to use IMU and LED drivers examples without any modifications and just add an embedded app to control them. This embedded app service will control the behavior of the alarm. This app can work on any node.

tip

We chose the LED node because there is plenty of memory space in it.

To summarize, the setup will looks like this:

  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ╭node 1 /!\ Not certified ┃
┃ │ Type Alias ID ┃
┃ ├> Unknown alarm_control 1 ┃
┃ ╰> Color alarm 2 ┃
╔>┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
║ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
╚══ 0>┃0 ╭node 2 /!\ Not certified ┃
┃ │ Type Alias ID ┃
┃ ╰> Imu gps 3 ┃
>┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Alarm controller app creation

Our app service will be hosted in a package, so first we need to organize the code by creating a folder and the associated files:

Alarm_controller
└>alarm_controller.c
└>alarm_controller.h

In the file alarm_controller.h:

#ifndef ALARM_CONTROLLER_H
#define ALARM_CONTROLLER_H

#include "luos_engine.h"

void AlarmController_Init(void);
void AlarmController_Loop(void);

#endif /* ALARM_CONTROLLER_H */
info

For more details about Luos code organization, please read the Luos project organization page.

Now we have to fill both functions in the C file.

Alarm controller init

Our init code has to be in the alarm_controller_init() function of alarm_controller.c file.

Let us create the service:

void AlarmController_Init(void)
{
revision_t revision = {.major = 1, .minor = 0, .build = 0};
// Create app
app = Luos_CreateService(AlarmController_MsgHandler, ALARM_CONTROLLER_APP, "alarm_control", revision);
}

As you can see, our app default alias will be "alarm_control", and we created a custom service type.

info

For more information about service creation, read the Create Luos service page.

To declare to others this custom service type, we need to add an enum in the C file:

typedef enum
{
ALARM_CONTROLLER_APP = LUOS_LAST_TYPE
} alarm_t;

For now, there is only one custom type dedicated to our app, but later on this tutorial we will have to add another one.

In order to measure bike's motion, our app will need to receive gyroscopic data from IMU and put an alarm flag if motion is detected. To manage it, we created a message reception callback AlarmController_MsgHandler. Here is how we made this function:

void AlarmController_MsgHandler(service_t *service, msg_t *msg)
{
if (msg->header.cmd == GYRO_3D)
{
// this is IMU information
float value[3];
memcpy(value, msg->data, msg->header.size);
if ((value[0] > 300) || (value[1] > 300) || (value[2] > 300))
{
// There is movement
blink_state = 1;
}
return;
}
}

Now we have to be able to get data from the IMU. To do that, our app will need a routing table. Routing tables are auto-generated and shared to all services in the network during detection.

info

For more information, read the routing table page.

Because this app is stand-alone, it has to run a detection to create the routing table and configure the IMU to send gyro data each 10ms.

The UPDATE_PUB command is a specific Luos engine command asking to a service to send back an update each X milliseconds. Thanks to this command, the target container will receive an update request at a given frequency. These auto-generated messages never transit into the real network and are generated locally for the container to save resources.

In the init code we added:

void AlarmController_Init(void)
{
revision_t revision = {.major = 1, .minor = 0, .build = 0};
// Create app
app = Luos_CreateService(AlarmController_MsgHandler, ALARM_CONTROLLER_APP, "alarm_control", revision);

// Detect all services of your network and create a routing_table
Luos_Detect(app);

// Try to find a IMU service and set parameters to disable quaternions data format and send back Gyro acceleration data format.
search_result_t result;
imu_report_t report;
report.gyro = 1;
report.quat = 0;
RTFilter_Type(RTFilter_Reset(&result), IMU_TYPE);
if (result.result_nbr > 0)
{
// We find a service, prepare a message with parameters, and send it.
msg_t msg;
msg.header.cmd = PARAMETERS;
msg.header.size = sizeof(imu_report_t);
msg.header.target = result.result_table[0]->id;
msg.header.target_mode = IDACK;
memcpy(msg.data, &report, sizeof(imu_report_t));
Luos_SendMsg(app, &msg);

// Setup auto update each 10ms on IMU
time_luos_t time = TimeOD_TimeFrom_ms(UPDATE_PERIOD_MS);
TimeOD_TimeToMsg(&time, &msg);
msg.header.cmd = UPDATE_PUB;
Luos_SendMsg(app, &msg);
}
}

Now the IMU service will automatically send gyroscopic data to your app each 10ms.

Alarm controller loop

Thanks to the initialization, your app will receive gyroscopic data in the AlarmController_MsgHandler function we made and raise the 'blink_state' flag accordingly to movements. We just need to manage a non-blocking blink code in the loop when the 'blink_state' flag changes:

void AlarmController_Loop(void)
{
staticuint8_t blink = 0;
staticuint8_t blink_nb = BLINK_NUMBER * 2; // For each blink we need to turn ON then OFF that's why we do *2
staticuint32_t last_blink = 0;
search_result_t result;

// ********** non blocking blink ************
if (blink_state)
{
// Init variables to blink
blink_state = 0;
blink_nb = 0;
blink = 0;
}
if (blink_nb < (BLINK_NUMBER*2))
{
// Blink is not finished
RTFilter_Type(RTFilter_Reset(&result), COLOR_TYPE);
if (result.result_nbr > 0)
{
// we get a led alarm, set color
color_t color;
color.r = 0;
color.g = 0;
color.b = 0;
if (!blink)
{
// turn led red
color.r = LIGHT_INTENSITY;
}
msg_t msg;
msg.header.target = result.result_table[0]->id;
msg.header.target_mode = IDACK;
IlluminanceOD_ColorToMsg(&color, &msg);
Luos_SendMsg(app, &msg);
}
}
}

Done! Our app is finished.

Assemble services on nodes, compile, and flash.

We now have a ready-to-work app. We need to load it into a node. As explained previously, we planned to put this app on a LED board's node because LED service is really light and there is plenty of space available for our app to execute. So we first duplicate the Led project available on Luos_engine examples, then we add the alarm_controller app folder into the compiler's include path, and finally add the init and loop functions in the main function of the project.

platformio.ini (in PlatformIO):

lib_extra_dirs =
../../../Drivers/
../../../Apps/
lib_deps =
Luos_engine
led
alarm_controller

main.c:

int main(void)
{
Luos_Init();
Led_Init();
AlarmController_Init();
while (1)
{
Luos_Loop();
Led_Loop();
AlarmController_Loop();
}
}
info

There is no management of IMU's init and loop functions here because we are working on the LED's node firmware. The IMU init and loop functions are running on the IMU board.

After that, you have to flash the IMU board with the Luos IMU example and the LED board with the custom project we just created.