Bekerja dengan USB Custom HID di Android

Mempertimbangkan oleh Freepik
Dalam aplikasi Android modern untuk berinteraksi dengan perangkat lain, protokol transfer data nirkabel, seperti Bluetooth, paling sering digunakan. Pada tahun-tahun ketika beberapa perangkat memiliki pengisian nirkabel, sulit untuk membayangkan sekelompok perangkat Android dan periferal, yang membutuhkan penggunaan antarmuka kabel. Namun, ketika kebutuhan seperti itu muncul, USB segera datang ke pikiran.

Mari kita bawa kasus hipotetis bersama Anda. Bayangkan seorang pelanggan mendatangi Anda dan berkata: “Saya membutuhkan aplikasi Android untuk mengontrol perangkat pengumpulan data dan menampilkan data ini di layar. Ada satu TETAPI - aplikasi harus ditulis pada komputer papan tunggal dengan sistem operasi Android, dan perangkat periferal terhubung melalui USB ”

Kedengarannya fantastis, tetapi kadang-kadang terjadi Dan di sini sangat berguna untuk memiliki pengetahuan yang mendalam tentang tumpukan USB dan protokolnya, tetapi artikel ini bukan tentang itu. Pada artikel ini, kita akan melihat cara mengontrol perangkat periferal menggunakan protokol USB Custom HID dari perangkat Android. Untuk kesederhanaan, kami akan menulis aplikasi Android (HOST) yang akan mengontrol LED pada perangkat periferal (PERANGKAT) dan menerima status tombol (tekan). Saya tidak akan memberikan kode untuk papan periferal, yang tertarik - tulis di komentar.

Jadi mari kita mulai.

Teori Sesingkat mungkin


Pertama, sedikit teori, sesingkat mungkin. Ini adalah minimum yang disederhanakan, cukup untuk memahami kode, tetapi untuk pemahaman yang lebih baik, saya sarankan Anda untuk membiasakan diri dengan sumber ini .

Untuk berkomunikasi melalui USB pada perangkat periferal, perlu untuk mengimplementasikan antarmuka interaksi. Berbagai fungsi (misalnya, USB HID, USB Mass Strorage atau USB CDC) akan mengimplementasikan antarmuka mereka, dan beberapa akan memiliki beberapa antarmuka. Setiap antarmuka berisi titik akhir - saluran komunikasi khusus, semacam clipboard.

Perangkat saya memiliki HID Kustom dengan satu antarmuka dan dua titik akhir, satu untuk menerima dan satu untuk transmisi. Biasanya informasi dengan antarmuka dan titik akhir yang ada pada perangkat ditulis dalam spesifikasi untuk perangkat, jika tidak mereka dapat ditentukan melalui program khusus, misalnya USBlyzer.

Perangkat di USB HID berkomunikasi melalui laporan. Apa itu laporan? Karena data ditransmisikan melalui titik akhir, kita perlu mengidentifikasi dan menguraikannya sesuai dengan protokol. Perangkat tidak hanya saling melempar byte data, tetapi juga menukar paket yang memiliki struktur yang jelas, yang dijelaskan pada perangkat dalam deskriptor laporan khusus. Dengan demikian, menurut deskriptor laporan, kita dapat secara akurat menentukan pengidentifikasi, struktur, ukuran dan frekuensi transmisi mana yang memiliki data tertentu. Paket diidentifikasi oleh byte pertama, yang merupakan ID laporan. Misalnya, status tombol masuk ke laporan dengan ID = 1, dan kami mengontrol LED melalui laporan dengan ID = 2.

Jauh dari besi, lebih dekat ke Android


Di Android, dukungan untuk perangkat USB muncul dimulai dengan API versi 12 (Android 3.1) .Untuk bekerja dengan perangkat periferal, kita perlu menerapkan mode host USB. Bekerja dengan USB dijelaskan dengan sangat baik dalam dokumentasi.

Pertama, Anda perlu mengidentifikasi perangkat Anda yang terhubung, di antara seluruh jenis perangkat USB. Perangkat USB diidentifikasi oleh kombinasi vid (vendor id) dan pid (id produk). Di folder xml, buat file device_filter.xml dengan konten berikut:

<resources> <usb-device vendor-id="1155" product-id="22352" /> </resources> 

Sekarang Anda perlu membuat izin dan tindakan yang sesuai (jika Anda membutuhkannya) di manifes aplikasi:

 <uses-permission android:name="android.permission.USB_PERMISSION" /> <uses-feature android:name="android.hardware.usb.host" /> <activity android:name=".MainActivity"> <intent-ilter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity> 

Di android: resource kami menentukan file dengan filter yang diperlukan untuk perangkat. Juga, seperti yang saya katakan sebelumnya, Anda dapat menetapkan filter maksud untuk meluncurkan aplikasi, misalnya, sebagai akibat dari menghubungkan perangkat Anda.

Pertama, Anda perlu mendapatkan UsbManager, cari perangkat, antarmuka, dan titik akhir perangkat. Ini harus dilakukan setiap kali perangkat terhubung.

 val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager private var usbConnection: UsbDeviceConnection? = null private var usbInterface: UsbInterface? = null private var usbRequest: UsbRequest? = null private var usbInEndpoint: UsbEndpoint? = null private var usbOutEndpoint: UsbEndpoint? = null fun enumerate(): Boolean { val deviceList = usbManager.deviceList for (device in deviceList.values) { /*      VID  PID */ if ((device.vendorId == VENDOR_ID) and (device.productId == PRODUCT_ID)) { /*      */ usbInterface = device.getInterface(CUSTOM_HID_INTERFACE) /*            */ for (idx in 0 until usbInterface!!.endpointCount) { if (usbInterface?.getEndpoint(idx)?.direction == USB_DIR_IN) usbInEndpoint = usbInterface?.getEndpoint(idx) else usbOutEndpoint = usbInterface?.getEndpoint(idx) } usbConnection = usbManager.openDevice(device) usbConnection?.claimInterface(usbInterface, true) usbRequest = UsbRequest() usbRequest?.initialize(usbConnection, usbInEndpoint) } } /*    */ return usbConnection != null } 

Di sini kita melihat antarmuka dan titik akhir yang dibahas pada bagian terakhir. Mengetahui nomor antarmuka, kami menemukan kedua titik akhir, untuk menerima dan mengirim, dan memulai koneksi usb. Itu saja, sekarang Anda bisa membaca datanya.

Seperti yang saya katakan sebelumnya, perangkat berkomunikasi melalui laporan.

  fun sendReport(data: ByteArray) { usbConnection?.bulkTransfer(usbOutEndpoint, data, data.size, 0) } fun getReport(): ByteArray { val buffer = ByteBuffer.allocate(REPORT_SIZE) val report = ByteArray(buffer.remaining()) if (usbRequest.queue(buffer, REPORT_SIZE)) { usbConnection?.requestWait() buffer.rewind() buffer.get(report, 0, report.size) buffer.clear() } return report } 

Kami mengirim array byte ke metode sendReport, di mana nol byte adalah ID laporan, kami mengambil koneksi USB saat ini ke perangkat dan melakukan transfer. Sebagai parameter, kami mentransfer nomor titik akhir, data, ukurannya, dan batas waktu transmisi ke metode BulkTransfer. Perlu dicatat bahwa kelas UsbDeviceConnection memiliki metode untuk mengimplementasikan pertukaran data dengan perangkat USB - bulkTransfer dan metode controlTransfer. Penggunaannya tergantung pada jenis transmisi yang didukung titik akhir. Dalam hal ini, kami menggunakan bulkTransfer, meskipun penggunaan titik akhir dengan tipe kontrol paling umum untuk HID. Tetapi kami memiliki Custom HID, jadi kami melakukan apa yang kami inginkan. Saya menyarankan Anda untuk membaca tentang jenis transmisi secara terpisah, karena volume dan frekuensi data yang dikirim tergantung padanya.

Untuk menerima data, Anda perlu mengetahui ukuran data yang dapat diperoleh, cara mengetahui sebelumnya, dan untuk mendapatkan dari titik akhir.

Metode penerimaan data melalui USB HID adalah sinkron dan memblokir dan harus dilakukan dalam utas yang berbeda, di samping itu, laporan dari perangkat dapat diterima terus-menerus atau kapan saja, oleh karena itu, perlu untuk menerapkan jajak pendapat konstan dari laporan agar tidak ketinggalan data. Mari kita lakukan dengan RxJava:

 fun receive() { Observable.fromCallable<ByteArray> { getReport() } .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .repeat() .subscribe({ /* check it[0] (this is report id) and handle data */ },{ /* handle exeption */ }) } 

Setelah menerima array byte, kita harus memeriksa byte nol, karena itu adalah ID laporan dan, menurutnya, menguraikan data yang diterima.

Setelah menyelesaikan semua tindakan dengan USB, Anda harus menutup koneksi. Anda dapat melakukan ini di aktivitas onDestroy atau di onCleared di ViewModel.

  fun close() { usbRequest?.close() usbConnection?.releaseInterface(usbInterface) usbConnection?.close() } 

Kesimpulan


Artikel ini membahas kode yang sangat kecil dan primitif, sangat demonstratif dengan implementasi untuk perangkat tertentu. Tentu saja, ada banyak kelas USB, tidak hanya HID, dan bagi mereka, tentu saja, implementasinya akan berbeda. Namun, semua metode didokumentasikan dengan cukup baik dan memiliki pemahaman yang baik tentang tumpukan USB, Anda dapat dengan mudah mengetahui cara menggunakannya.

X. Bahan yang berguna


Source: https://habr.com/ru/post/id470365/


All Articles