Skip to content

About

VW Digifiz digital instrument cluster, recreated in Python and pygame for a Raspberry Pi. Resolution-independent, fed live by MQTT, Arduino serial or OBD-II, with a full simulator so it runs with no hardware attached.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

Digifiz Dash

A faithful recreation of the VW Digifiz digital instrument cluster, built to run fullscreen on a Raspberry Pi and driven by live sensor data.

The Digifiz is one of the best looking clusters ever fitted to a production car, and original units are getting rare, expensive and hard to keep alive. The goal here is to save the design: rebuild it in software, pixel for pixel, so it can be run on a modern panel — and eventually fitted to my own project bike once the rest of the pieces come together.

Started as a fork of GFunkbus76/Digifiz-Dash, which supplied the original artwork and the idea of driving it from pygame. The code has since been rebuilt around a package layout, a data-source abstraction, resolution-independent rendering and a test suite.

Demo

The dash running the sweep scenario

The sweep scenario on the demo source: every gauge, every lamp and the odometer from end to end, while the MFA steps to its next mode every 3 seconds. The full video on YouTube (56 s) starts from a clean terminal, with the intro frames being extracted and the intro playing before the dash.

tools/extract_intro.sh das_auto.mp4 1366x768
.venv/bin/python main.py --source demo --scenario sweep --mfa-cycle 3 --size 1366x768

What it does

  • Tachometer, speedometer, clock, odometer and trip counter.
  • MFA panel with all six readings and the original mode chips, cycling clock → trip KM → L/100KM → KM/H → oil °C → ambient °C and back round. One chip lights at a time, as on the real cluster.
  • Four aux gauges: coolant temperature, exhaust gas temperature, oil pressure and boost.
  • Ten indicator lamps plus the fuel reserve warning.
  • A startup bulb check that sweeps every gauge and lights every lamp.
  • Resolution independent: the artwork is authored at 1920x720 and scaled once at startup to whatever panel it finds, letterboxed and centred. An 800x480 5 inch panel and a 1920x720 stretched cluster LCD both work with no code changes.
  • Runs with no data at all, so the look can be worked on anywhere.

Quick start

No hardware, no broker, one command:

python3 -m venv .venv
.venv/bin/pip install -U pip -r requirements.txt
.venv/bin/python -m digifiz.app --source demo --scenario drive --size 800x480 --debug

--debug overlays frame timing, the active source and every live value.

For the whole thing end to end — intro, bulb check, then every scenario with the MFA stepping through its modes and the odometer running:

tools/demo_suite.sh 800x480 15

Switching MFA modes

Every input steps through the same list in the same order, because they all end up calling the same code:

Input How
Keyboard m or → for next, ← for previous
GPIO button --mfa-button-pin 17, button between that pin and ground
Arduino button send mfabtn=1 / mfabtn=0 in the serial line
MQTT publish to cabin/mfa_next/state
Rotary switch publish a position to cabin/mfa_mode/state, or send mfa=3 over serial
Timer --mfa-cycle SECONDS

A button input is read as a press on any change to a non-zero value, so both a 0/1 edge and a press counter work, and holding the button down does not race through the modes. Esc or q quits.

The GPIO button needs pip install gpiozero lgpio on the Pi. Without it the dash logs a warning and carries on.

Useful flags

Flag What it does
--selftest [SECONDS] Bulb check on startup: all gauges sweep, all lamps light
--mfa-cycle SECONDS Step the MFA automatically (default 4s with demo)
--odometer KM / --trip KM Start the counters somewhere believable
--time-scale N Run simulated time N times faster, so kilometres tick over while you watch
--size WxH Preview a panel you do not physically have

Demo distance is never written back to odo.txt, so a demo cannot inflate the real odometer.

Data sources

Values arrive in engineering units — rpm, °C, psi, litres, km/h — and the dash maps them onto the artwork itself. Pick a source with --source or the DIGIFIZ_SOURCE environment variable:

Source What it is
mqtt (default) Subscribes to a broker. The intended path for a real install.
demo Synthetic data generated in-process. No broker, no hardware.
serial Reads keyed lines from an Arduino over USB serial.
obd Polls an OBD-II adapter. Needs a car with an ECU that speaks it.

MQTT

Topics, all publishing plain numbers:

engine/rpm/state          rpm        0..5000
engine/coolant/state      °C        50..120
engine/egt/state          °C         0..500
engine/oilpressure/state  psi        0..80
engine/boost/state        psi        0..30
engine/fuel/state         litres     0..60
engine/consumption/state  L/100km    0..30
engine/oil_temp/state     °C        40..150
cabin/speed_cv/state      km/h       0..199
cabin/mfa_next/state      button, any change to non-zero steps one mode
cabin/mfa_mode/state      mode index or name, e.g. 3 or "trip"
cabin/avg_speed/state     km/h       0..199
cabin/outside_temp/state  °C       -40..60
indicator/<name>/state    0 or 1

Indicator names: illumination, foglight, defog, highbeam, leftturn, rightturn, brakewarn, oillight, alt, glow.

Run it with a broker and the simulator:

sudo apt install mosquitto mosquitto-clients
tools/run_dev.sh --scenario drive

Or by hand:

.venv/bin/python tools/mqtt_sim.py --scenario drive --rate 20 --verbose
.venv/bin/python -m digifiz.app --source mqtt --size 800x480 --debug

Serial

One line per update, keys optional, so a sketch can send only what it has:

D rpm=1850 egt=430 boost=12.4 oilp=45 clt=88 fuel=32 spd=62 ind=0x1A4

Recognised keys: rpm, egt, boost, oilp, clt, fuel, spd, oat, cons, oilt, avg, plus mfabtn (mode button) and mfa (absolute mode).

ind is a bitfield over the ten indicator lamps, bit 0 first.

DIGIFIZ_SERIAL_PORT=/dev/ttyUSB0 .venv/bin/python -m digifiz.app --source serial

Simulator scenarios

Used by both --source demo and tools/mqtt_sim.py:

Scenario What it exercises
idle Warm engine, stationary. Proves idle frames are skipped.
drive A drive cycle with turbo lag, EGT inertia and a warm-up.
sweep Every gauge end to end, and with --source demo the odometer from 000000 to 999999 too. Checks all artwork and the layout.
warnings Each lamp in turn, blinking turn signals, fuel below reserve.
cold-start Glow plugs, cold coolant, oil pressure lamp clearing.

Intro animation

An optional splash plays before the dash comes up. The clip is converted to pre-scaled PNG frames once, so nothing is decoded at runtime:

tools/extract_intro.sh das_auto.mp4 800x480
.venv/bin/python -m digifiz.app --intro --size 800x480

Extraction uses ffmpeg, or GStreamer if ffmpeg is not installed. Frames land in images/intro/ and are not tracked, so re-run it for each panel size. Without them, --intro simply skips.

The handover into the dash is built to be seamless:

  • Black frames at the start and end of the clip are trimmed on extraction, so the intro is animation rather than a dead screen. The supplied clip loses 2.2 seconds of black this way.
  • The dash's artwork loads during playback, a surface at a time in whatever is left of each frame's budget, so there is no pause once the intro ends.
  • The last intro frame dissolves into the first dash frame over half a second (DIGIFIZ_INTRO_FADE, 0 to cut straight).

Performance

The dash is built to sit quietly on a Pi rather than peg a core:

  • Every image is decoded, format-converted and pre-scaled once at startup.
  • Rendered text is cached; the dash uses very few distinct strings.
  • A frame is drawn only when it would actually look different. A sensor moving inside one gauge step, or a rounding-identical reading, repaints nothing — at idle roughly 98% of frames are skipped.
  • The odometer is read once and written back atomically, at most every 30 seconds, to protect the SD card.
  • Under SDL_VIDEODRIVER=kmsdrm it draws straight to the panel with no X11, no Wayland and no compositor.

Layout

All coordinates live in digifiz/layout.py in 1920x720 logical space. Geometry scales them to the real panel once at startup, so retargeting a different screen never means touching drawing code.

Raspberry Pi

See deploy/README.md for packages, the HDMI mode for a 5 inch panel, the KMS/DRM setup and the systemd unit.

Tests

.venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest tests -q

Covers unit-to-frame scaling, the MQTT and serial parsers, odometer persistence and debounce, layout scaling at several panel sizes, the simulator staying renderable, MFA mode switching and chip lighting, and the repaint gate. The renderer tests run headless on SDL's dummy video driver.

Architecture

Now

Sensor data comes in from an Arduino, an OBD-II scanner or MQTT, is turned into one signal model, and is drawn by the renderer onto the display.

Current architecture: Arduino, OBD-II and MQTT inputs feed the data adapter and signal model, which the renderer draws to the display

Where it is going

Raw telemetry from the vehicle, a simulator or a recorded ride, processed into a derived vehicle state with calibration and fault detection, before it reaches the visualizer.

Planned architecture: vehicle, simulator and replay inputs produce raw telemetry, which telemetry processing turns into a derived vehicle state for the Digifiz visualizer

The sources are docs/diagrams/*.excalidraw; open them at excalidraw.com to edit, then re-export the PNGs.

Layout of the repository

digifiz/            the dash itself
  app.py            display setup, render loop, frame pacing
  config.py         tunables and environment variables
  layout.py         every coordinate, in logical space
  mfa.py            the MFA's modes, their chips and the cycle order
  button.py         a physical MFA button on a GPIO pin
  assets.py         load, convert and pre-scale artwork once
  render.py         drawing and the repaint gate
  scaling.py        engineering units to artwork frames
  signals.py        the one description of every value and topic
  simulation.py     synthetic engine behaviour
  sources/          mqtt, demo, serial, obd
tools/              simulator, dev runner, intro extraction and trimming
deploy/             systemd unit and Raspberry Pi notes
docs/               demo video, architecture diagrams and their sources
images/, fonts/     artwork
arduino-node-red/   sketches and notes for the sensor hardware
tests/

Credits

  • Original project and artwork: GFunkbus76, itself inspired by ManxGauged and miata-dash.
  • Seven-segment fonts: DSEG by Keshikan.

Not affiliated with Volkswagen. The Digifiz design is theirs; this is a tribute to it.

Disclaimer

Work in progress, and a hobby project. If you fit something like this to a vehicle, it is on you to make sure it does not distract you or replace anything safety critical, and to check your local rules. Use at your own risk.

About

VW Digifiz digital instrument cluster, recreated in Python and pygame for a Raspberry Pi. Resolution-independent, fed live by MQTT, Arduino serial or OBD-II, with a full simulator so it runs with no hardware attached.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages