With the MattzoTrainController for Bluetooth, MTC4BT for short, we have taken a completely new approach. We have deviated away from the classic Arduino IDE in favour of the modern development environment of Visual Studio Code. For now this is just an experiment but if we feel confident enough we might move the whole code base to VS Code.

Content

Purpose

MTC4BT acts as a bridge between Rocrail and BLE (Bluetooth Low Energy) hubs. The MTC4BT connects to Rocrail via WiFi and to the BLE hubs via Bluetooth Low Energy. The BLE hubs control train motors and lights through wires connected to each of their channels.

MattzoTrainController for Bluetooth (MTC4BT)

Support for Powered Up hub and SBrick

MTC4BT supports the Powered Up (PU) hub and the SBrick by Sengit. This means you can now control a mix of up to nine PU hubs and SBricks in your trains using just one ESP-32 controller. You can even combine a PU hub and an SBrick in one locomotive, if needed. Note that an SBrick has 4 channels (or ports) at your disposal where PU hubs only has two.

But wait, there is more! We use the ESP-32 controller and it already has all those pins at our disposal. MTC4BT enables you to use those pins to attach stuff to them. For now it’s limited to lights (LED’s), but support for other peripherals can be added in the future. Think of all the possibilities this could give you, if you’d put an ESP-32 inside your train…

These extras of MTC4BT open up so many new possibilities for your locomotives and trains in general. We can’t wait to see what you’ll do with it!

Will MTC4PU still be supported?

We see MTC4BT as the next but more versatile iteration of MTC4PU. In the future we might consider deprecating support for MTC4PU, but for now we keep them side by side.

Functional Description

When Rocrail wants to make a train go into a certain direction, it communicates this as a “loco” command via the MQTT broker.

MTC4BT receives this command and evaluates it, to see if the command is relevant for one of the BLE hubs that is connected to the controller. It checks if its own MattzoControllerID corresponds with the “address” attribute that is configured for the train in Rocrail (see below). If this is the case, MTC4BT converts the desired speed and direction into a command for one or more connected BLE hubs, which then regulate the power supply for the attached motors and lights.

All BLE hubs and attached motors / lights must be configured. You can also configure the sense of rotation for all motors individually. This flexibility enables an enourmous variety of deployment scenarios for your trains, e.g.:

  • A single loco with one BLE hub and just one motor.
  • A powerful loco with one BLE hub and two motors.
  • A commuter train (ICE, Eurostar, TGV, Regio Express etc.) with two locos, one in the front, and one in the back. Each loco has one BLE hub and one motor.
  • Multiple locos pulling a giant freight train.
  • A long train with additional motors hidden in some cars in the middle of the train.

Many other scenarios are possible.

The implementation of the MTCBT adds full support of Rocrail’s capability of up to 32 functions. Each function can be connected to either a hub channel or a pin on the ESP-32. It is possible to connect cheap standard LEDs to the MTC4BT, which will enable various independent lighting options for the train, which you can control from Rocrail.

In the future we hope to include things like direction-dependend automatic white/red head and tail lights, a flashing disco light for a special wagon using multi-color LEDs, flashing lights in any color for special trains, etcetera. We also plan to adapt the motor power to the decreasing voltage of the battery in future versions of the firmware.

Required Components

  • An ESP-32 controller. On the picture you see an ESP32 NodeMCU development board, like this one:
    https://www.az-delivery.de/nl/products/esp32-developmentboard
  • An alternative could the (smaller) ESP32 D1 Mini NodeMCU – but be careful: this board does not cope with 9V DC and you’ll need to know your way around VS Code to compile the code for it.
  • Some kind of power source. You could use a small USB power pack, or, if deployed on land, a small USB charger can be used alternatively.
  • A USB cable to connect the controller to the power source (also required for flashing the firmware, at least for the first time).
  • A LEGO Powered Up Hub or an SBrick.
SBrick with LEGO Power Functions motor and LEGO Power Functions battery box
SBrick with LEGO Power Functions motor and LEGO Power Functions battery box

Wiring

Just connect the MTC4BT to a power source and put it inside or outside your train.

The controller on the pictures fits on an area of 4×8 studs. If this is still to large for your situation, you can search for smaller ESP-32 controllers on the market, e.g. a D1 Mini ESP-32. Please note we haven’t tested any MTC4BT controllers, other than those listed on this page.

Setup your development environment

We assume you have Visual Studio Code installed with the PlatformIO plug-in. There are numerous tutotials on how to do this, so we won’t get into that here.

There are a couple of things you should get and some other things that are important when setting up your PlatformIO development environment.

Get a supported ESP-32 board

This is the easy part as there is currently only one ESP-32 board that we have verified that works with the MTC4BT:

If you successfully test the MTC4BT firmware with another board, please let us know in the comment section below!

Get the source code

The firmware source code is hosted in this github repository:

https://github.com/Mattzobricks/MattzoControllers.git

Check out the firmware to a local directory of your choice.

The source code for the MTC4BT is located in the folder MattzoControllers/src/MTC4BT/. In there is a file named MTC4BT-workspace.code-workspace, which you should open from VS Code (use menu File / Open Workspace from File). This will open the correct workspace in VS Code.

Now you need to configure a few things before you can actually configure your controller.

Prepare PlatformIO for the ESP-32 platform

You need to install the ESP-32 platform framework. In VS Code in the left navigation bar go to PlatformIO. Under QUICK ACCESS > PIO Home click Platforms. This will open the PIO Home with the PlatformIO Framework Installer. Go to the Frameworks tab and search for “esp32”. Install the Espressif 32 (espressif32) framework. At the time of writing we are using v3.2.0.

Determine board ID for your ESP-32 board

Then you need to determine the right board ID for your board. Click Boards to open the PlatformIO Board Explorer. In case you use the AZ Develivery ESP32 NodeMCU Module you could search for “az-delivery”. The first hit should be “AZ-Delivery ESP-32 Dev Kit C V4”. Click on the plus-sign to the left of the name. This will expose the board ID you need: az-delivery-devkit-v4. Make a note of it or copy it straight to your clipboard, so you have it for later.

For other boards you might find a different board ID!

Determine COM port of your development board

First you need to install a driver so your pc can communicate with the ESP-32. You can find the required driver and documentation here:

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/establish-serial-connection.html

Then you attach your PC to your pc using a USB cable. Windows will recognize the device and install it. Now you can determine the COM-port your ESP-32 is connected to. For Windows 10/11 you can find this in Device Manager. Search for this device under Ports (COM & LPT):

Configure platformio.ini for your controller

We have provided an example platformio.ini file for your convenience. The only thing you have to do is copy platformio.example.ini to platformio.ini. You then open it for further editing.

Assuming you use the same ESP-32 board we did, you don’t need to change the default board ID. If you use a different board then you must configure platformio.ini for it. You can manually add a new [env:…] section to the platformio.ini file or you can use the PlatformIO UI for this. Under default_envs you can configure the environments PlatformIO should compile when compiling the project. This can be more than one environment, if you want. We stick to the default for now, but if you use a different board and have created a separate [env:…] section for it, you should configure it here.

Last but not least you must configure the COM port of your pc the ESP-32 is attached to. There’s a setting named upload_com_port under the [common] section where you can change it. By default the example platformio.ini is configured to attempt to find your ESP-32 on COM3. Change this to the COM port you found on your pc under Device Manager.

Controller Configuration

Configuration of the controller has been moved out of the controller source code, into two separate JSON configuration files. The first file contains your network configuration, like your WiFi network’s SSID and password and the address of your MQTT broker. The second configuration file contains you ESP-32 controller’s configuration. This is where you setup your board’s I/O pins and your locomotives.

Separating the configuration files makes them more flexibel and reusable. It also opens the possibility to change the configuration of the controller, without compiling and uploading a completely new firmware for every small change. VS Code is able to upload just these configuration files (even over the air), which is obviously much faster and more convenient.

At controller boot time, these configuration files are checked and verified, so if something seems wrong you can attach the controller to your PC and look at the serial output or just search through your logs in your syslog host on your network (see the network configuration file) to determine the issue. Make the required changes, upload the config files and try again.

You can find examples of both the network_config.json and controller_config.json in the source code in the data_example folder.

Below you’ll will find some configuration examples, but first we need to determine the MAC address of at least one BLE hub.

Minimal configuration files

Here you’ll find the bare minimum configuration the MTC4BT needs to function.

network_config.json

Below you’ll find the minimal content you need for the network_config.json file. At the very least you need to configure your WiFi access point SSID and password, as well as your MQTT broker’s address.

{
  "wifi": {
    "password": "YOUR_WIFI_PASSWORD",
    "SSID": "YOUR_WIFI_SSID"
  },
  "mqtt": {
    "broker": "YOUR_BROKER_IP"
  }
}

controller_config.json

The controller would actually run with an empty controller controller_config.json file. Not very practical, but it works. You could start with an empty controller_config.json file, just to detect your hub’s MAC addresses or to rule out any controller configuration errors, for instance.

{ }

Detecting the MAC address of BLE Hubs

To configure the BLE hubs in the configuration file, you need to know the MAC address of the hubs. How do you figure out what they are?

Once you have uploaded the firmware and configuration files (can be as basic as the minimal examples in the previous paragraph), there is a simple option for this built into the controller. You can hook up the MTC4BT to your pc using a USB cable and watch its output on the serial monitor in VS Code:

Discover BLE Hub MAC addresses using VS Code serial monitor

Now that you know the MAC address(es) of your hub(s) you can start configuring your first locomotive.

Configuring your first locomotive

Let’s assume you have a locomotive you want to control using Rocrail. It has a PU Hub in it, with a motor attached to channel A and lights to channel B. In that case your controller_config.json could look like this:

{
  "locos": [
    {
      "address": 1,
      "name": "Your first loco",
      "bleHubs": [
        {
          "type": "PU",
          "address": "00:07:80:d0:47:43",
          "channels": [
            {
              "channel": "A",
              "attachedDevice": "motor"
            },
            {
              "channel": "B",
              "attachedDevice": "light"
            }
          ]
        }
      ]
    }
  ]
}

If you’re familiar with JSON files you can probably understand this structure. The root of this config file has a subnode locos, which contains an array of locomotives. For each locomotive you need to specify a few basic properties:

  • address: The Rocrail address of the locomotive
  • name: A name for your locomotive
  • bleHubs: This contains an array of BLE Hubs within this locomotive

For each BLE Hub that’s in your locomotive you also need to configure some properties:

  • type: The type of BLE Hub (either PU or SBrick)
  • address: The MAC address of the BLE Hub (see above on how to discover this)
  • channels: This contains an array of BLE Hub channels

For each channel of the BLE Hub you need to configure a few parameters as well:

  • channel:
    • for PU Hub:
      • A, B or LED
    • for SBrick:
      • A, B, C or D
  • attachedDevice: The type of device connected to the channel (either motor or light)

Assuming you have Rocrail setup correctly and made no errors in your configuration files, this should allow you to run your first train using Rocrail, controlled by the MTC4BT.

Please note these are only the basics and there are many other properties on all levels that you can use to tweak your locomotives. We’ll go into more detail below.

Advanced settings

There are many advanced settings to tweak your MTC4BT controller to your specific needs. Many of them have sensible default values that you probably don’t need to configure, but some might come in handy. Find our below what more you can do by modifying or extending your controller configuration files.

The network_config.json file, or any other configuration file, cannot exceed the max. size limit of 4k!
If you have a config file that is larger, your controller might crash at startup.
If your controller_config.json file is too large, use the “locoConfigs” structure to move your locomotive configurations to separate files.
Note that the same 4k limit also applies to each separate locomotive configuration file.

Network

The network_config.json file requires the forementioned settings for WiFi, but there is a lot more network related stuff to configure. There are more settings for logging, WiFi and MQTT.

Logging

By default logging to the serial console is enabled, but logging has more advanced features. For instance, you can control the minimum log level or setup logging to a central Syslog server.

This is a typical “logging” section in a network_config.json file:

"logging": {
  "min_level": "info",
  "serial": {
    "enabled": true
  },
  "syslog": {
    "enabled": true,
    "server": "192.168.86.102",
    "port": 514,
    "appname": "MTC4BT1"
  }
}
  • min_level: The minimal log level. Any log message with this level or higher will be output in logging. Valid values are (from low to high):
    • debug (debug)
    • info (informational)
    • warning (warning)
    • err (error)
    • crit (critical)
    • emerg (emergency)
  • serial:
    • enabled: true/false (default: true)
  • syslog:
    • enabled: true/false (default: false)
    • server: “192.168.1.1”
    • port: 6514 (default: 514)
    • appname: “your-app-name”
WiFi

Besides the SSID and password you can configure the hostname to be used by the controller when registering itself on your network. Also the password required for Over-The-Air updates of the controller firmware or the file system which holds the configuration files (which are both supported) can be configured here.

"wifi": {
  "SSID": "YOUR_WIFI_SSID",
  "password": "YOUR_WIFI_PASSWORD",
  "hostname": "dns.hostname.for.your.controller.com",
  "otaPassword": "your-ota-password",
  "wait": 500
}
  • hostname: A valid DNS hostname to make your controller easier accessible on your network
  • otaPassword: The password required for Over-The-Air updates of controller firmware or the file system
  • wait: The timeout in milliseconds the controller waits before it attempts to reconnect to you WiFi network after a disconnect
MQTT

Find below all MQTT related settings you can configure.

"mqtt": {
  "broker": "192.168.10.17",
  "port": 8883,
  "keepalive": 10,
  "ping": 10
}
  • port: MQTT server port number (default: 1883)
  • keepalive: Sets the keep alive interval in seconds used by the MQTT client (default: 15)
  • ping: Sets the time in seconds between ping messages to Rocrail (default: 0 = no ping)

Controller

Even though a controller can run with an empty controller_config.json file, for fun you need to at least add one locomotive configuration to it. But there’s a lot more you can configure related to the controller itself. Please find a complete list of settings below.

{
  "name": "MTC4BT1",
  "pwrIncStep": 10,
  "pwrDecStep": 10,
  "espPins": [
    {
      "pin": 17,
      "attachedDevice": "light"
    },
    {
      "pin": 18,
      "attachedDevice": "status"
    }
  ],
  "locos": [
    {
      "address": 1,
      "name": "Your first loco",
      "bleHubs": [
        {
          "type": "PU",
          "address": "00:07:80:d0:47:43",
          "channels": [
            {
              "channel": "A",
              "attachedDevice": "motor"
            },
            {
              "channel": "B",
              "attachedDevice": "light"
            }
          ]
        }
      ]
    }
  ],
  "locoConfigs": [
    "/loco_BC60052.json",
    "/loco_CR10277.json"
  ]
}
  • name: Name for your controller (default: MTC4BT)
  • pwrIncStep: The default power increment step to be used for all locomotives (default: 10)
  • pwrDecStep: The default power decrement step to be used for all locomotives (default: 10)
  • espPins: This contains an array of pins of the ESP-32 microcontroller to use
    • pin: pin number
    • attachedDevice: light (use as a regular LED) or status (use as a status LED)
  • locos: This contains an array of locomotive configurations
  • locoConfigs: This contains an array of separate locomotive configuration files to load during startup (to be placed in the same directory as the network_config.json and controller_config.json files)

Both nodes “locos” and “locoConfigs” can be used in the same controller_config.json file. Read more on all settings of a locomotive in the next paragraph.

Locomotives

Locomotives are what makes our world go round, so there are loads of settings to configure them just like you want them to behave on your layout. Below you find a complete set of all options for locomotives. The same structure can be used in the “nodes” section of the controller_config.json file as well as the content of separate locomotive configuration files.

{
  "address": 6,
  "name": "CR10277",
  "pwrIncStep": 10,
  "pwrDecStep": 10,
  "bleHubs": [
    {
      "type": "PU",
      "address": "90:84:2b:0a:e3:73",
      "channels": [
        {
          "channel": "LED"
        },
        {
          "channel": "A",
          "attachedDevice": "motor",
          "direction": "backward"
        },
        {
          "channel": "B",
          "attachedDevice": "light"
        }
      ]
    }
  ],
  "events": [
    {
      "triggers": [
        {
          "source": "rr",
          "eventType": "fnchanged",
          "identifier": "f1",
          "value": "on"
        },
        {
          "source": "loco",
          "eventType": "dirchanged",
          "value": "forward"
        },
        {
          "source": "loco",
          "eventType": "dirchanged",
          "value": "backward"
        }
      ],
      "actions": [
        {
          "channel": "LED",
          "color": "white"
        },
        {
          "channel": "B",
          "pwrPerc": 80
        }
      ]
    },
    {
      "triggers": [
        {
          "source": "rr",
          "eventType": "fnchanged",
          "identifier": "f1",
          "value": "off"
        },
        {
          "source": "loco",
          "eventType": "dirchanged",
          "value": "stopped"
        }
      ],
      "actions": [
        {
          "channel": "LED",
          "color": "off"
        },
        {
          "channel": "B",
          "pwrPerc": 0
        }
      ]
    }
  ]
}

We already established most of these basic settings for a locomotive:

  • address: The Rocrail address of the locomotive
  • name: A name for your locomotive
  • pwrIncStep: Can be used to override the “pwrIncStep” configured at controller level
  • pwrDecStep: Can be used to override the “pwrDecStep” configured at controller level
  • bleHubs: This contains an array of BLE Hubs within this locomotive

For each BLE Hub that’s in your locomotive you also need to configure some properties:

  • type: The type of BLE Hub (either PU or SBrick)
  • address: The MAC address of the BLE Hub (see above on how to discover this)
  • channels: This contains an array of BLE Hub channels

For each channel of the BLE Hub you need to configure a few parameters as well:

  • channel:
    • for PU Hub:
      • A, B or LED
    • for SBrick:
      • A, B, C or D
  • attachedDevice: The type of device connected to the channel, either motor, light or nothing (default: nothing)
  • direction: forward or backward, can be used to reverse a motor (default: forward).
    • Attention: up to firmware version V1.0.2, please use the keyword reverse instead of backward.

A PU Hub has an onboard LED. By default it flashes after you turn it on or the hub looses its connection. It turns white after establishing a connection with the controller. By explicitly specifying the “LED” channel in a PU Hub’s configuration, you basically get full control over it. It can now be references by event actions, which we get more into later. Did you know the onboard LED can display colors? B.t.w. there is no need to explicitly configure the “attachedDevice” for an “LED” channel. We enforce “light” as the attached device, no matter what you might configure here.

  • events: This contains an array of events
    • triggers: This contains an array of triggers
    • actions: This contains an array of actions
Events

Events can be used to automate behavior. They can be triggered by Rocrail functions, by the locomotive or both. Each event can have multiple possible triggers and can execute multiple actions. Any trigger defined in an event executes all actions defined for that event.

Rocrail triggers

You can configure a Rocrail trigger like this:

{
  "source": "rr",
  "eventType": "fnchanged",
  "identifier": "f1",
  "value": "on"
}
  • source: rr (default: loco)
  • eventType: fnchanged
  • identifier: Name of the Rocrail function to monitor (f0-f32)
  • value: The value that triggers the event: on or off
Locomotive triggers

A trigger from the locomotive can be configured like this:

{
  "source": "loco",
  "eventType": "dirchanged",
  "value": "forward"
}
  • source: loco (default: loco)
  • eventType: dirchanged (raised when the locomotive changes direction or stops)
  • value: The value that triggers the event: forward, backward or stopped
Controller pin actions

With actions you can configure what should happen if a trigger for an event is fired. Actions reference a channel of a specific device. An action turning on an LED attached to a pin of the microcontroller can be configured like so:

{
  "device": "espPin",
  "pin": 17,
  "pwrPerc": 50
}
  • device: espPin or bleHub (default: bleHub)
  • pin: ESP-32 pin number (note the pin must be configured at controller level!)
  • pwrPerc: Percentage of power to apply to the channel (can be negative for backward direction)
BLE Hub channel action

An action turning on an LED attached to a channel of a BLE Hub can be configured like so:

{
  "device": "bleHub",
  "address": "90:84:2b:0a:e3:73",
  "channel": "B",
  "pwrPerc": 50
}
  • device: espPin or bleHub (default: bleHub)
  • address: BLE Hub address (not required if only one hub configured, else it must match the address of a configured BLE Hub)
  • channel: BLE Hub channel (must match a configured BLE Hub channel)
  • pwrPerc: Percentage of power to apply to the channel (can be negative for backward direction)
BLE Hub LED action

An action changing the state of the onboard LED of a PU Hub can be configured like so:

{
  "device": "bleHub",
  "address": "90:84:2b:0a:e3:73",
  "channel": "led",
  "color": "red"
}
  • device: espPin or bleHub (default: bleHub)
  • address: PU Hub address (not required if only one BLE Hub configured, else it must match the address of a configured PU Hub)
  • channel: led
  • color: none, off/black, pink, purple, blue, lightblue, cyan, green, yellow, orange, red or white

Uploading configuration files to the ESP-32

As mentioned earlier on this page the controller configuration files (network_config.json and controller_config.json) are not part of the firmware itself. They are stored in a special part of the flash memory of the controller called Serial Peripheral Interface Flash File System or SPIFFS for short. It’s a file system for flash devices and it uses a flat file structure. Conveniently PlatformIO supports it from its UI, but you can also target it from the command line interface.

To upload our controller configuration files to the ESP-32 successfully there are a few prerequisites:

  1. The workspace must be opened in PlatformIO;
  2. Both configuration files must be located in the same folder;
  3. The location of the folder must be configured in your platformio.ini;

Assuming you have the workspace of the project open in PlatformIO and you’ve created both your controller configuration files, we can continue the next step: put them in the right location. This can be any folder really, but we recommend creating a \data folder in the root of the project (the \data folder is ignored by the project completely).

Then you create a subfolder, let’s say MTC4BT1, within that \data folder. This will be the folder that we’re going to be uploading to the controller. If for any reason you want to make some changes to your controller configuration files, you can just create a copy of the folder within the \data folder, say MTC4BT2. This makes it very easy to switch between controller configurations (even while the controller is running!), because you only need to point PlatformIO to the new folder and upload the configuration to the controller. You can even do this over-the-air!

This will trigger a reboot of the controller and your new configuration will be active and running in a matter of seconds.

Now that we have prepared the folder that holds our controller configuration files, we can put the folder path in platformio.ini. One of the first settings in platformio.ini is called data_dir. This setting should point to the folder your put your controller configuration files in. If you followed the example above it’s value should be: $PROJECT_DIR/data/MTC4BT1

Assuming you have your controller connected to your pc and configured the correct COM port in platformio.ini, you can now upload your controller configuration files to the ESP-32. For this PlatformIO has a very handy feature.

Click on the PlatformIO icon in the left bar in VS Code. The PlatformIO “Project Tasks” pane will open. Click “Upload Filesystem Image” to upload your controller configuration files to the ESP-32 (see image). You can also opt for “Upload Filesystem Image OTA” if you want to do the update over-the-air. Just make sure your platformio.ini and your controller configuration are configured accordingly.

Compiling and uploading firmware to the ESP-32

If you’ve followed the steps above succesfully, compiling and uploading the firmware to the ESP-32 should be pretty straight forward. You need to make sure you:

  1. opened the workspace file in VS Code (not the repository root folder!);
  2. connected the ESP-32 to your pc;
  3. configured the correct COM port in the platformio.ini file;
  4. configured the correct board in the platformio.ini file;

For the controller to be able to boot after uploading the firmware, you also need to have uploaded a valid network_config.json and controller_config.json. If you boot the controller without configuration files and with invalid ones, the controller will report this in the serial monitor with a message like “Config file is empty”.

Assuming you’ve done all this, you can compile and upload the firmware with a few clicks.

Click on the PlatformIO icon in the left bar in VS Code. The PlatformIO “Project Tasks” pane will open. Click “Upload” to compile and upload the firmware to the ESP-32 (see image). You can also opt for “Upload and Monitor” if you want to automatically open the serial monitor after uploading.

The output will look like this while uploading the firmware:

Output while uploading firmware

If you had the serial monitor opened or have opened it manually, you should see the controller boot:

Controller boot sequence

Congratulations if you made it this far! If you have loaded a valid configuration into your controller, you should now be able to control your trains from Rocrail.

Known issues

Even though there is a physical limit of 9 connected BLE hubs per controller, there is currently a practical limit of around 6 BLE hubs, dependend on the types of hubs you connect. This is due to a lack of code optimizations for this scenario. The end goal is to utilize all nine available channels eventually so you can attach up to nine BLE hubs to a single controller. For now your mileage may vary.

We know this page is still far from complete. If you feel there’s something missing, you’re probably right as we’d like to know. Also if you got your trains running using the MTC4BT, we’d love to hear about it. Please let us know in the comments below or through the forum!