pbLua Bluetooth Remote

Introduction

My friend Bryan was trying to build a robot that uses Bluetooth communication to receive commands from another NXT. Actually, what he wanted to do is send raw data to two NXTs from a third on using Bluetooth.

Normally, he uses NXT-G for this kind of thing, but apparently it’s not too easy to send data data to two NXTs because you have to keep setting up a new channel every time.

Here’s where pbLua can really shine. The Bluetooth API will let you send to one of 4 channels, and receive on one. For this application that’s all we need to get going.

It’s a bit tricky to set up a pairing between NXTs the first time using pbLua – but after that you just use the nxt.BtConnect() function and you’re away to the races. Follow along in this tutorial and you’ll soon be be an expert with Bluetooth and the NXT.

By the end of this tutorial you’ll know how to:

  1. Pair your NXT with your computer
  2. Pair multiple NXTs with each other
  3. Send data from one NXT to multiple NXTs
  4. Autostart a pbLua script

Note that this tutorial just gets you started. You’ll have to devise your own applications that fit into the framework outlined here.

Contents

Pairing each NXT with a Computer

The first thing we need to do is start with a fresh slate, so make sure your NXT has the latest and greatest pbLua firmware loaded and let’s get started.

A factory fresh NXT has not been paired with any other devices, and may actually have the Bluetooth radio turned off. To remove any confusion as we run through the tutorial, I’ll show you how to get the BT subsystem on the NXT into a factory fresh state so we’re all starting from the same place.

Here’s how to do it. Connect the NXT to your host machine using the USB cable and turn on the NXT. Your NXT should connect to the computer and a new serial device should be available on your computer.

Now fire up your favorite terminal emulator and connect to the serial device that is your NXT. First, let’s make sure the BT radio is turned on.

-- Turn the Bluetooth radio on:
nxt.BtPower(1)

Then reset the radio to a factory fresh state…

-- Reset the Bluetooth subsystem to factory defaults. Note theat this does
-- not reset the firmware, just the device tables. 
nxt.BtFactoryReset()

Wait a few seconds to let it finish. Please note that the BT radio is left in the off state after a factory reset, so you’ll have to power the BT radio on before starting the pairing process, like this…

-- Turn the Bluetooth radio on:
nxt.BtPower(1)

And then make it visible so that it can be found by other Bluetooth devices…

-- make the NXT visible for Bluetooth searches:
nxt.BtVisible(1)

Now give your newly recognized NXT a name. We’ll be setting up three different NXTs in this example – one to drive the motors on the left side of the robot (LEFTY), one to drive the motors on the right side (RIGHTY) and a third NXT that will be the remote controller (CONTROL)

-- Change the name of the NXT
nxt.BtSetName("LEFTY")

Now, we’ll go a head and pair each NXT with your computer. Every computer is different, so the next step is up to you to figure out. Begin the pairing process on your host computer by searching for new Bluetooth devices. It should find the NXT that’s turned on – let’s start with LEFTY.

If LEFTY is found, continue with the pairing process. There should be a request for a pairing code – usually an easy to remember 7 digit number like your phone number. It’s a good idea to use the same paring
code for all your NXTs so that you won’t forget it. You can also use a string of characters like “LMNOP” if you like.

After entering the pairing code on the host computer, enter the pairing code on your NXT. Recall that the pairing code is not actually a number, it’s a string of characters. If you pairing code is 5551212 then enter the following code to the console:

-- Enter the PIN code for your NXT
nxt.BtSetPIN("5551212") 

Repeat each of this step for the other two robots, RIGHTY and CONTROL.

Pairing LEFTY and RIGHTY with CONTROL

Now that we’ve had some practice pairing the three NXTs with the host, we’ll move on to pairing the NXTs with each other. The step of pairing with the host is not strictly necessary, but now you have a way to program your NXTs using pbLua without the requirement of a USB connection.

You may need a 4 port USB hub or multiple computers if you don’t have 3 USB ports handy on your computer.

Start this step by powering off all of the NXTs, and then connecting them to the host computer by means of the USB cables. Don’t worry, eventually we’ll be running untethered! It’s way easier to debug this if we’re all conencted.

The NXTs should have a separate console connection to each of them. You’ll be sending commands to each of the NXTs separately, so be sure you’re clear on which console is connected to which NXT!

The first thing to do is look at the current pairing data in each NXT. There should only be one pairing connection, and that’s to the host. Copy and paste the following code to each console:

-- btDevice(n) dumps the first n entries in the Bluetooth device table

function btDevice( n )
  for idx=0,n-1 do
    name, class, addr, status = nxt.BtGetDeviceEntry( idx )
   
    -- Format the BT device address
    addr = string.format("%02x:%02x:%02x:%02x:%02x:%02x", string.byte( addr, 1, 6 ) )
   
    -- Format the BT class
    class = string.format("%02x:%02x:%02x:%02x", string.byte( class, 1, 4 ))

    -- Print the device info
    print(string.format("Name:%16s Addr:%s Class:%s Status:%i",name,addr,class,status))
  end
end

And then review the connection table on CONTROL, like this:

-- Dump the first 4 entries in the device table
btDevice(4)

-- Results are below, do not paste the following text to the console!
Name:        DELLD610 Addr:00:10:c6:62:f6:ba Class:00:02:01:04 Status:2
Name:      BT GPS V10 Addr:00:0a:3a:24:33:97 Class:00:00:1f:00 Status:130
Name:                 Addr:00:00:00:00:00:00 Class:00:00:00:00 Status:0
Name:                 Addr:00:00:00:00:00:00 Class:00:00:00:00 Status:0

The BT GPS V10 is the little Bluetooth GPS device I used in the tutorial on interfacing the NXT to a GPS device.

It turns out that we can do the device search and pairing from the CONTROL node. Do a search for new devices and make sure that LEFTY and RIGHTY show up in the device table. Make sure you wait about 30 seconds after doing the search before sending anything else to the CONTROL node.

-- Search for the NXT
nxt.BtSearch(1) -- This takes about 20 seconds!

Make sure you wait about 30 seconds after doing the search before sending anything else to the CONTROL node.

-- Dump the first 4 entries in the device table
btDevice(4)

-- Results are below, do not paste the following text to the console!
Name:        DELLD610 Addr:00:10:c6:62:f6:ba Class:00:02:01:04 Status:66
Name:      BT GPS V10 Addr:00:0a:3a:24:33:97 Class:00:00:1f:00 Status:130
Name:           LEFTY Addr:00:16:53:09:f7:59 Class:00:00:08:04 Status:130
Name:          RIGHTY Addr:00:16:53:09:f9:26 Class:00:00:08:04 Status:65

If LEFTY and RIGHTY are not both visible, make sure they are turned on and try again.

Now, the next steps have to be executed in about 20 seconds, so have them prepared to cut and paste, and make sure you’re talking to the right node! Basically we’re going to:

  1. Start the connection on the CONTROL node
  2. Wait 5 seconds and enter the PIN code on the CONTROL node
  3. Enter the PIN code on the RIGHTY node

Here are the steps all in one code box. Read the comments carefully so you’re pasting the right line to the correct node!

-- Start the connection on CONTROL between channel 1 and device 3 (RIGHTY)
nxt.BtConnect(1,3)

-- Wait 5 seconds before entering the PIN on CONTROL
nxt.BtSetPIN("5551212")

-- Now enter the PIN on RIGHTY
nxt.BtSetPIN("5551212")

Double check that you’re connected by printing the connection table again, reload the btConnect() function if necessary…

-- Check the connection table on CONTROL
btConnect()

-- Results are below, do not paste the following text to the console!
Name:                 Addr:00:00:00:00:00:00 Class:00:00:00:00 PIN: Status:0
Name:          RIGHTY Addr:00:16:53:09:f9:26 Class:00:00:08:04 PIN:5551212 Status:0
Name:                 Addr:00:00:00:00:00:00 Class:00:00:00:00 PIN: Status:0
Name:                 Addr:00:00:00:00:00:00 Class:00:00:00:00 PIN: Status:0

If you’re observant, you’ll see that the PIN code is printed in the connection status. That’s only there the first time you exchange PIN codes. The next time you power up the NXT, it’s invisible.

Repeat this section for the other NXT, and we’ll be ready to do the next step, which is to set up LEFTY and RIGHTY to enter a loop that looks for data on the Bluetooth channel and just print it on the display.

Connecting LEFTY and RIGHTY with CONTROL

Start this step by powering off all of the NXTs, and then connecting them to the host computer by means of the USB cables. Don’t worry, eventually we’ll be running untethered! It’s way easier to debug this if we’re all conencted.

You may need a 4 port USB hub or multiple computers if you don’t have 3 USB ports handy on your computer.

Now power them on, load up the btDevice() script and review the BT device list on the CONTROL device. It should look something like this:

-- Check the device table on CONTROL
btDevice(4)

-- Results are below, do not paste the following text to the console!
Name:        DELLD610 Addr:00:10:c6:62:f6:ba Class:00:02:01:04 Status:2
Name:      BT GPS V10 Addr:00:0a:3a:24:33:97 Class:00:00:1f:00 Status:2
Name:           LEFTY Addr:00:16:53:09:f7:59 Class:00:00:08:04 Status:2
Name:          RIGHTY Addr:00:16:53:09:f9:26 Class:00:00:08:04 Status:2

Now paste the following identical code into LEFTY and RIGHTY. Basically it’s a loop that reads data from the Bluetooth radio and spits it out to the console. To stop the loop, just press the big orange button on the NXT.

-- Sit in a loop and spit out anything from the Bluetooth radio to the
-- console...copy this to LEFTY and RIGHT

function BtListen() 
  -- Make sure we're reading the raw stream, not NXT messages
  nxt.BtStreamMode(1)

  -- Spin in a loop and echo any data we get until the big
  -- orange button is pressed.
  repeat
    s = nxt.BtStreamRecv()
    if s then
      print( s )
    end
  until( 8 == nxt.ButtonRead() )
end

We’ll just set up a little sample program that shoots messages at LEFTY and RIGHTY on separate control channels until the user presses the big orange button. By now, you’ll have noticed that I’m a big fan of incremental testing so that we’re sure of what works and what doesn’t before moving on.

Set up the connections to LEFTY and RIGHTY by pasting this code into CONTROL. Remember to wait 5 seconds between connections:

-- A little piece of code to wait until the BT system is idle...
function BtIdleWait()
    local active
    repeat
      _,active = nxt.BtGetStatus()
    until 17 == active
end

-- Set up the connection to LEFTY on channel 1 and device 2 (your device
-- number may be different!
nxt.BtConnect(1,2)

-- Wait until idle
BtIdleWait()

-- Set up the connection to RIGHTY on channel 2 and device 3 (your device
-- number may be different!
nxt.BtConnect(2,3)

And load the following test code into the CONTROL node:

function BtStream() 
  -- Make sure we're reading the raw stream, not NXT messages
  nxt.BtStreamMode(1)
 
  local i = 0

  repeat
    nxt.BtStreamSend(1,"LEFTY"  .. i)
    nxt.BtStreamSend(2,"RIGHTY" .. i)
    i = i+1
  until( 8 == nxt.ButtonRead() )
end

Now run the BtListen() on LEFTY and RIGHTY and then run BtStream() on CONTROL:

-- Start listening on LEFTY by typing this in the LEFTY console:
BtListen()

-- Start listening on RIGHTY by typing this in the RIGHTY console:
BtListen()

-- Start streaming by typing this in the CONTROL console:
BtStream()

And watch the text scroll by on the consoles of LEFTY and RIGHTY! Just press the orange button on all three NXTs when you get bored.

Autostarting the Applications

Now we’re down to the last step, which is burning our application into FLASH so that it runs when the NXT is booted up. You may want to review the tutorial on the pbLua File System if you get stuck.

Basically, we’re going to modify the BtListen() program to put its results on the display, and then write it into FLASH so that when the NXT boots, it runs the program automatically.

Here’s the code for the LEFTY and RIGHTY nodes:

-- Copy the BtListen() function into FLASH on LEFTY and RIGHTY and run
-- it at startup

-- Create the program as an extended string...
s = [[
function BtListen() 
  -- Make sure we're reading the raw stream, not NXT messages
  nxt.BtStreamMode(1)

  -- Spin in a loop and echo any data we get until the big
  -- orange button is pressed.
  repeat
    s = nxt.BtStreamRecv()
    if s then
      nxt.DisplayScroll()
      nxt.DisplayText(s)
    end
  until( 8 == nxt.ButtonRead() )
end

repeat
  BtListen()

  -- Wait for button to be released 
  repeat
  until( 0 == nxt.ButtonRead() )

  -- Wait for button to be pressed
  repeat
  until( 8 == nxt.ButtonRead() )

  -- Wait for button to be released
  repeat
  until( 0 == nxt.ButtonRead() )
until false
]]

-- Create the file, save the handle so we can use it later...
h = nxt.FileCreate( "pbLuaStartup", string.len(s) )

-- Now write the string...
nxt.FileWrite( h, s )

-- And close the file...
nxt.FileClose( h )

-- The next time you boot LEFTY or RIGHTY, this program will run, even
-- if you're not connected to the console

And the code for CONTROL:

-- Copy the BtStream() function into FLASH on CONTROL and run
-- it at startup

-- Create the program as an extended string...
s = [[
function BtIdleWait()
    local active
    repeat
      _,active = nxt.BtGetStatus()
    until 17 == active
end


function BtStream() 
  nxt.BtDisconnectAll()
  
  -- Wait until idle
  BtIdleWait()

  -- Set up the connection to LEFTY on channel 1 and device 2 (your device
  -- number may be different!
  nxt.BtConnect(1,1)

  -- Wait until idle
  BtIdleWait()

  -- Set up the connection to RIGHTY on channel 2 and device 3 (your device
  -- number may be different!
  nxt.BtConnect(2,2)

    -- Wait until idle
  BtIdleWait()

  --Make sure we're reading the raw stream, not NXT messages
  nxt.BtStreamMode(1)
 
  local i = 0

  repeat
    nxt.BtStreamSend(1,"LEFTY"  .. i)
    nxt.BtStreamSend(2,"RIGHTY" .. i)
    i = i+1
  until( 8 == nxt.ButtonRead() )
end

repeat
  BtStream()

  -- Wait for button to be released 
  repeat
  until( 0 == nxt.ButtonRead() )

  -- Wait for button to be pressed
  repeat
  until( 8 == nxt.ButtonRead() )

  -- Wait for button to be released
  repeat
  until( 0 == nxt.ButtonRead() )
until false
]]

-- Create the file, save the handle so we can use it later...
h = nxt.FileCreate( "pbLuaStartup", string.len(s) )

-- Now write the string...
nxt.FileWrite( h, s )

-- And close the file...
nxt.FileClose( h )

-- The next time you boot CONTROL, this program will run, even
-- if you're not connected to the console

Now all you have to do is power off the NXTs, disconnect the USB cables and power on LEFTY and RIGHTY and then CONTROL a few seconds later. You’ll see the startup window spin around waiting for you to select a console. If you don’t select one, then the pbLuaStartup file is executed automagically.

Conclusion

That’s really all there is to it! You’ve got a skeleton application that runs on CONTROL that sends messages to LEFTY and RIGHTY about 3 times a second.

You can easily experiment with more features, and debug the code before comitting it to FLASH. These apps are really the bare minimum to get going. There are no screen updates or debug messages to let you know what’s going on.

The beauty of pbLua is that it’s a toolbox. You use it to shape your ideas for robots into real constructions, and it encourages experimentation and tinkering.

Have Fun!