You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:02:21