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 to measure the bike motions and, for now, an RGB led board 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 module 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 module: 
            └> Color               alarm               2      => Driver module: 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 module: https://github.com/Luos-io/Examples/tree/master/Drivers/Imu

Alarm controller app creation

Our app is a mere module, 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"

void alarm_controller_init(void);
void alarm_controller_loop(void);

#endif /* ALARM_CONTROLLER_H */

INFO: For more details about Luos module 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 module:

app = luos_module_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 module creation read the Create Luos modules page.

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

typedef enum
{
    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:

void rx_alarm_controller_cb(module_t *module, 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 modules 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 module each X time. Thank’s to it the target module 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 module to save ressources.

In the init code I add:

void alarm_controller_init(void)
{
    app = luos_module_create(rx_alarm_controller_cb, ALARM_CONTROLLER_APP, "alarm_control", "0.0.1");
    // Wait for all modules to be ready
    HAL_delay_ms(1500);
    // Detect all modules of your network and create a route_table
    detect_modules(app);
    // Try to find a IMU module 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 module, 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 module 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:

void alarm_controller_loop(void)
{
    static uint8_t blink = 0;
    static uint8_t blink_nb = BLINK_NUMBER*2;
    static uint32_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 modules 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 module 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: The purpose of this tutorial) | Next (Step 2: Make our alarm adaptable and hot-pluggable) >>