main index NXT to NXT communication, using NXC programs

What: have two LEGO® Mindstorms NXT exchanging short messages via wireless bluetooth connection.

How: the two NXT will use the standard bluetooth functions of the Mindstorms NXT firmware; the NXC language ("Not eXactly C") allows complex projects.

Why: some ongoing projects:


Quick steps:

1) compile the programs, using:
nbc master.nxc -w+ -O=master.rxe
nbc slave.nxc -w+ -O=slave.rxe

2) send the "master" program to the "master" NXT (that is: the one you chose as "master"), and the "slave" one to the "slave" NXT. On my Apple Mac I am unable to transfer them via USB cable, so I use either the NXTBrowser utility (point and click), or the nxtcom utility (command-line, found in latest nbc/nxc/bricxcc packages). You obviously need to have every NXT "paired" with your computer (and please do not use the default bluetooth password "1234"), enabling "serial" bluetooth communications - in the Mac OS X filesystem you will find it in /dev - for example, I have /dev/tty.NXT-DevB-1 and /dev/tty.NXT-DevB-2 and to send the RXE executables to the two NXT I use nxtcom:
nxtcom -S=/dev/tty.NXT-DevB-2 master.rxe
nxtcom -S=/dev/tty.NXT-DevB-1 slave.rxe

3) you should have the two NXT "paired" (if not, then enter the "Bluetooth" menu of the NXT, have a "Search" for the other NXT turned on, and then click onto it selecting some password). This step is generally needed only once;

4) now you are ready to go:

Notes:


"No Handshaking"?

This is a somewhat basic project: the data exchange has no "handshake" features (anyways, you can write your own protocol), meaning that an NXT cannot be sure if the message that was sent now will be actually parsed by the other NXT.

The NXT bluetooth stack has space for a few messages only (5, it seems; when the buffer is full and another bluetooth message comes in, the oldest message is lost); messages are strings whose length is 1 to 58 bytes.

You will have to "pack" your values in some convenient mode (the "master" example below, packs seven values in four comma-separated fields: the first contains four characters representing the four sensors, and the other three fields contain the master motors rotation counts).

My examples below show a "non-handshaked" protocol:

An "handshaking" protocol should do:


History and performance

I had a few hard hours trying to understand how I could make my two NXT communicate to each other. I eventually found the BTLib by Daniele Benedettelli, but I had to rewrite almost everything of it because it ran on older versions of the NBC/nxc compiler - most of the bugs, however, were related to the buggy -Z2 optimization option of the compiler.

I was finally able to have everything working with -Z2 optimized programs, but I do not recommend its use (at least for 1.0.1.b34 and older versions).

I did not have extensive testing, but it seems that even with a 10msec pause (the NAP constant in the master and slave source) and lots of screen output, it was able to parse 50-60 messages per second, which is suitable for most of "remote control" applications.

Getting rid of that Wait(NAP) and those TextOuts will make the NXT send thousands of messages per second (that will be a problem when parsing is not trivial; for example, printing something on screen requires a little time). Maybe I should try to create distinct tasks for preparing, sending, receiving and parsing routines...

Note that a simpler "remote control" project will need only a bare minimum (an NXT sends messages, without trying to receive anything, and the other NXT receives and parses the messages, without sending any reply).


NXT to NXT bluetooth data exchange (using NXC language)

Most of the times, you only need to #include "protocol.h" and go on.

All bluetooth-related connection stuff is done by NXT firmware (this means that you have to connect the two NXT before running the master and slave programs).

The "master" and "slave" have different routines only to comply to NXT firmware "channels" features (but currently we do not need to connect more than two NXT together).

Example of a "master" program:


#include "protocol.h"

task main()
{
  mastercheck();
...
  for(;;)
  {
...
    mystring = receivefromslave();
...
    sendtoslave(anotherstring);

A slave program will instead use slavecheck, receivefrommaster, sendtomaster functions.


Here is the protocol.h source:


//
//    protocol.h
//    data exchange functions using bluetooth
//    based on BTLib by Daniele Benedettelli
//

#ifndef CHANNEL
#define CHANNEL   1    // slave channel can be 1, 2 or 3
#endif

#ifndef MAILBOX
#define MAILBOX   0    // 0 to 9
#endif

#define MASTER    0    // master channel is always 0


byte __local_buffer[80];
byte __local_array[59];

void btwaitfor(int conn)
{
  byte e=NO_ERR+1;
  while(e!=NO_ERR)
  {
    e=BluetoothStatus(conn);

    if(e==NO_ERR) break;
    if(e==STAT_COMM_PENDING) continue;

   TextOut(0, LCD_LINE2, "Bluetooth error:", true);
   NumOut(30, LCD_LINE4, e);

   switch(e)
   {
     case ERR_COMM_CHAN_NOT_READY:
          TextOut(0, LCD_LINE6, "NXT bluetooth");
          TextOut(0, LCD_LINE7, "not connected!");
          break;

     case ERR_COMM_BUS_ERR:
          TextOut(0, LCD_LINE6, "bus error:");
          TextOut(0, LCD_LINE7, "please reboot");
          break;
    }
    Wait(7000);
    Stop(true);
  }
}


void sendtomaster(string msg)
{
  byte mbx=MAILBOX+10;
  btwaitfor(MASTER);
  SendMessage(mbx, msg);
  btwaitfor(MASTER);
}


void sendtoslave(string msg)
{
  byte len;
  int i;

  StrToByteArray(msg,__local_array);
  len = ArrayLen(__local_array);
  __local_buffer[0] = 0x80;    // no reply telegram
  __local_buffer[1] = 0x09;    // MessageWrite Direct Command
  __local_buffer[2] = MAILBOX; // mailbox number
  __local_buffer[3] = len+1;   // message size

  len=len+4;
  i=4;
  for(;;)
  {
    __local_buffer[i] = __local_array[i-4];
    i++;
    if(i>=len) break;
  }

  btwaitfor(CHANNEL);
  BluetoothWrite(CHANNEL, __local_buffer);
  btwaitfor(CHANNEL);
}


string receivefrommaster()
{
  string msg;
  btwaitfor(MASTER);
  ReceiveMessage(MAILBOX, true, msg);
  btwaitfor(MASTER);
  return msg;
}


string receivefromslave()

  string msg;
  btwaitfor(CHANNEL);
  ReceiveMessage(MAILBOX, true, msg);
  btwaitfor(CHANNEL);
  return msg;



void btchannelcheck(int conn)
{
  int e = BluetoothStatus(conn);
  string m;

  if(e==NO_ERR) return;

  TextOut(0, LCD_LINE3, "Bluetooth error", true);
  NumOut(0,  LCD_LINE4, e);
  TextOut(0, LCD_LINE8, "on channel -.");
  NumOut(66, LCD_LINE8, conn);

  if(conn==CHANNEL)
  {
    TextOut(0, LCD_LINE1, "Master NXT");
    TextOut(0, LCD_LINE6, "please connect");
    TextOut(0, LCD_LINE7, "the slave NXT");
  }
  else
  {
    TextOut(0, LCD_LINE1, "Slave NXT");
    TextOut(0, LCD_LINE6, "please wait for");
    TextOut(0, LCD_LINE7, "the master NXT");
  }
   
  Wait(11000);
  Stop(true);
}

// -- convenience functions --

void mastercheck()  { btchannelcheck(CHANNEL); }
void slavecheck()   { btchannelcheck(MASTER);  }

// --- end ---


Here is the "master" program, sending out the values of the three motors and the four touch sensors (it works on the first stage of my latest project!). Don't care about the low number of messages received (I placed too much TextOuts).


#include "protocol.h"

#define NAP 10  // milliseconds


task main()
{
// -- strings we will use later:
  string a, b, c, t1, t2, t3, t4, m;

// -- iteration counter, and received messages counter:
  int i = 1, n = 0;

// -- initialize screen:
  TextOut(0, LCD_LINE1, "          Master", true);
  TextOut(0, LCD_LINE2, "A: *");
  TextOut(0, LCD_LINE3, "B: *");
  TextOut(0, LCD_LINE4, "C: *");
  TextOut(0, LCD_LINE5, "S: * * * *");
  TextOut(0, LCD_LINE8, "rcvd msgs:");

// -- motors A,B,C in "passive" mode, and reset their counters:
  ResetAllTachoCounts(OUT_ABC);
  Float(OUT_ABC);

// -- initialize the four touch sensors:
  SetSensorTouch(S1);
  SetSensorTouch(S2);
  SetSensorTouch(S3);
  SetSensorTouch(S4);

// -- check the bluetooth connection (we need to behave as a "master"):
// -- if no "master" connection is there, then it will complain and exit
  mastercheck();

// -- main loop:
  for(;;)
  {
// -- get message from slave:
    m = receivefromslave();

// -- an empty string means "no message", so we test its lenght:
    if(StrLen(m)>0)
    {
// -- increment "received messages" counter, and print it and the message:
      n++;
      TextOut(0, LCD_LINE7, "                ");
      TextOut(0, LCD_LINE7, m);
      NumOut(66, LCD_LINE8, n);
    }

// -- first phase: get sensors and motors values and convert to strings:
    t1 = NumToStr(SENSOR_1);
    t2 = NumToStr(SENSOR_2);
    t3 = NumToStr(SENSOR_3);
    t4 = NumToStr(SENSOR_4);

    a = NumToStr(MotorRotationCount(OUT_A));
    b = NumToStr(MotorRotationCount(OUT_B));
    c = NumToStr(MotorRotationCount(OUT_C));

// -- build the status string to be sent via bluetooth:
//    first, the four 0/1 touch sensor values,
//    then the motors rotation count values separated by commas:
    m = StrCat(t1,t2,t3,t4, ",", a, ",", b, ",", c);

// -- output the number of the message we are going to send out:
    NumOut(0, LCD_LINE1, i);

// -- output motor and values:
    TextOut(18, LCD_LINE2, a);
    TextOut(18, LCD_LINE3, b);
    TextOut(18, LCD_LINE4, c);
    TextOut(18, LCD_LINE5, t1);
    TextOut(30, LCD_LINE5, t2);
    TextOut(42, LCD_LINE5, t3);
    TextOut(54, LCD_LINE5, t4);

// -- send out the message via bluetooth channel to the slave NXT:
    sendtoslave(m);

// -- have a small nap (to not to quickly drain batteries)...
    Wait(NAP);

// -- ...but don't fall asleep!
    ResetSleepTimer();

// -- update the counter, and continue looping:
    i++;
  }
}


// --- end ---


Here is the "slave" program:


#include "protocol.h"

#define NAP 10  // milliseconds

task main()
{
  string r, m, tmp;
  int i = 0, j;

// -- initialize as "slave" NXT
  slavecheck();

// -- screen preparation
  TextOut(0, LCD_LINE1, "sending:");
  TextOut(0, LCD_LINE4, "receiving:");

  for(;;)
  {
// -- get a message string from the remote NXT unit:
    r = receivefrommaster();

// -- lenght of message string (zero means "no message received"): 
    j = StrLen(r);

// -- print to screen only if there is a message
    if(j!=0)
    {
      TextOut(0, LCD_LINE5, "                ");
      TextOut(0, LCD_LINE5, r);
    }

// -- build some message to send out (and print to screen before sending):
    tmp = NumToStr(i);
    m = StrCat("msg", tmp);
    TextOut(54, LCD_LINE1, m);

// -- send the message via bluetooth channel to the master NXT:
    sendtomaster(m);

// -- have a small nap (to not to quickly drain batteries)...
    Wait(NAP);

// -- ...but don't fall asleep!
    ResetSleepTimer();

// -- update the counter, and continue looping:
    i++;
  }
}


// --- end ---