ADI console menu library

Committer:
mbradley
Date:
Fri Mar 27 18:55:32 2020 +0000
Revision:
2:6a97882f6144
Parent:
1:dcc17e5a913f
Child:
4:ced4fa6875ed
Removing flushf() as it is not required.

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 * @brief Reads a decimal string from the user
mahphalke 1:dcc17e5a913f 141 *
mahphalke 1:dcc17e5a913f 142 * @param input_len max number of character to accept from the user
mahphalke 1:dcc17e5a913f 143 *
mahphalke 1:dcc17e5a913f 144 * @return The integer value entered
mahphalke 1:dcc17e5a913f 145 *
mahphalke 1:dcc17e5a913f 146 * @details Allows a user to type in number, echoing back to the user,
mahphalke 1:dcc17e5a913f 147 * up to input_len chars
mahphalke 1:dcc17e5a913f 148 *
mahphalke 1:dcc17e5a913f 149 * @note Only positive integer numbers are supported currently
mahphalke 1:dcc17e5a913f 150 */
mahphalke 1:dcc17e5a913f 151 int32_t adi_get_decimal_int(uint8_t input_len)
mahphalke 1:dcc17e5a913f 152 {
mahphalke 1:dcc17e5a913f 153 char buf[20] = {0};
mahphalke 1:dcc17e5a913f 154 uint8_t buf_index = 0;
mahphalke 1:dcc17e5a913f 155 char ch;
mahphalke 1:dcc17e5a913f 156 bool loop = true;
mahphalke 1:dcc17e5a913f 157
mahphalke 1:dcc17e5a913f 158 assert(input_len < 19);
mahphalke 1:dcc17e5a913f 159
mahphalke 1:dcc17e5a913f 160 do {
mahphalke 1:dcc17e5a913f 161 ch = getchar();
mahphalke 1:dcc17e5a913f 162 if (isdigit(ch) && buf_index < (input_len)) {
mahphalke 1:dcc17e5a913f 163 // echo and store it as buf not full
mahphalke 1:dcc17e5a913f 164 buf[buf_index++] = ch;
mahphalke 1:dcc17e5a913f 165 putchar(ch);
mahphalke 1:dcc17e5a913f 166 }
mahphalke 1:dcc17e5a913f 167 if ((ch == '\x7F') && (buf_index > 0)) {
mahphalke 1:dcc17e5a913f 168 //backspace and at least 1 char in buffer
mahphalke 1:dcc17e5a913f 169 buf[buf_index--] = '\x00';
mahphalke 1:dcc17e5a913f 170 putchar(ch);
mahphalke 1:dcc17e5a913f 171 }
mahphalke 1:dcc17e5a913f 172 if ((ch == '\x0D') || (ch == '\x0A')) {
mahphalke 1:dcc17e5a913f 173 // return key pressed, all done, null terminate string
mahphalke 1:dcc17e5a913f 174 buf[buf_index] = '\x00';
mahphalke 1:dcc17e5a913f 175 loop = false;
mahphalke 1:dcc17e5a913f 176 }
mahphalke 1:dcc17e5a913f 177 } while(loop);
mahphalke 1:dcc17e5a913f 178
mahphalke 1:dcc17e5a913f 179 return atoi(buf);
mahphalke 1:dcc17e5a913f 180 }
mahphalke 1:dcc17e5a913f 181
mahphalke 1:dcc17e5a913f 182 /*!
mahphalke 1:dcc17e5a913f 183 * @brief Reads a hexadecimal number from the user
mahphalke 1:dcc17e5a913f 184 *
mahphalke 1:dcc17e5a913f 185 * @param input_len max number of character to accept from the user
mahphalke 1:dcc17e5a913f 186 *
mahphalke 1:dcc17e5a913f 187 * @return The integer value entered
mahphalke 1:dcc17e5a913f 188 *
mahphalke 1:dcc17e5a913f 189 * @details Allows a user to type in a hexnumber, echoing back to the user,
mahphalke 1:dcc17e5a913f 190 * up to input_len chars
mahphalke 1:dcc17e5a913f 191 */
mahphalke 1:dcc17e5a913f 192 uint32_t adi_get_hex_integer(uint8_t input_len)
mahphalke 1:dcc17e5a913f 193 {
mahphalke 1:dcc17e5a913f 194 char buf[9] = {0};
mahphalke 1:dcc17e5a913f 195 uint8_t buf_index = 0;
mahphalke 1:dcc17e5a913f 196 char ch;
mahphalke 1:dcc17e5a913f 197 bool loop = true;
mahphalke 1:dcc17e5a913f 198
mahphalke 1:dcc17e5a913f 199 assert(input_len < 8);
mahphalke 1:dcc17e5a913f 200
mahphalke 1:dcc17e5a913f 201 do {
mahphalke 1:dcc17e5a913f 202 ch = getchar();
mahphalke 1:dcc17e5a913f 203 if (isxdigit(ch) && buf_index < (input_len)) {
mahphalke 1:dcc17e5a913f 204 // echo and store it as buf not full
mahphalke 1:dcc17e5a913f 205 buf[buf_index++] = ch;
mahphalke 1:dcc17e5a913f 206 putchar(ch);
mahphalke 1:dcc17e5a913f 207 }
mahphalke 1:dcc17e5a913f 208 if ((ch == '\x7F') && (buf_index > 0)) {
mahphalke 1:dcc17e5a913f 209 //backspace and at least 1 char in buffer
mahphalke 1:dcc17e5a913f 210 buf[buf_index--] = '\x00';
mahphalke 1:dcc17e5a913f 211 putchar(ch);
mahphalke 1:dcc17e5a913f 212 }
mahphalke 1:dcc17e5a913f 213 if ((ch == '\x0D') || (ch == '\x0A')) {
mahphalke 1:dcc17e5a913f 214 // return key pressed, all done, null terminate string
mahphalke 1:dcc17e5a913f 215 buf[buf_index] = '\x00';
mahphalke 1:dcc17e5a913f 216 loop = false;
mahphalke 1:dcc17e5a913f 217 }
mahphalke 1:dcc17e5a913f 218 } while(loop);
mahphalke 1:dcc17e5a913f 219
mahphalke 1:dcc17e5a913f 220 return strtol(buf, NULL, 16);
mahphalke 1:dcc17e5a913f 221 }
mahphalke 1:dcc17e5a913f 222
mahphalke 1:dcc17e5a913f 223
mahphalke 1:dcc17e5a913f 224 /*!
mahphalke 1:dcc17e5a913f 225 * @brief Reads a floating string from the user
mahphalke 1:dcc17e5a913f 226 *
mahphalke 1:dcc17e5a913f 227 * @param input_len max number of character to accept from the user
mahphalke 1:dcc17e5a913f 228 *
mahphalke 1:dcc17e5a913f 229 * @return The float value entered
mahphalke 1:dcc17e5a913f 230 *
mahphalke 1:dcc17e5a913f 231 * @details Allows a user to type in number, echoing back to the user,
mahphalke 1:dcc17e5a913f 232 * up to input_len chars
mahphalke 1:dcc17e5a913f 233 *
mahphalke 1:dcc17e5a913f 234 * @note Only positive floating point numbers are supported currently
mahphalke 1:dcc17e5a913f 235 */
mahphalke 1:dcc17e5a913f 236 float adi_get_decimal_float(uint8_t input_len)
mahphalke 1:dcc17e5a913f 237 {
mahphalke 1:dcc17e5a913f 238 char buf[20] = { 0 };
mahphalke 1:dcc17e5a913f 239 uint8_t buf_index = 0;
mahphalke 1:dcc17e5a913f 240 char ch;
mahphalke 1:dcc17e5a913f 241 bool loop = true;
mahphalke 1:dcc17e5a913f 242
mahphalke 1:dcc17e5a913f 243 assert(input_len < 19);
mahphalke 1:dcc17e5a913f 244
mahphalke 1:dcc17e5a913f 245 do {
mahphalke 1:dcc17e5a913f 246 ch = getchar();
mahphalke 1:dcc17e5a913f 247 if ((isdigit(ch) || (ch == '.')) && buf_index < (input_len)) {
mahphalke 1:dcc17e5a913f 248 // echo and store it as buf not full
mahphalke 1:dcc17e5a913f 249 buf[buf_index++] = ch;
mahphalke 1:dcc17e5a913f 250 putchar(ch);
mahphalke 1:dcc17e5a913f 251 }
mahphalke 1:dcc17e5a913f 252 if ((ch == '\x7F') && (buf_index > 0)) {
mahphalke 1:dcc17e5a913f 253 //backspace and at least 1 char in buffer
mahphalke 1:dcc17e5a913f 254 buf[buf_index--] = '\x00';
mahphalke 1:dcc17e5a913f 255 putchar(ch);
mahphalke 1:dcc17e5a913f 256 }
mahphalke 1:dcc17e5a913f 257 if ((ch == '\x0D') || (ch == '\x0A')) {
mahphalke 1:dcc17e5a913f 258 // return key pressed, all done, null terminate string
mahphalke 1:dcc17e5a913f 259 buf[buf_index] = '\x00';
mahphalke 1:dcc17e5a913f 260 loop = false;
mahphalke 1:dcc17e5a913f 261 }
mahphalke 1:dcc17e5a913f 262 } while (loop);
mahphalke 1:dcc17e5a913f 263
mahphalke 1:dcc17e5a913f 264 return atof(buf);
mahphalke 1:dcc17e5a913f 265 }
mahphalke 1:dcc17e5a913f 266
mahphalke 1:dcc17e5a913f 267
mahphalke 1:dcc17e5a913f 268 /*!
mahphalke 1:dcc17e5a913f 269 * @brief Clears the console terminal
mahphalke 1:dcc17e5a913f 270 *
mahphalke 1:dcc17e5a913f 271 * @details Clears the console terminal using VT100 escape code, or can be changed to
mahphalke 1:dcc17e5a913f 272 * output blank lines if serial link doesn't support VT100.
mahphalke 1:dcc17e5a913f 273 */
mahphalke 1:dcc17e5a913f 274 void adi_clear_console(void)
mahphalke 1:dcc17e5a913f 275 {
mahphalke 1:dcc17e5a913f 276 /*
mahphalke 1:dcc17e5a913f 277 * clear console and move cursor to home location, followed by move to home location.
mahphalke 1:dcc17e5a913f 278 * Dedicated call to move home is because sometimes first move home doesn't work
mahphalke 1:dcc17e5a913f 279 * \r\n required to flush the uart buffer.
mahphalke 1:dcc17e5a913f 280 */
mahphalke 1:dcc17e5a913f 281 printf("\x1B[2J\x1B[H\r\n");
mahphalke 1:dcc17e5a913f 282
mahphalke 1:dcc17e5a913f 283 /*
mahphalke 1:dcc17e5a913f 284 * if VT100 is not supported, this can be enabled instead, but menu display may not work well
mahphalke 1:dcc17e5a913f 285 */
mahphalke 1:dcc17e5a913f 286 // for (uint8_t = 0; i < 100; i++)
mahphalke 1:dcc17e5a913f 287 // printf("\r\n\r");
mahphalke 1:dcc17e5a913f 288 }
mahphalke 1:dcc17e5a913f 289
mahphalke 1:dcc17e5a913f 290
mahphalke 1:dcc17e5a913f 291 /*!
mahphalke 1:dcc17e5a913f 292 * @brief waits for any key to be pressed, and displays a prompt to the user
mahphalke 1:dcc17e5a913f 293 *
mahphalke 1:dcc17e5a913f 294 * @details
mahphalke 1:dcc17e5a913f 295 */
mahphalke 1:dcc17e5a913f 296 void adi_press_any_key_to_continue(void)
mahphalke 1:dcc17e5a913f 297 {
mahphalke 1:dcc17e5a913f 298 printf("\r\nPress any key to continue...\r\n");
mahphalke 1:dcc17e5a913f 299 getchar();
mahphalke 1:dcc17e5a913f 300 }