🌐 PROS · pros::screen API

Custom Brain Screen

The V5 Brain has a 480×240 touchscreen sitting idle during most matches. A custom display turns it into a real-time competition dashboard — showing battery, motor temperatures, selected autonomous, sensor readings, and match timer — everything your drive team needs at a glance.

1
Why & How
2
Screen API
3
Disabled
4
Auton
5
Driver Control
6
Full Dashboard
// Section 01
Why a Custom Brain Screen Matters
EZ Template already uses the Brain screen for the auton selector. But that is just a list of text. The full 480×240 touchscreen can show anything you draw — and the right information at the right moment can prevent match-day mistakes.

What the Brain Screen Can Tell You

During Disabled
  • Selected auton name + side
  • Battery percentage (large)
  • IMU calibration status
  • All motor connection status
  • Team number & event name
During Driver Control
  • Match timer countdown
  • Battery %
  • Motor temperatures (bar graph)
  • Mechanism state (arm angle, claw open/closed)
  • Odometry position (if equipped)
💡
The most valuable use: a large, clear auton name during disabled prevents the single most common competition mistake — running the wrong autonomous. At a busy competition field, it is easy to forget to switch the selector. A screen showing ⚠ SKILLS AUTON SELECTED in giant red text when you are in a qualification match is impossible to miss.

The Architecture

Custom screen drawing runs in a background PROS Task that updates independently of the control loop. The task redraws the screen at a fixed rate (typically every 50–100ms). It reads robot state variables — battery, motor data, selected auton — and renders them to the screen. The control loop never touches the screen directly.

src/screen.cpp — basic structure
// A separate file for all screen drawing logic // Called once from initialize() to start the background task void screenTask(void* param) { while (true) { drawScreen(); // redraw everything pros::delay(100); // update 10 times per second } } // In initialize(): pros::Task screen_task(screenTask);
ℹ️
EZ Template’s auton selector also draws to the screen during disabled. If you add a custom screen task, you need to coordinate with EZ Template’s drawing or disable EZ Template’s default screen and draw your own auton selector. The approach in this guide draws everything custom — including the auton selection — so you have full control.
// Section 02
The pros::screen API
PROS exposes a straightforward drawing API for the V5 Brain’s 480×240 pixel touchscreen. Coordinate (0,0) is the top-left corner. X increases rightward, Y increases downward.

Screen Dimensions & Coordinate System

// V5 Brain screen
Width: 480 pixels (x: 0 → 479)
Height: 240 pixels (y: 0 → 239)
// Origin (0,0) = top-left corner
// X increases → rightward
// Y increases ↓ downward

Core Drawing Functions

pros::screen — essential functions
// ── Set the drawing colour ────────────────────────────────────── pros::screen::set_pen(0xFF0000); // red (0xRRGGBB hex) pros::screen::set_eraser(0x000000); // background colour for clear // ── Fill the whole screen ─────────────────────────────────────── pros::screen::erase(); // fills screen with eraser colour // ── Draw & fill rectangles ────────────────────────────────────── pros::screen::draw_rect(10, 10, 100, 50); // outline only (x1,y1,x2,y2) pros::screen::fill_rect(10, 10, 100, 50); // solid fill // ── Draw & fill circles ───────────────────────────────────────── pros::screen::draw_circle(240, 120, 40); // outline (cx, cy, radius) pros::screen::fill_circle(240, 120, 40); // solid fill // ── Draw lines & pixels ───────────────────────────────────────── pros::screen::draw_line(0, 120, 480, 120); // horizontal divider pros::screen::draw_pixel(100, 50); // single pixel // ── Print text ────────────────────────────────────────────────── // print(text_sig, line, fmt, ...) — line 0-12, text_sig = TEXT_SMALL/MEDIUM/LARGE pros::screen::print(pros::E_TEXT_MEDIUM, 1, "Battery: %.0f%%", battery::get_capacity()); pros::screen::print(pros::E_TEXT_LARGE, 0, "LEFT AUTON"); pros::screen::print(pros::E_TEXT_SMALL, 5, "IMU: %.1f deg", chassis.imu_get());

Text Sizes and Line Numbers

Constant Pixel Height Lines on Screen Best Used For
E_TEXT_SMALL~16pxup to 12 linesDetailed telemetry, motor data
E_TEXT_MEDIUM~20pxup to 10 linesGeneral status, labels
E_TEXT_LARGE~32pxup to 6 linesAuton name, alerts, battery %
E_TEXT_EXTRA_LARGE~48pxup to 4 linesMatch timer, critical warnings

Colour Reference

Colours are 24-bit hex values in 0xRRGGBB format. Common ones for competition screens:

0x4ADE80 Green
0xF87171 Red
0xFBBF24 Yellow
0x00D4FF Cyan
0xA78BFA Purple
0xFFFFFF White
// Section 03
The Disabled Screen — Pre-Match Confidence
The disabled period is when you set up for a match. Your screen should show everything you need to verify before the field enables autonomous — auton selection, battery, IMU, and motor connections.

Live Preview — Disabled Screen

V5 Brain — 480 × 240px — Disabled Mode

What to Show During Disabled

src/screen.cpp — disabled screen
void drawDisabled() { // ── Background ───────────────────────────────────────────────── pros::screen::set_eraser(0x000810); pros::screen::erase(); // ── Auton name — top half, colour by alliance ────────────────── std::string autonName = ez::as::get_auton_name(); uint32_t autonColor = 0x00D4FF; // default cyan if (autonName.find("Left") != std::string::npos) autonColor = 0x3B82F6; // blue if (autonName.find("Right") != std::string::npos) autonColor = 0xEF4444; // red if (autonName.find("Skills")!= std::string::npos) autonColor = 0xFBBF24; // yellow pros::screen::set_pen(autonColor); pros::screen::fill_rect(0, 0, 480, 80); // coloured header band pros::screen::set_pen(0x000000); pros::screen::print(pros::E_TEXT_LARGE, 1, " %s", autonName.c_str()); // ── Battery percentage ───────────────────────────────────────── double bat = pros::battery::get_capacity(); uint32_t batColor = bat > 50 ? 0x4ADE80 : (bat > 30 ? 0xFBBF24 : 0xF87171); pros::screen::set_pen(batColor); pros::screen::print(pros::E_TEXT_LARGE, 3, " BAT: %.0f%%", bat); // ── IMU status ───────────────────────────────────────────────── bool imuReady = !chassis.imu_calibrating(); pros::screen::set_pen(imuReady ? 0x4ADE80 : 0xF87171); pros::screen::print(pros::E_TEXT_MEDIUM, 6, " IMU: %s %.1f deg", imuReady ? "READY" : "CALIBRATING...", chassis.imu_get()); // ── Motor connection check ───────────────────────────────────── pros::screen::set_pen(0x4A6880); pros::screen::print(pros::E_TEXT_SMALL, 8, " MOTORS:"); int ports[] = {1,2,3,4,5,6,7,8}; // your motor ports for (int i = 0; i < 8; i++) { pros::Motor m(ports[i]); bool connected = (m.get_efficiency() != PROS_ERR_F); pros::screen::set_pen(connected ? 0x4ADE80 : 0xF87171); pros::screen::fill_rect(60 + i*50, 195, 105 + i*50, 225); pros::screen::set_pen(0x000000); pros::screen::print(pros::E_TEXT_SMALL, 11 + 0, " %d", ports[i]); // (approximate x placement — adjust to your layout) } }
🏆
Colour the whole header band by alliance side. At a busy competition, you can glance at the Brain from across the pit and immediately see blue = left auton, red = right auton, yellow = skills. No reading required.
// Section 04
The Autonomous Screen — Live Debug
During the 15-second autonomous, the screen can show what the robot is doing in real time. This is invaluable for debugging — when a run fails in practice, the screen tells you exactly where and why.

Live Preview — Autonomous Screen

V5 Brain — Autonomous Mode (live debug)

What to Show During Autonomous

src/screen.cpp — autonomous screen
// Global string updated before each movement in autons.cpp std::string g_autonAction = "INIT"; int g_autonStep = 0; void drawAuton() { static uint32_t startTime = pros::millis(); pros::screen::set_eraser(0x000810); pros::screen::erase(); // Timer bar at top uint32_t elapsed = pros::millis() - startTime; int barW = (int)(elapsed / 15000.0 * 480); // 15s full width pros::screen::set_pen(0x00D4FF); pros::screen::fill_rect(0, 0, barW, 12); // Elapsed time pros::screen::set_pen(0xFFFFFF); pros::screen::print(pros::E_TEXT_EXTRA_LARGE, 0, " %.1fs", elapsed / 1000.0); // Current action pros::screen::set_pen(0xFBBF24); pros::screen::print(pros::E_TEXT_LARGE, 2, " Step %d: %s", g_autonStep, g_autonAction.c_str()); // Drive error and heading pros::screen::set_pen(0x4A6880); pros::screen::print(pros::E_TEXT_MEDIUM, 5, " HDG: %.1f BAT: %.0f%%", chassis.imu_get(), pros::battery::get_capacity()); } // In autons.cpp — update before every movement: void myAuton() { g_autonAction = "DRIVE 36in"; g_autonStep = 1; chassis.pid_drive_set(36, 110); chassis.pid_wait(); g_autonAction = "TURN 90R"; g_autonStep = 2; chassis.pid_turn_set(90, 90); chassis.pid_wait(); // ... etc }
ℹ️
The step label is your best debugging tool. When a practice run fails, the last label on the screen tells you exactly which movement was executing when something went wrong. Without this, you are guessing from robot position alone.
// Section 05
The Driver Control Screen — Match Dashboard
During driver control, the Brain is facing the driver. A well-designed screen shows match timer, battery, motor temps, and mechanism state at a glance — without the driver ever needing to look away from the robot.

Live Preview — Driver Control Screen

V5 Brain — Driver Control (simulated)

The Match Timer

The Brain has no built-in match timer — you have to implement it yourself. Start a timer at the beginning of opcontrol() and count down from 105 seconds:

src/main.cpp — match timer in opcontrol
void opcontrol() { uint32_t matchStart = pros::millis(); while (true) { int elapsed = (pros::millis() - matchStart) / 1000; int remaining = 105 - elapsed; // 1:45 match if (remaining < 0) remaining = 0; // Store globally for the screen task to read g_matchRemaining = remaining; chassis.opcontrol_tank(); // ... mechanism controls ... pros::delay(ez::E_TASK_DELAY); } }

Motor Temperature Bar Graph

Motor overheating is a competition failure mode. A bar graph of all motor temperatures makes it visible before it becomes a problem. V5 motors start reducing performance at 55°C and shut down at higher temperatures:

src/screen.cpp — temperature bars
void drawTempBars(int yStart) { int ports[] = {1,2,3,4,5,6,7,8}; int n = 8; int barW = 480 / n; for (int i = 0; i < n; i++) { pros::Motor m(ports[i]); double temp = m.get_temperature(); // degrees C // Colour: green < 45°C, yellow 45–55°C, red > 55°C uint32_t col = temp < 45 ? 0x4ADE80 : (temp < 55 ? 0xFBBF24 : 0xF87171); pros::screen::set_pen(col); // Fill bar proportional to temp (0°C = 0, 80°C = full height) int barH = (int)(temp / 80.0 * 40); pros::screen::fill_rect( i*barW + 2, yStart + 40 - barH, i*barW + barW - 2, yStart + 40); // Port number label pros::screen::set_pen(0x4A6880); pros::screen::print(pros::E_TEXT_SMALL, yStart/20+3, " %d", ports[i]); } }
// Section 06
Putting It Together — The Full Screen Task
One background task that detects the current match phase and switches between the disabled, autonomous, and driver-control screens automatically. Add it once and it runs for every match.

Interactive Preview — All Three Modes

Disabled Mode — Pre-Match

The Complete Screen Task

src/screen.cpp — full competition screen task
// ── Globals shared between control code and screen task ──────── std::string g_autonAction = "INIT"; int g_autonStep = 0; int g_matchRemain = 105; // seconds remaining in driver // ── Main screen task — runs every 100ms in background ────────── void screenTask(void* param) { while (true) { if (pros::competition::is_disabled()) drawDisabled(); else if (pros::competition::is_autonomous()) drawAuton(); else drawDriver(); pros::delay(100); } } // ── Driver control screen ────────────────────────────────────── void drawDriver() { pros::screen::set_eraser(0x000810); pros::screen::erase(); // Big timer — colour changes at 30s and 10s uint32_t timerCol = g_matchRemain > 30 ? 0x4ADE80 : g_matchRemain > 10 ? 0xFBBF24 : 0xF87171; pros::screen::set_pen(timerCol); pros::screen::print(pros::E_TEXT_EXTRA_LARGE, 0, " %d:%02d", g_matchRemain/60, g_matchRemain%60); // Battery double bat = pros::battery::get_capacity(); pros::screen::set_pen(bat > 50 ? 0x4ADE80 : bat > 30 ? 0xFBBF24 : 0xF87171); pros::screen::print(pros::E_TEXT_MEDIUM, 4, " BAT: %.0f%%", bat); // Motor temperature bars (bottom strip) drawTempBars(190); } // ── In initialize(): launch the task ─────────────────────────── void initialize() { chassis.initialize(); // ... other setup ... pros::Task(screenTask); // ← start the screen task }
🏆
Add screen.cpp and screen.h as dedicated files — see the Organizing Code guide for how to split code across multiple files. The screen task is a perfect candidate for its own file: it has no dependencies on your auton logic and can be developed and tested independently.
⚠️
Update rate matters. Redrawing 100ms (10Hz) is smooth and uses minimal CPU. Do not redraw at 10ms — erasing and redrawing the entire screen that fast creates noticeable flicker. If flicker is visible at 100ms, add pros::screen::set_double_buffered(true) before drawing and pros::screen::render() after — this renders to an off-screen buffer and flips atomically, eliminating flicker entirely.
⚙ STEM Highlight Technology: Raster Graphics & Coordinate Systems
The V5 Brain screen uses a raster coordinate system: origin (0,0) at top-left, x increases right, y increases downward. This is the standard for 2D computer graphics — used in every screen from your phone to game engines. Drawing a rectangle with fill_rect(x1,y1,x2,y2) defines it by two corner coordinates, an application of the Cartesian plane. Colour is expressed in 24-bit RGB hex (0xRRGGBB) — each channel is 8 bits (0–255), giving 256³ = 16.7 million possible colours.
🎤 Interview line: “The Brain screen uses a 2D Cartesian coordinate system with origin at the top-left. We draw telemetry bars, colour-coded regions, and text using the pros::screen API — which is essentially a simplified 2D graphics library, the same concept behind game engines and UI frameworks.”
🔬 Check for Understanding
You want to draw a battery bar that is 80% full across a 400-pixel-wide area. What x-coordinate is the right edge of the filled portion?
80 pixels
400 pixels
320 pixels
0.8 pixels
Related Guides
📄 Organizing Code →📊 Data Logging →🤖 Full Robot Code →
← ALL GUIDES