因为第一次接触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。

android usb 设备的加载 android usb接口_USB串口

二、发现打开串口
首先我们需要获取到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;
    }
}