This page describes how JMRI encodes and decodes Märklin CAN (MCAN) frames. Please also read the Javadocs for the jmrix.marklin package.
Frame encoding and decoding live in the jmri.jmrix.marklin.mcan package
tree. These classes do not depend on the JMRI application layer. The adapter, the
monitor and standalone tools such as the MCAN TCP Bridge share the same frame handling.
This independence is enforced by a unit test.
ProtocolClassifier.classify() turns every 13-byte frame into exactly one
typed frame. All of them extend the common base class McanFrame, which
gives access to the raw bytes and the protocol family:
Cs2Frame or one of its typed subclasses. A CS2 frame with an
unrecognised command is a plain Cs2Frame. Builders for outgoing frames
are grouped by command family, for example Cs2DecoderCommands,
Cs2AccessoryCommands and Cs2ConfigCommands.CdBFrameParser.CdBFrame or one of its typed subclasses. Builders are in
CdBConfigCommands and the device-specific CdB...Commands
classes.UnknownFrame. It keeps the
raw bytes but does not interpret any header fields.Callers test the result with instanceof or switch on
protocol(). Malformed input (null or not 13 bytes) is rejected with an
exception rather than reported as an unknown frame.
Inside JMRI, MarklinReply.getFrame() and
MarklinMessage.getFrame() return this classification, cached until the
bytes change. The traffic controller hands each CdB configuration frame once to the
registered CdB configuration listeners and then delivers every frame, including CdB
frames, to all ordinary Märklin listeners such as the traffic monitor.
Each subpackage of jmri.jmrix.marklin.mcan has one responsibility:
| Package | Intent |
|---|---|
mcan |
Types shared by all protocol families: the frame base class
McanFrame, UnknownFrame, RawFrame and
FrameProtocol. McanUid is the 32-bit
UID of a device on the bus (CS2, MS2, Gleisbox, ...). It provides unsigned
ordering, the CS2 hash calculation and the broadcast and uninitialized values.
It is not used for mfx decoder UIDs or Loc-IDs. |
mcan.classify |
Entry point for received frames. ProtocolClassifier recognises the
protocol family and calls the matching parser. It depends on mcan.cs2
and mcan.cdb Neither of those depends on it. |
mcan.cs2 |
Märklin CS2 native protocol: Header, data and command types,
Cs2Frame and its typed subclasses, frame builders and parsers grouped
by command family and NativeDeviceInfo (PING reply contents). |
mcan.cdb |
CAN-digital-Bahn configuration protocol: CdBFrameType, typed frames,
frame builders and parsers, module-specific command sets (WeichenChef, LampenChef, Reporter) and the
CdBDeviceClass catalogue of known module classes. |
mcan.address |
Address arithmetic. AddressSpace handles native MS2 and throttle protocol
address ranges. CdBAddressCodec and AddressEncoding
convert DCC/MM/CAN user addresses to and from CdB register values. The
AddressCodec interface has only one implementation and no callers
yet. It may be removed. |
Device UIDs in the public API are McanUid values. For example, the
uid() getters on CS2 configuration frames and the
targetUid/senderUid parameters of the
Cs2SystemCommands builders use this type.
MCAN frames use a 13-byte format per the CS2 CAN Protocol 2.0 specification:
| Bytes | Field | Description |
|---|---|---|
| 0 | Priority + Command[14:7] | Priority (bits 7-4), Command high bits (bits 3-0) |
| 1 | Command[6:0] + Response | Command low bits (bits 7-1), Response flag (bit 0) |
| 2-3 | Hash | 16-bit hash/identifier |
| 4 | DLC | Data Length Code (0-8 bytes in the CAN payload) |
| 5-12 | Data | Up to 8 payload bytes; their meaning depends on the command |
| Range | Category | Example Commands |
|---|---|---|
| 0x00 | System | Stop (0x00), Go (0x01), Halt (0x02) |
| 0x01-0x0A | Management | Speed (0x04), Direction (0x05), Function (0x06) |
| 0x0B-0x0D | Accessory | Turnout control |
| 0x18-0x1C | Software | PING (0x18), CAN BOOT (0x1B) |
| 0x20-0x22 | GUI | GUI commands |
| 0x10-0x12 | Feedback | S88 events (0x11) |
| 0x30-0xFF | Automation | Automation commands |
Formats raw MCAN frames for display and logging. Used by both JMRI's
MarklinMon monitor pane and the standalone MCAN TCP Bridge monitor mode.
// Format a raw frame with device identification
String text = MarklinMessageFormatter.formatRaw(rawData, null, true);
// Format directly from raw bytes (standalone, no i18n)
String text = MarklinMessageFormatter.formatRaw(rawData, null);
Output example:
[MS2:0x00112233] Priority 4: Engine/Accessory Command Command: Change of speed 512 Request to Loco Address 3 0x5, 0x9, 0x47, 0x11, 0x2, 0x0, 0x0, 0x0, 0x3, 0x2, 0x0, 0x0, 0x0
An optional I18nProvider interface allows JMRI to supply localized strings while
the same class works without JMRI runtime in the bridge.
Holds the UID, device type, software version and hash reported in a CS2 native PING reply (command 0x18). Known device type codes (from CS2 CAN Protocol 2.0):
| Type Code | Device |
|---|---|
| 0x0000 | GFP (Gleis Format Processor) 60213/60214 |
| 0x0010 | Gleisbox 60112/60113 |
| 0x0020 | Connect 6021 (60128) |
| 0x0030 | MS2 (60653) |
| 0x0050 | CS3 |
| 0x1300 | CC-Schnitte CAN/USB interface (3.x series onwards) |
| 0xFFE0 | Wireless devices |
| 0xFFFF | CS2-GUI (Master) |
McanDevice is a JMRI NamedBean representing one device on the
MCAN bus. It tracks the device identity, software version, hardware version, first-seen
and last-seen timestamps and a lifecycle state.
Lifecycle states (McanDeviceState):
| State | Meaning |
|---|---|
| DISCOVERED | Ping reply received; identity established |
| INITIALIZING | Follow-up parameter reads in progress |
| ONLINE | Fully initialized and actively responding |
| STALE | No response within configured threshold |
| OFFLINE | Explicitly disconnected or lost |
Property change events are dispatched on the Swing EDT for thread safety.
Immutable identity for an MCAN device. Has two concrete subtypes:
McanDeviceIdentity.Native - identified by 32-bit UID and 16-bit device type
(e.g. CS2, MS2, Gleisbox). System name fragment: uid-00112233.McanDeviceIdentity.CdB - identified by CAN class and module ID
(CAN-digital-bahn modules). System name fragment: cdb-22-03.
McanDeviceIdentity.Native nat = McanDeviceIdentity.forNative(McanUid.of(0x00112233L), 0x0030);
// nat.getSystemName("M") == "M:dev:uid-00112233"
McanDeviceIdentity.CdB cdb = McanDeviceIdentity.forCdB(0x22, 3);
// cdb.getSystemName("M") == "M:dev:cdb-22-03"
McanDeviceManager is a JMRI AbstractManager that discovers and
tracks all MCAN devices seen on the bus. It listens to both native MCAN messages (via
MarklinListener) and CdB configuration frames (via CdBConfigListener).
// Discover native Marklin devices (CS2, MS2, Gleisbox, CC-Schnitte)
manager.discoverMarklinDevices();
// Discover all known CdB device classes
manager.discoverAllCdBDevices();
// Discover one specific CdB class
manager.discoverCdBDevice(CdBDeviceClass.WEICHEN_CHEF_MAGNET);
Here CdBDeviceClass is
jmri.jmrix.marklin.mcan.cdb.CdBDeviceClass.
jmri.jmrix.marklin.mcan.cdb.CdBDeviceClass lists the known CdB device
classes and the protocol generation used for discovery:
| Class ID | Device | Protocol |
|---|---|---|
| 0x03 | GleisReporter Jumbo | NEW |
| 0x0B | StromSniffer | NEW |
| 0x0D | GleisReporter | NEW |
| 0x21 | ReporterChef 002 | NEW |
| 0x22 | WeichenChef Magnet M / deLuxe | NEW |
| 0x23 | WeichenChef AC | NEW |
| 0x26 | WeichenChef Basic | NEW |
| 0x27 | WeichenChef Kombi | NEW |
| 0x2A | WeichenChef Motor | NEW |
| 0x4A | LampenChef | OLD |