Bluetooth Communications

Post your NXJ projects, project ideas, etc here!

Moderators: 99jonathan, roger, imaqine

Bluetooth Communications

Postby allonhadaya » Mon May 25, 2009 1:56 am

Hello Everyone,

Overview -

I've written a remote control class.

It extends thread, in order to be able to join any other program, but will run on its own.

Outline -

1. Uses the BTConnection read method to pick up 4 bytes (in a loop)
2. Converts byte[] to a 32-bit integer
3. Acts according to integer constants that the programmer defines

Code -

Go to Last Post for working code
Last edited by allonhadaya on Sat Jun 06, 2009 3:13 pm, edited 1 time in total.
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States

Postby gloomyandy » Mon May 25, 2009 8:17 am

Hi,
What are you using to send command to be read by this code? Without knowing this it is hard to judge what might be going wrong here... However there are a few potential problems....



It is not a good idea to use the available() call this way... The contract for available is...
Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream. The next invocation might be the same thread or another thread. A single read or skip of this many bytes will not block, but may read or skip fewer bytes.


Note that this is just an estimate and that (for instance) an implementation would be correct to always return 0.

The problem here is that for some Bluetooth stacks the only way to check for new data is to issue a blocking read. The available method is not allowed to block and so it is not possible to check for new data that has arrived, instead it simply returns the amount of data that is still remaining from in the buffers from a previous read. So data may be available to be read even if available() returns 0.

In particular this is true of the PC side of leJOS you cannot use available() in this way with our PC side code. The only safe way is to call a read function (which may block). On the NXT currently you can do this (because we have non blocking reads available to check if data is present). However this may change in the future and so you should avoid using available in this way.


The second problem is that you are not checking the return value of read. A blocking read will always return at least one byte (or an error), however it may not block until all of the bytes that you have requested have been read. So sometimes you may end up reading 1,2,3 bytes rather than the 4 that you have requested. You are using packet mode (you set the IO mode to 0), so you may get away with this because in this mode read will block until the entire packet is available, but without seeing how the data has been sent it is hard to be sure.

The third problem is that your byte to integer code will fail for large integers (because of the division by 256), with your current code this is probably not an issue, but it is easy to fix, simply change the code to...
Code: Select all
    private int byteArrayToInt(byte[] b)
    {         
          int value = 0;
          for(int index = 0;index<b.length;index++)
          {
             value <<= 8;
             value |= b[index] & 0xFF;
          }
          return value;
    }


Andy
User avatar
gloomyandy
leJOS Team Member
 
Posts: 3967
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Postby allonhadaya » Mon May 25, 2009 4:11 pm

Great tips Andy,

Sending Device -

I wrote a C#.net application for my Windows Mobile 6 phone using the In The Hand API.

Possible Solution -

1. I'll try calling the read() method with a false parameter (won't wait for 4 bytes)
2. Check if 4 bytes have been read.
3. If they have been read, act accordingly.
4. If not, the connection is terminated.

What do you think?

-------------------------------------------------------------------------------------

Thank you for the byte conversion fix! I just keep learning
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States

Postby gloomyandy » Mon May 25, 2009 4:52 pm

Hi,
Ok so you are writing to the NXT from a mobile phone, not using either the Lego software or the leJOS PC side APIs. If this is correct then you need to either...
1. Set the NXT I/O into raw mode or
2. add packet headers to the data you write from the phone.

The easy option is 1!

You can do this either by setting the mode when you call waitForConnection...
Code: Select all
// Wait forever for a RAW connection
connection = Bluetooth.waitForConnection(0, NXTConnection.RAW);


or you can set it using setIOMode
Code: Select all
// Use the connection in RAW mode
connection.setIOMode(NXTConnection.RAW);


NOTE: The above assumes you are using 0.7 or 0.8. The way that RAW connections changed with 0.7 (which may be why you have setIOMode(0) in your code)....

Andy
User avatar
gloomyandy
leJOS Team Member
 
Posts: 3967
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Postby gloomyandy » Mon May 25, 2009 4:55 pm

Hi again,
I'm not sure if your read solution will work. You can't be sure that all 4 bytes will arrive at the same time, they may get split up over two (low level) packets. So what you need to do is to loop reading the data until you have all of the bytes that you require (you can use either blocking or non blocking I/O for this). If there is an error then the read will return a -ve value... You shouldn't assume that because you have read less than four bytes that the rest won't be along real soon now...


Andy
User avatar
gloomyandy
leJOS Team Member
 
Posts: 3967
Joined: Fri Sep 28, 2007 2:06 pm
Location: UK

Postby allonhadaya » Tue May 26, 2009 3:15 am

Hey Andy,

I've been working on the program for a couple of hours, quite sporadically, but here is what I've gotten done.

Results -

Consistent Reading: Program runs dependably and reads every time that the phone application sends data. Operating in Raw mode solved that issue.

Bad Conversion: The NXJ program is converting the 4 bytes into large negative numbers ( about 2 billion ), rather than the numbers that I send from the phone.

Byte Conversion Code:
Code: Select all
    private int byteArrayToInt(byte[] b)
    {         
          int value = 0;
          for(int index = 0;index<b.length;index++)
          {
             value <<= 8;
             value |= b[index] & 0xFF;
          }
          return value;
    }


P.S. I will post everything when the class is written and tested!
Learning is good!!!
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States

Postby allonhadaya » Tue May 26, 2009 5:55 am

Could this be the issue?

C# bytes are unsigned 0 -> 255
Java bytes are signed -128 -> 127

I need to either send signed bytes or receive/convert unsigned bytes...

let me know what you think
Goodnight for now :)
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States

Bluetooth Communication Example

Postby allonhadaya » Sat Jun 06, 2009 3:26 am

The final error:

1. By reading one byte at a time, the data was being saved in reverse order.
2. I solved this by changing byteConversion to read from last byte to first.

Feel free to use this code, but do give me credit in the comments. :wink:

Code: Select all
import lejos.nxt.*;
import lejos.nxt.comm.*;
import lejos.navigation.*;

public class BC2 extends Thread
{
   //Text Constant
   private final String line = "________________";
   
   //Input Constants
   private final int FORWARD = 901;
   private final int RIGHT = 902;
   private final int BACKWARD = 903;
   private final int LEFT = 904;
   private final int FORWARD_OFF = 905;
   private final int RIGHT_OFF = 906;
   private final int BACKWARD_OFF = 907;
   private final int LEFT_OFF = 908;
   private final int ACTION = 909;
   private final int ACTION_OFF = 910;

   //Input Variables
   private byte[] input;
   private int value;
   private int previousValue;
   int readResult;
   private int loopCounter;

   //Wheels
   private Pilot wheels;
   
   //Bluetooth
   BTConnection connection;
   
   public BC2(Pilot newWheels)
   {
      input = new byte[4];
      value = -1;
      previousValue = -2;
      loopCounter = 0;
      wheels = newWheels;
      
      if (!Bluetooth.getPower())
         Bluetooth.setPower(true);
      
      System.out.println("BT Ready");
      connection = Bluetooth.waitForConnection(0, NXTConnection.RAW);
      System.out.println("BT Connected");
      connection.openStream();
      System.out.println("BT Stream Opened");
   }
   
   public void run()
   {
      while (true)
      {
         loopCounter++;
         readResult = 0;
         //Ensures a full read unless error hit
         do
         {
            readResult += connection.read(input, 4, false);
            
            if (Button.ESCAPE.isPressed())
               return;
         }
         while( readResult>=0 && readResult < 4 );
         
         //Good Read
         if(readResult==4)
         {
            value = byteArrayToInt(input);
            printState();
            
            if (value!=previousValue)
            {
               //Set Speed
               if(value >= 0 && value <= 900)
               {
                  wheels.setMoveSpeed(value);
                  wheels.setTurnSpeed(value);
               }
               //Other Commands
               else
               {   
                  switch(value)
                  {
                     case(FORWARD):
                        wheels.forward();
                        System.out.println("Forward");
                        break;
                     case(RIGHT):
                        wheels.steer(200);                  
                        System.out.println("Right");
                        break;
                     case(BACKWARD):
                        wheels.backward();
                        System.out.println("Backward");
                        break;
                     case(LEFT):
                        wheels.steer(-200);
                        System.out.println("Left");
                        break;
                     case(FORWARD_OFF):
                        wheels.stop();
                        System.out.println("Stop");
                        break;
                     case(RIGHT_OFF):
                        wheels.stop();
                        System.out.println("Stop");
                        break;
                     case(BACKWARD_OFF):
                        wheels.stop();
                        System.out.println("Stop");
                        break;
                     case(LEFT_OFF):
                        wheels.stop();
                        System.out.println("Stop");
                        break;
                     case(ACTION):
                        wheels.stop();
                        System.out.println("Action On");
                        break;
                     case(ACTION_OFF):
                        wheels.stop();
                        System.out.println("Action Off");
                        break;
                  }
               }
               previousValue=value;
            }
         }
         
         //Bad Read
         else
         {
            System.out.println("ERROR");
            printState();
            System.out.println("Read Error: " + readResult);
            try{Thread.sleep(5000);} catch (InterruptedException e){}
         }
      }
   }

    private static int byteArrayToInt(byte[] b)
    {
        long l = 0;
        l |= b[3] & 0xFF;
        l <<= 8;
        l |= b[2] & 0xFF;
        l <<= 8;
        l |= b[1] & 0xFF;
        l <<= 8;
        l |= b[0] & 0xFF;
        return (int)l;
    }
   
    private void printState()
    {
      System.out.println(line);
      System.out.println("Loop Count: " + loopCounter);
      System.out.println("Value: " + value);
    }
   
    /*For Debugging
    private static void printArray(byte[] array)
    {
       for (int index = 0;index<array.length;index++)
          System.out.println(array[index]);
    }
    */

   public static void main(String[] args)
   {
      BC2 me = new BC2(new TachoPilot( (float)5.7, (float)13.3, Motor.A, Motor.C));
      me.start();
   }
}
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States

How works

Postby sebalitter » Sat Jul 04, 2009 3:58 pm

What do this?
what direction is 0xff?

private static int byteArrayToInt(byte[] b)
{
long l = 0;
l |= b[3] & 0xFF;
l <<= 8;
l |= b[2] & 0xFF;
l <<= 8;
l |= b[1] & 0xFF;
l <<= 8;
l |= b[0] & 0xFF;
return (int)l;
}
sebalitter
New User
 
Posts: 20
Joined: Mon Feb 02, 2009 10:32 pm
Location: Argentina

Postby sebalitter » Sat Jul 04, 2009 4:38 pm

i think i understand...

it fucion all the bytes of the array in one intiger number. right?

But how input[] is fulled to set the speed ?
sebalitter
New User
 
Posts: 20
Joined: Mon Feb 02, 2009 10:32 pm
Location: Argentina

Postby allonhadaya » Fri Jul 10, 2009 4:44 pm

I set the speed by sending a value in the range 0 - 900.
All other commands occupy numbers above 900.

if (value >= 0 && value <= 900)
{
//Set speed to value
}
else
{
//Decide which command to use
}

:lol: Hope it helps!
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States

Abstract Bluetooth Class

Postby allonhadaya » Fri Jul 10, 2009 4:52 pm

Updated Class:

I've made the BluetoothControl class abstract in order to hide the details of communication. This way, a child class of BluetoothControl only needs to define the methods which get called automatically.

- There are two reserved ranges of values: 1-100 and 101-200
- Commands occupy 201 and above

Code: Select all
/*
 * Author: Allon Hadaya
 * Date: 06/19/09
 *
 * Description: -Abstract class that manages bluetooth communications.
 *             -Expects 32-bit integers.
 *             -Acts on predefined integer constants.
 *             -Abstract event methods for robot behavior.
 */

import lejos.nxt.*;
import lejos.nxt.comm.*;

public abstract class BluetoothControl extends Thread
{
   //input constants
   private static final int FORWARD = 201;
   private static final int FORWARD_RIGHT = 202;
   private static final int RIGHT = 203;
   private static final int BACKWARD_RIGHT = 204;
   private static final int BACKWARD = 205;
   private static final int BACKWARD_LEFT = 206;
   private static final int LEFT = 207;
   private static final int FORWARD_LEFT = 208;
   
   private static final int STOP = 209;
   private static final int ACTION = 210;
   private static final int ACTION_OFF = 211;
   private static final int EXIT = 212;
   private static final int B_UP_PRESS = 213;
   private static final int B_DOWN_PRESS = 214;
   private static final int B_UP_RELEASE = 215;
   private static final int B_DOWN_RELEASE = 216;
   
   //bluetooth
   static BTConnection connection;
   
   //input variables
   private static byte[] input;
   private static int value;
   private static int valueLast;
   private static int readResult;

   public BluetoothControl()
   {
      valueLast = 0;
      input = new byte[4];
      refreshBluetooth();
   }
   
   protected abstract void on1To100(int value);
   protected abstract void on101To200(int value);
   
   protected abstract void onForward();
   protected abstract void onForwardRight();
   protected abstract void onRight();
   protected abstract void onBackwardRight();
   protected abstract void onBackward();
   protected abstract void onBackwardLeft();
   protected abstract void onLeft();
   protected abstract void onForwardLeft();
   
   protected abstract void onStop();
   protected abstract void onAction();
   protected abstract void onActionOff();
   protected abstract void onExit();
   
   protected abstract void onBUpPress();
   protected abstract void onBDownPress();
   protected abstract void onBUpRelease();
   protected abstract void onBDownRelease();
   
   public void run()
   {
      while (true)
      {
         readResult = 0;
         //Read 4 bytes
         do
         {
            readResult += connection.read(input, 4, false);
            
            if (Button.ESCAPE.isPressed())
               return;
         }while( readResult>=0 && readResult < 4 );
         
         //Good Read
         if(readResult==4)
         {
            value = byteArrayToInt(input);
            //New Value
            if (value!=valueLast)
            {
               //1-100
               if(value >= 1 && value <= 100)
               {
                  on1To100(value);
               }
               //101-200
               else if(value >= 101 && value <= 200)
               {
                  on101To200(value);
               }
               //Other Commands
               else
               {   
                  switch(value)
                  {
                     case(FORWARD):
                        onForward();
                        break;
                     case(FORWARD_RIGHT):
                        onForwardRight();
                        break;
                     case(RIGHT):
                        onRight();
                        break;
                     case(BACKWARD_RIGHT):
                        onBackwardRight();
                        break;
                     case(BACKWARD):
                        onBackward();
                        break;
                     case(BACKWARD_LEFT):
                        onBackwardLeft();
                        break;
                     case(LEFT):
                        onLeft();
                        break;
                     case(FORWARD_LEFT):
                        onForwardLeft();
                        break;
                     case(STOP):
                        onStop();
                        break;
                     case(ACTION):
                        onAction();
                        break;
                     case(ACTION_OFF):
                        onActionOff();
                        break;
                     case(EXIT):
                        onExit();
                        return;
                     case(B_UP_PRESS):
                        onBUpPress();
                        break;
                     case(B_DOWN_PRESS):
                        onBDownPress();
                        break;
                     case(B_UP_RELEASE):
                        onBUpRelease();
                        break;
                     case(B_DOWN_RELEASE):
                        onBDownRelease();
                        break;
                  }
               }
               valueLast=value;
            }
         }
         
         //Bad Read
         else
         {
            System.out.println("Disconnected");
            //Reset Connection
            refreshBluetooth();
         }
      }
   }
   
   private static final void refreshBluetooth()
   {
      if (!Bluetooth.getPower())
         Bluetooth.setPower(true);
      System.out.println("BT Standby");
      connection = Bluetooth.waitForConnection(0, NXTConnection.RAW);
      connection.openStream();
   }

    private static final int byteArrayToInt(byte[] b)
    {
       long l = 0;
       for(int index=b.length - 1; index > 0; index--)
       {
          l |= b[index] & 0xFF;
          l <<= 8;
       }
       //Last one not shifted
       l |= b[0] & 0xFF;
       return (int)l;
    }

   public static void main(String[] args)
   {}
}
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States

Postby sebalitter » Mon Jul 13, 2009 12:44 am

hi again:

can u pass me the code for NXT?

thanks
Last edited by sebalitter on Mon May 31, 2010 8:49 pm, edited 1 time in total.
sebalitter
New User
 
Posts: 20
Joined: Mon Feb 02, 2009 10:32 pm
Location: Argentina

Postby allonhadaya » Sun Jul 19, 2009 3:19 am

I wrote a C# application for a Windows Mobile 6 phone. It's a little funky because C# uses unsigned bytes, whereas java deals with signed bytes.

In short, all that the C# Application does is start a connection, and write integer values to the stream.

This class could work with any device that sends 32-bit integers over a bluetooth connection.
allonhadaya
New User
 
Posts: 11
Joined: Mon May 25, 2009 1:32 am
Location: United States


Return to NXJ Projects

Who is online

Users browsing this forum: No registered users and 1 guest

more stuff