Как то-однажды мне понадобилось, чтобы один прибор, который управлялся через USB c десктопной программы, также мог управляться через Android приложение. Особенность была в том, что не использовались HID, CDC и прочие стандартные классы устройств. Передача данных осуществлялась через Bulk transfer и конечные точки. В основе работы с usb лежала библиотека libusb.
Сделаем тестовое приложение, с помощью которого можно будет передать и получить произвольные данные с прибора.
Я затрону ключевые моменты, а ссылка на полный код будет в конце.
Для начала действуем согласно официальной документации для работы с UsbHost.
В манифест добавляем строку
Если мы хотим, чтобы устройство автоматически определялось и запускалось приложение, тогда в манифесте для главной активити пишем следующее.
Указываем для нашего устройства его VID и PID в файле с ресурсами device_filter.xml
Usb менеджер мы получаем в методе onCreate.
Далее получаем список подключенных к USB устройств, и среди них ищем нужное нам устройство класса USB_CLASS_PER_INTERFACE.
Далее мы находим нужный нам интерфейс, не забываем проверить разрешения, перебираем все его контрольные точки, среди них выбираем точки на чтение и на запись и, наконец, подключаемся.
Для работы с операциями ввода-вывода, буферизации данных, асинхронности и прочих удобных вещей используется вспомогательный класс-менеджер, но по сути обмен данными сводится к следующему коду.
Чтение:
Запись:
Таким образом передаются и принимаются BulkTransfer пакеты.
В итоге наше простое приложение может передавать и принимать произвольные данные.
Работает!
Проект данного тестового приложения на GitHub
Полезные ссылки:
Сделаем тестовое приложение, с помощью которого можно будет передать и получить произвольные данные с прибора.
Я затрону ключевые моменты, а ссылка на полный код будет в конце.
Для начала действуем согласно официальной документации для работы с UsbHost.
В манифест добавляем строку
<uses-feature android:name="android.hardware.usb.host"/>
Если мы хотим, чтобы устройство автоматически определялось и запускалось приложение, тогда в манифесте для главной активити пишем следующее.
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter"/>
Указываем для нашего устройства его VID и PID в файле с ресурсами device_filter.xml
<resources>
<usb-device
product-id="0037"
vendor-id="8742"/>
</resources>
Usb менеджер мы получаем в методе onCreate.
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
Далее получаем список подключенных к USB устройств, и среди них ищем нужное нам устройство класса USB_CLASS_PER_INTERFACE.
UsbDevice findDevice(UsbManager usbManager) {
for (UsbDevice usbDevice : usbManager.getDeviceList().values()) {
if (usbDevice.getDeviceClass() == UsbConstants.USB_CLASS_PER_INTERFACE) {
return usbDevice;
} else {
UsbInterface usbInterface = findInterface(usbDevice);
if (usbInterface != null) return usbDevice;
}
}
return null;
}
UsbInterface findInterface(UsbDevice usbDevice) {
for (int nIf = 0; nIf < usbDevice.getInterfaceCount(); nIf++) {
UsbInterface usbInterface = usbDevice.getInterface(nIf);
if (usbInterface.getInterfaceClass() ==
UsbConstants.USB_CLASS_PER_INTERFACE) {
return usbInterface;
}
}
return null;
}
Далее мы находим нужный нам интерфейс, не забываем проверить разрешения, перебираем все его контрольные точки, среди них выбираем точки на чтение и на запись и, наконец, подключаемся.
private void initUsbDevice() {
PendingIntent mPermissionIntent =
PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
mUsbManager.requestPermission(mUsbDevice, mPermissionIntent);
mUsbInterface = findInterface(mUsbDevice);
for (int nEp = 0; nEp < mUsbInterface.getEndpointCount(); nEp++) {
UsbEndpoint tmpEndpoint = mUsbInterface.getEndpoint(nEp);
if (tmpEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_BULK)
continue;
if ((mOutEndpoint == null)
&& (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_OUT)) {
mOutEndpoint = tmpEndpoint;
} else if ((mInEndpoint == null)
&& (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_IN)) {
mInEndpoint = tmpEndpoint;
}
}
if (mOutEndpoint == null) {
Toast.makeText(this, "no endpoints", Toast.LENGTH_LONG).show();
}
mConnection = mUsbManager.openDevice(mUsbDevice);
if (mConnection == null) {
Toast.makeText(this, "can't open device", Toast.LENGTH_SHORT).show();
return;
}
mConnection.claimInterface(mUsbInterface, true);
startIoManager();
}
Для работы с операциями ввода-вывода, буферизации данных, асинхронности и прочих удобных вещей используется вспомогательный класс-менеджер, но по сути обмен данными сводится к следующему коду.
Чтение:
mConnection.bulkTransfer(mInEndpoint, data, size, READ_TIMEOUT);
Запись:
int bytesWritten = mConnection.bulkTransfer(mOutEndpoint,
Arrays.copyOfRange(data, offset, offset + size), size, WRITE_TIMEOUT);
Таким образом передаются и принимаются BulkTransfer пакеты.
В итоге наше простое приложение может передавать и принимать произвольные данные.
Работает!
Проект данного тестового приложения на GitHub
Полезные ссылки:
Shtucer
Не? Вкусовщина, конечно, но в ветке else всё равно ничего полезного с результатом findInterface(usbDevice) не происходит.