ADI console menu library

Committer:
mahphalke
Date:
Sat Feb 29 04:11:15 2020 +0000
Revision:
1:dcc17e5a913f
Parent:
0:47b30a147723
Child:
2:6a97882f6144
Library for displaying and processing the menus on serial or console terminal.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mahphalke 1:dcc17e5a913f 1 /*!
mahphalke 1:dcc17e5a913f 2 *****************************************************************************
mahphalke 1:dcc17e5a913f 3 @file: adi_console_menu.c
mahphalke 1:dcc17e5a913f 4
mahphalke 1:dcc17e5a913f 5 @brief: A simple console menu manager handler
mahphalke 1:dcc17e5a913f 6
mahphalke 1:dcc17e5a913f 7 @details: A way to define using arrays of structs a set of menus that can
mahphalke 1:dcc17e5a913f 8 be displayed to a user, easily, with all user interaction handled
mahphalke 1:dcc17e5a913f 9 by the library, leaving only the implementation of the menu actions
mahphalke 1:dcc17e5a913f 10 to be done by the library user.
mahphalke 1:dcc17e5a913f 11 -----------------------------------------------------------------------------
mahphalke 1:dcc17e5a913f 12 Copyright (c) 2019, 2020 Analog Devices, Inc.
mahphalke 1:dcc17e5a913f 13 All rights reserved.
mahphalke 1:dcc17e5a913f 14
mahphalke 1:dcc17e5a913f 15 This software is proprietary to Analog Devices, Inc. and its licensors.
mahphalke 1:dcc17e5a913f 16 By using this software you agree to the terms of the associated
mahphalke 1:dcc17e5a913f 17 Analog Devices Software License Agreement.
mahphalke 1:dcc17e5a913f 18
mahphalke 1:dcc17e5a913f 19 *****************************************************************************/
mahphalke 1:dcc17e5a913f 20
mahphalke 1:dcc17e5a913f 21 #include <stdio.h>
mahphalke 1:dcc17e5a913f 22 #include <stdlib.h>
mahphalke 1:dcc17e5a913f 23 #include <ctype.h>
mahphalke 1:dcc17e5a913f 24 #include <string.h>
mahphalke 1:dcc17e5a913f 25 #include <stdbool.h>
mahphalke 1:dcc17e5a913f 26 #include <assert.h>
mahphalke 1:dcc17e5a913f 27
mahphalke 1:dcc17e5a913f 28 #include "adi_console_menu.h"
mahphalke 1:dcc17e5a913f 29
mahphalke 1:dcc17e5a913f 30
mahphalke 1:dcc17e5a913f 31 #define DIV_STRING "\t=================================================="
mahphalke 1:dcc17e5a913f 32
mahphalke 1:dcc17e5a913f 33 /*!
mahphalke 1:dcc17e5a913f 34 * @brief displays the text of a console menu
mahphalke 1:dcc17e5a913f 35 *
mahphalke 1:dcc17e5a913f 36 * @details
mahphalke 1:dcc17e5a913f 37 */
mahphalke 1:dcc17e5a913f 38 static void adi_display_console_menu(const console_menu * menu)
mahphalke 1:dcc17e5a913f 39 {
mahphalke 1:dcc17e5a913f 40 adi_clear_console();
mahphalke 1:dcc17e5a913f 41
mahphalke 1:dcc17e5a913f 42 // call headerItem to allow display of other content
mahphalke 1:dcc17e5a913f 43 if (menu->headerItem != NULL) {
mahphalke 1:dcc17e5a913f 44 menu->headerItem();
mahphalke 1:dcc17e5a913f 45 printf(DIV_STRING EOL);
mahphalke 1:dcc17e5a913f 46 }
mahphalke 1:dcc17e5a913f 47
mahphalke 1:dcc17e5a913f 48 /*
mahphalke 1:dcc17e5a913f 49 * Display the menu title and menuItems
mahphalke 1:dcc17e5a913f 50 * The shortcutKey is used to display '[A]' before the dispayText
mahphalke 1:dcc17e5a913f 51 */
mahphalke 1:dcc17e5a913f 52 printf("\t%s" EOL "\t", menu->title);
mahphalke 1:dcc17e5a913f 53 // show an underline to distinguish title from item
mahphalke 1:dcc17e5a913f 54 for (uint8_t i = 0; i < strlen(menu->title); i++) {
mahphalke 1:dcc17e5a913f 55 putchar('-');
mahphalke 1:dcc17e5a913f 56 }
mahphalke 1:dcc17e5a913f 57 // Extend underline past end of string, and then new line
mahphalke 1:dcc17e5a913f 58 printf("--" EOL);
mahphalke 1:dcc17e5a913f 59
mahphalke 1:dcc17e5a913f 60 // If the shortcutKey is not unique, first found is used
mahphalke 1:dcc17e5a913f 61 for (uint8_t i = 0; i < menu->itemCount; i ++) {
mahphalke 1:dcc17e5a913f 62 if (menu->items[i].shortcutKey == '\00') {
mahphalke 1:dcc17e5a913f 63 // No shortcut key defined, but display item text if available
mahphalke 1:dcc17e5a913f 64 printf("\t%s" EOL, menu->items[i].text);
mahphalke 1:dcc17e5a913f 65 } else {
mahphalke 1:dcc17e5a913f 66 printf("\t[%c] %s" EOL, toupper(menu->items[i].shortcutKey),
mahphalke 1:dcc17e5a913f 67 menu->items[i].text);
mahphalke 1:dcc17e5a913f 68 }
mahphalke 1:dcc17e5a913f 69 }
mahphalke 1:dcc17e5a913f 70 if (menu->enableEscapeKey) {
mahphalke 1:dcc17e5a913f 71 printf(EOL "\t[ESC] Exit Menu" EOL);
mahphalke 1:dcc17e5a913f 72 }
mahphalke 1:dcc17e5a913f 73
mahphalke 1:dcc17e5a913f 74 printf(EOL "\tPlease make a selection." EOL);
mahphalke 1:dcc17e5a913f 75
mahphalke 1:dcc17e5a913f 76 // call footerItem to allow display of other content
mahphalke 1:dcc17e5a913f 77 if (menu->footerItem != NULL) {
mahphalke 1:dcc17e5a913f 78 printf(DIV_STRING EOL);
mahphalke 1:dcc17e5a913f 79 menu->footerItem();
mahphalke 1:dcc17e5a913f 80 }
mahphalke 1:dcc17e5a913f 81 }
mahphalke 1:dcc17e5a913f 82
mahphalke 1:dcc17e5a913f 83
mahphalke 1:dcc17e5a913f 84 /*!
mahphalke 1:dcc17e5a913f 85 * @brief Display a consoleMenu and handle User interaction
mahphalke 1:dcc17e5a913f 86 *
mahphalke 1:dcc17e5a913f 87 * @details This displays the menuItems defined by the console menu, and
mahphalke 1:dcc17e5a913f 88 * handles all user interaction for the menu.
mahphalke 1:dcc17e5a913f 89 */
mahphalke 1:dcc17e5a913f 90 int32_t adi_do_console_menu(const console_menu * menu)
mahphalke 1:dcc17e5a913f 91 {
mahphalke 1:dcc17e5a913f 92 int32_t itemSelected = MENU_ESCAPED;
mahphalke 1:dcc17e5a913f 93 bool enableKeyScan = true;
mahphalke 1:dcc17e5a913f 94
mahphalke 1:dcc17e5a913f 95 adi_display_console_menu(menu);
mahphalke 1:dcc17e5a913f 96
mahphalke 1:dcc17e5a913f 97 /*
mahphalke 1:dcc17e5a913f 98 * Loop waiting for valid user input. menuItem index is returned if
mahphalke 1:dcc17e5a913f 99 * user presses a valid menu option.
mahphalke 1:dcc17e5a913f 100 */
mahphalke 1:dcc17e5a913f 101 do {
mahphalke 1:dcc17e5a913f 102 char keyPressed = toupper(getchar());
mahphalke 1:dcc17e5a913f 103
mahphalke 1:dcc17e5a913f 104 if (menu->enableEscapeKey) {
mahphalke 1:dcc17e5a913f 105 if (keyPressed == ESCAPE_KEY_CODE) {
mahphalke 1:dcc17e5a913f 106 itemSelected = MENU_ESCAPED;
mahphalke 1:dcc17e5a913f 107 enableKeyScan = false;
mahphalke 1:dcc17e5a913f 108 break;
mahphalke 1:dcc17e5a913f 109 }
mahphalke 1:dcc17e5a913f 110 }
mahphalke 1:dcc17e5a913f 111
mahphalke 1:dcc17e5a913f 112 for (uint8_t i = 0; i < menu->itemCount; i ++) {
mahphalke 1:dcc17e5a913f 113 if (toupper(menu->items[i].shortcutKey) == keyPressed) {
mahphalke 1:dcc17e5a913f 114 itemSelected = i;
mahphalke 1:dcc17e5a913f 115
mahphalke 1:dcc17e5a913f 116 // If the menuAction function pointer is not NULL, call the action
mahphalke 1:dcc17e5a913f 117 if (menu->items[i].action != NULL) {
mahphalke 1:dcc17e5a913f 118 switch (menu->items[i].action(menu->items[i].id)) {
mahphalke 1:dcc17e5a913f 119 case MENU_DONE: {
mahphalke 1:dcc17e5a913f 120 enableKeyScan = false;
mahphalke 1:dcc17e5a913f 121 break;
mahphalke 1:dcc17e5a913f 122 }
mahphalke 1:dcc17e5a913f 123 case MENU_CONTINUE:
mahphalke 1:dcc17e5a913f 124 default: {
mahphalke 1:dcc17e5a913f 125 enableKeyScan = true;
mahphalke 1:dcc17e5a913f 126 adi_display_console_menu(menu);
mahphalke 1:dcc17e5a913f 127 break;
mahphalke 1:dcc17e5a913f 128 }
mahphalke 1:dcc17e5a913f 129 }
mahphalke 1:dcc17e5a913f 130 }
mahphalke 1:dcc17e5a913f 131 break;
mahphalke 1:dcc17e5a913f 132 }
mahphalke 1:dcc17e5a913f 133 }
mahphalke 1:dcc17e5a913f 134 } while (enableKeyScan);
mahphalke 1:dcc17e5a913f 135
mahphalke 1:dcc17e5a913f 136 return (itemSelected);
mahphalke 1:dcc17e5a913f 137 }
mahphalke 1:dcc17e5a913f 138
mahphalke 1:dcc17e5a913f 139
mahphalke 1:dcc17e5a913f 140 /*!
mahphalke 1:dcc17e5a913f 141 * @brief Clears the standard input buffer (typically
mahphalke 1:dcc17e5a913f 142 * needed after scanf function)
mahphalke 1:dcc17e5a913f 143 *
mahphalke 1:dcc17e5a913f 144 * @details
mahphalke 1:dcc17e5a913f 145 */
mahphalke 1:dcc17e5a913f 146 static void flushf(void)
mahphalke 1:dcc17e5a913f 147 {
mahphalke 1:dcc17e5a913f 148 // flush the standard input (i.e. clear the input buffer)
mahphalke 1:dcc17e5a913f 149 while((getchar()) != '\n');
mahphalke 1:dcc17e5a913f 150 }
mahphalke 1:dcc17e5a913f 151
mahphalke 1:dcc17e5a913f 152
mahphalke 1:dcc17e5a913f 153 /*!
mahphalke 1:dcc17e5a913f 154 * @brief Reads a decimal string from the user
mahphalke 1:dcc17e5a913f 155 *
mahphalke 1:dcc17e5a913f 156 * @param input_len max number of character to accept from the user
mahphalke 1:dcc17e5a913f 157 *
mahphalke 1:dcc17e5a913f 158 * @return The integer value entered
mahphalke 1:dcc17e5a913f 159 *
mahphalke 1:dcc17e5a913f 160 * @details Allows a user to type in number, echoing back to the user,
mahphalke 1:dcc17e5a913f 161 * up to input_len chars
mahphalke 1:dcc17e5a913f 162 *
mahphalke 1:dcc17e5a913f 163 * @note Only positive integer numbers are supported currently
mahphalke 1:dcc17e5a913f 164 */
mahphalke 1:dcc17e5a913f 165 int32_t adi_get_decimal_int(uint8_t input_len)
mahphalke 1:dcc17e5a913f 166 {
mahphalke 1:dcc17e5a913f 167 char buf[20] = {0};
mahphalke 1:dcc17e5a913f 168 uint8_t buf_index = 0;
mahphalke 1:dcc17e5a913f 169 char ch;
mahphalke 1:dcc17e5a913f 170 bool loop = true;
mahphalke 1:dcc17e5a913f 171
mahphalke 1:dcc17e5a913f 172 assert(input_len < 19);
mahphalke 1:dcc17e5a913f 173
mahphalke 1:dcc17e5a913f 174 do {
mahphalke 1:dcc17e5a913f 175 ch = getchar();
mahphalke 1:dcc17e5a913f 176 if (isdigit(ch) && buf_index < (input_len)) {
mahphalke 1:dcc17e5a913f 177 // echo and store it as buf not full
mahphalke 1:dcc17e5a913f 178 buf[buf_index++] = ch;
mahphalke 1:dcc17e5a913f 179 putchar(ch);
mahphalke 1:dcc17e5a913f 180 }
mahphalke 1:dcc17e5a913f 181 if ((ch == '\x7F') && (buf_index > 0)) {
mahphalke 1:dcc17e5a913f 182 //backspace and at least 1 char in buffer
mahphalke 1:dcc17e5a913f 183 buf[buf_index--] = '\x00';
mahphalke 1:dcc17e5a913f 184 putchar(ch);
mahphalke 1:dcc17e5a913f 185 }
mahphalke 1:dcc17e5a913f 186 if ((ch == '\x0D') || (ch == '\x0A')) {
mahphalke 1:dcc17e5a913f 187 // return key pressed, all done, null terminate string
mahphalke 1:dcc17e5a913f 188 buf[buf_index] = '\x00';
mahphalke 1:dcc17e5a913f 189 loop = false;
mahphalke 1:dcc17e5a913f 190 }
mahphalke 1:dcc17e5a913f 191 } while(loop);
mahphalke 1:dcc17e5a913f 192
mahphalke 1:dcc17e5a913f 193 flushf();
mahphalke 1:dcc17e5a913f 194 return atoi(buf);
mahphalke 1:dcc17e5a913f 195 }
mahphalke 1:dcc17e5a913f 196
mahphalke 1:dcc17e5a913f 197 /*!
mahphalke 1:dcc17e5a913f 198 * @brief Reads a hexadecimal number from the user
mahphalke 1:dcc17e5a913f 199 *
mahphalke 1:dcc17e5a913f 200 * @param input_len max number of character to accept from the user
mahphalke 1:dcc17e5a913f 201 *
mahphalke 1:dcc17e5a913f 202 * @return The integer value entered
mahphalke 1:dcc17e5a913f 203 *
mahphalke 1:dcc17e5a913f 204 * @details Allows a user to type in a hexnumber, echoing back to the user,
mahphalke 1:dcc17e5a913f 205 * up to input_len chars
mahphalke 1:dcc17e5a913f 206 */
mahphalke 1:dcc17e5a913f 207 uint32_t adi_get_hex_integer(uint8_t input_len)
mahphalke 1:dcc17e5a913f 208 {
mahphalke 1:dcc17e5a913f 209 char buf[9] = {0};
mahphalke 1:dcc17e5a913f 210 uint8_t buf_index = 0;
mahphalke 1:dcc17e5a913f 211 char ch;
mahphalke 1:dcc17e5a913f 212 bool loop = true;
mahphalke 1:dcc17e5a913f 213
mahphalke 1:dcc17e5a913f 214 assert(input_len < 8);
mahphalke 1:dcc17e5a913f 215
mahphalke 1:dcc17e5a913f 216 do {
mahphalke 1:dcc17e5a913f 217 ch = getchar();
mahphalke 1:dcc17e5a913f 218 if (isxdigit(ch) && buf_index < (input_len)) {
mahphalke 1:dcc17e5a913f 219 // echo and store it as buf not full
mahphalke 1:dcc17e5a913f 220 buf[buf_index++] = ch;
mahphalke 1:dcc17e5a913f 221 putchar(ch);
mahphalke 1:dcc17e5a913f 222 }
mahphalke 1:dcc17e5a913f 223 if ((ch == '\x7F') && (buf_index > 0)) {
mahphalke 1:dcc17e5a913f 224 //backspace and at least 1 char in buffer
mahphalke 1:dcc17e5a913f 225 buf[buf_index--] = '\x00';
mahphalke 1:dcc17e5a913f 226 putchar(ch);
mahphalke 1:dcc17e5a913f 227 }
mahphalke 1:dcc17e5a913f 228 if ((ch == '\x0D') || (ch == '\x0A')) {
mahphalke 1:dcc17e5a913f 229 // return key pressed, all done, null terminate string
mahphalke 1:dcc17e5a913f 230 buf[buf_index] = '\x00';
mahphalke 1:dcc17e5a913f 231 loop = false;
mahphalke 1:dcc17e5a913f 232 }
mahphalke 1:dcc17e5a913f 233 } while(loop);
mahphalke 1:dcc17e5a913f 234
mahphalke 1:dcc17e5a913f 235 flushf();
mahphalke 1:dcc17e5a913f 236 return strtol(buf, NULL, 16);
mahphalke 1:dcc17e5a913f 237 }
mahphalke 1:dcc17e5a913f 238
mahphalke 1:dcc17e5a913f 239
mahphalke 1:dcc17e5a913f 240 /*!
mahphalke 1:dcc17e5a913f 241 * @brief Reads a floating string from the user
mahphalke 1:dcc17e5a913f 242 *
mahphalke 1:dcc17e5a913f 243 * @param input_len max number of character to accept from the user
mahphalke 1:dcc17e5a913f 244 *
mahphalke 1:dcc17e5a913f 245 * @return The float value entered
mahphalke 1:dcc17e5a913f 246 *
mahphalke 1:dcc17e5a913f 247 * @details Allows a user to type in number, echoing back to the user,
mahphalke 1:dcc17e5a913f 248 * up to input_len chars
mahphalke 1:dcc17e5a913f 249 *
mahphalke 1:dcc17e5a913f 250 * @note Only positive floating point numbers are supported currently
mahphalke 1:dcc17e5a913f 251 */
mahphalke 1:dcc17e5a913f 252 float adi_get_decimal_float(uint8_t input_len)
mahphalke 1:dcc17e5a913f 253 {
mahphalke 1:dcc17e5a913f 254 char buf[20] = { 0 };
mahphalke 1:dcc17e5a913f 255 uint8_t buf_index = 0;
mahphalke 1:dcc17e5a913f 256 char ch;
mahphalke 1:dcc17e5a913f 257 bool loop = true;
mahphalke 1:dcc17e5a913f 258
mahphalke 1:dcc17e5a913f 259 assert(input_len < 19);
mahphalke 1:dcc17e5a913f 260
mahphalke 1:dcc17e5a913f 261 do {
mahphalke 1:dcc17e5a913f 262 ch = getchar();
mahphalke 1:dcc17e5a913f 263 if ((isdigit(ch) || (ch == '.')) && buf_index < (input_len)) {
mahphalke 1:dcc17e5a913f 264 // echo and store it as buf not full
mahphalke 1:dcc17e5a913f 265 buf[buf_index++] = ch;
mahphalke 1:dcc17e5a913f 266 putchar(ch);
mahphalke 1:dcc17e5a913f 267 }
mahphalke 1:dcc17e5a913f 268 if ((ch == '\x7F') && (buf_index > 0)) {
mahphalke 1:dcc17e5a913f 269 //backspace and at least 1 char in buffer
mahphalke 1:dcc17e5a913f 270 buf[buf_index--] = '\x00';
mahphalke 1:dcc17e5a913f 271 putchar(ch);
mahphalke 1:dcc17e5a913f 272 }
mahphalke 1:dcc17e5a913f 273 if ((ch == '\x0D') || (ch == '\x0A')) {
mahphalke 1:dcc17e5a913f 274 // return key pressed, all done, null terminate string
mahphalke 1:dcc17e5a913f 275 buf[buf_index] = '\x00';
mahphalke 1:dcc17e5a913f 276 loop = false;
mahphalke 1:dcc17e5a913f 277 }
mahphalke 1:dcc17e5a913f 278 } while (loop);
mahphalke 1:dcc17e5a913f 279
mahphalke 1:dcc17e5a913f 280 flushf();
mahphalke 1:dcc17e5a913f 281 return atof(buf);
mahphalke 1:dcc17e5a913f 282 }
mahphalke 1:dcc17e5a913f 283
mahphalke 1:dcc17e5a913f 284
mahphalke 1:dcc17e5a913f 285 /*!
mahphalke 1:dcc17e5a913f 286 * @brief Clears the console terminal
mahphalke 1:dcc17e5a913f 287 *
mahphalke 1:dcc17e5a913f 288 * @details Clears the console terminal using VT100 escape code, or can be changed to
mahphalke 1:dcc17e5a913f 289 * output blank lines if serial link doesn't support VT100.
mahphalke 1:dcc17e5a913f 290 */
mahphalke 1:dcc17e5a913f 291 void adi_clear_console(void)
mahphalke 1:dcc17e5a913f 292 {
mahphalke 1:dcc17e5a913f 293 /*
mahphalke 1:dcc17e5a913f 294 * clear console and move cursor to home location, followed by move to home location.
mahphalke 1:dcc17e5a913f 295 * Dedicated call to move home is because sometimes first move home doesn't work
mahphalke 1:dcc17e5a913f 296 * \r\n required to flush the uart buffer.
mahphalke 1:dcc17e5a913f 297 */
mahphalke 1:dcc17e5a913f 298 printf("\x1B[2J\x1B[H\r\n");
mahphalke 1:dcc17e5a913f 299
mahphalke 1:dcc17e5a913f 300 /*
mahphalke 1:dcc17e5a913f 301 * if VT100 is not supported, this can be enabled instead, but menu display may not work well
mahphalke 1:dcc17e5a913f 302 */
mahphalke 1:dcc17e5a913f 303 // for (uint8_t = 0; i < 100; i++)
mahphalke 1:dcc17e5a913f 304 // printf("\r\n\r");
mahphalke 1:dcc17e5a913f 305 }
mahphalke 1:dcc17e5a913f 306
mahphalke 1:dcc17e5a913f 307
mahphalke 1:dcc17e5a913f 308 /*!
mahphalke 1:dcc17e5a913f 309 * @brief waits for any key to be pressed, and displays a prompt to the user
mahphalke 1:dcc17e5a913f 310 *
mahphalke 1:dcc17e5a913f 311 * @details
mahphalke 1:dcc17e5a913f 312 */
mahphalke 1:dcc17e5a913f 313 void adi_press_any_key_to_continue(void)
mahphalke 1:dcc17e5a913f 314 {
mahphalke 1:dcc17e5a913f 315 printf("\r\nPress any key to continue...\r\n");
mahphalke 1:dcc17e5a913f 316 getchar();
mahphalke 1:dcc17e5a913f 317 }