Skip to content

Commit

Permalink
modules: trigger: Add SMF support
Browse files Browse the repository at this point in the history
Add SMF support

Signed-off-by: Simen S. Røstad <[email protected]>
  • Loading branch information
simensrostad committed Jun 7, 2024
1 parent d894d92 commit a230940
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 35 deletions.
2 changes: 2 additions & 0 deletions app/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ CONFIG_ZBUS=y
CONFIG_ZBUS_MSG_SUBSCRIBER=y
CONFIG_ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_SIZE=32
CONFIG_SMF=y
CONFIG_SMF_ANCESTOR_SUPPORT=y
CONFIG_SMF_INITIAL_TRANSITION=y

# Location
CONFIG_LOCATION=y
Expand Down
8 changes: 8 additions & 0 deletions app/src/common/message_channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,11 @@ ZBUS_CHAN_DEFINE(CLOUD_CHAN,
ZBUS_OBSERVERS(fota, app, location, trigger),
CLOUD_DISCONNECTED
);

ZBUS_CHAN_DEFINE(BUTTON_CHAN,
uint8_t,
NULL,
NULL,
ZBUS_OBSERVERS(trigger),
ZBUS_MSG_INIT(0)
);
3 changes: 2 additions & 1 deletion app/src/common/message_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ ZBUS_CHAN_DECLARE(
LED_CHAN,
CLOUD_CHAN,
FOTA_ONGOING_CHAN,
CONFIG_CHAN
CONFIG_CHAN,
BUTTON_CHAN
);

#ifdef __cplusplus
Expand Down
190 changes: 156 additions & 34 deletions app/src/modules/trigger/trigger.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,101 +9,223 @@
#include <zephyr/zbus/zbus.h>
#include <zephyr/task_wdt/task_wdt.h>
#include <dk_buttons_and_leds.h>
#include <zephyr/smf.h>

#include "message_channel.h"

/* Register log module */
LOG_MODULE_REGISTER(trigger, CONFIG_APP_TRIGGER_LOG_LEVEL);

#define MSG_SEND_TIMEOUT_SECONDS 1
#define REAL_TIME_TRIGGER_INTERVAL 10

/* Forward declarations */
static void trigger_work_fn(struct k_work *work);
static const struct smf_state states[];

/* Delayable work used to schedule triggers. */
static K_WORK_DELAYABLE_DEFINE(trigger_work, trigger_work_fn);

/* Set default update interval */
k_timeout_t update_interval = K_SECONDS(CONFIG_APP_TRIGGER_TIMEOUT_SECONDS);
enum state {
STATE_INIT,
STATE_CONNECTED,
STATE_DISCONNECTED
};

/* User defined state object.
* Used to transfer data between state changes.
*/
static struct s_object {
/* This must be first */
struct smf_ctx ctx;

/* Last channel type that a message was received on */
const struct zbus_channel *chan;

/* Set default update interval */
k_timeout_t update_interval;

/* Button number */
uint8_t button_number;

/* Cloud status */
enum cloud_status status;

/* Time in real-time mode */
int64_t time_in_real_time_mode_ms;

} state_object;

/* Delayed work used to schedule triggers */
static void trigger_work_fn(struct k_work *work)
{
int err;
int not_used = -1;
bool fota_ongoing = true;

err = zbus_chan_read(&FOTA_ONGOING_CHAN, &fota_ongoing, K_NO_WAIT);
LOG_DBG("Sending trigger message");

err = zbus_chan_pub(&TRIGGER_CHAN, &not_used, K_SECONDS(1));
if (err) {
LOG_ERR("zbus_chan_read, error: %d", err);
LOG_ERR("zbus_chan_pub, error: %d", err);
SEND_FATAL_ERROR();
return;
}
}

if (fota_ongoing) {
LOG_DBG("FOTA ongoing, skipping trigger message");
return;
/* Button handler that can be used to send asyncronous triggers */
static void button_handler(uint32_t button_states, uint32_t has_changed)
{
int err;
uint8_t button_number = 1;

if (has_changed & button_states & DK_BTN1_MSK) {
LOG_DBG("Button 1 pressed!");

err = zbus_chan_pub(&BUTTON_CHAN, &button_number, K_SECONDS(1));
if (err) {
LOG_ERR("zbus_chan_pub, error: %d", err);
SEND_FATAL_ERROR();
return;
}
}
}

LOG_DBG("Sending trigger message");
/* Zephyr State Machine framework handlers */

/* STATE_INIT */

static void init_entry(void *o)
{
ARG_UNUSED(o);

int err = dk_buttons_init(button_handler);

err = zbus_chan_pub(&TRIGGER_CHAN, &not_used, K_SECONDS(MSG_SEND_TIMEOUT_SECONDS));
if (err) {
LOG_ERR("zbus_chan_pub, error: %d", err);
LOG_ERR("dk_buttons_init, error: %d", err);
SEND_FATAL_ERROR();
return;
}
}

static void init_run(void *o)
{
struct s_object *user_object = o;

k_work_reschedule(&trigger_work, update_interval);
if ((user_object->status == CLOUD_CONNECTED) && user_object->chan == &CLOUD_CHAN) {
smf_set_state(SMF_CTX(&state_object), &states[STATE_CONNECTED]);
return;
}
}

static void button_handler(uint32_t button_states, uint32_t has_changed)
/* STATE_CONNECTED */

static void connected_init(void *o)
{
struct s_object *user_object = o;

k_work_reschedule(&trigger_work, user_object->update_interval);
}

static void connected_run(void *o)
{
if (has_changed & button_states) {
ARG_UNUSED(o);

struct s_object *user_object = o;

if ((user_object->status == CLOUD_DISCONNECTED) && user_object->chan == &CLOUD_CHAN) {
smf_set_state(SMF_CTX(&state_object), &states[STATE_DISCONNECTED]);
return;
} else if (user_object->chan == &BUTTON_CHAN) {
LOG_DBG("Button %d pressed in connected state", user_object->button_number);
trigger_work_fn(NULL);
}
}

/* STATE_DISCONNECTED */

static void diconnected_init(void *o)
{
ARG_UNUSED(o);

k_work_cancel_delayable(&trigger_work);
}

static void disconnected_run(void *o)
{
ARG_UNUSED(o);

struct s_object *user_object = o;

if ((user_object->status == CLOUD_CONNECTED) && user_object->chan == &CLOUD_CHAN) {
smf_set_state(SMF_CTX(&state_object), &states[STATE_CONNECTED]);
return;
}
}

/* Construct state table */
static const struct smf_state states[] = {
[STATE_INIT] = SMF_CREATE_STATE(
init_entry, init_run, NULL, NULL, NULL
),
[STATE_CONNECTED] = SMF_CREATE_STATE(
connected_init, connected_run, NULL, NULL, NULL
),
[STATE_DISCONNECTED] = SMF_CREATE_STATE(
diconnected_init, disconnected_run, NULL, NULL, NULL
)
};

/* Function called when there is a message received on a channel that the module listens to */
void trigger_callback(const struct zbus_channel *chan)
{
int err;

if (&CONFIG_CHAN == chan) {
/* Get update interval configuration from channel. */
const struct configuration *config = zbus_chan_const_msg(chan);

if (config->config_present == false) {
LOG_DBG("Configuration not present");
return;
}

LOG_DBG("New update interval: %lld", config->update_interval);
state_object.chan = chan;
state_object.update_interval = K_SECONDS(config->update_interval);
}

update_interval = K_SECONDS(config->update_interval);
if (&CLOUD_CHAN == chan) {
const enum cloud_status *status = zbus_chan_const_msg(chan);

/* Reschedule work */
k_work_reschedule(&trigger_work, update_interval);
state_object.chan = chan;
state_object.status = *status;
}

if (&CLOUD_CHAN == chan) {
LOG_DBG("Cloud connection status received");
if (&BUTTON_CHAN == chan) {
const int *button_number = zbus_chan_const_msg(chan);

const enum cloud_status *status = zbus_chan_const_msg(chan);
state_object.chan = chan;
state_object.button_number = *button_number;
}

if (*status == CLOUD_CONNECTED) {
LOG_DBG("Cloud connected, starting trigger");
k_work_reschedule(&trigger_work, update_interval);
} else {
LOG_DBG("Cloud disconnected, stopping trigger");
k_work_cancel_delayable(&trigger_work);
}
/* State object updated, run SMF */
err = smf_run_state(SMF_CTX(&state_object));
if (err) {
LOG_ERR("smf_run_state, error: %d", err);
SEND_FATAL_ERROR();
return;
}
}

ZBUS_LISTENER_DEFINE(trigger, trigger_callback);

static int trigger_init(void)
{
__ASSERT((dk_buttons_init(button_handler) == 0), "dk_buttons_init failure");
/* Set default update interval */
state_object.update_interval = K_SECONDS(CONFIG_APP_TRIGGER_TIMEOUT_SECONDS);

smf_set_initial(SMF_CTX(&state_object), &states[STATE_INIT]);

return 0;
}

SYS_INIT(trigger_init, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY);
/* Define a ZBUS listener for this module */
ZBUS_LISTENER_DEFINE(trigger, trigger_callback);

/* Initialize module at SYS_INIT() */
SYS_INIT(trigger_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

0 comments on commit a230940

Please sign in to comment.