Skip to content

Transports

Lewis edited this page Jan 25, 2026 · 3 revisions

This chapter discusses the options you have for communicating with the debugger running on the brain.

When you use GDB to debug a V5 brain, it has to connect to the v5gdb debug server (that's what this project is) to get info about what the brain is doing, command a single-step/resume, or manage breakpoints. The method that GDB uses to communicate with the debug server is called a transport. You have the option of either using one of v5gdb's builtin transports or making your own.

Stdio Transport (USB Serial)

In the StdioTransport configuration, v5gdb will reconfigure the brain's USB Serial connection so that both user serial writes (via printf or std::io::stdout) and debugger IO share the same communication method. All data sent from the brain is given extra framing so that a tool running on the user's PC can differentiate between user IO and debugger IO. This process is completely transparent to user code; no additional configuration is required for user serial writes to be formatted correctly.

This transport method encodes serial output using the following protocol:

  • Data is organized into arbitrarily-sized packets that are COBS-encoded.
  • The first byte of each packet is the channel identifier.
    • The byte 'd' indicates that the packet contains debugger protocol data.
    • The byte 'u' indicates that the packet contains user serial data.
  • The remaining bytes contain the packet body.
  • Encoded packets are never larger than 2048 bytes.
  • Invalid packets, or packets with an unknown channel ID, are considered unencoded user serial data.

The user serial data in the body of a packet is left completely unprocessed, so it might be doubly-COBS-encoded if the framework you are using is already COBS-encoding user serial data.

At this time, PC-to-brain data is not COBS-encoded. All data sent to the brain is assumed to be part of the debugger channel.

This repository contains the v5gdb command line tool (in ./cli/) which implements this protocol.

Custom Transport

You can create and use a custom transport method by implementing the Connection/ConnectionExt traits from the gdbstub crate (Rust) or by sub-classing v5gdb::Transport (C++).

pub struct SmartPortTransport {
    serial: SerialPort,
}

impl SmartPortTransport {
    pub fn new(mut serial: SerialPort) -> Self {
        Self { serial }
    }
}

impl Connection for SmartPortTransport {
    type Error = TransportError;
    // ...
}
impl ConnectionExt for SmartPortTransport {
    // ...
}
#include "v5gdb.h"
#include "api.h"
#include "pros/serial.hpp"
#include <cstring>

class SmartPortTransport : public v5gdb::Transport {
public:
    explicit SmartPortTransport(pros::Serial&& serial) : serial(serial) {}

    std::expected<std::monostate, char const *>
    write(std::span<uint8_t const> buffer) noexcept override;
    // ...

private:
    pros::Serial serial;
};

std::expected<std::monostate, char const *> SmartPortTransport::write(
    std::span<uint8_t const> buffer
) noexcept {
    auto data = const_cast<uint8_t *>(buffer.data());
    int written = serial.write(data, buffer.size());
    if (written == PROS_ERR) {
        return std::unexpected(strerror(errno));
    }
    if (written != buffer.size()) {
        return std::unexpected("not all bytes were written");
    }
    return std::monostate {};
}

Then pass it to the debugger when setting it up.

#[vexide::main]
async fn main(p: Peripherals) {
    let serial = SerialPort::open(p.port_1, 921600).await;
    v5gdb::install(V5Debugger::new(SmartPortTransport::new(serial)));
}
// In C++, custom (user-made) transports are not allowed to be destructed.
// This means they cannot be block-scope.
SmartPortTransport transport(pros::Serial(1, 921600));

void initialize() {
	v5gdb::install(transport);
}

The debugger will now run your callbacks to send and receive data. Generally, breakpoints will not be enabled when user transports are called, but it's probably a good idea to separate your transport code from the code you're trying to debug.

Clone this wiki locally