Über den ersten Teil
Im ersten Teil habe ich den physischen Teil der Konstruktion und nur einen kleinen Code beschrieben. Betrachten Sie nun die Softwarekomponente - eine Android-Anwendung und eine Arduino-Skizze.
Zuerst werde ich jeden Moment detailliert beschreiben und am Ende Links zum gesamten Projekt + Video des Ergebnisses hinterlassen, was Sie
enttäuschen sollte.
Android App
Das Programm für den Android ist in zwei Teile gegliedert: Der erste verbindet das Gerät über Bluetooth, der zweite ist der Steuer-Joystick.
Ich warne Sie - das Design der Anwendung wurde überhaupt nicht ausgearbeitet und wurde auf einen Fehler hin gemacht, wenn es nur funktionierte. Anpassungsfähigkeit und UX warten nicht, sollten sich aber nicht aus dem Bildschirm herausschleichen.
Layout
Die Startaktivität basiert auf Layout, Elementen: Schaltflächen und Layout für eine Liste von Geräten. Die Schaltfläche startet den Vorgang zum Suchen von Geräten mit aktivem Bluetooth. In der ListView werden die gefundenen Geräte angezeigt.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:layout_width="wrap_content" android:layout_height="60dp" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginStart="40dp" android:layout_marginTop="50dp" android:text="@string/start_search" android:id="@+id/button_start_find" /> <Button android:layout_width="wrap_content" android:layout_height="60dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" android:id="@+id/button_start_control" android:text="@string/start_control" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true"/> <ListView android:id="@+id/list_device" android:layout_width="300dp" android:layout_height="200dp" android:layout_marginEnd="10dp" android:layout_marginTop="10dp" android:layout_alignParentEnd="true" android:layout_alignParentTop="true" /> </RelativeLayout>
Der Steuerungsbildschirm basiert auf einem Layout, in dem es nur eine Schaltfläche gibt, die in Zukunft zu einem Joystick wird. Über das Hintergrundattribut wird eine Schaltfläche an die Schaltfläche angehängt, wodurch sie rund wird.
TextView wird in der endgültigen Version nicht verwendet, wurde jedoch ursprünglich zum Debuggen hinzugefügt: Über Bluetooth gesendete Nummern wurden angezeigt. In der Anfangsphase rate ich Ihnen zu verwenden. Dann werden die Zahlen jedoch in einem separaten Stream berechnet, von dem aus es schwierig ist, auf die Textansicht zuzugreifen.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="200dp" android:layout_height="200dp" android:layout_alignParentStart="true" android:layout_alignParentBottom="true" android:layout_marginBottom="25dp" android:layout_marginStart="15dp" android:id="@+id/button_drive_control" android:background="@drawable/button_control_circle" /> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentTop="true" android:minWidth="70dp" android:id="@+id/view_result_touch" android:layout_marginEnd="90dp" /> </RelativeLayout>
Die Datei button_control_circle.xml (Stil) muss im Zeichenordner abgelegt werden:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#00F" /> <corners android:bottomRightRadius="100dp" android:bottomLeftRadius="100dp" android:topRightRadius="100dp" android:topLeftRadius="100dp"/> </shape>
Sie müssen auch die Datei item_device.xml erstellen. Sie wird für jedes Listenelement benötigt:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="150dp" android:layout_height="40dp" android:id="@+id/item_device_textView"/> </LinearLayout>
Manifest
Für alle Fälle gebe ich den vollständigen Manifestcode an. Sie müssen über die Nutzungsberechtigung vollen Zugriff auf Bluetooth erhalten und dürfen nicht vergessen, die zweite Aktivität über das Aktivitäts-Tag anzugeben.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.bluetoothapp"> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name="com.arproject.bluetoothworkapp.MainActivity" android:theme="@style/Theme.AppCompat.NoActionBar" android:screenOrientation="landscape"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.arproject.bluetoothworkapp.ActivityControl" android:theme="@style/Theme.AppCompat.NoActionBar" android:screenOrientation="landscape"/> </application> </manifest>
Die Hauptaktivität ist das Pairing von Arduino und Android
Wir erben die Klasse von AppCompatActivity und deklarieren die Variablen:
public class MainActivity extends AppCompatActivity { private BluetoothAdapter bluetoothAdapter; private ListView listView; private ArrayList<String> pairedDeviceArrayList; private ArrayAdapter<String> pairedDeviceAdapter; public static BluetoothSocket clientSocket; private Button buttonStartControl; }
Ich werde die onCreate () -Methode Zeile für Zeile beschreiben:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
Die folgenden Funktionen prüfen, ob die Berechtigung zur Verwendung von Bluetooth erhalten wurde (ohne die Erlaubnis des Benutzers können wir keine Daten übertragen) und ob Bluetooth aktiviert ist:
private boolean permissionGranted() {
Wenn alle Prüfungen bestanden sind, beginnt die Gerätesuche. Wenn eine der Bedingungen nicht erfüllt ist, wird eine Benachrichtigung mit der Aufschrift "allow \ enable?" Angezeigt. Diese wird wiederholt, bis die Prüfung bestanden ist.
Die Gerätesuche gliedert sich in drei Teile: Erstellen der Liste, Hinzufügen zur Liste der gefundenen Geräte, Herstellen einer Verbindung mit dem ausgewählten Gerät.
private void findArduino() {
Wenn ein Bluetooth-Modul gefunden wird, das an einem Arduino hängt (dazu später mehr), wird es in der Liste angezeigt. Wenn Sie darauf klicken, erstellen Sie einen Socket (möglicherweise müssen Sie nach einem Klick 3-5 Sekunden warten oder erneut klicken). Sie werden verstehen, dass die Verbindung durch die LEDs am Bluetooth-Modul hergestellt wird: Ohne Verbindung blinken sie schnell, wenn eine Verbindung besteht, nimmt die Frequenz merklich ab.

Befehle verwalten und senden
Nachdem die Verbindung hergestellt wurde, können Sie mit der zweiten Aktivität fortfahren - ActivityControl. Auf dem Bildschirm wird nur ein blauer Kreis angezeigt - der Joystick. Es wird aus dem üblichen Button hergestellt, das Markup ist oben angegeben.
public class ActivityControl extends AppCompatActivity {
In der onCreate () -Methode finden alle Hauptaktionen statt:
Achten Sie darauf (!) - wir werden herausfinden, wie viele Pixel die Schaltfläche belegt. Dank dessen erhalten wir Anpassungsfähigkeit: Die Größe der Schaltfläche hängt von der Bildschirmauflösung ab, aber der Rest des Codes kann sich leicht daran anpassen, da wir die Größen nicht im Voraus festlegen. Später werden wir der Anwendung beibringen, herauszufinden, wo sich die Berührung befand, und sie dann in Werte übersetzen, die für Arduinki von 0 bis 255 verständlich sind (schließlich kann die Berührung 456 Pixel von der Mitte entfernt sein, und MK funktioniert nicht mit dieser Zahl).
Das Folgende ist der Code für ControlDriveInputListener (). Diese Klasse befindet sich in der Klasse der Aktivität selbst nach der onCreate () -Methode. In der ActivityControl-Datei wird die ControlDriveInputListener-Klasse zu einem untergeordneten Element, dh sie hat Zugriff auf alle Variablen der Hauptklasse.
Achten Sie nicht auf die Funktionen, die beim Klicken aufgerufen werden. Jetzt interessieren wir uns für den Prozess des Berührens: An welchem Punkt hat die Person ihren Finger gelegt und welche Daten werden wir darüber erhalten.
Bitte beachten Sie, dass ich die Klasse java.util.Timer verwende: Sie ermöglicht es Ihnen, einen neuen Thread zu erstellen, der möglicherweise eine Verzögerung aufweist und nach jeder n-ten Anzahl von Sekunden unendlich oft wiederholt wird. Es sollte für die folgende Situation verwendet werden: Die Person legte einen Finger, die ACTION_DOWN-Methode funktionierte, die Informationen gingen an das Arduino, und danach entschied sich die Person, den Finger nicht zu bewegen, da die Geschwindigkeit zu ihr passt. Beim zweiten Mal funktioniert die Methode ACTION_DOWN nicht, da Sie zuerst ACTION_UP aufrufen müssen (um Ihren Finger vom Bildschirm zu heben).
Nun, wir starten die Timer () - Klassenschleife und senden alle 10 Millisekunden dieselben Daten. Wenn der Finger verschoben (ACTION_MOVE funktioniert) oder angehoben (ACTION_UP), muss der Timer-Zyklus abgebrochen werden, damit die Daten der alten Presse nicht erneut gesendet werden.
public class ControlDriveInputListener implements View.OnTouchListener { private Timer timer; @Override public boolean onTouch(View view, MotionEvent motionEvent) {
Achten Sie noch einmal: Die x- und y-Methode onTouch () führt von der oberen linken Ecke der Ansicht aus. In unserem Fall befindet sich der Punkt (0; 0) hier bei Button:

Nachdem wir gelernt haben, wie die aktuelle Position des Fingers auf den Schaltflächen ermittelt wird, werden wir herausfinden, wie Pixel (da x und y nur der Abstand in Pixel sind) in Arbeitswerte konvertiert werden. Dazu verwende ich die Methode berechneAndSendCommand (x, y), die in die ControlDriveInputListener-Klasse eingefügt werden muss. Sie benötigen auch einige Hilfsmethoden, die wir nach berechneAndSendCommand (x, y) in dieselbe Klasse schreiben.
private void calculateAndSendCommand(float x, float y) {
Wenn die Daten berechnet und übertragen werden, kommt der zweite Stream ins Spiel. Er ist für das Versenden von Informationen verantwortlich. Sie können nicht darauf verzichten, da sonst der Socket, der Daten überträgt, die Erfassung von Berührungen verlangsamt, eine Warteschlange erstellt wird und das gesamte Ende kürzer wird.
Die ConnectedThread-Klasse befindet sich auch in der ActivityControl-Klasse.
private class ConnectedThread extends Thread { private final BluetoothSocket socket; private final OutputStream outputStream; public ConnectedThread(BluetoothSocket btSocket) {
Zusammenfassung der Android-Anwendung
Fassen Sie kurz alle oben umständlichen zusammen.
- In ActivityMain konfigurieren wir Bluetooth und stellen eine Verbindung her.
- Im ActivityControl hängen wir die Schaltfläche an und erhalten Daten darüber.
- Wir hängen an der OnTouchListener-Taste, die Berührungen, Bewegungen und das Heben eines Fingers erfasst.
- Die erhaltenen Daten (Punkt mit x- und y-Koordinaten) werden in Drehwinkel und Geschwindigkeit umgewandelt
- Wir senden Daten und trennen sie durch Sonderzeichen
Das endgültige Verständnis erhalten Sie, wenn Sie sich den gesamten Code ansehen -
github.com/IDolgopolov/BluetoothWorkAPP.git . Es gibt keinen Kommentarcode, daher sieht es viel sauberer, kleiner und einfacher aus.
Skizze Arduino
Die Android-Anwendung wird zerlegt, geschrieben, verstanden ... und hier wird es einfacher. Ich werde versuchen, alles schrittweise zu betrachten, und dann werde ich einen Link zur vollständigen Datei geben.
Variablen
Betrachten Sie zunächst die Konstanten und Variablen, die Sie benötigen.
#include <SoftwareSerial.h> // \ // SoftwareSerial BTSerial(8, 9); // int speedRight = 6; int dirLeft = 3; int speedLeft = 11; int dirRight = 7; // , int angleDirection = 4; int angleSpeed = 5; //, , // int pinAngleStop = 12; // String val; // int speedTurn = 180; //, // int pinRed = A0; int pinWhite = A1; int pinBlack = A2; // long lastTakeInformation; //, , boolean readAngle = false; boolean readSpeed = false;
Setup () -Methode
In der setup () -Methode legen wir die Parameter der Pins fest: Sie funktionieren bei der Eingabe oder Ausgabe. Wir stellen auch die Kommunikationsgeschwindigkeit des Computers mit dem Arduino ein, Bluetooth mit dem Arduino.
void setup() { pinMode(dirLeft, OUTPUT); pinMode(speedLeft, OUTPUT); pinMode(dirRight, OUTPUT); pinMode(speedRight, OUTPUT); pinMode(pinRed, INPUT); pinMode(pinBlack, INPUT); pinMode(pinWhite, INPUT); pinMode(pinAngleStop, OUTPUT); pinMode(angleDirection, OUTPUT); pinMode(angleSpeed, OUTPUT); // HC-05 // , BTSerial.begin(38400); // Serial.begin(9600); }
Loop () -Methode und zusätzliche Funktionen
Bei der sich ständig wiederholenden loop () -Methode werden Daten gelesen. Betrachten Sie zuerst den Hauptalgorithmus und dann die damit verbundenen Funktionen.
void loop() { // if(BTSerial.available() > 0) { // char a = BTSerial.read(); if (a == '@') { // @ ( ) // val val = ""; //, readSpeed = true; } else if (readSpeed) { // // val if(a == '#') { // , // Serial.println(val); //, readSpeed = false; // go(val.toInt()); // val val = ""; // , return; } val+=a; } else if (a == '*') { // readAngle = true; } else if (readAngle) { // , // , val if(a == '#') { Serial.println(val); Serial.println("-----"); readAngle = false; // turn(val.toInt()); val= ""; return; } val+=a; } // lastTakeInformation = millis(); } else { // , 150 // if(millis() - lastTakeInformation > 150) { lastTakeInformation = 0; analogWrite(angleSpeed, 0); analogWrite(speedRight, 0); analogWrite(speedLeft, 0); } } }
: "@##" (, "@200#60#". 100 , . , , , .
delay(), , - , . , , ( ).
, .
void go(int mySpeed) { // 0 if(mySpeed > 0) { // digitalWrite(dirRight, HIGH); analogWrite(speedRight, mySpeed); digitalWrite(dirLeft, HIGH); analogWrite(speedLeft, mySpeed); } else { // 0, digitalWrite(dirRight, LOW); analogWrite(speedRight, abs(mySpeed) + 30); digitalWrite(dirLeft, LOW); analogWrite(speedLeft, abs(mySpeed) + 30); } delay(10); } void turn(int angle) { // digitalWrite(pinAngleStop, HIGH); // , delay(5); // 150 , // 30 , // 31 149 if(angle > 149) { // , // , // return if( digitalRead(pinWhite) == HIGH && digitalRead(pinBlack) == LOW && digitalRead(pinRed) == LOW) { return; } // // digitalWrite(angleDirection, HIGH); analogWrite(angleSpeed, speedTurn); } else if (angle < 31) { if(digitalRead(pinRed) == HIGH && digitalRead(pinBlack) == HIGH && digitalRead(pinWhite) == HIGH) { return; } digitalWrite(angleDirection, LOW); analogWrite(angleSpeed, speedTurn); } // digitalWrite(pinAngleStop, LOW); delay(5); }
, , 60, 90, 120, , . , , , - .
: , . , , , .
.
. , . — , .
, ,
.