From 0de66a4f7b389ab0495870bf16d5ef488bf48124 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Thu, 16 May 2024 21:22:16 +0200 Subject: [PATCH] Complete the countdown state machine --- doc/sseg_codes.txt | 4 + src/main.c | 187 +++++++++++++++++++++++++++++++++++++++++++-- src/sseg.c | 19 +++++ 3 files changed, 202 insertions(+), 8 deletions(-) diff --git a/doc/sseg_codes.txt b/doc/sseg_codes.txt index 242fc87..a985121 100644 --- a/doc/sseg_codes.txt +++ b/doc/sseg_codes.txt @@ -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 diff --git a/src/main.c b/src/main.c index ec9fbba..d451560 100644 --- a/src/main.c +++ b/src/main.c @@ -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; - m_countdown_state = COUNTDOWN_RUNNING; + 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) { diff --git a/src/sseg.c b/src/sseg.c index 977dba7..7a3dbc8 100644 --- a/src/sseg.c +++ b/src/sseg.c @@ -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; }