How to make a voice controlled drone

Build a voice controlled rolling-spider with nodejs, socket.io, and annyang.js

Drones are becoming more popular these days, and it's getting easier to find one at a reasonable cost.

For $99, you can buy a Parrot Rolling Spider at Best Buy or Amazon.

And what's awesome about that is that they're controlled with bluetooth technology. And what's even more awesome is people like Voodootikigod did a bunch of ground work to translate the bluetooth signals from the mini-drone and made a node module that we can use to control our drone in javascript.

To get started, there are a few requirements to successfully use this demo:

The basic idea is we're going to use the Web Speech API via annyangjs in the browser which will then emit a message through websockets, which finally will use the rolling-spider library to send bluetooth commands to our mini drone.

So, to get started, we just want to build a simple nodejs application that will serve up an html page. (i use express for convenience, but you could be more simple than that as well).

app.js file:

var express = require('express');
var app = express();
var server = require('http').Server(app);

app.set('port', process.env.PORT || 3000);
app.use(express.static('public'));
app.get('/', function (req, res) {
  res.sendFile(__dirname + '/public/index.html')
});


server.listen(app.get('port'), function () {
  console.log('server listening on ' + app.get('port'));
});

Basically all this app.js file is doing is serving index.html on port 3000, as of now, but in order for us to listen through web sockets, we need to set that up:

var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);

// here we add our node-rolling-spider module as well and create an instance
var Drone = require('rolling-spider');

var spidey = new Drone(process.env.UUID)

app.set('port', process.env.PORT || 3000);
app.use(express.static('public'));
app.get('/', function (req, res) {
  res.sendFile(__dirname + '/public/index.html')
});


server.listen(app.get('port'), function () {
  console.log('server listening on ' + app.get('port'));
})

io.on('connection', function (socket) {
  // we'll be adding here shortly

});

Now that we have an instance of our drone, the rolling spider module has a very straightforward api. So if you want to turn left, you simply do something like drone.turnLeft({STEPS: 20}) or takeoff drone.takeoff()

I've abstracted away all of the commands to takeoff, turn, and flip into a separate file called droneControl.js.

This is what our drone controlling code looks like:

module.exports = (function () {
  'use strict';
  var ACTIVE = true;
  var STEPS = 5;
  var commands = {
    left: function (drone) {
      console.log('going left');
      drone.turnLeft({ steps: 20 });
      this.cooldown();
    },
    right: function (drone) {
      console.log('going right');
      drone.turnRight({ steps: 20 });
      this.cooldown();
    },
    up: function (drone) {
      console.log('up up');
      drone.up({ steps: STEPS  2.5 });
      this.cooldown();

    },
    down: function (drone) {
      console.log('down down');
      drone.down({ steps: STEPS  2.5 });
      this.cooldown();
    },
    forward: function (drone) {
      console.log('going forward');
      drone.forward({ steps: 20 });
      this.cooldown();
    },
    backward: function (drone) {
      console.log('going backward');
      drone.backward({ steps: 20 });
      this.cooldown();
    },
    flip: function (drone) {
      console.log('flippin awesome!');
      drone.frontFlip({ steps: STEPS });
      this.cooldown();
    },
    launch: function (drone) {
      drone.connect(function () {
        drone.setup(function () {
          console.log('Prepare for take off! ', drone.name);
          drone.flatTrim();
          drone.startPing();
          drone.flatTrim();

          drone.on('battery', function () {
            console.log('Battery: ' + drone.status.battery + '%');
            drone.signalStrength(function (err, val) {
              console.log('Signal: ' + val + 'dBm');
            });

          });
          setTimeout(function () {
            drone.takeOff();
            ACTIVE = true;
          }, 1000);

        });
      });

    },
    land: function (drone) {
      console.log('landing!!');
      drone.land();
      setTimeout(function () {
      process.exit();
      }, 3000);
    },
    cooldown: function () {
      ACTIVE = false;
      setTimeout(function () {
        ACTIVE = true;
      }, STEPS * 12);
    },
    shit: function (drone) {
      drone.emergency();
      setTimeout(function () {
        process.exit();
      }, 3000);
    }
  };
  return function(droneInstance, action) {
    return commands[action](droneInstance)
  };

})();

I did this to make it factor out the controlling code from the socket listeners, so you could use it directly in app.js like this:

var droneControl = require('./droneControl');

droneControl(spidey, 'launch');
droneControl(spidey, 'left');

Doing this allows us to write one (or two) socket listeners, so below is our final app.js:

var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var Drone = require('rolling-spider');
var droneControl = require('./droneControl');

var choppah = new Drone(process.env.UUID);
app.set('port', process.env.PORT || 3000);
app.use(express.static('public'));
app.get('/', function (req, res) {
  res.sendFile(__dirname + '/public/index.html')
});


server.listen(app.get('port'), function () {
  console.log('server listening on ' + app.get('port'));
})

io.on('connection', function (socket) {
  socket.on('choppah', function (data) {
    console.log('choppha!', data.action);
    droneControl(choppah, data.action);
  });
  socket.on('choppah:launch', function () {
    droneControl(choppah, 'launch')
  })
});

Now that we have our nodejs side complete, we simply need to create an index.html and main.js.

For the voice control, we are using annyangjs which basically will wrap the Web Speech API native in chrome and gives us an easy way to write commands to listen for to trigger our socket emitters.

Here's the index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>chopper!</title>
  </head>
  <body>
    <h1>hello choppa!</h1>
    <script src="//cdnjs.cloudflare.com/ajax/libs/annyang/2.0.0/annyang.min.js">

    </script>
    <script src="https://cdn.jsdelivr.net/socket.io-client/1.3.2/socket.io.js">

    </script>
    <script src="main.js" charset="utf-8"></script>
  </body>
</html>

And finally, main.js which detects voice commands and emits them with socket.io:

var socket = require('socket.io-client')('http://localhost:3000');

var commands = {
    'get to the choppa': function (data) {
      socket.emit('choppah:launch', {msg: "get to da choppa!"});
    },
    'go *action': function (action) {
      console.log("action: ", action);
      socket.emit('choppah', {action: action});
    }
  };

  annyang.addCommands(commands);
  annyang.start();

Now, you just need to run your app and fire up your browser, then talk to it!

Start the app: node app.js

Open Browser (I'm using Chrome) http://localhost:3000

Then say, 'get to da choppa' to launch, and go left, go up, and any other directional commands. To stop the app and land the mini drone, simply say - 'go land';

Here's the demo code

comments powered by Disqus