How to Train

The train controller system is a old system that was a black box to us.  After spending many weeks we learned how to interface with it and control the tracks.

Control the tracks

The tracks work by using a 9600/8N1 5V UART connection. This does not work at 3.3V so the signal must be amplified to 5V. The signal is applied to the yellow cable. The format of the packets is:
preamble dest src length b0 b1 b2 cs
The preamble is 0xf1. The destination is a hex value between 0x00 and 0x02. The source is ignored so I always used 0x00. The length is always 0x03 since there are 3 more bytes of data to follow. The bytes that follow contain information about the polarity of the tracks and the speed of the tracks. From MSB to LSB the first 2 bits are ignored, the next 2 set the direction then the last 4 set the speed:
xxddssss
There are 4 possible direction for the train tracks so that the cross roads can be used. We did not get the cross roads implemented so we are unsure exactly how they work.

A simple function to set the bytes correctly is as follows:

int set_speed(uint8_t dest, uint8_t dir0, uint8_t dir1, uint8_t dir2,
uint8_t speed0, uint8_t speed1, uint8_t speed2){
// Preset bytes
buffer[1] = 0x00; // The source is never used
buffer[2] = 0x03; // Always 3 bytes
if (dest > 0x02) return 1; // invalid destination
buffer[0] = dest; // set destination
// Set the speed and direction and clamp it
if (dir0 > 0x03) return 2; // invalid direction
if (dir1 > 0x03) return 2; // invalid direction
if (dir2 > 0x03) return 2; // invalid direction
if(speed0 > 0x0F) speed0 = 0xF; // Clamp the speed
if(speed1 > 0x0F) speed1 = 0xF;
if(speed2 > 0x0F) speed2 = 0xF;
// store the speeds and the directions
buffer[3] = (dir0 << 4) | speed0;
buffer[4] = (dir1 << 4) | speed1;
buffer[5] = (dir2 << 4) | speed2;
// packet correctly formatted
// send it
send_packet(buffer);
return 0;
}

And the function to send a packet:

void send_packet(uint8_t data[6]){
while(!UARTTransmitterIsReady(UART1));
UARTSendDataByte(UART1, 0xf1); // preamble
int i;
for (i=0; i<6; i++){
while(!UARTTransmitterIsReady(UART1));
UARTSendDataByte(UART1, data[i]);
}
while(!UARTTransmitterIsReady(UART1));
UARTSendDataByte(UART1,checksum(data)); // send the cs
}

This method is blocking but the packet is short enough that the LED will not noticeably change.

The checksum does not include the preamble. Also ensure that the PIC is connected to the controller ground, the black cable coming out of the station.

Controlling the switches

All of the switches are controlled by the 2×4 socket. All of them need to be tied to ground before the capacitors start charging. To change a switch the caps must be charged and there must be a voltage applied to one of the pins for more than 50us. In our setup we applied the voltage for 1 second. The voltage can be held on the pin for as long as desired but the caps will not start charging. The caps must be allowed to charge for approximately 10 seconds between each switch change. While the caps are charging all of the pins must be tied to ground. If the pins are not tied to ground then the caps will not charge and the switches will not work.

Using the reed switches

The reed switches are simple switches with the wires running in the open back to the large ribbon cable. For our setup we applied a voltage to one side and tied the other side to ground through a pull down resistor. This allowed the PIC to detect when the switch was closed due to the presence of a train.

Train network

We attempted to learn the byte and destination of every aspect of the tracks as well as the values for different switch position. We compiled this information into a PDF that contained all of the possible combinations. The Track_layout is linked here.

How to build test code

If you are attempting to talk to the train tracks via RealTerm rather than a PIC there are 2 ways that this can be achieved.  The first is through the send functionality of RealTerm.  This can send non printing hex values such as 0xf1 the preamble.  The alternative is to write a .bin file with the raw hex values then use RealTerm to send the file.  To do this the xxd function inside bash would be useful.  xxd converts from hex to a binary file and back with the -r flag.  Finally, to generate the checksum there is a simple python script to run:

data = [ 0x00, 0x00, 0x03, 0x2F, 0x20, 0x20]
check_sum = 0x00
for x in data:
check_sum ^= x
print(hex(x))
print(hex(check_sum))