ESR-2025/src/state_machine.c

376 lines
8.3 KiB
C

/* File: state_machine.c */
/**
* @file state_machine.c
* @brief Statemachine for the Core-Program
*
* ...
*
* @authors
* Frederik Beimgraben
* Minh Dan Cam
* Luis Meyer
* @date 2025-07-02
*/
#include <stdbool.h>
#include <msp430.h>
#include <driverlib.h>
#include <stdio.h>
#include <ctype.h>
#include "keypad.h"
#include "lcd.h"
#include "morse.h"
#include "door_sensor.h"
#include "state_machine.h"
#include "timer.h"
volatile char keypad_last_key = '\0';
volatile bool keypad_flag_chg = false;
volatile bool allow_open = false;
/* Function Declarations */
void handle_interrupt(void);
void buzz_invalid_input(void);
void sound_alarm(void);
void reset_alarm(void);
void transition(State_t state);
void ui_draw_idle(void);
void ui_draw_sel_count(void);
void ui_draw_confirmed(void);
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);
volatile State_t current_state = STATE_IDLE;
volatile bool state_chg = false;
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;
volatile bool door_open = false;
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();
}
}
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 '*':
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.
* @param key ASCII character of pressed key
*
* Stores key, sets ready flag, and blinks Morse code on LED.
*/
static void keypad_handler(char key)
{
keypad_last_key = key;
keypad_flag_chg = true;
switch (current_state) {
case STATE_IDLE:
handle_input_idle(key);
break;
case STATE_SEL_COUNT:
handle_input_sel_count(key);
break;
case STATE_CONFIRMED:
handle_input_confirmed(key);
break;
case STATE_OPEN:
handle_input_confirmed(key);
break;
default:
buzz_invalid_input();
}
}
bool any_selected(void) {
return count_a + count_b + count_c + count_d > 0;
}
static void door_opened_handler(void) {
if (current_state != STATE_CONFIRMED && current_state != STATE_UNLOCKED) {
transition(STATE_UNAUTHORIZED);
} else {
transition(STATE_OPEN);
}
door_open = true;
}
static void door_closed_handler(void) {
reset_amounts();
transition(STATE_IDLE);
door_open = false;
}
void buzz_invalid_input(void) {
// FIXME: Implement this later
}
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) {
lcd_clear();
lcd_set_cursor(0, 5);
lcd_print("Ready!");
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 ",
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
);
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]/[*]");
}
void ui_draw_error(void) {
lcd_clear();
lcd_set_cursor(0, 2);
lcd_print("UNAUTHORIZED");
lcd_set_cursor(1, 1);
lcd_print("CLOSE DOOR NOW");
}
void ui_draw_open(void) {
lcd_clear();
lcd_set_cursor(0, 2);
lcd_print("DOOR IS OPEN");
lcd_set_cursor(1, 0);
lcd_print("CLOSE /[A-D]/[*]");
}
void ui_draw_none_selected(void) {
lcd_clear();
lcd_set_cursor(0, 5);
lcd_print("SELECT");
lcd_set_cursor(1, 1);
lcd_print("ANY BEVERAGES!");
sleep(1000);
transition(STATE_SEL_COUNT);
ui_draw_sel_count();
}
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");
sleep(1000);
if (door_open) {
transition(STATE_OPEN);
ui_draw_open();
} else {
transition(STATE_IDLE);
ui_draw_idle();
}
}