MRBus Spec Table of Contents
  1. Section 1 - Introduction
  2. Section 2 - Hardware Layer
  3. Section 3 - Software Layer
  4. Section 4 - Component-Level Integration
Section 4 - Component-Level Integration - Basics

As would be expected, individual components spewing data are not, by themselves, very useful.

MRBus is designed to be a network of nodes transmitting and listening for stateless datagrams. While nothing precludes stateful connections, it won't be discussed as part of the baseline protocol. What does this mean?

A module that has inputs, for example, would transmit its input state every few seconds or upon the inputs changing. It would not listen for anything but basic packets, such as PING, VERSION, and possibly READ?/WRITE?, as it doesn't need the state of anything else to do its job of converting inputs to packet data.

A output node, however, needs to listen for whatever things make its output change. It should not query nodes asking for status, but rather initialize in a known state and stay there until it receives a datagram that it's configured to recognize as a source for its output data. It may also want to transmit a status every few seconds, showing the condition of its outputs.

Section 4.1 - Component-Level Integration - Model Railroad Example

Since MRBus is initially designed to replace signal systems with literally dozens of individual logic lines connecting nodes, a sensible starting point is the ability to build "virtual logic connections" between nodes.

For example, take the following ABS signaling setup: (insert picture here)

Nodes 0x71-0x73 clearly need logical inputs from block detectors 0x11 and 0x12 in order to determine the correct aspects on the signals. For simple R-Y-G signaling, at least two inputs are needed for each signal immediate block occupancy (the block the signal is guarding), and adjacent block occupancy (the block beyond the immediate block). We'll call these inputs IMD and ADJ for brevity. So, for the westbound signal (right-facing) on 0x73, it needs the occupancy of Block N for its IMD input, and Block O for its ADJ input.

Block detector 0x11 is transmitting Data ('D') packets that are as follows:
[0x11][0xFF][CRC (16b)][0x07][0x44][b0000(-)(-)(P)(O)]

Block detector 0x12 is transmitting Data ('D') packets that are as follows:
[0x12][0xFF][CRC (16b)][0x07][0x44][b0000(-)(M)(N)(-)]

In both cases, the last byte is a bitmap of occupancy statuses, where (*) represents the occupancy of block *, or (-) means unknown or not relevant.

A fundamental recommended practice with MRBus is to make all possibly needed outputs a bit. That way virtual logic connections are possible by just selecting bits out of incoming packets. This input selection process, known as "building virtual logic connections", should allow the user to specify by source address, packet type, and bit/byte position the desired input. For complex logic, a combination of inputs may be used, but as in most cases simplicity is to be valued.

Source and packet type are fairly straightforward if you want the input from a 'D' packet sent by a detector at 0x12, your source is 0x12 and your type is 0x44 ('D' in ASCII). Bit/byte are more complex, as they can be bitpacked into a single byte to save configuration memory space.

It's recommended they be assembled as follows:

Bit/byte = XXXYYYYY
XXX = Bit number, 0-7
YYYYY = Byte number, from start of packet. Source address is byte 0

So, signal node 0x73 has, in its configuration values (CV), positions for source address, packet type, and byte/bit to use as the IMD and ADJ virtual logic connections. Let's say these are CV10, CV20, and CV30 for IMD and CV11, CV21, and CV31 for ADJ.

So, to configure the Westbound IMD input to read block N:

CV10 (IMD Source Addr)= 0x12 (Block N's detector address)
CV20 (IMD Packet Type)= 0x44 ('D')
CV30 (IMD Byte/Bit) = 0x26 (Bit 1 (XXX=b001) and byte 6 (YYYYY=b00110)

Likewise, the Westbound ADJ input for Block O would be configured as:

CV11 (ADJ Source Addr)= 0x11 (Block O's detector address)
CV21 (ADJ Packet Type)= 0x44 ('D')
CV31 (ADJ Byte/Bit) = 0x06 (Bit 0 (XXX=b000) and byte 6 (YYYYY=b00110)

The virtual logic connections for the eastbound signal would have similar configurations, except being set up to read Block M and the unnamed (and not shown) block to the right of that.

So, the example conversation on the bus would look like:

[0x11][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:00 Regular update on 0x11
[0x12][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:00 Regular update on 0x12
[0x72][0xFF][CRC16][0x07][0x53][0x11] -- 00:00:00 Two greens on 0x72
[0x71][0xFF][CRC16][0x07][0x53][0x11] -- 00:00:00 Two greens on 0x71
[0x73][0xFF][CRC16][0x07][0x53][0x11] -- 00:00:00 Two greens on 0x73
---- Next Second ----
[0x12][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:01 Regular update on 0x12
[0x11][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:01 Regular update on 0x11
[0x12][0xFF][CRC16][0x07][0x44][b00000100] -- 00:00:01 Train enters Block M
[0x73][0xFF][CRC16][0x07][0x53][0x41] -- 00:00:01 Eastbound drops to red
[0x72][0xFF][CRC16][0x07][0x53][0x21] -- 00:00:01 Eastbound drops to yellow
---- Next Second ----
[0x12][0xFF][CRC16][0x07][0x44][b00000100] -- 00:00:02 Regular update on 0x12
[0x11][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:02 Regular update on 0x11
[0x71][0xFF][CRC16][0x07][0x53][0x11] -- 00:00:02 Two greens on 0x71
[0x73][0xFF][CRC16][0x07][0x53][0x41] -- 00:00:02 E=Red, W=Grn on 0x73
[0x72][0xFF][CRC16][0x07][0x53][0x21] -- 00:00:02 E=Ylw, W=Grn on 0x72
... Time elapses ...
[0x12][0xFF][CRC16][0x07][0x44][b00000110] -- 00:00:33 Train enters Block N
[0x71][0xFF][CRC16][0x07][0x53][0x21] -- 00:00:33 E=Ylw, W=Grn on 0x71
[0x72][0xFF][CRC16][0x07][0x53][0x41] -- 00:00:33 E=Red, W=Grn on 0x72
[0x73][0xFF][CRC16][0x07][0x53][0x41] -- 00:00:33 E=Red, W=Grn on 0x73
[0x12][0xFF][CRC16][0x07][0x44][b00000100] -- 00:00:33 Regular update on 0x12
[0x11][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:33 Regular update on 0x11
---- Next Second ----
[0x12][0xFF][CRC16][0x07][0x44][b00000110] -- 00:00:34 Regular update on 0x12
[0x11][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:34 Regular update on 0x11
---- Next Second ----
[0x11][0xFF][CRC16][0x07][0x44][b00000000] -- 00:00:35 Regular update on 0x11
[0x12][0xFF][CRC16][0x07][0x44][b00000010] -- 00:00:35 Block M clears
[0x71][0xFF][CRC16][0x07][0x53][0x21] -- 00:00:35 E=Ylw, W=Grn on 0x71
[0x73][0xFF][CRC16][0x07][0x53][0x11] -- 00:00:35 E=Grn, W=Grn on 0x73
[0x72][0xFF][CRC16][0x07][0x53][0x41] -- 00:00:35 E=Red, W=Grn on 0x72

...and so it goes. This bus example should give you some idea of how devices communicate on a simple, basic level using the example ABS system pictured above. In addition, it should be noted that CV0-15 are reserved for standardized uses. At the moment, only CV0 is used, however.

CV0 = Device Address (should initialize to 0x03)
CV1 = Undefined
CV2 = Undefined
CV3 = Undefined
CV4 = Undefined
CV5 = Undefined
CV6 = Undefined
CV7 = Undefined
CV8 = Undefined
CV9 = Undefined

A special set of commands, packet types 'R' and 'W', have been reserved to read and write the CV registers. The format of these packets should be as follows, and universal across devices:

  • SRC = Source of packet device doing the programming, usually a computer interface
  • DEST = Device being programmed, 0x00 if the device has not been set to its own address yet, since all programmed devices will ignore a destination of 0x00 (*nobody*).
  • LEN = Length of packet 7 for 'R' commands, 8 for 'W' commands
  • CRC16 = Packet checksum, computed when sent
  • CV_ADDR = Address of the configuration value to be manipulated (0 -???)
  • VAL = For writing CVs, the value to be written.
Copyright 2012 by the MRBus Group.
Licensed under a Creative Commons Attribution-ShareAlike 3.0 License.
Questions? Comments? Please email us at

Last modified on February 19, 2012, at 01:36 PM