Lag when using Bluetooth Adapter in Android Studio

381 Views Asked by At

My project: Controlling 3 stepper motors with bluetooth. I'm using Android Studio for the app and Arduino to receive the bluetooth data and control the Steppers.

My Problem When my Arduino receives the data it lags or misses some of the data.

Here is my ConnectedThread.java class used to communicate to the Arduino.

    package com.example.a3axisrig;
    
    import android.bluetooth.BluetoothSocket;
    import android.os.Handler;
    import android.os.SystemClock;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    import static com.example.a3axisrig.MainActivityKt.MESSAGE_READ;
    
    public class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private final Handler mHandler;
    
        public ConnectedThread(BluetoothSocket socket, Handler handler) {
            mmSocket = socket;
            mHandler = handler;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
    
            // Get the input and output streams, using temp objects because
            // member streams are final
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) { }
    
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }
    
        @Override
        public void run() {
            byte[] buffer = new byte[1024];  // buffer store for the stream
            int bytes; // bytes returned from read()
            // Keep listening to the InputStream until an exception occurs
            while (true) {
                try {
                    // Read from the InputStream
                    bytes = mmInStream.available();
                    if(bytes != 0) {
                        buffer = new byte[1024];
                        SystemClock.sleep(10); //pause and wait for rest of data. Adjust this depending on your sending speed.
                        bytes = mmInStream.available(); // how many bytes are ready to be read?
                        bytes = mmInStream.read(buffer, 0, bytes); // record how many bytes we actually read
                        mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                                .sendToTarget(); // Send the obtained bytes to the UI activity
                    }
                } catch (IOException e) {
                    e.printStackTrace();
    
                    break;
                }
            }
        }
    
        /* Call this from the main activity to send data to the remote device */
        public void write(char input) {
            //byte[] bytes = input.getBytes();           //converts entered String into bytes
            try {
                mmOutStream.write(input);
            } catch (IOException e) { }
        }
    
        /* Call this from the main activity to shutdown the connection */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }



And here's my Activity class(TimelapseActivity.kt) that inflates the view with a joystick to control two motors and a Left and Right buttons to control a 3rd motor.

package com.example.a3axisrig

import android.R.attr.button
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity


private lateinit var setButton: Button
private lateinit var doneButton: Button
private lateinit var backButton: Button
private lateinit var point1Button: Button
private lateinit var point2Button: Button
private lateinit var point3Button: Button

private lateinit var rightButton: Button
private lateinit var leftButton:Button


private var points: Int = 0

class TimelapseActivity : AppCompatActivity(), JoystickView.JoystickListener {
    @SuppressLint("ClickableViewAccessibility")
    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_timelapse)
        val joystick = JoystickView(this)

        window.decorView.setBackgroundColor(Color.argb(255, 212, 212, 212));

        setButton = findViewById(R.id.set_button)
        doneButton = findViewById(R.id.done_button)
        backButton = findViewById(R.id.back_button)

        rightButton = findViewById(R.id.right_button)
        leftButton = findViewById(R.id.left_button)

        point1Button = findViewById(R.id.point1Button)
        point2Button = findViewById(R.id.point2Button)
        point3Button = findViewById(R.id.point3Button)
        point1Button.setBackgroundColor(Color.BLUE)
        point2Button.setBackgroundColor(Color.BLUE)
        point3Button.setBackgroundColor(Color.BLUE)

        setButton.setOnClickListener { view: View ->
            points++
            updatePoints()
            if (mConnectedThread != null) { //First check to make sure thread created
                if (points==1) mConnectedThread!!.write('i') else if (points ==2)mConnectedThread!!.write('o')

            }


        }
        doneButton.setOnClickListener { view: View ->
            val intent = Intent(this, TimelapseSetupActivity::class.java)
            startActivity(intent)
            if (mConnectedThread != null) { //First check to make sure thread created
                mConnectedThread!!.write('r')
            }

        }
        backButton.setOnClickListener { view: View ->
            points=0
            updatePoints()
        }

        leftButton.setOnTouchListener(OnTouchListener { v, event ->
            if (event.action == MotionEvent.ACTION_DOWN) {
                if (mConnectedThread != null) { //First check to make sure thread created
                    mConnectedThread!!.write('a')
                }
            } else if (event.action == MotionEvent.ACTION_UP) {
                if (mConnectedThread != null) { //First check to make sure thread created
                    mConnectedThread!!.write('1')
                }
            }
            true
        })


        rightButton.setOnTouchListener(OnTouchListener { v, event ->
            if (event.action == MotionEvent.ACTION_DOWN) {
                if (mConnectedThread != null) { //First check to make sure thread created
                    mConnectedThread!!.write('9')
                }
            } else if (event.action == MotionEvent.ACTION_UP) {
                if (mConnectedThread != null) { //First check to make sure thread created
                    mConnectedThread!!.write('1')
                }
            }
            true
        })

    }

    override fun onJoystickMoved(xPercent: Float, yPercent: Float, id: Int) {
        Log.d("Joystick", "X percent: $xPercent Y percent: $yPercent")
        if (mConnectedThread != null) { //First check to make sure thread created
            if (xPercent > 0.1 && yPercent >0.1) mConnectedThread!!.write('1') //Pan Right & Tilt Up
            else if (xPercent > 0.1 && yPercent< -0.1) mConnectedThread!!.write('2') //Pan Right & Tilt Down
            else if (xPercent < -0.1 && yPercent > 0.1) mConnectedThread!!.write('3') //Pan Left & Tilt Up
            else if (xPercent < -0.1 && yPercent < -0.1) mConnectedThread!!.write('4') //Pan Left & Tilt Down
            else if (xPercent > 0.1) mConnectedThread!!.write('5') //Pan Right
            else if (xPercent < -0.1) mConnectedThread!!.write('6') //Pan Left
            else if (yPercent > 0.1) mConnectedThread!!.write('7') //Tilt Up
            else if (yPercent < -0.1) mConnectedThread!!.write('8') //Tilt Down
            else mConnectedThread!!.write('9')
        }


    }
}



private fun updatePoints(){
    if(points ==1){
        point1Button.setBackgroundColor(Color.GREEN)
    }else if (points ==2){
        point2Button.setBackgroundColor(Color.GREEN)
    }else if (points >=3){
        point3Button.setBackgroundColor(Color.GREEN)
    }else{
        point1Button.setBackgroundColor(Color.BLUE)
        point2Button.setBackgroundColor(Color.BLUE)
        point3Button.setBackgroundColor(Color.BLUE)

    }
}

And here's my arduino code

#include <AccelStepper.h>
#include <MultiStepper.h>
#include <SPI.h>
#include <Wire.h>
// -----------------------------------BLUETOOTH-----------------------------------


#include <SoftwareSerial.h> // use the software uart
SoftwareSerial bluetooth(0 , 1); // RX, TX


// -----------------------------------MENU-----------------------------------
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
//#include <Adafruit_PCD8544.h>
#include <ClickEncoder.h>
#include <TimerOne.h>

int contrast = 60;

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

int menuitem = 1;
int frame = 1;
int page = 0;
int mainMenuItem = 1;
int subMenuItem1 = 1;
int subMenuItem2 = 1;
int subMenuItem3 = 1;
int subMenuItem4 = 1;

String menuItem1 = "Setup";
String menuItem2 = "Free Control";
String menuItem3 = "Saved";
String menuItem4 = "Settings";
String setupItem1 = "Timelapse";
String setupItem2 = "Video";
String setupItem3 = "Back";

boolean up = false;
boolean down = false;
boolean middle = false;

bool sliderLengthSet = false;

// ------------------------------------------------------------------------------------

#define JoyX A0
#define JoyY A1
#define JoySwitch 10  // Joystick switch connected

// Rotary Encoder
#define CLK 8
#define DT 9
#define SW 12
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
unsigned long lastButtonPress = 0;

ClickEncoder *encoder;
int16_t last, value;

//Opto Trigger
#define opto 13

//limitSwitch
#define limitSwitch 11

AccelStepper tiltStepper(1, 2, 3);
AccelStepper panStepper(1, 4, 5);
AccelStepper sliderStepper(1, 6, 7);
MultiStepper StepperControl;

int JoyXPos = 0;
int JoyYPos = 0;
int tiltSpeed = 60;
int sliderSpeed = 1500;
int panSpeed = 10;
int inOutSpeed = 0;

int panInPoint = 0;
int tiltInPoint = 0;
int sliderInPoint = 0;
int panOutPoint = 0;
int tiltOutPoint = 0;
int sliderOutPoint = 0;

int sliderLength = 43; //in cm (725steps = 1cm)
int sliderLengthInSteps = (sliderLength * 725) - 100; //725steps = 1cm && 100 steps as safety

long gotoposition[3]; // An array to store the In or Out position for each stepper motor

//Rotary Encoder

int clickCount = 0;

bool rotaryEncoderMenuState = true; //true when in Menu mode, false when in slider mode

//Timer
long duration = 3000; //ms

//Intervalometer
int shotDuration = 100; //in ms
int interval = 3000; //in ms
int numberOfShots = 0;

char lastReading;

char bt ;


// ------------------------------------------------------------------ SETUP -------------------------------------------------------------------
void setup() {
  //Bluetooth
  bluetooth.begin(9600); // start the bluetooth uart at 9600 which is its default
  //delay(3000); // wait for settings to take affect.

  //Set initial speed values for the steppers
  tiltStepper.setMaxSpeed(6000);
  tiltStepper.setSpeed(0);
  tiltStepper.setAcceleration(10);
  panStepper.setMaxSpeed(6000);
  panStepper.setSpeed(0);
  panStepper.setAcceleration(5);
  sliderStepper.setMaxSpeed(6000);
  sliderStepper.setSpeed(0);
  sliderStepper.setAcceleration(10);

  StepperControl.addStepper(panStepper);
  StepperControl.addStepper(tiltStepper);
  StepperControl.addStepper(sliderStepper);

  //limitSwitch
  pinMode(limitSwitch, INPUT_PULLUP);

  //Opto Trigger
  pinMode(opto, OUTPUT);


  //  //Go to Home
  //  while (digitalRead(limitSwitch) == 0) {
  //    sliderStepper.setSpeed(500);
  //    sliderStepper.runSpeed();
  //    sliderStepper.setCurrentPosition(0); // When limit switch pressed set position to 0 steps
  //  }
  //  delay(20);
  //  // Move 200 steps back from the limit switch
  //  while (sliderStepper.currentPosition() != -500) {
  //    sliderStepper.setSpeed(-1500);
  //    sliderStepper.run();
  //  }


}



// ------------------------------------------------------------------ LOOP -------------------------------------------------------------------
void loop() {

  //
  //  if (digitalRead(limitSwitch) != 0 || sliderStepper.currentPosition() < sliderLengthInSteps + 3000) {
  //    sliderStepper.setSpeed(1500);
  //    sliderStepper.run();
  //  }



  if (bluetooth.available()) {
    bt = bluetooth.read();
  }

  int inOutSpeed = 500; //steps per second

  // -----------------------------------BLUETOOTH-----------------------------------
  lastReading = bluetooth.read();

  //  while (lastReading == bluetooth.read()) {
  //    lastReading = bluetooth.read();
  switch (bt) {
    case '1':
      sliderStepper.setSpeed(0);
      tiltStepper.setSpeed(0);
      panStepper.setSpeed(0);
      break;
    case '2':
      panStepper.setSpeed(500);
      bluetooth.print("Pan Right");
      break;
    case '3':
      panStepper.setSpeed(-500);
      bluetooth.print("Pan Left");
      break;
    case '4':
      tiltStepper.setSpeed(-500);
      bluetooth.print("Tilt Down");
      break;
    case '5':
      tiltStepper.setSpeed(500);
      bluetooth.print("Tilt Up");
      break;
    case '6':
      //sliderStepper.setSpeed(500);
      bluetooth.print("Slider Right");
      break;
    case '7':
      //sliderStepper.setSpeed(-500);
      bluetooth.print("Slider Left");
      break;
    case '9':
      sliderStepper.setSpeed(500);
      break;
    case 'a':
      sliderStepper.setSpeed(-500);
      break;
    case 'i':
      panInPoint = panStepper.currentPosition();
      tiltInPoint = tiltStepper.currentPosition();
      sliderInPoint = sliderStepper.currentPosition();
      break;
    case 'o':
      panOutPoint = panStepper.currentPosition();
      tiltOutPoint = tiltStepper.currentPosition();
      sliderOutPoint = sliderStepper.currentPosition();
      gotoposition[0] = panInPoint;
      gotoposition[1] = tiltInPoint;
      gotoposition[2] = sliderInPoint;
      //inOutSpeed = findSpeed();
      panStepper.setMaxSpeed(500);
      tiltStepper.setMaxSpeed(300);
      sliderStepper.setMaxSpeed(500);
      StepperControl.moveTo(gotoposition); // Calculates the required speed for all motors
      while (panStepper.distanceToGo() != 0 || tiltStepper.distanceToGo() != 0 || sliderStepper.distanceToGo() != 0) {
        StepperControl.run(); // Blocks until all are in position
      }
      delay(200);
      bluetooth.println("o called");
      break;
    case'r':
      bluetooth.println("r called");
      gotoposition[0] = panOutPoint;
      gotoposition[1] = tiltOutPoint;
      gotoposition[2] = sliderOutPoint;
      inOutSpeed = findSpeed();

      bluetooth.println(inOutSpeed);

      panStepper.setMaxSpeed(inOutSpeed);
      tiltStepper.setMaxSpeed(inOutSpeed);
      sliderStepper.setMaxSpeed(inOutSpeed);
      StepperControl.moveTo(gotoposition); // Calculates the required speed for all motors
      //StepperControl.runSpeedToPosition(); // Blocks until all are in position

      long timePassed = millis();
      long lastTrigger = millis();
      while (panStepper.distanceToGo() != 0 || tiltStepper.distanceToGo() != 0 || sliderStepper.distanceToGo() != 0) {
        StepperControl.run();
        if ((millis() - lastTrigger) / 1000 == interval / 1000) {
          Serial.println(lastTrigger);
          digitalWrite(opto, HIGH);
          delay(shotDuration);      // wait for a shutter speed
          digitalWrite(opto, LOW);
          //delay(interval+duration);
          Serial.println("triggered");
          lastTrigger = millis();

        }

      }
      break;
  }

  sliderStepper.runSpeed();
  tiltStepper.runSpeed();
  panStepper.runSpeed();
  }

int findSpeed() {
  numberOfShots = duration / (interval + shotDuration);
  if (abs(panInPoint - panOutPoint) > abs(tiltInPoint - tiltOutPoint) && abs(panInPoint - panOutPoint) > abs(sliderInPoint - sliderOutPoint)) {
    return (abs(panInPoint - panOutPoint) / ((duration - (shotDuration * numberOfShots)) / 1000));
  } else if (abs(tiltInPoint - tiltOutPoint) > abs(panInPoint - panOutPoint) && abs(tiltInPoint - tiltOutPoint) > abs(sliderInPoint - sliderOutPoint)) {
    return (abs(tiltInPoint - tiltOutPoint) / ((duration - (shotDuration * numberOfShots)) / 1000));
  } else {
    return (abs(sliderInPoint - sliderOutPoint) / ((duration - (shotDuration * numberOfShots)) / 1000));
  }
}


//----------------------------------------------------- DRAW MENU -----------------------------------------------------


void drawMenu()
{




  //Setup->Timelapse->Next
  panInPoint = panStepper.currentPosition();
  tiltInPoint = tiltStepper.currentPosition();
  sliderInPoint = sliderStepper.currentPosition();


  //Save Out Point
  int inOutSpeed = 0;
  panOutPoint = panStepper.currentPosition();
  tiltOutPoint = tiltStepper.currentPosition();
  sliderOutPoint = sliderStepper.currentPosition();
  Serial.println("out");
  Serial.println(panOutPoint);
  Serial.println(tiltOutPoint);
  Serial.println(sliderOutPoint);


  //go to Start Point
  gotoposition[0] = panInPoint;
  gotoposition[1] = tiltInPoint;
  gotoposition[2] = sliderInPoint;
  Serial.println("in");
  Serial.println(gotoposition[0]);
  Serial.println(gotoposition[1]);
  Serial.println(gotoposition[2]);
  //inOutSpeed = findSpeed();
  panStepper.setMaxSpeed(500);
  tiltStepper.setMaxSpeed(300);
  sliderStepper.setMaxSpeed(1000);
  StepperControl.moveTo(gotoposition); // Calculates the required speed for all motors
  StepperControl.runSpeedToPosition(); // Blocks until all are in position
  delay(200);

  //Setup->Timelapse->Next->Next-> Start
  // Execute Move

  gotoposition[0] = panOutPoint;
  gotoposition[1] = tiltOutPoint;
  gotoposition[2] = sliderOutPoint;
  inOutSpeed = findSpeed();

  panStepper.setMaxSpeed(inOutSpeed);
  tiltStepper.setMaxSpeed(inOutSpeed);
  sliderStepper.setMaxSpeed(inOutSpeed);
  StepperControl.moveTo(gotoposition); // Calculates the required speed for all motors
  //StepperControl.runSpeedToPosition(); // Blocks until all are in position

  long timePassed = millis();
  long lastTrigger = millis();
  while (panStepper.distanceToGo() != 0 || tiltStepper.distanceToGo() != 0 || sliderStepper.distanceToGo() != 0) {
    StepperControl.run();
    if ((millis() - lastTrigger) / 1000 == interval / 1000) {
      Serial.println(lastTrigger);
      digitalWrite(opto, HIGH);
      delay(shotDuration);      // wait for a shutter speed
      digitalWrite(opto, LOW);
      //delay(interval+duration);
      Serial.println("triggered");
      lastTrigger = millis();

    }

  }



}

I'm receiving bluetooth data to my Arduino and the motors are turning, it's just not very accurate and misses some button presses.

Please could you help me! :)

0

There are 0 best solutions below