Connecting to the Roco Z21

In a previous post I already introduced the Roco Z21 DCC command station. I mentioned the network interface as our gateway toward control using a computer. Now we’re going to make this happen!

The Roco Z21 has a network interface for the included Wifi base station. If you wish to use a different base station or a wired network. In that case use the Maintenance Tool (Windows only, but works with WINE on Mac) to change the configured IP address on the Z21. Be careful not to make any mistakes though: it is very hard to correct an incorrect IP-address.

Instead, it is probably easier and much safer to connect the Z21 to the included Wifi base station and connect your computer to it as well (either wired or wireless). The SSID of the Wifi network starts with Z21_ and followed by 4 digits. Find the password (PIN) on a label on the base station.

Note 1: If you have an iPhone or iPad, you’ll have to join this Wifi network as well in order to use the Roco Z21 mobile app.

Note 2: It is likely that your computer will lose internet connection once it is connected to this base station. Remember to switch Wifi networks back and forth when you use internet for looking for information online and testing things on the Z21.

Now, the tricky part of communicating with the Z21 is that the communication is asynchronous! In a synchronous environment, you would expect that when you send a command, the Z21 will reply with a status message acknowledging this command. However, the Z21 may want to inform you about events that happen on the layout as soon as they happen. Such events could be a section that is now suddenly occupied, another loc than the one you are controlling has changed its status, or a shortcut on the track and the power is switched off. In such cases, the Z21 will send the message you expect, but maybe after some other ones that came first.

This will be more of an issue on larger layouts or if we wish to create larger programs. Then the sending and receiving functions that we’ll implement will have to be asynchronous as well. For now we’ll focus on a dialog with the Z21 where we don’t attach any track or throttle at all. So we don’t expect any out-of-order messages to interfere in order to keep things simple.

What we will do now is the following:

  1. Establish a connection with the Z21
  2. Query its serial number
  3. Read its response.

We will use the Application Programming Interface from Roco that is documented very well, but unfortunately in German. I think with the guidance I give here, also non-German speaking readers should be able to follow how things work.

We’re going to use Python to start with. We could repeat this exercise later e.g. in JavaScript using Node.js or Swift as well.

Establish a connection

Once you have connected your computer to the Z21 via the included base station, you should be able to reach it via IP address 192.168.0.111. The connection is UDP/IP and the port to be used is 21105. Once your computer has made a connection, it can send packets containing commands to, and receive packets containing status messages from the Z21.

Create an UDP connection in Python via the following statements:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Z21 = ('192.168.0.111', 21105)

AF_INET stands for Internet, SOCK_DGRAM for UDP. The IP address might be different for you if you don’t use the default address.

Query Serial Number

The serial number is queried with the LAN_GET_SERIAL_NUMBER command (section 2.1 in the protocol specification). The format is as follows:

  1. DataLen (2 bytes little endian): 0x04, 0x00
  2. Header (2 bytes little endian): 0x10, 0x00
  3. No data section

The required code for sending the command will be:

command = b'\x04' + b'\x00' + b'\x10' + b'\x00'
s.sendto(command, Z21)

Receiving and interpreting the response

Receive an UDP packet in Python using:

incomingPacket, sender = s.recvfrom(1024)
print(sender)
print(incomingPacket)
print(incomingPacket[4:])
serialNumber = int.from_bytes(incomingPacket[4:], byteorder = 'little')
print(serialNumber)

As we can see, a pair of values are returned by the recvfrom function: the packet that arrived and the sender. The first thing we do for simplicity’s sake is just show the values as a result:

('192.168.0.111', 21105)

We hope that the sender was the Z21, but there is no guarantee. In case we cannot be sure, we have to verify this. Here we see the IP address and port number belonging to the Z21, so nothing to worry about.

Also the packet can have an additional complexity: according to the specifications, multiple messages may be included in the packet. One or more messages are simply added after each other in the packet. Each message starts a byte containing the length of the message.

b'\x08\x00\x10\x00\xa3\xcf\x01\x00'

In this case, the first byte has the value of 8, which is also the length of the entire packet. So we don’t have to add extra logic to process multiple messages in the packet. (I’ll come back to that in a later post.)

The format of the response of this function is:

  1. DataLen (2 bytes little endian): 0x08, 0x00
  2. Header (2 bytes little endian): 0x10, 0x00
  3. Data (32 bits, little endian)

The values for bytes in the Data section that we received are:

b'\xa3\xcf\x01\x00'

which translates to decimal:

118691

Now the Z21 is probably my first device that doesn’t have a serial number printed on it. Instead it can be found using the Maintenance Tool that only works for Windows, which I don’t have. When I compare with a few videos on YouTube where the Z21 serial number is mentioned, this number looks legit. That’s good enough for me!

So now we have a connection, we can send commands and receive results from the Z21. From here, we can receive locomotive information, control a locomotive, receive feedback from the layout and even start shunting.

For now I insert the full script that we have created so far:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Internet, UDP
Z21 = ('192.168.0.111', 21105)

# 2.1 LAN_GET_SERIAL_NUMBER
# Sending the command:
command = b'\x04' + b'\x00' + b'\x10' + b'\x00'
s.sendto(command, Z21)

# Receiving the response:
incomingPacket, sender = s.recvfrom(1024)
print(sender)
print(incomingPacket)
serialNumber = int.from_bytes(incomingPacket[4:], byteorder = 'little')
print(serialNumber)

s.close();

One Reply to “Connecting to the Roco Z21”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.