{"id":35031043,"url":"https://github.com/vulcu/terminal-commander","last_synced_at":"2026-04-27T10:31:57.937Z","repository":{"id":231185792,"uuid":"780736366","full_name":"vulcu/terminal-commander","owner":"vulcu","description":"Simple serial terminal interface for Arduino","archived":false,"fork":false,"pushed_at":"2026-03-24T21:45:41.000Z","size":246,"stargazers_count":1,"open_issues_count":6,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-26T03:27:00.396Z","etag":null,"topics":["arduino","arduino-library","device-control","i2c-bus","serial-communication"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vulcu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["vulcu"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2024-04-02T04:07:14.000Z","updated_at":"2025-08-06T23:53:58.000Z","dependencies_parsed_at":"2024-05-16T06:06:41.537Z","dependency_job_id":"1ecd6de6-26e3-408c-9530-693f9a0d555a","html_url":"https://github.com/vulcu/terminal-commander","commit_stats":null,"previous_names":["vulcu/terminal-commander"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vulcu/terminal-commander","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vulcu%2Fterminal-commander","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vulcu%2Fterminal-commander/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vulcu%2Fterminal-commander/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vulcu%2Fterminal-commander/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vulcu","download_url":"https://codeload.github.com/vulcu/terminal-commander/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vulcu%2Fterminal-commander/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32333196,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","arduino-library","device-control","i2c-bus","serial-communication"],"created_at":"2025-12-27T06:59:34.066Z","updated_at":"2026-04-27T10:31:57.921Z","avatar_url":"https://github.com/vulcu.png","language":"C++","funding_links":["https://github.com/sponsors/vulcu"],"categories":[],"sub_categories":[],"readme":"# terminal-commander\n\nTerminal Commander is an interactive serial terminal for Arduino, providing serial buffer parsing and command-line access to the I2C interface. The class is intended to streamline the creation of a simple command-line terminal on any Arduino device.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Basic Usage](#basic-usage)\n  - [Creating a Terminal Object](#creating-a-terminal-object)\n  - [Setup](#setup)\n  - [Loop](#loop)\n- [Using Built-In Commands](#using-built-in-commands)\n  - [Overloading Built-In Commands](#overloading-built-in-commands)\n- [Additional Functionality](#additional-functionality)\n  - [Enabling VT-100 Style Terminal Echo](#enabling-vt-100-style-terminal-echo)\n- [Creating User-Defined Terminal Commands](#creating-user-defined-terminal-commands)\n  - [Creating a Function Callback for a Custom Command](#creating-a-function-callback-for-a-custom-command)\n  - [Parsing Terminal Arguments in a User Command](#parsing-terminal-arguments-in-a-user-command)\n    - [Comparing Char Strings](#comparing-char-strings)\n  - [Using a Lambda Expression Instead of a Function](#using-a-lambda-expression-instead-of-a-function)\n\n## Installation\n\nInstall from the Arduino Library Manager, where it is listed as 'TerminalCommander'. Alternatively, Terminal Commander can also be installed as a Git submodule:\n\n```shell\ngit submodule add https://github.com/vulcu/terminal-commander.git \n# or\ngit submodule add git@github.com:vulcu/terminal-commander.git\n\n# followed by\ngit submodule --init\n```\n\n## Basic Usage\n\n### Creating a Terminal Object\n\nConstructing an instance of Terminal Commander requires a pointer to an instance of the Stream class, and a pointer to an instance of the TwoWire class. Optionally, a single-character command delimiter may be defined for deliniating custom user commands and their arguments. The default command delimiter is a space.\n\n```cpp\n// basic instantiation using the default command delimiter\nTerminalCommander::Terminal Terminal(\u0026Serial, \u0026Wire);\n\n// alternative instantiation using the custom command delimiter ':'\n// and the 'Serial1' hardware serial port for communication\nTerminalCommander::Terminal Terminal(\u0026Serial1, \u0026Wire, ':');\n```\n\nTerminal Commander will always use the newline character `\\n` (also known as `LF`) as the input buffer line ending. If necessary this can be changed by changing the `TERM_LINE_ENDING` definition in the header file, but `LF` is suggested.\n\n### Setup\n\nThe above instantiation should go at the top of your Arduino sketch, prior to the 'setup' section of the sketch (see the Terminal-LED-Control example). By default, Terminal Commander does not require any additional code in the 'setup' section of the sketch. However, the Stream and Wire classes each have their own  `begin()` methods and should be used as usual:\n\n```cpp\nvoid setup() {\n  // Initialize serial console and set baud rate.\n  // See Arduino 'Serial' documentation for details.\n  Serial.begin(9600);\n\n  // Initialize Wire library and set clock rate.\n  // See Arduino 'Wire' documentation for details.\n  Wire.begin();\n  Wire.setClock(400000);\n\n  // the rest of your setup code, etc. goes here\n}\n```\n\n### Loop\n\nTo use Terminal Commander interactively, simply add the following to the 'loop' section of your sketch:\n\n```cpp\nvoid loop() {\n  Terminal.loop();\n\n  // the rest of your loop code, etc. goes here\n}\n```\n\nPlease note that although Terminal Commander is very lightweight and fast, `Terminal.loop()` is only called once per iteration of the 'loop' section. So, if your 'loop' takes a long while to execute each iteration (e.g. if you are calling a non-asynchronous WiFi or MQTT library), Terminal Commander's responses and terminal echo function may appear sluggish.\n\n## Using Built-In Commands\n\nBy default, Terminal Commander has three built-in commands:\n\n- **SCAN**: Scan the I2C bus and return the I2C address of any device that acknowledges.\n- **I2C**:  Write (`i2c w`) or read (`i2c r`) the I2C bus directly, using the I2C address, register, and (in the case of a write) value.\n  - The I2C command supports sequential reads or writes, if supported by the I2C device.\n  - For example, to read four registers of some device with address **0x31** and starting at register address **0x02**, enter `i2c r 31 02 00 00 00` in the terminal.\n  - I2C commands must be submitted as two-digit hexadecimal byte values, e.g. `i2c r 31 01`  and not `i2c r 31 1`.\n  - Characters other than '**0-9**' and '**A-F**' will not be accepted for I2C reads/writes and will return an error.\n  - Spaces character delimiters are not necessary when using this command, so `i2c r 31 01` and `i2cr3101` are parsed the same.\n- **HELP**: (Implementation pending, see #5) Return this list of built-in commands and a usage summary for each. Also lists all user-defined commands, although it will not list any arguments to user-defined commands as these are outside the scope of the class.\n- All built-in commands are completely case insensitive, e.g. `scan`, `Scan`, and `SCAN` are all treated the same.\n  - NB: Only built-in commands are case-insensitive. User-defined commands _are_ case-sensitive (See [Creating User-Defined Terminal Commands](#creating-user-defined-terminal-commands) for more details).\n\n### Overloading Built-In Commands\n\nAll built-in commands can be overloaded by the user. Custom user commands are parsed and evaluated prior to evaluating any built-in commands. If you want to write your own `scan`, `i2c`, or `help` commands then simply create a custom command with that name:\n\n```cpp\nTerminal.onCommand(\"scan\", \u0026my_custom_scan_fn)\n```\n\nSee [Creating User-Defined Terminal Commands](#creating-user-defined-terminal-commands) for more details.\n\n## Additional Functionality\n\n### Enabling VT-100 Style Terminal Echo\n\nTerminal Commander supports VT-100 style terminal echo, for use with terminal programs that do not support local echo (e.g. the Arduino IDE Serial Monitor) or for which local echo is optional (e.g. TeraTerm, PuTTY). This feature is not compatible with some terminal programs which send the entire line in one transmission (e.g. VSCode).\n\nTo enable terminal echo, add the following lines to the 'setup' section of your Arduino sketch:\n\n```cpp\n// Add this inside the setup() block of your sketch\nTerminal.echo(true);\n```\n\nEnabling this feature will echo incoming terminal ASCII back to the source terminal. Terminal Commander correctly handles the 'backspace' input and will delete the previous terminal character. However, VT100-style control characters (`^[C`, `^[D`, etc.) are not supported, so Left/Right arrow keys will generate unrecognized inputs.\n\n## Creating User-Defined Terminal Commands\n\nUser-defined terminal commands can be easily created by calling the `onCommand` method in the 'setup' block of your sketch. All arguments following the user command (as defined [by the delimiter](#creating-a-terminal-object)) are passed directly to the user function with all whitespace, etc. intact.\n\nBy default, up to 10 user-defined functions can be created. This value can be modified by changing the `MAX_USER_COMMANDS` definition in the header file. Increasing the value will allow more commands at the expense of more SRAM usage, and conversely decreasing this value will decrease SRAM usage.\n\n### Creating a Function Callback for a Custom Command\n\nTo create a command called '**led**' and use it to turn on/off the built-in LED, first define the command and attach it to a callback function:\n\n```cpp\n// Add this inside the setup() block of your sketch\nTerminal.onCommand(\"led\", \u0026my_led_function);\n```\n\nThen define `my_led_function` outside of the 'setup' block, as you would define any other funtion:\n\n```cpp\nvoid my_led_function(char* args, size_t size) {\n  // your code goes here\n}\n```\n\nPlease note that functions definitions must match the type `TerminalCommander::user_callback_char_fn_`:\n\n```cpp\ntypedef void (user_callback_char_fn_t)(char*, size_t);\n```\n\n### Parsing Terminal Arguments in a User Command\n\nTerminal Commander will pass all terminal input following the first delimiter to your function as a pointer to a char array, and will pass the length of that char array as an integer of type `size_t`. These arguments can be checked and processed further inside of any user-defined function.\n\nUtilizing this functionality in your code is optional; if used it is highly recommended to include a check for 'no arguments'. If no arguments are passed to the user fuction (e.g. no terminal input followed the delimiter) then Terminal Commander will pass a `nullptr` to the callback function and a size of zero. This can be checked for easily in your code:\n\n```cpp\nif (args == nullptr || size == 0) {\n  // there was no terminal input following the delimiter\n}\n```\n\nIt is also recommended to copy the character array to a local variable within the function before doing anything with it:\n\n```cpp\nchar cmd[size + 1] = {'\\0'};\nmemcpy(cmd, args, size);\n```\n\n#### Comparing Char Strings\n\nChar strings, or char arrays, can be easily compared using the `strcmp()` function included with Terminal Commander. This version is identical to that of the standard `\u003cstring.h\u003e` library, and behaves exactly the same. It is included in Terminal Commander for convenience but is declared with `__attribute__((weak))` and will be overloaded if declared anywhere else in the code (e.g., if `\u003cstring.h\u003e` is included elsewhere). See official C/C++ documentation for details. \n\nUsing it to compare arguments passed to the user function in the above example would be:\n\n```cpp\nif (strcmp(cmd, \"on\") == 0) {\n  // The output of strcmp is 0 if the strings match\n}\nelse {\n  // The sign of the result is the sign of the difference between \n  // the values of the first pair of characters (both interpreted \n  // as unsigned char) that differ in the strings being compared\n}\n```\n\nPutting this all together, the `my_led_function` from the previous section could contain the following:\n\n```cpp\nvoid my_led_function(char* args, size_t size) {\n  if (args == nullptr || size == 0) {\n    Serial.println(F(\"Error: No LED state provided\"));\n    return;\n  }\n    \n  // type 'led on' or 'led off' in terminal turn built-in LED on/off\n  char cmd[size + 1] = {'\\0'};\n  memcpy(cmd, args, size);\n  if (strcmp(cmd, \"on\") == 0) {\n    // turn on built-in LED\n    digitalWrite(LED_BUILTIN, HIGH);\n  }\n  else if (strcmp(cmd, \"off\") == 0) {\n    // turn off built-in LED\n    digitalWrite(LED_BUILTIN, LOW);\n  }\n  else {\n    Serial.println(F(\"Error: Unrecognized LED state\"));\n  }\n}\n```\n\n### Using a Lambda Expression Instead of a Function\n\nIn place of a separately defined function, a lambda expression can also be used to define a user command. The lambda expression must also match the type `TerminalCommander::user_callback_char_fn_`:\n\n```cpp\ntypedef void (user_callback_char_fn_t)(char*, size_t);\n```\n\nFor example, using a lambda expression to define the `led` command detailed above would look like this:\n\n```cpp\n// Add this inside the setup() block of your sketch\nTerminal.onCommand(\"led\", [](char* args, size_t size) {\n  // your code goes here\n});\n```\n\nIf defining your custom commands using a lamda expression, no additional function definition is necessary. For this application, there are no behavioral or performance differences between these implementations. Both options are available to suit code structure and organizational preferences.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvulcu%2Fterminal-commander","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvulcu%2Fterminal-commander","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvulcu%2Fterminal-commander/lists"}