Complete the countdown state machine

This commit is contained in:
Thomas Kolb 2024-05-16 21:22:16 +02:00
parent 7d36e71923
commit 0de66a4f7b
3 changed files with 202 additions and 8 deletions

View file

@ -5,6 +5,10 @@ A: 11101110 0111 0111 0x77
M: 11101100 0111 0110 0x76
O: 11111100 0111 1110 0x7E
F: 10001110 0001 0111 0x47
#: 01101100 0110 0110 0x66 note: two vertical lines only
|: 00001100 0000 0110 0x06
d: 01111010 0110 1101 0x6D
H: 01101110 0110 0111 0x67
0: 11111100 0111 1110 0x7E
1: 01100000 0110 0000 0x60

View file

@ -11,10 +11,16 @@
typedef enum {
COUNTDOWN_UNINITIALIZED,
COUNTDOWN_STOPPED,
COUNTDOWN_STOPPING,
COUNTDOWN_STARTING,
COUNTDOWN_RUNNING
} countdown_state_t;
static const uint32_t ONE_DAY = 86400 * 1000;
static const uint32_t ONE_HOUR = 3600 * 1000;
static const uint32_t ONE_MINUTE = 60 * 1000;
static const uint32_t ONE_SECOND = 1000;
static countdown_state_t m_countdown_state;
static uint32_t m_last_state_change_time;
@ -53,6 +59,53 @@ static void configure_ms_trig_alarm(void)
m_next_ms_trigger = timer_hw->timelr + 10000;
}
static void show_time(uint32_t time_ms)
{
bool blinking_dot_on = (time_ms % 1000) < 500;
if(time_ms >= 100 * ONE_DAY) { // more than 100 days
// time too large to display
sseg_set_char(0, 'i', false);
sseg_set_char(1, 'n', false);
sseg_set_char(2, 'f', false);
} else if(time_ms >= 10 * ONE_DAY) { // 10 .. 100 days
// show days without decimal
uint32_t days = (time_ms + ONE_DAY/2) / ONE_DAY;
sseg_set_char(0, '0' + days / 10, false);
sseg_set_char(1, '0' + days % 10, blinking_dot_on);
sseg_set_char(2, 'd', false);
} else if(time_ms >= 10 * ONE_HOUR) { // 10 hours .. 10 days
// show days with one decimal
uint32_t decidays = (time_ms + ONE_DAY/20) * 10 / ONE_DAY;
sseg_set_char(0, '0' + decidays / 10, blinking_dot_on);
sseg_set_char(1, '0' + decidays % 10, false);
sseg_set_char(2, 'd', false);
} else if(time_ms >= 1000 * ONE_SECOND) { // 1000 seconds (0.27 hours) .. 10 hours
// show hours with one decimal
uint32_t decihours = (time_ms + ONE_HOUR/20) * 10 / ONE_HOUR;
sseg_set_char(0, '0' + decihours / 10, blinking_dot_on);
sseg_set_char(1, '0' + decihours % 10, false);
sseg_set_char(2, 'H', false);
} else if(time_ms >= 100 * ONE_SECOND) { // 100 .. 1000 seconds
// show seconds
uint32_t seconds = (time_ms + ONE_SECOND/2) / ONE_SECOND;
sseg_set_char(0, '0' + seconds / 100, false);
sseg_set_char(1, '0' + (seconds / 10) % 10, false);
sseg_set_char(2, '0' + seconds % 10, true);
} else { // <100 seconds
// show seconds with one decimal
uint32_t deciseconds = time_ms * 10 / ONE_SECOND;
sseg_set_char(0, '0' + deciseconds / 100, false);
sseg_set_char(1, '0' + (deciseconds / 10) % 10, true);
sseg_set_char(2, '0' + deciseconds % 10, false);
}
}
static void update_countdown_fsm(uint32_t time_ms)
{
countdown_state_t last_state = m_countdown_state;
@ -70,6 +123,9 @@ static void update_countdown_fsm(uint32_t time_ms)
case COUNTDOWN_STOPPED:
if(state_entered) {
// countdown has been stopped -> switch off the load
gpio_put(OUTPUT_ENABLE_PIN, true);
sseg_set_char(0, 'o', false);
sseg_set_char(1, 'f', false);
sseg_set_char(2, 'f', false);
@ -81,16 +137,62 @@ static void update_countdown_fsm(uint32_t time_ms)
break;
case COUNTDOWN_STARTING:
// TODO: show a fancy animation + beep code
if(buttons_get_events(BTN_ON) & BTN_EVENT_RELEASED) {
// button released too early -> back to stopped state
m_countdown_state = COUNTDOWN_STOPPED;
}
if(time_in_state == 3000) {
m_countdown_off_time = time_ms + 30 * 60 * 1000;
switch(time_in_state) {
case 1:
sseg_set_char(0, '|', false);
sseg_set_char(1, ' ', false);
sseg_set_char(2, ' ', false);
break;
case 500:
sseg_set_char(0, '#', false);
buzzer_on(1000);
break;
case 1000:
sseg_set_char(1, '|', false);
buzzer_on(1414);
break;
case 1500:
sseg_set_char(1, '#', false);
buzzer_on(2000);
break;
case 2000:
sseg_set_char(2, '|', false);
buzzer_on(2828);
break;
case 2500:
sseg_set_char(2, '#', false);
buzzer_on(4000);
break;
case 700:
case 1200:
case 1700:
case 2200:
case 2700:
buzzer_off();
break;
case 3000:
if(buttons_is_pressed(BTN_OFF)) {
// „permanent on mode“
m_countdown_off_time = time_ms + 14 * ONE_DAY;
} else {
// default mode
m_countdown_off_time = time_ms + 30 * ONE_MINUTE;
}
m_countdown_state = COUNTDOWN_RUNNING;
break;
}
break;
@ -102,9 +204,19 @@ static void update_countdown_fsm(uint32_t time_ms)
countdown_time_remaining = m_countdown_off_time - time_ms;
// TODO: show time on display
// TODO: off button handling
// TODO: on button handling (extend time)
show_time(countdown_time_remaining);
// off button pressed -> go to stop procedure
if(buttons_get_events(BTN_ON) & BTN_EVENT_PRESSED) {
m_countdown_state = COUNTDOWN_STOPPING;
}
// extend time when on button is pressed again
if(buttons_get_events(BTN_ON) & BTN_EVENT_PRESSED) {
if(countdown_time_remaining < 10 * ONE_HOUR) {
m_countdown_off_time += 30 * ONE_MINUTE;
}
}
// beep codes
switch(countdown_time_remaining) {
@ -142,6 +254,65 @@ static void update_countdown_fsm(uint32_t time_ms)
gpio_put(OUTPUT_ENABLE_PIN, false);
m_countdown_state = COUNTDOWN_STOPPED;
}
break;
case COUNTDOWN_STOPPING:
if(state_entered) {
buzzer_off();
}
if(buttons_get_events(BTN_OFF) & BTN_EVENT_RELEASED) {
// button released too early -> back to running state
m_countdown_state = COUNTDOWN_RUNNING;
// add the time that the button was pressed to the deadline
m_countdown_off_time += time_in_state;
}
switch(time_in_state) {
case 1:
sseg_set_char(0, '#', false);
sseg_set_char(1, '#', false);
sseg_set_char(2, '#', false);
break;
case 500:
sseg_set_char(0, '|', false);
buzzer_on(4000);
break;
case 1000:
sseg_set_char(0, ' ', false);
buzzer_on(2828);
break;
case 1500:
sseg_set_char(1, '|', false);
buzzer_on(2000);
break;
case 2000:
sseg_set_char(1, ' ', false);
buzzer_on(1414);
break;
case 2500:
sseg_set_char(2, '|', false);
buzzer_on(1000);
break;
case 700:
case 1200:
case 1700:
case 2200:
case 2700:
buzzer_off();
break;
case 3000:
m_countdown_state = COUNTDOWN_STOPPED;
break;
}
break;
}
if(m_countdown_state != last_state) {

View file

@ -83,6 +83,25 @@ static uint8_t ascii_to_sseg(char c)
case 'F':
return 0x47;
case '#':
return 0x66;
case 'i':
case 'I':
case '|':
return 0x06;
case 'd':
case 'D':
return 0x6D;
case 'h':
case 'H':
return 0x67;
case ' ':
return 0x00;
default: // '-'
return 0x01;
}