The infrared remote controlled LEGO train is out of view, but it needs to be stopped! How can we control a train if you can’t see it?

WiFi

Goal

  • Build a WiFi controlled LEGO Power Functions train.

Technologies

  • NodeMCU
    • ESP8266 WiFi dev board
  • Arduino IDE
    • Used to program NodeMCU
  • Node-RED
    • A platform to connect the Internet of Things
  • Raspberry-Pi
    • Single board Linux computer to run Node-RED
  • LEGO Power Functions & Train
    • LiPo battery powered motors

Overview

The ESP8266 is the greatest thing since sliced bread, for IoT projects. It’s basically an 802.11n WiFi chip with a microcontroller that provides GPIOs. The only issue with this is that it can be a pain to get going. The chip only supports 3.3v and requires some additional circuitry to program and connect to a USB interface. It’s doable, but the small chip expands into a larger circuit which increases overall install size (in development environments). The NodeMCU attempts to solve these problems by placing the ESP8266 on a dev board, that includes GPIO breakouts, a USB interface and power management.

By writing some minimal code on the chip, I can control a motor via a simple REST interface. Basically, I can just go to a web URL and something happens. 🙂

To provide a front-end and connect it to the Internet of Things, I will use Node-RED hosted on a Raspberry Pi. Like many of the other Internet of LEGO projects, Node-RED is used to send commands, collect data and build a simple UI to interact with the network.

A previous IoL article, Lego Train Automation – IR Power Functions, used the standard infrared receiver and a Raspberry Pi based transmitter to control the train. It worked really well and didn’t require any modifications to the train or controller for it to work. The problems started surfacing as I tried to control the trains when they were behind buildings or in a tunnel. Since there is a subway station and track intersections, I need to be able to control the train at all times.

Hardware

NodeMCU

http://nodemcu.com/index_en.html

The NodeMCU consists of a firmware and ESP8266 12-E development board. The system can be programmed using Lua script, but it can also be programmed using the Arduino IDE. Since many of my projects have been based on the classic Arduino dev boards, I will stick with what I know.

 

Node-MCU-Pin-Out-Diagram1

NodeMCU Motor Shield

The motor shield will provide a simple way to power the LEGO train motor. Since the microcontroller is not intended for higher voltages and current, a shield will be used to switch on the heavier loads. This version supports two motors and provides access to the remaining GPIO pins.

NodeMcuMotorshield

 

Both the NodeMCU and the motor shield were purchased on eBay for around $10.

NodeMCU with motorshield

Raspberry Pi

https://www.raspberrypi.org/

The Raspberry Pi will be used to host Node-RED and run a Mosquitto MQTT broker/bridge. It’s a small, inexpensive, single board computer that runs Linux. I have a Pi at the center of my IoL city, and use it for most of the high-level logic. Although it does have its own GPIOs, I find it better to use a microcontroller like an Arduino/ESP8266 for embedded projects and use the Pi for orchestration.

Pi2ModB1GB_-comp

LEGO Power Functions

The LEGO Group have released various versions of LEGO train electronics over the years. The latest version uses a LiPo battery, infrared controller and 7.4v motor. This project will effectively replace the infrared controller with a NodeMCU WiFi controller.

 

 

Wiring

  • Cut a LEGO power cable in half.
  • Connect the first LEGO half to the LiPo battery and connect the 9v and GND wires to the VIN and GND of the NodeMCU motor shield, respectively.
  • Connect the second LEGO half to the train motor and connect the C1 & C2 wires to the motor shield’s A+/A- connectors.
  • The B+/B- motor connectors will be used to power the cabin lights, but could be used in the future for a motorized decoupler or something else cool.

LEGO PF cable pinout picture

Installation

Build a LEGO train, then cram the components into it!

This is the high speed rail, Horizon Express, that I just acquired for the task. The train is hard to find, but is one of the few “expert” train sets released so I was happy to find one out of Hungary on Brick Owl!

 

This slideshow requires JavaScript.

 

Software

Here is the general flow for this entire system.

WiFi Train - Logic Flow

  • The Master Dashboard is the complete IoL City project, that will be able to leverage the WiFi controlled train. It will speak to the Local Dashboard using MQTT.
  • The Local Dashboard will be hosted on a Raspberry Pi and provide a simple UI. This will provide a fast and easy way to control the train.
  • The Controller is the NodeMCU which will provide a REST interface to allow the GPIOs to be manipulated.
  • The Horizon Express is the LEGO train with a motor and lights.

 

Programming the NodeMCU

Let’s start from the bottom up with the NodeMCU, which will control the train and define the API for the dashboard access.

To program the NodeMCU with the Arduino IDE, first install the extra support for the development board.

By using the Boards Manager in the Arduino IDE, search for ESP8266 and select install.

Arduino Boards Manager - ESP8266

Now you can select “NodeMCU 1.0” from the Tools –> Board menu.

Board menu

Controller Logic

The aREST library provides an easy way to create a REST interface for exposed functions and variables.

The ESP8266WiFi library takes care of all the WiFi connection mechanics.

  • This program starts by including our libraries and setting global settings.
  • It then defines the GPIO pins for the motor & lights and exposes variables & functions.
  • The system is then given an identity and connected to WiFi.
  • It then sits in a loop(), waiting for a REST command.
  • Once a command is received, the respective GPIO is adjusted and/or a status message is returned.

 

/*
  This program provides a WiFi connected REST interface for a LEGO Power Functions motor and lights.

  Written in 2016 by Cory Guynn under a GPL license.
  www.InternetOfLEGO.com
*/

// Import required libraries
#include <ESP8266WiFi.h>
#include <aREST.h>

// Create aREST instance
aREST rest = aREST();

// WiFi parameters
const char* ssid = "yourssid";
const char* password = "yourpassword";

// The port to listen for incoming TCP connections
#define LISTEN_PORT           80

// Create an instance of the server
WiFiServer server(LISTEN_PORT);

// Variables to be exposed to the API
int speed = 0; //stopped
int direction = 1; //forward
int lights = 1; //lights on

int motor = 5;
int led = 4;

void setup(void)
{
  // Start Serial
  Serial.begin(115200);

  // 4 pins control the motor shield.
  // Pins 4 and 5 control the speed of the motors with PWM in a default range of 0..1023
  // while pins 0 and 2 define the direction the motors will turn.
  // The second motor power source will be used for the cabin lights.
  pinMode(motor, OUTPUT);
  pinMode(0, OUTPUT);

  pinMode(led, OUTPUT);
  pinMode(2, OUTPUT);

  // initialize motor and set direction
  digitalWrite(motor, speed);
  digitalWrite(0, direction);

  // initialize lights
  digitalWrite(led, lights);
  digitalWrite(2, 1); // forward polarity through motor driver for LEDs

  // expose variables to REST API
  rest.variable("speed", &speed);
  rest.variable("lights", &lights);
  rest.variable("direction", &direction);

  // Function to be exposed to REST API
  rest.function("motor",motorControl);
  rest.function("lights",lightControl);

  // Give name and ID to device
  rest.set_id("1");
  rest.set_name("Horizon Express");

  // Connect to WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // Start the server
  server.begin();
  Serial.println("Server started");

  // Print the IP address
  Serial.println(WiFi.localIP());
}

void loop() {

  // Handle REST calls
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  while(!client.available()){
    delay(1);
  }
  rest.handle(client);

}

// Custom function accessible by the API
int lightControl(String command) {

  // Get new state from command
  lights = command.toInt();

  digitalWrite(led,lights);
  return 1;
}

int motorControl(String command){

  // set motor direction
  if (command.toInt() < 0) { // reveresed motor
      direction = 1; //forward
    } else {
      direction = 0; //reverse
  }

  speed = abs(command.toInt());
  analogWrite(motor, speed);
  digitalWrite(0, direction);
}

Once the code has been configured with your settings and uploaded to the NodeMCU, the serial monitor will show that it is connected and the IP address you can use to contact it.

WiFi Train - Serial Monitor

Local Dashboard with Node-RED

http://nodered.org/

Node-RED is “a visual tool for wiring the Internet of Things”, which is built on NodeJS. It is also now included with the default Raspberry Pi image, Raspbian “Jessie”.

The basic concept is to create input nodes (buttons, PubNub, MQTT, Tweet, etc) and pass along the msg object. Function nodes (JavaScript function, JSON converter, switches) will then manipulate the object so that it can be formatted or be used to make decisions for the next flow step. HTTP request nodes will be used to send the GET requests to the NodeMCU. There are various green debug nodes, that will display the state of the msg object in the debug console.

 

WiFi Train - Node-RED local flow and debug

Flow Overview

Download Source

  • Copy and paste the flow source code using the Import –> Clipboard option within Node-RED and paste in the JSON flow.

This Node-RED “flow” is pretty straight-forward. Since we built a REST interface for the train, we just need to send a command by hitting a web URL. The input nodes will inject a “topic” and “payload” corresponding to the device and command.

There are also MQTT inputs and outputs so that this system can be managed from a cloud Pub/Sub model. This allows the Raspberry Pi to run in a private network with a random IP address and still be accessible from a dashboard server or other system in a different network.

HTTP Request

The http request node is how the command gets sent to the train.

The topic will indicate the device (i.e. motor or lights) and the payload will indicate the command.

node-red http request

 

http://yourserver/{{{topic}}}?params={{{payload}}}

Example Commands

  • Motor Forward: http://yourserver/motor?params=1023
  • Motor Reverse: http://yourserver/motor?params=-1023
  • Motor Stop: http://yourserver/motor?params=0
  • Lights On: http://yourserver/lights?params=1023
  • Lights Off: http://yourserver/lights?params=0

UI

There are several blue inject buttons, to control the direction, throttle and brake directly from Node-RED. This is handy for testing.

By using the “node-red-contrib-ui” UI nodes, I can create a simple and attractive AngularJS interface to send these commands.

 

Button Row

button group node - motor

This node will create a group of buttons using AngularJS material icons.

WiFi Train - UI button group

 

 

Status

status flow

In order to update the light switch and speed gauge, the current status needs to be known.

To pull the status, we just need to go to the root URL.

http://yourNodeMCU-IP/

 

This status info will be used to update the state of the UI.

Function nodes are used to extract the status variables I am concerned with and forward them on to the next node.

function - train status movingfunction - train status stopped

// update trainStatus indicator
if(msg.payload.variables.speed !== undefined){
    if(msg.payload.variables.speed !== 0){
        node.status({fill:"green",shape:"ring",text:"moving "+ msg.payload.variables.speed+":"+msg.payload.variables.direction});
    }else{
        node.status({fill:"red",shape:"ring",text:"stopped"});
    }
}else{
    node.status({fill:"yellow",shape:"ring",text:"unknown"});
}


return msg;

 

// format UI
msg.payload = msg.payload.variables.lights || 0;
return msg;

 

I also update the throttle gauge by scaling it first.

// format throttle for UI gauge

// convert the speed from an absolute value with direction
// to a negative/positive number
var speed = msg.payload.variables.speed;
var direction = msg.payload.variables.direction;

if (direction === 1){
    msg.payload = -1*speed;
}else{
    msg.payload = speed;
}
return msg;

Master Dashboard

Download Source

I added MQTT flows to send commands and receive updates for the wider IoL City project. This allows me to control the train from anywhere in the world. I have a much larger Ubuntu machine that runs the Node-RED “IoL Dashboard” and currently runs a Mosquitto MQTT broker. With this dashboard, I can tie all of the city sensors and functions together. I also run PubNub on this dashboard which communicates with several IoT devices that can be used to trigger events.

WiFi Train - Node-RED Master Dashboard

 

Now I can really do some fun IoT stuff, such as linking the train routine to a schedule or run two trains on a shared track. I can also get notifications when the status of the train changes or have it run when a BLE beacon is detected. Stay tuned for future enhancements!

Success!

Now I can easily control the Horizon Express train using WiFi!

Gallery

Using NodeMCU (ESP8266 dev board)Node-RED

Posted by Internet of Lego on Friday, April 8, 2016