User key-script mapping function:
You can specify the mappings as @<key-1>=<script-description-1>
@<key-2>=<script-description-2>... @<key-N>=<script-description-N>.
Script-description is script-filename or '!'script-commands.
After that,
When you press ctrl-t and <key-n>, tio executes <script-description-n>.
This user keymap takes precedence over the default settings (except for
ctrl-t q).
Example of startup option:
tio /dev/ttyUSB1 --keymap '@1=!print(tio.banner())
@2=!tio.write("test\r") @ctrl-a=!tio.write("ctrl-a\r")
@ctrl-j=test-script.tio'
Example of .tioconfig: (note: backslash escape needed.)
keymap = @1=!print(tio.banner()) @2=!tio.write("test\\r")
@ctrl-a=!tio.write("ctrl-a\\r") @ctrl-j=test-script.tio
Fix the v3.9-incompatible changes in 8e02cde ("Lua API Timeout
Specification Changes to Enable Non-Blocking Reads", November 15, 2025)
to make them compatible.
The timeout arguments for tio.expect/s(), tio.read(), and tio.readline()
have been changed as follows:
- The timeout is in milliseconds, and the default is 0, which means to
wait forever (as in v3.9).
- A negative value means to nowait.
A constant table (tio.C) has also been added,
defining tio.C.FOREVER to 0 and tio.C.NOWAIT to -1.
The "shell command execution feature" previously only supported the
ability to transfer a command's stdout and stderr to a device.
To support bidirectional commands, a feature will be added to connect
input from a device to the command's stdin.
Since some communication commands (sz, rz) require stderr to be kept
local, a feature will also be added to not transfer stderr to a device
if the command string begins with a '?'.
On Linux, you can terminate a running command by pressing Ctrl-t R again
while the command is running (this uses the command /usr/bin/pkill
internally).
Non-interactive operations (input from a pipe, running a shell command,
XYMODEM transfers) often fail when mapping, soft flow control, or output
delay are enabled.
To reduce the hassle of switching settings, add the function shown in
the title.
The raw option can be set to one of the following:
- off ... no effects
- on ... soft-flow off, character mapping off, output delay enabled
- on-nodelay ... soft-flow off, character mapping off, output delay
disable
raw option is for Piped-input / Shell command execution / XYMODEM
transfering. default is on.
raw-interactive option is for socket-mode and normal terminal use.
default is off.
You can type Ctrl-t j if you need to change raw setting for
non-interactive case. it toggles the raw setting.
You can type Ctrl-t J if you need to change raw setting of interactive
case. it toggles the raw setting.
It is useful when transferring files in socket mode.
Add tty_raw_write function and reorganize roles of them.
tty_raw_write:
Performs any processing that must be done character-by-character on the
fly. That is, output delays and ONULBRK processing.
tty_write :
Converts input data using output mapping and passes it to tty_raw_write.
forward_to_tty :
Converts input data according to the input/output mode and passes it to
tty_write. (output-mapping function move to tty_write. tx_total
statistics counting move to tty_raw_write and tty_sync.)
On exit, do not reset the tty device settings to the state they were in
before the program started.
This is effective when sending characters at a low baud rate and then
immediately exiting, to ensure that the last character is output
correctly.
Typing "@repl" after Ctrl-t r, enter Lua REPL mode.
Typing "@exit" in REPL mode will return you to normal mode.
Note:
- the determination of continuation lines is not done automatically, and
if the end of a line is \, it is determined to be a continuation
instruction.
- In REPL mode, tio's main loop is blocked. (Ctrl-t q works.)
Example:
>> t = {1,2,3,4,5}
>> for _,v in ipairs(t) do\
>> print(v,"\r")\
>> end
Implementation improvements:
- Add tty_init() and script_interp_init() in order to only once
initialization.
- Fix script function to work without device_fd.
When calling the Lua interpreter with Ctrl-t r, change to start with the
previous call state.
This allows you to use it like REPL:
(example)
Ctrl-t r : my_library.lua
Ctrl-t r : !result1 = my_func("test1"); result2 = my_func("test2")
Ctrl-t r : !if (result1 == result2) then print("OK") else print("NG")
Ctrl-t r : @new
When you enter strings using Ctrl-t r, the string is interpreted as
follows:
- If the string does not begin with either "!" or "@", the string is
assumed to be the file name of a script and is executed.
- If the string begins with "!", the string excluding the "!" is
interpreted as Lua commands.
- If the string begins with "@", Strings beginning with "@" are
considered instructions to the interpreter. Currently valid instructions
are:
@new: Clear the Lua state. (== reset the Lua interpreter.)
@doopt: Execute the Lua script action specified by the option that start
with clearing the Lua state.
@nuldo=opt: do @doopt action when an empty string is entered (default).
@nuldo=none: Do nothing when an empty string is entered.
And now, lua interpreter start with GC.
If you need to stop GC, do lua function collectgarbage("stop").
Ctrl-t r/R/x/y commands require string input. To reduce the hassle of
repeatedly entering this information, I apply readline functions
(line-editing and history) to the string input.
Now tio have two histories: one for the line input-mode and one for the
ctrl-t command's string input.
And the following changes are made to the line input:
- Add prompts.
"> ": line input-mode, ">> " Ctrl-t string input.
- Omits duplicate lines from the history.
To implement the above functionality, we will modify the readline
functions to use the multi-instance style.
If output-line-delay is non-zero and output-delay is any, line delay
time set output-line-delay.
If output-line-delay is zero and output-delay is non-zero, line delay
time set output-delay.
It was previously (output-line-delay + output-delay) in any case.
Also, since the buffer was flushed after the line delay, there was a
possibility that the delay would be reduced or not applied at all.
This commit only effects Linux.
The description field of the `device_list`, populated by
`tty_search_for_serial_devices()`, was either incorrect or less than ideal
for CDC ACM virtual com ports. For instance:
(i) Some devices incorrectly have the description field populated by
the 'product' property of USB hub they are connected via.
(ii) Other devices have description fields populated with the interface,
e.g. CDC, when there is a 'product' property available that would give a
clearer description.
To solve these issues, we first prioritise searching for the 'product' property
of the device over the 'interface' property. We also look for the
'product' property in an additional directory.
This timestamp format will print the seconds since epoch along with
subdivision in microseconds.
Example:
[1748009585.087083] tio v3.9-8-g2fb788f-dirty
[1748009585.087156] Press ctrl-t q to quit
[1748009585.087683] Connected to /dev/ttyUSB0
Git is being dumb about
67c071633d This PR is identical to that one and will supercede it.
Fix --auto new and --auto latest on MacOS.
'device_list' was both a global (eww!) and a local inside
tty_search_for_serial_devices(). The local got set and
returned, so it looked sane, but the caller used the global
instead of the return value of the function it had just
called, meaning (global) device_list was NULL while
(ignored, local) device_list held a perfectly lovely
linked list.
Tested:
tio --auto new waits for a new device to appare and connects
tio --latest will connect to the most recently attached device
which, in most worlds, is the most recently enumerated USB
device, conveniently skipping all the bluetooth nonsense.
If the lone USB device is disconnected, it then connects to
one of those, meaning you really do have to restart tio.
for MacOS
- Added error handling and memory management
- Improved code readability and consistency
- Updated coding style to match project conventions
- Added robust error checking for CoreFoundation property retrieval
- Implemented more defensive memory allocation and type checking
- Switched to using callout device key for more reliable device discovery
- Added single-line block bracing consistent with project style
- Improved comments and code formatting
- Used `kIOCalloutDeviceKey` instead of `kIODialinDeviceKey` for device path retrieval
- Enhanced type checking for CoreFoundation objects
- Simplified memory management and error handling
- Added additional logging and error reporting
- Verified functionality on MacOS 10.11 and 10.15. Tested with ESP32-P4 and ESP32-BOX
Resolves potential device discovery and memory management issues in the MacOS serial device detection code.
+ Add missing timestamp-format epoch
+ Update send_ to use fsync and tcdrain like normal tty_sync does
+ Rework read_line to save partial line at timeout
+ Simplified read_line to reduce cyclomatic complexity
+ renamed example files read.lua and read_line.lua
+ moved #define READ_LINE_SIZE to top of file
+ renamed g_linebuf to linebuf, and moved it into read_line as a static variable
This caused a problem for some highly timing sensitive modem read-eval-print
loops because the input line and line termination characters (cr/nl) would be
shifted out on the UART with too big delay inbetween because of two
syncs.