UDS-on-CAN traffic monitor – one layer above candump
Project description
udsdump
A command-line tool for monitoring UDS (Unified Diagnostic Services) traffic on CAN buses — one abstraction layer above candump.
While candump shows raw CAN frames, udsdump works at the UDS service level: it reassembles ISO-TP multi-frame messages, identifies request/response pairs, and outputs one line per complete UDS transaction with decoded metadata (service name, DID, sub-function, NRC, latency).
11:04:25.131 0x0680→0x0690 ReadDataByIdentifier DID=0x0100 (256) req=0x03 rsp=0x22 dt=8.0ms SF/MF ok
11:04:25.209 0x0680→0x0690 WriteDataByIdentifier DID=0x023A (570) req=0x07 rsp=0x03 dt=12.3ms MF/SF ok
11:04:25.308 0x0680→0x0690 DiagnosticSessionControl sub=0x01 req=0x02 rsp=0x02 dt=5.1ms SF/SF ok
11:04:26.416 0x0680→0x0690 ReadDataByIdentifier DID=0x0200 (512) req=0x03 rsp=0x00 SF timeout
11:04:26.524 0x0680→0x0690 SecurityAccess sub=0x01 req=0x02 rsp=0x03 dt=6.7ms SF/SF nrc NRC=0x35(invalidKey)
Features
- One line per UDS transaction — request and response combined, regardless of SF or MF
- ISO-TP reassembly — handles Single Frame and Multi-Frame messages transparently;
SF/MFin the output shows the frame type of each direction - Parallel conversations — multiple simultaneous UDS sessions on different ID pairs are handled independently
- Status metadata — every line reports the outcome:
ok,nrc(negative response with code and name), ortimeout - NRC 0x78 handling — ResponsePending is treated correctly: the timeout is restarted, the transaction stays open, and the final
pending_countis reported - Traffic statistics — periodic and final summary with latency percentiles, success rates, and optional breakdown by ID pair and/or service
- Optional raw payload —
--payloadappends the raw UDS bytes as hex strings - JSON output —
--jsonfor machine-readable output (only non-empty fields) - Flexible ID configuration — automatic offset mode (default
req + 0x10 = rsp) or explicit ID pairs
Requirements
- Python 3.10+
- python-can ≥ 4.3
- A CAN interface supported by python-can (SocketCAN, PEAK, Kvaser, virtual, …)
Installation
pip install udsdump
To pin a specific version:
pip install udsdump==0.1.1
For local development, clone the repository and install in editable mode:
git clone https://github.com/MyHomeMyData/udsdump.git
cd udsdump
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Quick Start
Monitor UDS traffic on vcan0 with default settings (SocketCAN, ID range 0x600–0x6FF, response offset +0x10):
udsdump --channel vcan0
Monitor specific device pairs (e.g. Viessmann E3 heat pump):
udsdump --channel can0 --id-pair 0x680:0x690 --id-pair 0x6A1:0x6B1
JSON output, piped to jq for filtering:
udsdump --channel can0 --json | jq 'select(.status == "nrc")'
Statistics only, printed every 5 minutes:
udsdump --channel can0 --stats-interval 300 --no-transactions
CLI Reference
udsdump [options]
CAN interface:
--interface, -i INTERFACE python-can interface name (default: socketcan)
--channel, -c CHANNEL CAN channel (default: vcan0)
--bitrate, -b BITRATE Bus bitrate in bit/s (default: 500000)
ID pair configuration (mutually exclusive):
--id-pair REQ:RSP Explicit request:response ID pair (hex).
May be repeated for multiple pairs.
--response-offset OFFSET Response ID = request ID + OFFSET (hex).
(default: 0x10)
--id-range MIN:MAX CAN ID range to monitor (hex).
Used with --response-offset. (default: 0x600:0x6FF)
Ignored when --id-pair is used.
--ignore-requesters IDS Comma-separated hex CAN IDs to exclude as
requesters (e.g. 0x691,0x696).
Behaviour:
--timeout, -t SECONDS Response timeout in seconds (default: 1.0)
Transaction output:
--json One JSON object per line instead of text
--payload Append raw UDS payload bytes (hex) to each line
--no-transactions Suppress per-transaction output (statistics only)
Statistics:
--stats-interval N Print periodic statistics every N seconds
--stats-breakdown KEY Break down statistics by: pair, service, or pair,service
ID pair configuration
Offset mode (default): Every CAN ID in --id-range is treated as a potential request ID. The corresponding response ID is request_id + offset. This covers the common UDS convention and Viessmann E3 devices without any extra configuration.
# Viessmann E3: request 0x680, response 0x690; request 0x6A1, response 0x6B1; …
udsdump --channel can0 --response-offset 0x10 --id-range 0x680:0x6B1
Explicit pair mode: Use --id-pair when devices do not follow a fixed offset. Multiple --id-pair arguments are supported.
udsdump --channel can0 --id-pair 0x7DF:0x7E8 --id-pair 0x712:0x733
Ignoring specific requesters: Some CAN nodes send requests that are outside the monitored topology (e.g. they communicate with a peer whose response arrives on a different ID than expected). Use --ignore-requesters to exclude them entirely.
# Viessmann E3: suppress noise from 0x691 and 0x696
udsdump --channel can0 --ignore-requesters 0x691,0x696
Output Format
Text (default)
HH:MM:SS.mmm REQ_ID→RSP_ID ServiceName [DID=0xNNNN (DDD)|sub=0xNN] req=0xNN rsp=0xNN [dt=N.Nms] FT status [NRC]
| Field | Description |
|---|---|
HH:MM:SS.mmm |
Timestamp of the request |
REQ_ID→RSP_ID |
CAN ID pair (hex) |
ServiceName |
UDS service (e.g. ReadDataByIdentifier) |
DID=0x… (DDD) |
Data Identifier in hex and decimal |
sub=0x… |
Sub-function where applicable |
req=0xNN / rsp=0xNN |
Payload length in bytes (hex) |
dt=N.Nms |
Round-trip latency; absent on timeout |
FT |
Frame type: SF/SF, SF/MF, MF/SF, MF/MF, or just SF/MF on timeout |
status |
ok, nrc, or timeout |
NRC=0xNN(name) |
NRC code and name on nrc status |
pending×N |
Number of NRC 0x78 (ResponsePending) received before final answer |
With --payload, two additional hex fields are appended: req_data=… rsp_data=….
JSON
Each transaction is a JSON object on a single line. Only non-empty fields are included.
{"timestamp": 1746789865.131, "request_id": 1664, "response_id": 1680, "service_id": 34, "service_name": "ReadDataByIdentifier", "req_frame_type": "SF", "rsp_frame_type": "MF", "status": "ok", "did": 256, "req_length": 3, "rsp_length": 34, "duration_ms": 8.0}
Timeout example:
{"timestamp": 1746789866.416, "request_id": 1664, "response_id": 1680, "service_id": 34, "service_name": "ReadDataByIdentifier", "req_frame_type": "SF", "status": "timeout", "did": 512, "req_length": 3}
Statistics
When any --stats-* flag or --no-transactions is set, udsdump collects traffic statistics. Transaction lines are written to stdout; statistics are written to stderr — the two streams never mix, so JSON piping remains clean.
A final summary is always printed on Ctrl+C.
Periodic intervals
# Print stats every 60 seconds, with breakdown by ID pair
udsdump --channel can0 --stats-interval 60 --stats-breakdown pair
────────────────────────────────────────────────────────────────────────────────
Stats [11:05:00 – 11:06:00] (60s)
Transactions total=28 ok=26 nrc=1 timeout=1 rate=0.47/s success=92.9%
NRC conditionsNotCorrect=1
Latency (ok) min=5.1ms mean=14.3ms median=12.1ms p95=38.2ms max=45.2ms
By ID pair:
0x0680→0x0690 ok=24 nrc=1 timeout=1 min=5.1ms mean=14.3ms ...
0x06A1→0x06B1 ok=2 nrc=0 timeout=0 min=6.2ms mean=8.1ms ...
────────────────────────────────────────────────────────────────────────────────
Final summary
════════════════════════════════════════════════════════════════════════════════
Summary (runtime: 5m 23s)
Transactions total=142 ok=135 nrc=3 timeout=2 rate=0.44/s success=95.1%
NRC conditionsNotCorrect=2 invalidKey=1
Pending 0x78 4 transaction(s)
Latency (ok) min=4.2ms mean=18.7ms median=12.3ms p95=89.1ms max=312.5ms
By service:
ReadDataByIdentifier ok=115 nrc=2 timeout=1 min=4.2ms mean=15.1ms p95=89.1ms max=312.5ms
TesterPresent ok=20 nrc=0 timeout=0 min=4.5ms mean=5.3ms p95=7.2ms max=8.1ms
DiagnosticSessionControl ok=5 nrc=1 timeout=1 min=12.1ms mean=21.3ms p95=45.2ms max=48.0ms
════════════════════════════════════════════════════════════════════════════════
Statistics-only mode
Suppress transaction lines entirely and collect a summary over the full run:
udsdump --channel can0 --no-transactions --stats-breakdown pair,service
Or combine with periodic output:
udsdump --channel can0 --no-transactions --stats-interval 300 --stats-breakdown service
Combining with JSON
Statistics always appear as human-readable text on stderr, regardless of --json. This allows clean downstream processing:
# JSON transactions to file, stats visible on terminal
udsdump --channel can0 --json --stats-interval 60 > transactions.jsonl
# Filter NRC events while monitoring stats
udsdump --channel can0 --json --stats-breakdown pair | jq 'select(.status == "nrc")'
Decoded UDS Services
| SID | Service |
|---|---|
| 0x10 | DiagnosticSessionControl |
| 0x11 | ECUReset |
| 0x14 | ClearDiagnosticInformation |
| 0x19 | ReadDTCInformation |
| 0x22 | ReadDataByIdentifier |
| 0x27 | SecurityAccess |
| 0x28 | CommunicationControl |
| 0x2E | WriteDataByIdentifier |
| 0x31 | RoutineControl |
| 0x34 | RequestDownload |
| 0x36 | TransferData |
| 0x37 | RequestTransferExit |
| 0x3E | TesterPresent |
Positive responses (SID + 0x40) and negative responses (0x7F) are matched automatically.
Library API
udsdump can also be used as a Python library:
import asyncio
from udsdump import UDSMonitor
async def main():
monitor = UDSMonitor(
interface="socketcan",
channel="can0",
explicit_pairs=[(0x680, 0x690)],
timeout=1.0,
)
async for tx in monitor.transactions():
print(tx.service_name, tx.did, tx.status, tx.duration_ms)
asyncio.run(main())
Or with a callback:
monitor.on_transaction(lambda tx: print(tx))
await monitor.run()
Development
# Install with dev dependencies
pip install -e .
pip install pytest
# Run tests
pytest
81 tests cover the ISO-TP reassembler, the UDS decoder, the transaction manager (pairing, timeouts, NRC 0x78, parallel sessions, multi-frame handling), the statistics collector, and the ignore-requesters filter.
Acknowledgements
The ISO-TP reassembler is adapted from E3onCANserver by the same author.
Changelog
0.1.1 — 2026-05-19
- Update installation docs for PyPI release
- Fix publish workflow to target pypi.org
0.1.0 — 2026-05-11
- Initial release
- ISO-TP reassembly (SF and MF), one line per UDS transaction
- NRC 0x78 (ResponsePending) handling: timeout is restarted, transaction stays open, final
pending_countis reported - Traffic statistics with periodic intervals and final summary; optional breakdown by ID pair and/or service
--ignore-requestersto suppress noisy requester IDs whose responses never arrive- JSON output (
--json), raw payload option (--payload) - 81 tests
License
MIT License
Copyright (c) 2026 MyHomeMyData juergen.bonfert@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file udsdump-0.1.1.tar.gz.
File metadata
- Download URL: udsdump-0.1.1.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
917fcf3c5652042e87307a1ae8dd433c07973cb5c9f5e04caf65a52990745d2e
|
|
| MD5 |
aa82fe144415e773edbd4dd4f78d2c59
|
|
| BLAKE2b-256 |
4f2d3c5bd289882adbd53cd41ceb8d50d62e647f65e6af5538969cbc6ea95133
|
Provenance
The following attestation bundles were made for udsdump-0.1.1.tar.gz:
Publisher:
publish.yml on MyHomeMyData/udsdump
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
udsdump-0.1.1.tar.gz -
Subject digest:
917fcf3c5652042e87307a1ae8dd433c07973cb5c9f5e04caf65a52990745d2e - Sigstore transparency entry: 1573239074
- Sigstore integration time:
-
Permalink:
MyHomeMyData/udsdump@6e6fc003c2ac53fb8c0ec11fb6fb15c4eb00a265 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/MyHomeMyData
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6e6fc003c2ac53fb8c0ec11fb6fb15c4eb00a265 -
Trigger Event:
push
-
Statement type:
File details
Details for the file udsdump-0.1.1-py3-none-any.whl.
File metadata
- Download URL: udsdump-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb496027fa93e04b3fc2c7708616e5164819e079a91a43ff37ed2a8c712555fa
|
|
| MD5 |
51e1dbc839c6ce2d71a94831ae986d20
|
|
| BLAKE2b-256 |
f259b51e03da045f0b27295cf81bd340773c2a737e390d508220c857ddb3bd34
|
Provenance
The following attestation bundles were made for udsdump-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on MyHomeMyData/udsdump
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
udsdump-0.1.1-py3-none-any.whl -
Subject digest:
fb496027fa93e04b3fc2c7708616e5164819e079a91a43ff37ed2a8c712555fa - Sigstore transparency entry: 1573239078
- Sigstore integration time:
-
Permalink:
MyHomeMyData/udsdump@6e6fc003c2ac53fb8c0ec11fb6fb15c4eb00a265 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/MyHomeMyData
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6e6fc003c2ac53fb8c0ec11fb6fb15c4eb00a265 -
Trigger Event:
push
-
Statement type: