PretoWatch (Part 5) State Machine

Last time we discussed button handling, and now we’re moving on to implementing the state machine controlled by user button inputs.

Here are all possible states for PretoWatch:

In the diagram, all arrows represent a short press of Button 2. A long press of Button 2 on the Main screen leads to the Setting Menu, while in other states, it brings us back to the Main screen. Button 1 and Button 3 short presses are used for navigating the menu or toggling ON/OFF states.

For this project, multiple state machines are nested within each other to decide what display content to show and what actions to trigger when the user presses buttons in specific states.

To implement the current state for the UI, we use the following definition:

typedef enum {
	UI_HOME, UI_MENU, UI_SET_TIME, UI_SET_ALARM, UI_SET_VIBRATION, UI_LENGTH
} UIState;

UIState currentState;

Then, in the Partial Update Task loop:

void StartPartialUpdateTask(void const * argument)
{
  /* USER CODE BEGIN 5 */
	/* Infinite loop */
	// enter partial mode
	for (;;) {
		switch (currentState) {
		case UI_HOME:
			displayHomeScreen();
			break;
		case UI_MENU:
			// Navigate to the first menu item
			displayMenuScreen();
			break;
		case UI_SET_TIME:
			// Handle setting time
			displaySetTimeScreen();
			break;
		case UI_SET_ALARM:
			// Handle setting alarm
			displaySetAlarmScreen();
			break;
		case UI_SET_VIBRATION:
			// Handle setting vibration
			displaySetVibrationScreen();
			break;
		default:
			break;
		}
		osDelay(50);
	}
  /* USER CODE END 5 */
}

We show respective content according to the current UIState.

To change the current UIState, we can use the following:

void handleButton1Press() {
...
	case UI_MENU:
		// Navigate to the first menu item
		currentMenuState = (currentMenuState + 1) % M_SET_LENGTH;
...
}

void handleButton3Press() {
...
	case UI_MENU:
		// Navigate to the first menu item
		currentMenuState = (currentMenuState - 1 + M_SET_LENGTH) % M_SET_LENGTH;
...
}

void handleButton2Press() {
...
	switch (currentState) {
	case UI_MENU:
		// Navigate to the first menu item
		switch (currentMenuState) {
		case M_SET_TIME:
			currentState = UI_SET_TIME;
			currentSetTimeState = ST_HOUR;
			break;
		case M_SET_ALARM:
			currentState = UI_SET_ALARM;
			currentSetAlarmState = SA_ALLALARMS;
			break;
		case M_SET_VIBRATION:
			currentState = UI_SET_VIBRATION;
			currentSetVibrateState = SV_ALLVIBRATIONS;
			break;
		case M_EXIT:
			currentState = UI_HOME;
			currentMenuState = M_SET_TIME;
			break;
		default:
			break;
		}
		break;
		...
		
		}

This is a simple state machine, but it works effectively 🙂

Hope you enjoy this short blog. Next time, I will try to finish my watch and wrap up the whole series. Thanks for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *