Build a Phone-controlled Robot Using Node.js, RN-XV WiFly, Arduino and Twilio

June 27, 2012
Written by
Jonathan Gottfried
Contributor
Opinions expressed by Twilio contributors are their own

Twilio Bug Logo

Weigh in on the discussion on Hacker News!

This post uses the Twilio Node.JS Module and the Twilio Rest Message API

Robots have fascinated me for as long as I can remember, beginning with simple circuits and eventually moving on to PIC boards. Luckily for modern evil geniuses, creating basic robots has been made easier than ever by the Arduino and its awesome community of contributors. In this tutorial, I’m going to take you through the ins and outs of building a basic robot that you can wirelessly control from your phone’s dialpad using Twilio, Arduino, Node.js, and the RN-XV WiFly module. First things first, I’d like to show you what the finished robot looks like in action:
 

 

The Parts

I have a whole slew of spare parts sitting around that I used to build my robot. However, you don’t need everything I used in order to create your own phone-controlled Arduino with different functionality.

Required Parts (for basic WiFi functionality)Optional Parts (to build the robot)
1 Arduino Uno

1 Wireless SD Shield

1 RN-XV WiFly Module

1 Breadboard

1 LED

1 MaxBotix LV-EZ1 Range Finder

1 9V to Barrel Jack Adapter

1 Arduino/Breadboard Holder

1 Pack M/F Jumper Wires

1 Pack M/M Jumper Wires

1 Chassis Kit

2 HS-422 Servos Modded for Continuous Rotation

1 Pair of XWheels

 
Just a note, that the plastic chassis I’m using is no longer in production, but the one on the parts list is comparable and made by the same company. If you want to build the complete robot, you will need all of the parts listed or at least a comparable set. If you’d just like to experiment with a WiFi-enabled Arduino, you only need the required parts.

Wiring it Up

The wiring is relatively simple, and requires very little soldering.

Step 1: Plug the Wireless SD Shield into your Arduino

Wireless SD Shield Plugged Into Arduino Uno

Step 2: Plug your LED into digital pin 13 and GND. Note that the shorter pin on the LED is GND.

LED Plugged Into Arduino

Step 3a: Plug the +5V and GND lines from your two servo motors into the breadboard. I used the M/M jumpers to do this, since my servos had 3-prong female plugs to begin with.

M/M Jumpers Plugged Into Servo
+5V and GND Plugged Into Breadboard From Servo

Step 3b: Run M/M jumpers from the breadboard to the Arduino’s 5V and GND power slots.

+5V/GND Plugs From Breadboard to Arduino
M/M Jumpers From Breadboard Plugged Into Arduino +5V/GND

Step 3c: Connect your left servo’s signal wire into digital output 10 on your Arduino and your right servos’ signal wire into digital output 9.

Servos Connected to Arduino Digital Outputs

Step 4a: Connect your sonar sensor’s +5V power and GND to the breadboard. In order to do this, I soldered a set of headers onto the sonar module to make it easier to plug into. I then used the M/F jumpers to connect it to the breadboard.

Sonar +5V/GND Output to M/F Jumper
Sonar +5V/GND Plugged Into Breadboard via Jumper

Step 4b:  Connect your sonar sensor’s PWM output to digital input 5 on your Arduino.

Connect Another M/F Jumper to Sonar PWM Output
Plug Sonar PWM Output Into Arduino Digital Input 5

Step 5: Plug RN-XV WiFly module into Wireless SD Shield. Note that I would recommend doing this after you upload your program onto your Arduino, as I had a lot of issues uploading while the RN-XV module was plugged in.

RN-XV WiFly Plugged Into Wireless SD Shield

Step 6: This is where you would screw all of your parts to your chassis. I didn’t include step-by-step instructions for this part since it will vary depending on how you wish to set up your chassis, but on my model I have two wheels, each connected to one of the motors, and one free-spinning wheel in the front. The Arduino and breadboard are sitting in a SparkFun Arduino+Breadboard Holder and taped to the top of the bot. I also have a 9V battery taped to the bottom of the bot to power the Arduino.

Chassis Setup

The TwilioBot

Completed TwilioBot

Software Hacking – Arduino

The Arduino portion of the software is relatively simple. We’ll begin with our basic setup() method, adding only our WiFly setup functions to it. Note that we are using this WiFly library by jcrouchley. You will need to download it and put it in your Arduino IDE’s libraries directory. Once that is done, you can create a new sketch and start it off with the following code:

#include <SPI.h>
#include <WiFly.h>

char* ssid = "my$ssid$lol"; //enter your SSID here, replace all spaces with $ (ex. "my ssid lol" = "my$ssid$lol")
char* pass = "abc123"; //enter your wifi passphrase here

char* serverAddress = "1.2.3.4"; //enter the IP of your node.js server
int serverPort = 1337; //enter the port your node.js server is running on, by default it is 1337

WiFlyClient client;

void setup() {
  Serial.begin(9600);
  WiFly.setUart(&Serial);
  WiFly.begin();
  WiFly.join(ssid, pass, true);
  client.connect(serverAddress,serverPort);
}

void loop()
{
}

 
This code is mostly self-documenting, however the one area that confused me starting out was that you need to open a Serial port in order to use the WiFly module. This is due to the wiring of the Wireless SD Shield – it uses the Arduino RX/TX ports which are normally used for native Serial connections to instead communicate with the RN-XV (which is configured via commands sent over Serial). Because of this, we have to tell the WiFly library to explicitly use the native Serial object at 9600 baud.

The one possible issue to note is that you cannot use the Serial monitor or the Serial.print commands because the WiFly has taken over the usual port. With that sketch, you should be able to establish a WiFi connection and open a TCP socket to an arbitrary Node.js server (which we have not yet created). From there we can go ahead and add some code to our loop() to make the Arduino turn on its LED when it is connected to the Node.js server and to accept a TCP reply from the server.

void loop()
{
  //turn on the LED if the TCP socket is open
  if (client.connected()) {
    digitalWrite(ledPin, HIGH);
  }
  else {
    digitalWrite(ledPin, LOW);
  }
  
  //check for incoming data over TCP
  if (client.available()) {
    char c = client.read();
  }
}

We can then add this code to our setup() function to configure our LED pin-out:

const int ledPin = 13; //digital pin your LED is plugged into, I recommend 13 since its next to GND

WiFlyClient client;

void setup() {
  //...
  pinMode(ledPin, OUTPUT);
}

Now that we can establish a TCP connection and turn on and off a light to indicate an open TCP socket, we can dig into the fun part of this robot – movement! The first step is to add the following defines to the top of your sketch:

#include <Servo.h> 

const int rightservoPin = 9; //digital pin your right servo is plguged into
const int leftservoPin = 10; //digital pin your left servo is plguged into

const int rightStopPos = 91; //stop position of your right servo
const int leftStopPos = 91; //stop position of your left servo

Servo rightservo;
Servo leftservo;

 
The pin-outs are relatively straightforward, though you will also notice the rightStopPos and leftstopPos variables. These indicate the stopped positions of the continuous rotation servos.

Due to the way that continuous rotation servos are modified, their individual ‘stopped’ positions will usually be different. There is a useful snippet of code here that you can use to find your servo’s stop position.

Now you can go ahead and add the following code to your setup() function to configure our servo objects:

void setup() {
  //...
 
  rightservo.attach(rightservoPin);
  leftservo.attach(leftservoPin);
}

 
Now that we have our Servos configured, we can create some functions to control their movement. You should note that since the motors are on opposite sides of the robot in the same position, the left servo is actually reversed (backwards is forwards). You might also notice that position 180 is full speed forward and 0 is full speed backwards – this is specific to continuous rotation servos, on unmodified servos these will simply set the servo to a specific angular position.

void stopMovement() {
  rightservo.write(rightStopPos);
  leftservo.write(leftStopPos);
}

void turnLeft() {
  rightservo.write(180);
  leftservo.write(180);
}

void turnRight() {
  rightservo.write(0);
  leftservo.write(0);
}

//motors are reversed since they're on opposite sides of the bot, opposite directions make it go straight!
void moveForward() {
  rightservo.write(180);
  leftservo.write(0);
}

void moveBackward() {
  rightservo.write(0);
  leftservo.write(180);
}
 
You can also go ahead and add 'stopMovement();' to your setup() function so that the motors start out in the stopped position.
<pre class="lang:arduino decode:true">
void stopMovement() {
  rightservo.write(rightStopPos);
  leftservo.write(leftStopPos);
}

void turnLeft() {
  rightservo.write(180);
  leftservo.write(180);
}

void turnRight() {
  rightservo.write(0);
  leftservo.write(0);
}

//motors are reversed since they're on opposite sides of the bot, opposite directions make it go straight!
void moveForward() {
  rightservo.write(180);
  leftservo.write(0);
}

void moveBackward() {
  rightservo.write(0);
  leftservo.write(180);
}

 
The last step in setting up our motors is to set up the bot to expect and respond to specific commands from the server. Since we are controlling the bot via the Digits that the user presses in their call, our inputs are actually somewhat simple – a single number for each command.

void loop()
{
  //turn on the LED if the TCP socket is open
  if (client.connected()) {
    digitalWrite(ledPin, HIGH);
  }
  else {
    digitalWrite(ledPin, LOW);
  }
  
  //check for incoming data over TCP
  if (client.available()) {
    char c = client.read();
   
    //command handler (for dialpad digits)
    if (c == '2') {
      moveForward();
      client.print("forward"); 
    }
    else if (c == '4') {
      turnLeft();
      client.print("left");
    }
    else if (c == '6') {
      turnRight();
      client.print("right");
    }
    else if (c == '8') {
      moveBackward();
      client.print("back"); 
    }
    else if (c == '0') {
      stopMovement();
      client.print("stopped"); 
    }
  }
}

You can see here that in each iteration of the loop, we look for a TCP input from our server, and respond to it by moving in a given direction. We then send a text string as response to our Node.js server – this is currently not in use on the server-side but could be used in the future to confirm that an action occurred. And last but not least is the configuration of our Sonar module. This requires no special library and functions using basic digital I/O.
You will need to add the following defines to set up the MaxSonar input:

const int sonarPin = 5; //digital pin your sonar sensor is plugged into

long pulse, inches, cm;

void setup() {
  //...
  pinMode(sonarPin, INPUT);
}

Now that the sonar module is properly configured, we can go ahead and add a way for it to communicate with our server. To do this, we will simply reply with the distance detected, in inches, whenever the Arduino receives ‘5’ on TCP input. This goes in the loop() function:

if (c == '5') {
      pulse = pulseIn(sonarPin, HIGH);
      inches = pulse/147; //convert PWM output from sonar sensor to inches
      client.print(String(inches));
    } 

 
That’s all for the Arduino portion of our code, I encourage you to double check your sketch against the completed example here.

Software Hacking – Node.js Server

Now that our Arduino sketch is completed, we can go ahead and start writing the server portion of our little TwilioBot. I decided to write the server portion in Node.js simply due to the ease of maintaining a TCP socket with the Arduino and simultaneously communicating with Twilio in real-time.

There are only two dependencies which you can install with npm as follows:
npm install twilio-api@0.3.0 npm install express@">=3.0"

Now that our npm packages are installed, we can start to set up our app. The first thing to do is to log in to your account and navigate to your Dashboard. If you’re new to the platform, I recommend watching our first Quickstart tutorial for information on how to set up an account and purchase a phone number.

Once you have an account and a phone number, you will need to create a new TwiML App from the Apps tab in your Dashboard. You can then put your server URL (with port 8002) in the SMS and Voice Request URL fields and the Status Callback URL fields. It should look something like this:

TwiML App Setup

You can then head back to the Numbers tab and configure your phone number to use your newly created App:

Configure Your Phone Number

Now that your phone number is configured properly, you can start writing some JavaScript. In server.js, add the following code:

var express = require('express'), app = express.createServer(), net = require('net'), twilioAPI = require('twilio-api');

var cli = new twilioAPI.Client(process.env.account_sid, process.env.auth_token);

app.use(cli.middleware());
app.listen(8002);

var arduinoTcp = null;
var curr_call = null;

var tcpServer = net.createServer(function (socket) {
  console.log('tcp server running on port 1337');
});

 
This simply sets up a new express server on port 8002 for communicating with Twilio and a TCP server on port 1337 for communicating with your Arduino. Note that you will either need to modify the twilioAPI.Client call with your Account Sid and Auth Token or edit your environment variables and set your account_sid and auth_token (using the export command on Linux).

In this example, we are using the Node-Twilio-API package as express middleware – it will auto-generate all of your routes and TwiML responses.

Now go ahead and add the following JavaScript to the bottom of your server.js file. This portion of the script configures your existing TwiML App that we created above. Note that you will need to either modify the getApplication call with your app_sid or set the environment variable, as we did above.

var twilio_app = cli.account.getApplication(process.env.app_sid, function (err, app) {
  if (err) {
    throw err;
  }
  app.register();
  app.on('incomingCall', function (call) {
    if (arduinoTcp === null) {
      call.say("I can't do that for you, Hal. I'm offline.");
    } else {
      curr_call = call;
      call.gather(getDigits, {numDigits: 1}).say("Use 2, 4, 6, and 8 to drive. Press 0 to stop, 5 for distance.");
    }
  });
});

 
This snippet of code sets up our incomingCall callback. It detects whether or not our bot is connected to the Node.js server, and if it is, it Gathers the digits that are used to command the bot and passes them to the getDigits callback function. You might notice that we also set the curr_call global variable to store the current call objcet – this will be used later.

Now we can go create the getDigits callback function that we referenced in our initial TwiML:

function getDigits(call, input) {
  console.log('pressed ' + input);
  if (arduinoTcp === null) {
    call.say("I can't do that for you, Hal. I'm offline.");
  } else {
	curr_call = call;
	if (['2', '4', '5', '6', '8', '0'].indexOf(input) >= 0) {
	  arduinoTcp.write(input);
	  call.gather(getDigits, {numDigits: 1});
	} else {
	  call.gather(getDigits, {numDigits: 1}).say("I can't do that for you, Hal. Invalid command.");
	}
  }
}

This function again detects if our bot is connected, and also adds some additional validation of the digits pressed to make sure that the bot is able to process them. If it is a valid digit, we pass it along to our bot via our TCP connection and then Gather our next command digit.

Last but not least, we set up our TCP server that our bot connects to:

tcpServer.on('connection', function (socket) {
  console.log('num of connections on port 1337: ' + tcpServer.connections);
  arduinoTcp = socket;

  socket.on('data', function (mydata) {
    console.log('received on tcp socket:' + mydata);
    curr_call.load(function (err, call) {
      if (err) {
        throw err;
      }
      if (!isNaN(mydata)) {
        curr_call = call;
        call.liveCb(function (err, subcall) {
          if (err) {
            throw err;
          }
          subcall.gather(getDigits, {numDigits: 1}).say(mydata + " inches");
        });
      }
    });
  });
});
tcpServer.listen(1337);

 
We have a global variable (arduinoTcp) set to keep track of our bot’s socket, since this app is not able to control multiple robots. You will also notice that curr_call reappears here – this is so that we can interrupt our current call flow and pass along data from our sonar sensor. We refer to this as “modifying a live call” and it allows us to stop the call from gathering the next digit and instead use to output the number of inches our bot is away from the object in front of it, and then Gather more digits so that we can continue controlling it.

That’s all for the Node.js portion of our software, feel free to compare your code to the finished server.js script here.

That’s it for the software portion of the TwilioBot. You can find the full source on GitHub at jonmarkgo/TwilioBot. I encourage you to go out and build your own phone-controlled Arduino now! I welcome our robot overlords.

If you have any questions, feedback, or angry rants about how your robot locked you out of your house please feel free to Tweet @jonmarkgo or e-mail me at jonmarkgo@twilio.com