The JavaScript Evolution Of Open Source Robotics – cylonjs.com – Arduino



The JavaScript Evolution Of Open Source Robotics – cylonjs.com – Arduino

0 1


cylon-preso-jsconf-2014

Cylon.js Presentation At JSConf 2014

On Github hybridgroup / cylon-preso-jsconf-2014

The JavaScript Evolution Of Open Source Robotics

cylonjs.com

Good Morning

This is @JSConf

I am @deadprogram

Ringleader

@hybrid_group

The other guy is @adzankich

Serious Programming Guy

@hybrid_group

hybridgroup.com

kidsruby.com

JavaScript Robotics

The future is already here, it's just not very evenly distributed William Gibson

A JavaScript Framework for Robotics & Physical Computing

Multiple Hardware Devices

Different Hardware Devices

At the Same Time!

Cylon.js makes programming devices as easy as web development

Built-in API

Test-Driven Robotics

Command Line Interface

gort.io

RobotOps - DevOps For Robots

robotops.com

Show Me The Demo!

Arduino

var Cylon = require('cylon');

Cylon.robot({
  connection: { name: 'arduino', adaptor: 'firmata', port: '/dev/ttyACM0' },
  device: { name: 'led', driver: 'led', pin: 13 },

  work: function(my) {
    every((1).second(), function(){ my.led.toggle() });
  }
}).start();

Tessel

var Cylon = require('cylon');
 
Cylon.robot({
  connection: { name: 'tessel', adaptor: 'tessel', port: 'GPIO' },
  device: { name: 'led', driver: 'led', pin: 3 },
 
  work: function(my) {
    every((1).second(), function(){ my.led.toggle() });
  }
}).start();

BeagleBone Black

var Cylon = require('cylon');

Cylon.robot({
  connection: { name: 'beaglebone', adaptor: 'beaglebone' },
  device: { name: 'led', driver: 'led', pin: 'P9_12' },

  work: function(my) {
    every((1).second(), function(){ my.led.toggle() });
  }
}).start();

Choose Your Own Hardware Adventure!

To win a Sphero, tweet

@gosphero and @cylonjs

To win a Pebble, tweet

@pebble and @cylonjs

To win a Beaglebone Black, tweet

@beagleboardorg and @cylonjs

Sphero + Leap Motion

var Cylon = require('cylon');

Cylon.robot({
  connections: [
    { name: 'leapmotion', adaptor: 'leapmotion', port: '127.0.0.1:6437' },
    { name: 'sphero', adaptor: 'sphero', port: '/dev/rfcomm0' }
  ],
  devices: [
    { name: 'leapmotion', driver: 'leapmotion', connection: 'leapmotion' },
    { name: 'sphero', driver: 'sphero', connection: 'sphero' }
  ],
  work: function(my) {
    var y = 0;
    var x = 0;
    my.sphero.setBackLED(255)

    my.leapmotion.on('hand', function(hand) {
      y = hand.palmY.fromScale(50, 200).toScale(0, 90) | 0;
      x = hand.palmX.fromScale(-30, 50).toScale(0, 90) | 0;
    });

    every((1).second(), function() {
      my.sphero.setRGB(Math.floor(Math.random() * 16777216));
    });
    
    every((0.01).second(), function() {
      if (x != 0) {
        if (x > 45) {
          my.sphero.roll(40, x.fromScale(45, 90).toScale(90, 180) | 0);
        } else {
          my.sphero.roll(40, x.fromScale(0, 45).toScale(270, 359) | 0);
        }
      } else {
        if (y > 45) {
          my.sphero.roll(40, y.fromScale(45, 90).toScale(0, 90) | 0);
        } else {
          my.sphero.roll(40, y.fromScale(0, 45).toScale(180, 270) | 0);
        }
      }
    });
  }
})

Cylon.start();

Sphero + Robeaux

Conway's Game Of Life

Conway's Game Of Life... With ROBOTS!

var Cylon = require('cylon');

var bots = [
  { port: '/dev/rfcomm0', name: 'Thelma' },
  { port: '/dev/rfcomm1', name: 'Louise' },
  { port: '/dev/rfcomm2', name: 'Grace' },
  { port: '/dev/rfcomm3', name: 'Ada' },
  { port: '/dev/rfcomm4', name: 'Mary' }
];

var Green = 0x00FF00;
var Red = 0xFF0000;

var ConwayRobot = (function() {
  function ConwayRobot() {}

  ConwayRobot.prototype.connection = { name: 'Sphero', adaptor: 'sphero' };
  ConwayRobot.prototype.device = { name: 'sphero', driver: 'sphero' };

  ConwayRobot.prototype.born = function() {
    this.contacts = 0;
    this.age = 0;
    this.alive = true;
    this.life();
    this.move();
  };

  ConwayRobot.prototype.move = function() {
    this.sphero.roll(60, Math.floor(Math.random() * 360));
  };

  ConwayRobot.prototype.life = function() {
    this.alive = true;
    this.sphero.setRGB(Green);
  };

  ConwayRobot.prototype.death = function() {
    this.alive = false;
    this.sphero.setRGB(Red);
    this.sphero.stop();
  };

  ConwayRobot.prototype.enoughContacts = function() {
    return (this.contacts > 2 && this.contacts < 7);
  };

  ConwayRobot.prototype.birthday = function() {
    this.age += 1;

    console.log("Happy birthday, " + this.name + ". You are " + this.age + " and had " + this.contacts + " contacts.");

    if (this.enoughContacts()) {
      if (this.alive == false) { this.rebirth(); }
    } else {
      this.death();
    }

    this.contacts = 0;
  };

  ConwayRobot.prototype.work = function(me) {
    var self = this;
    me.sphero.on('connect', function() {
        me.sphero.detectCollisions();
        me.born();
    });

    me.sphero.on('collision', function() {
      self.contacts += 1;
    });

    every((3).seconds(), function() {
      if (me.alive != false) { me.move(); }
    });

    every((10).seconds(), function() {
      if (me.alive != false) { 
        me.birthday(); 
      }
    });
  };

  return ConwayRobot;

})();

for (var i = 0; i < bots.length; i++) {
  var bot = bots[i];
  var robot = new ConwayRobot;

  robot.connection.port = bot.port;
  robot.name = bot.name;

  Cylon.robot(robot);
}

Cylon.api({host: '192.168.1.116', port: '8080', ssl:  false});

Cylon.start();

Robeaux - Universal Robot API

ARDrone + OpenCV + PS3 Controller + Neurosky

var Cylon = require('cylon');

Cylon.robot({
  connections: [
    {name: 'opencv', adaptor: 'opencv'},
    {name: 'dualshock3', adaptor: 'joystick', controller: 'dualshock3'}, 
    {name: 'ardrone', adaptor: 'ardrone', port: '192.168.1.1'}
  ],
  devices: [
    {name: 'mat', driver: 'mat', conneciton: 'opencv' },
    {name: 'window', driver: 'window', conneciton: 'opencv'},
    {name: 'controller', driver: 'dualshock3', connection: 'dualshock3'}, 
    {name: 'drone', driver: 'ardrone', connection: 'ardrone'}
  ],
  validate_pitch: function(data, offset) {
    var value;
    value = Math.abs(data) / offset;
    if (value >= 0.1) {
      if (value <= 1.0) {
        return Math.round(value * 100.0) / 100.0;
      } else {
        return 1.0;
      }
    } else {
      return 0.0;
    }
  },
  work: function(my) {
    var self = this; 
    self.offset = 125.0;
    self.right_stick = {
      x: self.offset,
      y: self.offset
    };
    self.left_stick = {
      x: self.offset,
      y: self.offset
    };

    my.drone.getPngStream().on('data', function(png) {
      my.mat.readImage(png, function(err, img) {
        my.window.show(img);
      });
    });
    my.controller.on("square:press", function(data) {
      my.drone.takeoff();
    });
    my.controller.on("triangle:press", function(data) { 
      my.drone.hover()
    });
    my.controller.on("x:press", function(data) {
      my.drone.land()
    });
    my.controller.on("right:move", function(data) {
      self.right_stick = data;
    });
    my.controller.on("left:move", function(data) {
      self.left_stick = data;
    });
    every(0, function() {
      var pair;
      pair = self.left_stick;
      if (pair.y < self.offset - 5) {
        my.drone.front(my.validate_pitch(pair.y - self.offset, self.offset));
      } else if (pair.y > self.offset + 5) {
        my.drone.back(my.validate_pitch(pair.y - self.offset, self.offset));
      }
      if (pair.x > self.offset + 5) {
        my.drone.right(my.validate_pitch(pair.x - self.offset, self.offset));
      } else if (pair.x < self.offset - 5) {
        my.drone.left(my.validate_pitch(pair.x - self.offset, self.offset));
      }
    });
    every(0, function() {
      var pair;
      pair = self.right_stick;
      if (pair.y < self.offset - 5) {
        my.drone.up(my.validate_pitch(pair.y - self.offset, self.offset));
      } else if (pair.y > self.offset + 5) {
        my.drone.down(my.validate_pitch(pair.y - self.offset, self.offset));
      }
      if (pair.x > self.offset + 20) {
        my.drone.clockwise(my.validate_pitch(pair.x - self.offset, self.offset));
      } else if (pair.x < self.offset - 20) {
        my.drone.counterClockwise(my.validate_pitch(pair.x - self.offset, self.offset));
      }
    });
    every((0.1).seconds(), my.drone.hover);
  }
});

Cylon.start();
var Cylon = require('cylon');

Cylon.robot({
  connection:
    {name: 'neurosky', adaptor: 'neurosky', port: '/dev/rfcomm0'},

  device:
    {name: 'headset', driver: 'neurosky'},

  work: function(my) {
    my.headset.on('eeg', function(data) {
      Logger.info(data);
    });
  }
});

Cylon.start();

Pebble (Featuring Artoo) +

ARDrone + OpenCV +

Sphero + DigiSpark (Featuring Gobot)

artoo.io

require 'artoo'
require 'http'
 
connection :pebble, :adaptor => :pebble
device     :watch,  :driver  => :pebble, :name => 'pebble'
 
api :host => '0.0.0.0', :port => '8080'
 
name 'pebble'
 
def button_push(*data)
  unless data[1].nil?
    puts "Posting to Cylon"
    HTTP[:content_type => 'application/json'].get "https://192.168.1.3:3000/robots/drone/commands/faceOff"
    p "#{data[1]} button pushed"
  end
end
 
work do
  pebble.send_notification("Hello Pebble!")
  on pebble, :button => :button_push
end
var Cylon = require('cylon');
var rest = require('restler');
var haarcascade = "" + __dirname + "/haarcascade_frontalface_alt.xml";
 
var DroneRobot = (function(){ 

  function DroneRobot() {} 
 
  DroneRobot.prototype.commands = ["faceOff"];
 
  DroneRobot.prototype.connections = [
    { name: 'opencv', adaptor: 'opencv' }, 
    { name: 'ardrone', adaptor: 'ardrone', port: '192.168.1.1' }
  ];
  
  DroneRobot.prototype.devices = [
    { name: 'drone', driver: 'ardrone', connection: 'ardrone' }, 
    { name: 'window', driver: 'window', conneciton: 'opencv' }, 
    { name: 'mat', driver: 'mat', conneciton: 'opencv' }
  ];
 
  DroneRobot.prototype.work = function(my) {
    console.log("SHOW ME YOUR FACE!");
  };
 
  DroneRobot.prototype.faceOff = function() {
    var my = this;
    var self = this;
    self.detect = false;
    self.image = null;
    my.drone.getPngStream().on('data', function(png) {
      my.mat.readImage(png, function(err, img) {
        self.image = img;
        if (self.detect === false) {
          my.window.show(img);
        }
      });
    });
    my.mat.on('facesDetected', function(err, im, faces) {
      var biggest, center_x, f, face, turn;
      biggest = 0;
      face = null;
      for (var i = 0; i < faces.length; i++) {
        f = faces[i];
        if (f.width > biggest) {
          biggest = f.width;
          face = f;
        }
      }
      if (face !== null && (face.width <= 100 && face.width >= 45)) {
        rest.get('http://192.168.1.1:3000/robots/digispark/devices/led/commands/ToggleC').on('complete', function(result) {
          if (result instanceof Error) {
            console.log('Error:', result.message);
          } 
        });
        rest.get('http://127.0.0.1:3001/robots/mod/commands/WakeUp').on('complete', function(result) {
          if (result instanceof Error) {
            console.log('Error:', result.message);
          } 
        });
        im.rectangle([face.x, face.y], [face.x + face.width, face.y + face.height], 
          [0, 255, 0], 2);
        center_x = im.width() * 0.5;
        turn = -(face.x - center_x) / center_x;
        console.log("turning: " + turn);
        if (turn < 0) {
          my.drone.clockwise(Math.abs(turn * 0.7));
        } else {
          my.drone.counterClockwise(Math.abs(turn * 0.7));
        }
      } else {
        rest.get('http://192.168.1.1:3000/robots/digispark/devices/led/commands/OffC').on('complete', function(result) {
          if (result instanceof Error) {
            console.log('Error:', result.message);
          } 
        });
        rest.get('http://127.0.0.1:3001/robots/mod/commands/Snooze').on('complete', function(result) {
          if (result instanceof Error) {
            console.log('Error:', result.message);
          } 
        });
      }
      my.window.show(im);
    });
    my.drone.takeoff();
    after((8).seconds(), function() { my.drone.up(0.5); });
    after((10).seconds(), function() { my.drone.hover(); });
    after((13).seconds(), function() {
      self.detect = true;
      every(0.3.seconds(), function() {
        my.drone.hover();
        my.mat.detectFaces(self.image, haarcascade);
      });
      after((30).seconds(), function() { my.drone.land(); });
    });
  };
 
  return DroneRobot;
})();
 
var robot = new DroneRobot;
robot.name = "drone";
 
Cylon.robot(robot);
Cylon.api({ host: '192.168.1.3', port: '3000' });
Cylon.start();

gobot.io

package main
 
import (
  "github.com/hybridgroup/gobot"
  "github.com/hybridgroup/gobot-digispark"
  "github.com/hybridgroup/gobot-gpio"
)
 
func main() {
  master := gobot.GobotMaster()
  gobot.Api(master)
 
  digispark := new(gobotDigispark.DigisparkAdaptor)
  digispark.Name = "digispark"
 
  led := gobotGPIO.NewLed(digispark)
  led.Name = "led"
  led.Pin = "2"
 
  master.Robots = append(master.Robots, &gobot.Robot{
    Name:        "digispark",
    Connections: []gobot.Connection{digispark},
    Devices:     []gobot.Device{red},
  })
 
  master.Start()
}

Was that fun?

Join the Robot Evolution!

cylonjs.com

@cylonjs

artoo.io

@artooio

gobot.io

@gobotio

Thank you!

@deadprogram

@adzankich