A new way to design embedded app' using Luos [Step 3]


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

Add another app controlling the alarm

Our alarm is ready to work with other apps. So now we can develop other apps that provide other functions for our bike.
To keep it simple, I will add a mono-stable push button board to the network and create a new app that monitors it to activate or deactivate the alarm app.

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.

To start my app, I follow the same steps than for the previous app:

  1. App folder creation (I called it start_controller)
  2. .c and .h files creation in the folder
  3. Module creation by writing the init and loop functions
  4. Detection management in the loop preventing any other detection by any other app
  5. initialization of the Button to return a state value each 10ms
void start_controller_init(void)
{
    // By default this app is running
    control_mode.mode_control = PLAY;
    // Create app
    app = luos_module_create(rx_start_controller_cb, START_CONTROLLER_APP, "start_control", STRINGIFY(VERSION));
}

void start_controller_loop(void)
{
    static short previous_id = -1;
    // ********** hot plug management ************
    // Check if we have done the first init or if module ID has changed
    if (previous_id != id_from_module(app))
    {
        if (id_from_module(app) == 0)
        {
            // We don't have any ID, meaning no detection occurred, or a detection is occurring.
            if (previous_id == -1)
            {
                // This is the really first init, we have to make it.
                // Be sure the network is powered up 1000 ms before starting a detection
                if (HAL_GetTick() > 1000)
                {
                    // No detection occurred, do it
                    detect_modules(app);
                }
            }
            else
            {
                // Another app is making a detection, let it finish.
                // reset the init state to be ready to setup module at the end of detection
                previous_id = 0;
            }
        }
        else
        {
            // Make modules configurations
            int id = id_from_alias("lock");
            if (id > 0)
            {
                msg_t msg;
                msg.header.target = id;
                msg.header.target_mode = IDACK;
                // Setup auto update each UPDATE_PERIOD_MS on button
                // This value is reset on all module at each detection
                // It's important to set it each time.
                time_luos_t time = time_from_ms(UPDATE_PERIOD_MS);
                time_to_msg(&time, &msg);
                msg.header.cmd = UPDATE_PUB;
                luos_send(app, &msg);
            }
            previous_id = id_from_module(app);
        }
        return;
    }
}

NOTE: To prevent detection collision after a power up of the network with the other apps, I configure this one to make the detection 1000ms after the power-up (the ‘alarm_controller’ one is configured to do it after 1500ms). This way no detection can occur at the same time.

As you can see, I created a new type called START_CONTROLLER_APP. I added this app to the structure I already made for the first one:

typedef enum
{
    ALARM_CONTROLLER_APP = LUOS_LAST_TYPE,
    START_CONTROLLER_APP
} alarm_apps_type_t;

I choose to search the button board in my routing table using the alias, instead of using the type like in the ‘alarm_controller’ app. This way this app can work with any other kind of modules with this particular alias.
Now I can receive a data each 10ms that I can use to control my alarm from a module called lock.
In my reception callback I have to deal differently whether the data come from a button or not, because buttons are (most of the time) mono-stable. I need to detect a push event in order to control the alarm, and with a mono-stable button a push give me 0->1->0. In this App we want to switch the state and not go back to the default state (0) each time, so I have to get the 0->1->0 event to switch the state of a variable.
If the sender is not a button, I can take the state without any filtering:

void rx_start_controller_cb(module_t *module, msg_t *msg)
{
    if (msg->header.cmd == IO_STATE)
    {
        if (control_mode.mode_control == PLAY)
        {
            if (type_from_id(msg->header.source) == STATE_MOD) 
            {
                // this is the button reply, we have filter it to manage mono-stability
                if ((!last_btn_state) & (last_btn_state != msg->data[0]))
                {
                    lock = (!lock);
                    state_switch++;
                }
            }
            else
            {
                // this is an already filtered information
                if ((last_btn_state != msg->data[0]))
                {
                    lock = msg->data[0];
                    if (!lock)
                    {
                        state_switch++;
                    }
                }
            }
            last_btn_state = msg->data[0];
        }
        return;
    }
    if (msg->header.cmd == CONTROL)
    {
        control_mode.unmap = msg->data[0];
        return;
    }
}

Now I have all I need to shut down the alarm app at each button push and turn the green LED to display the alarm state on the loop:

void rx_start_controller_cb(module_t *module, msg_t *msg)
{
    if (msg->header.cmd == IO_STATE)
    {
        if (control_mode.mode_control == PLAY)
        {
            if (type_from_id(msg->header.source) == BUTTON_MOD) 
            {
                // this is the button reply, we have filter it to manage mono-stability
                if ((!last_btn_state) & (last_btn_state != msg->data[0]))
                {
                    lock = (!lock);
                    state_switch++;
                }
            }
            else
            {
                // this is an already filtered information
                if ((last_btn_state != msg->data[0]))
                {
                    lock = msg->data[0];
                    if (!lock)
                    {
                        state_switch++;
                    }
                }
            }
            last_btn_state = msg->data[0];
        }
        return;
    }
    if (msg->header.cmd == CONTROL)
    {
        control_mode.unmap = msg->data[0];
        return;
    }
}

Now to finish this app, I have to put it into a project. I will put this module in the button board so I 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 !

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

<< Previous (Step 2: Make our alarm adaptable and hot-pluggable) | Next (Step 4: Make our alarm evolve using a truck horn and more) >>