Lego Duplo Train Remote

Some time ago I saw instructions for a remote for the Lego Duplo train that comes with Bluetooth onboard. As we have the same train and the kids love to build some tracks (or have me build a nice course 😏), I wanted to build something similar.

What’s inside?

From ebay I got a Fischer old controller for model railroads and removed everything inside it. The board connecting to the train is an Espruino MDBT42Q that can be programmed via JavaScript. I have to say that keeping a connection from the Web IDE to this board was sometimes rough. I think for future projects I’ll prefer boards with a wired connection. Nonetheless, this was my first time tinkering with a microcontroller and I’m really happy with it! 🙌

To be independent from a power outlet, I added an Adafruit USB-C Charger with a battery. For the capacity I looked for what size best fits into my old controller. The 10kΩ potentiometer and a switch I reused from the old controller to turn it on and off add everything we need for functionality.

Light up!

Last minute I added a bright red LED to show when the remote is turned on — this was a great addition as the kids often leave the remote laying around and play somewhere else. Easy to spot with the red light that it’s still turned on. Phew.

It’s fun to drive the train! See yourself:

The inner part while turned on.
Looks quite organized, I’m proud about that. Doubted myself after previous projects…

The code

For completeness, here is the whole code. Taken & optimized from instructables. Thanks ThePlayshed!

var devices;
var gatt;
var connected = false;
var connecting = false;
var DevName="e0:7d:ea:0c:03:29";
var serviceUUID="00001623-1212-efde-1623-785feabcd123";
var characteristicUUID="00001624-1212-efde-1623-785feabcd123";
var Characteristic_store;
pinMode(D31, 'input_pulldown');
var prepval = new Uint8Array([0x0a, 0x00, 0x41, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01]);
var sendvalue;

function connect_train() {
  connecting = true;

  NRF.connect("18:04:ed:f5:cc:be").then(function(g) {
    console.log("Starting connecting2");
    gatt = g;
    return gatt.getPrimaryService("00001623-1212-efde-1623-785feabcd123");
  }).then(function(service) {
    return service.getCharacteristic("00001624-1212-efde-1623-785feabcd123");
  }).then(function(characteristic) {
    Characteristic_store = characteristic;
    return characteristic.readValue();
  }).then(value => {
    console.log(value);
  }).then(function() {
    console.log("Train Connected");
    connected = true;
    connecting = false;
  }).catch(function(error) {
    console.log('Error', error);
  });
}

function train_direction(dir_val) {
  sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0x00]);
  if (dir_val < 100) {
    console.log('ganz schnell rueckwaerts');
    sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0x9c]);
  } else if (dir_val >= 100 && dir_val < 200) {
    console.log('schneller rueckwaerts');
    sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0xce]);
  } else if (dir_val >= 200 && dir_val < 300) {
    console.log('rueckwaerts');
    sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0xe2]);
  } else if (dir_val >= 300 && dir_val < 400) {
    console.log('stopp');
    sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0x00]);
  } else if (dir_val >= 400 && dir_val < 475) {
    console.log('vorwaerts');
    sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0x1e]);
  } else if (dir_val >= 475 && dir_val < 550) {
    console.log('schneller vorwaerts');
    sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0x32]);
  } else if (dir_val >= 550) {
    console.log('ganz schnell vorwaerts');
    sendvalue = new Uint8Array([0x08, 0x00, 0x81, 0x00, 0x01, 0x51, 0x00, 0x64]);
  }
  Characteristic_store.writeValue(prepval).then(_ => {
    Characteristic_store.writeValue(sendvalue);
  });
}

setInterval(function() {
  var reading = analogRead(D31);
  if (connected) {
    train_direction(reading * 600);
  } else if (!connecting) {
    connect_train();
  }
}, 100);