A real-time telemetry interface for Alfa Romeo Giulia / Stelvio dashboards using two MCP2515 CAN modules. It streams live data from sim racing titles to a genuine OEM cluster over CAN bus, replicating factory behavior with high accuracy.
- ✅ Full support for Alfa Romeo Giulia / Stelvio OEM instrument clusters
- ✅ Compatible with both Arduino and STM32 platforms
- ✅ Dual CAN bus architecture:
- 500 kbps — Powertrain CAN (primary signals, gauges, warnings)
- 125 kbps — Infotainment / IHS CAN (display, messages, auxiliary data)
| Simulator | Protocol |
|---|---|
| BeamNG.drive | OutGauge + Mod |
| Assetto Corsa | Shared Memory |
| Component | Description |
|---|---|
| Python Script | Reads telemetry from the game and streams structured data over serial |
| Arduino / STM32 | Parses incoming data, applies mapping logic, and transmits CAN frames |
| MCP2515 #1 | CAN @ 500 kbps — mandatory (powertrain bus: gauges, warnings, core signals) |
| MCP2515 #2 | CAN @ 125 kbps — optional (IHS bus: display, text, auxiliary functions) |
ASCII lines in format {KEY[VALUE]}, one per line.
{RPM[3100]}
{VEL[88]}
{TMPOLIO[64]}
{CARB[54]}
{MARCIA[3]}
{MEDIA[Best Lap: 1:45.872]}
📁 Alfa-Romeo-Giulia-SimRacing.zip --> Arduino / STM32 code
📁 Python.zip --> Games Support / Windows
📁 ARG_SimR.zip --> BeamNG Mod*
*place ARG_SimR.zip in
C:\Users\%User%\AppData\Local\BeamNG\BeamNG.drive\current\mods
or equivalent folder
- Arduino IDE
- Supported Boards: Arduino all variants
- MCP2515 Library:
MCP_CANby coryjfowler
- Open Arduino IDE
- Go to Sketch → Include Library → Manage Libraries...
- Search for
"MCP_CAN by coryjfowler" - Click Install
- Open Arduino IDE
- Go to File > Preferences
- Add this URL to Additional Board Manager URLs:
https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json
-
Go to Tools > Board > Boards Manager
-
Search for
STM32and install:- STM32 MCU based boards by STMicroelectronics
-
Set the following:
| Setting | Value |
|---|---|
| Board | Generic STM32F4 series* |
| Board part number | Generic F401CCUx* |
| Upload method | STM32CubeProgrammer (DFU) |
| Optimize | Smallest (-Os default) |
| C Runtime Library | Newlib Nano + Float Printf |
| USB Support | CDC (generic "Serial" supersede U(S)ART) |
| U(S)ART Support | Disabled (no Serial support) |
| USB Speed | Low/Full Speed |
*STM32F401CCU6 Black Pill, adjust according to your board
This is the only file you need to edit to adapt the firmware to your setup.
#define STM32Uncomment if using STM32. Leave commented for Arduino.
#define CAN2_OPTIONALEnable second CAN bus (IHS).
⚠️ STM32 users must physically disconnect the unused CAN2 module or it may cause SPI instability.
// #define MY23 // For 2023 clusters (shift light handling)
// #define SHIFT // Enable shift light for 7" TFT only
#define LEVEL_1 5400 // rpm value
#define LEVEL_2 5900 // rpm value
#define LEVEL_3 6400 // rpm valueMY23: enables dedicated handling for Giulia/Stelvio MY2023 clusters.SHIFT: enables shift light display on 7" clusters (do not use on 3.5").LEVEL_*: defines the 3 available shift warning levels on 7" clusters. Requires RACE mode enabled on the cluster (via PROXI alignment).
Leave them commented unless you need these features.
#define MCP1_CLOCK MCP_8MHZ
#define CAN1_SPEED CAN_500KBPS
#define MCP2_CLOCK MCP_8MHZ
#define CAN2_SPEED CAN_125KBPS- Clock frequency of each MCP2515 (typically 8MHz or 16MHz).
- Bitrate of each CAN bus (values for Giulia/Stelvio: 500kbps for CAN1, 125kbps for CAN2).
#define SEND_INTERVAL 0Set to 0 for maximum throughput (recommended). Increase only for debugging or bandwidth issues
// SPI
#define SCK_PIN PA5
#define MISO_PIN PA6
#define MOSI_PIN PA7
// CAN1
#define CAN1_CS_PIN PA4
#define CAN1_INT_PIN PA10
// CAN2 (optional)
#define CAN2_CS_PIN PB12
#define CAN2_INT_PIN PB13// CAN1
#define CAN1_CS_PIN 10
#define CAN1_INT_PIN 2
// CAN2 (optional)
#define CAN2_CS_PIN 9
#define CAN2_INT_PIN 3- Python 3.10+
pyserial
Install with (in Powershell):
pip install pyserialTo interact with the STM32 from your PC, the firmware exposes a USB CDC serial port (COM port).
Use the following parameters in Python script:
| Item | Value | Description |
|---|---|---|
| SERIAL_PORT | "COMx" | Adjust according to your system |
| BAUD_RATE | 250000 | Must match the STM32 USB CDC speed |
- ✅ STM32 or Arduino
- ✅ 2x MCP2515 CAN modules (TJA1050) (120Ω termination enabled — jumper or soldered pins)
- ✅ Alfa Romeo Giulia / Stelvio OEM instrument cluster (well… obviously)
- ✅ 12V power supply
- ✅ Wiring and connectors
| Pin | Description |
|---|---|
| 1 | Ground |
| 2 | Battery |
| 3 | ACC |
| 5 | BH - CAN A (Low) |
| 6 | BH - CAN B (High) |
| 11 | CAN-C1 A (Low) |
| 12 | CAN-C1 A (Low) |
| 13 | CAN-C1 B (High) |
| 14 | CAN-C1 B (High) |
⚠️ Notes:
- Pins 2 and 3 must be bridged together to +12V.
- Pins 11 and 12 are electrically connected (same for 13 and 14).
- You can use either pin in each pair — CAN A/B pairing order does not matter.
- I use a variable power supply set to 14.4V, but any source in the 12V–16V range works fine.
- If you don’t have the original OEM cluster connector, it’s not an issue.
Standard Dupont jumper wires or 2.54mm pitch connectors (2× 1x6) work reliably without major problems.
| STM32 Pin | MCP2515 #1 | MCP2515 #2 | Description |
|---|---|---|---|
| PA5 | SCK | SCK | SPI Clock |
| PA6 | MISO | MISO | SPI Master In |
| PA7 | MOSI | MOSI | SPI Master Out |
| 3.3V | VCC | VCC | Logic power |
| GND | GND | GND | Common ground |
| STM32 Pin | CAN Module | Description |
|---|---|---|
| PB12 | MCP2515 #1 | CS1 (CAN1 - 500k) |
| PA4 | MCP2515 #2 | CS2 (CAN2 - 125k) |
| PB13 | MCP2515 #1 | INT1 (IRQ input) |
| PA13 | MCP2515 #2 | INT2 (IRQ input) |
⚠️ Both MCP2515 share the same SPI bus. Only CS and INT are separate.
| Arduino Pin | MCP2515 #1 | MCP2515 #2 | Description |
|---|---|---|---|
| D13 | SCK | SCK | SPI Clock |
| D12 | MISO | MISO | SPI Master In |
| D11 | MOSI | MOSI | SPI Master Out |
| 5V | VCC | VCC | Logic power (5V) |
| GND | GND | GND | Common ground |
| Arduino Pin | CAN Module | Description |
|---|---|---|
| D10 | MCP2515 #1 | CS1 (CAN1 - 500k) |
| D9 | MCP2515 #2 | CS2 (CAN2 - 125k) |
| D2 | MCP2515 #1 | INT1 (IRQ input) |
| D3 | MCP2515 #2 | INT2 (IRQ input) |
⚠️ Both MCP2515 share the same SPI bus. Only CS and INT are separate.
- BeamNG.drive
- Assetto Corsa
- Alfa Romeo Giulia OEM cluster (2017 and 2020)
- STM32F401CCU6 (Black Pill)
- Arduino Nano
- Dual MCP2515 modules (8 MHz, TJA1050, 120Ω termination)
| Feature | Can Bus |
|---|---|
| RPM | CAN1 – BeamNG / Assetto Corsa |
| Speed | CAN1 – BeamNG / Assetto Corsa |
| Oil Temperature | CAN1 – Assetto Corsa / BeamNG (possible mismatch due to static OEM map) |
| Fuel Level | CAN1 – BeamNG / Assetto Corsa |
| ABS Light (lamp) | CAN1 – BeamNG / Assetto Corsa |
| ABS Active | CAN1 – BeamNG / Assetto Corsa |
| ESC Light | CAN1 – BeamNG / Assetto Corsa |
| ESC Active (TC/ESP) | CAN1 – BeamNG / Assetto Corsa |
| Handbrake Light | CAN1 – Only BeamNG |
| Battery Light | CAN1 – Only BeamNG |
| Check Engine (MIL) | CAN1 – Only BeamNG |
| Left Indicator | CAN2 – Only BeamNG |
| Right Indicator | CAN2 – Only BeamNG |
| Position Lights | CAN2 – Only BeamNG |
| Low Beam | CAN2 – Only BeamNG |
| High Beam | CAN2 – Only BeamNG |
| Front Fog Lights | CAN2 – Only BeamNG |
| Rear Fog Lights | CAN2 – Not currently driven by scripts |
| Auto Lights | CAN2 – Not currently driven by scripts |
| DNA Mode | CAN1 – BeamNG / Assetto Corsa (limited: Race/Normal only) |
| Shift Light | CAN1 – BeamNG / Assetto Corsa |
| Current Gear | CAN1 – BeamNG / Assetto Corsa |
| External Temperature | CAN2 – Assetto Corsa / BeamNG (BeamNG is static 20°) |
| MEDIA | CAN2 – Assetto Corsa (Best lap, or current if unavailable) / BeamNG (dummy) |
⚠️ When the cluster is in RACE profile, MEDIA and External Temp are not shown on 7" displays — this is OEM behavior.
ℹ️ The optional second CAN module (IHS) helps prevent odometer blinking by supplying additional messages expected by the cluster.
My glorious Setup:
-
General:
-
On first cluster power-up, a handbrake system fault warning may appear.
This is expected and will clear automatically after a few seconds. -
The following signals depend on game support:
- Primary lights (indicators, headlights, fog lights) → available only in BeamNG
- Battery / Check Engine → available only in BeamNG
-
The following values are approximated:
- Oil temperature (Assetto Corsa) → derived from water temperature
- DNA (Assetto Corsa) → simplified (Race / Normal only)
-
-
BeamNG.drive:
-
Requires OutGauge enabled
-
Fully supported:
- Primary lights
- Secondary warnings (ABS, ESC, handbrake, battery, MIL)
- Gear, RPM, speed, fuel, DNA
-
Limitations:
- **Oil temperature may have a mapping mismatch
- External temperature is not transmitted
-
-
Assetto Corsa:
-
No plugin required — uses shared memory
-
Supported:
- Core telemetry (RPM, speed, gear, fuel)
- Secondary warnings (ABS, ESC)
- External temperature
- Lap time via MEDIA
-
Limitations:
- No primary lights support (indicators, headlights, fog lights)
- No handbrake / battery / check engine signals
-
☕️ Like the project? Buy me a coffee!
Your support helps keep clusters alive and CAN buses busy.
MIT License — Use at your own risk.
Not affiliated with Alfa Romeo, FCA, or Stellantis.


