Notes on Interfaces

I2C over servo cable

I2C was designed to work over relatively short distances, limited by the capacitance between the conductors in the cable.  I am planning to use I2C to connect modules for several of the systems on the project, such as the switch machine controllers, the banks of relays in the block switching matrix, and trackside signals. Since some of these systems will have their modules spread out, I need to test a few different forms of I2C cabling to see what works where.

For example, on the bus connecting the switch machine controller modules I am considering using standard white/red/black servo wire for the I2C bus.  Because the controller modules will receive their 5 volt power independently of the bus, the servo cable only needs to carry the SDA (data), SCL (clock), and ground signals.  To keep the capacitance between the SDA/SCL wires and the ground balanced, the center wire (red) will be the ground wire.  I’ve arbitrarily decided to make the white wire SDA and the black wire SCL.  So far this has worked out fine in testing over 2 meters (about 6.6 ft) of servo wire.  If I have problems with driving longer runs of servo wire, I have some I2C bus extender chips (P82B715) which will increase the distances I can run the bus between modules.

Because the block switching controller modules will all reside in a pair of rack-mounted units, the distances between modules will be short enough that I won’t need the bus extenders there.

UDP network communications

Most network communications over Ethernet cable is done using one of two protocols, TCP and UDP. TCP provides message acknowledgements and error-correction, while UDP has neither unless the application using UDP provides them. You can find a detailed comparison of the two protocols here. The bottom line is that UDP is more suited to real-time communications, at the expense of communications reliability.

For a steady stream of data, say from a sensor or to a display device, timing is usually more important than completeness of the data samples. An example of this is my throttle system. Sometimes though, it’s important that every message (datagram in UDP terms) be received and processed correctly. For example, dropping a command intended to position a turnout, or to switch a track block to another throttle’s control, would be very bad news for operations. For this type of application, you need either the reliable communications of TCP with its real-time performance issues, or a protocol layered on UDP to provide reliability and performance. A draft standard for a protocol named Reliable UDP or RUDP exists, but it’s more complex than I want to implement on Arduinos, so (in true Maker fashion 😁) I have rolled my own. For now I will call it UDP-E, or UDP with Echo.

The first application of UDP-E which I have tested is for the block switching system. The control console, a pi-topCEED which contains a Raspberry Pi 3, will connect to all subsystems via its Ethernet port through one or more network switches. The block switching system will have an Ethernet adapter which will connect to the network. This adapter module will be connected to an Arduino processor, which will translate commands which arrive via UDP datagrams into I2C messages for the block switching controllers.

The table below shows the format for the messages controlling the block switching relay matrix.

0signature82 (ASCII code for ‘R’)
1sequence0 to 255
2block0 to highest block number
3throttle0 to 7, or 255 to deselect all throttles

The signature byte marks this as a relay-matrix command (‘R’ is for relay). The sequence byte is incremented with each new command being sent, rolling over to 0 after 255; it is not incremented when a command is re-transmitted (more details below). The block and throttle bytes indicate which block is to be switched, and to which throttle channel it is being switched.

Each time the block switching controller receives a datagram from the central processor, it first echoes the datagram back to the central processor before acting upon it. The command is executed only if the datagram is the correct length (4 bytes) and the signature byte is the character ‘R’. The sequence byte is not used at all by the controller, only echoed back to the central processor. The throttle byte selects the throttle to which the block’s track will be connected; if the throttle byte is 255, the block is disconnected from all throttles.

Meanwhile, the central processor listens for the echoed datagram after it transmits each one. If no echo is received after a short wait, the same datagram (with the same sequence byte) is sent again. This process repeats up to five times until the exact same datagram is received. If the expected echoed datagram is still not received after five retries, this means there’s a hardware or software issue that must be resolved.