Go Down

Topic: Android Bluetooth joystick (Read 360411 times) previous topic - next topic

Juris3D

Next step: differential steering for left and right turns (V0.4)
Hello, and thanks for new version to try, and to learn! ;-)
Code was working, but it was acting weird (very fast) in "back hemisphere" of joystick movements. Forward, Forward-Right, Forward-Left was working perfectly. Then I found a problem in code: in "backward" operations there was digitalWrite for PWM value, and analogWrite for LOW/HIGH values. That needed to be swapped, of course (analog for PWM, digital for LOW/HIGH). Now all joystick movements are perfect. Here is corrected version, let's call it "V0.41" :-)
Code: [Select]

// ***********************************************************************
// *   TankControl   V0.41                                   @kas 2016   *
// *   Code for driving a generic tank with two motors and L298 driver   *
// ***********************************************************************

// Rev history
// V0.41  Differential drive (FW/BW/left/right)
// V0.3  Basic moves (FW/BW only), with RC control, no buttons, no feedback
// V0.2  Basic moves, no RC control

#define   IN1_L     3        // direction control for left motor
#define   IN2_L     5        // PWM control for left motor
#define   IN3_R     6        // direction control for right motor
#define   IN4_R     9        // PWM control for right motor

#define    STX          0x02
#define    ETX          0x03

byte cmd[8] = {0, 0, 0, 0, 0, 0, 0, 0};                 // bytes received

void setup()  {
  pinMode(IN1_L, OUTPUT);
  pinMode(IN2_L, OUTPUT);
  pinMode(IN3_R, OUTPUT);
  pinMode(IN4_R, OUTPUT);
//  Serial.begin(57600);         //  XXXXXXX   change to actual BT card baud rate   XXXXXXXXXXXXXXX
Serial.begin(9600);
  while(Serial.available())  Serial.read();           // empty RX buffer
}

void loop() {
  if(Serial.available())  {                           // data received from smartphone
    delay(2);
    cmd[0] =  Serial.read();
    if(cmd[0] == STX)  {
      int i=1;     
      while(Serial.available())  {
        delay(1);
        cmd[i] = Serial.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==7)          getJoystickState(cmd);     // 6 Bytes  ex: < STX "200" "180" ETX >
    }
  }
}

void getJoystickState(byte data[8])    {
  int pwm_L, pwm_R;
  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

  if(joyY >-10)  {                                                    // differential steering
    pwm_L = (joyY - (3*joyX)/4);
    pwm_R = (joyY + (3*joyX)/4);
  }  else  {
    pwm_L = (joyY + (3*joyX)/4);
    pwm_R = (joyY - (3*joyX)/4);
  }
  pwm_L = map(pwm_L, -100, 100, -255, 255);
  pwm_R = map(pwm_R, -100, 100, -255, 255);
  pwm_L = constrain(pwm_L, -255, 255);
  pwm_R = constrain(pwm_R, -255, 255);

  motorControl_Left(pwm_L);
  motorControl_Right(pwm_R);
}
 
void motorControl_Left(int pwmValue)  {
  if(pwmValue >=0) {                                                // forward
    digitalWrite(IN1_L, LOW);
    analogWrite (IN2_L, pwmValue);
  }  else  {                                                        // backward
    analogWrite(IN1_L, -pwmValue);
    digitalWrite (IN2_L, LOW);
  }
}

void motorControl_Right(int pwmValue)  {
  if(pwmValue >=0) {                                                // forward
    digitalWrite(IN3_R, LOW);
    analogWrite(IN4_R, pwmValue);
  }  else  {                                                        // backward
    analogWrite(IN3_R, -pwmValue);
    digitalWrite(IN4_R, LOW);
  }
}

void stopped()   {
  digitalWrite(IN1_L, LOW);
  digitalWrite(IN3_R, LOW);
  digitalWrite(IN2_L, LOW);
  digitalWrite(IN4_R, LOW);
}

Juris3D

#766
Sep 08, 2016, 08:52 am Last Edit: Sep 08, 2016, 09:28 am by Juris3D
Off topic: does anyone tried little physical joysticks, that you stick to touchscreen, in place where virtual joystick on the screen is? Are they working at all? If yes, is it better experience than touch (which does not give you sense, in what relative position your finger/joystick is)?
If not, maybe somewhere in the future there could be DIY physical joystick for tank control, similar to this:
(DIY BT controller transmitter at the end of article)

https://sites.google.com/site/bluetoothrccar/home/6-Joystick-Control

kas

#767
Sep 09, 2016, 11:24 am Last Edit: Sep 09, 2016, 11:32 am by kas
Quote
Then I found a problem in code: in "backward" operations there was digitalWrite for PWM value, and analogWrite for LOW/HIGH values. That needed to be swapped, of course (analog for PWM, digital for LOW/HIGH). Now all joystick movements are perfect
Nice catch
I can't test the code, those copy/paste errors are really difficult to chase   ::)

TankControl V0.5:
Two way communication between Android device and Arduino
Buttons now are enabled
For this demo
- App datafield #1 & #2 display moving numbers, generated by Arduino
- datafield #3 displays buttons status
- button #5 is configured as push/momentary button
Code: [Select]
// ***********************************************************************
// *   TankControl   V0.5                                    @kas 2016   *
// *   Code for driving a generic tank with two motors and L298 driver   *
// ***********************************************************************

// Rev history
// V0.5  Buttons and two way communication
// V0.41 Differential drive (FW/BW/left/right)
// V0.3  Basic moves (FW/BW only), with RC control, no buttons, no feedback
// V0.2  Basic moves, no RC control

// Demo setup:
// Button #5 configured as "push" button (momentary)
// Other buttons display demo message in datafield #3
// Datafields show arbitrary moving numbers

// Arduino pin#0 to TX BlueTooth module
// Arduino pin#1 to RX BlueTooth module
// -->> remove pin#0 connection before downloading the sketch <<--
// make sure your BT board is set @57600 bps

#define   IN1_L           3                             // direction/PWM control for left motor
#define   IN2_L           5                             // direction/PWM control for left motor
#define   IN3_R           6                             // direction/PWM control for right motor
#define   IN4_R           9                             // direction/PWM control for right motor
#define    STX          0x02
#define    ETX          0x03

byte cmd[8] = {0, 0, 0, 0, 0, 0, 0, 0};                 // bytes received
byte buttonStatus = 0;                                  // first Byte sent to Android device
long sendInterval = 600;                                // interval between Buttons status transmission (milliseconds)
String displayStatus = "xxxx";                          // message to Android device

void setup()  {
  pinMode(IN1_L, OUTPUT);
  pinMode(IN2_L, OUTPUT);
  pinMode(IN3_R, OUTPUT);
  pinMode(IN4_R, OUTPUT);
//Serial.begin(57600);        //  XXXXXXX   change to actual BT card baud rate   XXXXXXXXXXXXXXX
  Serial.begin(9600);
  while(Serial.available())  Serial.read();           // empty RX buffer
}

void loop() {
  if(Serial.available())  {                           // data received from smartphone
    delay(2);
    cmd[0] =  Serial.read();  
    if(cmd[0] == STX)  {
      int i=1;      
      while(Serial.available())  {
        delay(1);
        cmd[i] = Serial.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 >
    }
  }
}

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 >    // data example

    Serial.print((char)STX);                                             // Start of Transmission
    Serial.print(getButtonStatusString());  Serial.print((char)0x1);     // buttons status feedback
    Serial.print(GetdataInt1());            Serial.print((char)0x4);     // datafield #1
    Serial.print(GetdataFloat2());          Serial.print((char)0x5);     // datafield #2
    Serial.print(displayStatus);                                         // datafield #3
    Serial.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 pwm_L, pwm_R;
  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
  
  if(joyY >-10)  {                                                    // differential steering
    pwm_L = (joyY - (3*joyX)/4);
    pwm_R = (joyY + (3*joyX)/4);
  }  else  {
    pwm_L = (joyY + (3*joyX)/4);
    pwm_R = (joyY - (3*joyX)/4);
  }
  pwm_L = map(pwm_L, -100, 100, -255, 255);
  pwm_R = map(pwm_R, -100, 100, -255, 255);
  pwm_L = constrain(pwm_L, -255, 255);
  pwm_R = constrain(pwm_R, -255, 255);

  motorControl_Left(pwm_L);
  motorControl_Right(pwm_R);
}

void motorControl_Left(int pwmValue)  {
  if(pwmValue >=0) {                                                // forward
    digitalWrite(IN1_L, LOW);
    analogWrite (IN2_L, pwmValue);
  }  else  {                                                        // backward
    analogWrite (IN1_L, -pwmValue);
    digitalWrite(IN2_L, LOW);
  }
}

void motorControl_Right(int pwmValue)  {
  if(pwmValue >=0) {                                                // forward
    digitalWrite(IN3_R, LOW);
    analogWrite (IN4_R, pwmValue);
  }  else  {                                                        // backward
    analogWrite (IN3_R, -pwmValue);
    digitalWrite(IN4_R, LOW);
  }
}

void stopped()   {                                                  // not used
  digitalWrite(IN1_L, LOW);
  digitalWrite(IN3_R, LOW);
  digitalWrite(IN2_L, LOW);
  digitalWrite(IN4_R, LOW);
}

void getButtonState(int bStatus)  {
  switch (bStatus) {
// -----------------  BUTTON #1  -----------------------
    case 'A':
      buttonStatus |= B000001;        // ON
      // your code...      
      displayStatus = "Button1 <ON>"; // Demo text message
      break;
    case 'B':
      buttonStatus &= B111110;        // OFF
      // your code...      
      displayStatus = "Button2 <OFF>"; // Demo text message
      break;
** see next message


The message exceeds the maximum allowed length (9000 characters)
** see next message

Please adjust your BT card baudrate to 57600



kas


The message exceeds the maximum allowed length (9000 characters)
TankControl   V0.5       part 2


Code: [Select]
// -----------------  BUTTON #2  -----------------------
    case 'C':
      buttonStatus |= B000010;        // ON
      // your code...     
      displayStatus = "Button2 <ON>"; // Demo text message
      break;
    case 'D':
      buttonStatus &= B111101;        // OFF
      // your code...     
      displayStatus = "Button2 <OFF>"; // Demo text message
      break;

// -----------------  BUTTON #3  -----------------------
    case 'E':
      buttonStatus |= B000100;        // ON
      // your code...     
      displayStatus = "button3 <ON>"; // Demo text message
      break;
    case 'F':
      buttonStatus &= B111011;      // OFF
      // your code...     
      displayStatus = "button3 <OFF>";
      break;

// -----------------  BUTTON #4  -----------------------
    case 'G':
      buttonStatus |= B001000;       // ON
      // your code...     
      displayStatus = "button4 <ON>"; // Demo text message
      break;
    case 'H':
      buttonStatus &= B110111;    // OFF
      // your code...     
      displayStatus = "button4 <OFF>"; // Demo text message
     break;

// -----------------  BUTTON #5  -----------------------
    case 'I':           // configured as momentary button
//      buttonStatus |= B010000;        // ON
      // your code...     
      displayStatus = "Button5: <pushed>"; // Demo text message
      break;
//   case 'J':
//     buttonStatus &= B101111;        // OFF
//     // your code...     
//     break;

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

Juris3D

Trying sketch V0.5, tank control is still working, but nothing happens on data fields ("xxx" displayed). Now trying to understand code and trying to guess what is wrong.

kas

#770
Sep 10, 2016, 08:55 am Last Edit: Sep 10, 2016, 09:01 am by kas
Quote
Trying sketch V0.5, tank control is still working, but nothing happens on data fields ("xxx" displayed)
Easy  :-*
Within loop() the sendBlueToothData() function is never called

At the end of loop(), please replace

     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 >
   }
 }
}


by


     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();
}


Let me know the outcome



Juris3D

Easy  :-*
Within loop() the sendBlueToothData() function is never called
.......
Let me know the outcome
Thank You for this correction, now data is being sent from Tank to Phone just fine. Two datafields shows those demo number counters, third field tells about button presses.
Btw, there was cosmetic syntaxis error in "Button1 <OFF>" (was "Button2" written, but that's not important.
Tested with both "Joy RC Commander" and "Total RC Commander", all fine so far (except there is no "Button5" in "TC" to test).

kas

Quote
Tested with both "Joy RC Commander" and "Total RC Commander", all fine so far (except there is no "Button5" in "TC" to test)
Go to Options/Buttons Properties/Buttons to display
and help yourself


Juris3D

Go to Options/Buttons Properties/Buttons to display
and help yourself
Hehe, stupid me! :)
Thanks. I guess, I was too distracted. Or, too sleepy :)
Speaking of Android apk, I think it would be nice it would turn off Bluetooth on program exit. Or, maybe have that as an "Advanced option".

kas

#774
Sep 12, 2016, 08:57 am Last Edit: Sep 12, 2016, 09:02 am by kas
TankControl V0.6:
Turbo mode enabled on Button#1 (gearbox)


Should you modify the code , please post back a copy
 ** reminder **  Please adjust your BT card baudrate to 57600 


Juris3D

TankControl V0.6:
Turbo mode enabled on Button#1 (gearbox)
New V.6 tested, and it is working very well, Button1 changes "gear", when "ON" - maximum speed is higher. Hey, I didn't know my tank chassis can jump! :D  Now I know.
For me, values both Turbo ON and OFF are just fine, did not edit anything.
When tank was on V.5, I did quite extended "field testing" outside (before that only little room driving), and I must say, after very little practice, this control feels fine and natural. I have been driving with two sliders control before, and I was thinking, theoretically, that 2-slider control should be best for tank, but it's not, actually. I like joystick more.
So, once again, thanks for this code! :-)
I wonder, what's next? I would vote for headlights On/Off and floodlight On/Off. Then some night driving outside!  ;-)

** reminder **  Please adjust your BT card baudrate to 57600 
Yes, since version V.5 I have changed my BT module baudrate to 57600.

kas

#776
Sep 15, 2016, 09:01 pm Last Edit: Aug 22, 2018, 06:50 pm by kas
TankControl   V0.7
Led ON/OFF on button#2

Asumption: You have a "main stream" Arduino board with built in led attached to D13

Change #define ledPin 13 to your chosen headlights pin
Should you need high power don't forget to add the requested transistor






Juris3D

TankControl   V0.7
Led ON/OFF on button#2
Asumption: You have a "main stream" Arduino board with built in led attached to D13

Hello, and Thanks again! I just did a quick check (quick, because it is really late night here), and new option, LED On/Off is working. In my case it was Arduino Nano onboard LED (D13).
Now I guess I can clone relevant pieces of code and make several On/Off commands, if needed. That I will try next night, I guess.

kas

Quote
Now I guess I can clone relevant pieces of code and make several On/Off commands
Let me have your V0.71 version once modifications are made


Juris3D

Let me have your V0.71 version once modifications are made


Here it is, version V0.71, with two switchable ON/OFF lights (or whatever you want there).

Headlights:             ON/OFF on button#2 - output pin D13 (and onboard Arduino LED);
Spotlight (Projector): ON/OFF on button#3 - output pin D12.

Code is tested and it is working.
Hardware idea: for powerful night driving spotlight I will probably use some ready made bike light with its own batteries (put a transistor+relay to switch on), so it won't be depleting tank battery.
"Headlights" will be just some white LEDs in front of tank platform.

Go Up