Robot Fightin' Time(r) [for the league I helped]

Why Write Something Custom

In short, the reason I went with something custom over something that existed was a desire for higher polish and integration of competitor experience. it's easier to advocate for an institution when it feels they value you, and vice versa! This was one way I could show that.

Table of Contents

Why Not Use Custom Hardware / Electronics / PCBs

GSCRL used to! The previous solution was somewhat tedious and had a number of rough edges for both iteration and use. The requirement to use an ESP32 with none of our volunteers having more than a passing interaction with C++, much less Arduino framework antics on an ESP32, left us in a rough spot when we needed to make changes or improvements to the timer (remote control, multi-arena, including it in stream, etc.).

The controller was usually out of the way, and long USB cables for re-flashing the ESP32 combined with limited production-space requirements lead to a myriad of headaches. This encouraged us to find something else, and I opted for a web-based solution. This let us scale and re-arrange arena software and design as needed, even during unexpected event setup difficulties.

In short, it cost more time and effort for slower iteration. Something bespoke software-wise still be less effort.

PCBs and code for the old arena controls can be obtained here.[H-1][H-2]

A photo of work-in-progress hardware mid-way through iteration of a standalone hardware controller (ESP32 powered.)

So what does it need to do?

Choices

Using Bracketeer

Bracketeer, a tool for those who bracket (running the tournament matches) has a few major viewmodes, all of which were made to help users save time and effort.

Home menu of Bracketeer as a tool.

Queueing

A major pain point for this and other robot sports alike is knowing how much time you have before you are next actively in a match. This is sometimes called "turnaround time", but "late or not" was what it usually became at events.

TrueFinals, the bracketing software GSCRL now uses, handles a decent portion of queueing information for us, but it has rough edges, largely involving multiple tournaments existing concurrently for the same "real" event.

An example of what the preview would look like on a high resolution screen at tournaments prior, something we didn't have.

For example, with three weightclasses of combat robot, we were forced to have three distinct "tournaments" and switch between their views, adding friction and letting competitors more easily miss information.

As such, we'd prioritized a visible, information-dense overlay that can be viewed from anywhere on the arena network.

By making the upcoming matches easy to see, and how long they'd been aware of said match, and all competitors are able to manage their own timelines without asking other volunteers or competitors. It saves everyone time, frustration, and effort.

Bracketeer's queueing screen. There's also a simpler variant that only has the upcoming matches, for increased visibility at a distance.

Match Control

We stated that a major criteria of the tool we're building (and already built!) is that it needs to be able to run more than one arena easily. This is where the Match Control page comes in.

While many events only run one arena, GSCRL opts to run two sometimes due to the number of competitors. As such, the timer controls for each arena can be delegated to a referee, judge, or production volunteer, minimizing the risk of one arena's timer being conflated for another.

The layout the arena controller or production volunteer uses, and makes their life easier.

The left pane is for timer controls, starting, stopping, or manually setting the match timer. by default there's a countdown built in from 3 to 0 seconds, and then 150 seconds of match play, with optional warning sounds played on stream and via the arena screens specifically.

The message box also allows for custom information to be rendered on the timer displays for competitors, enabling things like "matches resume at 1PM" to be said.

The sound test, hide timer, set cage unsafe, and set cage safe buttons are all shorthand for items that can be done otherwise with the menu tools present.

The right pane however, has some novelty and minutae that can at a glance give production crew better information faster.

Taking inspiration from the Queueing screen, the list of upcoming matches is visible, as well as hyperlinks to the competitor's path through the tournament so far. This enables any announcers or emcees to have easier access to the data they might need or want to help build an entertaining narrative.[A-1]

If the time since match was called is under 20 minutes, it's in orange. If it's marked as active or past the 20 minute buffer time, it turns red and shows a danger glyph next to it.

Quirks

Despite quite a few iterations of software, there exist some quirks in how Bracketeer ingests and handles data.

Notably, the quirks exist in the control page, and with how TrueFinals data is retrieved.

It's possible for repeatedly polling the TrueFinals API to cause rate limit exhaustion. The naive (but functional) resolution was to make a least-recently used cache that validates whether the rate limit is exhausted or not yet, and potentially returns (intentionally) stale data matching that endpoint to avoid any malformed data rendering or crashes.

At the time, I had no way of knowing the server crashing was due to rate limiting as it wasn't included in the TrueFinals' API Docs. After a quick chat with Tom (the developer of TrueFinals), he'd told me that information on how exhausted the rate limit was could be found in response headers.[A-2]

    
'x-ratelimit-limit': '10',
'x-ratelimit-remaining': '8',
'x-ratelimit-reset': '1736225420000',
'x-vercel-cache': 'MISS', 
'x-vercel-id': ' [ removed ]',
'transfer-encoding': 'chunked'

The other major architectural decision that's known is how the timer itself functions.

Because I wasn't absolutely sure how consistent or deterministic the timer would be, I wanted the production folks to be able to manually call out timer information on the off-chance it broke somehow. This lead to the timer being client side.

This means that any timer recipients have to deal with the latency of client <--> sockets.io server <--> all timer clients. This round trip inherently adds latency, and while I did attempt to make the timer library keep to < 1/10th of a second, it also means to make the timer jitter less that all clients should be connected via Ethernet.

In the future, I'd love to move the timer to a per-client basis with remote controls (start/stop/jitter calculations borrowed from NTP) sent via the socket.io connection. As of yet though (2025/02/12), that work hasn't been done.

Debug Tools

Hidden in Bracketeer's codebase include some tools that I use to evaluate functionality and status (and while subject to change, may be interesting), like the clients page (showing all current socket.io clients and their open location) or the requests cache view (showing the last requests as internal to the database of the running server.)

The Setup As It Is

While there exist quite a few places I'd like to keep evolving Bracketeer as my skillset grows, moving from Philadelphia to Chicago has inevitably put a small damper on things. Until I get involved in a more local league (MRCA for example) it's unlikely I'll spend the time and effort on Bracketeer.

Some of those low-hanging fruit, you might ask? Well:

Right now any league can take the code we've put together and use it for their own league. The quirks mentioned before still do apply, but it's also open-source. Anyone can contribute and pull-requests are welcome.


Personal Aside

Thank you to everyone at GSCRL and at FUBAR Labs for letting me tinker and grow with the league. It's been an honor working with you, and I can't wait to see what y'all come up with next.


References