Arduino firmware for model railroad control modules. The project is built with PlatformIO and currently targets Arduino Uno plus an initial Olimex ESP32-POE2 port for the I2C scanner.
| Path | Purpose |
|---|---|
src/ladder_track/ |
Main route-selection sketch for ladder-track switching. |
src/switch_board/ |
Two-button, two-servo switch-board controller. |
src/button_test/ |
Serial test sketch for PCF8574 button inputs. |
src/servo_test/ |
Serial test sketch for PCA9685 servo movement. |
src/i2c_scan/ |
I2C bus scanner sketch. |
include/ |
Shared helpers such as ButtonInput and small utility functions. |
docs/vendor/olimex/ESP32-POE2/ |
Vendored official Olimex ESP32-POE2 documentation. |
mock/ |
Local Arduino/Wire mocks used by the experimental CMake setup. |
The Makefile wraps PlatformIO through uv.
make build
make build-allBy default, make build builds the uno_ladder_track environment. To build a
specific environment:
make build PLATFORMIO_ENV=uno_switch_board
make build PLATFORMIO_ENV=uno_button_test
make build PLATFORMIO_ENV=uno_servo_test
make build PLATFORMIO_ENV=uno_i2c_scan
make build PLATFORMIO_ENV=esp32_poe2_i2c_scanTo upload, set the environment and serial port as needed:
make upload PLATFORMIO_ENV=uno_switch_board UPLOAD_PORT=/dev/ttyUSB0
make upload PLATFORMIO_ENV=esp32_poe2_i2c_scan UPLOAD_PORT=/dev/ttyACM0The configured PlatformIO environments are:
| Environment | Sketch | Notes |
|---|---|---|
uno_ladder_track |
src/ladder_track/LadderTrack.cpp |
Default Arduino Uno firmware. Uses AceRoutine and PCF8574-style I2C I/O modules. |
uno_switch_board |
src/switch_board/SwitchBoard.cpp |
Arduino Uno build for driving two switch servos and their position LEDs. |
uno_button_test |
src/button_test/ButtonTest.cpp |
Arduino Uno build that prints debounced button press events over serial. |
uno_servo_test |
src/servo_test/ServoTest.cpp |
Arduino Uno build that sweeps PCA9685 servo channels for calibration/testing. |
uno_i2c_scan |
src/i2c_scan/I2cScan.cpp |
Arduino Uno build that prints detected I2C device addresses. |
esp32_poe2_i2c_scan |
src/i2c_scan/I2cScan.cpp |
ESP32-POE2 build of the I2C scanner. Uses UEXT I2C pins GPIO13 SDA and GPIO33 SCL. |
esp32_poe2_i2c_scan is the first ESP32-POE2 target. It uses the same scanner
sketch as the uno_i2c_scan environment, but selects Olimex's ESP32-POE2 board
definition and configures the UEXT I2C pins explicitly.
Current ESP32-POE2 details:
| Item | Value |
|---|---|
| PlatformIO environment | esp32_poe2_i2c_scan |
| Board manifest | boards/esp32-poe2.json |
| PlatformIO platform | espressif32 |
| Board | esp32-poe2 |
| Framework | Arduino |
| Serial baud | 115200 |
| UEXT SDA | GPIO13 |
| UEXT SCL | GPIO33 |
The local boards/esp32-poe2.json file is based on Olimex's PlatformIO board
manifest. The linker-script field is adapted to PlatformIO's current local board
schema by placing esp32_out.ld under build.arduino.ldscript.
Important hardware note from Olimex: ESP32-POE2 has no galvanic isolation from Ethernet power. Disconnect PoE Ethernet while programming over USB unless the setup is properly isolated.
Official Olimex resources used for this port:
The Olimex repository states that hardware is under CERN-OHL-S-2.0, software is
under GPL-3.0, documentation is under CC BY-SA 4.0, and box design files are
under CC BY 4.0. Local copies of the key board documents are stored under
docs/vendor/olimex/ESP32-POE2/ with upstream URLs, license notes, source
revision, download date, and checksums.
src/switch_board/SwitchBoard.cpp controls two railroad switches from two
active-low buttons on an I2C input expander.
The firmware uses 7-bit I2C addresses, matching the addresses printed by the
i2c_scan sketch.
| Device | Address | Role | Constant |
|---|---|---|---|
| PCA9685 | 0x40 |
Servo PWM outputs | SERVO_PWM_ADDR |
| PCA9685 | 0x41 |
LED PWM outputs | LED_PWM_ADDR |
| PCF8574-compatible input expander | 0x38 |
Button inputs | INPUTS_0_ADDR |
Default switch-board mapping:
| Function | Pin/channel |
|---|---|
| Switch 0 button | Input expander pin 0 |
| Switch 1 button | Input expander pin 1 |
| Switch 0 left LED | LED PWM channel 0 |
| Switch 0 right LED | LED PWM channel 1 |
| Switch 1 left LED | LED PWM channel 2 |
| Switch 1 right LED | LED PWM channel 3 |
| Switch 0 servo | PCA9685 servo channel 0 |
| Switch 1 servo | PCA9685 servo channel 1 |
LEDs are configured with LedOutputConfig and assigned to PCA9685 PWM channels
with pca9685_led(...).
Each debounced button press toggles the matching switch between left and right.
The firmware writes the configured servo angle to the matching PCA9685 channel,
waits for SWITCH_MOVE_DELAY_MS, then updates that switch's two LEDs.
Servo endpoints are configured as angles:
| Constant | Default |
|---|---|
SWITCH_LEFT_ANGLE_DEG |
0 |
SWITCH_RIGHT_ANGLE_DEG |
180 |
SERVO_MIN_US |
1000 |
SERVO_MAX_US |
2000 |
SERVO_FREQ |
50 Hz |
The angle is constrained to SERVO_MIN_ANGLE_DEG through
SERVO_MAX_ANGLE_DEG and mapped to the configured microsecond pulse range
before writing to the servo driver.
The LEDs show the commanded position after the movement delay. The hardware does not provide physical position feedback, so the firmware cannot verify that the switch reached the requested position.
Button handling uses edge detection so holding a button does not repeatedly
toggle a switch. Debounce is isolated in ButtonInput; set
DEBOUNCE_ENABLED to false in SwitchBoard.cpp to bypass debounce timing
while keeping press-edge detection.
src/ladder_track/LadderTrack.cpp waits for two input selections: a source
position and a destination position. It resolves that pair through an internal
route table and pulses the configured I2C output modules for the required switch
actions.
Current ladder-track details:
| Item | Value |
|---|---|
| Input read mode | Polling |
| Poll interval | 20 ms |
| Input module read by firmware | 0x27 |
| Output modules | 0x20, 0x22, 0x24, 0x26, 0x28 |
The route table is currently defined directly in Route::TABLE inside
LadderTrack.cpp.
The README is now aligned with the current source, but these sections still need project-specific detail before the documentation is complete:
- Wiring diagrams or pinout tables for the actual hardware modules.
- Power requirements and servo/LED supply notes.
- Ladder-track route table documentation outside the source code.
- Servo calibration procedure for choosing angle and pulse-range constants.
- Serial monitor examples for each test environment.
- Decision on whether the CMake/mock workflow is still supported; the current
root
CMakeLists.txtdoes not match the current source layout.
This project is licensed under GPL-3.0; see LICENSE.