/* 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 #include #include #include #include #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(); } }