kopia lustrzana https://github.com/N0BOY/FT8CN
375 wiersze
12 KiB
Java
Executable File
375 wiersze
12 KiB
Java
Executable File
package com.bg7yoz.ft8cn.connector;
|
||
/**
|
||
* 用于USB串口操作的类。USB串口驱动在serialport目录中,主要是CDC、CH34x、CP21xx、FTDI等。
|
||
*
|
||
* @author BGY70Z
|
||
* @date 2023-03-20
|
||
*/
|
||
|
||
import android.annotation.SuppressLint;
|
||
import android.app.Activity;
|
||
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.UsbDeviceConnection;
|
||
import android.hardware.usb.UsbManager;
|
||
import android.os.Build;
|
||
import android.util.Log;
|
||
|
||
import com.bg7yoz.ft8cn.BuildConfig;
|
||
import com.bg7yoz.ft8cn.serialport.CdcAcmSerialDriver;
|
||
import com.bg7yoz.ft8cn.serialport.UsbSerialDriver;
|
||
import com.bg7yoz.ft8cn.serialport.UsbSerialPort;
|
||
import com.bg7yoz.ft8cn.serialport.UsbSerialProber;
|
||
import com.bg7yoz.ft8cn.serialport.util.SerialInputOutputManager;
|
||
|
||
import java.io.IOException;
|
||
import java.util.ArrayList;
|
||
import java.util.EnumSet;
|
||
|
||
|
||
public class CableSerialPort {
|
||
private static final String TAG = "CableSerialPort";
|
||
private OnConnectorStateChanged onStateChanged;
|
||
public static final int SEND_TIMEOUT = 2000;
|
||
|
||
private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID;
|
||
|
||
public enum UsbPermission {Unknown, Requested, Granted, Denied}
|
||
|
||
private UsbPermission usbPermission = UsbPermission.Unknown;
|
||
|
||
private BroadcastReceiver broadcastReceiver;
|
||
private final Context context;
|
||
|
||
private int vendorId = 0x0c26;//设备号
|
||
private int portNum = 0;//端口号
|
||
private int baudRate = 19200;//波特率
|
||
|
||
private UsbSerialPort usbSerialPort;
|
||
private SerialInputOutputManager usbIoManager;
|
||
public SerialInputOutputManager.Listener ioListener = null;
|
||
|
||
private UsbManager usbManager;
|
||
private UsbDeviceConnection usbConnection;
|
||
private UsbSerialDriver driver;
|
||
|
||
|
||
private boolean connected = false;//是否处于连接状态
|
||
|
||
public CableSerialPort(Context mContext, SerialPort serialPort, int baud, OnConnectorStateChanged connectorStateChanged) {
|
||
vendorId = serialPort.vendorId;
|
||
portNum = serialPort.portNum;
|
||
baudRate = baud;
|
||
context = mContext;
|
||
this.onStateChanged=connectorStateChanged;
|
||
doBroadcast();
|
||
}
|
||
|
||
public CableSerialPort(Context mContext) {
|
||
context = mContext;
|
||
doBroadcast();
|
||
}
|
||
|
||
private void doBroadcast() {
|
||
broadcastReceiver = new BroadcastReceiver() {
|
||
@Override
|
||
public void onReceive(Context context, Intent intent) {
|
||
if (INTENT_ACTION_GRANT_USB.equals(intent.getAction())) {
|
||
usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
|
||
? UsbPermission.Granted : UsbPermission.Denied;
|
||
connect();
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
private boolean prepare() {
|
||
registerRigSerialPort(context);
|
||
UsbDevice device = null;
|
||
usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||
|
||
//此处把connection设成Null,这样在后面来通过是否null判断是否有权限。
|
||
usbConnection = null;
|
||
//此处是不是做个权限判断?
|
||
if (usbManager == null) {
|
||
return false;
|
||
}
|
||
|
||
|
||
for (UsbDevice v : usbManager.getDeviceList().values()) {
|
||
if (v.getVendorId() == vendorId) {
|
||
device = v;
|
||
}
|
||
}
|
||
if (device == null) {
|
||
Log.e(TAG, String.format("串口设备打开失败: 没有找到设备0x%04x", vendorId));
|
||
return false;
|
||
}
|
||
driver = UsbSerialProber.getDefaultProber().probeDevice(device);
|
||
if (driver == null) {
|
||
//试着把未知的设备加入到cdc驱动上
|
||
driver = new CdcAcmSerialDriver(device);
|
||
}
|
||
if (driver.getPorts().size() < portNum) {
|
||
Log.e(TAG, "串口号不存在,无法打开。");
|
||
return false;
|
||
}
|
||
Log.e(TAG, "connect: port size:" + String.valueOf(driver.getPorts().size()));
|
||
usbSerialPort = driver.getPorts().get(portNum);
|
||
usbConnection = usbManager.openDevice(driver.getDevice());
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
//@RequiresApi(api = Build.VERSION_CODES.S)
|
||
public boolean connect() {
|
||
connected = false;
|
||
if (!prepare()) {
|
||
//return false;
|
||
}
|
||
if (driver == null) {
|
||
if (onStateChanged!=null){
|
||
onStateChanged.onRunError("无法连接串口,没有驱动或串口不存在!");
|
||
}
|
||
return false;
|
||
}
|
||
if (usbConnection == null && usbPermission == UsbPermission.Unknown
|
||
&& !usbManager.hasPermission(driver.getDevice())) {
|
||
usbPermission = UsbPermission.Requested;
|
||
|
||
PendingIntent usbPermissionIntent;
|
||
|
||
//在android12 开始,增加了PendingIntent.FLAG_MUTABLE保护机制,所以要做版本判断
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||
usbPermissionIntent = PendingIntent.getBroadcast(context, 0
|
||
, new Intent(INTENT_ACTION_GRANT_USB), PendingIntent.FLAG_MUTABLE);
|
||
} else {
|
||
usbPermissionIntent = PendingIntent.getBroadcast(context, 0
|
||
, new Intent(INTENT_ACTION_GRANT_USB), 0);
|
||
}
|
||
|
||
|
||
//PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(context, 0
|
||
// , new Intent(INTENT_ACTION_GRANT_USB), PendingIntent.FLAG_MUTABLE);
|
||
// , new Intent(INTENT_ACTION_GRANT_USB), 0);
|
||
|
||
|
||
usbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
|
||
prepare();
|
||
}
|
||
if (usbConnection == null) {
|
||
if (onStateChanged!=null){
|
||
onStateChanged.onRunError("无法连接串口,可能没有访问USB设备的权限!");
|
||
}
|
||
|
||
return false;
|
||
}
|
||
try {
|
||
usbSerialPort.open(usbConnection);
|
||
//波特率、停止位
|
||
usbSerialPort.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
|
||
usbIoManager = new SerialInputOutputManager(usbSerialPort, new SerialInputOutputManager.Listener() {
|
||
@Override
|
||
public void onNewData(byte[] data) {
|
||
if (ioListener != null) {
|
||
ioListener.onNewData(data);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onRunError(Exception e) {
|
||
if (ioListener != null) {
|
||
ioListener.onRunError(e);
|
||
}
|
||
disconnect();
|
||
}
|
||
});
|
||
usbIoManager.start();
|
||
Log.d(TAG, "串口打开成功!");
|
||
connected = true;
|
||
|
||
if (onStateChanged!=null){
|
||
onStateChanged.onConnected();
|
||
}
|
||
|
||
|
||
} catch (Exception e) {
|
||
Log.e(TAG, "串口打开失败: " + e.getMessage());
|
||
if (onStateChanged!=null){
|
||
onStateChanged.onRunError("串口打开失败: " + e.getMessage());
|
||
}
|
||
disconnect();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public boolean sendData(final byte[] src) {
|
||
if (usbSerialPort != null) {
|
||
try {
|
||
usbSerialPort.write(src, SEND_TIMEOUT);
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
Log.e(TAG, "发送数据出错:" + e.getMessage());
|
||
return false;
|
||
}
|
||
return true;
|
||
} else {
|
||
Log.e(TAG, "无法发送数据,串口没有打开。");
|
||
return false;
|
||
}
|
||
|
||
}
|
||
|
||
public void disconnect() {
|
||
connected = false;
|
||
if (onStateChanged!=null){
|
||
onStateChanged.onDisconnected();
|
||
}
|
||
if (usbIoManager != null) {
|
||
usbIoManager.setListener(null);
|
||
usbIoManager.stop();
|
||
}
|
||
usbIoManager = null;
|
||
try {
|
||
if (usbSerialPort != null) {
|
||
usbSerialPort.close();
|
||
}
|
||
} catch (IOException ignored) {
|
||
}
|
||
usbSerialPort = null;
|
||
}
|
||
|
||
public void registerRigSerialPort(Context context) {
|
||
Log.d(TAG, "registerRigSerialPort: registered!");
|
||
context.registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB));
|
||
}
|
||
|
||
public void unregisterRigSerialPort(Activity activity) {
|
||
Log.d(TAG, "unregisterRigSerialPort: unregistered!");
|
||
activity.unregisterReceiver(broadcastReceiver);
|
||
}
|
||
|
||
|
||
/**
|
||
* 打开和关闭RTS
|
||
*
|
||
* @param rts_on true:打开,false:关闭
|
||
*/
|
||
public void setRTS_On(boolean rts_on) {
|
||
try {
|
||
EnumSet<UsbSerialPort.ControlLine> controlLines = usbSerialPort.getSupportedControlLines();
|
||
if (controlLines.contains(UsbSerialPort.ControlLine.RTS)) {
|
||
usbSerialPort.setRTS(rts_on);
|
||
}
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
public void setDTR_On(boolean dtr_on) {
|
||
try {
|
||
EnumSet<UsbSerialPort.ControlLine> controlLines = usbSerialPort.getSupportedControlLines();
|
||
if (controlLines.contains(UsbSerialPort.ControlLine.DTR)) {
|
||
usbSerialPort.setDTR(dtr_on);
|
||
}
|
||
} catch (IOException e) {
|
||
e.printStackTrace();
|
||
Log.d(TAG, "setDTR_On: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
public OnConnectorStateChanged getOnStateChanged() {
|
||
return onStateChanged;
|
||
}
|
||
|
||
public void setOnStateChanged(OnConnectorStateChanged onStateChanged) {
|
||
this.onStateChanged = onStateChanged;
|
||
}
|
||
|
||
public int getVendorId() {
|
||
return vendorId;
|
||
}
|
||
|
||
public void setVendorId(int deviceId) {
|
||
this.vendorId = deviceId;
|
||
}
|
||
|
||
public int getPortNum() {
|
||
return portNum;
|
||
}
|
||
|
||
public void setPortNum(int portNum) {
|
||
this.portNum = portNum;
|
||
}
|
||
|
||
public int getBaudRate() {
|
||
return baudRate;
|
||
}
|
||
|
||
public void setBaudRate(int baudRate) {
|
||
this.baudRate = baudRate;
|
||
}
|
||
|
||
/**
|
||
* 获取本机可用的串口设备串列表
|
||
*
|
||
* @param context context
|
||
* @return 串口设备列表
|
||
*/
|
||
public static ArrayList<SerialPort> listSerialPorts(Context context) {
|
||
ArrayList<SerialPort> serialPorts = new ArrayList<>();
|
||
UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||
|
||
for (UsbDevice device : usbManager.getDeviceList().values()) {
|
||
UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device);
|
||
if (driver == null) {
|
||
continue;
|
||
//试着把未知的设备加入到cdc驱动上
|
||
//driver = new CdcAcmSerialDriver(device);
|
||
}
|
||
for (int i = 0; i < driver.getPorts().size(); i++) {
|
||
serialPorts.add(new SerialPort(device.getDeviceId(), device.getVendorId()
|
||
, device.getProductId(), i));
|
||
}
|
||
}
|
||
return serialPorts;
|
||
}
|
||
|
||
public boolean isConnected() {
|
||
return connected;
|
||
}
|
||
|
||
|
||
public static class SerialPort {
|
||
public int deviceId = 0;
|
||
public int vendorId = 0x0c26;//厂商号
|
||
public int productId = 0;//设备号
|
||
public int portNum = 0;//端口号
|
||
|
||
public SerialPort(int deviceId, int vendorId, int productId, int portNum) {
|
||
this.deviceId = deviceId;
|
||
this.vendorId = vendorId;
|
||
this.productId = productId;
|
||
this.portNum = portNum;
|
||
}
|
||
|
||
@SuppressLint("DefaultLocale")
|
||
@Override
|
||
public String toString() {
|
||
return String.format("SerialPort:deviceId=0x%04X, vendorId=0x%04X, portNum=%d"
|
||
, deviceId, vendorId, portNum);
|
||
}
|
||
|
||
@SuppressLint("DefaultLocale")
|
||
public String information() {
|
||
return String.format("\\0x%04X\\0x%04X\\0x%04X\\0x%d", deviceId, vendorId, productId, portNum);
|
||
}
|
||
}
|
||
}
|