Отставание при использовании Bluetooth-адаптера в Android Studio
Мой проект: Управление 3 шаговыми двигателями по bluetooth. Я использую Android Studio для приложения и Arduino для получения данных Bluetooth и управления степперами.
Моя проблема Когда мой Arduino получает данные, он отстает или пропускает некоторые данные.
Вот мой класс ConnectedThread.java, используемый для связи с 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)
}
}
А вот мой код Arduino
#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();
}
}
}
Я получаю данные по bluetooth на свой Arduino, и моторы вращаются, это просто не очень точно и не хватает нажатия некоторых кнопок.
Пожалуйста, не могли бы вы мне помочь!:)