GitHub - ZIMO-Elektronik/MDU: MDU protocol for ZPP and ZSU updates (original) (raw)
MDU
MDU is an acronym for Multi Decoder Update, a protocol for ZPP and ZSU updates over the track. The protocol is currently supported by the following products:
- Command stations
- Decoders
- ZIMO MN decoders
- ZIMO small- and large-scale MS decoders Table of Contents
Protocol
Entry
Activation of the MDU protocol is accomplished through a sequence of commands to verify configuration variables (CVs) in DCC operations mode. The entire sequence must be broadcast and thus sent to broadcast address 0. Details on the command structure can be found in RCN-214, especially point 2 ("Configuration variable access command - long form"). Depending on the type of update desired, ZPP or ZSU, the following sequences are to be sent:
ZPP | ZSU |
---|---|
CV8 == 0xFE | CV8 == 0xFF |
CV105 == 0xAA | CV105 == ID |
CV106 == 0x55 | CV106 == ID |
CV105 == 0x55 | CV105 == ID |
CV106 == 0xAA | CV106 == ID |
CV105 == SN | CV105 == SN |
CV106 == SN | CV106 == SN |
CV105 == SN | CV105 == SN |
CV106 == SN | CV106 == SN |
By specifying a serial number(SN), it is possible to activate only a very specific decoder. Setting the SN bytes to zero will activate the desired state in all connected decoders(ZPP), or all connected decoders with a certain ID(ZSU). Also, setting the ID and SN bytes zero will active all connected decoders(ZSU).
Alternative Entry
As an alternative to entry via DCC CV verify commands, MDU commands with default bit timings can be sent directly after switching on the track voltage. It is recommended to send out the shortest command, Busy, for at least 200ms.
Transmission
Bit transmission takes place MSB-first similar to the DCC protocol described in RCN-210 through zero crossings (change of polarity) of the track signal. In contrast to DCC, the transmission of a bit does not require two, but only one zero crossing. The decision as to whether a received bit represents a zero bit, one bit or acknowledgment bit is determined by the time interval between the zero crossings. This time interval has a default value at the beginning, but can be varied using a separate command (see Config-Transfer-Rate command). In addition, there are so-called fallback timings that must be able to be received at any time.
At the end of a data packet, so-called acknowledgment bits are sent by the command station. Selected decoders (see Ping command) can respond within this time by means of current pulses (ack bits). This is comparable to the programming mode of the DCC protocol (service mode) described in RCN-216. The feedback phase is divided into two channels.
Channel 1 (ackreq bits 2-4) is intended for transmission and checksum errors (CRC). Decoders that have not completely received a packet or whose CRC check failed can declare a packet invalid by sending ack bits. command stations must then repeat the last packet sent.
Channel 2 (ackreq bits 6-8) is for data acknowledgment. The meaning depends on the last command transmitted. For an overview, refer to the table in the acknowledgment chapter. The detailed description of individual commands that follows later also goes into more detail about the meaning.
Command stations must send at least 10 acknowledgment bits. Decoders that want to give feedback in a channel must answer at least 2 of 3 ackreq bits within this channel with an ack bit. Even a single received ack bit is to be evaluated by the command station as a response. In order not to overload command stations with sensitive overcurrent shutdown, the ack bits can also be transmitted as PWM instead of continuous current pulses. For ROCO's Z21, for example, 90% duty cycle with a period of 10µs turned out to be ideal.
Bit Timings
At the beginning of a transfer, all devices start with the default setting. The command station can now gradually increase the transmission speed. If one of the decoders responds with an ack bit to signal that the desired speed is not supported, the station must transmit a Config-Transfer-Rate command to revise the setting with fallback timings. This is the only way to ensure that the settings on the decoders do not diverge. The fallback timings (speed 0) are therefore always active and must always be able to be received regardless of the selected speed.
Speed | One Bit [µs] | Zero Bit [µs] | Ackreq Bit [µs] | Ack Bit [µs] | Decoder Tolerance [%] |
---|---|---|---|---|---|
0 (fallback) | 1200 | 2400 | 3600 | 100 | 10 |
1 | 10 | 20 | 60 | 40 | 30 |
2 | 20 | 40 | 60 | 40 | 20 |
3 | 40 | 80 | 120 | 80 | 20 |
4 (default) | 75 | 150 | 225 | 100 | 10 |
Structure of a Data Packet
The following flowchart describes the general structure of a MDU data packet.
In principle, each command packet contains the phases preamble, data and acknowledgement. The meaning of the transmitted data and the acknowledgment depends on the command package itself and will be itemized later for each command.
Command Phase | Description |
---|---|
Preamble | Identification and synchronization of a MDU packet |
Data (coding) | 4-byte identification of the command |
Data (optional) | Optional transmission of data depending on the command package |
Data (CRC) | 1- or 4-byte checksum of the packet |
Acknowledgement (optional) | Optional acknowledgement depending on command package |
Commands
The supported commands of the MDU protocol are divided into 3 categories: general, ZPP and ZSU. Devices that only want to support either ZPP or ZSU updates only have to support the command set actually used. However, the general command set must be implemented.
General Commands | Coding | ZPP Commands | Coding | ZSU Commands | Coding |
---|---|---|---|---|---|
ZPP-Valid-Query | 0xFFFF'FF06 | ||||
Ping | 0xFFFF'FFFF | ZPP-LC-DC-Query | 0xFFFF'FF07 | ZSU-Salsa20-IV | 0xFFFF'FFF7 |
Config-Transfer-Rate | 0xFFFF'FFFE | ZPP-Erase | 0xFFFF'FF05 | ZSU-Erase | 0xFFFF'FFF5 |
Binary-Tree-Search | 0xFFFF'FFFA | ZPP-Update | 0xFFFF'FF08 | ZSU-Update | 0xFFFF'FFF8 |
CV-Read | 0xFFFF'FFF6 | ZPP-Update-End | 0xFFFF'FF0B | ZSU-CRC32-Start | 0xFFFF'FFFB |
CV-Write | 0xFFFF'FFF9 | ZPP-Exit | 0xFFFF'FF0C | ZSU-CRC32-Result | 0xFFFF'FFFC |
Busy | 0xFFFF'FFF2 | ZPP-Exit&Reset | 0xFFFF'FF0D | ZSU-CRC32-Result&Exit | 0xFFFF'FFFD |
Acknowledgment
Ackreq Bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
Ping | Reference | Incomplete package | CRC8 error | buffer full | Ping successful | ||||||
Config-Transfer-Rate | Reference | Incomplete package | CRC8 error | buffer full | Bit timings are not supported | ||||||
Binary-Tree-Search | Reference | Incomplete package | CRC8 error | buffer full | See description | ||||||
CV-Read | Reference | Incomplete package | CRC8 error | buffer full | Bit in CV is set | ||||||
CV-Write | Reference | Incomplete package | CRC8 error | buffer full | Write not possible | ||||||
Busy | Reference | Incomplete package | CRC8 error | Decoder busy | |||||||
ZPP-Valid-Query | Reference | Incomplete package | CRC8 error | buffer full | ZPP invalid | ||||||
ZPP-LC-DC-Query | Reference | Incomplete package | CRC8 error | buffer full | Load code wrong | ||||||
ZPP-Erase | Reference | Incomplete package | CRC8 error | buffer full | Invalid memory area | ||||||
ZPP-Update | Reference | Incomplete package | CRC32 error | buffer full | Invalid address | CRC32 error | ||||||
ZPP-Update-End | Reference | Incomplete package | CRC8 error | buffer full | Invalid memory area | ||||||
ZPP-Exit | Reference | Incomplete package | CRC8 error | buffer full | - | ||||||
ZPP-Exit&Reset | Reference | Incomplete package | CRC8 error | buffer full | - | ||||||
ZSU-Salsa20-IV | Reference | Incomplete package | CRC8 error | buffer full | CRC8 error | ||||||
ZSU-Erase | Reference | Incomplete package | CRC8 error | buffer full | Invalid memory area | ||||||
ZSU-Update | Reference | Incomplete package | CRC32 error | buffer full | Invalid address | CRC32 error | ||||||
ZSU-CRC32-Start | Reference | Incomplete package | CRC8 error | buffer full | Invalid memory area | ||||||
ZSU-CRC32-Result | Reference | Incomplete package | CRC8 error | buffer full | Received CRC32 not equal own | ||||||
ZSU-CRC32-Result&Exit | Reference | Incomplete package | CRC8 error | buffer full | Received CRC32 not equal own |
General Commands
The general command set contains commands for searching and selecting decoders, setting the bit timings, writing and reading configuration variables, and a busy query.
Ping
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFFF |
Data | 4-byte serial number |
Data | 4-byte decoder ID |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Ping successful |
A ping command allows individual decoders or decoder types to be selected. Only selected decoders may execute commands and send acknowledgements to the command station. In the initial state after a reset, all decoders are selected. The selection is made by transmitting a 4-byte serial number and a 4-byte decoder ID. A decoder is considered to be selected if all received numbers that are not 0 match those of the decoder. If only 0 is transmitted, all decoders are selected. This results in the following variants:
- If a serial number and a decoder ID are transmitted, a single decoder with the corresponding serial number and ID is selected.
- If a serial number and decoder ID 0 is transmitted, all decoders with the corresponding serial number are selected.
- If serial number 0 and a decoder ID is transmitted, all decoders with the corresponding ID are selected.
- If serial number 0 and decoder ID 0 is transmitted, all decoders are selected.
Config-Transfer-Rate
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFFE |
Data | 1-byte index into bit timings |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Bit timings are not supported |
With the help of a Config-Transfer-Rate command, the transmission speed can be adapted to the decoder by setting the bit timings. The exact times for one bit, zero bit, ackreq bit and ack bit can be found in the bit timings. If a decoder does not support the selected transmission speed, an acknowledgement must be sent in channel 2.
Binary-Tree-Search
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFFA |
Data | 1-byte see description |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | See description |
The Binary-Tree-Search command is used to search for decoders that support MDU. The following combination of serial number and decoder ID is used for clear identification:
uint64_t unique_id = (decoder_id << 32u) | serial_number;
With the exception of the MSB (always 0), that number can be queried bit by bit. Again, reference is made to RCN-214, which provides a similar command for the programming mode of the DCC protocol to read CVs bit by bit.
Since, in contrast to DCC, several decoders can send an acknowledgment at the same time, further commands are required in addition to querying a bit, which are represented with the help of special values or closed intervals.
- 255
Special value that initiates the start or restart of the search. All decoders that have received this packet reply with an acknowledgment. All decoders that have not received this packet are excluded from further search history and also send no acknowledgment. - [0...62]
The data byte received corresponds to the bit number of the bit to be checked. All decoders with this bit set respond. - [64...64+62]
The received data byte-64 corresponds to the bit number of the inverted bit to be checked. All decoders with this bit cleared respond. - [128...128+62]
The received data byte-128 corresponds to the bit number of the bit to be checked. All decoders with this bit set end the search. Only special value 255 can restart the search at this point. - [192...192+62]
The received data byte-192 corresponds to the bit number of the inverted bit to be checked. All decoders with this bit cleared end the search. Only special value 255 can restart the search at this point.
The following flowchart shows the search process from the perspective of the decoder. See Maxim Integrated's application note 1-Wire Search Algorithm for more information.
CV-Read
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFF6 |
Data | 2-byte CV number (MSB first) |
Data | 1-byte bit number (0...7) |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Bit in CV is set |
CV-Read reads a single bit of the configuration variable with the received number. If the bit is set, an acknowledgement is sent in channel 2.
CV-Write
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFF9 |
Data | 2-byte CV number (MSB first) |
Data | 1-byte CV value |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Write not possible |
CV-Write writes a configuration variable with the received number-value pair. Any write errors must be answered with an acknowledgement in channel 2.
Busy
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFF2 |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Decoder busy |
The Busy command can be used to check whether the decoder is still busy with the last packet. If a decoder is not yet ready for a new packet, it can reply with an acknowledgment in channel 2. If the command station sends packets other than Busy to decoders that are still busy, the packets are discarded and acknowledged with a response in channel 1.
ZPP Commands
The ZPP command set is used to update the ZPP project. It contains, among other things, an erase and update command, commands for ending the transfer and an exit command.
ZPP-Valid-Query
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FF06 |
Data | 2-byte ZPP identifier |
Data | 4-byte flash size |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | ZPP invalid |
A ZPP-Valid-Query can be used to check whether the decoders are able to load the desired ZPP at all. The check includes the 2-character identifier of the ZPP file and the size of its flash data. If the ZPP cannot be loaded, an acknowledgment must be sent in channel 2.
ZPP-LC-DC-Query
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FF07 |
Data | 4-byte developer code |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Load code wrong |
A ZPP-LC-DC query can be used to check whether the decoders contain a valid load code before deleting the flash. If the received load code is not correct, an acknowledgment must be sent in channel 2.
ZPP-Erase
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FF05 |
Data | 4-byte start address |
Data | 4-byte end address |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Invalid memory area |
With the help of ZPP-Erase, a certain memory area of the flash can be deleted. If an invalid memory area is received, an acknowledgment must be given in channel 2.
Warning
Deleting a NOR flash can take up to 200s depending on the manufacturer and type. To ensure that the operation is completed, polling can be done via the Busy command.
ZPP-Update
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FF08 |
Data | 4-byte start address |
Data | N-byte payload |
Data (CRC) | 4-byte CRC32 |
Acknowledgement | Invalid address or CRC32 error |
ZPP-Update is used to transfer ZPP data. If an invalid memory area or a CRC32 error is received, there must be an acknowledgment in channel 2.
Warning
Current implementations only support payloads up to 256 bytes.
ZPP-Update-End
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FF0B |
Data | 4-byte start address |
Data | 4-byte end address |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Invalid memory area |
ZPP-Update-End marks the end of the current data transmission and retransmits the written memory area. Any ZPP data that is still buffered must be written when this command is received. If an invalid memory area is received, an acknowledgment must be given in channel 2.
ZPP-Exit
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FF0C |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | - |
ZPP-Exit is used to reset the decoder. The reset is only carried out if the memory area previously transferred via the ZPP-Update-End matches that written by the decoder. If this is not the case, the decoder discards all written data.
ZPP-Exit&Reset
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FF0D |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | - |
See ZPP-Exit. In addition, decoders reset their configuration variables (CV8=8).
ZSU commands
The ZSU command set is used to update the decoder software. Among other things, it contains an update command, commands for a final CRC32 check and a command for transmitting the initialization vector of the Salsa20 encryption used.
ZSU-Salsa20-IV
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFF7 |
Data | 8-byte Salsa20 initialization vector |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | CRC8 error |
ZSU-Salsa20-IV is used to transmit the 8-byte initialization vector of the Salsa20 encryption.
Warning
For reasons of backward compatibility, CRC8 errors must be answered in both channel 1 and channel 2.
ZSU-Erase
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFF5 |
Data | 4-byte start address |
Data | 4-byte end address |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Invalid memory area |
The processor flash is deleted before an update package is written. If an invalid memory area is received, an acknowledgment must be given in channel 2.
Warning
After the command, a delay of at least 3.5s must be observed.
ZSU-Update
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFF8 |
Data | 4-byte start address |
Data | N-byte payload |
Data (CRC) | 4-byte CRC32 |
Acknowledgement | Invalid address or CRC32 error |
ZSU-Update is used to transfer firmware data. If an invalid address or a CRC32 error is received, there must be an acknowledgment in channel 2.
Warning
Current implementations only support payloads of exactly 64 bytes. Smaller payloads must contain appropriate padding.
ZSU-CRC32-Start
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFFB |
Data | 4-byte start address |
Data | 4-byte end address |
Data | 4-byte CRC32 |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Invalid memory area |
ZSU-CRC32-Start transfers the written memory area and the CRC32 of the encrypted firmware again at the end of the update. It should be noted that the checksum to be compared must be calculated using the encrypted data! If the transferred memory area does not match the one received via ZSU-Update packets, then a response must be made in channel 2.
Warning
The transferred memory area is a closed interval. The last address actually written corresponds to the end address!
ZSU-CRC32-Result
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFFC |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Received CRC32 not equal own |
With the help of the ZSU-CRC32-Result command, the command station queries the result of the checksum previously transmitted via ZSU-CRC32-Start. If the checksum is not correct, there must be an acknowledgment in channel 2.
ZSU-CRC32-Result&Exit
Command Phase | Description |
---|---|
Preamble | Identification and synchronization |
Data (coding) | 0xFFFF'FFFD |
Data (CRC) | 1-byte CRC8 |
Acknowledgement | Received CRC32 not equal own |
See ZSU-CRC32-Result. If the checksum is correct, the decoder must perform a reset.
Warning
Sending this command is mandatory. Updated decoders only mark the received software as complete upon receipt of this command.
Typical processes
ZPP Update
- Config-Transfer-Rate to find a transmission speed that is supported by all decoders
- ZPP-Valid-Query
- ZPP-Exit on answer
- ZPP-LC-DC-Query (optional)
- ZPP-Exit on answer
- ZPP-Erase
- ZPP-Update
- ZPP-Update-End
- ZPP-Exit | ZPP-Exit&Reset
- Leave track voltage switched on for at least 1s
ZSU Update
- Config-Transfer-Rate to find a transmission speed that is supported by all decoders
Warning
Due to a bug in bootloader versions <2.10.7 of the MN/S decoder (#21), the speed must be limited to 3 regardless of the reported capabilities.
- Binary-Tree-Search to find all connected decoders (optional)
- Ping the desired decoders
- ZSU-Salsa20-IV
- ZSU-Erase
- ZSU-Update
- ZSU-CRC32-Start
- ZSU-CRC32-Result (optional)
- ZSU-CRC32-Result&Exit
- Leave track voltage switched on for at least 1s
Getting Started
Prerequisites
- C++23 compatible compiler
- CMake ( >= 3.25 )
- Optional
Installation
This library is meant to be consumed with CMake,
Either by including it with CPM
cpmaddpackage("gh:ZIMO-Elektronik/MDU@0.18.0")
or the FetchContent module
FetchContent_Declare( MDU GIT_REPOSITORY "https://github.com/ZIMO-Elektronik/MDU" GIT_TAG v0.18.0)
target_link_libraries(YourTarget PRIVATE MDU::MDU)
or, on ESP32 platforms, with the IDF Component Manager by adding it to a idf_component.yml
file.
dependencies: zimo-elektronik/mdu: version: "0.18.0"
Build
The library itself can be built via CMake as follows. Depending on the target platform, it may be necessary to pass along a suitable toolchain file.
cmake -Bbuild -DCMAKE_TOOLCHAIN_FILE=toolchain-arm-none-eabi-gcc.cmake cmake --build build --target MDU
Host
On host platforms, a target for the skeleton code of a ZPP update is also created. More useful examples are on the TODO list.
cmake -Bbuild cmake --build build --target MDUZppLoad
ESP32
On ESP32 platforms examples from the examples subfolder can be built directly using the IDF Frontend.
idf.py create-project-from-example "zimo-elektronik/mdu^0.15.4:esp32"
Usage
To use the MDU library, a number of virtual functions must be implemented. Depending on whether ZPP or ZSU is to be transferred, one of the following abstract classes must be derived:
mdu::rx::ZppBase
mdu::rx::ZsuBase
mdu::rx::ZppZsuBase
The following example shows the skeleton code for implementing the ZPP update.
#include <mdu/mdu.hpp>
class ZppLoad : public mdu::rx::ZppBase { public: // Ctor takes configuration struct containing serial number, decoder ID and // supported transfer rate ZppLoad() : mdu::rx::ZppBase{{.serial_number = SERIAL_NUMBER, .decoder_id = ID, .transfer_rate = mdu::TransferRate::Fast}} {}
private: // Generate current pulse of length "us" in µs void ackbit(uint32_t us) const final {}
// Read CV bit bool readCv(uint32_t cv_addr, uint32_t pos) const final {}
// Write CV bool writeCv(uint32_t cv_addr, uint8_t byte) final {}
// Check if ZPP is valid bool zppValid(std::string_view zpp_id, size_t zpp_flash_size) const final {}
// Check if load code is valid bool loadCodeValid(std::span<uint8_t const, 4uz> developer_code) const final {}
// Erase ZPP in the closed-interval [begin_addr, end_addr[ bool eraseZpp(uint32_t begin_addr, uint32_t end_addr) final {}
// Write ZPP bool writeZpp(uint32_t cv_addr, std::span bytes) final {}
// Update done bool endZpp() final {}
// Exit (eventually perform CV reset) [[noreturn]] void exitZpp(bool reset_cvs) final {}
// Timer interrupt calls receive with captured value void interrupt() { receive(TIMER_VALUE); } };
The entry into the MDU protocol can be handled by the rx::entry::Point
class. The ctor takes the decoder SN, ID and two optional function objects hooks to call before starting the ZPP or ZSU update.
// Ctor takes SN, ID und function object hooks with void() signature mdu::rx::entry::Point entry_point{{.serial_number = SN, .decoder_id = ID, .zpp_entry = enter_zpp_update, .zsu_entry = nullptr}};
// Forward DCC verifies entry_point.verify(index, byte);