最近在研究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、PIDString 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; //连接状态改变为truebt_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、PIDString 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 = "";@Overridepublic 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;}}// 如果获取到的所有接口没有我们需要的就返回-1return -1;}/*** 关闭USB连接、链路、数据通道等*/public void close(){connect = false; //连接状态为falseusbDeviceConnection.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;}@Overridepublic void handleMessage(@NonNull Message msg){switch (msg.what){case USB_CONNECT_SUCCESS:  //USB设备连接成功MyHandler.USB_CONNECT_STATE  = true; //连接状态改变为truebt_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 意图*/@Overridepublic 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;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView(); //实例化当前页面控件initData(); //加载初始数据m_bt_send.setOnClickListener(new View.OnClickListener(){@Overridepublic void onClick(View v){//开启新线程进行数据发送new Thread(new Runnable(){@Overridepublic void run(){usbCDC.send(m_et_messageText.getText().toString()); //向当前连接的USB设备发送消息}}).start();}});m_bt_clean.setOnClickListener(new View.OnClickListener(){@Overridepublic 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(){@Overridepublic 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() //创建定时任务{@Overridepublic void run(){   //定时任务要执行的内容usbCDC.send(m_et_messageText.getText().toString());}};new Thread(new Runnable() //开启新线程执行 开启计时器{@Overridepublic 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(){@Overridepublic void onClick(View v){//展示设置波特率的diaog对象setPorter();}});}@Overridepublic void onDeviceInsert(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice){usbMonitor.requestOpenDevice(usbDevice); //请求USB连接权限}@Overridepublic void onDevicePullOut(UsbMonitor usbMonitor, UsbManager usbManager, UsbDevice usbDevice){closeAll(); //执行关闭所有连接的方法myHandler.sendEmptyMessage(MyHandler.USB_CONNECT_FAILED); //向消息中心发送 断开连接 信息}@Overridepublic 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设备)}@Overrideprotected 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(){@Overridepublic 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 ,当连接成功,就可以自动读写;代码比这个更为精简;接下来我会慢慢的记录下来

【USB】Android实现读写USB串口数据相关推荐

  1. linux 使用usb转串口模块并读串口数据

    买了一个无线通信模块,在windows下还要装驱动才能读.在windows下测试无线模块没问题后,在ubantu14.2中测试怎么读串口.步骤如下: 1.查看看系统信息 dmesg | tail -f ...

  2. Android(Linux)实时监控串口数据

    之前在做WinCE车载方案时,曾做过一个小工具TraceMonitor,用于显示WinCE系统上应用程序的调试信息,特别是在实车调试时,用于监控和显示CAN盒与主机之间的串口数据.因为需要抢占市场先机 ...

  3. xp usb android,windowsxp系统设置usb手机网络分享的方法

    现在,我们只要使用USB数据线将手机与计算机绑定,就能轻松实现计算机共享手机的互联网连接.不过,一些xp系统用户在操作过程中,也会遇到连接失败的情况.这该怎么办呢?下面,系统城小编就为大家详细介绍下x ...

  4. xp系统usb android,xp系统usb手机网络共享怎么设置,xp设置usb手机网络分享的方法

    随着智能手机的不断普及,如今通过USB数据线将手机与计算机绑定,从而实现计算机共享手机的互联网连接早已成为现实.但于对于Android类型的智能手机而言,通常情况下支持的绑定只限于Windows Vi ...

  5. android aoa 串口,沁恒股份USB Android AOA转接概述

    概 述 本方案是以CH9343为核心芯片的全速USB Android HOST接口控制方案,具有高集成度.低功耗.单芯片体积小等特点,可配置为6种扩展接口. 该方案为安卓手机或平板USB连接外部GPI ...

  6. C++ MFC界面读写USB HID设备数据程序

    C++ MFC界面读写USB HID设备数据程序 发一个简单易用的界面,用来对USB HID设备(比如说游戏手柄,控制面板等)读写数据,一般情况下面板上有一些LED,可以帮助我们测试读写是否正确.另外 ...

  7. 树莓派3b接收USB串口数据并解析处理

    通过树莓派3b使用wiringPi接收串口数据,并对帧头帧尾进行判断,解析出符合帧协议的数据. 1. 帧头.帧尾.帧长度定义 我们在这里定义串口数据的帧头为 0x3A 0x3B,帧尾为 0x7E 0x ...

  8. Bus Hound 工具抓取串口数据(PC端抓取USB转串口数据)

    测试环境: PC端 USB转串口 链接终端板卡串口 目标:抓取通信过程中的通信数据 工具介绍: Bus Hound是是由美国perisoft公司研制的一款超级软件总线协议分析器,它是一种专用于PC机各 ...

  9. linux 下串口转usb不能发送数据包,Linux ,USB转串口驱动,没法读到数据

    Linux ,USB转串口驱动,无法读到数据 usb 1-1.1: new full-speed USB device number 5 using ehci-pci usb 1-1.1: New U ...

最新文章

  1. 认证连接_长江连接器哪些产品通过认证?
  2. Mac OS 错误代码 -8072的可行解决方法
  3. php获取页面的可视内容高度,网页制作技巧:获取页面可视区域的高度_css
  4. BCD码与十进制的相互转换
  5. 使用嵌套循环,打印 5 行 5 列的直角三角形
  6. 前端工程师的迷茫:不知道我这种前端是不是被淘汰了?
  7. python hello world
  8. jsp页面时间戳转换为时间格式
  9. 人工智能 AI技术学习路线图 初阶+中阶+高阶
  10. VirtualBox的BUG:没超线程也认为有
  11. 如何查找电脑的MAC地址?(上)
  12. 蒙特卡洛模拟最牛的地方在哪里呢?
  13. 你必须知道的指针基础-6.内存的初始化及结构体的使用
  14. 关于ioncube扩展的安装和使用
  15. 破解LanStar技术揭秘
  16. 【PHPWord】如何解决PHPWord的输出checkbox复选框并设置checked已勾选
  17. 架构衍变过程----58同城沈剑:好的架构源于不停地衍变,而非设计
  18. 2022 Robocom世界机器人开发者大赛 CAIP编程赛道 本科组-省赛 挨打记录+题解
  19. Vue 组件封装之 Content 列表(处理多行输入框 textarea)
  20. 小区挤不挤?来看用ArcGIS计算小区的容积率(附练习数据下载)

热门文章

  1. Spring Cloud Alibaba微服务架构实战教程—06让你躺平的敏捷开发
  2. 关于ADMM的研究(一)
  3. 安装typora时出现错误
  4. c#工业自动化通信开发库,工业自动软件必备的基本程序
  5. 使用ajax实现文件上传,使用input实现本地图片展示
  6. 数字图像学习邻域运算
  7. apk签名相关工具及使用方法
  8. 图片上传预览的几种方式,了解下?
  9. MLlib spark 垃圾邮件分类
  10. 【C语言你不知道的那些事儿】C语言Printf的用法以及如何打印%号