mirror of https://github.com/ArduPilot/ardupilot
Add a simple commandline menu library.
The library has an interface suitable for both interactive and automated use (i.e. Arduino console or GCS operation). git-svn-id: https://arducopter.googlecode.com/svn/trunk@544 f9c3cf11-9bcb-44bc-f272-b75c42450872
This commit is contained in:
parent
7721e622aa
commit
eb6113e685
|
@ -6,23 +6,18 @@
|
|||
// your option) any later version.
|
||||
//
|
||||
|
||||
#ifndef _AP_COMMON_H
|
||||
#define _AP_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
///
|
||||
/// @file AP_Common.h
|
||||
/// @brief Common definitions and utility routines for the ArduPilot
|
||||
/// libraries.
|
||||
///
|
||||
/// @note For correct operation, all sketches and libraries should
|
||||
/// include this header *before* any other. In
|
||||
/// particular, this is critical for things like the
|
||||
/// FastSerial library, which need the opportunity to
|
||||
/// override parts of the Arduino infrastructure.
|
||||
///
|
||||
|
||||
#ifndef _AP_COMMON_H
|
||||
#define _AP_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "include/menu.h" /// simple menu subsystem
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @name Types
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
#include <FastSerial.h>
|
||||
#include <AP_Common.h>
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
FastSerialPort0(Serial);
|
||||
|
||||
int
|
||||
menu_test(uint8_t argc, const Menu::arg *argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
Serial.printf("This is a test with %d arguments\n", argc);
|
||||
for (i = 1; i < argc; i++) {
|
||||
Serial.printf("%d: int %ld float ", i, argv[i].i);
|
||||
Serial.println(argv[i].f, 6); // gross
|
||||
}
|
||||
}
|
||||
|
||||
const struct Menu::command top_menu_commands[] PROGMEM = {
|
||||
{"test", menu_test},
|
||||
};
|
||||
|
||||
MENU(top, "menu", top_menu_commands);
|
||||
|
||||
void
|
||||
setup(void)
|
||||
{
|
||||
Serial.begin(38400);
|
||||
top.run();
|
||||
}
|
||||
|
||||
void
|
||||
loop(void)
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
|
||||
|
||||
/// @file menu.h
|
||||
/// @brief Simple commandline menu subsystem.
|
||||
|
||||
#define MENU_COMMANDLINE_MAX 32 ///< maximum input line length
|
||||
#define MENU_ARGS_MAX 4 ///< maximum number of arguments
|
||||
#define MENU_COMMAND_MAX 14 ///< maximum size of a command name
|
||||
|
||||
/// Class defining and handling one menu tree
|
||||
class Menu {
|
||||
public:
|
||||
/// argument passed to a menu function
|
||||
struct arg {
|
||||
const char *str; ///< string form of the argument
|
||||
long i; ///< integer form of the argument (if a number)
|
||||
float f; ///< floating point form of the argument (if a number)
|
||||
};
|
||||
|
||||
/// menu command function
|
||||
///
|
||||
typedef int (*func)(uint8_t argc, const struct arg *argv);
|
||||
|
||||
/// menu command description
|
||||
///
|
||||
/// Note that the array of menu commands is expected to be in program
|
||||
/// memory.
|
||||
struct command {
|
||||
const char command[MENU_COMMAND_MAX]; ///< name of the command
|
||||
int (*func)(uint8_t argc, const struct arg *argv); ///< callback function
|
||||
};
|
||||
|
||||
/// constructor
|
||||
///
|
||||
/// @param prompt The prompt to be displayed with this menu.
|
||||
/// @param commands An array of ::command structures.
|
||||
/// @param entries The number of entries in the menu.
|
||||
///
|
||||
Menu(const char *prompt, const struct command *commands, uint8_t entries);
|
||||
|
||||
/// menu runner
|
||||
void run(void);
|
||||
|
||||
private:
|
||||
void _help(void); ///< implements the 'help' command
|
||||
const char *_prompt; ///< prompt to display
|
||||
const command *_commands; ///< array of commands
|
||||
const uint8_t _entries; ///< size of the menu
|
||||
|
||||
static char _inbuf[MENU_COMMANDLINE_MAX]; ///< input buffer
|
||||
static arg _argv[MENU_ARGS_MAX + 1]; ///< arguments
|
||||
};
|
||||
|
||||
/// Macro used to define a menu.
|
||||
///
|
||||
/// Use name.run() to run the menu.
|
||||
///
|
||||
#define MENU(name, prompt, commands) \
|
||||
static const char __menu_name__ ##name[] PROGMEM = prompt; \
|
||||
static Menu name(__menu_name__ ##name, commands, sizeof(commands) / sizeof(commands[0]))
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
|
||||
|
||||
//
|
||||
// Simple commandline menu system.
|
||||
//
|
||||
|
||||
#include <FastSerial.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "include/menu.h"
|
||||
|
||||
// statics
|
||||
char Menu::_inbuf[MENU_COMMANDLINE_MAX];
|
||||
Menu::arg Menu::_argv[MENU_ARGS_MAX + 1];
|
||||
|
||||
// constructor
|
||||
Menu::Menu(const prog_char *prompt, const Menu::command *commands, uint8_t entries) :
|
||||
_prompt(prompt),
|
||||
_commands(commands),
|
||||
_entries(entries)
|
||||
{
|
||||
}
|
||||
|
||||
// run the menu
|
||||
void
|
||||
Menu::run(void)
|
||||
{
|
||||
uint8_t len, i, ret;
|
||||
uint8_t argc;
|
||||
int c;
|
||||
func fn;
|
||||
|
||||
// loop performing commands
|
||||
for (;;) {
|
||||
|
||||
// loop reading characters from the input
|
||||
len = 0;
|
||||
Serial.printf("%S] ", _prompt);
|
||||
for (;;) {
|
||||
c = Serial.read();
|
||||
if (-1 == c)
|
||||
continue;
|
||||
// carriage return -> process command
|
||||
if ('\r' == c) {
|
||||
_inbuf[len] = '\0';
|
||||
Serial.write('\r');
|
||||
Serial.write('\n');
|
||||
break;
|
||||
}
|
||||
// backspace
|
||||
if ('\b' == c) {
|
||||
if (len > 0) {
|
||||
len--;
|
||||
Serial.write('\b');
|
||||
Serial.write(' ');
|
||||
Serial.write('\b');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// printable character
|
||||
if (isprint(c) && (len < (MENU_COMMANDLINE_MAX - 1))) {
|
||||
_inbuf[len++] = c;
|
||||
Serial.write((char)c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// split the input line into tokens
|
||||
argc = 0;
|
||||
_argv[argc++].str = strtok(_inbuf, " ");
|
||||
while (argc <= MENU_ARGS_MAX) {
|
||||
_argv[argc].str = strtok(NULL, " ");
|
||||
if ('\0' == _argv[argc].str)
|
||||
break;
|
||||
_argv[argc].i = atol(_argv[argc].str);
|
||||
_argv[argc].f = atof(_argv[argc].str); // calls strtod, > 700B !
|
||||
argc++;
|
||||
}
|
||||
|
||||
// look for a command matching the first word (note that it may be empty)
|
||||
for (i = 0; i < _entries; i++) {
|
||||
if (!strcmp_P(_argv[0].str, _commands[i].command)) {
|
||||
fn = (func)pgm_read_word(&_commands[i].func);
|
||||
ret = fn(argc, &_argv[0]);
|
||||
if (-2 == ret)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if the menu doesn't provide more comprehensive help, print the command list
|
||||
if ((i == _entries) && (!strcmp(_argv[0].str, "?") || (!strcmp_P(_argv[0].str, PSTR("help")))))
|
||||
_help();
|
||||
}
|
||||
}
|
||||
|
||||
// display the list of commands in response to the 'help' command
|
||||
void
|
||||
Menu::_help(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
Serial.println("Commands:");
|
||||
for (i = 0; i < _entries; i++)
|
||||
Serial.printf(" %S\n", _commands[i].command);
|
||||
}
|
Loading…
Reference in New Issue