Arduino + Bluetooth + XBox 360 Controller = Fun!

Earlier this week I was asked to put together a robotics demo for the Electrical and Computer Engineering first year discipline night. This is the night where we try and entice the first years (Queen’s is a general first year) to choose ECE for their discipline for second and hopefully subsequent years. Now I could have ran any number of basic demonstrations that would have taken no time at all but of course I chose to take the opportunity to do something cool (well at least I think it is cool…).

Being the teaching assistant for ELEC 299, a second year course which uses mobile robots, I had access to some pretty cool hardware. I’d been playing with the robots for a while so I knew all the basic features now it was time to go above and beyond.

robot

I hooked up the bluetooth shield and started looking into how to send commands from my computer. I had a program from previous years in the course that did just this but it was in the form of a windows binary and I a) couldn’t be bothered to boot into windows and b) wanted to do it myself anyways. I googled around for a bluetooth library and decided on pybluez with python. It took me a bit to get set up to send data between the two but it wasn’t too tough. I borrowed a getch() class from stack overflow to facilitate only grabbing one key press at a time and then sent it to the robot. Of course I chose the familiar control scheme of W S A D`.

Here is the python code, its pretty simple:

from bluetooth import *
from getch import getch

MAC_ADR = "00:3C:B8:B1:14:22"

# Discovery
#print "performing inquiry..."
#nearby_devices = discover_devices(lookup_names = True)
#print "found %d devices" % len(nearby_devices)
#for name, addr in nearby_devices:
     #print " %s - %s" % (addr, name)

# Create the client socket
client_socket = BluetoothSocket( RFCOMM )
client_socket.connect((MAC_ADR, 1))

print "Connected"
print "Press 'q' to quit"

key = 0;
while key != 'q':
    key = getch() #gets 1 key only
    print key
    client_socket.send(key)

# Close the connection
client_socket.close()

and the getch class

class _Getch:
    """Gets a single character from standard input.  Does not echo to the screen."""

    def __init__(self):
      try:
          self.impl = _GetchWindows()
      except ImportError:
          self.impl = _GetchUnix()

    def __call__(self): return self.impl()

class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()

getch = _Getch()

After I got the connection stuff working and out of the way it was pretty simple to write an Arduino program to accept my input and respond accordingly.

But I wasn’t done just yet! I wanted to take my demo further and use an XBox 360 controller instead of the keyboard. It turns out this really wasn’t too hard I used pygame to read from the joystick and the rest is pretty much history. Now I will admit at this point I had pretty much proved my point and I needed to get back to more important work so the final result was a bit of a cop out. Rather than modify my Arduino program to read analog data over serial and really use the controller I simply mapped joystick values to W A S D in python before sending it over bluetooth. It would be really cool to come back and finish this properly but for now my demo was done.

XBox 360 Controller python code:

#!/usr/bin/env python

import bluetooth
import serial
import pygame
import time
import math

# init controller
pygame.init()
controller = pygame.joystick.Joystick(0)
controller.init()
print 'Xbox Controller Connected'


# Create the client socket
MAC_ADR = "00:3C:B8:B1:14:22"
client_socket = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
client_socket.connect((MAC_ADR, 1))
print "Bluetooth Connected"
print ' '
print ' '


print '/**************************/'
print 'Joystick Drive Program'
print "Press 'q' to quit"
print '/**************************/'

key = 0
y = 0
x = 0
while key != 'q':
    for event in pygame.event.get():
        if event.type == pygame.JOYAXISMOTION:
            if event.axis == 1:
                y = event.value
                if math.fabs(y) < 0.2:
                    y = 0
            if event.axis == 3: #4 in windows, 3 in linux
                x = event.value
                if math.fabs(x) < 0.2:
                    x = 0


    # send to arduino
    command = ' '
    if y < 0:
        command = 'w'
    elif y > 0:
        command = 's'
    elif x < 0:
        command = 'a'
    elif x > 0:
        command = 'd'

    print command
    client_socket.send(command)
    print client_socket.recv(1024)


# Close the connection
client_socket.close()

And finally the Arduino Program:

// bluetoothDrive
// Kevin Hughes
// 2012

// Motor Pins
int E1 = 6;
int M1 = 7;
int E2 = 5;
int M2 = 4;

void setup()
{
  // set pin modes
  pinMode(E1, OUTPUT);
  pinMode(M1, OUTPUT);
  pinMode(E2, OUTPUT);
  pinMode(M2, OUTPUT);

  // init
  Serial.begin(115200);
}


void loop()
{
  int command;
  if(Serial.available()) {
    command = Serial.read();

    // Moving
    if(command==119)
      driveForwards();
    if(command==115)
      driveReverse();
    if(command==97)
      turnLeft();
    if(command==100)
      turnRight();
    if(command==32)
      driveStop();

  }// end if
}


// Subroutines and Functions
void driveForwards() {
  digitalWrite(M1,HIGH);
  digitalWrite(M2,HIGH);
  analogWrite(E1,100);
  analogWrite(E2,100);  
}

void driveReverse() {
  digitalWrite(M1,LOW);
  digitalWrite(M2,LOW);
  analogWrite(E1,100);
  analogWrite(E2,100);
}

void driveStop() {
  digitalWrite(M1,HIGH);
  digitalWrite(M2,HIGH);
  analogWrite(E1,0);
  analogWrite(E2,0);
}

void turnLeft() {
  digitalWrite(M1,HIGH);
  digitalWrite(M2,LOW);
  analogWrite(E1,100);
  analogWrite(E2,100);
}

void turnRight() {
  digitalWrite(M1,LOW);
  digitalWrite(M2,HIGH);
  analogWrite(E1,100);
  analogWrite(E2,100);
}

Pretty simple really it just waits for a serial command, checks if it matches W A S or D and then executes the appropriate code.

Hope you enjoyed this, the demo was a hit at the discipline night!

read more

Just Finished my 2 first massive open online courses (MOOCs)

A while back I heard that Stanford was going to offer 3 of their 4th year / grad level computer science courses: Introduction to Artificial Intelligence, Machine Learning and Databases for free over the internet. This sounded like an opportunity not to be missed so I signed up for AI and Machine Learning. I’m not going to lie these courses kind of pushed my work load over the limit for the term as I was also enrolled in 2 graduate classes at my own university but it was totally worth it! So much learning!!!

Both classes were fantastic but I think I have to hand it to Machine Learning for being the single best class I’ve ever taken. I think it was the superbly done programming assignments that really made that class!

I want to post my hard-earned Statements of Accomplishment for these two amazing courses:

ml_cert

ai_cert

I really hope there are more classes like this in the future and that other Universities take note of the success Stanford had with this experiment. This could be the future of education!!!

read more