Tentang bagian pertama
Pada bagian pertama, saya menggambarkan bagian fisik konstruksi dan hanya sepotong kecil kode. Sekarang pertimbangkan komponen perangkat lunak - aplikasi Android dan sketsa Arduino.
Pertama, saya akan memberikan uraian terperinci tentang setiap momen, dan pada akhirnya saya akan meninggalkan tautan ke seluruh proyek + video hasil, yang seharusnya
mengecewakan Anda.
Aplikasi Android
Program untuk android dibagi menjadi dua bagian: yang pertama menghubungkan perangkat melalui Bluetooth, yang kedua adalah kontrol joystick.
Saya memperingatkan Anda - desain aplikasi tidak berhasil sama sekali dan dilakukan pada kesalahan, jika saja itu berhasil. Kemampuan beradaptasi dan UX tidak menunggu, tetapi tidak harus keluar dari layar.
Tata letak
Memulai aktivitas terletak pada tata letak, elemen: tombol dan tata letak untuk daftar perangkat. Tombol memulai proses menemukan perangkat dengan Bluetooth aktif. ListView menampilkan perangkat yang ditemukan.
<?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>
Layar kontrol didasarkan pada tata letak, di mana hanya ada tombol, yang di masa depan akan menjadi joystick. Sebuah tombol dilampirkan ke tombol melalui atribut latar belakang, membuatnya bulat.
TextView tidak digunakan dalam versi final, tetapi pada awalnya ditambahkan untuk debugging: angka yang dikirim melalui bluetooth ditampilkan. Pada tahap awal, saya menyarankan Anda untuk menggunakannya. Tetapi kemudian angka-angka akan mulai dihitung dalam aliran terpisah, dari mana sulit untuk mengakses TextView.
<?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>
File button_control_circle.xml (style), harus diletakkan di folder yang dapat digambar:
<?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>
Anda juga perlu membuat file item_device.xml, diperlukan untuk setiap item daftar:
<?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>
Terwujud
Untuk berjaga-jaga, saya akan memberikan kode manifes lengkap. Anda perlu mendapatkan akses penuh ke bluetooth melalui izin penggunaan dan jangan lupa untuk menunjukkan aktivitas kedua melalui tag aktivitas.
<?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>
Aktivitas utamanya, memasangkan Arduino dan Android
Kami mewarisi kelas dari AppCompatActivity dan mendeklarasikan variabel:
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; }
Saya akan menjelaskan metode onCreate () baris demi baris:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
Fungsi-fungsi di bawah ini memeriksa apakah izin untuk menggunakan bluetooth diperoleh (tanpa izin pengguna kami tidak akan dapat mentransfer data) dan apakah bluetooth diaktifkan:
private boolean permissionGranted() {
Jika semua pemeriksaan dilewati, pencarian perangkat dimulai. Jika salah satu kondisi tidak terpenuhi, maka pemberitahuan akan ditampilkan, mengatakan, "izinkan \ aktifkan?", Dan ini akan diulangi sampai cek dilewati.
Pencarian perangkat dibagi menjadi tiga bagian: menyiapkan daftar, menambah daftar perangkat yang ditemukan, membuat koneksi dengan perangkat yang dipilih.
private void findArduino() {
Ketika modul Bluetooth digantung di Arduino (lebih lanjut tentang ini nanti) ditemukan, itu akan muncul dalam daftar. Dengan mengkliknya, Anda akan mulai membuat soket (Anda mungkin harus menunggu 3-5 detik setelah klik atau klik lagi). Anda akan memahami bahwa koneksi dibuat oleh LED pada modul Bluetooth: tanpa koneksi, mereka berkedip dengan cepat, jika ada koneksi, frekuensinya berkurang.

Kelola dan kirim perintah
Setelah koneksi dibuat, Anda dapat melanjutkan ke aktivitas kedua - ActivityControl. Hanya akan ada lingkaran biru di layar - joystick. Itu dibuat dari Tombol biasa, markup diberikan di atas.
public class ActivityControl extends AppCompatActivity {
Dalam metode onCreate (), semua tindakan utama terjadi:
Perhatikan (!) - kami akan mencari tahu berapa banyak piksel yang ditempati tombol. Berkat ini, kami mendapatkan kemampuan beradaptasi: ukuran tombol akan tergantung pada resolusi layar, tetapi sisa kode akan dengan mudah beradaptasi dengan ini, karena kami tidak memperbaiki ukuran sebelumnya. Nantinya, kami akan mengajarkan aplikasi untuk mencari tahu di mana sentuhan itu berada, dan kemudian menerjemahkannya ke dalam nilai-nilai yang dapat dimengerti untuk arduinki dari 0 hingga 255 (lagipula, sentuhannya bisa 456 piksel dari pusat, dan MK tidak akan bekerja dengan angka itu).
Berikut ini adalah kode untuk ControlDriveInputListener (), kelas ini terletak di kelas aktivitas itu sendiri, setelah metode onCreate (). Berada di file ActivityControl, kelas ControlDriveInputListener menjadi anak, yang berarti ia memiliki akses ke semua variabel dari kelas utama.
Jangan memperhatikan fungsi yang dipanggil saat diklik. Sekarang kami tertarik pada proses menangkap sentuhan: pada titik apa orang itu meletakkan jarinya dan data apa yang akan kami dapatkan tentang itu.
Harap dicatat bahwa saya menggunakan kelas java.util.Timer: ini memungkinkan Anda untuk membuat utas baru yang mungkin mengalami penundaan dan akan diulang beberapa kali setelah setiap detik. Ini harus digunakan untuk situasi berikut: orang itu meletakkan jari, metode ACTION_DOWN bekerja, informasi pergi ke Arduino, dan setelah itu orang tersebut memutuskan untuk tidak menggerakkan jari, karena kecepatan cocok untuknya. Kedua kalinya, metode ACTION_DOWN tidak akan berfungsi, karena pertama-tama Anda perlu menelepon ACTION_UP (untuk mengangkat jari Anda dari layar).
Baiklah, kita memulai loop kelas Timer () dan mulai mengirim data yang sama setiap 10 milidetik. Ketika jari digeser (ACTION_MOVE akan bekerja) atau dinaikkan (ACTION_UP), siklus Timer harus dimatikan sehingga data dari pers lama tidak mulai dikirim lagi.
public class ControlDriveInputListener implements View.OnTouchListener { private Timer timer; @Override public boolean onTouch(View view, MotionEvent motionEvent) {
Perhatikan lagi: metode x dan y mengandalkan onTouch () mengarah dari sudut kiri atas Tampilan. Dalam kasus kami, titik (0; 0) terletak di Tombol di sini:

Sekarang kami telah belajar cara mendapatkan lokasi jari saat ini di tombol, kami akan mencari cara untuk mengonversi piksel (karena x dan y hanyalah jarak dalam piksel) ke nilai yang berfungsi. Untuk melakukan ini, saya menggunakan metode calculAndSendCommand (x, y), yang harus ditempatkan di kelas ControlDriveInputListener. Anda juga akan memerlukan beberapa metode tambahan, kami menulisnya di kelas yang sama setelah calculAndSendCommand (x, y).
private void calculateAndSendCommand(float x, float y) {
Saat data dihitung dan ditransfer, streaming kedua memasuki permainan. Dia bertanggung jawab untuk mengirim informasi. Anda tidak dapat melakukannya tanpa itu, jika tidak, data pemancar soket akan memperlambat penangkapan sentuhan, antrian akan dibuat dan keseluruhan ujungnya akan lebih pendek.
Kelas ConnectedThread juga terletak di kelas ActivityControl.
private class ConnectedThread extends Thread { private final BluetoothSocket socket; private final OutputStream outputStream; public ConnectedThread(BluetoothSocket btSocket) {
Menyimpulkan aplikasi Android
Ringkaslah semua yang rumit di atas.
- Di ActivityMain kami mengkonfigurasi bluetooth, kami membuat koneksi.
- Di ActivityControl kami melampirkan tombol dan mendapatkan data tentang hal itu.
- Kami bertahan pada tombol OnTouchListener, itu menangkap sentuhan, gerakan dan mengangkat jari.
- Data yang diperoleh (titik dengan koordinat x dan y) dikonversi ke sudut rotasi dan kecepatan
- Kami mengirim data, memisahkannya dengan karakter khusus
Dan pemahaman akhir akan datang kepada Anda ketika Anda melihat seluruh kode -
github.com/IDolgopolov/BluetoothWorkAPP.git . Tidak ada kode komentar, sehingga terlihat jauh lebih bersih, lebih kecil dan lebih sederhana.
Sketsa Arduino
Aplikasi Android dibongkar, ditulis, dipahami ... dan di sini akan lebih mudah. Saya akan mencoba mempertimbangkan semuanya secara bertahap, dan kemudian saya akan memberikan tautan ke file lengkap.
Variabel
Pertama, pertimbangkan konstanta dan variabel yang Anda perlukan.
#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;
Metode pengaturan ()
Dalam metode setup () kita mengatur parameter pin: mereka akan bekerja pada input atau output. Kami juga mengatur kecepatan komunikasi komputer dengan arduino, bluetooth dengan 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); }
Metode loop () dan fungsi tambahan
Dalam metode loop berulang berulang (), data dibaca. Pertama, pertimbangkan algoritma utama, dan kemudian fungsi yang terlibat di dalamnya.
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); } } }
Kami mendapatkan hasilnya: dari ponsel kami mengirim byte dengan gaya "@ speed # angle #" (misalnya, perintah khas "@ 200 # 60 #". Siklus ini berulang setiap 100 milidetik, karena pada android kami mengatur interval ini untuk mengirim perintah. Singkatnya, lakukan tidak masuk akal, karena mereka mulai mengantri, dan jika Anda membuatnya lebih lama, roda mulai bergerak tersentak-sentak. Semuapenundaan melalui perintah delay (), yang akan Anda lihat nanti, dipilih bukan melalui perhitungan fisik dan matematika, tetapi secara empiris. zadrezham, mesin berjalan dengan lancar, dan masuk Semua tim punya waktu untuk berolahraga (arus punya waktu untuk berlari.)Dua fungsi sisi digunakan dalam siklus, mereka mengambil data yang diterima dan membuat mesin berputar. 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); }
Hidupkan ketika android mengirimkan data bahwa pengguna menjepit sudut 60, 90, 120, tidak layak, jika tidak Anda tidak akan bisa langsung lurus. Ya, mungkin Anda seharusnya tidak segera mengirim perintah belok dari android jika sudutnya terlalu kecil, tapi menurut saya ini kikuk.Buat sketsa hasil
Sketsa hanya memiliki tiga langkah penting: membaca perintah, memproses batas rotasi, dan memasok arus ke motor. Semuanya terdengar sederhana, dan dalam pelaksanaannya lebih mudah daripada mudah, meskipun itu dibuat untuk waktu yang lama dan dengan tumpul. Versi lengkap sketsa .Pada akhirnya
Inventaris lengkap untuk beberapa bulan pekerjaan telah berakhir. Bagian fisik dibongkar, semua perangkat lunak lebih. Prinsipnya tetap sama - kontak untuk fenomena yang tidak bisa dipahami, kita akan mengerti bersama.Dan komentar di bawah bagian pertama menarik, mereka menyarankan segunung tips yang berguna, terima kasih kepada semua orang.Video Hasil