One of the most useful debugging tools available to you is the terminal, which allows you to print live data from the brain to your computer.
print
and println
The easiest way to send some data to the terminal in a vexide program is through the print
family of macros. You might recall println
from our “Hello World” example:
#![no_std]#![no_main]use vexide::prelude::*;#[vexide::main]async fn main(peripherals: Peripherals) { println!("Hello World!");}
That will send Hello World!
followed by a newline to your computer over a USB or bluetooth connection. This is useful in cases where you need to quickly view or debug a value on the brain, or send some logs over for future use.
We also have a print
macro, which is the same as println
except it won’t end your message in a newline.
#[vexide::main]async fn main(peripherals: Peripherals) { print!("Hello"); print!("Hello");}
That will send the data HelloHello
to your computer.
That’s cool and all but… where am I supposed to be seeing all this?
Reading the Terminal
Simply running all of this code with no connection to a computer won’t visibly do anything, so let’s change that.
Terminal data can be read over a USB connection to a V5 brain or controller using cargo-v5
(one of the prerequisites that you should already have installed). To open a terminal connection with the brain, we’ll simply run the following command:
cargo v5 terminal
When you run your program, you should be greeted by vexide’s startup banner along with the output of print
/println
.
Disabling the vexide Banner
In the video above, vexide printed a startup banner along with your Hello world!
message. If you don’t want this banner to be printed, you can disable this in your program:
#[vexide::main(banner(enabled = false))]async fn main(_peripherals: Peripherals) { println!("Look ma! No banners!");}
Low-level Standard I/O: Stdin
and Stdout
print!
and println!
are macros, meaning they expand to some larger block of code at compile-time. If you’re familiar with Rust, you’ve probably used them before in other programs to print out some program output. There are a few subtle differences in the behavior of vexide’s printing macros from what you’d encounter in std
’s versions though.
To understand this behavior, lets look into what underlying APIs these macros actually expand to:
println!("Hello world!");
{ use ::vexide::core::io::{Write, stdout}; if let Err(e) = stdout().write_fmt(format_args!("Hello world!")) { panic!("failed printing to stdout: {e}"); }}
The brain communicates with your computer through two buffers: Stdin
and Stdout
. Stdout
, or “standard output” is what’s being used to print our “Hello world!” message here, as it represents the outgoing message from the brain to your computer.
Stdout
is part of vexide’s io
API.
Example: Printing Without Macros
To manually print to Stdout
we can write to its buffer by obtaining a lock, which will give us exclusive access to the buffer until we release the lock (either by explicitly drop
ping it or having it fall out of scope). This ensures that we won’t get interrupted by another print attempt while we are writing to our buffer.
StdoutLock
implements the Write
trait, which provides some methods for writing to the buffer:
use vexide::core::io::{Write, stdout};#[vexide::main]async fn main(_peripherals: Peripherals) { let lock = stdout().lock();Obtain a lock of Stdout. lock.write(b"Hello World!\n").unwrap(); core::mem::drop(lock);Bring the lock out of scope to release it.}
Advanced Logging Solutions
vexide takes a fairly unopinionated approach on how you should use the terminal, providing a similar API surface to what’s given to you in normal rust programs in a std
-enviornment through macros and writers. If you need a more advanced logging solution, crates in the rust ecosystem such as log may be of interest to you.