A dashboard for a panel that had no manual.
The NAS shipped with a small USB LCD bar and no public protocol. I reverse engineered its serial framing and built Vitrine, a driver that turns the panel into a configurable dashboard, at roughly 80% lower cost than sourcing the OEM's accessory.
What was not working
The hardware included a secondary display with real potential and no documentation. The vendor's own driver was expensive to license and only showed what the vendor decided it should.
What the user actually needed
If the panel could accept any image over serial, then any web page could be a dashboard. The problem was not what to display; it was learning how the panel wanted to be spoken to.
What I owned
- Reverse engineered and implemented an auxiliary display driver for the consumer NAS prototype, validating the hardware interface and adding custom display functions in coordination with product and engineering teams.
- Captured and decoded the panel's serial traffic and worked out the framing: a five byte header followed by a raw JPEG, no container.
- Built the driver end to end: a Flask control panel, a headless Chromium renderer, the rotation step, and the serial writer.
- Designed the scene model so new dashboards are ordinary HTML pages, no firmware and no rebuild.
- Shipped eight scenes including a drag to arrange custom widget board.
What had to be true
- No SDK, no protocol document, no vendor support.
- The panel is portrait, 360 by 960, and the natural way to author a dashboard is landscape.
- It had to run on the NAS itself alongside everything else.
Learn the protocol
The panel's traffic turned out to be simpler than feared: a fixed five byte header and then JPEG bytes. Once that was confirmed, the whole problem collapsed into producing the right JPEG often enough.
Make scenes cheap
Instead of drawing widgets in code, every scene is an HTML page rendered by headless Chromium. Telemetry, weather, now playing, a slideshow: each is a template plus a data source. Adding a scene is adding a file.
What shipped
- Vitrine: a browser control panel with a scene library, a renderer, and a serial driver for the panel.
- Eight scenes: industrial telemetry, NAS status, minimal clock, weather, now playing, custom dashboard, media loop, photo slideshow.
How it was built
- Flask serves the control panel and scene pages.
- Playwright drives headless Chromium to screenshot the active scene at 960 by 360.
- The frame is rotated to the panel's portrait orientation, encoded as JPEG, and written over USB serial behind the header.
- A refresh loop keeps the panel current; scenes with live data poll their sources.
From a web page to a panel
- 1Pick a scene in the browser control panel
- 2A small server renders that scene's HTML at 960 by 360 in headless Chromium
- 3The frame is rotated 90 degrees into the panel's native 360 by 960 portrait JPEG
- 4Pushed over serial behind a five byte frame header, the undocumented part
- 5The USB panel displays it, refreshed continuously
Trade offs I made on purpose
- Chose HTML as the scene format so anyone on the team could author one.
- Optimized for flexibility over frame rate: the panel is a status surface, not a video display.
Takeaways
- Undocumented does not mean complicated. It means nobody wrote it down.
- Choosing a format people already know (a web page) is a product decision with an engineering disguise.