Automated Lego Train System

by kidznco in Circuits > Raspberry Pi

3008 Views, 2 Favorites, 0 Comments

Automated Lego Train System

F1K9JMKKUCXS9ER.jpeg

This is a proof of concept to automate my Lego trains so that multiple trains can run on the same loop of track without crashing into each other and having to go back to the repair shop.

In this my first Instructable and I intend to continue to document the development of this project.

I am using an LED and a LDR to create a light beam tripwire across the track. This will allow the system to determine when a train gets to a specific spot and can stop the train if the track ahead has a train or to send the train if the track ahead is clear.

Supplies

Raspberry Pi (running Python 3.7 or later)

An MQTT broker (can be running on the RPi or any other local network broker)

2 x ESP32 (DOIT ESP 32 DEVKIT V1 or similar)

3 x 5mm LED

3 x LDR (Light Dependent Resistor)

3 x 220 ohm resistor (current limiting for LED)

3 x 10k ohm resistor

Hook up wire

Large breadboard

Some Lego to make an enclosure for the sensor including 6 1x2 technic bricks or 1x4 technic bricks.

Micro USB cable

5v power supply

2 x Lego train engines with Lego Powered Up Hubs (and batteries)

Lego track pieces

Make the Sensor Boxes

20211005_225704.jpg
20211005_225712.jpg
20211005_225730.jpg
20211005_225754.jpg
20211005_225810.jpg
20211005_225817.jpg

Using the Lego, create a box with a technic brick at the front.

Remove the technic brick and place an LED in the hole.

Replace the technic brick with LED back to the Lego box.

Repeat using a LDR instead of a LED. you may need to make some padding to keep the LDR in place.

Repeat so that you have 3 LED and 3 LDR boxes.

It is a good idea to label each of the sensor boxes as 0, 1, and 2 for easy identification later.

Wire the Sensors

As we are working with a small track we will be using 1 ESP32 to monitor all the sensors. On a larger track each sensor might have it own ESP32 due to the distance between sensors.

Create your track and determine 3 points roughly equally spaced around the track for the sensor placement.

Roughly place the sensors in those places with the LED's on the inside of the track (not really necessary but just to avoid stray light from hitting other sensors.

Connect the Anode (long leg) of each LED to a current limiting resistor and then to 5v from the power supply.

Connect the Cathode (short leg) of each LED to ground (GND)

Connect one leg of each LDR to GND

Connect the other leg of the LDR to both a ADC pin on the ESP32 and to the 10k resistor.

Connect the other end of each 10k resistor to 3.3V from the ESP32

Connect GND from the power supply to GND on the ESP32

Connect 5v from the power supply to Vin in the ESP32

Connect Sensors and Flash Firmware

Connect sensor 0, 1, and 2 to ADR pins 36, 39, and 34 respectively on the ESP32.


Modify the trackSensor_LED_tripwire.ino firmware to include your wifi and mqtt details.

Flash the firmware to the ESP.

Test Sensors

Sensor Test Terminal.png

Setup the Raspberry Pi (RPi). Make sure that Python 3.7 or later is installed.

If you do not already have a MQTT broker you can install one on the RPi. Random Nerd Tutorials have a good basic guide (https://randomnerdtutorials.com/how-to-install-mosquitto-broker-on-raspberry-pi/).

Make sure that the sensor firmware from Step 3 has the correct IP address for your MQTT server. You may want to set your RPi up with a static IP address in your router. Check your router documentation for details on how to so that.

On the RPi run the mqtt_watcher.py script

$ python3 mqtt_watcher.py

Put an object in front of a sensor (blocking the light) and remove it. Do this for each of the sensors and you should get an output similar terminal shown. If you receive more messages you will need to adjust the subscribe call in the on_connect() function of mqtt_watcher.py.

Once the sensors are running as expected then continue to the next step.

Downloads

Get the Bluetooth Address of a Train Hub

get_bluetooth_adddress.png

Run the get_BT_address.py script on the RPi and turn on one train hub.

$ python3 get_BT_address.py

It may take a couple of attempts but you will get an output similar to that shown.

Take a note of the bluetooth address (circled in picture). I stick a note to the top of the train for later so that I can easily identify train engines. (I have more than 1 red passenger engine).

Make sure that train hub is turned off. (press button for 10sec.)

Do this for each train hub.

You will need these bluetooth address' later.

Downloads

Create and Test Control Devices

readSerialToCli1.png
hub_setup.png
hub_connected.png

Take another ESP32 and flash the 'Lego_PU_Serial_Multi_Controller.ino' firmware to it.

Connect this freshly minted controller to the RPi by USB.

On the RPi run the readSerialToCli.py script

$ python3 readSerialToCli.py

You will get a list showing Hub: 0 Hub: 1 and Hub:2 all with Status: NOT connected

If this does not appear, press the reset button on the ESP32 (left side of usb when looking at usb end of ESP32). If still no luck then you will need to change the USB port in the readSerialToCli.py script.

Once you are getting output from the controller we can then setup a train hub and test control.

In another terminal we are going to use testHubComs.py to send commands to the controller.

First we need to send a hub information.

Put the controller into setup mode

$ python3 testHubComs.py 0 0

This program accepts arguments which are used in the program to define it function. The first '0' defines the USB port the controller is connected to. in my case /dev/ttyUSB0. The second '0' puts the controller into setup mode.

The terminal running readSerialToCli.py (the serial terminal) now states 'Setting train settings' andthat it is now 'Waiting for engine ID' this refers to Hub: 0 Hub: 1 Hub: 2 displayed before. We are going to set up Hub: 0

$ python3 testHubComs.py 0 0

The controller is now 'Waiting for bluetooth address'. enter the followingbut use your bluetooth address.

$ python3 testHubComs.py 0 90:84:2b:01:a7:3a

The controller is now 'Waiting for engine description". this is a short human readable description. Use quotation marks if your description uses spaces.

$ python3 testHubComs.py 0 "Red Pass 1a"

The controller is now "Waiting for motor directions". This is 2 numbers the first representing the motor connected to PortA and the second to PortB. '1' means that positive speed moves the engine forward. '-1'means that positive speed moves the engine backward. '0' means no motor connected to that port. My Red Pass 1a only has a forward facing motor connected to PortA.

$ python3 testHubComs.py 0 1,0

The controller will now go back to normal mode showing the status of the 3 hub slots. Hub: 0 is now filled in and the controller will attempt to connect to the hub. This may take a couple of power cycles of the hub.

Once connected we get it to move. To change the speed of the engine we need to know the usb port the controller is on, and the hub Id that the hub was setup on. To move our newly connected engine forward at 30% power.

$ python3 testHubComs.py 0 1,0,30

The numbers mean

  • USB0
  • function 1 -i.e. send speed to hub
  • hub ID: 0
  • speed 30%

setting speed to 0 (zero) will stop the engine. Negative speed will make the engine go backwards.

Now we can start with some automation and use the sensors to control the speed.

The Automation

The system is broken up into train objects and track segment objects. Train objects are defined by the class Train in Train.py. Track segment objects are defined by the class Segment in Segment.py. The system is setup, trains/segments defined and segments attached together to form a track, in myTrack.py. The myTrack.py script also monitors mqtt messages and triggers appropriate functions.

Save the below python scripts to your chosen folder, where hubComs.py is located.

You will need to alter myTrack.py in the following places

  • Line 6 - change the mqtt server address to the address of your mqtt server
  • In createObjects() function
  • create tracks - if you changed the mqtt topics for the sensors then change the 9th parameter of the Segment.Segment() function to suit.
  • attach tracks - check how each track segment is connected to the next and previous
  • create trains - for each train adjust the following
  • the name (ie "passanger00" to what you want your train to be called limit to 19 characters
  • the bluetooth ("90:84:2b:01:a7:3a")address to the address that you discovered in step 5
  • the usb port ("/dev/ttyUSB0")that will be setup to control this hub. This should be the usb port that you used in step 6. Remember that if you have more than 3 hubs you will need more controllers and they will have different usb ports.
  • the controller hubID (0). this needs to be different for every hub on the same usb port (ie same controller) each controller has 3 ID slots (0, 1, or 2)
  • the motors connected ((1,0)). the parenthesis are important. This defined the motor direction or the motor existence of a motor connected to portA and portB respectively. As per step 6
  • 1 means running forward,
  • -1 means running backwards
  • 0 means no motor connected.
  • locate trains on segments - adjust to suite your needs. Current setup is.
  • train 0 "passanger00' is located just before sensor 2
  • train 1 "cargo00' is located just before sensor 1

Once the changes are made, locate the trains on the track where stated in the program.

Run the program

$ python3 myTrack.py

Power on all hubs. Once all hubs have a solid led (this may take a couple of power on's) press Enter to confirm that the train is connected.

Each train will take turns moving to the empty segment.