Building the Trofeo Vision App Thermalright Should Have Shipped

Software Build Log & Development Update

Building the Trofeo Vision App
Thermalright Should Have Shipped

Part 2 of my Trofeo Vision project: probing how the USB display works, replacing TRCC with a custom Windows control app, and turning a clever little screen into a proper live PC dashboard.

View the Trofeo Vision on Amazon
Part 2 Windows app build USB HID display No HWiNFO target Work in progress

Why I Started Building My Own Software

In the first article, I looked at the Thermalright Trofeo Vision as a piece of hardware. The short version is that the device itself is far better than its bundled software suggests. The display is sharp, the form factor is useful, the USB-C connection is clean, and the mounting options make it flexible enough to sit on a desk, live near a monitor, or be placed inside a PC case.

The problem is not the screen. The problem is that Thermalright Control Center, or TRCC, treats the display like a basic decoration panel rather than a proper programmable information screen. It can place detected sensor values, text, date, day and time on the canvas. It can use an image or video as a background. It can adjust zoom, brightness and rotation. It can use a layer mask. It also has media player and push flow modes. Those are useful foundations, but they are nowhere near what this type of device could be doing.

For my setup, the limitations were immediate. TRCC did not correctly expose every sensor I cared about. My RTX 5090 was not detected until I disabled onboard graphics in the BIOS. Wi-Fi upload and download rates were missing. My second Ethernet port was not available in the way I wanted. There was no real widget system, no weather panel, no application-aware display switching, no game profile behaviour, and no obvious route for adding custom integrations.

The goal: keep the excellent hardware, bypass the limitations of TRCC, and build a Windows app that can talk directly to the Trofeo Vision while pulling richer data from the PC and external services.

The Starting Point: Treating It Like a Real Display

The first breakthrough was understanding that the Trofeo Vision does not need to behave like a normal HDMI or DisplayPort monitor. It is not extending the Windows desktop in the usual way. The device is driven over USB, and the host software sends image frames to it. That changes the entire approach.

Instead of asking Windows to render a second monitor, the custom app can render its own canvas in memory, compress it into frames, and push those frames to the device. That means the display can become whatever the software makes it: a sensor panel, a media display, a notification surface, a game status panel, a compact wallboard, or a rotating set of themed dashboards.

In practice, the early work focused on three questions:

Can the screen be addressed directly?

The app had to connect to the USB device without relying on TRCC, then send frames reliably enough that the display stayed alive instead of returning to its built-in splash screen.

Can frames be rendered cleanly?

The screen needed a renderer capable of drawing text, bars, panels and values into a 1280 by 480 layout, with a preview visible inside the control app.

Can useful data be collected?

The project needed real system data without depending entirely on TRCC or HWiNFO, especially for network rates, GPU data, memory, storage and future app integrations.

Probing the Device: What Actually Happens Over USB

The most important part of the project has been working out how the screen is actually driven. With devices like this, the hardware often looks more mysterious than it really is. The manufacturer app is closed, but the display still needs a repeatable way to receive image data. Once you can identify that pattern, the screen stops being a black box and starts becoming a canvas.

The working approach is to render image frames on the PC side and send them over the USB connection to the Trofeo Vision. The app connects to the device, prepares the frame, compresses it, then transmits it repeatedly. When that loop is alive, the display shows the custom dashboard. When it stops, the screen eventually falls back to the Thermalright splash screen, which is exactly what I saw during the early tests.

That fallback was useful. It proved that the display was not broken. It was simply no longer receiving valid frames. So instead of guessing whether the cable, device, or driver was at fault, the issue could be narrowed down to the app lifecycle: the process was starting, sending frames briefly, then dying or being killed.

Observed during testing:
[App] Connected - USBDISPLAY
Frame 1 - 59 KB - CPU 0.0% - OK - 54ms
Frame 20 - 60 KB - CPU 9.2% - OK - 1253ms
Then the display returned to the built-in Thermalright splash screen when the app stopped sending frames.

This is the type of debugging that matters with small USB display projects. A pretty interface is not enough. The sender loop must be reliable, the frame timing must be consistent, and the app has to keep running even when the user closes the wrong window, switches tabs, or changes settings.

The First Proper App Structure

Rather than hacking together a single script, the project was split into a proper multi-file Windows application. That matters because this is not just a proof of concept that draws one image. It needs device communication, sensor collection, rendering, a GUI, settings, connectors, and eventually profile management.

The first structure created separate pieces for the major responsibilities:

Device layer

Handles connecting to the Trofeo Vision and sending rendered frames to the physical display.

Sensor layer

Collects CPU, memory, disk, network, GPU, battery and process data without making TRCC the centre of the system.

Renderer

Builds the actual 1280 by 480 display image, including panels, text, values, bars and future widgets.

GUI

Provides the control panel with a live preview, widget toggles, connector settings and display output controls.

Launcher

Starts the app, checks dependencies, writes logs, and gives a safer way to debug problems when the GUI fails.

The initial files included a renderer, a GUI, a main application loop, an entry-point script and a batch launcher. That gave the project a real shape: not finished, not polished, but no longer just an experiment.

What the App Can Already Do

The current build is already past the point of being a static mock-up. It connects to the screen, sends frames, shows a live preview in the control panel, and has the early shape of a dashboard system.

1280x480 Rendered canvas
640x240 GUI preview
1-10 FPS target range
40-95 JPEG quality range
4 tabs Control interface
Tray Background control

The GUI is built around four main areas. The Dashboard tab shows a live scaled preview of the Trofeo output, along with FPS, frame count and JPEG size counters. The Widgets tab is intended for turning individual display elements on and off. The Connectors tab is where external integrations such as Outlook, weather and media player data can live. The Settings tab handles output behaviour such as FPS, JPEG quality, startup behaviour and tray options.

On the display side, the first widget set already covers the kind of information TRCC should have made easier to show: CPU usage, per-core bars, frequency, memory, swap, disk I/O, storage, network rates, GPU usage, VRAM, temperature, fan speed, power, battery, top processes, clock, Outlook unread count, latest subject, weather and now-playing media.

Important distinction: this is not just copying TRCC. The plan is to build a more extensible system where the Trofeo Vision is one supported display target, not the entire limit of the project.

Why I Do Not Want to Depend on TRCC

Using TRCC as a backbone sounds tempting because it already talks to the screen. In reality, that would also inherit the main problem. TRCC is a closed application with a fixed way of thinking. It is designed around its own supported elements, its own sensor detection, and its own modes. Expanding that cleanly is not the same as building on a documented platform.

The custom app takes the harder route at the beginning, but it gives more control later. If the app owns the rendering and device communication, it can draw whatever is needed. If the app owns the sensor and connector layer, it can add data that TRCC does not understand. If the app owns the profile system, it can eventually switch layouts based on games, active windows, time of day, workload, or manual user selection.

That is the difference between a decorated sensor display and a real dashboard engine.

The Good Progress So Far

What is working

  • The app can connect to the Trofeo Vision as a USB display target.
  • The screen can show custom rendered frames from the new app.
  • The GUI opens as a dark control panel rather than only running as a hidden process.
  • There is a live preview, frame counter and JPEG size feedback.
  • The render loop is producing usable display frames.
  • Core system monitoring is already represented in the first widget plan.
  • Logging now exists, making silent crashes much easier to diagnose.
  • Tab switching crashes were identified and fixed in the current test path.

What fought back

  • The app initially started hidden in the system tray, making it look like nothing had opened.
  • Closing the console window killed Python, which stopped the frame loop and returned the screen to the splash screen.
  • Using pythonw.exe hid crashes, which made debugging harder rather than easier.
  • A logging wrapper failed because it did not expose the expected encoding attribute.
  • CustomTkinter rejected some widget arguments that normal Tkinter would tolerate.
  • Passing width=None into CTk widgets caused scaling errors.
  • Destroyed dashboard labels were still being updated after tab changes.
  • One launcher message showed encoding mojibake, proving that console output still needs tidying.

The First Real Bug Hunt

The first failure looked like a device problem. The display showed the custom output briefly, then dropped back to the Thermalright splash screen. That could easily be mistaken for a USB issue, a screen issue, or a protocol issue. It was actually more mundane and more useful: the app process was not staying alive in the way I expected.

The initial launcher approach used a GUI-style Python process so that the user would not be stuck with a console window. That is fine once the app is stable. During development, it is the wrong trade-off. If the program crashes silently, you lose the most important information: the traceback.

The fix was to make the launcher more honest. During development, the console should stay open, the frame log should be visible, and errors should be written to a log file. That is not as polished as a tray-only app, but it is much better for getting the application stable.

Lesson learned:
A clean background app is the end goal. During early development, visible logs are more valuable than a silent tray icon.

CustomTkinter: Good Looking, But Not Always Forgiving

The GUI work also exposed a few differences between standard Tkinter assumptions and CustomTkinter behaviour. The app needed a modern dark interface, so CustomTkinter made sense. It gives a cleaner look quickly, but it is stricter about arguments passed into widgets.

One of the early mistakes was passing keyword arguments with a value of None. In standard GUI code it is easy to build helper functions that pass optional values around, but CTk widgets do not always accept that. In this case, width values that were meant to be ignored were still being passed into the widget constructor. CustomTkinter then tried to scale a None value as if it were a number, causing a type error.

That was fixed by only passing the argument when it is actually valid. It is a small detail, but it is the sort of detail that separates a quick script from an app that can survive real interaction.

The second issue happened when switching tabs. Dashboard labels were destroyed when the interface changed, but the periodic update loop still tried to update them. That produced invalid command errors because Tkinter was being asked to configure widgets that no longer existed. The fix was to make the dashboard update routine defensive: only update labels that still exist, and do not assume the Dashboard tab is always visible.

The Sensor Problem Is Bigger Than Just CPU and GPU

Most simple sensor panels focus on CPU usage, GPU temperature and RAM percentage. That is fine, but it does not solve the real annoyance I had with TRCC. The issue is not just whether it can show a CPU number. It is whether the app can expose the specific data the user actually cares about on their own machine.

My requirements are wider than a basic TRCC layout. I want reliable Wi-Fi and Ethernet throughput, even where TRCC and HWiNFO do not present the values cleanly. I want GPU information from NVIDIA directly where possible. I want storage, top processes, fan and power data where available. I want app-aware elements like media state and Outlook unread count. I also want external data such as weather, without pretending that a sensor display has to be limited to motherboard telemetry.

That is why the current build leans towards a connector model. A CPU module, a GPU module, a network module, a weather connector and an Outlook connector should all feed a common renderer. The display should not care where the data came from. It should only care that a widget has a value, a label, a style and a position.

Rendering: The Display Is the Output, Not the Interface

A key design decision is to separate the control GUI from the actual Trofeo output. The Windows app can have buttons, tabs, toggles and sliders. The Trofeo Vision itself should only receive the finished display frame. That keeps the device output clean and avoids trying to make the tiny display behave like a normal application window.

The renderer is therefore responsible for building a complete visual frame: background, panels, text, bars, sensor values and eventually images or animation. The control panel is responsible for choosing what should appear, not for being the thing that appears.

This is also why the live preview matters. It lets me see what the Trofeo should be showing without constantly looking down at the physical screen. It also gives useful feedback about frame size and performance. When the app is rendering at a target FPS and the JPEG size stays reasonable, it is easier to tune quality without overloading the sender loop.

Where the App Is Heading

The current build proves the foundation. The next stage is turning that foundation into something people would actually want to install and use every day.

Drag-and-drop widget positioning
Saved layouts and theme packs
Per-game and per-app display profiles
Better GPU and network detection
Weather, media and Outlook connectors
Custom text, images and background layers
Plugin-style data modules
Multiple display target support
Installer and auto-start handling
Stable tray mode with proper logging
Import/export of display profiles
Community-ready documentation

The bigger idea

The Trofeo Vision is the first target, but the concept should not be locked to one Thermalright display. If the app is structured correctly, the renderer and widget system can stay the same while the device output layer changes. That opens the door to supporting other USB-driven mini displays later.

What Still Needs Work

The app is not finished, and pretending otherwise would be pointless. It can connect. It can render. It can send frames. The GUI is now usable enough to click through the tabs without throwing the earlier errors. But there is still a long way between a working development build and a polished release.

The immediate priorities are stability, settings persistence and layout control. The app needs to remember user choices cleanly. It needs to start in the right state. It needs to recover gracefully if the display is unplugged and reconnected. It needs a proper profile format rather than hard-coded assumptions. It needs better error messages for normal users, not just Python tracebacks for development.

After that, the work becomes more interesting: visual theme editing, app/game detection, better connectors, and a clean way for other users to add their own data modules without modifying the core application.

Current status: this is a promising working prototype, not a finished public release. The hard part is no longer proving that the display can be controlled. The hard part is turning that control into a stable, configurable product.

The Most Useful Thing I Learned

The biggest lesson so far is that the Trofeo Vision is much more capable than TRCC makes it feel. The limitation is not the panel. It is not the USB-C connection. It is not the resolution. The limitation is the software layer that decides what gets rendered and how much control the user has over it.

Once the display is treated as a frame target, the whole product becomes more interesting. It stops being a fixed sensor accessory and becomes a small programmable dashboard. That is exactly where this project is heading.

What Comes Next

The next phase is to make the app more configurable. The display output needs proper layout editing rather than fixed widget placement. The connector system needs to be cleaned up. The startup behaviour needs to be polished so the app can run quietly when it is stable, while still keeping useful logs when something goes wrong.

I also want to document the process properly as the build develops. Part 1 covered the hardware and the limitations of TRCC. This part covers the first real development push. The next update will focus on the visual dashboard system: layout control, themes, profiles, and how close the app is getting to replacing TRCC completely.

Check back for Part 3, where I will go deeper into the actual dashboard design, profile system and whether this can become a proper release rather than just a personal workaround.

The Hardware Is Still Worth It

Even with TRCC's limitations, the Trofeo Vision remains a strong little display for the money. The reason I am building software for it is because the hardware deserves better, not because the device itself is bad.

Check the Trofeo Vision on Amazon

As an Amazon Associate I earn from qualifying purchases. Price and availability can change.

Final Thoughts

This project started because TRCC did not expose the kind of control I expected from a display this useful. It has already moved beyond frustration and into something more interesting: a working custom app with a real architecture, visible progress, and a realistic path towards becoming a proper replacement.

The early bugs were not wasted time. The hidden tray launch, the console lifecycle problem, the silent pythonw crash, the logging wrapper issue, the CustomTkinter argument errors and the destroyed-label update bug all helped define what the app needs to be. It needs to be visible when it should be visible, quiet when it is stable, honest when it fails, and flexible enough that the display can be used for far more than a fixed set of sensor labels.

The Trofeo Vision is still the same device I liked in the first article. What has changed is that I am now much more confident that the software can be taken further. TRCC shows what the product is out of the box. This project is about showing what the hardware can become.

Comments

Popular posts from this blog

Converting Xibo Windows Client from 32-bit to 64-bit: A Complete Technical Walkthrough

Why I Built My PC Around the AMD Ryzen 9 9950X3D