Android USB串口开发
因为第一次接触Android下的串口开发,在网上找了很多例子都不能满足自己的需要以及不能正常使用,于是结合网上的资源以及查阅资料,终于完成了关于这个串口的开发,在此记录下usb转串口通信开发的过程。
Android串口开发步骤总共分为四大类,如下
1. 权限获取
2. 发现打开串口
3. 串口操作(发送与读取)
4. 关闭串口
一、权限获取
首先我们需要在AndroidMainfest.xml文件中配置USB使用权限
<uses-feature android:name="android.hardware.usb.host" />
并在我们的Activity标签中配置intent-filter
<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" />
resource资源为res资源文件夹下的xml文件下的device_filter.xml文件。device_filter.xml文件是我们需要知道我们USB设备硬件的vendor-id和product-id。文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device
vendor-id="xxxx"
product-id="xxxx"/>
</resources>
其中vendor-id以及product-id我们可以通过计算机管理 -> 电脑电脑设备管理器,Android Phone 查看详细信息,属性选择硬件ID。
二、发现打开串口
首先我们需要获取到UsbManager管理类,通过此类的getDeviceList()方法得到包含所有已连接的USB设备的列表。最后,通过设备名称来得到给设备对象。
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceMap = usbManager.getDeviceList();
UsbDevice device = deviceMap.get("deviceName");
上面是获取指定的串口设备,如果你想获取到所有的可以通过usbManager.getDeviceList().values();获取在foreach循环迭代拿到每一个设备。
当我们获取到一个设备之后,我们首先要判断是否有该权限,通过usbManager.hasPermission(device)
判断是否有该权限。当有权限我们就可以打开串口设备以及对他设置波特率、检验位等参数信息。
private void permissionAllow(UsbDevice device) {
List<UsbSerialPort> result = new ArrayList<>();
for (final UsbSerialDriver driver : drivers) {
final List<UsbSerialPort> ports = driver.getPorts();
result.addAll(ports);
}
UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(device);
try {
serialPort = result.get(0);
serialPort.open(usbDeviceConnection);
// 第一个参数为波特率 第二个为停止位 第三个为奇偶校验,具体的应该根据自己的协议来配置
serialPort.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
} catch (IOException e) {
e.printStackTrace();
}
UsbInterface anInterface = device.getInterface(0);
if (anInterface == null) {
Toast.makeText(this, "初始化失败", Toast.LENGTH_SHORT).show();
return;
}
// 判断端口号
for (int i = 0; i < anInterface.getEndpointCount(); i++) {
UsbEndpoint endpoint = anInterface.getEndpoint(i);
if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
// 输入端口
usbEndpointIn = endpoint;
} else if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
// 输出端口
usbEndpointOut = endpoint;
}
}
}
}
当没有权限的使用,需要注册本地广播去申请权限
// private static final String ACTION_USB_PERMISSION = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
UsbPermissionActionReceiver mUsbPermissionActionReceiver = new UsbPermissionActionReceiver();
Intent intent = new Intent(ACTION_USB_PERMISSION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
IntentFilter intentFilter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbPermissionActionReceiver, intentFilter);
usbManager.requestPermission(device, pendingIntent);
private class UsbPermissionActionReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
// user choose YES for your previously popup window asking for grant perssion for this usb device
if (null != usbDevice) {
permissionAllow(usbDevice);
}
} else {
//user choose NO for your previously popup window asking for grant perssion for this usb device
Toast.makeText(context, String.valueOf("Permission denied for device" + usbDevice), Toast.LENGTH_LONG).show();
}
}
}
}
}
三、串口操作
通过以上我们已经对串口的获取以及配对都已经完成了,接下来只需要发送数据给串口就OK了。
private void sendToUsb(String[] hexString) throws Exception {
// 在这里需要转换byte数组,因为串口按位(bit)发送和接收字节
byte[] bytes = new byte[hexString.length];
for (int i = 0; i < hexString.length; i++) {
bytes[i] = (byte) Integer.parseInt(hexString[i].substring(2), 16);
}
serialPort.write(bytes, bytes.length);
}
已某一个指令为例
private void studyCode() {
try {
// 该数据为协议规定的十六进制的数据值
String[] studyCodeStrArr = "0X55 0X4C 0X42 0X00 0X00 0X00 0X00 0X01 0X00 0X00 0X00 0X00 0X00 0X00 0X00 0X01 0X0A 0X00 0XEF".replace("X", "x").split(" ");
sendToUsb(studyCodeStrArr);
} catch (Exception e) {
Toast.makeText(this, "学码失败", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
四、关闭串口
当你完成数据的传输或者你的设备已拔出时,通过调用releaseInterface()和 close()来关闭接口和连接。
支持已完成了串口的开发,下面附上完整代码。
package com.huruwo.serialporthelper;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* author: YJZ
* date: 2018/6/15
* des: USB串口开发
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener, IAudioListen, SeekBar.OnSeekBarChangeListener {
private UsbEndpoint usbEndpointIn;
private UsbEndpoint usbEndpointOut;
private UsbManager usbManager;
private UsbSerialPort serialPort;
private TextView textView;
private TextView seekBarValue;
private List<UsbSerialDriver> drivers;
public MainActivity() {
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initView();
initUsbSerial();
}
private void initUsbSerial() {
// 1.查找设备
usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
drivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager);
if (drivers.size() <= 0) {
Toast.makeText(this, "无串口设备", Toast.LENGTH_SHORT).show();
return;
}
UsbDevice device = drivers.get(0).getDevice();
if (usbManager.hasPermission(device)) {
permissionAllow(device);
} else {
Log.e("TAG", "没有权限");
UsbPermissionActionReceiver mUsbPermissionActionReceiver = new UsbPermissionActionReceiver();
Intent intent = new Intent(ACTION_USB_PERMISSION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
IntentFilter intentFilter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbPermissionActionReceiver, intentFilter);
usbManager.requestPermission(device, pendingIntent);
}
}
private void permissionAllow(UsbDevice device) {
List<UsbSerialPort> result = new ArrayList<>();
for (final UsbSerialDriver driver : drivers) {
final List<UsbSerialPort> ports = driver.getPorts();
result.addAll(ports);
}
UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(device);
try {
serialPort = result.get(0);
serialPort.open(usbDeviceConnection);
serialPort.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
} catch (IOException e) {
e.printStackTrace();
}
UsbInterface anInterface = device.getInterface(0);
if (anInterface == null) {
Toast.makeText(this, "初始化失败", Toast.LENGTH_SHORT).show();
return;
}
// 判断端口号
for (int i = 0; i < anInterface.getEndpointCount(); i++) {
UsbEndpoint endpoint = anInterface.getEndpoint(i);
if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
// 输入端口
usbEndpointIn = endpoint;
} else if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
// 输出端口
usbEndpointOut = endpoint;
}
}
}
}
private static final String ACTION_USB_PERMISSION = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
@Override
public void top() {
topTurn();
}
@Override
public void pause() {
pauseTurn();
}
@Override
public void update(final double volume) {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("分贝:" + String.valueOf(volume));
}
});
Log.e("TAG", "volume = " + volume);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
AudioRecordDemo.MAX_VOLUME = progress;
seekBarValue.setText(String.format(Locale.CHINA, "阀值:%s", progress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
private class UsbPermissionActionReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
// user choose YES for your previously popup window asking for grant perssion for this usb device
if (null != usbDevice) {
permissionAllow(usbDevice);
}
} else {
//user choose NO for your previously popup window asking for grant perssion for this usb device
Toast.makeText(context, String.valueOf("Permission denied for device" + usbDevice), Toast.LENGTH_LONG).show();
}
}
}
}
}
private void initView() {
findViewById(R.id.study_code).setOnClickListener(this);
findViewById(R.id.top).setOnClickListener(this);
findViewById(R.id.pause).setOnClickListener(this);
findViewById(R.id.bottom).setOnClickListener(this);
findViewById(R.id.start_audio).setOnClickListener(this);
findViewById(R.id.stop_audio).setOnClickListener(this);
SeekBar seekBar = findViewById(R.id.seek_bar);
seekBarValue = findViewById(R.id.seek_bar_value);
seekBar.setOnSeekBarChangeListener(this);
textView = findViewById(R.id.value);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.study_code:
studyCode();
break;
case R.id.top:
topTurn();
break;
case R.id.pause:
pauseTurn();
break;
case R.id.bottom:
bottomTurn();
break;
case R.id.start_audio:
AudioRecordDemo demo = new AudioRecordDemo();
demo.setAudioRecordListener(this);
demo.getNoiseLevel();
break;
case R.id.stop_audio:
AudioRecordDemo.isGetVoiceRun = false;
break;
}
}
private void bottomTurn() {
try {
String[] bottomStrArr = "0X55 0X4C 0X42 0X00 0X00 0X00 0X00 0X01 0X00 0X00 0X00 0X00 0X00 0X00 0X00 0X01 0X0C 0X00 0XF1".replace("X", "x").split(" ");
sendToUsb(bottomStrArr);
} catch (Exception e) {
Toast.makeText(this, "失败", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
private void pauseTurn() {
try {
String[] pauseStrArr = "0X55 0X4C 0X42 0X00 0X00 0X00 0X00 0X01 0X00 0X00 0X00 0X00 0X00 0X00 0X00 0X01 0X0D 0X00 0XF2".replace("X", "x").split(" ");
sendToUsb(pauseStrArr);
} catch (Exception e) {
Toast.makeText(this, "失败", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
private void topTurn() {
try {
String[] topStrArr = "0X55 0X4C 0X42 0X00 0X00 0X00 0X00 0X01 0X00 0X00 0X00 0X00 0X00 0X00 0X00 0X01 0X0E 0X00 0XF3".replace("X", "x").split(" ");
sendToUsb(topStrArr);
} catch (Exception e) {
Toast.makeText(this, "失败", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
private void studyCode() {
try {
String[] studyCodeStrArr = "0X55 0X4C 0X42 0X00 0X00 0X00 0X00 0X01 0X00 0X00 0X00 0X00 0X00 0X00 0X00 0X01 0X0A 0X00 0XEF".replace("X", "x").split(" ");
sendToUsb(studyCodeStrArr);
} catch (Exception e) {
Toast.makeText(this, "学码失败", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
private void sendToUsb(String[] hexString) throws Exception {
byte[] bytes = new byte[hexString.length];
for (int i = 0; i < hexString.length; i++) {
bytes[i] = (byte) Integer.parseInt(hexString[i].substring(2), 16);
}
serialPort.write(bytes, bytes.length);
}
@Override
protected void onPause() {
super.onPause();
AudioRecordDemo.isGetVoiceRun = false;
}
@Override
protected void onDestroy() {
super.onDestroy();
AudioRecordDemo.isGetVoiceRun = false;
}
}