Skip to content

Option Parsing

Arg::Opt provides a convenient way to define and parse options for custom shell commands. It supports boolean flags, string options, integer options, and float options.

Building and Flashing the Program

Create a new Pico SDK project named customcmd-option.

Create Pico SDK Project
  1. In VSCode, run >Raspberry Pi Pico: New Pico Project in the command palette.
  2. In the dialog below, select C/C++. new-project-dialog

  3. Create a project with the following settings:

    • Name ... Enter the project name.
    • Board type ... Select your board type.
    • Location ... Select the parent directory where the project directory will be created.
    • Stdio support ... Leave Console over USB unchecked when you use LABOPlatform or other USB features because they conflict with each other. You can enable it by editing the CMakeLists.txt file later.
    • Code generation options ... Check Generate C++ code.

new-project

Open Existing Pico SDK Project

Open the project folder in VSCode using one of the following methods:

  • In a command prompt, change the current directory to the project folder and execute code ..
  • In a Explorer, choose the project folder, push Alt+D to focus the address bar, and execute code ..

    vscode-from-explorer

If the folder is already prepared as a Pico SDK project, just proceed with editing and building the project.

If not, you will see the following message at the bottom right corner of VSCode.

do-you-want-to-import

Click Yes and you will see the following window.

import-project

Click Import and the project will be prepared as a Pico SDK project.

When the Do you want to import this project as Raspberry Pi Pico project? message disappears before you click Yes, you can reveal it by clicking the icon notify-icon at the bottom right corner of VSCode.

Clone the pico-jxglib repository from GitHub so the direcory structure looks like this:

├── pico-jxglib/
└── customcmd-option/
    ├── CMakeLists.txt
    ├── customcmd-option.cpp
    └── ...
Clone the Repository

Change the current directory to the parent directory where you want to clone the pico-jxglib repository and run the following commands:

$ git clone https://github.com/ypsitau/pico-jxglib.git
$ cd pico-jxglib
$ git submodule update --init --recursive

pico-jxglib is updated almost daily. If you've already cloned it, run the following command in the pico-jxglib directory to get the latest version:

$ git pull

A directory from a Git repository can safely be moved to another location even after cloning.

Add the following lines to the end of CMakeLists.txt:

CMakeLists.txt
1
2
3
target_link_libraries(customcmd-option
    jxglib_Shell jxglib_Serial jxglib_ShellCmd_Basic)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)

Enable UART or USB stdio as described below.

Enable UART and/or USB stdio

Find the following lines in CMakeLists.txt:

CMakeLists.txt
pico_enable_stdio_uart(your-project 0)
pico_enable_stdio_usb(your-project 0)

You can set the value to 1 to enable stdio or 0 to disable it.

  • pico_enable_stdio_uart() enables UART stdio that uses GPIO0 (UART0 TX) and GPIO1 (UART0 RX).
  • pico_enable_stdio_usb() enables USB stdio that uses the USB interface. Make sure to disable USB stdio when you use USB features because they conflict with each other.

Edit customcmd-option.cpp as follows:

customcmd-option.cpp
#include "pico/stdlib.h"
#include "jxglib/Serial.h"
#include "jxglib/Shell.h"

using namespace jxglib;

ShellCmd(customcmd, "tests command line arguments")
{
    static const Arg::Opt optTbl[] = {
        Arg::OptBool("help",   'h', "display help"),
        Arg::OptBool("flag",   'b', "bool value"),
        Arg::OptString("str",  's', "string value", "STR"), 
        Arg::OptInt("num",     'i', "int value",    "NUM"),
        Arg::OptFloat("float", 'f', "float value",  "FLOAT"),
    };
    Arg arg(optTbl, count_of(optTbl));
    if (!arg.Parse(terr, argc, argv)) return Result::Error;
    if (arg.GetBool("help")) {
        arg.PrintHelp(terr);
        return Result::Success;
    }
    for (int i = 0; i < argc; i++) {
        tout.Printf("argv[%d] \"%s\"\n", i, argv[i]);
    }
    tout.Printf("flag  %s\n", arg.GetBool("flag")? "true" : "false");
    const char* strValue;
    if (arg.GetString("str", &strValue)) {
        tout.Printf("str   \"%s\"\n", strValue);
    } else {
        tout.Printf("str   (none)\n");
    }
    int intValue;
    if (arg.GetInt("num", &intValue)) {
        tout.Printf("num   %d\n", intValue);
    } else {
        tout.Printf("num   (none)\n");
    }
    float floatValue;
    if (arg.GetFloat("float", &floatValue)) {
        tout.Printf("float %f\n", floatValue);
    } else {
        tout.Printf("float (none)\n");
    }
    return Result::Success;
}

int main(void)
{
    ::stdio_init_all();
    Serial::Terminal terminal;
    terminal.Initialize();
    Shell::AttachTerminal(terminal);
    for (;;) {
        Tickable::Tick();
    }
}

Build and flash the program to the board.

Build and Flash

The simplest way to build and flash the program is to use the UF2 file generated by the build process. Pico board, a USB cable, and a computer are all you need to get started. Here are the steps to build and flash the program:

  1. Pressing F7 on VSCode will build the project. If this is the first time you build the project, you will see the dialog shown below. Select Pico Using compilers: ... and the build process will start.

    select-a-kit

  2. After building, you can find the generated UF2 file in the build directory.

  3. Connect your Pico to the computer using a USB cable while holding the BOOTSEL button, and it will appear as a mass storage device. Copy the generated UF2 file to this device to flash it. No need to mind the destination directory, just copy it to the root directory of the device.

If you have a debug probe like this, you can also flash the program using OpenOCD and GDB. This is the recommended method for development, as it allows you to debug the program while running it on the board!

Running the Program

Open a terminal emulator to connect it.

Setup Tera Term for LABOPlatform

Tera Term is available here.

From the menu bar, select [File] - [New Connection...] to open the dialog below:

teraterm-new-connection

pico-jxgLABO or a firmware that links jxglib_LABOPlatform provides two USB serial ports: one for terminal use and the other for applications such as logic analyzers and plotters. Select one of the ports and press Enter key in the terminal. When successfully connected, you will see a prompt in the terminal.

L:/>

Run the command with no arguments:

>customcmd
argv[0] "customcmd"
flag  false
str   (none)
num   (none)
float (none)

Run the command with a bool flag:

>customcmd --flag
argv[0] "customcmd"
flag  true
str   (none)
num   (none)
float (none)

Run the command with a string option:

>customcmd --str=value1
argv[0] "customcmd"
flag  false
str   "value1"
num   (none)
float (none)

Run the command with an int option:

>customcmd --num=1234
argv[0] "customcmd"
flag  false
str   (none)
num   1234
float (none)

Run the command with a float option:

>customcmd --float=3.14
argv[0] "customcmd"
flag  false
str   (none)
num   (none)
float 3.140000

Run the command with multiple options:

>customcmd --str=value1 --num=1234 --float=3.14
argv[0] "customcmd"
flag  false
str   "value1"
num   1234
float 3.140000

Run the command with multiple options in short form:

>customcmd -s value1 -i 1234 -f 3.14
argv[0] "customcmd"
flag  false
str   "value1"
num   1234
float 3.140000

Print the help message:

>customcmd --help
Options:
 -h --help        display help
 -b --flag        bool value
 -s --str=STR     string value
 -i --num=NUM     int value
 -f --float=FLOAT float value

Program Explanation

Here is a brief explanation of the program.

static const Arg::Opt optTbl[] = {
    Arg::OptBool("help",   'h', "display help"),
    Arg::OptBool("flag",   'b', "bool value"),
    Arg::OptString("str",  's', "string value", "STR"), 
    Arg::OptInt("num",     'i', "int value",    "NUM"),
    Arg::OptFloat("float", 'f', "float value",  "FLOAT"),
};

Define the options table using Arg::Opt. Each option is defined using one of the following functions, depending on the type of the option.

Function Description
Arg::OptBool Defines a boolean option
Arg::OptString Defines a string option
Arg::OptInt Defines an integer option
Arg::OptFloat Defines a float option
  • The first argument is the long name of the option, which is used in the command line as --long-name.
  • The second argument is the short name of the option, which is used in the command line as -s (if the short name is 's'). If there is no short name, set it to 0.
  • The third argument is the description of the option, which is displayed in the help message.
  • The fourth argument is the placeholder for the option value in the help message, which is only used for non-boolean options. For example, for the --str option, the help message will show --str=STR, where STR is the placeholder.
Arg arg(optTbl, count_of(optTbl));
if (!arg.Parse(terr, argc, argv)) return Result::Error;

Create an Arg instance and parse the command line arguments. The constructor of Arg takes the options table and the number of options as arguments. Then, call arg.Parse to parse the command line arguments. If parsing fails, return an error result. Arguments that are recognized as options will be removed from argv, and argc will be updated accordingly.

Option values can be retrieved using the following functions:

bool defined = arg.GetBool("flag")

Returns true if the --flag option is specified, or false if it is not specified.

const char* strValue;
bool defined = arg.GetString("str", &strValue)

Returns true if the --str option is specified, and sets strValue to the string value of the option. Returns false if the option is not specified.

int intValue;
bool defined = arg.GetInt("num", &intValue)

Returns true if the --num option is specified, and sets intValue to the integer value of the option. Returns false if the option is not specified.

float floatValue;
bool defined = arg.GetFloat("float", &floatValue)

Returns true if the --float option is specified, and sets floatValue to the float value of the option. Returns false if the option is not specified.