Initial Check-in of CLI Library

This commit is contained in:
2019-04-06 15:20:52 +02:00
commit 47b5f8d1ea
15 changed files with 2477 additions and 0 deletions

226
examples/Blink/Blink.ino Normal file
View File

@@ -0,0 +1,226 @@
/*
Blink.ino - CLI library sample implementing a CLI for the Blink sample code
Version 1.0, latest version, documentation and bugtracker available at:
https://gitlab.lindenaar.net/arduino/CLI
Copyright (c) 2019 Frederik Lindenaar
This example shows how to build a simple command line to control the default
Blink example of the Arduino IDE. This requires additional code to control
the parameters (maintained within the C++ objects for the commands) as well
as a different main loop logic to become a real-time process with a Command
Line in te backgrouns while blinking and no longer using delay() for timing.
The implementation provides the following commands available through the
Serial Monitor (available from the Tools menu of the Arduino IDE):
- mode display and/or set the LED mode (either on, off or blink)
- delay display and/or set the blinking delay for the led
- faster double the blinking rate (half the delay)
- slower half the blinking rate (double the delay)
- help show the available commands and how to use (builtin command)
For each of the commands an CLI_Command derived class is implemented with :
- Private variables to hold the internal state of the command
- Object constructor to set default values
- a setparams() method to handle parameters (when applicable)
- a execute() method for the implementation of the command
- Getter (and Setter) methods to access object state (where applicable)
This sketch is free software: you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published by the
Free Software Foundation, or (at your option) a later version of the license.
This code is distributed in the hope that it will be useful but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, visit <http://www.gnu.org/licenses/> to download it.
*/
#include <CLI.h>
enum LED_MODE { OFF, ON, BLINKING }; // LED modes implemented/supported
#define BLINK_MIN_DELAY 25 // min blink delay is 25ms
#define BLINK_MAX_DELAY 10000 // max blink delay is 10s
// Convert macro to string (https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html)
#define TO_STRING(val) #val
#define VAL_TO_STRING(val) TO_STRING(val)
// Store mode command parameters as static PROGMEM strings (needed below)
const char CMD_MODE_PARAM_ON[] PROGMEM = "on";
const char CMD_MODE_PARAM_OFF[] PROGMEM = "off";
const char CMD_MODE_PARAM_BLINK[] PROGMEM = "blink";
// struct stored in PROGMEM to map the parameters for mode command to a value
const struct CLI_Command_Param Mode_Command_Parameters[] PROGMEM = {
{ CMD_MODE_PARAM_ON, ON, },
{ CMD_MODE_PARAM_OFF, OFF, },
{ CMD_MODE_PARAM_BLINK, BLINKING, },
{ NULL }
};
class Mode_Command : CLI_Command { // Implementation of the mode command
LED_MODE _mode; // Private variable to store current mode
public:
Mode_Command(CLI &cli, LED_MODE mode = OFF) : // Constructor with default initial mode
CLI_Command(cli, // CLI to register with
PSTR("mode"), // Command name
PSTR("Set and/or show LED mode"), // Description
PSTR("Usage:\tmode [<led_mode>]\n" // Usage Help
"where <led_mode> is one of:\n"
"\ton\tturn LED on\n"
"\toff\tturn LED off\n"
"\tblink\tblink the LED\n")), _mode(mode) { };
bool setparams(const char *params) { // Called once before execute to process parameters
if (params) { // Check if we have parameters
// Use the CLI_STR_parseParam_P utility function to parse the parameter
const struct CLI_Command_Param *p = CLI_STR_parseParam_P(params, Mode_Command_Parameters);
if (!p) return false; // return false in case of an invalid parameter
_mode = pgm_read_word(&p->uint16); // set led_mode to the one of the parameter
if (_mode != BLINKING) // ensure LED state == led_mode if not blinking
digitalWrite(LED_BUILTIN, _mode == ON);
}
return true; // Return true in case of no or a valid parameter
}
bool execute(CLI &cli) { // Implementation, called as long as it returns true
cli.print_P(PSTR("LED mode is ")); // Simply show the LED mode (need to do that always)
cli.print_P((_mode == ON) ? PSTR("ON") :
(_mode == OFF) ? PSTR("OFF") :
(_mode == BLINKING) ? PSTR("blink") : PSTR("unknown"));
return false; // return false as we're done
}
inline LED_MODE get() { // Getter for the mode
return _mode;
}
inline void set(LED_MODE mode) { // Setter for the mode
_mode = mode;
}
};
class Delay_Command : CLI_Command { // Implementation of the delay command
unsigned int _delay; // Private variable to store current delay
public:
Delay_Command(CLI &cli) : // Constructor
CLI_Command(cli, // CLI to register with
PSTR("delay"), // Command name
PSTR("set and/or show LED blink delay"), // Description
PSTR("Usage:\tdelay [<blink_delay>]\n" // Usage Help
"\twhere <blink_delay> is the delay in milliseconds (integer between "
VAL_TO_STRING(BLINK_MIN_DELAY) " and " VAL_TO_STRING(BLINK_MAX_DELAY) ")\n")) {
_delay = 1000;
};
bool setparams(const char *params) { // Called once before execute to process parameters
if (params) { // Check if we have parameters
int d;
params = CLI_STR_parseInt(params, d); // parse params using CLI_STR_parseInt utility function
if (params && !*params) { // check if an int was found and reached end of params
return set(d); // return true if all OK or false when not within range
}
return false; // return false if no int or more param input was found
}
return true; // return true in case of no or a valid parameter
}
bool execute(CLI &cli) { // Implementation, called as long as it returns true
cli.print_P(PSTR("blink delay ")); // Simply show the blink delay (need to do that always)
cli.print(_delay);
cli.print_P(PSTR(" milliseconds\n"));
return false; // return false as we're done
}
inline unsigned int get() { // Getter for the mode
return _delay;
}
inline bool set(unsigned int d) { // Setter for the mode, checking it is within range
if (d < BLINK_MIN_DELAY || d > BLINK_MAX_DELAY)
return false; // Return false if new value is out of bounds
_delay = d; // store new value
return true; // return true as new value is OK
}
};
class Faster_Command : CLI_Command { // Implementation of the faster command
Delay_Command &_delay; // Private variable to store delay command
public:
Faster_Command(CLI &cli, Delay_Command &delay) : // Constructor, requires delay command reference
CLI_Command(cli, // CLI to register with
PSTR("faster"), // Command name
PSTR("Blink faster")), // Description, no Usage Help provided
_delay(delay) { }; // Store reference to delay command, empty constructor
// Please note: no parameters will be accepted (using default setparams() implementation)
bool execute(CLI &cli) { // Implementation, called as long as it returns true
if (_delay.set(_delay.get() / 2)) { // Half the blink time, returns false if outside range
_delay.setparams(0); // clear _delay's parameters as we call it's execute() next
_delay.execute(cli); // call _delay's execute() method to print the delay
} else {
cli.print_P(PSTR("Can't blink faster"));// print an error message if half blink delay is out of bounds
}
return false; // return false as we're done
}
};
class Slower_Command : CLI_Command { // Implementation of the slower command
Delay_Command &_delay; // Private variable to store delay command
public:
Slower_Command(CLI &cli, Delay_Command &delay) : // Constructor, requires delay command reference
CLI_Command(cli, // CLI to register with
PSTR("slower"), // Command name
PSTR("Blink slower")), // Description, no Usage Help provided
_delay(delay) { }; // Store reference to delay command, empty constructor
// Please note: no parameters will be accepted (using default setparams() implementation)
bool execute(CLI &cli) { // Implementation, called as long as it returns true
if (_delay.set(_delay.get() * 2)) { // Half the blink time, returns false if outside range
_delay.setparams(0); // clear _delay's parameters as we call it's execute() next
_delay.execute(cli); // call _delay's execute() method to print the delay
} else {
cli.print_P(PSTR("Can't blink slower"));// print an error message if half blink delay is out of bounds
}
return false; // return false as we're done
}
};
// Initialize the Blink Command Line Interface
const char banner[] PROGMEM = "Blink Sample CLI"; // Banner to show upon startup of the CLI
CLI CLI(Serial, banner); // Initialize the CLI, telling it to attach to Serial
Mode_Command Mode(CLI, BLINKING); // Initialize/Register mode command, BLINKING initially
Delay_Command Delay(CLI); // Initialize/Register delay command
Faster_Command Faster(CLI, Delay); // Initialize/Register faster command
Slower_Command Slower(CLI, Delay); // Initialize/Register slower command
Help_Command Help(CLI); // Initialize/Register (built-in) help command
// the setup function runs once when you reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
// Initialize the Serial port for the CLI
while (!Serial); // For Leonardo: wait for serial USB to connect
Serial.begin(9600);
}
// variable to be preserved
unsigned long last_blink = 0;
// the loop function runs over and over again forever
void loop() {
// First handle CLI, if this returns true, a command is running and the code
// block with blink logic is skipped till the command has finished executing
if (!CLI.process()) {
// check if mode is blinking and last_blink was longer ago than delay
// millis() will wrap every 49 days, below approach is wrap-proof
if (Mode.get() == BLINKING && millis() - last_blink > Delay.get()) {
last_blink = millis(); // led state will change, store when
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // invert LED PIN
}
}
}

View File

@@ -0,0 +1,94 @@
/*
DS1307RTC.ino - CLI library sample implementing a CLI to control a DS1307 RTC
Version 1.0, latest version, documentation and bugtracker available at:
https://gitlab.lindenaar.net/arduino/CLI
Copyright (c) 2019 Frederik Lindenaar
This example shows how to add commands to read and set an DS1307 RTC (Real
Time Clock) module to the CLI. It extends the Debug example so that the CLI
library debugging commands are also included to check the I2C bus wiring and
access the module's NVRAM and EEPROM. The implementation uses Paul
Stoffregen's DS1307RTC library (https://github.com/PaulStoffregen/DS1307RTC)
so please ensure you have added that (and it's dependency TimeLib) through
the Aruino IDE's built-in library manager.
The following commands are available through the Serial Monitor (available
from the Tools menu of the Arduino IDE):
- ds1307 Set and/or show DS1307 RTC date and time
- eeprom_dump dump the contents of the built-in EEPROM
- i2c_scan scan the I2C bus for slave devices
- i2c_dump dump the contents of I2C attached EEPROM or Memory
- reset restart the microcontroller (software reset)
- help show the available commands and how to use them
To read the the DS1307 use:
ds1307 show the current time of the DS1307
i2c_dump 0x68 64 8 single show NVRAM (assuming device ID is 0x68)
(dump 56-byte NVRAM, start at offset 8)
To set the DS1307 clock use:
ds1307 2019-01-01 12:34:56 to set both the date and time
ds1307 2019-01-31 to set only the date
ds1307 12:34 to set only the time (seconds set to 0)
When your module also has an EEPROM (assuming device ID is 0x50) use:
i2c_dump 0x50 4096 dump 24LC32 EEPROM contents (4k)
Please note that the ds1307 command is implemented in separate files (that
opens in a separate tab in the Arduino IDE)
This sketch is free software: you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published by the
Free Software Foundation, or (at your option) a later version of the license.
This code is distributed in the hope that it will be useful but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, visit <http://www.gnu.org/licenses/> to download it.
*/
#include <CLI.h>
#include <Wire.h>
#include "DS1307_Command.h"
// Initialize the Debug Command Line Interface
const char banner[] PROGMEM = "DS1307RTC Sample CLI"; // Banner to show upon startup of the CLI
CLI CLI(Serial, banner); // Initialize the CLI, telling it to attach to Serial
DS1307_Command DS1307RTC(CLI); // Initialize/Register ds1307 command
EEPROM_Dump_Command EEPROM_Dump(CLI); // Initialize/Register (built-in) eeprom_dump command
I2C_Scan_Command I2C_Scan(CLI); // Initialize/Register (built-in) i2c_scan command
I2C_Dump_Command I2C_Dump(CLI); // Initialize/Register (built-in) i2c_dump command
Reset_Command Reset(CLI); // Initialize/Register (built-in) reset command
Help_Command Help(CLI); // Initialize/Register (built-in) help command
// the setup function runs once when you reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
// Initialize the Serial port for the CLI
while (!Serial); // For Leonardo: wait for serial USB to connect
Serial.begin(9600);
// Initialize the Wire Interface
Wire.begin();
}
// the loop function runs over and over again forever
void loop() {
// handle CLI, if this returns true a command is running. Set Builtin LED accordingly
if (CLI.process()) {
digitalWrite(LED_BUILTIN, HIGH); // turn LED on when processing CLI command
} else {
digitalWrite(LED_BUILTIN, LOW); // turn LED off when not processing CLI command
}
}

View File

@@ -0,0 +1,114 @@
/*
DS1307_Command.cpp - CLI library for Arduino/ESP8266 - DS1307 Command implementation
Version 1.0, latest version, documentation and bugtracker available at:
https://gitlab.lindenaar.net/arduino/CLI
Copyright (c) 2019 Frederik Lindenaar
This library is free software: you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published by the
Free Software Foundation, or (at your option) a later version of the license.
This code is distributed in the hope that it will be useful but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, visit <http://www.gnu.org/licenses/> to download it.
*/
#include <TimeLib.h>
#include <DS1307RTC.h>
#include "DS1307_Command.h"
DS1307_Command::DS1307_Command(CLI &cli) : // Constructor for ds1307 command
CLI_Command(cli, // CLI to register with
PSTR("ds1307"), // Command name
PSTR("Set and/or show DS1307 RTC date and time"), // Description
PSTR("Usage:\tds1307 [YYYY-MM-DD] [HH:MM[:SS]]\n" // Usage Help
"where YYYY-MM-DD is the date in ISO format\n"
" and HH:MM[:SS] the new time (seconds are optional)\n")) { };
// macro to determine whether the given year is a leap year
#define LEAP_YEAR(y) (!((y) % 4) && (((y) % 100 ) || !((y) % 400) ))
inline uint8_t monthDays(int y, uint8_t m) { // get number of days for month
return (m == 2) ? 28 + LEAP_YEAR(y) : 30 + ((m & 1) == (m <= 7));
}
// Function below parses the string passed in params (if provided) and sets the
// time accordingly. returns false in case an invalid date/time was found (and
// an invalid parameter message will be given) and true in case of no params or
// when a string of the format: [YYYY-MM-DD] [HH:MM[:SS]] is found. In case a
// partial date/time is provided, the remaining part will be substituted with
// the DS1307's current value. Additional whitespaces are ignored and values are
// checked for validity.
bool DS1307_Command::setparams(const char *params) {
if (!params) return true; // return true when no parameter
int value;
tmElements_t tm;
if (const char *sep = CLI_STR_parseInt(params, value)) { // parse first integer value
RTC.read(tm); // Got a valid start get current time
// check if character following the integer is a '-' and we have a valid year
if (*sep == '-' && value >= tmYearToCalendar(0) && value <= tmYearToCalendar(255)) {
// Got a valid year and date separator, parse the rest of the date
tm.Year = CalendarYrToTm(value);
if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 1 && value <= 12 && *sep == '-') {
tm.Month = value;
if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 1 &&
value <= monthDays(tmYearToCalendar(tm.Year), value)) {
tm.Day = value;
sep = CLI_STR_skipSep(sep);
if (*sep) sep = CLI_STR_parseInt(sep, value); // parse the hour integer
} else return false; // exit if parsing a date and day is invald
} else return false; // exit if parsing a date and month is invald
}
// check if character following the integer is a ':' and we have a valid hour
if (*sep == ':' && value >= 0 && value < 24) {
// Got a valid hour and time separator, parse the rest of the time
tm.Hour = value;
if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 0 && value <= 59) {
tm.Minute = value;
if (*sep == ':') { // if next char is a separator, parse seconds
if ((sep = CLI_STR_parseInt(sep + 1, value)) && value >= 0 && value <= 59) {
tm.Second = value;
} else return false;
} else tm.Second = 0; // otherwise set seconds to 0
} else return false; // exit if minutes is invalid
}
if (*CLI_STR_skipSep(sep)) return false; // if not the end of the string, return false
RTC.write(tm); // if we get here, we have a valid time in tm, set it
return true; // return true as we got valid input
}
return false; // no valid parameter, return false
}
bool DS1307_Command::execute(CLI &cli) { // execute prints the current time
tmElements_t tm;
if (RTC.read(tm)) { // get current time from DS1307
cli.print(dayStr(tm.Wday)); // print day of week
cli.print(' ');
cli.print(tm.Day); // print day of month
cli.print(' ');
cli.print(monthStr(tm.Month)); // print month name
cli.print(' ');
cli.print(tmYearToCalendar(tm.Year)); // print year
cli.print(' ');
cli.print2digits(tm.Hour); // print hour
cli.print(':');
cli.print2digits(tm.Minute); // print minute
cli.print(':');
cli.print2digits(tm.Second); // print seconds
} else { // if get time fails, print an error
if (RTC.chipPresent()) { // if a chip was found, it is not set
cli.print_P(PSTR("Error: cannot read RTC clock, please set time\n"));
} else { // otherwise tell user no DS1307 was found
cli.print_P(PSTR("Error: RTC clock not found, check wiring\n"));
}
}
return false; // We're done and should not be called again
}

View File

@@ -0,0 +1,29 @@
/*
DS1307_Command.cpp - CLI library for Arduino/ESP8266 - DS1307 Command definitions
Version 1.0, latest version, documentation and bugtracker available at:
https://gitlab.lindenaar.net/arduino/CLI
Copyright (c) 2019 Frederik Lindenaar
This library is free software: you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published by the
Free Software Foundation, or (at your option) a later version of the license.
This code is distributed in the hope that it will be useful but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, visit <http://www.gnu.org/licenses/> to download it.
*/
#include <CLI.h>
class DS1307_Command : public CLI_Command { // Definition of ds1307 command
public:
DS1307_Command(CLI &cli); // Constructor, requires CLI
bool setparams(const char *params); // Parameter parser
bool execute(CLI &cli); // Implementation of logic
};

71
examples/Debug/Debug.ino Normal file
View File

@@ -0,0 +1,71 @@
/*
Debug.ino - CLI library sample implementing a CLI for the Blink sample code
Version 1.0, latest version, documentation and bugtracker available at:
https://gitlab.lindenaar.net/arduino/CLI
Copyright (c) 2019 Frederik Lindenaar
This sketch demonstrates the debugging commands available as part of the CLI
library. These commands are intended to eliminate the need to write code to
check things in or connected to your microcontroller. Over time, additional
commands will be added, when needed (feel free to submit your favorites for
inclusion!). At this moment it makes the following commands available through
the Serial Monitor (available from the Tools menu of the Arduino IDE):
- eeprom_dump dump the contents of the built-in EEPROM
- i2c_scan scan the I2C bus for slave devices
- i2c_dump dump the contents of I2C attached EEPROM or Memory
- reset restart the microcontroller (software reset)
- help show the available commands and how to use them
For each of these commands the implementation is included in the CLI library.
This sketch is free software: you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published by the
Free Software Foundation, or (at your option) a later version of the license.
This code is distributed in the hope that it will be useful but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, visit <http://www.gnu.org/licenses/> to download it.
*/
#include <CLI.h>
#include <Wire.h>
// Initialize the Debug Command Line Interface
const char banner[] PROGMEM = "Debug Sample CLI"; // Banner to show upon startup of the CLI
CLI CLI(Serial, banner); // Initialize the CLI, telling it to attach to Serial
EEPROM_Dump_Command EEPROM_Dump(CLI); // Initialize/Register (built-in) eeprom_dump command
I2C_Scan_Command I2C_Scan(CLI); // Initialize/Register (built-in) i2c_scan command
I2C_Dump_Command I2C_Dump(CLI); // Initialize/Register (built-in) i2c_dump command
Reset_Command Reset(CLI); // Initialize/Register (built-in) reset command
Help_Command Help(CLI); // Initialize/Register (built-in) help command
// the setup function runs once when you reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
// Initialize the Serial port for the CLI
while (!Serial); // For Leonardo: wait for serial USB to connect
Serial.begin(9600);
// Initialize the Wire Interface
Wire.begin();
}
// the loop function runs over and over again forever
void loop() {
// handle CLI, if this returns true a command is running. Set Builtin LED accordingly
if (CLI.process()) {
digitalWrite(LED_BUILTIN, HIGH); // turn LED on when processing CLI command
} else {
digitalWrite(LED_BUILTIN, LOW); // turn LED off when not processing CLI command
}
}