目录

一、准备工作

1. 硬件要求

2. 软件权限配置

二、核心实现步骤

1. 检测 USB 设备连接

2. 请求 USB 设备访问权限

3. 打开设备并配置串口参数

方式 1:使用第三方库(推荐)

4. 数据读写操作

发送数据

接收数据

5. 关闭连接与释放资源

三、常见问题与排查

四、总结


在安卓设备中,通过 USB 与外部设备(如传感器、单片机、PLC 等)进行串口通信是工业控制、物联网等场景的常见需求。核心原理是通过 USB-to-Serial 适配器将 USB 信号转换为串口(UART)信号,再通过安卓的 USB Host API 与适配器交互,实现数据收发。

一、准备工作

1. 硬件要求
  • 安卓设备支持 USB Host 模式:需设备硬件支持(大部分手机 / 平板支持),可在系统设置中查看 “USB 主机模式” 是否开启(部分设备默认开启)。
  • USB-to-Serial 适配器:用于将 USB 信号转为 TTL/RS232 串口信号,连接外部设备。
  • 物理连接:安卓设备(通过 OTG 线)→ USB-to-Serial 适配器 → 外部串口设备(如单片机的 UART 接口)。
2. 软件权限配置

需在 AndroidManifest.xml 中声明 USB 相关权限和特性:

<!-- 声明支持USB Host模式 -->
<uses-feature android:name="android.hardware.usb.host" android:required="true" />

<!-- USB设备访问权限 -->
<uses-permission android:name="android.permission.USB_PERMISSION" />

<!-- (可选)针对特定设备的过滤,在<intent-filter>中添加 -->
<activity ...>
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
    </intent-filter>
    <!-- 设备过滤规则:指定要通信的USB设备的VID和PID(可通过adb命令查看) -->
    <meta-data
        android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/usb_device_filter" />
</activity>

在 res/xml/ 下创建 usb_device_filter.xml,定义需要过滤的 USB 设备(根据适配器的 VID 和 PID 填写,如  VID=0x1A86,PID=0x7523):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 匹配CH340适配器 -->
    <usb-device vendor-id="6790" product-id="29987" /> <!-- 1A86转10进制=6790,7523转10进制=29987 -->
    <!-- 可添加其他设备:如PL2303(VID=0x067B, PID=0x2303) -->
    <usb-device vendor-id="1659" product-id="8963" />
</resources>

二、核心实现步骤

1. 检测 USB 设备连接

通过 UsbManager 管理 USB 设备,并注册广播接收器监听设备的 “插入 / 拔出” 事件:

// 获取UsbManager实例
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

// 注册广播接收器,监听USB设备插入/拔出
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
            // 设备插入:获取设备
            UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                // 检查是否有权限,若无则请求
                requestUsbPermission(device);
            }
        } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            // 设备拔出:关闭连接、释放资源
            closeSerialConnection();
        } else if (UsbManager.ACTION_USB_PERMISSION.equals(action)) {
            // 权限请求结果
            synchronized (this) {
                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) && device != null) {
                    // 权限已授予:打开设备并初始化串口
                    openSerialDevice(device);
                } else {
                    Toast.makeText(context, "未获取USB权限", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
};

// 注册接收器(在onCreate中调用)
private void registerUsbReceiver() {
    IntentFilter filter = new IntentFilter();
    filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
    filter.addAction(UsbManager.ACTION_USB_PERMISSION);
    registerReceiver(usbReceiver, filter);
}
2. 请求 USB 设备访问权限

安卓要求必须获得用户授权才能访问 USB 设备,通过 UsbManager.requestPermission() 发起请求:

// 请求USB设备权限
private void requestUsbPermission(UsbDevice device) {
    if (usbManager.hasPermission(device)) {
        // 已有权限:直接打开设备
        openSerialDevice(device);
    } else {
        // 无权限:向用户请求
        PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, 
                new Intent(UsbManager.ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
        usbManager.requestPermission(device, permissionIntent);
    }
}
3. 打开设备并配置串口参数

USB-to-Serial 适配器本质是 USB 设备,需找到其对应的 接口(UsbInterface) 和 端点(UsbEndpoint)(串口通信通常使用 “批量传输端点”:BULK_IN 用于接收,BULK_OUT 用于发送)。

不同芯片的配置逻辑不同,推荐使用第三方库简化操作(如 usb-serial-for-android,已封装多种芯片的驱动)。

方式 1:使用第三方库(推荐)

usb-serial-for-android 是开源库,支持主流芯片,无需手动处理不同芯片的驱动差异。

  • 添加依赖(在 build.gradle):

    implementation 'com.hoho.android:usb-serial-for-android:3.7.0'
    
  • 打开设备并配置参数

    private UsbSerialPort serialPort; // 串口端口实例
    private OutputStream outputStream; // 输出流(发送数据)
    private InputStream inputStream; // 输入流(接收数据)
    
    // 打开设备并初始化串口
    private void openSerialDevice(UsbDevice device) {
        // 获取设备支持的驱动(库自动匹配芯片型号)
        List<UsbSerialDriver> drivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager);
        UsbSerialDriver driver = null;
        for (UsbSerialDriver d : drivers) {
            if (d.getDevice().getDeviceId() == device.getDeviceId()) {
                driver = d;
                break;
            }
        }
        if (driver == null) {
            Toast.makeText(this, "未找到支持的驱动", Toast.LENGTH_SHORT).show();
            return;
        }
    
        try {
            // 打开设备连接
            UsbDeviceConnection connection = usbManager.openDevice(driver.getDevice());
            if (connection == null) {
                Toast.makeText(this, "无法打开设备连接", Toast.LENGTH_SHORT).show();
                return;
            }
    
            // 获取串口端口(通常一个设备只有一个端口)
            serialPort = driver.getPorts().get(0);
            // 打开端口并配置参数(波特率、数据位、停止位、校验位)
            serialPort.open(connection);
            serialPort.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
    
            // 获取输入输出流
            outputStream = serialPort.getOutputStream();
            inputStream = serialPort.getInputStream();
    
            // 启动线程监听接收数据
            startReadThread();
    
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "打开串口失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
    
4. 数据读写操作
发送数据

通过 OutputStream 发送字节数据:

// 发送数据(字符串转字节数组)
public void sendData(String data) {
    if (outputStream == null) return;
    try {
        outputStream.write(data.getBytes(StandardCharsets.UTF_8));
        outputStream.flush(); // 立即发送
    } catch (IOException e) {
        e.printStackTrace();
    }
}
接收数据

需在子线程中读取(避免阻塞 UI 线程),通过 InputStream 读取字节:

// 启动接收线程
private void startReadThread() {
    new Thread(() -> {
        byte[] buffer = new byte[1024]; // 缓冲区
        int bytesRead;
        while (true) {
            try {
                // 读取数据(阻塞操作,无数据时等待)
                if ((bytesRead = inputStream.read(buffer)) != -1) {
                    // 转换为字符串(根据实际协议处理)
                    String receivedData = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
                    // 切换到UI线程更新界面
                    runOnUiThread(() -> {
                        // 处理接收的数据(如显示到TextView)
                        tvReceived.setText(receivedData);
                    });
                }
            } catch (IOException e) {
                e.printStackTrace();
                break; // 读取失败(如设备断开),退出线程
            }
        }
    }).start();
}
5. 关闭连接与释放资源

设备断开或页面销毁时,需关闭串口连接,避免资源泄漏:

// 关闭串口连接
private void closeSerialConnection() {
    if (serialPort != null) {
        try {
            serialPort.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        serialPort = null;
    }
    outputStream = null;
    inputStream = null;
}

// 在Activity销毁时调用
@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(usbReceiver); // 注销广播接收器
    closeSerialConnection(); // 关闭连接
}

三、常见问题与排查

  1. 设备不识别

    • 检查 OTG 线是否正常,安卓设备是否开启 USB Host 模式。
    • 通过 adb shell dumpsys usb 查看已连接的 USB 设备 VID/PID,确认是否在 usb_device_filter.xml 中正确配置。
  2. 权限请求失败

    • 确保 PendingIntent 的 Flag 正确(如 FLAG_IMMUTABLE,适配 Android 12+)。
    • 手动在系统设置中授予应用 “USB 权限”。
  3. 数据收发异常

    • 检查串口参数(波特率、数据位等)是否与外部设备一致(必须完全匹配)。
    • 排查硬件连接(如 Tx/Rx 线是否接反,地线是否连接)。
  4. 驱动不支持

    • 确认 usb-serial-for-android 库是否支持当前适配器芯片(可查看库的文档,不支持的芯片需自行开发驱动)。

四、总结

安卓通过 USB 进行串口通信的核心流程是:检测设备→请求权限→打开设备并配置参数→数据读写→释放资源。使用第三方库(如 usb-serial-for-android)可大幅简化不同芯片的驱动适配工作,推荐优先采用。实际开发中需注意线程管理(避免 UI 阻塞)和异常处理(如设备突然断开),确保通信稳定。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐