/* * Copyright (c) 2025 Hochschule Darmstadt * * This software is provided exclusively for the practical part of the * course 'Embedded Systems' at 'Darmstadt University of Applied Sciences * (Hochschule Darmstadt, h_da)'. * * Any use, distribution, or modification outside this context is prohibited * without explicit permission. * * AUTHORS: David Heiß, Manfred Pester, Jens-Peter Akelbein * FILE: menu.c * CONTENTS: menu communicating with PC via serial */ #include #include #include #include #define MENU_POS(n, m, f) "\e[" #n ";" #m "H" f #define CLEAR_SCREEN(n) "\e[" #n "J" #if MENU_BOX_DRAWING static char menu[] = "\e[?25l \e[2J" " ┏━━━━━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━┓\r\n" " ┃ c Clock │ l LED │ m Motion │ s Sonic │ 1-9 Goal │ d Debug ┃\r\n" "┏━━━━━━━╋━━━━━━━━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━━━┫\r\n" "┃ State ┃ │ │ │ │ │ ┃\r\n" "┠───────╂╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┨\r\n" "┃ Value ┃ │ │ │ │ │ ┃\r\n" "┠───────╂╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┨\r\n" "┃ Unit ┃ │ │ │ │ │ ┃\r\n" "┗━━━━━━━┻━━━━━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┛\r\n"; #else static char menu[] = "\e[?25l \e[2J" " +----------------+----------+----------+----------+----------+----------+\r\n" " | c Clock | l LED | m Motion | s Sonic | 1-9 Goal | d Debug |\r\n" "+-------+----------------+----------+----------+----------+----------+----------+\r\n" "| State | | | | | | |\r\n" "+-------+----------------+----------+----------+----------+----------+----------+\r\n" "| Value | | | | | | |\r\n" "+-------+----------------+----------+----------+----------+----------+----------+\r\n" "| Unit | | | | | | |\r\n" "+-------+----------------+----------+----------+----------+----------+----------+\r\n"; #endif char buffer[1000]; void menu_init(void) { while (!serial_transmit_string(menu, sizeof(menu))) { } } void menu_loop(void) { if (!serial_transmit_ready()) return; unsigned int n = snprintf( buffer, sizeof(buffer), MENU_POS(4, 11, "%14s") MENU_POS(4, 28, "%8s") MENU_POS(4, 39, "%8s") MENU_POS(4, 50, "%8s") MENU_POS(4, 61, "%8u") MENU_POS(4, 72, "%8s"), clock_state_text[state.clock], led_state_text[state.led], motion_state_text[state.motion], sonic_state_text[state.sonic], state.goal + 1, debug_state_text[state.debug] ); switch (state.clock) { case CLOCK_STATE_TIME: n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 11, "%14d"), value.clock.time); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 11, "%14s"), "ms"); break; case CLOCK_STATE_MAX_LATENCY: n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 11, "%14f"), value.clock.latency); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 11, "%14s"), "ms"); break; case CLOCK_STATE_LOOP_FREQUENCY: n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 11, "%14d"), value.clock.frequency); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 11, "%14s"), "Hz"); break; case CLOCK_STATE_INVALID: __builtin_unreachable(); } n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 28, "%8d"), value.led); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 28, "%8s"), "On/Off"); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 39, "%8.2f"), value.motion); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 39, "%8s"), "%"); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 50, "%8u"), value.sonic.distance); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 50, "%8s"), "mm"); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 61, "%8u"), value.goal); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 61, "%8s"), "mm"); static enum DebugState last_debug_state = DEBUG_STATE_INVALID; if (last_debug_state != state.debug) { n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(6, 72, "%8s"), ""); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(8, 72, "%8s"), ""); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(10, 0, "%s"), CLEAR_SCREEN(0)); last_debug_state = state.debug; } switch (state.debug) { default: // DEBUG_STATE_OFF break; case DEBUG_STATE_PID: n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(10, 0, " P: %17.6f"), value.debug.p); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(11, 0, " I: %17.6f"), value.debug.i); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(12, 0, " D: %17.6f"), value.debug.d); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(13, 0, "PID: %17.6f (duty cycle)"), value.debug.pid); break; case DEBUG_STATE_RAW_DATA: n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(10, 0, "Distance: %10d mm"), value.sonic.distance); n += snprintf(buffer + n, sizeof(buffer) - n, MENU_POS(11, 0, " Time: %10d ms"), value.sonic.time); break; // case DEBUG_STATE_CUSTOM: // break; // case DEBUG_STATE_INVALID: // __builtin_unreachable(): } // while(!serial_transmit_string(buffer, n)); serial_transmit_string(buffer, n); }