最近在研究USB方面的内容;先后做了关于Android读写HID、串口设备的DEMO。本文比较简单,主要介绍的是Android实现读取串口数据的功能
废话不多说,先看一下业务层是如何调用的;如图:
首先,监听USB连接状况,当USB 进行请求USB权限,当USB权限申请成功,进行调用打开Usb设备的方法;当监听到USB断开,进行关闭连接;
这是向串口写入数据的方法;
本DEMO主要使用Handle进行数据各个线程之间的数据传到,以及USB连接读写情况的反馈;
下面直接上代码:
连接USB设备的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
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); } } |
这个代码有点多,可能看的不太清楚,我删减一下,是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
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 读取串口数据
1 2 3 4 5 6 7 8 9 10 11 |
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,要读取的数据长度,和放要读取的数据的数组,超时时间
有了读,当然也需要写,向串口写入数据;代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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类的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
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类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
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类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
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连接状况的广播类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
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 ,当连接成功,就可以自动读写;代码比这个更为精简;接下来我会慢慢的记录下来。