Saturday, February 13, 2010

Connecting to the NXT via Bluetooth with Ubuntu

I recently decided to explore the possibility of controlling a NXT robot from a PC via Bluetooth. I have a super cheap Bluetooth USB dongle that I purchased for fewer than $5 from Fry's that I've never gotten to work under Windows XP with NXT-G, RobotC, or Bricx Command Center. However, Ubuntu (Karmic) finds the dongle automatically and connecting to the NXT is easy.

The first step is to make sure that your Bluetooth device is present:

$ hcitool dev
Devices:
    hci0    00:11:67:58:A7:7A

You can then ensure that your device is UP using:

$ hciconfig hci0
hci0:    Type: USB
    BD Address: 00:11:67:58:A7:7A ACL MTU: 678:8 SCO MTU: 48:10
    UP RUNNING PSCAN ISCAN
    RX bytes:4779 acl:74 sco:0 events:195 errors:0
    TX bytes:2263 acl:98 sco:0 commands:55 errors:0

To find the address of your NXT:

$ hcitool scan
Scanning ...
    00:16:53:01:53:38    NXT

The NXT exposes a serial port profile on channel 1. So to create a COM port connection to it (substitute your own address for mine):

$ rfcomm connect /dev/rfcomm0 00:16:53:01:53:38 1
Connected /dev/rfcomm0 to 00:16:53:01:53:38 on channel 1
Press CTRL-C for hangup

If your NXT hasn't been paired with your PC yet you should see a dialog requesting the NXT's PIN. The default is "1234". If the connection fails try again. If the connection worked you should see "*<>" in the top left of your NXT's LCD.

The specifics for talking to the NXT are well-documented in the "Bluetooth Developer KIt" that is available at http://mindstorms.lego.com/en-us/support/files/default.aspx.

The serial port configuration should be 9600baud, 1 stop bit, 8 bits per message, and no parity. Here is a sample Python script for reading the Firmware ID of your brick:

#! /usr/bin/python

import serial

# Configure and open the port.
port = serial.Serial()
port.setPort('/dev/rfcomm0')
port.setBaudrate(9600)
port.setStopbits(1)
port.setByteSize(8)
port.setTimeout(500)
port.setParity('N')
port.open()

# Send FW Version request.
port.write('\x02')
port.write('\x00')

port.write('\x01')
port.write('\x88')

# Response expected is [7,0][2,88,0,?,?,?,?]

result = port.read(9)
for c in result:
    print "%x" % ord(c)

port.close()

All of the packets sent and received via Bluetooth should start with a size packet that consists of two bytes. The first is the number of bytes in the command (NOT including the size packet, maximum of 64). The second is zero. So if we write the size packet (0x02,0x00) and then the firmware version command (0x01,0x88) we get back (0x07,0x00) for the size header, (0x02, 0x88, 0x00) for the response and status, and then four bytes for the firmware and protocol numbers.

$ ./HWID.py
7
0
2
88
0
7c
1
d3
1

If you've gotten this far you are ready to experiment with the rest of the commands in the Developer Kit. Have fun and remember to expect a 30-60mS latency between sending a command and receiving a response (if you are expecting one).