Android Things 教程:Android Things USB 通信

Android Things 现在支持 USB Host(从 DP3 后),它允许用户空间的 Android 应用程序与定制的 USB 设备通话。

为了探索这个功能,我们将创建一个自定义 USB 传感器,并通过 USB 将所有事件转发到 Android Things 板。

您可以在下面看到我们将实现的视频:USB 设备将是一个 Arduino NFC 阅读器。当扫描标签(或者amiibo)时,Arduino 会通过 USB 将 NFC 数据转发到 Android Things 板。

Android Things + USB + Arduino + NFC

注意事项

有两种不同的方式通过 Android Things 与 USB 设备进行通信。

  • 如果设备是 USB 串行设备,并且通过内核报告了 /dev/tty* 设备句柄,则不需要使用任何 USB Host API。而是尝试调用 PeripheralManagerService.getUartDeviceList()。如果您可以看到一个新的 UART 结点,这意味着您可以直接使用 UART API 与 USB 设备进行通信。根本不需要使用 USB API(如果你不需要的话)。
  • 如果设备在插入之后没有报告新的 /dev/tty*(例如,内核没有内置的驱动程序),则不得不使用 USB 层。您将需要操作 USB Host API,它允许常规用户空间应用程序与 USB 设备进行通信,而无需 root 权限或 Linux 内核所需的支持。

在之前的文章中,我们已经看到了如何通过 UART 与串口设备进行通信。这一次,我们将使用 USB Host API,并且将使用第三方库执行串行通信。这样,我们在这里写的代码将不会特定于 Android Things,但也可以在任何Android(3.1及以上)的智能手机上工作。

烧录 Arduino

我们将从简单的事情开始:我们首先要让 Arduino 不断在串行端口上发送 “Hello!” ,波特率为 115200。

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("Hello!");
  delay(1000);
}

当我们将 Arduino 连接到 Android Things 板时,我们希望我们的应用程序每秒能够收到 “Hello!” 文本。 为此,我们首先要编辑 AndroidManifest.xml

AndroidManifest.xml

我们希望在外部 USB 设备连接到 Android 设备时收到通知。它可以添加一个新的 intent-filter 条目到 activity 中,当 USB 设备插入时应该由系统启动。此外,我们将添加一个元数据元素,指向外部 XML 资源文件(在 res/xml/device_filter.xml 中),该文件声明了有关我们要检测的设备的标识信息。

<activity launchMode="singleTop"...>...
  <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"/>
</activity>

如果 device_filter.xml 文件具有以下内容,则每次插入任何 USB 设备时都会收到通知:

<resources>
    <usb-device/>
</resources>

这不完全是我们想要的。 我们只想在 Arduino 插入时通知,而忽略所有其他 USB 设备,所以我们将添加一个特定的规则。

将 Arduino 连接到 Android Things 板后,我们可以使用 dmesg 查看内核日志以获取一些设备信息:

$ adb shell dmesg
New USB device found, idVendor=2341, idProduct=0001
New USB device strings: Mfr=1, Product=2, SerialNumber=220
Product: Arduino Uno
Manufacturer: Arduino (www.arduino.cc)

我们只想在 Arduino(idVendor = 0x2341)被连接时得到通知,所以我们将这个供应商 id 指定到 usb-device 标签中:

<usb-device vendor-id="9025"/>

请注意,vendor-id 预期十进制值,而不是十六进制值。

这个过滤器对我们来说已经足够了。有关我们可以做什么的完整列表,请参阅 USB Host 文档

开始 USB 连接

现在,我们的 Activity 在每次接入 Arduino 时都会收到一个意图(Intent)。

首先,我们将列出所有连接的 USB 设备,如果发现 Arduino,则打开一个 USB 连接:

UsbManager usbManager = getSystemService(UsbManager.class);
Map<String, UsbDevice> connectedDevices = usbManager.getDeviceList();
for (UsbDevice device : connectedDevices.values()) {
  if (device.getVendorId() == 0x2341 && device.getProductId() == 0x0001) {
    Log.i(TAG, "Device found: " + device.getDeviceName());
    startSerialConnection(usbManager, device);
    break;
  }
}

startSerialConnection 方法将使用 felHR85 的 USBSerial 库,来打开 Arduino 和 Android 设备之间的串行连接:

void startSerialConnection(UsbManager usbManager, UsbDevice device) {
  UsbDeviceConnection connection = usbManager.openDevice(device);
  UsbSerialDevice serial = UsbSerialDevice.createUsbSerialDevice(device, connection);

  if (serial != null && serial.open()) {
    serial.setBaudRate(115200);
    serial.setDataBits(UsbSerialInterface.DATA_BITS_8);
    serial.setStopBits(UsbSerialInterface.STOP_BITS_1);
    serial.setParity(UsbSerialInterface.PARITY_NONE);
    serial.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
    serial.read(mCallback);
  }
}

UsbSerialDevice.read() 方法需要一个 UsbReadCallback 引用,每次接收数据时都会调用它。以下是一个简单的实现:

UsbSerialInterface.UsbReadCallback mCallback = (data) -> {
  String dataStr = new String(data, "UTF-8");
  Log.i(TAG, "Data received: " + dataStr);
};

我们将 byte[] 数据转换为 UTF-8 字符串,并记录这些数据。

现在,每次通过 USB 从 Arduino 发送数据时,都会触发回调,并记录数据。 我们可以运行 adb logcat 来确认,我们每秒收到来自 Arduino 的 “Hello!” 消息:

Data received: Hello!
Data received: Hello!
Data received: Hello!

要获得完整和优化的源代码,请查看 github.com/Nilhcem/usbfun-androingsings

增加一些乐趣

每一秒输出 “你好!” 到日志都很无聊。

为这个项目添加一些乐趣的快速方法是,将 NFC 模块连接到 Arduino,并通过 USB 将此模块的 NFC 标签数据发送给 Android Things 开发板。

我使用 Elechouse 的 PN532 模块通过 SPI,使用的是这个 Arduino 程序

为了简化,我只是将标签 ID 转发到 Android Things 开发板。Android 应用根据收到的 ID 显示合适的图像。

将此项目移植到 Android 智能手机

正如介绍中所解释的,该项目使用 Android SDK 中的 USB Host API,因此完全兼容任何 Android 智能手机或平板电脑(minSdk=12)。如果您拥有 USB-OTG 电缆,则可以直接将 Arduino 插入手机。

下面是一个 Android 手机的演示(带声音),通过 USB 将数据发送到 Arduino 上,在压电式蜂鸣器上播放音乐。(源代码在这里

Android smartphone + Arduino + Buzzer

但是,还是有一个区别:连接 USB 设备时,将显示一个 UI 对话框,用户需要授予 USB 访问设备的权限。检查权限是否被授予在我们编写的 Android Things 源代码项目上跳过了,因为与运行时权限类似,您不必检查/请求 Android Things 上的 USB 权限,因为可能没有附加的显示,因此,没有办法让用户授予这些权限。 他们是默认允许的。

走得更远

USB Host API 可以做的一个更好的例子是官方的 USB 枚举示例。该项目遍历主机发现的所有 USB 设备,并打印其接口和端点。

阅读源代码是有趣的,因为我们可以了解 USB 描述符。

例如,在检测到 USB 设备后,使用我们在本文中使用的相同 API(通过 UsbManager.openDevice() )打开 USB 设备,然后代码立即查询 USB 设备和配置,而不是打开串行连接 (s)描述符:

connection.controlTransfer(0x80, 0x06, 0x100, 0x00, buffer, length, timeout);
int deviceClass = (buffer[4] & 0xFF);
int deviceProtocol = (buffer[6] & 0xFF);
int vendorId = (buffer[8] & 0xFF) + ((buffer[9] & 0xFF) << 8);
int productId = (buffer[10] & 0xFF) + ((buffer[11] & 0xFF) << 8);

读取 USB 规格时,指定所有 USB 设备必须至少支持一个默认端点。以此端点为目标的传输被称为控制传输,并且是主机获取设备信息的一种方式。

  • 0x80 是数据方向。 这里的意思是 “IN”(从 USB 设备到主机)
  • 0x06 是获取描述符的请求类型
  • 0x100 用于设备描述符(0x200 用于配置描述符)
  • 0x00 是描述符索引(默认描述符)

然后,代码接收来自缓冲区的数据,如文档中所述。

+--------+------+---------------------------------------+
| Offset | Size | Description                           |
+--------+------+---------------------------------------+
|    4   |   1  | Device class code                     |
|    6   |   1  | Protocol code                         |
|    8   |   2  | Vendor ID (assigned by USB.org)       |
|   10   |   2  | Product ID (assigned by Manufacturer) |
+--------+------+---------------------------------------+

等等。一旦我们明白,示例代码更容易阅读。

结论

这篇文章介绍了如何将 USB Host API 与 Android Things 结合使用的例子。

在真实场景中,您不需要使用 Arduino 将 NFC 支持带到 Android Things,因为您可以直接开发 NFC 驱动程序,即使您需要,也可以使用 Arduino 与 Arduino 进行通信直接的 UART API。您可能不需要使用 USB Host API + 第三方依赖项。

如果您拥有一个USB串行设备,并且如果后者被内核识别,那么使用 UART API 是在两个设备之间进行通信的最简单和推荐的方式。但是,如果您需要从应用程序访问任何类型的 USB 设备,那么您将很高兴在 Android Things 上拥有用户空间 USB 支持。

正如 Marcos Placona 在博客中解释的,一些组件需要超高速的驱动程序(发送几微秒内从 vcc 到地的信号)。尽管 Android Things 还无法实现,但您可以使用微控制器作为代理,并通过 USB 将数据导向 Android Things 板,以便在 Android Things 项目中使用这些组件。

英语原文链接:Android Things - USB communications

尚未评分
您的评分将帮助我们做出更好的玩法

观光\评论区

Copyright © 2017 玩点什么. All Rights Reserved.