【USB】Android实现读写USB串口数据

最近在研究USB方面的内容;先后做了关于Android读写HID、串口设备的DEMO。本文比较简单,主要介绍的是Android实现读取串口数据的功能

废话不多说,先看一下业务层是如何调用的;如图:
在这里插入图片描述

首先,监听USB连接状况,当USB 进行请求USB权限,当USB权限申请成功,进行调用打开Usb设备的方法;当监听到USB断开,进行关闭连接;
在这里插入图片描述
这是向串口写入数据的方法;

本DEMO主要使用Handle进行数据各个线程之间的数据传到,以及USB连接读写情况的反馈;

下面直接上代码:
连接USB设备的代码

 public void openCDC(UsbDevice usbDevice, UsbDeviceConnection usbDeviceConnection)
    {
        this.usbDeviceConnection = usbDeviceConnection;
        usbInterface = usbDevice.getInterface(findCDC(usbDevice)); //获取USB设备接口
        if (usbDeviceConnection == null) //判断USB设备链路是否为空
        {
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
            return;
        }
        if (!usbDeviceConnection.claimInterface(usbInterface,true)) //USB设备链路绑定获取到的接口
        {
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
            return;
        }
        int numberEndpoints = usbInterface.getEndpointCount(); //获取USB设备接口的数据传输通道数量
        for (int num = 0; num <= numberEndpoints-1; num++)
        {
            UsbEndpoint usbEndpoint = usbInterface.getEndpoint(num);

            switch (usbEndpoint.getType())
            {
                //USB控制传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_CONTROL:
                    controlUsbEndpoint = usbEndpoint;
                    break;
                //USB块传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_BULK:
                    switch (usbEndpoint.getDirection())
                    {
                        case UsbConstants.USB_DIR_OUT:
                            bulkOutUsbEndpoint = usbEndpoint; //USB块传输模式输出通道
                            break;
                        case UsbConstants.USB_DIR_IN:
                            bulkInUsbEndpoint = usbEndpoint; //USB块传输模式输入通道
                            break;
                    }
                    break;
                //USB中断传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_INT:
                    switch (usbEndpoint.getDirection())
                    {
                        case UsbConstants.USB_DIR_OUT:
                            intOutUsbEndpoint = usbEndpoint;
                            break;
                        case UsbConstants.USB_DIR_IN:
                            intInUsbEndpoint = usbEndpoint;
                            break;
                    }
                    break;
            }
        }
        if (bulkOutUsbEndpoint != null && bulkInUsbEndpoint != null) //如果USB块传输模式输入输通道都不为空出
        {
            //USB连接成功
            connect = true;
            //获取到USB设备的ID、VID、PID
            String usbData = "Name:"+usbDevice.getDeviceName()+"\nID:"+usbDevice.getDeviceId()+"    VID:"+usbDevice.getVendorId()+"    PID:"+usbDevice.getProductId();
            mes = new Message();
            mes.obj = usbData;
            mes.what = MyHandler.USB_CONNECT_SUCCESS;
            myHandler.sendMessage(mes);
            threadReadData.start(); //开启接收数据线程
        }
        else
        {
            //USB连接失败
            connect = false;
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
        }
    }

这个代码有点多,可能看的不太清楚,我删减一下,是这样的

 public void openCDC(UsbDevice usbDevice, UsbDeviceConnection usbDeviceConnection)
    {
        this.usbDeviceConnection = usbDeviceConnection;
        usbInterface = usbDevice.getInterface(findCDC(usbDevice)); //获取USB设备接口
      
        int numberEndpoints = usbInterface.getEndpointCount(); //获取USB设备接口的数据传输通道数量
        for (int num = 0; num <= numberEndpoints-1; num++)
        {
            UsbEndpoint usbEndpoint = usbInterface.getEndpoint(num);

            switch (usbEndpoint.getType())
            {
                //USB控制传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_CONTROL:
                    controlUsbEndpoint = usbEndpoint;
                    break;
                //USB块传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_BULK:
                    switch (usbEndpoint.getDirection())
                    {
                        case UsbConstants.USB_DIR_OUT:
                            bulkOutUsbEndpoint = usbEndpoint; //USB块传输模式输出通道
                            break;
                        case UsbConstants.USB_DIR_IN:
                            bulkInUsbEndpoint = usbEndpoint; //USB块传输模式输入通道
                            break;
                    }
                    break;
                //USB中断传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_INT:
                    switch (usbEndpoint.getDirection())
                    {
                        case UsbConstants.USB_DIR_OUT:
                            intOutUsbEndpoint = usbEndpoint;
                            break;
                        case UsbConstants.USB_DIR_IN:
                            intInUsbEndpoint = usbEndpoint;
                            break;
                    }
                    break;
            }
        }
       
    }

设备打开之后,就可以进行读写了;

Android 读取串口数据

   public String readData()
    {
        byte[] tempByte = new byte[4096];
       
        int i = usbDeviceConnection.bulkTransfer(bulkInUsbEndpoint,tempByte,tempByte.length,100);//读取数据,100为超时时间,接收的数据为数组类型
   
        //将接收的数组转为字符串并返回
        byte[] messageByte = new byte[i];
        System.arraycopy(tempByte,0, messageByte,0, i);
        return new String(messageByte);
    }

这里主要通过调用谷歌提供的bulkTransfer方法进行的,传递的参数分别有endpoint,要读取的数据长度,和放要读取的数据的数组,超时时间

在这里插入图片描述

有了读,当然也需要写,向串口写入数据;代码如下:

 public boolean send(String message)
    {
      
        byte[] messageBytes = message.getBytes(); //字符串转为数组
        int result = usbDeviceConnection.bulkTransfer(bulkOutUsbEndpoint,messageBytes,messageBytes.length,100);//发送数据,发送转换为数组后的数据,超时时间为100毫秒
        if ((result >= 0)) //发送数据返回值大于等于0代表发送成功
        {
            //向信息处理中心发送“发送成功”的信息,并将信息内容传递过去
           
            return true;
        }
        else
        {
            //发送失败
            connect = false;
            return false;
        }
    }

传输数据的方式是用Handler;以下是Handler类的写法

  public void handleMessage(@NonNull Message msg)
    {
        switch (msg.what)
        {
            case USB_CONNECT_SUCCESS:  //USB设备连接成功
                MyHandler.USB_CONNECT_STATE  = true; //连接状态改变为true
                bt_send.setEnabled(true); //发送控件可以使用
                bt_sendTiming.setEnabled(true);
                bt_send.setTextColor(Color.BLACK); //定时发送控件可以使用
                bt_sendTiming.setTextColor(Color.BLACK); 
                tv_usbDataShow.setText(msg.obj.toString()); //填充意图发送过来的信息
                Toast.makeText(context,"连接成功",Toast.LENGTH_LONG).show();
                break;
            case USB_CONNECT_FAILED: //USB设备连接失败
                MyHandler.USB_CONNECT_STATE  = false;
                bt_send.setEnabled(false);
                bt_sendTiming.setEnabled(false);
                bt_send.setTextColor(Color.GRAY);
                bt_sendTiming.setTextColor(Color.GRAY);
                bt_sendTiming.setText("定时发送");
                tv_usbDataShow.setText("未连接设备");
                mainActivity.closeAll(); //连接断开或连接失败,执行关闭所有连接和对象的方法
                Toast.makeText(context,"断开连接",Toast.LENGTH_LONG).show();
                break;
            case OUTPUT:  //发送消息
                if (messageShowNeedRoll(tv_sendMessageShow) != 0) tv_sendMessageShow.scrollTo(0, messageShowNeedRoll(tv_sendMessageShow));//如果TextView填充满可使用高度就滚动到最新更新处
                tv_sendMessageShow.append("[TX]"+gteNowDate()+": "+msg.obj.toString()+"\n"); //给控件填充意图发送来的信息
                break;
            case INPUT:  //接收消息
                if (messageShowNeedRoll(tv_receiveMessageShow) != 0) tv_receiveMessageShow.scrollTo(0, messageShowNeedRoll(tv_receiveMessageShow));
                tv_receiveMessageShow.append("[RX]"+gteNowDate()+": "+msg.obj.toString()+"\n");
                break;
        }
    }

主要代码已展示完毕,接下来展示具体代码
先看USB类,如下:

public class UsbCDC
{
    private boolean connect; //USB连接状态
    private UsbInterface usbInterface; //USB设备的物理接口

    //控制传输模式通道
    private UsbEndpoint controlUsbEndpoint;
    //块传输模式通道
    private UsbEndpoint bulkInUsbEndpoint;
    private UsbEndpoint bulkOutUsbEndpoint;
    //中断传输模式通道
    private UsbEndpoint intInUsbEndpoint;
    private UsbEndpoint intOutUsbEndpoint;

    private UsbDeviceConnection usbDeviceConnection; //USB设备连接链路,用来进行设备通讯

    private Message mes; //信息包

    private MyHandler myHandler;//信息处理中心对象
    UsbCDC(MyHandler myHandler)
    {
        this.myHandler = myHandler;
    }

    /**
     * 向USB设备发送数据
     * @param message 要发送的数据,字符串类型
     * @return 数据发送结果,true代表发送成功
     */
    public boolean send(String message)
    {
        if (this.usbDeviceConnection == null) //判断USB链路是否获取到,不为空才能进行数据发送
        {
            //如果USB链路为空,执行该作用域代码
            connect = false;
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
            return false;
        }
        byte[] messageBytes = message.getBytes(); //字符串转为数组
        int result = usbDeviceConnection.bulkTransfer(bulkOutUsbEndpoint,messageBytes,messageBytes.length,100);//发送数据,发送转换为数组后的数据,超时时间为100毫秒
        if ((result >= 0)) //发送数据返回值大于等于0代表发送成功
        {
            //向信息处理中心发送“发送成功”的信息,并将信息内容传递过去
            mes = new Message();
            mes.obj = new String(messageBytes);
            mes.what = MyHandler.OUTPUT;
            myHandler.sendMessage(mes);
            return true;
        }
        else
        {
            //发送失败
            connect = false;
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
            return false;
        }
    }

    /**
     * 接收数据
     * @return 接收的数据内容
     */
    public String readData()
    {
        byte[] tempByte = new byte[4096];
        if (usbDeviceConnection == null) //判断USB连接链路是否为空,不为空才能进行数据接收
        {
            connect = false;
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
            return null;
        }
        int i = usbDeviceConnection.bulkTransfer(bulkInUsbEndpoint,tempByte,tempByte.length,100);//读取数据,100为超时时间,接收的数据为数组类型
        if (i < 0) //小于0代表接收失败或未接收到数据,接收结果也受USB设备的影响
        {
            return null;
        }
        //将接收的数组转为字符串并返回
        byte[] messageByte = new byte[i];
        System.arraycopy(tempByte,0, messageByte,0, i);
        return new String(messageByte);
    }

    /**
     * 设置USB设备的波特率,方法内涉及算法等;
     * @param paramInt 要设置波特率的数值
     * @return 设置结果,true代表设置成功
     */
    public boolean configUsb(int paramInt)
    {
        if (usbDeviceConnection != null)
        {
            byte[] arrayOfByte = new byte[8];
            usbDeviceConnection.controlTransfer(192, 95, 0, 0, arrayOfByte, 8, 1000);
            usbDeviceConnection.controlTransfer(64, 161, 0, 0, null, 0, 1000);
            long l1 = 1532620800 / paramInt;
            for (int i = 3; ; i--)
            {
                if ((l1 <= 65520L) || (i <= 0))
                {
                    long l2 = 65536L - l1;
                    int j = (short) (int) (0xFF00 & l2 | i);
                    int k = (short) (int) (0xFF & l2);
                    usbDeviceConnection.controlTransfer(64, 154, 4882, j, null, 0, 1000);
                    usbDeviceConnection.controlTransfer(64, 154, 3884, k, null, 0, 1000);
                    usbDeviceConnection.controlTransfer(192, 149, 9496, 0, arrayOfByte, 8, 1000);
                    usbDeviceConnection.controlTransfer(64, 154, 1304, 80, null, 0, 1000);
                    usbDeviceConnection.controlTransfer(64, 161, 20511, 55562, null, 0, 1000);
                    usbDeviceConnection.controlTransfer(64, 154, 4882, j, null, 0, 1000);
                    usbDeviceConnection.controlTransfer(64, 154, 3884, k, null, 0, 1000);
                    usbDeviceConnection.controlTransfer(64, 164, 0, 0, null, 0, 1000);
                    return true;
                }
                l1 >>= 3;
            }
        }
        else  return false;
    }

    /**
     * 获取收发数据的通道
     * @param usbDevice USB设备
     * @param usbDeviceConnection USB连接链路
     */
    public void openCDC(UsbDevice usbDevice, UsbDeviceConnection usbDeviceConnection)
    {
        this.usbDeviceConnection = usbDeviceConnection;
        usbInterface = usbDevice.getInterface(findCDC(usbDevice)); //获取USB设备接口
        if (usbDeviceConnection == null) //判断USB设备链路是否为空
        {
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
            return;
        }
        if (!usbDeviceConnection.claimInterface(usbInterface,true)) //USB设备链路绑定获取到的接口
        {
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
            return;
        }
        int numberEndpoints = usbInterface.getEndpointCount(); //获取USB设备接口的数据传输通道数量
        for (int num = 0; num <= numberEndpoints-1; num++)
        {
            UsbEndpoint usbEndpoint = usbInterface.getEndpoint(num);

            switch (usbEndpoint.getType())
            {
                //USB控制传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_CONTROL:
                    controlUsbEndpoint = usbEndpoint;
                    break;
                //USB块传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_BULK:
                    switch (usbEndpoint.getDirection())
                    {
                        case UsbConstants.USB_DIR_OUT:
                            bulkOutUsbEndpoint = usbEndpoint; //USB块传输模式输出通道
                            break;
                        case UsbConstants.USB_DIR_IN:
                            bulkInUsbEndpoint = usbEndpoint; //USB块传输模式输入通道
                            break;
                    }
                    break;
                //USB中断传输模式通道
                case UsbConstants.USB_ENDPOINT_XFER_INT:
                    switch (usbEndpoint.getDirection())
                    {
                        case UsbConstants.USB_DIR_OUT:
                            intOutUsbEndpoint = usbEndpoint;
                            break;
                        case UsbConstants.USB_DIR_IN:
                            intInUsbEndpoint = usbEndpoint;
                            break;
                    }
                    break;
            }
        }
        if (bulkOutUsbEndpoint != null && bulkInUsbEndpoint != null) //如果USB块传输模式输入输通道都不为空出
        {
            //USB连接成功
            connect = true;
            //获取到USB设备的ID、VID、PID
            String usbData = "Name:"+usbDevice.getDeviceName()+"\nID:"+usbDevice.getDeviceId()+"    VID:"+usbDevice.getVendorId()+"    PID:"+usbDevice.getProductId();
            mes = new Message();
            mes.obj = usbData;
            mes.what = MyHandler.USB_CONNECT_SUCCESS;
            myHandler.sendMessage(mes);
            threadReadData.start(); //开启接收数据线程
        }
        else
        {
            //USB连接失败
            connect = false;
            myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);
        }
    }

    /**
     * USB接收数据线程
     */
    private Thread threadReadData = new Thread(new Runnable()
    {
        String message = "";
        @Override
        public void run()
        {
            while (connect) //USB处于连接状态就循环执行
            {
                String temMes = readData(); //获取到接收到的字符串
                if (temMes != null) 
                {
                    //如果接收到的数据不为空,就一直拼接,因为这些可能属于同一组数据(除非USB设备发送频率小于我们设置的超时时间100毫秒)
                    message = message+temMes;
                    continue;
                }
                else
                {
                    //接收到的数据为空了,表示该组数据接收完整了,就可以发送给消息处理中心进行处理了
                    if (!message.equals("")) //接收到的数据要不为空,不然没意义
                    {
                        mes = new Message();
                        mes.obj = message;
                        mes.what = MyHandler.INPUT;
                        myHandler.sendMessage(mes);
                        message = "";
                    }
                }
            }
        }
    });

    /**
     * 获取USB可以收发数据的接口号
     * @param usbDevice USB设备
     * @return USB可以收发数据的接口号
     */
    private int findCDC(UsbDevice usbDevice)
    {
        int interfaceCount = usbDevice.getInterfaceCount(); //获取USB设备接口数量
        for (int count = 0; count < interfaceCount; ++count)
        {
            //遍历获取到的接口进行判断是否为收发数据的接口,是就返回该接口号
            if (usbDevice.getInterface(count).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
            {
                return count;
            }
        }
        // 如果获取到的所有接口没有我们需要的就返回-1
        return -1;
    }

     /**
     * 关闭USB连接、链路、数据通道等
     */
    public void close()
    {
        connect = false; //连接状态为false
        usbDeviceConnection.releaseInterface(usbInterface); //USB设备链路解绑接口
        usbDeviceConnection.close(); //关闭USB设备链路
        usbDeviceConnection = null; //USB设备链路赋值为空
        bulkOutUsbEndpoint = null; //输出通道赋值为空
        bulkInUsbEndpoint = null; //输入通道赋值为空
    }

}

接下来是连接USB类与业务层的中枢,HANDLER类:

package com.jlkj.lsk.usb_host;

import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyHandler extends Handler
{

    public static final int OUTPUT = 0; //发送消息
    public static final int INPUT = 1; //接收消息

    public static final int USB_CONNECT_SUCCESS = 2; //USB设备连接成功
    public static final int USB_CONNECT_FAILED = 3; //USB设备连接失败或断开连接

    public static boolean USB_CONNECT_STATE = false; //当前USB设备连接状态

    private Button bt_send,bt_sendTiming; 
    private TextView tv_sendMessageShow,tv_receiveMessageShow,tv_usbDataShow;
    private Context context; //上下文
    private MainActivity mainActivity;
    MyHandler(TextView tv_sendMessageShow, TextView tv_receiveMessageShow, TextView tv_usbDataShow, Button bt_send, Button bt_sendTiming, Context context,MainActivity mainActivity)
    {
        this.bt_send = bt_send;
        this.tv_sendMessageShow = tv_sendMessageShow;
        this.tv_receiveMessageShow = tv_receiveMessageShow;
        this.tv_usbDataShow = tv_usbDataShow;
        this.bt_sendTiming = bt_sendTiming;
        this.context = context;
        this.mainActivity = mainActivity;
    }

    @Override
    public void handleMessage(@NonNull Message msg)
    {
        switch (msg.what)
        {
            case USB_CONNECT_SUCCESS:  //USB设备连接成功
                MyHandler.USB_CONNECT_STATE  = true; //连接状态改变为true
                bt_send.setEnabled(true); //发送控件可以使用
                bt_sendTiming.setEnabled(true);
                bt_send.setTextColor(Color.BLACK); //定时发送控件可以使用
                bt_sendTiming.setTextColor(Color.BLACK); 
                tv_usbDataShow.setText(msg.obj.toString()); //填充意图发送过来的信息
                Toast.makeText(context,"连接成功",Toast.LENGTH_LONG).show();
                break;
            case USB_CONNECT_FAILED: //USB设备连接失败
                MyHandler.USB_CONNECT_STATE  = false;
                bt_send.setEnabled(false);
                bt_sendTiming.setEnabled(false);
                bt_send.setTextColor(Color.GRAY);
                bt_sendTiming.setTextColor(Color.GRAY);
                bt_sendTiming.setText("定时发送");
                tv_usbDataShow.setText("未连接设备");
                mainActivity.closeAll(); //连接断开或连接失败,执行关闭所有连接和对象的方法
                Toast.makeText(context,"断开连接",Toast.LENGTH_LONG).show();
                break;
            case OUTPUT:  //发送消息
                if (messageShowNeedRoll(tv_sendMessageShow) != 0) tv_sendMessageShow.scrollTo(0, messageShowNeedRoll(tv_sendMessageShow));//如果TextView填充满可使用高度就滚动到最新更新处
                tv_sendMessageShow.append("[TX]"+gteNowDate()+": "+msg.obj.toString()+"\n"); //给控件填充意图发送来的信息
                break;
            case INPUT:  //接收消息
                if (messageShowNeedRoll(tv_receiveMessageShow) != 0) tv_receiveMessageShow.scrollTo(0, messageShowNeedRoll(tv_receiveMessageShow));
                tv_receiveMessageShow.append("[RX]"+gteNowDate()+": "+msg.obj.toString()+"\n");
                break;
        }
    }

    /**
     * 返回格式化后的当前时间
     * @return 当前时间字符串形式
     */
    private String gteNowDate()
    {
        SimpleDateFormat sdf = new SimpleDateFormat();// 格式化时间
        sdf.applyPattern("HH:mm:ss");// 时:分:秒
        Date date = new Date();// 获取当前时间戳
        return sdf.format(date); //返回格式化后的时间戳
    }

    /**
     * 判断当前TextView是否已经填充满控件可使用高度,如果高度已满就滚动需要的距离高度
     * @param textView 需要判断的TextView控件
     * @return 已满就返回对应高度,否则就返回0
     */
    private int messageShowNeedRoll(TextView textView)
    {
        int offset = textView.getLineCount() * textView.getLineHeight(); //添加的textview数量 x 字体高度
        if (offset > textView.getHeight()) return offset - tv_receiveMessageShow.getHeight(); //如果乘积大于控件高度就返回需要滚动的距离
        else return 0; //小于就返回0
    }

}

监听USB连接状况的广播
类,如下:

package com.jlkj.lsk.usb_host;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.widget.TextView;
import android.widget.Toast;
import java.util.HashMap;

public class UsbMonitor extends BroadcastReceiver //继承USB广播对象
{

    private static final String ACTION_USB_PERMISSION = "com.spark.teaching.answertool.USB_PERMISSION"; //USB设备的操作权限,可自定义
    //private static final String ACTION_USB_PERMISSION = "android.USB"; //USB设备的操作权限,可自定义
    private int VID = 1155; //USB设备生产厂商ID,用来区分选择目标USB设备,如果不是该ID的USB设备,不对其进行操作
    private int PID = 1155; //USB设备生产厂商ID,用来区分选择目标USB设备,如果不是该ID的USB设备,不对其进行操作
    private UsbController usbController; //USB动作管理接口
    private UsbManager usbManager; //USB状态、管理对象
    private UsbDevice usbDevice; //USB设备
    private Context context; //上下文
    private TextView tv_usbDeviceDataShow; //USB信息数据展示控件

    /**
     * 数据初始化
     * @param usbController usb控制器接口
     * @param context 上下文
     * @param tv_usbDeviceDataShow USB信息数据展示控件
     */
    UsbMonitor(UsbController usbController,Context context,TextView tv_usbDeviceDataShow)
    {
        this.usbController = usbController;
        this.context = context;
        this.tv_usbDeviceDataShow = tv_usbDeviceDataShow;
    }

    /**
     * 注册USB广播监听、USB权限
     */
    public void register()
    {
        if (this.context != null)
        {
            IntentFilter intentFilter = new IntentFilter(); //意图过滤器
            intentFilter.addAction(ACTION_USB_PERMISSION); //添加USB设备的操作权限意图
            intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); //添加设备接入意图
            intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); //添加设备拔出意图
            this.context.registerReceiver(this, intentFilter); //注册添加的意图
            usbManager = (UsbManager)this.context.getSystemService(Context.USB_SERVICE); //获取USB设备管理

            if (usbManager != null)
            {
                HashMap<String,UsbDevice> list = usbManager.getDeviceList(); //获取USB设备,返回的是 UsbDevice 的Hash列表,里面是所有当前连接主机的USB设备
                for (UsbDevice usbDevice : list.values()) //遍历获取到的UsbDevice
                {
                    if ((usbDevice.getVendorId() == VID)&&(usbDevice.getProductId() == PID)) //找到目标USB设备
                    {
                        this.usbDevice = usbDevice;
                        usbController.onDeviceInsert(this, usbManager,usbDevice); //执行USB接入时接口
                        break;
                    }
                }
                tv_usbDeviceDataShow.setText("不支持该设备"); //如果列表里面没有目标USB设备,执行该操作
            }
            tv_usbDeviceDataShow.setText("未连接设备"); //如果没有USB设备接入,执行该操作
        }
    }

    /**
     * 请求打开此USB设备的权限
     * @param usbDevice usb设备
     */
    public void requestOpenDevice(UsbDevice usbDevice)
    {
        if (usbManager != null)
        {
            if (usbManager.hasPermission(usbDevice))//如果有该USB设备的操作权限
            {
                usbController.onDeviceOpen(this,usbManager,usbDevice);//连接USB设备(打开USB设备)
            }
            else
            {
                usbManager.requestPermission(usbDevice,PendingIntent.getBroadcast(context, 666, new Intent(ACTION_USB_PERMISSION), 0));//如果没有USB操作权限则请求权限
            }
        }
    }

    /**
     * 注销USB广播监听
     */
    public void unregister()
    {
        if (context != null)
        {
            context.unregisterReceiver(this); //注销USB设备广播监听
            context = null;
            usbManager = null;
            usbController = null;
        }
    }

    /**
     * 广播事务处理中心
     * @param context 上下文
     * @param intent 意图
     */
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (intent.getExtras() != null && !intent.getExtras().isEmpty())
        {
            usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); //获取意图中的USB设备
            switch(intent.getAction())
            {
                case UsbManager.ACTION_USB_DEVICE_ATTACHED: //USB设备接入
                    Toast.makeText(context, "设备接入", Toast.LENGTH_LONG).show();
                    if ((usbDevice.getVendorId() == VID)&&(usbDevice.getProductId() == PID)) usbController.onDeviceInsert(this, usbManager,usbDevice); //找到目标USB设备,执行USB设备接入时接口
                    else tv_usbDeviceDataShow.setText("不支持该设备"); //未找到目标USB设备
                    break;
                case UsbManager.ACTION_USB_DEVICE_DETACHED: //USB设备拔出
                    Toast.makeText(context, "设备断开", Toast.LENGTH_LONG).show();
                    usbController.onDevicePullOut(this,usbManager,usbDevice); //执行USB设备拔出时接口
                    break;
                case UsbMonitor.ACTION_USB_PERMISSION: //请求USB设备操作权限
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
                    {
                        //同意USB权限
                        usbController.onDeviceOpen(this,usbManager,usbDevice); //执行连接USB设备接口
                    }
                    else
                    {
                        //拒绝USB权限
                        Toast.makeText(context, "拒绝USB权限!", Toast.LENGTH_LONG).show();
                    }
                    break;
            }
        }
        else
        {
            Toast.makeText(this.context,"请检查USB设备!",Toast.LENGTH_LONG).show();
        }
    }

}

接口类 UsbController

public interface UsbController
{

     /**
     * USB设备接入时的接口
     * @param usbMonitor USB监听广播对象
     * @param usbManager USB状态、管理对象
     * @param usbDevice USB设备对象
     */
    void onDeviceInsert(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice);

    //USB设备拔出时的接口
    void onDevicePullOut(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice);

    //连接USB设备(打开USB设备)的接口
    void onDeviceOpen(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice);
    
}

activity类

package com.jlkj.lsk.usb_host;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity implements UsbController
{
    private Timer timer; //计时器对象
    private TimerTask timerTask; //计时器任务对象

    private UsbCDC usbCDC; //当前连接的USB设备对象
    private MyHandler myHandler; //消息处理中心对象
    private UsbMonitor usbMonitor; //USB监听广播对象
    private TextView m_tv_sendMessageShow,m_tv_receiveMessageShow,m_tv_usbDataShow,m_tv_porterShow;
    private EditText m_et_messageText,m_et_time;
    private Button m_bt_send,m_bt_sendTiming,m_bt_clean,m_tv_porterSet;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView(); //实例化当前页面控件
        initData(); //加载初始数据

        m_bt_send.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                //开启新线程进行数据发送
                new Thread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        usbCDC.send(m_et_messageText.getText().toString()); //向当前连接的USB设备发送消息
                    }
                }).start();
            }
        });

        m_bt_clean.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                m_tv_sendMessageShow.setText(""); //清除发送的消息文本
                m_tv_receiveMessageShow.setText(""); //清除接收的消息文本
                m_tv_sendMessageShow.scrollTo(0, 0); //发送的消息文本回滚到最顶部
                m_tv_receiveMessageShow.scrollTo(0, 0); //接收的消息文本回滚到最顶部
            }
        });

        m_bt_sendTiming.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                //判断计时器是否为空,不为就代表正在执行计时任务,就停止当前任务
                if (timer != null)
                {
                    timer.cancel(); //停止计时器
                    timer = null; //计时器设为空
                    m_bt_sendTiming.setTextColor(Color.BLACK); //改变定时发送控件的颜色
                    m_bt_sendTiming.setText("定时发送"); //控件 恢复为“定时发送”
                }
                else
                {
                    //如果为空,就开始定时发送任务
                    if (!m_et_time.getText().toString().equals("")) //获取定时任务的时间间隔
                    {
                        timer = new Timer(); //创建定时器
                        timerTask = new TimerTask() //创建定时任务
                        {
                            @Override
                            public void run()
                            {   
                                //定时任务要执行的内容
                                usbCDC.send(m_et_messageText.getText().toString());
                            }
                        };
                        new Thread(new Runnable() //开启新线程执行 开启计时器
                        {
                            @Override
                            public void run()
                            {
                                //开启计时器
                                timer.schedule(timerTask,0,Integer.parseInt(m_et_time.getText().toString()));
                            }
                        }).start();
                        m_bt_sendTiming.setTextColor(Color.RED);
                        m_bt_sendTiming.setText("停止"); //计时器开始后“定时发送”控件就改变颜色和字体
                    }
                    else
                    {
                        //判断计时器是否为空,如果为空,就执行该作用域内容
                        Toast.makeText(MainActivity.this,"定时不能为空",Toast.LENGTH_LONG).show();
                    }
                }
            }
        });

        m_tv_porterSet.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                //展示设置波特率的diaog对象
                setPorter();
            }
        });
    }

    @Override
    public void onDeviceInsert(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice)
    {
        usbMonitor.requestOpenDevice(usbDevice); //请求USB连接权限
    }

    @Override
    public void onDevicePullOut(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice)
    {
       closeAll(); //执行关闭所有连接的方法
       myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED); //向消息中心发送 断开连接 信息
    }

    @Override
    public void onDeviceOpen(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice)
    {
        usbCDC = new UsbCDC(myHandler); //创建USB连接的对象
        UsbDeviceConnection connection = usbManager.openDevice(usbDevice); //获取此USB链路
        usbCDC.openCDC(usbDevice, connection); //连接USB设备(打开USB设备)
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        closeAll();//执行关闭所有连接的方法
        myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED);//向消息中心发送 断开连接 信息
    }

    //关闭所有连接
    public void closeAll()
    {
        if (usbCDC != null)
        {
            usbCDC.close();
            usbCDC = null;
        }
        if (timer != null)
        {
            timer.cancel();
            timer = null;
        }
    }

    //展示设置波特率dialog的对象,用一个AlertDialog让用户进行选择比特率
    private void setPorter()
    {
        final String[] items = {"2400","4800","9600","19200","38400","57600","115200","230400","460800","1700000","2300000","3400000"};
        AlertDialog.Builder listDialog = new AlertDialog.Builder(MainActivity.this);
        listDialog.setTitle("设置波特率");
        listDialog.setItems(items, new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialog, int which)
            {
                //判断当前USB设备是否连接,连接之后才可以设置波特率
               if (usbCDC != null)
               {
                   boolean porter = usbCDC.configUsb(Integer.parseInt(items[which])); //执行设置波特率的对象,返回true代表设置成功
                   m_tv_porterShow.setText(porter ? "波特率:"+items[which]:"波特率:9600"); //设置波特率对象返回true才改变控件字体,否则设置失败,不改变控件字体
               }
               else
               {
                    //判断当前USB设备是否连接,未连接则提示 设备未连接
                   Toast.makeText(MainActivity.this,"设备未连接",Toast.LENGTH_LONG).show();
               }
            }
        });
        listDialog.show(); //展示dialog
    }

    //加载数据
    private void initData()
    {
        myHandler = new MyHandler(m_tv_sendMessageShow,m_tv_receiveMessageShow,m_tv_usbDataShow,m_bt_send,m_bt_sendTiming,this,MainActivity.this); //实例化消息处理中心
        usbMonitor = new UsbMonitor(this,this,m_tv_usbDataShow); //实例化USB广播监听
        usbMonitor.register(); //注册USB广播监听,注册之后,才可以正常监听USB设备
    }

    //实例化控件
    private void initView()
    {
        m_tv_receiveMessageShow = (TextView)findViewById(R.id.m_tv_receiveMessageShow);
        m_tv_receiveMessageShow.setMovementMethod(ScrollingMovementMethod.getInstance());
        m_tv_sendMessageShow = (TextView)findViewById(R.id.m_tv_sendMessageShow);
        m_tv_sendMessageShow.setMovementMethod(ScrollingMovementMethod.getInstance());
        m_tv_usbDataShow = (TextView)findViewById(R.id.m_tv_usbDataShow);
        m_et_messageText = (EditText)findViewById(R.id.m_et_messageText);
        m_et_time = (EditText)findViewById(R.id.m_et_time);
        m_bt_sendTiming = (Button)findViewById(R.id.m_bt_sendTiming);
        m_bt_clean = (Button)findViewById(R.id.m_bt_clean);
        m_bt_send = (Button)findViewById(R.id.m_bt_send);
        m_tv_porterShow = (TextView)findViewById(R.id.m_tv_porterShow);
        m_tv_porterSet = (Button)findViewById(R.id.m_bt_porterSet);
    }

}

这个比较初级,接下来,我会再出一个精简版本;详细探讨Android读取USB串口、HID设备的实现;我把这两个集成在了一起,并打包成了arr文件,这个库可以自动识别USB 串口或USB hid ,当连接成功,就可以自动读写;代码比这个更为精简;接下来我会慢慢的记录下来

  • 8
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值