A new way to design embedded app’ using Luos [Step 1]

A new way to design embedded app’ using Luos [Step 1]

Intro: The purpose of this tutorial
Step 1: Make a basic alarm
Step 2: Make our alarm adaptable and hot-pluggable
Step 3: Add another app controlling the alarm
Step 4: Make our alarm evolve using a truck horn and more
Step 5: Connect our system to the cloud

Make a basic alarm

The purpose of our bike alarm is to constantly measure bike movements and raise an alarm if the bike is moving.
To make it work I will use an IMU board 3 to measure the bike motions and, for now, an RGB led board 1 to display an alarm using a red blinky LED.
I’m going to use default IMU and LED drivers without any modifications and just add an embedded app to control them.
This embedded App container will control the behavior of the alarm. This App can work on any node. I chose the LED node because there is plenty of memory space in it.

To summarize, my setup will look like that:

root: [2097186, 1194612503, 540554032]                      => Node project:
           |  Type                Alias               ID  
           └> Unknown             alarm_control       1      => App container:
           └> Color               alarm               2      => Driver container: https://github.com/Luos-io/Examples/tree/master/Drivers/Led
   └── 1<=>0: [2818086, 1194612503, 540554032]              => Node project: https://github.com/Luos-io/Examples/tree/master/Projects/Imu
               |  Type                Alias               ID  
               └> Imu                 gps                 3  => Driver container: https://github.com/Luos-io/Examples/tree/master/Drivers/Imu

Alarm controller app creation

Our app is a mere container, so first I organize my code by creating a folder and the associated files:


Capture d’écran 2020-06-08 à 18.00.25

In the file alarm_controller.h:

#ifndef ALARM_CONTROLLER_H
#define ALARM_CONTROLLER_H

#include "luos.h"

voidalarm_controller_init(void);
voidalarm_controller_loop(void);

#endif /* ALARM_CONTROLLER_H */

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

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

Alarm controller init

Our init code have to be on the alarm_controller_init() function of alarm_controller.c file.

Lets create the container:

app = luos_container_create(rx_alarm_controller_cb, ALARM_CONTROLLER_APP, "alarm_control", "0.0.1");

As you can see, my app default alias is alarm_control.

INFO: For more information about container creation read the Create Luos containers page.

For this project, I created custom containers types using an enum:

typedefenum
{
   ALARM_CONTROLLER_APP = LUOS_LAST_TYPE
} alarm_apps_type_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 motion of the bike, our app will need to receive gyroscopic data from IMU and put an alarm flag if there is motion. To manage it I created a message reception callback rx_alarm_controller_cb.
Here is how I made this function:

voidrx_alarm_controller_cb(container_t *container, 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 containers during detection.

INFO: for more information, read our 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.

INFO: The UPDATE_PUB command is a specific Luos command asking it to auto generate a ASK_PUB_CMD for a target container each X time. Thank’s to it the target container just will receive an update request at a given frequency. Those auto-generated messages never transit into the real network and are generated locally for the container to save ressources.

In the init code I add:

voidalarm_controller_init(void)
{
   app = luos_container_create(rx_alarm_controller_cb, ALARM_CONTROLLER_APP, "alarm_control", "0.0.1");
// Wait for all containers to be ready
   HAL_delay_ms(1500);
// Detect all containers of your network and create a route_table
   detect_containers(app);
// Try to find a IMU container and set parameters to disable quaternions data format and send back Gyro acceleration data format.
imu_report_t report;
   report.gyro = 1;
   report.quat = 0;
   id = id_from_type(IMU_MOD);
if (id > 0)
   {
// We find a container, 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 = id;
       msg.header.target_mode = IDACK;
       memcpy(msg.data, &report, sizeof(imu_report_t));
       luos_send(app, &msg);

// Setup auto update each 10ms on IMU
time_luos_t time = time_from_ms(10);
       time_to_msg(&time, &msg);
       msg.header.cmd = UPDATE_PUB;
       luos_send(app, &msg);
   }
}

Now the IMU container 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 rx_alarm_controller_cb function we made and raise the ‘blink_state’ flag accordingly to movements.
In the loop we just need to manage a non-blocking blink code when the ‘blink_state’ flag changes:

voidalarm_controller_loop(void)
{
staticuint8_t blink = 0;
staticuint8_t blink_nb = BLINK_NUMBER*2;
staticuint32_t last_blink = 0;

// ********** non blocking blink ************
if (blink_state)
   {
       blink_state = 0;
       blink_nb = 0;
       blink = 0;
if (blink_nb < (BLINK_NUMBER*2))
   {
if ((HAL_GetTick() - last_blink) >= 500)
       {
           blink_nb++;
int id = id_from_type(COLOR_MOD);
if (id > 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 = 30;
               }
msg_t msg;
               msg.header.target = id;
               msg.header.target_mode = IDACK;
               color_to_msg(&color, &msg);
               luos_send(app, &msg);
               blink = (!blink);
               last_blink = HAL_GetTick();
           }
       }
   }
}

Done! Our app is finished.

Assemble containers on nodes, compile, and flash

Now we have a ready-to-work app. We need to put it into a node and on a compilable project.
As I said previously, I planned to put this app on a LED board node because LED container is really light and there is plenty of space for our app to execute. So basically, I duplicate the Led project example, add the alarm_controller app folder into compiler include path, and add the init and loop functions on the main of the project.

(on platformio) platformio.ini:

lib_extra_dirs =
   ../../../Drivers/
   ../../../Apps/
lib_deps =
   Luos@>0.6.7
   led
   alarm_controller

main.c:

int main(void)
{
   luos_init();
   led_init();
   alarm_controller_init();
while (1)
   {
       luos_loop();
       led_loop();
       alarm_controller_loop();
   }
}

There is no management of IMU init and loop functions here because we are doing the LED node firmware. The Imu init and loop functions are running on the IMU board.
After that, you have to flash the IMU board and the LED board with your custom firmware:

If you have any question or comment about this step of the tutorial, feel free to reply to this post.

<< Previous: Intro | Next: Step 2 >>

Is Luos for me? 🤔