refactor: State Machine Refinement 1
This commit is contained in:
parent
aac5c5a44c
commit
dc6198f587
@ -1,40 +1,90 @@
|
||||
/* 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 per‐type stock levels, enforces stock limits on orders,
|
||||
* and provides an “*” menu in IDLE to edit stock via the keypad.
|
||||
*
|
||||
* @authors
|
||||
* Frederik Beimgraben
|
||||
* Minh Dan Cam
|
||||
* Luis Meyer
|
||||
* @date 2025-07-02
|
||||
* @date 2025-07-03
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <msp430.h>
|
||||
#include <driverlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <msp430.h>
|
||||
#include "driverlib.h"
|
||||
#include "keypad.h"
|
||||
#include "lcd.h"
|
||||
#include "morse.h"
|
||||
#include "door_sensor.h"
|
||||
#include "state_machine.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* ——— Configurable Defaults ———————————————————————————————— */
|
||||
#define INITIAL_STOCK_DEFAULT 20U /**< Default stock for each beverage */
|
||||
#define MAX_STOCK 255U /**< Maximum stock representable */
|
||||
|
||||
/* ——— Internal State Variables —————————————————————————————— */
|
||||
|
||||
/** Last key pressed on the keypad. */
|
||||
volatile char keypad_last_key = '\0';
|
||||
/** Flag set when a new key is ready to handle. */
|
||||
volatile bool keypad_flag_chg = false;
|
||||
/** True if the door is currently open. */
|
||||
volatile bool door_open = false;
|
||||
/** True if the state has just changed and UI needs redrawing. */
|
||||
volatile bool state_chg = false;
|
||||
|
||||
volatile bool allow_open = 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;
|
||||
|
||||
/* Function Declarations */
|
||||
void handle_interrupt(void);
|
||||
void buzz_invalid_input(void);
|
||||
void sound_alarm(void);
|
||||
void reset_alarm(void);
|
||||
void transition(State_t state);
|
||||
/** Order counts (per‐session) for beverages A–D. */
|
||||
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 A–D. */
|
||||
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);
|
||||
|
||||
/* Order‐entry */
|
||||
void update_amount(uint8_t digit);
|
||||
void reset_amount(void);
|
||||
void reset_amounts(void);
|
||||
|
||||
/* Stock‐editing */
|
||||
void update_new_stock(uint8_t digit);
|
||||
void reset_new_stock(void);
|
||||
|
||||
/* UI drawing */
|
||||
void ui_draw_idle(void);
|
||||
void ui_draw_sel_count(void);
|
||||
void ui_draw_confirmed(void);
|
||||
@ -42,144 +92,59 @@ void ui_draw_error(void);
|
||||
void ui_draw_open(void);
|
||||
void ui_draw_none_selected(void);
|
||||
void ui_draw_reset(void);
|
||||
void reset_amount();
|
||||
void reset_amounts();
|
||||
bool any_selected(void);
|
||||
void ui_draw_stock_select(void);
|
||||
void ui_draw_stock_set(void);
|
||||
void ui_draw_stock_confirmed(void);
|
||||
|
||||
volatile State_t current_state = STATE_IDLE;
|
||||
volatile bool state_chg = false;
|
||||
/* Error/signal feedback */
|
||||
void buzz_invalid_input(void);
|
||||
|
||||
volatile uint8_t count_a = 0;
|
||||
volatile uint8_t count_b = 0;
|
||||
volatile uint8_t count_c = 0;
|
||||
volatile uint8_t count_d = 0;
|
||||
volatile SelectedBev_t selected_bev = BEV_A;
|
||||
/* Door callbacks */
|
||||
static void door_opened_handler(void);
|
||||
static void door_closed_handler(void);
|
||||
|
||||
volatile bool door_open = false;
|
||||
/* Keypad Callback */
|
||||
static void keypad_handler(char key);
|
||||
|
||||
void handle_input_idle(unsigned char key) {
|
||||
switch (key) {
|
||||
case 'A':
|
||||
selected_bev = BEV_A;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
case 'B':
|
||||
selected_bev = BEV_B;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
case 'C':
|
||||
selected_bev = BEV_C;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
case 'D':
|
||||
selected_bev = BEV_D;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
default:
|
||||
buzz_invalid_input();
|
||||
}
|
||||
}
|
||||
/* General interrupt dispatcher */
|
||||
void handle_general_interrupt(void);
|
||||
|
||||
void handle_input_confirmed(unsigned char key) {
|
||||
switch (key) {
|
||||
case 'A':
|
||||
selected_bev = BEV_A;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
case 'B':
|
||||
selected_bev = BEV_B;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
case 'C':
|
||||
selected_bev = BEV_C;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
case 'D':
|
||||
selected_bev = BEV_D;
|
||||
transition(STATE_SEL_COUNT);
|
||||
break;
|
||||
case '*':
|
||||
/* ——— Implementation ————————————————————————————————————— */
|
||||
|
||||
/**
|
||||
* @brief Initialize peripherals and draw the initial UI.
|
||||
*/
|
||||
void sm_init(void)
|
||||
{
|
||||
/* register keypad + door callbacks */
|
||||
keypad_init(/* callback = */ keypad_handler);
|
||||
door_init(door_opened_handler, door_closed_handler);
|
||||
|
||||
/* start in IDLE with default stocks */
|
||||
reset_amounts();
|
||||
transition(STATE_RESET);
|
||||
break;
|
||||
default:
|
||||
buzz_invalid_input();
|
||||
}
|
||||
reset_new_stock();
|
||||
stock_a = stock_b = stock_c = stock_d = INITIAL_STOCK_DEFAULT;
|
||||
|
||||
ui_draw_idle();
|
||||
}
|
||||
|
||||
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 Main event loop: low‐power + interrupts.
|
||||
*/
|
||||
void sm_loop(void)
|
||||
{
|
||||
for (;;) {
|
||||
__bis_SR_register(LPM0_bits | GIE); /* sleep until interrupt */
|
||||
__no_operation();
|
||||
handle_general_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Keypad callback invoked on key press interrupt.
|
||||
* @param key ASCII character of pressed key
|
||||
* @brief Keypad interrupt handler.
|
||||
* @param key ASCII code of the pressed key.
|
||||
*
|
||||
* Stores key, sets ready flag, and blinks Morse code on LED.
|
||||
* Routes key to the correct state‐specific handler.
|
||||
*/
|
||||
static void keypad_handler(char key)
|
||||
{
|
||||
@ -194,183 +159,443 @@ static void keypad_handler(char key)
|
||||
handle_input_sel_count(key);
|
||||
break;
|
||||
case STATE_CONFIRMED:
|
||||
handle_input_confirmed(key);
|
||||
break;
|
||||
case STATE_OPEN:
|
||||
handle_input_confirmed(key);
|
||||
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:
|
||||
buzz_invalid_input();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle keypress when in STATE_IDLE.
|
||||
* @param key ASCII of pressed key.
|
||||
*
|
||||
* A–D start ordering that beverage.
|
||||
* ‘*’ enters stock‐editing 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 idle‐handler.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* A–D re‐select 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.
|
||||
*
|
||||
* A–D 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();
|
||||
}
|
||||
}
|
||||
|
||||
bool any_selected(void) {
|
||||
return count_a + count_b + count_c + count_d > 0;
|
||||
/**
|
||||
* @brief Update the order count for selected beverage.
|
||||
* @param digit 0–9 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;
|
||||
}
|
||||
}
|
||||
|
||||
static void door_opened_handler(void) {
|
||||
if (current_state != STATE_CONFIRMED && current_state != STATE_UNLOCKED) {
|
||||
/**
|
||||
* @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 0–9 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);
|
||||
} else {
|
||||
transition(STATE_OPEN);
|
||||
}
|
||||
|
||||
door_open = true;
|
||||
}
|
||||
|
||||
static void door_closed_handler(void) {
|
||||
/**
|
||||
* @brief Called when door closes.
|
||||
*/
|
||||
static void door_closed_handler(void)
|
||||
{
|
||||
reset_amounts();
|
||||
transition(STATE_IDLE);
|
||||
|
||||
door_open = false;
|
||||
}
|
||||
|
||||
void buzz_invalid_input(void) {
|
||||
// FIXME: Implement this later
|
||||
}
|
||||
/* ——— UI Drawing Functions —————————————————————————————— */
|
||||
|
||||
void sound_alarm(void) {
|
||||
// FIXME: Implement this later
|
||||
}
|
||||
|
||||
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) {
|
||||
/**
|
||||
* @brief Draw the IDLE screen.
|
||||
*/
|
||||
void ui_draw_idle(void)
|
||||
{
|
||||
lcd_clear();
|
||||
lcd_set_cursor(0, 5);
|
||||
lcd_print("Ready!");
|
||||
lcd_set_cursor(1, 1);
|
||||
lcd_print("Press A/B/C/D");
|
||||
lcd_set_cursor(0,5); lcd_print("Ready!");
|
||||
lcd_set_cursor(1,0); lcd_print("A-D to order * to edit");
|
||||
}
|
||||
|
||||
void ui_draw_sel_count(void) {
|
||||
snprintf(buffer_ab, 17, "A:%c%4d B:%c%4d ",
|
||||
selected_bev == BEV_A ? '*' : ' ',
|
||||
count_a,
|
||||
selected_bev == BEV_B ? '*' : ' ',
|
||||
count_b
|
||||
);
|
||||
snprintf(buffer_cd, 17, "C:%c%4d D:%c%4d ",
|
||||
selected_bev == BEV_C ? '*' : ' ',
|
||||
count_c,
|
||||
selected_bev == BEV_D ? '*' : ' ',
|
||||
count_d
|
||||
);
|
||||
/**
|
||||
* @brief Draw the quantity‐selection screen.
|
||||
*/
|
||||
void ui_draw_sel_count(void)
|
||||
{
|
||||
snprintf(buffer_ab, 17, "A:%c%3u B:%c%3u",
|
||||
(selected_bev==BEV_A?'*':' '), count_a,
|
||||
(selected_bev==BEV_B?'*':' '), count_b);
|
||||
snprintf(buffer_cd, 17, "C:%c%3u D:%c%3u",
|
||||
(selected_bev==BEV_C?'*':' '), count_c,
|
||||
(selected_bev==BEV_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_set_cursor(0, 3);
|
||||
lcd_print("Confirmed!");
|
||||
lcd_set_cursor(1, 1);
|
||||
lcd_print("OPEN/[A-D]/[*]");
|
||||
lcd_set_cursor(0,0); lcd_print(buffer_ab);
|
||||
lcd_set_cursor(1,0); lcd_print(buffer_cd);
|
||||
}
|
||||
|
||||
void ui_draw_error(void) {
|
||||
/**
|
||||
* @brief Draw the “order confirmed” screen.
|
||||
*/
|
||||
void ui_draw_confirmed(void)
|
||||
{
|
||||
lcd_clear();
|
||||
lcd_set_cursor(0, 2);
|
||||
lcd_print("UNAUTHORIZED");
|
||||
lcd_set_cursor(1, 1);
|
||||
lcd_print("CLOSE DOOR NOW");
|
||||
lcd_set_cursor(0,3); lcd_print("Confirmed!");
|
||||
lcd_set_cursor(1,1); lcd_print("Press A-D or *");
|
||||
}
|
||||
|
||||
void ui_draw_open(void) {
|
||||
/**
|
||||
* @brief Draw the unauthorized‐open error.
|
||||
*/
|
||||
void ui_draw_error(void)
|
||||
{
|
||||
lcd_clear();
|
||||
lcd_set_cursor(0, 2);
|
||||
lcd_print("DOOR IS OPEN");
|
||||
lcd_set_cursor(1, 0);
|
||||
lcd_print("CLOSE /[A-D]/[*]");
|
||||
lcd_set_cursor(0,2); lcd_print("UNAUTHORIZED");
|
||||
lcd_set_cursor(1,1); lcd_print("DOOR OPENED!");
|
||||
}
|
||||
|
||||
void ui_draw_none_selected(void) {
|
||||
/**
|
||||
* @brief Draw the “door is open” screen.
|
||||
*/
|
||||
void ui_draw_open(void)
|
||||
{
|
||||
lcd_clear();
|
||||
lcd_set_cursor(0, 5);
|
||||
lcd_print("SELECT");
|
||||
lcd_set_cursor(1, 1);
|
||||
lcd_print("ANY BEVERAGES!");
|
||||
lcd_set_cursor(0,2); lcd_print("DOOR OPEN");
|
||||
lcd_set_cursor(1,0); lcd_print("Close then *");
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
transition(STATE_SEL_COUNT);
|
||||
ui_draw_sel_count();
|
||||
}
|
||||
|
||||
void ui_draw_reset(void) {
|
||||
/**
|
||||
* @brief Transient “amounts reset” screen.
|
||||
*/
|
||||
void ui_draw_reset(void)
|
||||
{
|
||||
lcd_clear();
|
||||
lcd_set_cursor(0, 2);
|
||||
lcd_print("AMOUNTS HAVE");
|
||||
lcd_set_cursor(1, 3);
|
||||
lcd_print("BEEN RESET");
|
||||
lcd_set_cursor(0,2); lcd_print("All Reset");
|
||||
lcd_set_cursor(1,1); lcd_print("Ready");
|
||||
|
||||
sleep(1000);
|
||||
|
||||
if (door_open) {
|
||||
transition(STATE_OPEN);
|
||||
ui_draw_open();
|
||||
} else {
|
||||
transition(STATE_IDLE);
|
||||
ui_draw_idle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draw the stock‐selection screen.
|
||||
*/
|
||||
void ui_draw_stock_select(void)
|
||||
{
|
||||
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 */
|
||||
}
|
||||
@ -1,44 +1,59 @@
|
||||
/* 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 keypad‐and‐LCD UI to select beverage types (A–D),
|
||||
* choose quantities, confirm orders, detect door events, and edit stock levels.
|
||||
*
|
||||
* @authors
|
||||
* Frederik Beimgraben
|
||||
* Minh Dan Cam
|
||||
* Luis Meyer
|
||||
* @date 2025-07-02
|
||||
* @author Frederik Beimgraben
|
||||
* @author Minh Dan Cam
|
||||
* @author Luis Meyer
|
||||
* @date 2025-07-03
|
||||
*/
|
||||
|
||||
#ifndef STATE_MACHINE_H
|
||||
#define STATE_MACHINE_H
|
||||
|
||||
static void keypad_handler(char key);
|
||||
|
||||
void sm_init(void);
|
||||
|
||||
void sm_loop(void);
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @enum State_t
|
||||
* @brief All possible states of the UI state machine.
|
||||
*/
|
||||
typedef enum {
|
||||
STATE_IDLE,
|
||||
STATE_SEL_COUNT,
|
||||
STATE_CONFIRMED,
|
||||
STATE_OPEN,
|
||||
STATE_SECRET_INPUT,
|
||||
STATE_UNLOCKED,
|
||||
STATE_UNAUTHORIZED,
|
||||
STATE_NONE_SELECTED,
|
||||
STATE_RESET
|
||||
STATE_IDLE, /**< Waiting for user input */
|
||||
STATE_SEL_COUNT, /**< Selecting order quantity */
|
||||
STATE_CONFIRMED, /**< Order confirmed */
|
||||
STATE_OPEN, /**< Door is open */
|
||||
STATE_UNAUTHORIZED, /**< Unauthorized door opening */
|
||||
STATE_NONE_SELECTED, /**< No beverage selected at confirm */
|
||||
STATE_RESET, /**< Transient “reset” display */
|
||||
STATE_EDIT_STOCK_SELECT, /**< Select which beverage’s stock to edit */
|
||||
STATE_EDIT_STOCK_SET, /**< Enter new stock value */
|
||||
STATE_EDIT_STOCK_CONFIRMED /**< Stock update confirmed */
|
||||
} State_t;
|
||||
|
||||
/**
|
||||
* @enum SelectedBev_t
|
||||
* @brief Identifiers for the four beverage types.
|
||||
*/
|
||||
typedef enum {
|
||||
BEV_A,
|
||||
BEV_B,
|
||||
BEV_C,
|
||||
BEV_D
|
||||
BEV_A, /**< Beverage A */
|
||||
BEV_B, /**< Beverage B */
|
||||
BEV_C, /**< Beverage C */
|
||||
BEV_D /**< Beverage D */
|
||||
} SelectedBev_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the state machine, keypad, door sensor, and LCD.
|
||||
*/
|
||||
void sm_init(void);
|
||||
|
||||
/**
|
||||
* @brief Enter the state‐machine’s main loop (low‐power + interrupts).
|
||||
*/
|
||||
void sm_loop(void);
|
||||
|
||||
#endif // STATE_MACHINE_H
|
||||
Loading…
Reference in New Issue
Block a user