DBC Authoring Best Practices, Multiplexing, CAN FD Extensions, and Tooling
Document ID: DBC-02 Series: Telematics Tutorial Series Target audience: Advanced — you should be comfortable writing Database CAN (DBC) files (DBC-01) and have experience with at least one CAN toolchain.
Prerequisites and Cross-References
- For DBC syntax and signal encoding, see DBC-01.
- For CAN FD frame format, see CAN-02.
- For CAN FD bit timing, see CAN-03.
- For socketCAN integration, see CAN-04.
Learning Objectives
By the end of this document, you will be able to:
- Apply DBC authoring conventions that improve readability, maintainability, and tool compatibility
- Define multiplexed messages using the standard
mandMsyntax - Use extended multiplexing (
SG_MUL_VAL_) for complex multiplex schemes - Extend DBC files for CAN Flexible Data-rate (CAN FD) payloads larger than 8 bytes
- Compare DBC tooling options (CANdb++, cantools, canmatrix, SavvyCAN) and choose the right tool for your workflow
1. Authoring Conventions
Well-structured DBC files are easier to maintain, less likely to cause parsing errors, and more portable across tools. These conventions are not enforced by the format but are widely followed in the automotive and industrial automation industries. (For the complete DBC section-by-section grammar and BNF syntax of each keyword used below, see DBC-01, Section 3.)
1.1 Naming Conventions
Messages:
- Use PascalCase: EngineData, TransmissionStatus, BrakePressure
- Prefix with the subsystem or Electronic Control Unit (ECU) name for large databases: ENG_EngineData, TCU_GearStatus
- Keep names under 32 characters for compatibility with older tools
Signals:
- Use PascalCase: EngineSpeed, CoolantTemp, WheelSpeed_FL
- Include the axis or location suffix for multi-instance signals: _FL (front left), _RR (rear right), _X, _Y, _Z
- Avoid abbreviations that are not universally understood — EngSpd is acceptable, ES is not
Nodes:
- Use UPPER_CASE or PascalCase: ENGINE_ECU, EngineECU
- Match the names used in the system architecture documentation
💡 This naming convention is consistent with Vector CANdb++ defaults and AUTOSAR naming guidelines, making DBC files produced this way portable across the widest range of industry tools.
1.2 Message Organization
- Assign Controller Area Network (CAN) IDs in logical blocks by subsystem:
- 0x100–0x1FF: Powertrain
- 0x200–0x2FF: Chassis
- 0x300–0x3FF: Body
- 0x400–0x4FF: Infotainment
-
0x700–0x7FF: Diagnostics
-
Set the
GenMsgCycleTimeattribute for every cyclic message - Set the
GenMsgSendTypeattribute (Cyclic,Event,IfActive,NoMsgSendType) - Group related signals contiguously within a message — do not scatter bits randomly
💡 The CAN ID block allocation above aligns with common OEM practice and makes it easy to configure hardware message filters by subsystem (e.g., accept only 0x100–0x1FF on a powertrain gateway).
1.3 Signal Packing Rules
- Align signals to byte boundaries when possible — an 8-bit signal starting at bit 0, 8, 16, or 24 is easier to read in raw hex dumps
- Reserve unused bits — define placeholder signals (e.g.,
Reserved_24_8) for unused bit regions to prevent accidental overlap when signals are added later - Use consistent byte order within a DBC file — mixing Intel and Motorola signals in the same message is valid but error-prone (for signal byte-order encoding details and the start-bit conventions for Intel vs Motorola, see DBC-01, Section 4)
- Set the minimum signal length needed for the value range — do not use 16 bits for a signal that only needs 8
- Document assumptions in
CM_comments — scaling factors, offset rationale, and source specification references
1.4 Version Control
- Store DBC files in version control (Git)
- Use meaningful commit messages: “Add WheelSpeed signals to ChassisData (0x201)”
- Add a
VERSIONstring that matches your version control tag - Consider generating DBC files from a structured source (spreadsheet, AUTOSAR XML (ARXML)) rather than hand-editing, to reduce human error
💡 Tip: For large databases (50+ messages), use canmatrix to convert between DBC and other formats (ARXML, **Kayak CAN Definition (KCD)**, Excel). This lets you maintain the database in a format that is easier to diff and review, then generate the DBC for tool consumption.
1.5 Conventions Applied — Complete Minimal Example
The following DBC snippet demonstrates naming conventions, message organization, reserved bits, byte-aligned signals, and CM_ documentation working together:
VERSION "1.0"
NS_ :
BS_:
BU_: ENGINE_ECU DASHBOARD_ECU
BO_ 256 ENG_EngineData: 8 ENGINE_ECU
SG_ EngineSpeed : 0|16@1+ (0.25,0) [0|16383.75] "RPM" DASHBOARD_ECU
SG_ CoolantTemp : 16|8@1+ (1,-40) [-40|215] "degC" DASHBOARD_ECU
SG_ ThrottlePos : 24|8@1+ (0.3922,0) [0|100.0] "%" DASHBOARD_ECU
SG_ Reserved_32_32 : 32|32@1+ (1,0) [0|0] "" Vector__XXX
CM_ BO_ 256 "Engine data broadcast at 100 ms. Source: powertrain spec v2.3.";
CM_ SG_ 256 EngineSpeed "Crankshaft RPM. Factor 0.25 per SAE J1979 PID 0x0C.";
CM_ SG_ 256 Reserved_32_32 "Reserved for future use — do not assign.";
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 10000;
BA_DEF_ BO_ "GenMsgSendType" STRING;
BA_ "GenMsgCycleTime" BO_ 256 100;
BA_ "GenMsgSendType" BO_ 256 "Cyclic";
This example follows all conventions from Sections 1.1–1.4: PascalCase names with subsystem prefix, CAN ID in the powertrain block (0x100), byte-aligned signals, a reserved-bit placeholder, CM_ comments documenting source specifications, and GenMsgCycleTime / GenMsgSendType attributes.
📝 Note: on ThrottlePos factor: The factor `0.3922` is chosen so that the full 8-bit range maps to approximately 100%: `255 × 0.3922 = 100.011`. A coarser factor such as `0.392` would yield `255 × 0.392 = 99.96`, falling slightly short of the intended 100% maximum. When specifying factors, verify that `(max_raw × factor) + offset` produces the intended engineering maximum. Rounding the factor can cause the decoded range to fall slightly short.
📝 Note: on `BS_:` spacing: The `BS_:` line must appear exactly as shown — `BS_:` followed by no content. Some parsers are strict about the space before the colon. If you see parse errors near the bus-speed section, verify there are no trailing characters or missing whitespace on the `BS_:` line.
2. Multiplexed Messages
A multiplexed message is a CAN message where the interpretation of some signals depends on the value of a multiplexer (selector) signal. This allows a single CAN ID to carry different signal sets in different frames, effectively multiplying the data capacity of one message ID.
2.1 Why Use Multiplexing?
- CAN has a limited number of IDs (2,048 for standard, though practically fewer due to priority allocation)
- Low-frequency signals (e.g., configuration parameters, Diagnostic Trouble Codes (DTC)) do not need a dedicated message ID
- Multiplexing lets you share one ID across multiple signal sets, each identified by a multiplexer value
2.2 Basic Multiplexing Syntax
In a DBC file, multiplexing uses two signal modifiers:
graph TB
MSG[" CAN Message 0x320 (8 bytes)
MUX Selector in bits 0-3"]
MSG --> MUX{"MUX Selector
(bits 0-3)"}
MUX -->|"= 0"| M0[" m0: Battery Data
BatteryVoltage (8:16) V
BatteryTemp (24:8) °C"]
MUX -->|"= 1"| M1[" m1: Fuel Data
FuelLevel (8:16) L
FuelConsumption (24:16) L/h"]
MUX -->|"= 2"| M2[" m2: Odometer
OdometerTotal (8:32) km"]
style MSG fill:#37474F,stroke:#333,color:#fff
style MUX fill:#FFC107,stroke:#333
style M0 fill:#42A5F5,stroke:#333,color:#fff
style M1 fill:#66BB6A,stroke:#333,color:#fff
style M2 fill:#FF9800,stroke:#333,color:#fff
Figure: DBC02 01 multiplexed message
M— marks the multiplexer signal (the selector)m<N>— marks a multiplexed signal that is valid only when the multiplexer equalsN
(For the full SG_ line grammar including the multiplexor designator field, see DBC-01, Section 3.6.)
BO_ 800 DiagMultiplex: 8 DiagTester
SG_ MuxSelector M : 0|4@1+ (1,0) [0|15] "" Vector__XXX
SG_ BatteryVoltage m0 : 8|16@1+ (0.01,0) [0|655.35] "V" DashboardECU
SG_ BatteryTemp m0 : 24|8@1+ (1,-40) [-40|215] "degC" DashboardECU
SG_ FuelLevel m1 : 8|16@1+ (0.1,0) [0|6553.5] "L" DashboardECU
SG_ FuelConsumption m1 : 24|16@1+ (0.01,0) [0|655.35] "L/h" DashboardECU
SG_ OdometerTotal m2 : 8|32@1+ (0.1,0) [0|429496729.5] "km" DashboardECU
See Diagram D-DBC02-01: Multiplexed Message Structure for a visual representation of how the MUX selector branches to different signal sets.
In this example:
- MuxSelector (bits 0–3) is the multiplexer signal, marked with M
- When MuxSelector = 0: BatteryVoltage and BatteryTemp are valid (m0)
- When MuxSelector = 1: FuelLevel and FuelConsumption are valid (m1)
- When MuxSelector = 2: OdometerTotal is valid (m2)
2.3 Decoding Multiplexed Messages
# Requires: pip install cantools>=39.0
# Note: CAN FD features (DLC > 8, SIG_VALTYPE_ float/double) require cantools 39.x+.
import cantools
db = cantools.database.load_file('vehicle.dbc')
# Frame with MuxSelector = 0 (battery data)
data_mux0 = bytes([0x00, 0xD0, 0x07, 0x54, 0x00, 0x00, 0x00, 0x00])
decoded = db.decode_message(800, data_mux0)
print(decoded)
# Frame with MuxSelector = 1 (fuel data)
data_mux1 = bytes([0x01, 0xE8, 0x03, 0x64, 0x00, 0x00, 0x00, 0x00])
decoded = db.decode_message(800, data_mux1)
print(decoded)
# Output
{'MuxSelector': 0, 'BatteryVoltage': 20.0, 'BatteryTemp': 44}
{'MuxSelector': 1, 'FuelLevel': 100.0, 'FuelConsumption': 1.0}
The cantools library automatically detects the multiplexer value and decodes only the signals that are valid for that value.
2.4 Encoding Multiplexed Messages
To encode a multiplexed frame, pass the multiplexer value along with the signals for that mux group:
# (cantools already imported above)
import cantools
db = cantools.database.load_file('vehicle.dbc')
# Encode a frame with MuxSelector = 0 (battery data)
encoded = db.encode_message('DiagMultiplex', {
'MuxSelector': 0,
'BatteryVoltage': 20.0,
'BatteryTemp': 44
})
print(encoded.hex())
# Output
00d0075400000000
The encode_message method sets the multiplexer signal to the specified value and packs only the signals that belong to that mux group. Signals from other mux groups are not included and their bit positions are zero-filled.
2.5 Extended Multiplexing (SG_MUL_VAL_)
Standard multiplexing supports only one multiplexer signal per message, and each multiplexed signal maps to exactly one multiplexer value. Extended multiplexing removes these limitations:
- Multiple levels of multiplexing (a multiplexed signal can itself be a multiplexer)
- Range-based multiplexer values (a signal valid for multiplexer values 3–7)
- Multiple multiplexer signals per message
The extended multiplexing syntax uses the SG_MUL_VAL_ section:
SG_MUL_VAL_ <CAN_ID> <MultiplexedSignal> <MultiplexerSignal> <Range1>, <Range2>, ... ;
Where each range is: <start>-<end>
SG_MUL_VAL_ 800 BatteryVoltage MuxSelector 0-0;
SG_MUL_VAL_ 800 BatteryTemp MuxSelector 0-0;
SG_MUL_VAL_ 800 FuelLevel MuxSelector 1-1;
SG_MUL_VAL_ 800 FuelConsumption MuxSelector 1-3;
SG_MUL_VAL_ 800 OdometerTotal MuxSelector 4-7;
In this example, FuelConsumption is valid for multiplexer values 1, 2, and 3 (range 1-3), and OdometerTotal is valid for values 4 through 7.
⚠️ Warning: Extended multiplexing (`SG_MUL_VAL_`) is not supported by all DBC parsers. CANdb++ (Vector), cantools, and canmatrix support it. Some older tools and embedded DBC parsers do not. If your toolchain does not support extended multiplexing, fall back to standard `M`/`m` syntax or use separate message IDs.
Extended Multiplexing (SG_MUL_VAL_) Decode Example
Extended multiplexing (SG_MUL_VAL_) allows a signal’s presence to depend on the value of one or more multiplexer signals, with range-based conditions rather than simple equality.
BO_ 600 SensorData: 8 SensorHub
SG_ MuxSelector : 0|8@1+ (1,0) [0|255] "" Vector__XXX M
SG_ Temperature : 8|16@1- (0.1,-40) [-40|120] "degC" Vector__XXX m0
SG_ Pressure : 8|16@1+ (0.1,0) [0|1000] "kPa" Vector__XXX m1
SG_ Humidity : 8|16@1+ (0.1,0) [0|100] "%" Vector__XXX m2
SG_ Accel_X : 8|16@1- (0.01,0) [-327.68|327.67] "m/s2" Vector__XXX m3
SG_ Accel_Y : 24|16@1- (0.01,0) [-327.68|327.67] "m/s2" Vector__XXX m3
SG_ DiagCode : 8|8@1+ (1,0) [0|255] "" Vector__XXX m200
SG_MUL_VAL_ 600 Accel_X MuxSelector 3-3;
SG_MUL_VAL_ 600 Accel_Y MuxSelector 3-3;
SG_MUL_VAL_ 600 DiagCode MuxSelector 200-255;
# Requires: pip install cantools>=39.0
import cantools
db = cantools.database.load_file('sensor_mux.dbc')
msg = db.get_message_by_name('SensorData')
# Frame with MuxSelector = 3 (accelerometer data)
frame_accel = bytes([3, 0x00, 0x10, 0xFF, 0xF0, 0, 0, 0])
decoded = msg.decode(frame_accel)
print(f"MuxSelector=3: {decoded}")
# Output: {'MuxSelector': 3, 'Accel_X': 40.96, 'Accel_Y': -40.96}
# Frame with MuxSelector = 210 (diagnostic range 200-255)
frame_diag = bytes([210, 0x42, 0, 0, 0, 0, 0, 0])
decoded = msg.decode(frame_diag)
print(f"MuxSelector=210: {decoded}")
# Output: {'MuxSelector': 210, 'DiagCode': 66}
3. CAN FD Extensions
See Diagram D-DBC02-03: Classic CAN vs CAN FD DBC Syntax for a side-by-side comparison of the key differences.
graph LR
subgraph CLASSIC["Classic CAN DBC"]
direction TB
C1["BO_ 291 EngineData: 8 ECU1"]
C2["SG_ Speed : 0|16@1+
(0.1,0) [0|6553.5] RPM"]
C3["Max bit range: 0-63"]
C4["BusType: 'CAN'"]
end
subgraph CANFD["CAN FD DBC"]
direction TB
F1["BO_ 300 SensorDataFD: 64 SensorECU"]
F2["SG_ Accel_X : 0|16@1-
(0.001,0) [-32|32] g"]
F3["Max bit range: 0-511"]
F4["BusType: 'CAN FD'"]
F5["SIG_VALTYPE_ 300 FloatSig : 1"]
end
CLASSIC -->|"Extended for
larger payloads"| CANFD
style CLASSIC fill:#E3F2FD,stroke:#1565C0
style CANFD fill:#FFF3E0,stroke:#E65100
style C1 fill:#BBDEFB,stroke:#1565C0
style F1 fill:#FFE0B2,stroke:#E65100
style F5 fill:#FFCC80,stroke:#E65100
Figure: DBC02 03 canfd extensions
Standard DBC files were designed for classical CAN with a maximum of 8 data bytes. CAN FD supports up to 64 bytes, requiring extensions to the DBC format.
📝 Note: A CAN FD node can transmit classical CAN frames (without **Bit Rate Switch (BRS)** or **FD Format indicator (FDF)** bits set). When a DBC file includes both classical and CAN FD messages, the message's DLC and any FD-specific attributes (e.g., `BRS`) determine which frame format is used on the bus. (For the CAN FD frame format and the role of BRS/FDF bits, see CAN-02. For CAN FD bit timing and data-phase baud rate configuration, see CAN-03.)
3.1 DLC Values Above 8
For CAN FD messages, set the Data Length Code (DLC) in the BO_ line to the actual byte count (up to 64). The DLC follows the CAN FD non-linear mapping:
| DLC Code | Data Length (bytes) |
|---|---|
| 0–8 | 0–8 |
| 9 | 12 |
| 10 | 16 |
| 11 | 20 |
| 12 | 24 |
| 13 | 32 |
| 14 | 48 |
| 15 | 64 |
📝 Note: The table above shows the CAN FD *protocol-level* DLC codes. In a DBC file, the number after the message name in the `BO_` line is the **payload byte count**, not the 4-bit DLC code. For a 64-byte CAN FD payload, write `64`, not `15`.
BO_ 300 SensorDataFD: 64 SensorECU
SG_ Accel_X : 0|16@1- (0.001,0) [-32.768|32.767] "g" ControllerECU
SG_ Accel_Y : 16|16@1- (0.001,0) [-32.768|32.767] "g" ControllerECU
SG_ Accel_Z : 32|16@1- (0.001,0) [-32.768|32.767] "g" ControllerECU
SG_ Gyro_X : 48|16@1- (0.01,0) [-327.68|327.67] "deg/s" ControllerECU
SG_ Gyro_Y : 64|16@1- (0.01,0) [-327.68|327.67] "deg/s" ControllerECU
SG_ Gyro_Z : 80|16@1- (0.01,0) [-327.68|327.67] "deg/s" ControllerECU
SG_ Timestamp : 96|32@1+ (0.001,0) [0|4294967.295] "s" ControllerECU
This message uses 128 bits (16 bytes) of signal data within a 64-byte CAN FD frame.
3.2 Bus Type Attribute
To indicate that a DBC file describes a CAN FD network, set the BusType attribute:
BA_DEF_ "BusType" STRING;
BA_DEF_DEF_ "BusType" "CAN";
BA_ "BusType" "CAN FD";
3.3 Signal Value Type for Floating Point
CAN FD’s larger payloads make it practical to transmit Institute of Electrical and Electronics Engineers (IEEE) 754 floating-point values directly. Use SIG_VALTYPE_ to declare a signal as float (1) or double (2):
SIG_VALTYPE_ 300 FloatSignal : 1; /* IEEE 754 single-precision float */
SIG_VALTYPE_ 300 DoubleSignal : 2; /* IEEE 754 double-precision float */
When a signal is declared as float or double, the factor and offset in the SG_ line are typically set to (1,0) — the raw bits are interpreted directly as an IEEE 754 value.
3.4 Classic CAN vs CAN FD DBC Comparison
| Feature | Classic CAN DBC | CAN FD DBC |
|---|---|---|
| Max DLC | 8 | 64 |
| Signal start bit range | 0–63 | 0–511 |
| BusType attribute | “CAN” or absent | “CAN FD” |
| SIG_VALTYPE_ (float) | Rare | Common |
| Tool compatibility | Universal | Requires CAN FD-aware tools |
CAN FD DBC Decode/Encode Walkthrough
This walkthrough demonstrates encoding and decoding a 64-byte CAN FD message with mixed signal types.
BO_ 512 BMS_CellData: 64 BMS_Master
SG_ CellVoltage_01 : 0|16@1+ (0.001,0) [0|6.5] "V" Vector__XXX
SG_ CellVoltage_02 : 16|16@1+ (0.001,0) [0|6.5] "V" Vector__XXX
SG_ CellVoltage_03 : 32|16@1+ (0.001,0) [0|6.5] "V" Vector__XXX
SG_ CellVoltage_04 : 48|16@1+ (0.001,0) [0|6.5] "V" Vector__XXX
SG_ PackTemperature : 64|16@1- (0.1,-40) [-40|120] "degC" Vector__XXX
SG_ PackCurrent : 80|32@1- (0.01,0) [-3276.8|3276.7] "A" Vector__XXX
SG_ SOC_Precise : 112|16@1+ (0.01,0) [0|100] "%" Vector__XXX
SG_ BRS_Flag : 504|1@1+ (1,0) [0|1] "" Vector__XXX
# Requires: pip install cantools>=39.0
import cantools
import struct
# Load database (or construct inline for demonstration)
db = cantools.database.load_file('bms_canfd.dbc')
msg = db.get_message_by_name('BMS_CellData')
# Encode signals to raw CAN FD frame
data = msg.encode({
'CellVoltage_01': 3.456, # 3.456V
'CellVoltage_02': 3.512,
'CellVoltage_03': 3.489,
'CellVoltage_04': 3.501,
'PackTemperature': 25.3, # 25.3°C
'PackCurrent': -12.50, # -12.50A (discharging)
'SOC_Precise': 78.45, # 78.45%
'BRS_Flag': 1
})
print(f"Encoded CAN FD frame ({len(data)} bytes):")
print(' '.join(f'{b:02X}' for b in data))
# Decode back
decoded = msg.decode(data)
for name, value in decoded.items():
signal = msg.get_signal_by_name(name)
print(f" {name}: {value} {signal.unit}")
# Expected output (exact hex values depend on encoding):
Encoded CAN FD frame (64 bytes):
80 0D 98 0D A1 0D AD 0D 3B 02 34 FB FF FF 9D 1E ...
CellVoltage_01: 3.456 V
CellVoltage_02: 3.512 V
CellVoltage_03: 3.489 V
CellVoltage_04: 3.501 V
PackTemperature: 25.3 degC
PackCurrent: -12.5 A
SOC_Precise: 78.45 %
BRS_Flag: 1
BRS Flag Handling:
The BRS (Bit Rate Switch) flag is a CAN FD bus-level feature, not a DBC signal. However, some DBC files include a BRS pseudo-signal for documentation or simulation purposes. When encoding/decoding real CAN FD frames, the BRS flag is set via the CAN driver, not the DBC:
# Requires: pip install python-can>=4.0
import can
msg = can.Message(
arbitration_id=0x200,
data=data,
is_fd=True,
bitrate_switch=True # This sets the BRS flag at the CAN FD level
)
SIG_VALTYPE_ Float Example with IEEE 754 Round-Trip:
# SIG_VALTYPE_ 512 PackCurrent : 2; — declares PackCurrent as IEEE 754 double
# When SIG_VALTYPE_ is set to 1 (float) or 2 (double), the raw bits are
# interpreted as IEEE 754 floating-point rather than integer * factor + offset
import struct
# Encode a float signal directly
current = -12.50
raw_bytes = struct.pack('<f', current) # Little-endian IEEE 754 float
print(f"IEEE 754 float: {current} -> {raw_bytes.hex()}")
# Decode back — verify round-trip
decoded = struct.unpack('<f', raw_bytes)[0]
print(f"Round-trip: {decoded}")
assert abs(decoded - current) < 1e-6, "Round-trip failed!"
print("IEEE 754 round-trip: PASS")
# Expected output
IEEE 754 float: -12.5 -> 000048c1
Round-trip: -12.5
IEEE 754 round-trip: PASS
4. Tooling Ecosystem
See Diagram D-DBC02-02: DBC Tooling Ecosystem for an overview of how DBC files flow between the tools described below.
graph TB
DBC[" DBC File
(Database CAN)"]
CANDB[" CANdb++
(Vector)
Author & Edit"]
CANTOOLS[" cantools
(Python)
Encode & Decode"]
CANMATRIX[" canmatrix
(Python)
Format Conversion"]
SAVVY[" SavvyCAN
(GUI)
Analysis & Visualization"]
CANDB <-->|"creates / edits"| DBC
CANTOOLS <-->|"reads / writes"| DBC
CANMATRIX <-->|"reads / writes"| DBC
SAVVY -->|"reads"| DBC
CANMATRIX <-->|".arxml"| ARXML["ARXML
(AUTOSAR)"]
CANMATRIX <-->|".xlsx"| EXCEL["Excel"]
CANMATRIX <-->|".kcd"| KCD["KCD
(Kayak)"]
style DBC fill:#FFC107,stroke:#333,stroke-width:3px
style CANDB fill:#1565C0,stroke:#333,color:#fff
style CANTOOLS fill:#388E3C,stroke:#333,color:#fff
style CANMATRIX fill:#388E3C,stroke:#333,color:#fff
style SAVVY fill:#388E3C,stroke:#333,color:#fff
Figure: DBC02 02 tooling ecosystem
4.1 CANdb++ (Vector)
CANdb++ is the original DBC editor from Vector Informatik. It is the reference implementation for the DBC format.
Strengths: - Definitive DBC format support (it defines the format) - Visual signal layout editor with bit-level grid - Integrated with Vector’s CANoe/CANalyzer toolchain - Full support for extended multiplexing, CAN FD, and all DBC features - Network-wide consistency checking
Limitations: - Windows only - Requires a Vector license (free basic version available as part of CANdb++ standalone) - Proprietary — no Command-Line Interface (CLI) or scripting API (automation requires Communication Access Programming Language (CAPL) scripts within CANoe)
When to use: You are working in an automotive Original Equipment Manufacturer (OEM) or Tier-1 supplier environment that uses Vector tools.
4.2 cantools (Python)
cantools is an open-source Python library for working with CAN databases. It supports DBC, ARXML (AUTOSAR), KCD, and CDD formats.
# Requires: pip install cantools>=39.0
# CAN FD features (DLC > 8, SIG_VALTYPE_ float/double) require cantools 39.x+.
pip install cantools
Strengths:
- Open-source (MIT license)
- Pythonic API for loading, encoding, decoding, and modifying DBC files
- CAN FD support (DLC > 8, SIG_VALTYPE_)
- Extended multiplexing support
- CLI tool: cantools decode, cantools dump, cantools generate
- Active development and community
Limitations: - No visual editor — text/code only - Some edge cases in Motorola byte order handling (improved in recent versions) - Does not support all CANdb++ proprietary extensions
# Decode a CAN log file using a DBC
cantools decode vehicle.dbc candump.log
# Generate C code from a DBC
cantools generate_c_source vehicle.dbc
# Dump DBC file structure
cantools dump vehicle.dbc
When to use: Python-based workflows, automated testing, CI/CD pipelines, open-source projects.
4.3 canmatrix
canmatrix is an open-source Python library focused on converting between CAN database formats.
pip install canmatrix # Core library
pip install canmatrix[all] # All format support (ARXML, KCD, SYM, Excel, etc.)
Supported formats:
| Format | Extension | Read | Write |
|---|---|---|---|
| DBC (Vector) | .dbc | Yes | Yes |
| ARXML (AUTOSAR) | .arxml | Yes | Yes |
| KCD (Kayak) | .kcd | Yes | Yes |
| SYM (Symbol Editor) | .sym | Yes | Yes |
| Excel | .xlsx | Yes | Yes |
| Comma-Separated Values (CSV) | .csv | Yes | Yes |
| JavaScript Object Notation (JSON) | .json | Yes | Yes |
| Field Bus Exchange Format (FIBEX) | .xml | Yes | No |
Strengths: - Format conversion is its primary purpose — excellent for migrating between tool ecosystems - Excel import/export is invaluable for maintaining databases in spreadsheets - Handles large databases efficiently
When to use: Converting between DBC and ARXML, maintaining databases in Excel, migrating between tool vendors.
💡 cantools vs canmatrix — when to use which: cantools is optimized for DBC encode/decode operations — loading a DBC, decoding CAN frames to signals, and encoding signals to frames. canmatrix is a format-conversion and database-manipulation library — it excels at converting between DBC, ARXML, KCD, SYM, and Excel formats, merging databases, and comparing signal definitions across files. If your workflow is “load DBC, decode CAN data,” use cantools. If your workflow is “convert DBC to ARXML” or “merge two DBC files,” use canmatrix.
canmatrix — Format Conversion and Database Manipulation
# pip install canmatrix[all] # Install with all format support
import canmatrix
# 1. Format conversion: DBC → ARXML
canmatrix.formats.convert('vehicle.dbc', 'vehicle.arxml')
print("Converted DBC to ARXML")
# 2. Format conversion: DBC → Excel (for review by non-engineers)
canmatrix.formats.convert('vehicle.dbc', 'vehicle.xlsx')
print("Converted DBC to Excel spreadsheet")
# 3. Load and inspect a database
db_dict = canmatrix.formats.loadp('vehicle.dbc')
db = db_dict[''] # Default database (DBC files have one database)
print(f"\nDatabase contains {len(db.frames)} messages:")
for frame in db.frames:
print(f" 0x{frame.arbitration_id.id:03X} {frame.name}: "
f"{len(frame.signals)} signals, DLC={frame.size}")
# 4. Compare two DBC files (e.g., after vendor update)
db_old = canmatrix.formats.loadp('vehicle_v1.dbc')['']
db_new = canmatrix.formats.loadp('vehicle_v2.dbc')['']
old_signals = {(f.name, s.name) for f in db_old.frames for s in f.signals}
new_signals = {(f.name, s.name) for f in db_new.frames for s in f.signals}
added = new_signals - old_signals
removed = old_signals - new_signals
print(f"\nSignals added: {len(added)}")
print(f"Signals removed: {len(removed)}")
for msg_name, sig_name in sorted(added):
print(f" + {msg_name}.{sig_name}")
for msg_name, sig_name in sorted(removed):
print(f" - {msg_name}.{sig_name}")
4.4 SavvyCAN
SavvyCAN is an open-source CAN bus reverse engineering and analysis tool with a graphical interface.
Strengths: - Graphical User Interface (GUI) for CAN bus analysis and reverse engineering - DBC file loading, editing, and live decoding - Signal plotting and analysis - Supports multiple CAN interfaces (socketCAN, PCAN, Kvaser) (for socketCAN setup and integration, see CAN-04) - Cross-platform (Windows, Linux, macOS)
Limitations: - Not a full DBC editor (better for analysis than authoring) - CAN FD support is improving but not yet complete - Less stable than commercial tools for very large databases
When to use: Reverse engineering unknown CAN protocols, visual analysis of CAN traffic, hobbyist and research projects.
4.5 Tool Comparison Matrix
| Feature | CANdb++ | cantools | canmatrix | SavvyCAN |
|---|---|---|---|---|
| DBC read/write | Full | Full | Full | Read + basic write |
| Visual editor | Yes | No | No | Basic |
| CAN FD | Yes | Yes | Yes | Partial |
| Extended MUX | Yes | Yes | Yes | No |
| Format conversion | DBC only | DBC, ARXML, KCD | 10+ formats | DBC only |
| Platform | Windows | Any (Python) | Any (Python) | Cross-platform |
| License | Commercial | MIT | BSD | GPL |
| CLI / scripting | No | Yes | Yes | No |
| Price | $0–$$$ | Free | Free | Free |
5. Common DBC Pitfalls
5.1 The Top Five DBC Mistakes
-
Motorola start bit confusion — The DBC Motorola start bit is the Most Significant Bit (MSB) position, not the Least Significant Bit (LSB). This is the single most common source of incorrect signal decoding. Always verify with the byte-grid layout. A Motorola/Intel byte-order mismatch typically manifests as decoded values that change erratically or seem “backwards” — e.g., a steadily increasing sensor reading appears to jump between high and low values because the MSB and LSB bytes are swapped.
-
Missing leading space on SG_ lines — The
SG_keyword must be preceded by exactly one space character. Omitting it causes the signal to be parsed as a separate entity or ignored entirely. -
Extended ID without the flag — For 29-bit CAN IDs, you must add 0x80000000 (2,147,483,648) to the CAN ID in the
BO_line. Forgetting this flag means the message will be treated as an 11-bit standard frame. -
DLC mismatch — The DLC in the
BO_line must match the actual number of bytes transmitted. If your signals extend beyond the declared DLC, tools may silently truncate or decode garbage. -
Value description semicolon — Every
VAL_line must end with a semicolon preceded by a space (;). A missing semicolon causes the parser to attempt to read the next line as part of the value list.
5.2 DBC Validation Checklist
- All signals fit within the declared DLC (no signal extends beyond byte `DLC - 1`)
- No two signals in the same message overlap in bit position (unless multiplexed)
- All transmitter and receiver node names match `BU_` entries
- Extended (29-bit) CAN IDs include the 0x80000000 flag
- All `VAL_` lines end with ` ;`
- All `SG_` lines begin with a single leading space
- Factor is not zero (division by zero during encoding)
- Min/Max range is consistent with signal length and encoding (e.g., 8-bit unsigned with factor 1 and offset 0 should have max 255)
💡 This checklist covers the issues that account for the vast majority of DBC integration failures. Running through it before releasing a DBC file to another team or tool can save hours of debugging.
Troubleshooting
| # | Symptom | Likely cause | Diagnostic step | Resolution |
|---|---|---|---|---|
| 1 | Multiplexed signals not decoded | Multiplexer signal (M) not defined, or m<N> suffix missing |
Check that exactly one signal has the M suffix and multiplexed signals have m0, m1, etc. |
Add the M and m<N> markers to the correct signals |
| 2 | cantools raises “overlapping signals” error | Two signals occupy the same bit positions without multiplexing | Visualize the message layout with cantools dump or a bit-grid tool |
Move one signal to a different bit position or add multiplexing |
| 3 | CAN FD message has DLC 8 but 64 bytes of data | DLC in the BO_ line is still set to 8 |
Check the DLC value in the BO_ line |
Change DLC to match actual payload size (12, 16, 20, 24, 32, 48, or 64) |
| 4 | Tool cannot parse SG_MUL_VAL_ section |
Tool does not support extended multiplexing | Check tool documentation for extended MUX support | Use standard M/m syntax, or upgrade to a tool that supports SG_MUL_VAL_ |
| 5 | Signal decoded as negative when it should be positive | Value type is - (signed) when it should be + (unsigned) |
Check the +/- flag after @0 or @1 |
Change to + for unsigned values |
| 6 | DBC file works in one tool but fails in another | Tool-specific extensions or syntax variations | Compare the DBC against the grammar in DBC-01; remove non-standard extensions | Use only standard DBC syntax, or maintain separate DBC files per tool if necessary |
| 7 | Excel-to-DBC conversion produces garbled signals | canmatrix conversion lost byte order or start bit information | Compare a few signals before and after conversion | Verify byte order column in Excel matches DBC conventions; explicitly set byte order in canmatrix options |
| 8 | cantools raises ZeroDivisionError during encode_message |
Signal factor is set to 0 in the SG_ line |
Search for the pattern (0, in SG_ lines — i.e., grep "SG_.*(\s*0," vehicle.dbc — to find signals with a factor of zero. A factor of zero makes a signal undecodable — the raw value multiplied by zero always produces zero. Be precise: the pattern (0, targets the factor field inside the parenthesized (factor,offset) pair and avoids false positives from other numeric fields in the SG_ line. |
Set factor to the correct non-zero scaling value |
| 9 | CAN FD signals decode as zero or garbage on classic CAN interface | DBC contains CAN FD messages (DLC > 8) but the interface is classic CAN only | Check the BusType attribute and verify the CAN interface supports FD |
Use a CAN FD-capable interface, or create a separate classic CAN DBC with only 8-byte messages |
References
-
Vector Informatik — CANdb++ User Manual. Defines the DBC file format and all supported features.
-
cantools — Python CAN bus tools. Repository: github.com/cantools/cantools. DBC parser, codec, and code generator.
-
canmatrix — CAN database conversion library. Repository: github.com/ebroecker/canmatrix. Multi-format conversion.
-
SavvyCAN — Open-source CAN analysis tool. Repository: github.com/collin80/SavvyCAN.
-
CSS Electronics — DBC File Multiplexing Explained. Community resource for DBC multiplexing syntax.
-
AUTOSAR — Specification of Communication (COM). Defines ARXML format for CAN databases, which can be converted to/from DBC.
-
International Organization for Standardization (ISO) 11898-1:2015 — Road vehicles — Controller area network (CAN) — Part 1: Data link layer and physical signalling. Defines CAN FD frame format including DLC-to-byte mapping.
Changelog
| Version | Date | Author | Summary of changes |
|---|---|---|---|
| 1.0 | 2026-03-16 | Telematics Tutorial Series | Initial publication |
```