376 lines
8.3 KiB
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();
|
|
}
|
|
} |