refactor: State Machine Refinement 1

This commit is contained in:
Frederik Beimgraben 2025-07-03 04:13:27 +02:00
parent aac5c5a44c
commit dc6198f587
2 changed files with 545 additions and 305 deletions

View File

@ -1,40 +1,90 @@
/* File: state_machine.c */ /* File: state_machine.c */
/** /**
* @file state_machine.c * @file state_machine.c
* @brief Statemachine for the Core-Program * @brief State machine implementation for the beverage dispenser core program.
* *
* ... * Tracks pertype stock levels, enforces stock limits on orders,
* and provides an * menu in IDLE to edit stock via the keypad.
* *
* @authors * @authors
* Frederik Beimgraben * Frederik Beimgraben
* Minh Dan Cam * Minh Dan Cam
* Luis Meyer * Luis Meyer
* @date 2025-07-02 * @date 2025-07-03
*/ */
#include <stdbool.h> #include <stdbool.h>
#include <msp430.h> #include <stdint.h>
#include <driverlib.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <msp430.h>
#include "driverlib.h"
#include "keypad.h" #include "keypad.h"
#include "lcd.h" #include "lcd.h"
#include "morse.h"
#include "door_sensor.h" #include "door_sensor.h"
#include "state_machine.h" #include "state_machine.h"
#include "timer.h" #include "timer.h"
volatile char keypad_last_key = '\0'; /* ——— Configurable Defaults ———————————————————————————————— */
volatile bool keypad_flag_chg = false; #define INITIAL_STOCK_DEFAULT 20U /**< Default stock for each beverage */
#define MAX_STOCK 255U /**< Maximum stock representable */
volatile bool allow_open = false; /* ——— Internal State Variables —————————————————————————————— */
/* Function Declarations */ /** Last key pressed on the keypad. */
void handle_interrupt(void); volatile char keypad_last_key = '\0';
void buzz_invalid_input(void); /** Flag set when a new key is ready to handle. */
void sound_alarm(void); volatile bool keypad_flag_chg = false;
void reset_alarm(void); /** True if the door is currently open. */
void transition(State_t state); volatile bool door_open = false;
/** True if the state has just changed and UI needs redrawing. */
volatile bool state_chg = false;
/** Current UI state. */
volatile State_t current_state = STATE_IDLE;
/** Beverage currently selected either for ordering or editing stock. */
volatile SelectedBev_t selected_bev = BEV_A;
/** Order counts (persession) for beverages AD. */
volatile uint8_t count_a = 0;
volatile uint8_t count_b = 0;
volatile uint8_t count_c = 0;
volatile uint8_t count_d = 0;
/** Stock levels for beverages AD. */
volatile uint8_t stock_a = INITIAL_STOCK_DEFAULT;
volatile uint8_t stock_b = INITIAL_STOCK_DEFAULT;
volatile uint8_t stock_c = INITIAL_STOCK_DEFAULT;
volatile uint8_t stock_d = INITIAL_STOCK_DEFAULT;
/** Temporary buffer for building a new stock value. */
volatile uint8_t new_stock_count = 0;
/** LCD line buffers. */
char buffer_ab[17];
char buffer_cd[17];
/* ——— Function Prototypes ———————————————————————————————— */
/* Keypad input handlers */
void handle_input_idle(unsigned char key);
void handle_input_sel_count(unsigned char key);
void handle_input_confirmed(unsigned char key);
void handle_input_edit_stock_select(unsigned char key);
void handle_input_edit_stock_set(unsigned char key);
/* Utility */
bool any_selected(void);
void transition(State_t next);
/* Orderentry */
void update_amount(uint8_t digit);
void reset_amount(void);
void reset_amounts(void);
/* Stockediting */
void update_new_stock(uint8_t digit);
void reset_new_stock(void);
/* UI drawing */
void ui_draw_idle(void); void ui_draw_idle(void);
void ui_draw_sel_count(void); void ui_draw_sel_count(void);
void ui_draw_confirmed(void); void ui_draw_confirmed(void);
@ -42,144 +92,59 @@ void ui_draw_error(void);
void ui_draw_open(void); void ui_draw_open(void);
void ui_draw_none_selected(void); void ui_draw_none_selected(void);
void ui_draw_reset(void); void ui_draw_reset(void);
void reset_amount(); void ui_draw_stock_select(void);
void reset_amounts(); void ui_draw_stock_set(void);
bool any_selected(void); void ui_draw_stock_confirmed(void);
volatile State_t current_state = STATE_IDLE; /* Error/signal feedback */
volatile bool state_chg = false; void buzz_invalid_input(void);
volatile uint8_t count_a = 0; /* Door callbacks */
volatile uint8_t count_b = 0; static void door_opened_handler(void);
volatile uint8_t count_c = 0; static void door_closed_handler(void);
volatile uint8_t count_d = 0;
volatile SelectedBev_t selected_bev = BEV_A;
volatile bool door_open = false; /* Keypad Callback */
static void keypad_handler(char key);
void handle_input_idle(unsigned char key) { /* General interrupt dispatcher */
switch (key) { void handle_general_interrupt(void);
case 'A':
selected_bev = BEV_A; /* ——— Implementation ————————————————————————————————————— */
transition(STATE_SEL_COUNT);
break; /**
case 'B': * @brief Initialize peripherals and draw the initial UI.
selected_bev = BEV_B; */
transition(STATE_SEL_COUNT); void sm_init(void)
break; {
case 'C': /* register keypad + door callbacks */
selected_bev = BEV_C; keypad_init(/* callback = */ keypad_handler);
transition(STATE_SEL_COUNT); door_init(door_opened_handler, door_closed_handler);
break;
case 'D': /* start in IDLE with default stocks */
selected_bev = BEV_D; reset_amounts();
transition(STATE_SEL_COUNT); reset_new_stock();
break; stock_a = stock_b = stock_c = stock_d = INITIAL_STOCK_DEFAULT;
default:
buzz_invalid_input(); ui_draw_idle();
}
} }
void handle_input_confirmed(unsigned char key) { /**
switch (key) { * @brief Main event loop: lowpower + interrupts.
case 'A': */
selected_bev = BEV_A; void sm_loop(void)
transition(STATE_SEL_COUNT); {
break; for (;;) {
case 'B': __bis_SR_register(LPM0_bits | GIE); /* sleep until interrupt */
selected_bev = BEV_B; __no_operation();
transition(STATE_SEL_COUNT); handle_general_interrupt();
break;
case 'C':
selected_bev = BEV_C;
transition(STATE_SEL_COUNT);
break;
case 'D':
selected_bev = BEV_D;
transition(STATE_SEL_COUNT);
break;
case '*':
reset_amounts();
transition(STATE_RESET);
break;
default:
buzz_invalid_input();
}
}
void update_amount(uint8_t new_digit) {
switch (selected_bev) {
case BEV_A:
if (count_a * 10 + new_digit < 255)
count_a = count_a * 10 + new_digit;
break;
case BEV_B:
if (count_b * 10 + new_digit < 255)
count_b = count_b * 10 + new_digit;
break;
case BEV_C:
if (count_c * 10 + new_digit < 255)
count_c = count_c * 10 + new_digit;
break;
case BEV_D:
if (count_d * 10 + new_digit < 255)
count_d = count_d * 10 + new_digit;
break;
}
}
void reset_amount() {
switch (selected_bev) {
case BEV_A:
count_a = 0;
break;
case BEV_B:
count_b = 0;
break;
case BEV_C:
count_c = 0;
break;
case BEV_D:
count_d = 0;
break;
}
}
void reset_amounts() {
count_a = 0;
count_b = 0;
count_c = 0;
count_d = 0;
}
void handle_input_sel_count(unsigned char key) {
// Check if input is number
if (key >= '0' && key <= '9') {
update_amount((uint8_t) key - '0');
transition(current_state);
} else if (key == '#') {
if (door_open) {
transition(STATE_OPEN);
} else {
if (!any_selected()) {
transition(STATE_NONE_SELECTED);
} else {
transition(STATE_CONFIRMED);
}
}
} else if (key == '*') {
reset_amount();
transition(current_state);
} else {
handle_input_idle(key);
} }
} }
/** /**
* @brief Keypad callback invoked on key press interrupt. * @brief Keypad interrupt handler.
* @param key ASCII character of pressed key * @param key ASCII code of the pressed key.
* *
* Stores key, sets ready flag, and blinks Morse code on LED. * Routes key to the correct statespecific handler.
*/ */
static void keypad_handler(char key) static void keypad_handler(char key)
{ {
@ -194,183 +159,443 @@ static void keypad_handler(char key)
handle_input_sel_count(key); handle_input_sel_count(key);
break; break;
case STATE_CONFIRMED: case STATE_CONFIRMED:
handle_input_confirmed(key);
break;
case STATE_OPEN: case STATE_OPEN:
handle_input_confirmed(key); handle_input_confirmed(key);
break; break;
case STATE_EDIT_STOCK_SELECT:
handle_input_edit_stock_select(key);
break;
case STATE_EDIT_STOCK_SET:
handle_input_edit_stock_set(key);
break;
case STATE_EDIT_STOCK_CONFIRMED:
/* any key closes confirmation */
transition(STATE_IDLE);
break;
default: default:
buzz_invalid_input(); buzz_invalid_input();
break;
} }
} }
bool any_selected(void) { /**
return count_a + count_b + count_c + count_d > 0; * @brief Handle keypress when in STATE_IDLE.
* @param key ASCII of pressed key.
*
* AD start ordering that beverage.
* * enters stockediting menu.
*/
void handle_input_idle(unsigned char key)
{
switch (key) {
case 'A': case 'B': case 'C': case 'D':
selected_bev = (SelectedBev_t)(key - 'A');
transition(STATE_SEL_COUNT);
break;
case '*':
reset_new_stock();
transition(STATE_EDIT_STOCK_SELECT);
break;
default:
buzz_invalid_input();
break;
}
} }
static void door_opened_handler(void) { /**
if (current_state != STATE_CONFIRMED && current_state != STATE_UNLOCKED) { * @brief Handle keypress when selecting order quantity.
* @param key ASCII of pressed key.
*
* Digits build count (up to stock), # confirms, * clears,
* anything else falls back to idlehandler.
*/
void handle_input_sel_count(unsigned char key)
{
if (key >= '0' && key <= '9') {
update_amount(key - '0');
transition(STATE_SEL_COUNT);
}
else if (key == '#') {
if (door_open) {
transition(STATE_OPEN);
} else if (!any_selected()) {
transition(STATE_NONE_SELECTED);
} else {
/* deduct stock before confirming */
switch (selected_bev) {
case BEV_A: stock_a -= count_a; break;
case BEV_B: stock_b -= count_b; break;
case BEV_C: stock_c -= count_c; break;
case BEV_D: stock_d -= count_d; break;
}
transition(STATE_CONFIRMED);
}
}
else if (key == '*') {
reset_amount();
transition(STATE_SEL_COUNT);
}
else {
handle_input_idle(key);
}
}
/**
* @brief Handle keypress in STATE_CONFIRMED (and STATE_OPEN).
* @param key ASCII of pressed key.
*
* AD reselect beverage for new order.
* * resets all order counts.
*/
void handle_input_confirmed(unsigned char key)
{
switch (key) {
case 'A': case 'B': case 'C': case 'D':
selected_bev = (SelectedBev_t)(key - 'A');
transition(STATE_SEL_COUNT);
break;
case '*':
reset_amounts();
transition(STATE_RESET);
break;
default:
buzz_invalid_input();
break;
}
}
/**
* @brief Handle keypress in STATE_EDIT_STOCK_SELECT.
* @param key ASCII pressed.
*
* AD pick which stock to edit, * cancels to IDLE.
*/
void handle_input_edit_stock_select(unsigned char key)
{
switch (key) {
case 'A': case 'B': case 'C': case 'D':
selected_bev = (SelectedBev_t)(key - 'A');
reset_new_stock();
transition(STATE_EDIT_STOCK_SET);
break;
case '*':
transition(STATE_IDLE);
break;
default:
buzz_invalid_input();
break;
}
}
/**
* @brief Handle keypress in STATE_EDIT_STOCK_SET.
* @param key ASCII pressed.
*
* Digits build new stock, # confirms update, * cancels.
*/
void handle_input_edit_stock_set(unsigned char key)
{
if (key >= '0' && key <= '9') {
update_new_stock(key - '0');
transition(STATE_EDIT_STOCK_SET);
}
else if (key == '#') {
/* apply new stock */
switch (selected_bev) {
case BEV_A: stock_a = new_stock_count; break;
case BEV_B: stock_b = new_stock_count; break;
case BEV_C: stock_c = new_stock_count; break;
case BEV_D: stock_d = new_stock_count; break;
}
transition(STATE_EDIT_STOCK_CONFIRMED);
}
else if (key == '*') {
reset_new_stock();
transition(STATE_EDIT_STOCK_SELECT);
}
else {
buzz_invalid_input();
}
}
/**
* @brief Update the order count for selected beverage.
* @param digit 09 to append.
*
* Ensures you never exceed the stock level.
*/
void update_amount(uint8_t digit)
{
uint16_t tentative;
switch (selected_bev) {
case BEV_A:
tentative = count_a * 10 + digit;
if (tentative <= stock_a) count_a = (uint8_t)tentative;
else buzz_invalid_input();
break;
case BEV_B:
tentative = count_b * 10 + digit;
if (tentative <= stock_b) count_b = (uint8_t)tentative;
else buzz_invalid_input();
break;
case BEV_C:
tentative = count_c * 10 + digit;
if (tentative <= stock_c) count_c = (uint8_t)tentative;
else buzz_invalid_input();
break;
case BEV_D:
tentative = count_d * 10 + digit;
if (tentative <= stock_d) count_d = (uint8_t)tentative;
else buzz_invalid_input();
break;
}
}
/**
* @brief Reset the order count for the currently selected beverage.
*/
void reset_amount(void)
{
switch (selected_bev) {
case BEV_A: count_a = 0; break;
case BEV_B: count_b = 0; break;
case BEV_C: count_c = 0; break;
case BEV_D: count_d = 0; break;
}
}
/**
* @brief Reset all order counts to zero.
*/
void reset_amounts(void)
{
count_a = count_b = count_c = count_d = 0;
}
/**
* @brief Build up new stock value from digit.
* @param digit 09 to append.
*
* Caps at MAX_STOCK.
*/
void update_new_stock(uint8_t digit)
{
uint16_t tentative = new_stock_count * 10 + digit;
if (tentative <= MAX_STOCK) {
new_stock_count = (uint8_t)tentative;
} else {
buzz_invalid_input();
}
}
/**
* @brief Reset the new stock entry buffer to zero.
*/
void reset_new_stock(void)
{
new_stock_count = 0;
}
/**
* @brief True if any order count > 0.
* @return bool
*/
bool any_selected(void)
{
return (count_a + count_b + count_c + count_d) > 0;
}
/**
* @brief Perform the visual update when state changes.
*/
void handle_general_interrupt(void)
{
if (!state_chg) return;
switch (current_state) {
case STATE_IDLE: ui_draw_idle(); break;
case STATE_SEL_COUNT: ui_draw_sel_count(); break;
case STATE_CONFIRMED: ui_draw_confirmed(); break;
case STATE_UNAUTHORIZED: ui_draw_error(); break;
case STATE_OPEN: ui_draw_open(); break;
case STATE_NONE_SELECTED: ui_draw_none_selected(); break;
case STATE_RESET: ui_draw_reset(); break;
case STATE_EDIT_STOCK_SELECT: ui_draw_stock_select(); break;
case STATE_EDIT_STOCK_SET: ui_draw_stock_set(); break;
case STATE_EDIT_STOCK_CONFIRMED: ui_draw_stock_confirmed(); break;
default: buzz_invalid_input(); break;
}
state_chg = false;
}
/**
* @brief Transition to a new state (requests a redraw).
* @param next Next state.
*/
void transition(State_t next)
{
current_state = next;
state_chg = true;
}
/* ——— Door Callbacks —————————————————————————————————————— */
/**
* @brief Called when door opens.
*/
static void door_opened_handler(void)
{
if (current_state != STATE_CONFIRMED) {
transition(STATE_UNAUTHORIZED); transition(STATE_UNAUTHORIZED);
} else { } else {
transition(STATE_OPEN); transition(STATE_OPEN);
} }
door_open = true; door_open = true;
} }
static void door_closed_handler(void) { /**
* @brief Called when door closes.
*/
static void door_closed_handler(void)
{
reset_amounts(); reset_amounts();
transition(STATE_IDLE); transition(STATE_IDLE);
door_open = false; door_open = false;
} }
void buzz_invalid_input(void) { /* ——— UI Drawing Functions —————————————————————————————— */
// FIXME: Implement this later
}
void sound_alarm(void) { /**
// FIXME: Implement this later * @brief Draw the IDLE screen.
} */
void ui_draw_idle(void)
void reset_alarm(void) { {
// FIXME: Implement this later
}
void transition(State_t state) {
state_chg = true;
current_state = state;
}
void sm_init(void) {
keypad_init(keypad_handler);
door_init(door_opened_handler, door_closed_handler);
ui_draw_idle();
}
void sm_loop(void) {
for (;;) {
__bis_SR_register(LPM0_bits | GIE); /* Enter LPM0 with interrupts enabled */
__no_operation();
handle_general_interrupt();
}
}
void handle_general_interrupt(void) {
if (state_chg) {
switch (current_state) {
case STATE_IDLE:
ui_draw_idle();
break;
case STATE_SEL_COUNT:
ui_draw_sel_count();
break;
case STATE_CONFIRMED:
ui_draw_confirmed();
break;
case STATE_UNAUTHORIZED:
ui_draw_error();
break;
case STATE_OPEN:
ui_draw_open();
break;
case STATE_NONE_SELECTED:
ui_draw_none_selected();
break;
case STATE_RESET:
ui_draw_reset();
break;
}
state_chg = false;
}
}
char buffer_ab[16];
char buffer_cd[16];
void ui_draw_idle(void) {
lcd_clear(); lcd_clear();
lcd_set_cursor(0, 5); lcd_set_cursor(0,5); lcd_print("Ready!");
lcd_print("Ready!"); lcd_set_cursor(1,0); lcd_print("A-D to order * to edit");
lcd_set_cursor(1, 1);
lcd_print("Press A/B/C/D");
} }
void ui_draw_sel_count(void) { /**
snprintf(buffer_ab, 17, "A:%c%4d B:%c%4d ", * @brief Draw the quantityselection screen.
selected_bev == BEV_A ? '*' : ' ', */
count_a, void ui_draw_sel_count(void)
selected_bev == BEV_B ? '*' : ' ', {
count_b snprintf(buffer_ab, 17, "A:%c%3u B:%c%3u",
); (selected_bev==BEV_A?'*':' '), count_a,
snprintf(buffer_cd, 17, "C:%c%4d D:%c%4d ", (selected_bev==BEV_B?'*':' '), count_b);
selected_bev == BEV_C ? '*' : ' ', snprintf(buffer_cd, 17, "C:%c%3u D:%c%3u",
count_c, (selected_bev==BEV_C?'*':' '), count_c,
selected_bev == BEV_D ? '*' : ' ', (selected_bev==BEV_D?'*':' '), count_d);
count_d
);
lcd_set_cursor(0, 0);
lcd_print(buffer_ab);
lcd_set_cursor(1, 0);
lcd_print(buffer_cd);
}
void ui_draw_confirmed(void) {
lcd_clear(); lcd_clear();
lcd_set_cursor(0, 3); lcd_set_cursor(0,0); lcd_print(buffer_ab);
lcd_print("Confirmed!"); lcd_set_cursor(1,0); lcd_print(buffer_cd);
lcd_set_cursor(1, 1);
lcd_print("OPEN/[A-D]/[*]");
} }
void ui_draw_error(void) { /**
* @brief Draw the order confirmed screen.
*/
void ui_draw_confirmed(void)
{
lcd_clear(); lcd_clear();
lcd_set_cursor(0, 2); lcd_set_cursor(0,3); lcd_print("Confirmed!");
lcd_print("UNAUTHORIZED"); lcd_set_cursor(1,1); lcd_print("Press A-D or *");
lcd_set_cursor(1, 1);
lcd_print("CLOSE DOOR NOW");
} }
void ui_draw_open(void) { /**
* @brief Draw the unauthorizedopen error.
*/
void ui_draw_error(void)
{
lcd_clear(); lcd_clear();
lcd_set_cursor(0, 2); lcd_set_cursor(0,2); lcd_print("UNAUTHORIZED");
lcd_print("DOOR IS OPEN"); lcd_set_cursor(1,1); lcd_print("DOOR OPENED!");
lcd_set_cursor(1, 0);
lcd_print("CLOSE /[A-D]/[*]");
} }
void ui_draw_none_selected(void) { /**
* @brief Draw the door is open screen.
*/
void ui_draw_open(void)
{
lcd_clear(); lcd_clear();
lcd_set_cursor(0, 5); lcd_set_cursor(0,2); lcd_print("DOOR OPEN");
lcd_print("SELECT"); lcd_set_cursor(1,0); lcd_print("Close then *");
lcd_set_cursor(1, 1); }
lcd_print("ANY BEVERAGES!");
/**
* @brief Draw when no beverage was selected at #.
*/
void ui_draw_none_selected(void)
{
lcd_clear();
lcd_set_cursor(0,4); lcd_print("SELECT!");
lcd_set_cursor(1,1); lcd_print("Choose A-D");
sleep(1000); sleep(1000);
transition(STATE_SEL_COUNT); transition(STATE_SEL_COUNT);
ui_draw_sel_count(); ui_draw_sel_count();
} }
void ui_draw_reset(void) { /**
* @brief Transient amounts reset screen.
*/
void ui_draw_reset(void)
{
lcd_clear(); lcd_clear();
lcd_set_cursor(0, 2); lcd_set_cursor(0,2); lcd_print("All Reset");
lcd_print("AMOUNTS HAVE"); lcd_set_cursor(1,1); lcd_print("Ready");
lcd_set_cursor(1, 3);
lcd_print("BEEN RESET");
sleep(1000); sleep(1000);
transition(STATE_IDLE);
ui_draw_idle();
}
if (door_open) { /**
transition(STATE_OPEN); * @brief Draw the stockselection screen.
ui_draw_open(); */
} else { void ui_draw_stock_select(void)
transition(STATE_IDLE); {
ui_draw_idle(); lcd_clear();
lcd_set_cursor(0,3); lcd_print("Edit Stock");
lcd_set_cursor(1,1); lcd_print("A-D:*cancel");
}
/**
* @brief Draw the enter new stock screen.
*/
void ui_draw_stock_set(void)
{
uint8_t cur = 0;
switch (selected_bev) {
case BEV_A: cur = stock_a; break;
case BEV_B: cur = stock_b; break;
case BEV_C: cur = stock_c; break;
case BEV_D: cur = stock_d; break;
} }
} lcd_clear();
snprintf(buffer_ab,17,"%c stock:%3u", 'A'+selected_bev, cur);
lcd_set_cursor(0,0); lcd_print(buffer_ab);
snprintf(buffer_cd,17,"New:%3u *cancel", new_stock_count);
lcd_set_cursor(1,0); lcd_print(buffer_cd);
}
/**
* @brief Draw the stock updated confirmation.
*/
void ui_draw_stock_confirmed(void)
{
lcd_clear();
lcd_set_cursor(0,1); lcd_print("Stock Updated!");
lcd_set_cursor(1,1); lcd_print("Press any key");
}
/* ——— Feedback ——————————————————————————————————————————— */
/**
* @brief Short buzzer tone for invalid input.
*/
void buzz_invalid_input(void)
{
/* stub: beep or flash LED */
}

View File

@ -1,44 +1,59 @@
/* File: state_machine.h */ /* File: state_machine.h */
/** /**
* @file state_machine.h * @file state_machine.h
* @brief Statemachine for the Core-Program * @brief State machine interface for the beverage dispenser core program.
* *
* ... * This module drives a keypadandLCD UI to select beverage types (AD),
* choose quantities, confirm orders, detect door events, and edit stock levels.
* *
* @authors * @author Frederik Beimgraben
* Frederik Beimgraben * @author Minh Dan Cam
* Minh Dan Cam * @author Luis Meyer
* Luis Meyer * @date 2025-07-03
* @date 2025-07-02
*/ */
#ifndef STATE_MACHINE_H #ifndef STATE_MACHINE_H
#define STATE_MACHINE_H #define STATE_MACHINE_H
static void keypad_handler(char key); #include <stdint.h>
#include <stdbool.h>
void sm_init(void);
void sm_loop(void);
/**
* @enum State_t
* @brief All possible states of the UI state machine.
*/
typedef enum { typedef enum {
STATE_IDLE, STATE_IDLE, /**< Waiting for user input */
STATE_SEL_COUNT, STATE_SEL_COUNT, /**< Selecting order quantity */
STATE_CONFIRMED, STATE_CONFIRMED, /**< Order confirmed */
STATE_OPEN, STATE_OPEN, /**< Door is open */
STATE_SECRET_INPUT, STATE_UNAUTHORIZED, /**< Unauthorized door opening */
STATE_UNLOCKED, STATE_NONE_SELECTED, /**< No beverage selected at confirm */
STATE_UNAUTHORIZED, STATE_RESET, /**< Transient “reset” display */
STATE_NONE_SELECTED, STATE_EDIT_STOCK_SELECT, /**< Select which beverages stock to edit */
STATE_RESET STATE_EDIT_STOCK_SET, /**< Enter new stock value */
STATE_EDIT_STOCK_CONFIRMED /**< Stock update confirmed */
} State_t; } State_t;
/**
* @enum SelectedBev_t
* @brief Identifiers for the four beverage types.
*/
typedef enum { typedef enum {
BEV_A, BEV_A, /**< Beverage A */
BEV_B, BEV_B, /**< Beverage B */
BEV_C, BEV_C, /**< Beverage C */
BEV_D BEV_D /**< Beverage D */
} SelectedBev_t; } SelectedBev_t;
#endif // STATE_MACHINE_H /**
* @brief Initialize the state machine, keypad, door sensor, and LCD.
*/
void sm_init(void);
/**
* @brief Enter the statemachines main loop (lowpower + interrupts).
*/
void sm_loop(void);
#endif // STATE_MACHINE_H