Android USB Host模式下Datalogic 9800秤端通信失败排查
问题描述
正在对接一款处于USB-OEM模式的Android无驱动设备——Datalogic 9800扫描秤,该设备为双USB设备,包含两个独立端点:
- 扫描端点:通过空批量传输监听数据,运行正常;
- 秤端点:发送重量请求指令(
0x01, 0x00, 0x00, 0x00, 0x00)后,bulkTransfer始终返回-1,无法获取重量响应。
扫描端点正常接收数据的代码
device?.getInterface(0)?.also { intf -> intf.getEndpoint(0)?.also { endpoint -> println(endpoint.direction) manager.openDevice(device)?.apply { claimInterface(intf, forceClaim) coroutineScope.launch { // Loop and check if any data is incoming. val buffer = ByteArray(endpoint.maxPacketSize) while (true) { val bytesRead = bulkTransfer(endpoint, buffer, buffer.size, TIMEOUT) if (bytesRead > 0) { val bufferString = buffer.joinToString(separator = " ") val receivedData = bufferString.split(" ").subList(3, 16).joinToString("") println(receivedData) val ret = JSObject() ret.put("upc", receivedData) call.resolve(ret) } } } } } }
用于收据打印机的正常发送数据代码
device?.getInterface(0)?.also { intf -> intf.getEndpoint(0)?.also { endpoint -> manager.openDevice(device)?.apply { claimInterface(intf, forceClaim) bulkTransfer(endpoint, bytes, bytes.size, TIMEOUT) } } }
秤端尝试的指令发送与响应接收代码(返回-1)
device?.getInterface(1)?.also { intf -> intf.getEndpoint(0)?.also { endpoint -> println(intf) manager.openDevice(device)?.apply { claimInterface(intf, forceClaim) coroutineScope.launch { val buffer = byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x00) val bytesSent = bulkTransfer(endpoint, buffer, buffer.size, TIMEOUT) // Loop and check if any data is incoming. while (true) { val bytesRead = bulkTransfer(endpoint, buffer, buffer.size, TIMEOUT) println(bytesRead) } } } } }
分析与解决方案
1. 端点方向不匹配
USB端点分**输入(IN,设备到主机)和输出(OUT,主机到设备)**两种方向:
- 发送指令必须用OUT端点,接收响应必须用IN端点。
- 你当前复用同一个端点既发又收,大概率是方向错误。秤端接口1的
endpoint(0)可能是IN端点(仅用于接收),发送指令需要找对应的OUT端点(比如endpoint(1))。
解决:遍历接口1的端点,区分方向后分别使用:
// 筛选出秤端的OUT和IN端点 val outEndpoint = intf.endpoints.find { it.direction == UsbConstants.USB_DIR_OUT } val inEndpoint = intf.endpoints.find { it.direction == UsbConstants.USB_DIR_IN }
2. 缓冲区复用导致数据污染
用同一个buffer存储发送指令和接收数据,会导致发送的指令残留干扰后续接收结果。
解决:发送和接收使用独立缓冲区:
// 发送指令专用缓冲区 val sendBuffer = byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x00) val bytesSent = bulkTransfer(outEndpoint, sendBuffer, sendBuffer.size, TIMEOUT) // 接收响应专用缓冲区 val receiveBuffer = ByteArray(inEndpoint.maxPacketSize) val bytesRead = bulkTransfer(inEndpoint, receiveBuffer, receiveBuffer.size, TIMEOUT)
3. 接口权限与设备占用问题
- 确认
claimInterface(intf, forceClaim)返回true,若返回false说明接口被其他进程占用,无法操作。 - 操作秤端前,确保扫描端的
UsbDeviceConnection已关闭、接口已释放,避免设备资源冲突。 - 检查
manager.openDevice(device)是否成功获取连接,避免空指针引发隐性错误。
4. 指令格式与设备模式验证
- 核对Datalogic 9800 OEM模式下的指令规范,确认重量请求指令是否需要校验位、长度是否符合要求。
- 部分设备需先发送初始化指令或进入特定模式才能响应请求,建议查阅设备官方文档。
修正后的示例代码
device?.getInterface(1)?.also { intf -> val outEndpoint = intf.endpoints.find { it.direction == UsbConstants.USB_DIR_OUT } val inEndpoint = intf.endpoints.find { it.direction == UsbConstants.USB_DIR_IN } if (outEndpoint == null || inEndpoint == null) { println("未找到秤端的OUT/IN端点") return@also } manager.openDevice(device)?.apply { if (!claimInterface(intf, true)) { println("无法获取秤端接口权限") close() return@apply } coroutineScope.launch { val sendBuffer = byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x00) val bytesSent = bulkTransfer(outEndpoint, sendBuffer, sendBuffer.size, TIMEOUT) if (bytesSent < 0) { println("指令发送失败") releaseInterface(intf) close() return@launch } println("成功发送 $bytesSent 字节指令") // 接收响应 val receiveBuffer = ByteArray(inEndpoint.maxPacketSize) val bytesRead = bulkTransfer(inEndpoint, receiveBuffer, receiveBuffer.size, TIMEOUT) if (bytesRead > 0) { println("收到响应字节数:$bytesRead") val responseData = receiveBuffer.copyOfRange(0, bytesRead) println("响应原始数据:${responseData.joinToString(" ")}") // 此处添加响应数据解析逻辑 } else { println("接收响应超时或出错") } // 释放资源 releaseInterface(intf) close() } } }
内容的提问来源于stack exchange,提问作者Zach Wright
相关产品推荐
相关产品推荐

