Skip to content

Repository files navigation

trvision

Stream your desktop to Thermalright Vision USB displays — without running TRCC.

Python Platform License: MIT

Why

The TL-M10 Vision / Mjolnir Vision panels plug into an internal USB 2.0 header and are normally driven by TRCC, Thermalright's own Windows app. I wanted the panels to show what I choose — a region of my desktop, live — without keeping TRCC running in the background. So trvision talks to the panels directly over USB: grab the screen, encode a frame, push it out.

Status

Working. It drives both panels on my machine (a 320×240 and a 1920×462) — capture → JPEG → USB, targeting 30 FPS. The protocol details come from community reverse engineering (credited below) and are confirmed empirically on this hardware; other panel revisions may behave differently.

Hardware

Device USBDISPLAYVID 0x87AD, PID 0x70DB (ChiZhu Tech family)
Driver WinUSB, bound automatically via MS OS descriptors — Zadig is not needed
Interface Vendor-specific class 0xFF, bulk endpoints
Panel size Derived from the handshake reply, or from a per-serial table in config.py

How it works

  • Handshake — a 64-byte packet; the device replies with 1024 bytes. Two bytes of that reply (PM, SUB) describe the panel. PM=32 panels want raw RGB565 (big-endian); the rest accept JPEG.
  • Frame — a 64-byte header (magic, command, width, height, payload length) followed by the encoded image.
  • Transfer — written in 16 KiB bulk chunks, with a zero-length packet when the total is an exact multiple of 512.
  • The firmware blanks the panel as soon as the frame stream stops, so trvision keeps sending continuously.

Screen capture uses dxcam (GPU Desktop Duplication) and falls back to mss if dxcam is unavailable.

Install

Windows with Python 3.12 (dxcam wheels are happiest there):

py -3.12 -m venv venv
venv\Scripts\activate
pip install -r requirements.txt

Close TRCC completely (tray → exit) before running — it holds the USB device open and trvision won't be able to claim it.

Usage

python tools\usb_enum.py                              # list devices and endpoints
python tools\live_test.py --send-frame                # handshake + push one solid color
python main.py --dry-run                              # capture/encode speed, no device needed
python main.py --serial 2d1d021814a37504 --seconds 10 # 10-second test on one panel
python main.py --region 0,0,800,480                   # stream a desktop region, forever

Everything else lives in config.py: USB identity, per-serial panel resolutions, capture region, target FPS, JPEG quality, per-panel transform (rotate 180° / mirror for upside-down mounts), and whether to draw the mouse cursor (dxcam doesn't capture it).

Tools

  • tools/analyze_pcap.py — a standalone USBPcap .pcapng parser with no dependencies and no Wireshark required. It walks the capture, decodes the frame headers, and can extract the transferred images (--extract). This is the tool to reach for when adding support for a panel that behaves differently.
  • tools/usb_enum.py — endpoint discovery.
  • tools/live_test.py — the smallest possible end-to-end check: handshake, print PM/SUB, optionally push a single solid-color frame.

Credits

The wire protocol for these 87AD:70DB panels was reverse-engineered and documented by the community:

trvision was written from that protocol documentation; no GPL-licensed code was copied into this project.

Notes

  • Windows-only for now — the capture layer uses Desktop Duplication. The USB side is portable; a Linux capture backend would be the missing piece.
  • Only tested against 87AD:70DB panels. Other Thermalright displays may use a different protocol entirely.
  • USB 2.0 bandwidth is the real limit, which is why frames go out as JPEG on panels that accept it.

License

MIT © Osman Demir · osmandemir.net

About

Stream your desktop to Thermalright Vision USB displays (87AD:70DB, ChiZhu) without TRCC - Python, WinUSB, no Zadig required.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages