Android USB通信(host转串口)

这篇具有很好参考价值的文章主要介绍了Android USB通信(host转串口)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:公司属于北斗通信行业,项目大多都需要和各式各样的硬件设备相结合来满足项目需求,因此所涉及到的各种技术也相对比较冷门。前段时间有个项目用到了一款定制 Android 设备,其中有多个接口,包括两个 USB 接口和一个 RS232 串口,需要用到其中一个 USB 连接北斗设备实现指令互通,经过摸索现在也大致了解了 Android USB(host)通信流程后续还有另一个项目则用到了 USB(accessory)有空再写一篇。在这里记录和分享一下,有错漏或可优化之处欢迎大家留言。

一、导入模块

工具基于 github 上的串口工具库:usb-serial-for-android

1. 在项目级 build.gradle 文件添加 jitpack.io 库

maven { url 'https://jitpack.io' }

2. 添加依赖

implementation 'com.github.mik3y:usb-serial-for-android:3.4.6'
可能有更新的版本自行修改

3. 本地

2023年11月30日更新:最近把代码拉到家里的电脑准备在家办公时恰好停网了导致第三方库导入不了,于是就到工具库源码上把代码都拉到了本地,如果你也恰好遇到了这种情况也可以这样处理。这里顺手打包了一份,自己重新导入一下即可:https://download.csdn.net/download/lxt1292352578/88565529

android usb串口,Android,android

二、上代码

DEMO:https://github.com/LXTTTTTT/USBtoSerialPortDemo
源码资源:https://download.csdn.net/download/lxt1292352578/88717549
复制再把报红的部分直接去掉或者换成自己的就能直接使用

package com.example.SecondProject.Utils.Transfer.USB;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;

import com.example.SecondProject.Base.MainApplication;
import com.example.SecondProject.BuildConfig;
import com.example.SecondProject.Global.Constant;
import com.example.SecondProject.Global.Variable;
import com.example.SecondProject.Utils.DataUtil;
import com.example.SecondProject.Utils.NotificationCenter;
import com.example.SecondProject.Utils.ProtocolUtil;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.hoho.android.usbserial.util.SerialInputOutputManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

// usb 数据传输工具
public class USBTransferUtil {

    private String TAG = "USBTransferUtil";
    private MainApplication APP = MainApplication.getInstance();  // 主程序,替换为你自己的
    private UsbManager manager = (UsbManager) APP.getSystemService(Context.USB_SERVICE);  // usb管理器

    private BroadcastReceiver usbReceiver;  // 广播监听:判断usb设备授权操作
    private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".INTENT_ACTION_GRANT_USB";  // usb权限请求标识
    private final String IDENTIFICATION = " USB-Serial Controller D";  // 目标设备标识

    private List<UsbSerialDriver> availableDrivers = new ArrayList<>();  // 所有可用设备
    private UsbSerialDriver usbSerialDriver;  // 当前连接的设备
    private UsbDeviceConnection usbDeviceConnection;  // 连接对象
    private UsbSerialPort usbSerialPort;  // 设备端口对象,通过这个读写数据
    private SerialInputOutputManager inputOutputManager;  // 数据输入输出流管理器

// 连接参数,按需求自行修改 ---------
    private int baudRate = 115200;  // 波特率
    private int dataBits = 8;  // 数据位
    private int stopBits = UsbSerialPort.STOPBITS_1;  // 停止位
    private int parity = UsbSerialPort.PARITY_NONE;// 奇偶校验

// 单例 -------------------------
    private static USBTransferUtil usbTransferUtil;
    public static USBTransferUtil getInstance() {
        if(usbTransferUtil == null){
            usbTransferUtil = new USBTransferUtil();
        }
        return usbTransferUtil;
    }


    public void connect(){
        // “Variable.isConnectUSB” 我的变量标识,自行删除或修改
        if(!Variable.isConnectUSB){
            registerReceiver();  // 注册广播监听
            refreshDevice();  // 拿到已连接的usb设备列表
            connectDevice();  // 建立连接
        }
    }


    // 注册usb授权监听广播
    public void registerReceiver(){
        usbReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if(INTENT_ACTION_GRANT_USB.equals(intent.getAction())) {
                    // 授权操作完成,连接
//                    boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);  // 不知为何获取到的永远都是 false 因此无法判断授权还是拒绝
                    connectDevice();
                }
            }
        };
        APP.registerReceiver(usbReceiver,new IntentFilter(INTENT_ACTION_GRANT_USB));
    }

    // 刷新当前可用 usb设备
    public void refreshDevice(){
        availableDrivers.clear();
        availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
        Log.e(TAG, "当前可用 usb 设备数量: " + availableDrivers.size() );


        // 有设备可以连接
        if(availableDrivers.size() != 0){

            // 当时开发用的是定制平板电脑有 2 个usb口,所以搜索到两个
            if(availableDrivers.size()>1){
                for (int i = 0; i < availableDrivers.size(); i++) {
                    UsbSerialDriver availableDriver = availableDrivers.get(i);
                    // 我是通过 ProductName 这个参数来识别我要连接的设备
                    if(availableDriver.getDevice().getProductName().equals(IDENTIFICATION)){
                        usbSerialDriver = availableDriver;
                    }
                }
            }
            // 通常手机只有充电口 1 个
            else {
                usbSerialDriver = availableDrivers.get(0);
            }
            usbSerialPort = usbSerialDriver.getPorts().get(0);  // 一般设备的端口都只有一个,具体要参考设备的说明文档
            // 同时申请设备权限
            if(!manager.hasPermission(usbSerialDriver.getDevice())){
                int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0;
                PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(APP, 0, new Intent(INTENT_ACTION_GRANT_USB), flags);
                manager.requestPermission(usbSerialDriver.getDevice(), usbPermissionIntent);
            }
        }
        // 没有设备
        else {
            APP.showToast("请先接入设备",0);
        }


    }

    // 连接设备
    public void connectDevice(){
        if(usbSerialDriver == null || inputOutputManager != null){return;}
        // 判断是否拥有权限
        boolean hasPermission = manager.hasPermission(usbSerialDriver.getDevice());
        if(hasPermission){
            usbDeviceConnection = manager.openDevice(usbSerialDriver.getDevice());  // 拿到连接对象
            if(usbSerialPort == null){return;}
            try {
                usbSerialPort.open(usbDeviceConnection);  // 打开串口
                usbSerialPort.setParameters(baudRate, dataBits, stopBits, parity);  // 设置串口参数:波特率 - 115200 , 数据位 - 8 , 停止位 - 1 , 奇偶校验 - 无
                startReceiveData();  // 开启读数据线程
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            APP.showToast("请先授予权限再连接",0);
        }

    }

    // 开启数据接收监听
    public void startReceiveData(){
        if(usbSerialPort == null || !usbSerialPort.isOpen()){return;}
        inputOutputManager = new SerialInputOutputManager(usbSerialPort, new SerialInputOutputManager.Listener() {
            @Override
            public void onNewData(byte[] data) {
                // 在这里处理接收到的 usb 数据
                String data_str = DataUtil.bytes2string(data).toUpperCase();
                Log.e(TAG, "收到 usb 数据: " + data_str);
            }
            @Override
            public void onRunError(Exception e) {
                Log.e(TAG, "usb 断开了" );
                disconnect();
                e.printStackTrace();
            }
        });
        inputOutputManager.start();
        Variable.isConnectUSB = true;  // 修改连接标识
        NotificationCenter.standard().postNotification(Constant.CONNECT_USB);  // 发送全局广播
        APP.showToast("连接成功" ,Toast.LENGTH_SHORT);
    }

    // 下发数据
    public void write(String data_hex){
        if(usbSerialPort != null){
            Log.e(TAG, "当前usb状态: isOpen-" + usbSerialPort.isOpen() );
            // 当串口打开时再下发
            if(usbSerialPort.isOpen()){
                byte[] data_bytes = DataUtil.hex2bytes(data_hex);  // 将字符数据转化为 byte[]
                if (data_bytes == null || data_bytes.length == 0) return;
                try {
                    usbSerialPort.write(data_bytes,100);  // 写入数据
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else {
                APP.showToast(" usb 未连接" ,Toast.LENGTH_SHORT);
            }
        }
    }


    // 断开连接
    public void disconnect(){
        try{
            // 停止数据接收监听
            if(inputOutputManager != null){
                inputOutputManager.stop();
                inputOutputManager = null;
            }
            // 关闭端口
            if(usbSerialPort != null){
                usbSerialPort.close();
                usbSerialPort = null;
            }
            // 关闭连接
            if(usbDeviceConnection != null){
                usbDeviceConnection.close();
                usbDeviceConnection = null;
            }
            // 清除设备
            if(usbSerialDriver != null){
                usbSerialDriver = null;
            }
            // 清空设备列表
            availableDrivers.clear();
            // 注销广播监听
            APP.unregisterReceiver(usbReceiver);
            Variable.isConnectUSB = false;  // 修改标识
            NotificationCenter.standard().postNotification(Constant.DISCONNECT_USB);  // 发送广播
            APP.showToast("断开连接",0);

        }catch (Exception e){
            e.printStackTrace();
        }
    }


    // 下发初始化指令
    public void init_device(){
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        write(ProtocolUtil.CCICR(0,"00"));  // 查询 IC 信息
    }



}

三、使用例子

1. 使用场景

原本项目设备使用的是左边这种标准的 USB 转串口线连接北斗设备,但是现在手头没有当时的设备,恰好有一条右图这种 type-c 转串口线,那就用手机和电脑连接模拟使用场景吧
android usb串口,Android,androidandroid usb串口,Android,android
连接之后是这个样子的
android usb串口,Android,android
手机接口被占用了,这时可以使用无线调试,附个无线调试教程:https://blog.csdn.net/lxt1292352578/article/details/131954052

2. 使用案例

电脑需要准备一个串口调试工具,注意需要把串口参数设置为相同
附个串口调试工具:https://download.csdn.net/download/lxt1292352578/88595528
android usb串口,Android,android
直接调用连接设备方法:USBTransferUtil.getInstance().connect();

android usb串口,Android,android
注意首次连接需要手动授权
android usb串口,Android,android
连接成功后在电脑上使用串口调试助手发送数据模拟互通过程
下发数据方法:USBTransferUtil.getInstance().write("363636");
由于我下发北斗指令时使用的是16进制字符,有其他需求自行在方法里面修改
android usb串口,Android,android
接收数据的处理:直接在工具类内部处理,不知道是因为设备的不同还是转接线的不同,这里接收到的数据是碎片化的而不像我在开发时收到的是完整的数据,恰好之前开发串口连接北斗设备时也是这种碎片化数据,之后有空的时候顺便做一个北斗协议拼接/解析教程吧
android usb串口,Android,android
互通成功
android usb串口,Android,android

3. 设备接入监听

Android系统在每次拔插 USB 设备时都会广播一个意图,这样如果我们需要在 USB 设备连接时进行某种操作只需要在 manifest 文件里面给对应的 activty 添加一个声明并指定过滤规则即可
<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" />
android usb串口,Android,android
在xml资源文件夹中添加 device_filter 文件
android usb串口,Android,android
附上过滤规则文件代码:这里包含了大部分USB设备的厂商的厂商ID和产品ID,如有别的需求自行增加

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="MissingDefaultResource">
    <!-- 0x0403 / 0x60??: FTDI -->
    <usb-device vendor-id="1027" product-id="24577" /> <!-- 0x6001: FT232R -->
    <usb-device vendor-id="1027" product-id="24592" /> <!-- 0x6010: FT2232H -->
    <usb-device vendor-id="1027" product-id="24593" /> <!-- 0x6011: FT4232H -->
    <usb-device vendor-id="1027" product-id="24596" /> <!-- 0x6014: FT232H -->
    <usb-device vendor-id="1027" product-id="24597" /> <!-- 0x6015: FT230X, FT231X, FT234XD -->

    <!-- 0x10C4 / 0xEA??: Silabs CP210x -->
    <usb-device vendor-id="4292" product-id="60000" /> <!-- 0xea60: CP2102 and other CP210x single port devices -->
    <usb-device vendor-id="4292" product-id="60016" /> <!-- 0xea70: CP2105 -->
    <usb-device vendor-id="4292" product-id="60017" /> <!-- 0xea71: CP2108 -->

    <!-- 0x067B / 0x23?3: Prolific PL2303x -->
    <usb-device vendor-id="1659" product-id="8963" /> <!-- 0x2303: PL2303HX, HXD, TA, ... -->
    <usb-device vendor-id="1659" product-id="9123" /> <!-- 0x23a3: PL2303GC -->
    <usb-device vendor-id="1659" product-id="9139" /> <!-- 0x23b3: PL2303GB -->
    <usb-device vendor-id="1659" product-id="9155" /> <!-- 0x23c3: PL2303GT -->
    <usb-device vendor-id="1659" product-id="9171" /> <!-- 0x23d3: PL2303GL -->
    <usb-device vendor-id="1659" product-id="9187" /> <!-- 0x23e3: PL2303GE -->
    <usb-device vendor-id="1659" product-id="9203" /> <!-- 0x23f3: PL2303GS -->

    <!-- 0x1a86 / 0x?523: Qinheng CH34x -->
    <usb-device vendor-id="6790" product-id="21795" /> <!-- 0x5523: CH341A -->
    <usb-device vendor-id="6790" product-id="29987" /> <!-- 0x7523: CH340 -->

    <!-- CDC driver -->
    <usb-device vendor-id="9025" />                   <!-- 0x2341 / ......: Arduino -->
    <usb-device vendor-id="5824" product-id="1155" /> <!-- 0x16C0 / 0x0483: Teensyduino  -->
    <usb-device vendor-id="1003" product-id="8260" /> <!-- 0x03EB / 0x2044: Atmel Lufa -->
    <usb-device vendor-id="7855" product-id="4"    /> <!-- 0x1eaf / 0x0004: Leaflabs Maple -->
    <usb-device vendor-id="3368" product-id="516"  /> <!-- 0x0d28 / 0x0204: ARM mbed -->
    <usb-device vendor-id="1155" product-id="22336" /><!-- 0x0483 / 0x5740: ST CDC -->
    <usb-device vendor-id="11914" product-id="5"   /> <!-- 0x2E8A / 0x0005: Raspberry Pi Pico Micropython -->
    <usb-device vendor-id="11914" product-id="10"  /> <!-- 0x2E8A / 0x000A: Raspberry Pi Pico SDK -->
    <usb-device vendor-id="6790" product-id="21972" /><!-- 0x1A86 / 0x55D4: Qinheng CH9102F -->
</resources>

通过声明以上的intent-filter和meta-data,表明它是一个能够处理USB设备连接事件的Activity,并且根据res/xml/device_filter.xml中的规则对连接的USB设备进行过滤和处理。这样,在Android设备连接USB设备时,系统就会将相应的事件发送给Activity,从而实现对USB设备的交互和数据处理。比如我在这个 activity 的 onResume 生命周期添加连接USB设备设备操作,这样当系统发送了USB设备接入通知并授权成功后就能直接跳转到这个 activity 并进行后续操作,简单粗暴。
系统监测到 USB 设备接入并发送通知,授权并跳转至对应 activity :
android usb串口,Android,androidandroid usb串口,Android,android

四、小结

整个连接流程大致是这样的:
获取当前系统可用的 USB 设备列表 → 选中对应的USB设备并申请权限(首次)→ 获取设备端口(通常只有一个)→ 按照特定参数打开端口
具体操作参考代码,参考的项目是上文提到的工具库官方demo:
https://github.com/mik3y/usb-serial-for-android/tree/master
我的demo:https://github.com/LXTTTTTT/USBtoSerialPortDemo
源码资源:https://download.csdn.net/download/lxt1292352578/88717549
原工具库官方demo代码比较复杂,我只是按照自己的需求把对应的部分整理出来所以比较简单。由于这个功能用的不多所以也没有做过多的封装几乎所有的连接/通信操作都直接写在工具类里面,如果有需求自行改造即可。文章来源地址https://www.toymoban.com/news/detail-718728.html

到了这里,关于Android USB通信(host转串口)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android usb 配件模式通信示例

    当使用 Android 设备作为 USB 配件时,可以使用 Android 提供的 USB Accessory API 来进行 USB 通信。以下是一个简单的 Android USB 配件模式的通信例子。在本例中,我们将接收连接到 Android 设备的 USB 主机发送的数据并向 USB 主机发送响应。 首先,在 AndroidManifest.xml 文件中添加以下权限

    2024年02月15日
    浏览(55)
  • Android 沾包处理,以串口接入为例 (usb-serial-for-android)

    我们在通过串口、 TCP 、 UDP 等方式接收协议的时候,由于单次接收数据有限,导致一条命令可能被分割成多次进行接收。 这种情况下,就需要进行沾包处理,使多次接收的数据,合并成一条数据。本文通过博主本人一个真实的工作案例,实例讲解Android串口的接入和对于沾包

    2023年04月16日
    浏览(38)
  • 【Android车载系列】第8章 车载通信-USB协议代码实现

      上一篇已经简单介绍了USB协议的相关知识,其中的描述符较为重要,描述符成功返回,USB通信已经成功了一大半,具体描述符的知识点可以翻阅上一篇来了解。下面我们来看一下USB协议在的分层。   USB协议用的地方非常多,比如U盘、麦克风、充电器等等。其中传输、

    2023年04月08日
    浏览(55)
  • 通过usb利用adb实现android手机和pc机通信

    1、 adb forward 原理概述 adb forward 的功能是建立一个转发 如:adb forward tcp:8000 tcp:9000 的意思是,将PC端的 8000 端口收到的数据,转发给手机中的 9000 端口。 但是光执行这个命令还不能转发数据,还需要完成下面两个步骤才能传数据: (a)在手机端,建立一个端口为9000的 serv

    2024年01月22日
    浏览(48)
  • 【CH340N USB转串口通信】

    CH340N芯片是一种USB转串口芯片,它可以将USB接口转换为UART串口接口,使计算机可以通过USB接口和单片机等设备进行通信。该芯片具有低功耗、高稳定性等特点,被广泛用于电子设备中。 CH340N是一个 USB 总线的转接芯片,实现 USB 转串口,支持 5V 电源电压和 3.3V 电源电压。 全

    2024年02月01日
    浏览(52)
  • C#上位机的USB通信与串口通信接口区别

    USB通信与串口通信接口的代码区别有以下几个方面: 通信方式不同:串口通信采用全双工或半双工通信方式,而USB通信采用异步、同步和批量传输等多种传输方式。 端口号设置不同:串口通信设置端口号,如\\\"COM1\\\"、\\\"COM2\\\"等,而USB通信通过设备描述符和VID/PID等参数进行识别和

    2024年02月08日
    浏览(46)
  • .net6中WPF的串口通信和USB通信

    之前写过串口通信,不过是winform的。 c#使用串口进行通信_c# 串口通信_故里2130的博客-CSDN博客 今天说一下,.net6中wpf的串口通信和USB通信,在工控行业中,这2种的方式非常多,还有网口通信,它们都是用来和硬件打交道的,进行交互信息。 一、串口通信 1.安装System.IO.Ports

    2024年02月16日
    浏览(43)
  • STM32与USB3300共同实现USB OTG HS的CDC串口通信速度测试

      STM32和上位机传统通信方式就是串口,IIC,SPI等。IIC和SPI一般不常用,串口是用的最多的通信方式。然而串口一般用于输出调试信息这种对传输速度没要求的场景,那种大容量数据快速传输的场景,串口显得捉襟见肘。STM32自带USB FS,然而既然都使用USB了,为什么不用US

    2024年02月07日
    浏览(45)
  • STM32与Python上位机通过USB虚拟串口通信

    在详细阅读广大网友的教程之后,我对STM32和Python通过USB通信的流程烂熟于心。 尝试用ST公司的NUCLEO-L476RG板子进行简单的回环通信测试,发现还是存在网上无法找到的问题,这个耽搁了几天,期间找到了原因,但没有焊接调试,所以暂时就不以它为例子进行写了。 后采用正点

    2024年02月15日
    浏览(40)
  • Android 9.0 禁止usb键盘和usb鼠标挂载

    在9.0的系统产品开发中,对于系统中usb鼠标和usb键盘的等外设输入设备挂载处理,系统是在inputflinger模块中处理的,在产品的需求中对于外设输入设备的usb鼠标和usb键盘的挂载是禁用的,所以需要从挂载入手,禁止挂载usb鼠标和usb键盘 在android系统中是由各个子系统分工协作

    2024年02月09日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包