Android Bluetooth joystick

kas:
@john_lenfr

Ok, c'est bien l'appli avec le retour vidéo possible?

oops... désolé :blush:
The video App is not yet mainstream and will be available after Joystick BT Commander V5.0 release
Not too familiar with FPV equipment...
Video is acquired by any standalone IP camera, or any Android device with integrated camera + IP webcam App
Stream is sent via WiFi to the App through a router (for IP cameras) or directly (for Android cameras)
I use this mini router
Did you already test Joystick BT Commander V4.0 ??

Ok, that's what I thought.
No I didn't test it because I'm just starting to use HC-05 modules.
I just found how to do the AT commands in Arduino Monitor
So I am at the beginning of the beginning :grin:

Howdy Kas,
Great news.....second try's a charm, very nice.
many thanks!
:smiley: :smiley: :smiley: :smiley:

kas:

Hello Kas,

First I would like to say thank you for the great piece of software you have written. It has made implementing this type of controls into my project SO much easier.

I do have one question for you, but it would probably be better to give you a little background first...

I'm currently developing an autonomous mobile robot platform, with the UI to control it on an Android tablet. I've developed most of the UI already for my device, and included a button that opens your Joystick BT Commander app directly from within my UI, for when the end user needs to manually control the robot.

However the Joystick BT Commnader app looks nothing like my UI design/theme, most notably you have a dark theme while my UI uses a "light" theme with a white background.

So my question for you is are you planning on implementing any sort of customization in this area into your app in the near future at all? If not, is there some way that I can change the colors so our software would better compliment eachother? I know I can go back and redesign my UI to match your app, but for my particular application (outdoors and/or in direct sunlight) a dark theme is a lot harder for the end user to see.

Thanks in advance for any assistance you can provide. It is greatly appreciated.

Hi joepro
Thanks for the comments
Should be included in V5.0, to be posted within 4 weeks

Sounds good, I look forward to it. Also, one other thing I noticed recently is that you have your app locked to Landscape. Obviously by the way it is designed a landscape view is required. Would it be possible for you to set it to sensorLandscape instead, that way it will automatically flip if the user turns their device upside down.

Once again, thank you for your fantastic software and the excellent support you provide. Now if we could get all software developers to listen to their end users like this, the world would be a much better place. :slight_smile:

Would it be possible for you to set it to sensorLandscape instead, that way it will automatically flip if the user turns their device upside down

Thanks joepro for this suggestion, makes a lot of sense, will definitely be included in V5.0

However the Joystick BT Commander app looks nothing like my UI design/theme, most notably you have a dark theme while my UI uses a "light" theme with a white background.

Let me have the code snippet with your theme definition

Version 5.0 to be released

Early Warning

Backgroung:
Initial communication protocol was developped one year ago
At that time the App was simple (two buttons only, joystick values coded on a single Byte, no feedback communication from Arduino to Android)
Time has evolved, with added features, transmitted data amount has rapidly inflated

In some situations (RF noise environment) Arduino may receive erratic values and joystick values may be seen as button commands

Version 5.0 will feature a pure ASCII transmission protocol and definitely address this problem

This new version will be online next week
For those who want to anticipate and start modifying their code,
I will post the new communication protocol, together with the relevant demo sketch (AndroTest V2.0)

New communication protocol for Joystick BT Commander V5.x

Communication from Android to Arduino (or to any microcontroller)

  • Joystick information:
    Joystick data is transmitted as ASCII data, according to this data frame:
    <STX "Joy_X + 200" "Joy_Y + 200" ETX>

with STX=0x2, ETX=0x3

This is the relevant Android (Java) code snippet from Joystick Bluetooth Commander:

    final int XvalB = Xval + 200;
    final int YvalB = Yval + 200;
    if((((Xant!=Xval) || (Yant!=Yval)) || (mTimeoutCounter>=mMaxTimeoutCount && mMaxTimeoutCount>-1)))	   // joystick position changed, or timeout occurred
    	String toSend = String.format("%c%d%d%c", STX, XvalB, YvalB, ETX);
    	sendMessage(toSend);

So, for Xval=0 Yval=0:
XvalB = YvalB = 200
'2' is Ascii 32 hex (decimal 50)
'0' is Ascii 30 hex (decimal 48)
Data frame: <0x02 0x32 0x30 0x30 0x32 0x30 0x30 0x03>

for Xval=100 Yval=100:
XvalB = YvalB = 300
'3' is Ascii 33 hex (decimal 51)
'0' is Ascii 30 hex (decimal 48)
Data frame: <0x02 0x33 0x30 0x30 0x33 0x30 0x30 0x03>

The data frame is send via BlueTooth to the arduino board and decoded.
This is the Arduino code snippet from AndroTest V2.0:

 void getJoystickState(byte data[8])    {
  int joyX = (data[1]-48)*100 + (data[2]-48)*10 + (data[3]-48);       // obtain an Int from the ASCII representation
  int joyY = (data[4]-48)*100 + (data[5]-48)*10 + (data[6]-48);
  joyX = joyX - 200;                                                  // Offset to avoid
  joyY = joyY - 200;                                                  // transmitting negative numbers
 }
  • Button information:
    Each time an android button is pressed, the following data frame is transmitted

buttonState is transmitted as a Byte value and reflects the state of the pushed button
Button #1: ON: 'A' OFF: 'B'
Button #2: ON: 'C' OFF: 'D'
Button #3 ...

On the Arduino side, data frame is decoded in getButtonState()

void getButtonState(int bStatus)  {
  switch (bStatus) {
// -----------------  BUTTON #1  -----------------------
    case 'A':
      buttonStatus |= B000001;        // ON
      Serial.println("\n** Button_1: ON **");
      // your code...      
      break;
    case 'B':
      buttonStatus &= B111110;        // OFF
      Serial.println("\n** Button_1: OFF **");
      // your code...      
      break;

// -----------------  BUTTON #2  -----------------------
    case 'C':
      buttonStatus |= B000010;        // ON
      Serial.println("\n** Button_2: ON **");
      // your code...      
      break;
    case 'D':
      buttonStatus &= B111101;        // OFF
      Serial.println("\n** Button_2: OFF **");
      // your code...      
      break;

    ....
}

Communication back from Arduino to Android:
(same as V4.0)

Arduino code

void sendBlueToothData()  {

  ....

    mySerial.print((char)STX);                                             // Start of Transmission
    mySerial.print(getButtonStatusString());  mySerial.print((char)0x1);   // buttons status feedback
    mySerial.print(GetdataInt1());            mySerial.print((char)0x4);   // datafield #1
    mySerial.print(GetdataFloat2());          mySerial.print((char)0x5);   // datafield #2
    mySerial.print(displayStatus);                                         // datafield #3
    mySerial.print((char)ETX);                                             // End of Transmission
  
  ....  
}

Data frame transmitted back from Arduino to Android device: (by default, every 1000ms)
< STX Buttons state 0X01 DataField#1 0x04 DataField#2 0x05 DataField#3 ETX >

example: < 0X02 001011 0X01 120.00 0x04 -4500 0x05 Motor enabled 0x03 >

Button state:
This is a six character string reflecting the Arduino state for buttons position
button #1,#2,#4: ON, all others: OFF >>> 001011
This feedback avoid any discrepancy with the Android device button state
An Arduino Reset will reinitialize the Android buttons, Two way communication is mandatory for "clean" control
Button state string is created in the getButtonStatusString() function.

String getButtonStatusString()  {
  String bStatus = "";
  for(i=0; i<6; i++)  {
    if(buttonStatus & B100000 >>i)      bStatus += "1";
    else                                bStatus += "0";
  }
  return bStatus;
}

Datafields:
Data is transmitted as ASCII characters using Serial.print()
Numbers are printed using an ASCII character for each digit
120.00 >>> 0X31, 0X32, 0X30, 0X2E, 0X30, 0X30

So < 0x02 001011 0x01 120.00 0x04 -4500 0x05 Motor enabled 0x03 >
is actually transmitted as
< 0x02, 0x30,0x30,0x31,0x30,0x31,0x31, 0X01, 0x31,0x32,0x30,0x2E,0x30,0x30, **
** 0x04, 0x2D,0x34,0x35,0x30,0x30, 0x05, 0x4D,0x6F,0x74,0x6F,0x72,0x20,0x65,

** 0x6E,0x61,0x62,0x6C,0x65,0x64, 0x03 >**
without commas and spaces

Should you have additional questions, let me know :wink:

will this protocol also be used in Total RC commander ?

will this protocol also be used in Total RC commander ?

It will

Joystick BT Commander & Total RC commander share the same Bluetooth engine
Total RC commander has WiFi live video atop

Great, thanks

just a few ideas that maybe can implemented in future releases.

  1. Text overlay on the video feed, like a HUD. Instead of printing the adruino sent messages under the video screen overlay them on the video feed.
  2. Have an option to switch between the joystick style control and a four directional arrow control.

Any idea when Total RC commander will be available with the new protocol and a test sketch ?

This is AndroTest V2.0 demo sketch for Joystick BT Commander V5.x

modified variables

  • STX
  • ETX
  • cmd[8]

modified functions:

  • loop()
  • getJoystickState()
  • getButtonState()
#define VERSION     "\n\nAndroTest V2.0 - @kas2014\ndemo for V5.x App"

// V2.0  changed to pure ASCII Communication Protocol ** not backward compatible **
// V1.4  improved communication errors handling
// V1.3  renamed for publishing, posted on 09/05/2014
// V1.2  Text display   ** not backward compatible **
// V1.1  Integer display
// V1.0  6 buttons + 4 data char implemented

// Demo setup:
// Button #1 controls pin #13 LED
// Button #4 toggle datafield display rate
// Button #5 configured as "push" button (momentary)
// Other buttons display demo message

// Arduino pin#2 to TX BlueTooth module
// Arduino pin#3 to RX BlueTooth module
// make sure your BT board is set @57600 bps
// better remove SoftSerial for PWM based projects

// For Mega 2560:
// remove   #include "SoftwareSerial.h", SoftwareSerial mySerial(2,3);
// search/replace  mySerial  >> Serial1
// pin#18 to RX bluetooth module, pin#19 to TX bluetooth module

#include "SoftwareSerial.h"

#define    STX          0x02
#define    ETX          0x03
#define    ledPin       13
#define    SLOW         750                            // Datafields refresh rate (ms)
#define    FAST         250                             // Datafields refresh rate (ms)

SoftwareSerial mySerial(2,3);                           // BlueTooth module: pin#2=TX pin#3=RX
byte cmd[8] = {0, 0, 0, 0, 0, 0, 0, 0};                 // bytes received
byte buttonStatus = 0;                                  // first Byte sent to Android device
long previousMillis = 0;                                // will store last time Buttons status was updated
long sendInterval = SLOW;                               // interval between Buttons status transmission (milliseconds)
String displayStatus = "xxxx";                          // message to Android device

void setup()  {
  Serial.begin(57600);
  mySerial.begin(57600);                                // 57600 = max value for softserial
  pinMode(ledPin, OUTPUT);     
  Serial.println(VERSION);
  while(mySerial.available())  mySerial.read();         // empty RX buffer
}

void loop() {
  if(mySerial.available())  {                           // data received from smartphone
    delay(2);
    cmd[0] =  mySerial.read();  
    if(cmd[0] == STX)  {
      int i=1;      
      while(mySerial.available())  {
        delay(1);
        cmd[i] = mySerial.read();
        if(cmd[i]>127 || i>7)                 break;     // Communication error
        if((cmd[i]==ETX) && (i==2 || i==7))   break;     // Button or Joystick data
        i++;
      }
      if     (i==2)          getButtonState(cmd[1]);    // 3 Bytes  ex: < STX "C" ETX >
      else if(i==7)          getJoystickState(cmd);     // 6 Bytes  ex: < STX "200" "180" ETX >
    }
  } 
  sendBlueToothData(); 
}

void sendBlueToothData()  {
  static long previousMillis = 0;                             
  long currentMillis = millis();
  if(currentMillis - previousMillis > sendInterval) {   // send data back to smartphone
    previousMillis = currentMillis; 

// Data frame transmitted back from Arduino to Android device:
// < 0X02   Buttons state   0X01   DataField#1   0x04   DataField#2   0x05   DataField#3    0x03 >  
// < 0X02      "01011"      0X01     "120.00"    0x04     "-4500"     0x05  "Motor enabled" 0x03 >    // example

    mySerial.print((char)STX);                                             // Start of Transmission
    mySerial.print(getButtonStatusString());  mySerial.print((char)0x1);   // buttons status feedback
    mySerial.print(GetdataInt1());            mySerial.print((char)0x4);   // datafield #1
    mySerial.print(GetdataFloat2());          mySerial.print((char)0x5);   // datafield #2
    mySerial.print(displayStatus);                                         // datafield #3
    mySerial.print((char)ETX);                                             // End of Transmission
  }  
}

String getButtonStatusString()  {
  String bStatus = "";
  for(int i=0; i<6; i++)  {
    if(buttonStatus & (B100000 >>i))      bStatus += "1";
    else                                  bStatus += "0";
  }
  return bStatus;
}

int GetdataInt1()  {              // Data dummy values sent to Android device for demo purpose
  static int i= -30;              // Replace with your own code
  i ++;
  if(i >0)    i = -30;
  return i;  
}

float GetdataFloat2()  {           // Data dummy values sent to Android device for demo purpose
  static float i=50;               // Replace with your own code
  i-=.5;
  if(i <-50)    i = 50;
  return i;  
}

void getJoystickState(byte data[8])    {
  int joyX = (data[1]-48)*100 + (data[2]-48)*10 + (data[3]-48);       // obtain the Int from the ASCII representation
  int joyY = (data[4]-48)*100 + (data[5]-48)*10 + (data[6]-48);
  joyX = joyX - 200;                                                  // Offset to avoid
  joyY = joyY - 200;                                                  // transmitting negative numbers

  if(joyX<-100 || joyX>100 || joyY<-100 || joyY>100)     return;      // commmunication error
  
// Your code here ...
    Serial.print("Joystick position:  ");
    Serial.print(joyX);  
    Serial.print(", ");  
    Serial.println(joyY); 
}

void getButtonState(int bStatus)  {
  switch (bStatus) {
// -----------------  BUTTON #1  -----------------------
    case 'A':
      buttonStatus |= B000001;        // ON
      Serial.println("\n** Button_1: ON **");
      // your code...      
      displayStatus = "LED <ON>";
      Serial.println(displayStatus);
      digitalWrite(ledPin, HIGH);
      break;
    case 'B':
      buttonStatus &= B111110;        // OFF
      Serial.println("\n** Button_1: OFF **");
      // your code...      
      displayStatus = "LED <OFF>";
      Serial.println(displayStatus);
      digitalWrite(ledPin, LOW);
      break;

// -----------------  BUTTON #2  -----------------------
    case 'C':
      buttonStatus |= B000010;        // ON
      Serial.println("\n** Button_2: ON **");
      // your code...      
      displayStatus = "Button2 <ON>";
      Serial.println(displayStatus);
      break;
    case 'D':
      buttonStatus &= B111101;        // OFF
      Serial.println("\n** Button_2: OFF **");
      // your code...      
      displayStatus = "Button2 <OFF>";
      Serial.println(displayStatus);
      break;

// -----------------  BUTTON #3  -----------------------
    case 'E':
      buttonStatus |= B000100;        // ON
      Serial.println("\n** Button_3: ON **");
      // your code...      
      displayStatus = "Motor #1 enabled"; // Demo text message
      Serial.println(displayStatus);
      break;
    case 'F':
      buttonStatus &= B111011;      // OFF
      Serial.println("\n** Button_3: OFF **");
      // your code...      
      displayStatus = "Motor #1 stopped";
      Serial.println(displayStatus);
      break;

// -----------------  BUTTON #4  -----------------------
    case 'G':
      buttonStatus |= B001000;       // ON
      Serial.println("\n** Button_4: ON **");
      // your code...      
      displayStatus = "Datafield update <FAST>";
      Serial.println(displayStatus);
      sendInterval = FAST;
      break;
    case 'H':
      buttonStatus &= B110111;    // OFF
      Serial.println("\n** Button_4: OFF **");
      // your code...      
      displayStatus = "Datafield update <SLOW>";
      Serial.println(displayStatus);
      sendInterval = SLOW;
     break;

// -----------------  BUTTON #5  -----------------------
    case 'I':           // configured as momentary button
//      buttonStatus |= B010000;        // ON
      Serial.println("\n** Button_5: ++ pushed ++ **");
      // your code...      
      displayStatus = "Button5: <pushed>";
      break;
//   case 'J':
//     buttonStatus &= B101111;        // OFF
//     // your code...      
//     break;

// -----------------  BUTTON #6  -----------------------
    case 'K':
      buttonStatus |= B100000;        // ON
      Serial.println("\n** Button_6: ON **");
      // your code...      
       displayStatus = "Button6 <ON>"; // Demo text message
     break;
    case 'L':
      buttonStatus &= B011111;        // OFF
      Serial.println("\n** Button_6: OFF **");
      // your code...      
      displayStatus = "Button6 <OFF>";
      break;
  }
// ---------------------------------------------------------------
}

AndroTest_V20_POSTED.ino (8.14 KB)

@tolisn63

Great, thanks
just a few ideas that maybe can implemented in future releases.

  1. Text overlay on the video feed, like a HUD. Instead of printing the arduino sent messages under the video screen overlay them on the video feed.

Well... to keep it simple, I have Bluetooth and WiFi modules well apart
This is easier for maintenance and upgrade
Text overlay would create modules intricacy

  1. Have an option to switch between the joystick style control and a four directional arrow control.

Nice suggestion
I will first implement the long awaited accelerometer control (tilting the Android device)

Any idea when Total RC commander will be available with the new protocol and a test sketch ?

Will wait for Joystick BT commander V5.0 feedback
Would say two or three weeks

Hi Kas

Have tried the updated Andro_Pan_Tilt sketch but it did not work.
What happens is the servos centre and when I use the joystick I get a few lines of positions in serial monitor and then 'communication error' repeating lines. MyservoX changes to full 180 position and stays there (motor whirring) whilst MyservoY does nothing.

I played with the delay settings in the void loop to no avail (same behavior) except delay of 1 stops the communication error but the servos do not respond.

My Phone is Sony experia, am using an arduino Uno and a HC-06 bluetooth module (Set to 57600 with your AT sketch)
BT TX to D0, Servos to 9 and 10 (BT RX not used ?)

Hope this helps

Joystick Bluetooth Commander Version 5.0 has just been published on Google Play

Change log:

  • improved communication protocol see reply #252
  • smoother Joystick behavior (thanks Bob1943)
  • automatically flip if the user turns device upside down (thanks joepro)
  • optimized layout (thanks billcat)

Demo sketch: AndroTest V2.0 see reply #256

Please let me have feedback, including screenshots

Enjoy :wink:

@thornlv

Have tried the updated Andro_Pan_Tilt sketch but it did not work.
What happens is the servos centre and when I use the joystick I get a few lines of positions in serial monitor and then 'communication error' repeating lines. MyservoX changes to full 180 position and stays there (motor whirring) whilst MyservoY does nothing.

Go to settings/About and confirm your actual App version
What is your Arduino board model ??
You don't need to connect motors before you get sensible readings on your Serial Monitor

BT TX to D0, Servos to 9 and 10 (BT RX not used ?)

Yes, no need to send data back to your phone

Hi Kas
App is V4 and the board is a Uno v3

Hope this helps
Cheers, Vic

@thornlv
Sorry guy, I was too self confident in modifying the code and not testing it :blush:
I just modified my code (reply #354)
Now fully tested on Serial Monitor :wink:

Please upgrade and use Joystick BT Commander V5.0

Hi Kas.

Have briefly tried it and it works ok, will give a thorough test when I have finished my domestic duties today by order of her indoors :wink:

Cheers mate

Vic

Hi Kas.
Have briefly tried it and it works ok, will give a thorough test when I have finished my domestic duties today by order of her indoors :wink:

Cheers mate
Vic

Great :slight_smile:
Remember, you can adjust Zero position, max move and motors direction
When you have time, please post a video showing both App screen and moving gimball

Hi Kas

Gave it a good going over, everything works ok. going to work out how to adjust maximum movement as I dont need full 180 deg field - at around 90deg for pan and 45 for tilt. Next will be to include an old HTC phone to act as a 'monitor' with V5 - because live view with a 300/600mm lens is a narrow field which makes it difficult to adjust position for the subject, I see the option of a 'wider angle' view on the smart phone will aid that much more quickly - I will try to show you what I mean when record a video of the intended set up.

Many thanks.#

Vic

hi kas , i saw your nice app and i thought telling you a problem that i have with my app.. well my hc-06 disconnects in really random times.. it might work for 10 minutes or more with no problem while on the other hand it might start disconnecting again and again without being able to understand why..
Any ideas why this might happening?

Thank you :slight_smile: :slight_smile:

@zaxarias

Some shots in the dark

  • Your board is resetting due to high load, use a separate power supply for Arduino
  • intermittent contact within breadboard, move and secure components
  • defective BT board, not an expensive item, try a spare one

Let us know the outcome