Good approach for modular design and versatile features... eager to see it soon in action.
Thanks for feedback = Appreciated.
Look at the short demo video (post #1) to see the HubController in action using the B4J Client
RailYard .
The loco, named
Lokky , follows an automated schedule. The track end points are reed switches, the track turnout and the 2 signal-masts are set accordingly.
Schedules are defined in external file with JSON format.
More information about the
RailYard Application:
RailYard is a standalone LEGO® train and layout control system built for learning purposes and demonstrations.
It uses the ESP32-based
HubController communicating over a hardware serial interface for track-side device control (signals, switches, sensors) and BLE for Loco control with BLE Bricks. It is a hobby project to demonstrate:
How to develop a microcontroller (ESP32) to control a LEGO simple railroad layout with 1 loco (BuWizz2 BLE Motor), 1 switch (TrixBrix motor), 3 detectors (REED).
How to control the loco (via BLE) and the peripherals via client applications (B4A, B4J, Python PySide6) using Windows 11, Ubuntu & Raspberry Pi OS Trixie.
How to communicate using the serial line and BLE (for Loco only).
How to create simple automated schedules (this has been the ultimate goal and the real fun part).
Hardware
HubController: ESP32-WROOM-32
IO: MCP23017 Port Expander, PCA9685 PWM Servo driver, 3x REED switches
TrixBrix : 2x Signal Masts (2-aspect), 1x Servo Switch, 2x Boom Barrier LEDs
Bricks:
LEGO® PoweredUp 88009 Hub to power and control PoweredUp sensors and motors.
BuWizz 2.0 "Ludicrous": a remote control & battery in one brick, compatible with all LEGO® Power Functions motors and lights.
Custom BLE-based ESP32 bricks (planned).
Software
B4R: 4.00 (64 bit)
B4R Additional external libraries: Adafruit MCP23017 Arduino 2.3.2 (B4R Lib rAdafruitMCP23017_I2C), Adafruit PWM Servo Driver PCA9685 3.0.3 (B4R Lib rAdafruitPWMServoDriver), NimBLE (B4R Lib: rNimBLEClient), rConvert
B4J: 10.50 (64 bit), B4A: 13.4 (64 bit), Python: 3.10.11, Qt: 6.10.2, PySide6: 6.10.2
Wiring
Spoiler: Wiring
B4X:
IO ID Addr ORANGE RED BROWN Notes
----------------------------------------------------------------------------------------------------------------------------------------------
SIGNAL MAST
Extension cable WHITE RED BLACK
Bottom Top
Signal Mast North SMN 20 PB1 3V3 PB0 MCP23017, False ON
Signal Mast South SMN 21 PB3 3V3 PB2 MCP23017, False ON
TRAIN DETECTOR (2 options REED or TCRT5000)
Extension cable WHITE RED BLACK
REED SWITCH (INPUT_PULLUP)
Train Detector West TDW 40 PA0 n/a GND MCP23017
Train Detector East TDE 41 PA1 n/a GND MCP23017
Train Detector North TDN 42 PA2 n/a GND MCP23017
TrixBrix TCRT5000 (INPUT)
Train Detector West TDW 40 PA0 3V3 GND MCP23017
Train Detector East TDE 41 PA1 3V3 GND MCP23017
Train Detector North TDN 42 PA2 3V3 GND MCP23017
BOOM BARRIER LED
Extension cable WHITE RED BLACK
LEFT RIGHT
BoomBarrier LED MCU BLM 30 PB7 3V3 PB6 MCP23017 PB7 = Left LED (False ON); PB6 = Right LED (False ON)
BoomBarrier Tower BLM 31 PB5 3V3 PB4 MCP23017
TRACK SWITCH
Extension cable ORANGE RED BROWN
Track Switch South SWS 50 0 VCC GND PCA9685 Channel 0
Note:
Each peripheral has extension cable (white,red,black) which is wired accordingly.
## MCP23017 Overview
PA0 = Trans Detector West
PA1 = Trans Detector East
PA2 = Trans Detector North
PA3 =
PA4 =
PA5 =
PA6 =
PA7 =
PB0 = Signal Mast North Top LED
PB1 = Signal Mast North Bottom LED
PB2 = Signal Mast South Top LED
PB3 = Signal Mast South Bottom LED
PB4 =
PB5 =
PB6 = BoomBarrier LED Left MCU
PB7 = BoomBarrier LED Right MCU
## PWM Servo Driver
PCA9685 = MCU
GND = GND (Black)
OE = n/a
SCL = IO22 (Blue)
SDA = IO21 (Green)
VCC = 3V3 (Red)
V+ = 5V 3A (recommend 1A per servo switch)
I2C Address = 0x40
External Power = PCA9685
V+ = VCC (orange)
GND = GND (black)
## PCA9685 Overview
Channel 0 = Track Switch South
Channel 1 =
Channel 2 =
Channel 3 =
Channel 4 =
Channel 5 =
Channel 6 =
Channel 7 =
Channel 8 =
Channel 9 =
Channel 10 =
Channel 11 =
Channel 12 =
Channel 13 =
Channel 14 =
Channel 15 =
## Multi IO Expander
MCP23017 = MCU
VCC = 3V3 (Red)
GND = GND (Black)
SCL = IO22 (Blue)
SDA = IO21 (Green)
I2C Address = 0x27
## Multiple I2C
Connect multiple I2C devices.
Each device must have unique I2C address. Check out with an I2C Scanner.
AdafruitMCP23017 = 0x27
AdafruitPWMServoDriver PCA9685 = 0x40
Power Setup
ESP32 (bare module or development board with 3.3V regulator). Powered by an external 3.3V supply.
PCA9685: Powered by external 5V supply with 3A (servo switch requires 1A).
Command Data Format
Commands are sent from clients via the serial line to the HubController using the HubBin frame format (see post #1).
Examples
B4X:
signal_south_off: HDR, SIGNALMAST1_ADDRESS, SIGNAL_CMD_ASPECT, SIGNAL_ASPECT_OFF, FTR
signal_south_go: HDR, SIGNALMAST1_ADDRESS, SIGNAL_CMD_ASPECT, SIGNAL_ASPECT_GO, FTR
HubBin Frame 5-Bytes
HDR = 0x19
SIGNALMAST1_ADDRESS = 20
SIGNAL_CMD_ASPECT = 0
SIGNAL_ASPECT_OFF = 0
SIGNAL_ASPECT_GO = 2
FTR = 0x58
Modules
The application has a modular setup used by the HubController firmware and client applications.
Module Purpose Examples B4R,B4A,B4J (.bas), Python (.py) Device Addresses Static device and peripheral addresses HubAddresses.bas
addresses.py Model System model definitions (types, commands, states) HubModel.bas
model.py Protocol Definition of the communication frame + helpers at the protocol level (HubBin) HubProtocol.bas
protocol.py Commands High-level named commands built on hub_model constants
CmdMap.bas
commands.py
Clients
Note that any client which can send binary data over a serial line can be used.
B4J
- B4J/RailYardGUI: Control the yard using GUI with a graphical track layout, loco controller, scheduler
- RailYardHMI: For development - HMI Tiles based GUI to test the yard
- RailYardScheduler: For development - run defined schedules
- RailYardBlockly: Experimental - Control using Blockly to send commands
- RailYardDCCBridge: Experimental - Bridge between DCC and HubController (tested with with RocRail)
- SerialTest: Tool - Test sending commands over the serial line
B4A
- RailYardCommander: For development - control the yard using direct commands
Python
- RailYardGUI: Control the yard using GUI with a graphical track layout, loco controller, scheduler
- RailYardDCCBridge: Experimental - Bridge between DCC and HubController (tested with RocRail)
- SerialTest: Tool - Test sending commands over the serial line
RocRail (Experimental)
- RocView (and RocRail server) to manually control or via predefined schedules
Schedule Example (JSON Format)
Lokky the loco starts at south track west position, runs to the south track east position and then back with low speed 25.
JSON:
{
"route": "WEST_to_EAST_vv",
"steps": [
{"type": "COMMAND", "value": "signal_south_stop"},
{"type": "WAIT_TIME", "value": 2},
{"type": "COMMAND", "value": "signal_south_go"},
{"type": "COMMAND", "value": "switch_south_straight"},
{"type": "COMMAND", "value": "motor_set_direction_bck"},
{"type": "COMMAND", "value": "motor_set_speed_25"},
{"type": "WAIT_TIME", "value": 2},
{"type": "WAIT_DETECTOR", "value": "DETECTOR2"},
{"type": "COMMAND", "value": "motor_stop"},
{"type": "WAIT_TIME", "value": 2},
{"type": "COMMAND", "value": "motor_set_direction_fwd"},
{"type": "COMMAND", "value": "motor_set_speed_25"},
{"type": "WAIT_TIME", "value": 1},
{"type": "WAIT_DETECTOR", "value": "DETECTOR1"},
{"type": "WAIT_TIME", "value": 1},
{"type": "COMMAND", "value": "motor_stop"},
{"type": "COMMAND", "value": "signal_south_stop"}
]
},