Button events are not triggering the transitions between states using the state machine framework

I have been trying to program a finite state machine using the zephyr RTOS. I chose this framework because of its extensive libraries and its finite state machine framework. However there is not that many examples out there to show how to use the SM framework correctly. The FSM I am trying to implement is for a booking system for a telematics (the box inside cars) in a car sharing system. Basically this system is supposed to receive a booking object, and this object traverses the booking state machine with mock events triggering the transitions. I chose the mock events to be button presses. I am using the nrf52832 Nordic Board to test the code. In zephyr you define the states based on their entry, run, and exit functions. My problem is that the button presses are not causing the state machine to transition. Basically all my transitions follow the same pattern. I will copy paste the code of the WAITING (see UML state chart for ref.) state's entry and run functions, and the ONGOING state's entry and run functions to show where I am stuck. The WAITING state has been working fine, it is when I arrive to the ONGOING state that it stops working the way it is supposed to.

 

struct booking
{
	struct smf_ctx ctx;
	uint32_t id;
	time_t start_tm;
	time_t end_tm;

	/* events */
	struct k_event isStarting;
	struct k_event DoneWaiting;
	struct k_event StartPressed;
	struct k_event FinishPressed;
	struct k_event isLate;
	struct k_event EngineOn;
	struct k_event EngineOff;
	struct k_event V_isIncrease;
	struct k_event V_isZero;
	struct k_event LocationConfirm;
	int32_t events;
} booking_t;

struct
{
	uint32_t id;
	time_t booking_starting_time;
	time_t booking_ending_time;
	time_t duration;
	time_t delayed_start_time;
	time_t delayed_end_time;
	bool isBooking;
	bool isStarted;
	bool isDoneWaiting;
	bool isStopped;
	bool isLate;
	bool isOn;
	bool isOff;
	bool isMoving;
	bool isStill;
} b_data;

/* IDLE STATE  */

static void idle_entry(void *o)
{
	printk("Entered IDLE state\nNo booking to manage\n\n");
	gpio_pin_toggle_dt(&led1);
}

static void idle_run(void *o)
{
	struct booking *b = (struct booking *)o;

	k_event_wait(&booking_t.isStarting, timer_expiry, true, K_FOREVER);
	b_data.id = b->id;
	b_data.booking_starting_time = b->start_tm;
	b_data.booking_ending_time = b->end_tm;
	b_data.duration = b_data.booking_ending_time - b_data.booking_starting_time;
	b_data.isBooking = true;
	b_data.isStarted = false;
	b_data.isDoneWaiting = false;
	b_data.isStopped = false;
	b_data.isLate = false;
	b_data.isOn = false;
	b_data.isOff = true;
	b_data.isMoving = false;
	b_data.isStill = true;
	smf_set_state(SMF_CTX(&booking_t), &bsm[WAITING]);
}

/* WAITING STATE */

static void waiting_entry(void *o)
{
	printk("Entered WAITING state\nBooking No: %u has started\nDriver has 3 mins to start until booking is discarded\n\n", b_data.id);

	gpio_pin_toggle_dt(&led1);
	gpio_pin_toggle_dt(&led2);

	k_timer_start(&waiting_for_start_press_timer, K_MINUTES(3), K_FOREVER);

	// find a way to timestamp
}

static void waiting_run(void *o)
{
	int ret = k_event_wait(&booking_t.StartPressed, btn1_press, true, K_MINUTES(3));
	if (ret == 0)
	{
		// discardBooking();
		smf_set_state(SMF_CTX(&booking_t), &bsm[IDLE]);
	}
	// unlockDoor();
	smf_set_state(SMF_CTX(&booking_t), &bsm[ONGOING]);
}

/* ONGOING STATE */
static void ongoing_entry(void *o)
{
	printk("Entered ONGOING state\n");

	gpio_pin_toggle_dt(&led2);
	gpio_pin_toggle_dt(&led3);

	k_timer_stop(&waiting_for_start_press_timer);
	k_timer_start(&duration_of_ride_timer, K_SECONDS(30), K_FOREVER);
}

static void ongoing_run(void *o)
{
	printk("\tongoinging run function here\n\n");
	smf_set_state(SMF_CTX(&booking_t), &bsm[ONTIME_PARKING]);
}

/* ONTIME & PARKING STATE */

static void ontime_parking_entry(void *o)
{
	printk("Entered ONTIME_PARKING state\n");
}

static void ontime_parking_run(void *o)
{
	struct booking *b = (struct booking *)o;

	k_msleep(10000);
	if (b->events & btn1_press)
	{
		printk("\t\tbtn1_press event\n");
		smf_set_state(SMF_CTX(&booking_t), &bsm[FINISHING]);
	}
	else if (b->events & btn2_press)
	{
		printk("\t\tbtn2_press event\n");
		smf_set_state(SMF_CTX(&booking_t), &bsm[ONTIME_STATIONARY]);
	}
}

/*ONTIME AND STATIONARY */

static void ontime_stationary_entry(void *o)
{
	printk("Entered ONTIME_STATIONARY state\n");
}

static void ontime_stationary_run(void *o)
{
	struct booking *b = (struct booking *)o;

	while (!b_data.isLate)
	{
		if (b->events & btn2_press)
		{
			smf_set_state(SMF_CTX(&booking_t), &bsm[ONTIME_PARKING]);
		}
		else if (b->events & btn3_press)
		{
			smf_set_state(SMF_CTX(&booking_t), &bsm[ONTIME_MOVING]);
		}
		else if (b->events & btn1_press)
		{
			smf_set_state(SMF_CTX(&booking_t), &bsm[FINISHING]);
		}
	}
	smf_set_state(SMF_CTX(&booking_t), &bsm[DELAYED_STATIONARY]);
}

/* ONTIME & MOVING STATE */
static void ontime_moving_entry(void *o)
{
	printk("Entered ONTIME_MOVING state\n");
}

static void ontime_moving_run(void *o)
{
	struct booking *b = (struct booking *)o;

	while (!b_data.isLate)
	{
		if (b->events & btn3_press)
		{
			smf_set_state(SMF_CTX(&booking_t), &bsm[ONTIME_STATIONARY]);
		}
		else if (b->events & btn1_press)
		{
			smf_set_state(SMF_CTX(&booking_t), &bsm[FINISHING]);
		}
	}
	smf_set_state(SMF_CTX(&booking_t), &bsm[DELAYED_MOVING]);
}

I essentially would need some help with the following:

  1. How to make the transitions happen with the button press event, which mocks the StartPressed event by the driver (can also be seen in the UML statechart)
  2. How to pass a booking object to the state machine. I should mention that I trigger the start of the ride by defining a thread that enters in a function that posts the event isStarting, triggering the transition from the IDLE to the WAITING state, but I would like to treat this thread as the booking itself such that it starts in 3 minutes after boot-up time, has an id, and a ending time.

What I expeted from following the event-driven example given in the Zephyr website, was for the if statement (b->events & btn1_press) to be entered when the event was posted which is when the button 1 is pressed. I am not entirely sure how the state machine framework works, but given that the states are defined with the entry, run and exit actions, I thought that the run function is what is continuously being executed, so basically when the button is pressed, the callback function posts the event to the address btn1_press and then the if statement will be entered and transition to the corresponding state. What is actually happening is that the if statement is not being entered at all in the first place.

The following link has the state chart for the booking state machine: 

  • Hi,

    This sounds like a more general "FSM/FSM in Zephyr question" ,so I recommend you to also reach out to the Zephyr forums on Discord to see if anyone in the forums there has any experiences with what you're developing. You will find a link to their discord at https://www.zephyrproject.org/community/ 

     

    I will also leave this case as open in case there are any other users of DevZone who wants to chime in on your project, but as mentioned I think you have better luck on the Zephyr forums.

    I will also update you if I find anything for your project, unfortunately this is the best I can do for you as of now

    Kind regards,
    Andreas

Related