25 changed files with 1539 additions and 665 deletions
-
10app/build.gradle
-
81app/src/main/java/com/baidu/idl/face/main/finance/utils/TestPopWindow.java
-
28app/src/main/java/com/ouxuan/oxface/device/Ox485.java
-
511app/src/main/java/com/ouxuan/oxface/device/OxGpio.java
-
22datalibrary/build.gradle
-
75datalibrary/src/main/java/com/example/datalibrary/db/DBManager.java
-
13facelibrary/build.gradle
-
26financelibrary/build.gradle
-
1lib-serialport/.gitignore
-
50lib-serialport/build.gradle
-
17lib-serialport/proguard-rules.pro
-
9lib-serialport/src/main/AndroidManifest.xml
-
9lib-serialport/src/main/cpp/CMakeLists.txt
-
167lib-serialport/src/main/cpp/SerialPort.c
-
30lib-serialport/src/main/cpp/SerialPort.h
-
48lib-serialport/src/main/java/com/kongqw/serialportlibrary/Device.java
-
54lib-serialport/src/main/java/com/kongqw/serialportlibrary/Driver.java
-
47lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPort.java
-
66lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortFinder.java
-
187lib-serialport/src/main/java/com/kongqw/serialportlibrary/SerialPortManager.java
-
23lib-serialport/src/main/java/com/kongqw/serialportlibrary/listener/OnSerialPortDataListener.java
-
231lib-serialport/src/main/java/com/kongqw/serialportlibrary/thread/SerialPortReadThread.java
-
487lib-serialport/src/main/java/com/kongqw/serialportlibrary/utils/ByteUtils.java
-
3lib-serialport/src/main/res/values/strings.xml
-
3settings.gradle
@ -1,81 +0,0 @@ |
|||||
package com.baidu.idl.face.main.finance.utils; |
|
||||
|
|
||||
import android.content.Context; |
|
||||
import android.util.Log; |
|
||||
import android.view.Gravity; |
|
||||
import android.view.View; |
|
||||
import android.view.ViewGroup; |
|
||||
import android.widget.PopupWindow; |
|
||||
|
|
||||
import com.baidu.idl.main.facesdk.financelibrary.R; |
|
||||
|
|
||||
public class TestPopWindow extends PopupWindow { |
|
||||
private String TAG = "TestPopWindow"; |
|
||||
private final Context gContext; |
|
||||
private View view; |
|
||||
|
|
||||
public TestPopWindow(Context context) { |
|
||||
this(context, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); |
|
||||
Log.d(TAG, "TestPopWindow: " + ViewGroup.LayoutParams.WRAP_CONTENT |
|
||||
+ "bbb:" + ViewGroup.LayoutParams.WRAP_CONTENT); |
|
||||
} |
|
||||
|
|
||||
public TestPopWindow(Context context, int width, int height) { |
|
||||
super(context); |
|
||||
this.gContext = context; |
|
||||
view = View.inflate(context, R.layout.layout_no_face_detected, null); |
|
||||
view.findViewById(R.id.retest_detectBtn).setOnClickListener(new View.OnClickListener() { |
|
||||
@Override |
|
||||
public void onClick(View view) { |
|
||||
mOnClickFinance.rester(true); |
|
||||
} |
|
||||
}); |
|
||||
view.findViewById(R.id.back_homeBtn).setOnClickListener(new View.OnClickListener() { |
|
||||
@Override |
|
||||
public void onClick(View view) { |
|
||||
mOnClickFinance.rester(false); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
setContentView(view); |
|
||||
// 设置窗口的高和宽 |
|
||||
setWidth(width); |
|
||||
setHeight(height); |
|
||||
// 设置弹窗内科点击 |
|
||||
setTouchable(true); |
|
||||
setOutsideTouchable(true); |
|
||||
setFocusable(true); |
|
||||
// TODO去除背景 |
|
||||
setBackgroundDrawable(null); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 显示popupWindow |
|
||||
*/ |
|
||||
public void showPopupWindow(View parent) { |
|
||||
if (!this.isShowing()) { |
|
||||
// 以下拉方式显示popupwindow,调整位置使其不会完全遮挡预览界面 |
|
||||
this.showAtLocation(parent, Gravity.CENTER, 0, -50); |
|
||||
} else { |
|
||||
this.dismiss(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void closePopupWindow() { |
|
||||
if (this.isShowing()) { |
|
||||
this.dismiss(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
public void setmOnClickFinance(OnClickFinance mOnClickFinance) { |
|
||||
this.mOnClickFinance = mOnClickFinance; |
|
||||
} |
|
||||
|
|
||||
public OnClickFinance mOnClickFinance; |
|
||||
|
|
||||
public interface OnClickFinance { |
|
||||
void rester(boolean isReTest); |
|
||||
} |
|
||||
} |
|
@ -1,511 +0,0 @@ |
|||||
package com.ouxuan.oxface.device; |
|
||||
|
|
||||
import android.os.Handler; |
|
||||
import android.os.Looper; |
|
||||
import android.os.SystemClock; |
|
||||
import com.ouxuan.oxface.utils.LogManager; |
|
||||
|
|
||||
import java.io.BufferedReader; |
|
||||
import java.io.BufferedWriter; |
|
||||
import java.io.DataOutputStream; |
|
||||
import java.io.FileReader; |
|
||||
import java.io.FileWriter; |
|
||||
import java.io.IOException; |
|
||||
import java.util.concurrent.CompletableFuture; |
|
||||
import java.util.concurrent.TimeUnit; |
|
||||
|
|
||||
/** |
|
||||
* OxGpio GPIO控制和485通信模块 |
|
||||
* 基于原GpioUtils重写,并集成485串口通信功能 |
|
||||
* |
|
||||
* @author AI Assistant |
|
||||
* @version 1.0 |
|
||||
* @date 2024/09/11 |
|
||||
*/ |
|
||||
public class OxGpio { |
|
||||
|
|
||||
private static final String TAG = "OxGpio"; |
|
||||
|
|
||||
// GPIO引脚定义 - RK3399 |
|
||||
public static final int IO1_3399 = 1066; |
|
||||
public static final int IO2_3399 = 1067; |
|
||||
public static final int IO3_3399 = 1072; |
|
||||
public static final int IO4_3399 = 1071; |
|
||||
|
|
||||
// GPIO引脚定义 - RK3368 |
|
||||
public static final int IO1_3368 = 91; |
|
||||
public static final int IO2_3368 = 90; |
|
||||
public static final int IO3_3368 = 111; |
|
||||
public static final int IO4_3368 = 109; |
|
||||
|
|
||||
// 485通信配置 |
|
||||
private static final String SERIAL_PORT_PATH = "/dev/ttyS6"; |
|
||||
private static final int BAUD_RATE = 9600; |
|
||||
private static final int STOP_BITS = 1; |
|
||||
private static final String HEX_COMMAND_GET_PEOPLE_NUM = "0F030001000294E5"; |
|
||||
private static final long TIMEOUT_485_MILLIS = 10000; // 10秒超时 |
|
||||
|
|
||||
// 单例实例 |
|
||||
private static volatile OxGpio instance; |
|
||||
|
|
||||
// 485通信相关 |
|
||||
private SerialPort485 serialPort485; |
|
||||
private Handler mainHandler; |
|
||||
private boolean gateCamera485OxOn = false; |
|
||||
|
|
||||
// GPIO读取失败计数 |
|
||||
private static long mTime = 0; |
|
||||
private static int mFailTimes = 0; |
|
||||
|
|
||||
/** |
|
||||
* 485通信回调接口 |
|
||||
*/ |
|
||||
public interface PeopleNumCallback { |
|
||||
void onSuccess(int peopleNum); |
|
||||
void onError(String errorMessage); |
|
||||
} |
|
||||
|
|
||||
private OxGpio() { |
|
||||
mainHandler = new Handler(Looper.getMainLooper()); |
|
||||
// 初始化SerialPort485实例 |
|
||||
serialPort485 = SerialPort485.getInstance(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 获取OxGpio单例实例 |
|
||||
* @return OxGpio实例 |
|
||||
*/ |
|
||||
public static OxGpio getInstance() { |
|
||||
if (instance == null) { |
|
||||
synchronized (OxGpio.class) { |
|
||||
if (instance == null) { |
|
||||
instance = new OxGpio(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return instance; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 初始化OxGpio模块 |
|
||||
* @param context 上下文 |
|
||||
*/ |
|
||||
public void initialize(android.content.Context context) { |
|
||||
// 初始化SerialPort485 |
|
||||
serialPort485.initialize(context); |
|
||||
LogManager.logInfo(TAG, "OxGpio模块初始化完成"); |
|
||||
} |
|
||||
|
|
||||
// ==================== GPIO操作方法 ==================== |
|
||||
|
|
||||
/** |
|
||||
* 给export申请权限 |
|
||||
*/ |
|
||||
public static boolean upgradeRootPermissionForExport() { |
|
||||
LogManager.logInfo(TAG, "申请GPIO export权限"); |
|
||||
return upgradeRootPermission("/sys/class/gpio/export"); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 配置一个gpio路径 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
public static boolean exportGpio(int gpio) { |
|
||||
LogManager.logInfo(TAG, "导出GPIO: " + gpio); |
|
||||
return writeNode("/sys/class/gpio/export", String.valueOf(gpio)); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 给获取io口的状态的路径申请权限,该方法需要在exportGpio后调用 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
*/ |
|
||||
public static boolean upgradeRootPermissionForGpio(int gpio) { |
|
||||
LogManager.logInfo(TAG, "申请GPIO权限: " + gpio); |
|
||||
boolean directionResult = upgradeRootPermission("/sys/class/gpio/gpio" + gpio + "/direction"); |
|
||||
boolean valueResult = upgradeRootPermission("/sys/class/gpio/gpio" + gpio + "/value"); |
|
||||
return directionResult && valueResult; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 设置io口为输入或输出 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @param direction 0=输出(out), 1=输入(in) |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
public static boolean setGpioDirection(int gpio, int direction) { |
|
||||
String gpioDirection; |
|
||||
if (direction == 0) { |
|
||||
gpioDirection = "out"; |
|
||||
} else if (direction == 1) { |
|
||||
gpioDirection = "in"; |
|
||||
} else { |
|
||||
LogManager.logError(TAG, "无效的GPIO方向: " + direction); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
LogManager.logInfo(TAG, "设置GPIO方向 - 引脚: " + gpio + ", 方向: " + gpioDirection); |
|
||||
return writeNode("/sys/class/gpio/gpio" + gpio + "/direction", gpioDirection); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 获取io口的状态为输出还是输入 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @return "in" 或 "out" |
|
||||
*/ |
|
||||
public static String getGpioDirection(int gpio) { |
|
||||
String direction = readNode("/sys/class/gpio/gpio" + gpio + "/direction"); |
|
||||
LogManager.logDebug(TAG, "获取GPIO方向 - 引脚: " + gpio + ", 方向: " + direction); |
|
||||
return direction; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 给输出io口写值,高电平或低电平 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @param value "1"=高电平, "0"=低电平 |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
public static boolean writeGpioValue(int gpio, String value) { |
|
||||
LogManager.logInfo(TAG, "写入GPIO值 - 引脚: " + gpio + ", 值: " + value); |
|
||||
return writeNode("/sys/class/gpio/gpio" + gpio + "/value", value); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 获取当前gpio是高还是低 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @return "1"=高电平, "0"=低电平 |
|
||||
*/ |
|
||||
public static String getGpioValue(int gpio) { |
|
||||
String value = readNode("/sys/class/gpio/gpio" + gpio + "/value"); |
|
||||
LogManager.logDebug(TAG, "获取GPIO值 - 引脚: " + gpio + ", 值: " + value); |
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
// ==================== 485通信方法(转换自uniapp) ==================== |
|
||||
|
|
||||
/** |
|
||||
* 设置485模式开关 |
|
||||
* @param enabled 是否启用485模式 |
|
||||
*/ |
|
||||
public void setGateCamera485OxOn(boolean enabled) { |
|
||||
this.gateCamera485OxOn = enabled; |
|
||||
serialPort485.setGateCamera485OxOn(enabled); |
|
||||
LogManager.logInfo(TAG, "设置485模式开关: " + enabled); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 获取485模式开关状态 |
|
||||
* @return true表示485模式已启用 |
|
||||
*/ |
|
||||
public boolean isGateCamera485OxOn() { |
|
||||
return gateCamera485OxOn; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 通过485获取摄像头人数 |
|
||||
* 对应uniapp中的sendHex485ForPeopleNum方法 |
|
||||
* @param callback 结果回调 |
|
||||
*/ |
|
||||
public void sendHex485ForPeopleNum(PeopleNumCallback callback) { |
|
||||
LogManager.logInfo(TAG, "开始通过485获取摄像头人数"); |
|
||||
|
|
||||
if (!gateCamera485OxOn) { |
|
||||
String errorMsg = "485模式未开启"; |
|
||||
LogManager.logError(TAG, errorMsg); |
|
||||
if (callback != null) { |
|
||||
mainHandler.post(() -> callback.onError(errorMsg)); |
|
||||
} |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 使用SerialPort485进行通信 |
|
||||
serialPort485.sendHex485ForPeopleNum(new SerialPort485.SerialPort485Callback() { |
|
||||
@Override |
|
||||
public void onSuccess(int peopleNum) { |
|
||||
LogManager.logInfo(TAG, "485获取人数成功: " + peopleNum); |
|
||||
if (callback != null) { |
|
||||
mainHandler.post(() -> callback.onSuccess(peopleNum)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onError(String errorMessage) { |
|
||||
LogManager.logError(TAG, "485获取人数失败: " + errorMessage); |
|
||||
if (callback != null) { |
|
||||
mainHandler.post(() -> callback.onError(errorMessage)); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 异步获取摄像头人数(使用CompletableFuture) |
|
||||
* @return CompletableFuture<Integer> 人数结果 |
|
||||
*/ |
|
||||
public CompletableFuture<Integer> sendHex485ForPeopleNumAsync() { |
|
||||
LogManager.logInfo(TAG, "异步获取摄像头人数"); |
|
||||
|
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>(); |
|
||||
|
|
||||
sendHex485ForPeopleNum(new PeopleNumCallback() { |
|
||||
@Override |
|
||||
public void onSuccess(int peopleNum) { |
|
||||
future.complete(peopleNum); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onError(String errorMessage) { |
|
||||
future.completeExceptionally(new RuntimeException(errorMessage)); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
return future; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 检查是否需要打开485串口 |
|
||||
*/ |
|
||||
public void checkIsNeedOpen485() { |
|
||||
LogManager.logInfo(TAG, "检查是否需要打开485串口"); |
|
||||
serialPort485.checkIsNeedOpen485(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 发送自定义485 HEX命令 |
|
||||
* @param hexCommand HEX命令字符串 |
|
||||
*/ |
|
||||
public void send485HexCommand(String hexCommand) { |
|
||||
LogManager.logInfo(TAG, "发送485 HEX命令: " + hexCommand); |
|
||||
serialPort485.sendHex(hexCommand); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 获取485串口状态 |
|
||||
* @return 串口状态描述 |
|
||||
*/ |
|
||||
public String get485Status() { |
|
||||
return serialPort485.getSerialPortStatus(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 关闭485串口 |
|
||||
*/ |
|
||||
public void close485SerialPort() { |
|
||||
LogManager.logInfo(TAG, "关闭485串口"); |
|
||||
serialPort485.closeSerialPort(); |
|
||||
} |
|
||||
|
|
||||
// ==================== 内部辅助方法 ==================== |
|
||||
|
|
||||
/** |
|
||||
* 申请root权限 |
|
||||
* @param path 路径 |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
private static boolean upgradeRootPermission(String path) { |
|
||||
LogManager.logDebug(TAG, "申请root权限: " + path); |
|
||||
Process process = null; |
|
||||
DataOutputStream os = null; |
|
||||
try { |
|
||||
String cmd = "chmod 777 " + path; |
|
||||
process = Runtime.getRuntime().exec("su"); // 切换到root账号 |
|
||||
os = new DataOutputStream(process.getOutputStream()); |
|
||||
os.writeBytes(cmd + "\n"); |
|
||||
os.writeBytes("exit\n"); |
|
||||
os.flush(); |
|
||||
process.waitFor(); |
|
||||
} catch (Exception e) { |
|
||||
LogManager.logError(TAG, "申请root权限异常: " + path, e); |
|
||||
return false; |
|
||||
} finally { |
|
||||
try { |
|
||||
if (os != null) { |
|
||||
os.close(); |
|
||||
} |
|
||||
if (process != null) { |
|
||||
process.destroy(); |
|
||||
} |
|
||||
} catch (Exception e) { |
|
||||
LogManager.logError(TAG, "关闭进程异常", e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
boolean result = process != null && process.waitFor() == 0; |
|
||||
LogManager.logInfo(TAG, "申请root权限结果: " + result + " - " + path); |
|
||||
return result; |
|
||||
} catch (InterruptedException e) { |
|
||||
LogManager.logError(TAG, "等待进程中断", e); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 写入节点 |
|
||||
* @param path 节点路径 |
|
||||
* @param value 写入值 |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
private static boolean writeNode(String path, String value) { |
|
||||
LogManager.logDebug(TAG, "写入节点 - 路径: " + path + ", 值: " + value); |
|
||||
|
|
||||
if (path == null || value == null) { |
|
||||
LogManager.logError(TAG, "写入节点参数错误 - 路径或值为空"); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
FileWriter fileWriter = null; |
|
||||
try { |
|
||||
fileWriter = new FileWriter(path); |
|
||||
fileWriter.write(value); |
|
||||
LogManager.logDebug(TAG, "节点写入成功: " + path); |
|
||||
return true; |
|
||||
} catch (Exception e) { |
|
||||
LogManager.logError(TAG, "写入节点异常 - 路径: " + path + ", 值: " + value, e); |
|
||||
return false; |
|
||||
} finally { |
|
||||
try { |
|
||||
if (fileWriter != null) { |
|
||||
fileWriter.close(); |
|
||||
} |
|
||||
} catch (IOException e) { |
|
||||
LogManager.logError(TAG, "关闭FileWriter异常", e); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 读取节点 |
|
||||
* @param path 节点路径 |
|
||||
* @return 读取的值 |
|
||||
*/ |
|
||||
private static String readNode(String path) { |
|
||||
LogManager.logDebug(TAG, "读取节点: " + path); |
|
||||
String result = ""; |
|
||||
|
|
||||
FileReader fread = null; |
|
||||
BufferedReader buffer = null; |
|
||||
|
|
||||
try { |
|
||||
fread = new FileReader(path); |
|
||||
buffer = new BufferedReader(fread); |
|
||||
String str; |
|
||||
while ((str = buffer.readLine()) != null) { |
|
||||
result = str; |
|
||||
break; |
|
||||
} |
|
||||
mFailTimes = 0; |
|
||||
LogManager.logDebug(TAG, "节点读取成功: " + path + " = " + result); |
|
||||
} catch (IOException e) { |
|
||||
LogManager.logError(TAG, "读取节点IO异常: " + path, e); |
|
||||
if (mTime == 0 || SystemClock.uptimeMillis() - mTime < 1000) { |
|
||||
mFailTimes++; |
|
||||
} |
|
||||
if (mFailTimes >= 3) { |
|
||||
LogManager.logWarning(TAG, "读取节点连续失败3次,可能需要检查权限: " + path); |
|
||||
} |
|
||||
} finally { |
|
||||
try { |
|
||||
if (buffer != null) { |
|
||||
buffer.close(); |
|
||||
} |
|
||||
if (fread != null) { |
|
||||
fread.close(); |
|
||||
} |
|
||||
} catch (IOException e) { |
|
||||
LogManager.logError(TAG, "关闭文件流异常", e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
// ==================== 便捷方法 ==================== |
|
||||
|
|
||||
/** |
|
||||
* 初始化GPIO引脚(包含导出、申请权限、设置方向) |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @param direction 0=输出, 1=输入 |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
public static boolean initializeGpio(int gpio, int direction) { |
|
||||
LogManager.logInfo(TAG, "初始化GPIO - 引脚: " + gpio + ", 方向: " + direction); |
|
||||
|
|
||||
// 1. 申请export权限 |
|
||||
if (!upgradeRootPermissionForExport()) { |
|
||||
LogManager.logError(TAG, "申请export权限失败"); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
// 2. 导出GPIO |
|
||||
if (!exportGpio(gpio)) { |
|
||||
LogManager.logError(TAG, "导出GPIO失败: " + gpio); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
// 3. 申请GPIO权限 |
|
||||
if (!upgradeRootPermissionForGpio(gpio)) { |
|
||||
LogManager.logError(TAG, "申请GPIO权限失败: " + gpio); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
// 4. 设置GPIO方向 |
|
||||
if (!setGpioDirection(gpio, direction)) { |
|
||||
LogManager.logError(TAG, "设置GPIO方向失败: " + gpio); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
LogManager.logInfo(TAG, "GPIO初始化成功: " + gpio); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 设置GPIO输出高电平 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
public static boolean setGpioHigh(int gpio) { |
|
||||
LogManager.logInfo(TAG, "设置GPIO高电平: " + gpio); |
|
||||
return writeGpioValue(gpio, "1"); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 设置GPIO输出低电平 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @return 是否成功 |
|
||||
*/ |
|
||||
public static boolean setGpioLow(int gpio) { |
|
||||
LogManager.logInfo(TAG, "设置GPIO低电平: " + gpio); |
|
||||
return writeGpioValue(gpio, "0"); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 检查GPIO是否为高电平 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @return true表示高电平 |
|
||||
*/ |
|
||||
public static boolean isGpioHigh(int gpio) { |
|
||||
String value = getGpioValue(gpio); |
|
||||
return "1".equals(value); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 检查GPIO是否为低电平 |
|
||||
* @param gpio GPIO引脚号 |
|
||||
* @return true表示低电平 |
|
||||
*/ |
|
||||
public static boolean isGpioLow(int gpio) { |
|
||||
String value = getGpioValue(gpio); |
|
||||
return "0".equals(value); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 释放资源 |
|
||||
*/ |
|
||||
public void release() { |
|
||||
LogManager.logInfo(TAG, "释放OxGpio资源"); |
|
||||
if (serialPort485 != null) { |
|
||||
serialPort485.release(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1 @@ |
|||||
|
/build |
@ -0,0 +1,50 @@ |
|||||
|
apply plugin: 'com.android.library' |
||||
|
|
||||
|
android { |
||||
|
namespace 'com.kongqw.serialportlibrary' |
||||
|
compileSdk 35 |
||||
|
buildToolsVersion "35.0.0" |
||||
|
|
||||
|
defaultConfig { |
||||
|
minSdkVersion 21 |
||||
|
targetSdkVersion 35 |
||||
|
versionCode 1 |
||||
|
versionName "1.0" |
||||
|
|
||||
|
consumerProguardFiles "consumer-rules.pro" |
||||
|
|
||||
|
externalNativeBuild { |
||||
|
cmake { |
||||
|
cppFlags "" |
||||
|
abiFilters "armeabi-v7a", "arm64-v8a" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
externalNativeBuild { |
||||
|
cmake { |
||||
|
path "src/main/cpp/CMakeLists.txt" |
||||
|
version "3.31.6" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
buildTypes { |
||||
|
release { |
||||
|
minifyEnabled false |
||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
||||
|
} |
||||
|
debug { |
||||
|
debuggable true |
||||
|
jniDebuggable true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
compileOptions { |
||||
|
sourceCompatibility JavaVersion.VERSION_1_8 |
||||
|
targetCompatibility JavaVersion.VERSION_1_8 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
implementation 'androidx.annotation:annotation:1.7.0' |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
# Add project specific ProGuard rules here. |
||||
|
# By default, the flags in this file are appended to flags specified |
||||
|
# in D:\Android\sdk/tools/proguard/proguard-android.txt |
||||
|
# You can edit the include path and order by changing the proguardFiles |
||||
|
# directive in build.gradle. |
||||
|
# |
||||
|
# For more details, see |
||||
|
# http://developer.android.com/guide/developing/tools/proguard.html |
||||
|
|
||||
|
# Add any project specific keep options here: |
||||
|
|
||||
|
# If your project uses WebView with JS, uncomment the following |
||||
|
# and specify the fully qualified class name to the JavaScript interface |
||||
|
# class: |
||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||
|
# public *; |
||||
|
#} |
@ -0,0 +1,9 @@ |
|||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
package="com.kongqw.serialportlibrary"> |
||||
|
|
||||
|
<application android:allowBackup="false" android:label="@string/app_name" |
||||
|
android:supportsRtl="true"> |
||||
|
|
||||
|
</application> |
||||
|
|
||||
|
</manifest> |
@ -0,0 +1,9 @@ |
|||||
|
cmake_minimum_required(VERSION 3.4.1) |
||||
|
|
||||
|
add_library(SerialPort SHARED |
||||
|
SerialPort.c) |
||||
|
|
||||
|
# Include libraries needed for libserial_port lib |
||||
|
target_link_libraries(SerialPort |
||||
|
android |
||||
|
log) |
@ -0,0 +1,167 @@ |
|||||
|
/* |
||||
|
* Copyright 2009-2011 Cedric Priscal |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
#include <termios.h> |
||||
|
#include <unistd.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/stat.h> |
||||
|
#include <fcntl.h> |
||||
|
#include <string.h> |
||||
|
#include <jni.h> |
||||
|
|
||||
|
#include "SerialPort.h" |
||||
|
|
||||
|
#include "android/log.h" |
||||
|
static const char *TAG="serial_port"; |
||||
|
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) |
||||
|
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) |
||||
|
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) |
||||
|
|
||||
|
static speed_t getBaudrate(jint baudrate) |
||||
|
{ |
||||
|
switch(baudrate) { |
||||
|
case 0: return B0; |
||||
|
case 50: return B50; |
||||
|
case 75: return B75; |
||||
|
case 110: return B110; |
||||
|
case 134: return B134; |
||||
|
case 150: return B150; |
||||
|
case 200: return B200; |
||||
|
case 300: return B300; |
||||
|
case 600: return B600; |
||||
|
case 1200: return B1200; |
||||
|
case 1800: return B1800; |
||||
|
case 2400: return B2400; |
||||
|
case 4800: return B4800; |
||||
|
case 9600: return B9600; |
||||
|
case 19200: return B19200; |
||||
|
case 38400: return B38400; |
||||
|
case 57600: return B57600; |
||||
|
case 115200: return B115200; |
||||
|
case 230400: return B230400; |
||||
|
case 460800: return B460800; |
||||
|
case 500000: return B500000; |
||||
|
case 576000: return B576000; |
||||
|
case 921600: return B921600; |
||||
|
case 1000000: return B1000000; |
||||
|
case 1152000: return B1152000; |
||||
|
case 1500000: return B1500000; |
||||
|
case 2000000: return B2000000; |
||||
|
case 2500000: return B2500000; |
||||
|
case 3000000: return B3000000; |
||||
|
case 3500000: return B3500000; |
||||
|
case 4000000: return B4000000; |
||||
|
default: return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Class: android_serialport_SerialPort |
||||
|
* Method: open |
||||
|
* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; |
||||
|
*/ |
||||
|
JNIEXPORT jobject JNICALL Java_com_kongqw_serialportlibrary_SerialPort_open |
||||
|
(JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) |
||||
|
{ |
||||
|
int fd; |
||||
|
speed_t speed; |
||||
|
jobject mFileDescriptor; |
||||
|
|
||||
|
/* Check arguments */ |
||||
|
{ |
||||
|
speed = getBaudrate(baudrate); |
||||
|
if (speed == -1) { |
||||
|
/* TODO: throw an exception */ |
||||
|
LOGE("Invalid baudrate"); |
||||
|
return NULL; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Opening device */ |
||||
|
{ |
||||
|
jboolean iscopy; |
||||
|
const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); |
||||
|
LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); |
||||
|
fd = open(path_utf, O_RDWR | flags); |
||||
|
LOGD("open() fd = %d", fd); |
||||
|
(*env)->ReleaseStringUTFChars(env, path, path_utf); |
||||
|
if (fd == -1) |
||||
|
{ |
||||
|
/* Throw an exception */ |
||||
|
LOGE("Cannot open port"); |
||||
|
/* TODO: throw an exception */ |
||||
|
return NULL; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Configure device */ |
||||
|
{ |
||||
|
struct termios cfg; |
||||
|
LOGD("Configuring serial port"); |
||||
|
if (tcgetattr(fd, &cfg)) |
||||
|
{ |
||||
|
LOGE("tcgetattr() failed"); |
||||
|
close(fd); |
||||
|
/* TODO: throw an exception */ |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
cfmakeraw(&cfg); |
||||
|
cfsetispeed(&cfg, speed); |
||||
|
cfsetospeed(&cfg, speed); |
||||
|
|
||||
|
if (tcsetattr(fd, TCSANOW, &cfg)) |
||||
|
{ |
||||
|
LOGE("tcsetattr() failed"); |
||||
|
close(fd); |
||||
|
/* TODO: throw an exception */ |
||||
|
return NULL; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Create a corresponding file descriptor */ |
||||
|
{ |
||||
|
jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); |
||||
|
jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V"); |
||||
|
jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); |
||||
|
mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); |
||||
|
(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); |
||||
|
} |
||||
|
|
||||
|
return mFileDescriptor; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Class: cedric_serial_SerialPort |
||||
|
* Method: close |
||||
|
* Signature: ()V |
||||
|
*/ |
||||
|
JNIEXPORT void JNICALL Java_com_kongqw_serialportlibrary_SerialPort_close |
||||
|
(JNIEnv *env, jobject thiz) |
||||
|
{ |
||||
|
jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); |
||||
|
jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); |
||||
|
|
||||
|
jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); |
||||
|
jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); |
||||
|
|
||||
|
jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); |
||||
|
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); |
||||
|
|
||||
|
LOGD("close(fd = %d)", descriptor); |
||||
|
close(descriptor); |
||||
|
} |
||||
|
|
@ -0,0 +1,30 @@ |
|||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */ |
||||
|
#include <jni.h> |
||||
|
/* Header for class android_serialport_api_SerialPort */ |
||||
|
|
||||
|
#ifndef _Included_qingwei_kong_serialportlibrary_SerialPort |
||||
|
#define _Included_qingwei_kong_serialportlibrary_SerialPort |
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
/* |
||||
|
* Class: android_serialport_api_SerialPort |
||||
|
* Method: open |
||||
|
* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; |
||||
|
*/ |
||||
|
JNIEXPORT jobject JNICALL Java_com_kongqw_serialportlibrary_SerialPort_open |
||||
|
(JNIEnv *, jclass, jstring, jint, jint); |
||||
|
|
||||
|
/* |
||||
|
* Class: android_serialport_api_SerialPort |
||||
|
* Method: close |
||||
|
* Signature: ()V |
||||
|
*/ |
||||
|
JNIEXPORT void JNICALL Java_com_kongqw_serialportlibrary_SerialPort_close |
||||
|
(JNIEnv *, jobject); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif |
||||
|
#endif |
@ -0,0 +1,48 @@ |
|||||
|
package com.kongqw.serialportlibrary; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.Serializable; |
||||
|
|
||||
|
/** |
||||
|
* Created by Kongqw on 2017/11/13. |
||||
|
* Device |
||||
|
*/ |
||||
|
|
||||
|
public class Device implements Serializable{ |
||||
|
|
||||
|
private static final String TAG = Device.class.getSimpleName(); |
||||
|
|
||||
|
private String name; |
||||
|
private String root; |
||||
|
private File file; |
||||
|
|
||||
|
public Device(String name, String root, File file) { |
||||
|
this.name = name; |
||||
|
this.root = root; |
||||
|
this.file = file; |
||||
|
} |
||||
|
|
||||
|
public String getName() { |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
public void setName(String name) { |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public String getRoot() { |
||||
|
return root; |
||||
|
} |
||||
|
|
||||
|
public void setRoot(String root) { |
||||
|
this.root = root; |
||||
|
} |
||||
|
|
||||
|
public File getFile() { |
||||
|
return file; |
||||
|
} |
||||
|
|
||||
|
public void setFile(File path) { |
||||
|
this.file = file; |
||||
|
} |
||||
|
} |
@ -0,0 +1,54 @@ |
|||||
|
package com.kongqw.serialportlibrary; |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
/** |
||||
|
* Created by Kongqw on 2017/11/13. |
||||
|
* Driver |
||||
|
*/ |
||||
|
|
||||
|
public class Driver { |
||||
|
|
||||
|
private static final String TAG = Driver.class.getSimpleName(); |
||||
|
|
||||
|
private String mDriverName; |
||||
|
private String mDeviceRoot; |
||||
|
|
||||
|
public Driver(String name, String root) { |
||||
|
mDriverName = name; |
||||
|
mDeviceRoot = root; |
||||
|
} |
||||
|
|
||||
|
public ArrayList<File> getDevices() { |
||||
|
ArrayList<File> devices = new ArrayList<>(); |
||||
|
File dev = new File("/dev"); |
||||
|
|
||||
|
if (!dev.exists()) { |
||||
|
Log.i(TAG, "getDevices: " + dev.getAbsolutePath() + " 不存在"); |
||||
|
return devices; |
||||
|
} |
||||
|
if (!dev.canRead()) { |
||||
|
Log.i(TAG, "getDevices: " + dev.getAbsolutePath() + " 没有读取权限"); |
||||
|
return devices; |
||||
|
} |
||||
|
|
||||
|
File[] files = dev.listFiles(); |
||||
|
|
||||
|
int i; |
||||
|
for (i = 0; i < files.length; i++) { |
||||
|
if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { |
||||
|
Log.d(TAG, "Found new device: " + files[i]); |
||||
|
devices.add(files[i]); |
||||
|
} |
||||
|
} |
||||
|
return devices; |
||||
|
} |
||||
|
|
||||
|
public String getName() { |
||||
|
return mDriverName; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
package com.kongqw.serialportlibrary; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileDescriptor; |
||||
|
import java.io.IOException; |
||||
|
|
||||
|
public class SerialPort { |
||||
|
|
||||
|
static { |
||||
|
System.loadLibrary("SerialPort"); |
||||
|
} |
||||
|
|
||||
|
private static final String TAG = SerialPort.class.getSimpleName(); |
||||
|
|
||||
|
/** |
||||
|
* 文件设置最高权限 777 可读 可写 可执行 |
||||
|
* |
||||
|
* @param file 文件 |
||||
|
* @return 权限修改是否成功 |
||||
|
*/ |
||||
|
boolean chmod777(File file) { |
||||
|
if (null == file || !file.exists()) { |
||||
|
// 文件不存在 |
||||
|
return false; |
||||
|
} |
||||
|
try { |
||||
|
// 获取ROOT权限 |
||||
|
Process su = Runtime.getRuntime().exec("/system/bin/su"); |
||||
|
// 修改文件属性为 [可读 可写 可执行] |
||||
|
String cmd = "chmod 777 " + file.getAbsolutePath() + "\n" + "exit\n"; |
||||
|
su.getOutputStream().write(cmd.getBytes()); |
||||
|
if (0 == su.waitFor() && file.canRead() && file.canWrite() && file.canExecute()) { |
||||
|
return true; |
||||
|
} |
||||
|
} catch (IOException | InterruptedException e) { |
||||
|
// 没有ROOT权限 |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 打开串口 |
||||
|
protected native FileDescriptor open(String path, int baudRate, int flags); |
||||
|
|
||||
|
// 关闭串口 |
||||
|
protected native void close(); |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
package com.kongqw.serialportlibrary; |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileReader; |
||||
|
import java.io.IOException; |
||||
|
import java.io.LineNumberReader; |
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
public class SerialPortFinder { |
||||
|
|
||||
|
private static final String TAG = SerialPortFinder.class.getSimpleName(); |
||||
|
private static final String DRIVERS_PATH = "/proc/tty/drivers"; |
||||
|
private static final String SERIAL_FIELD = "serial"; |
||||
|
|
||||
|
public SerialPortFinder() { |
||||
|
File file = new File(DRIVERS_PATH); |
||||
|
boolean b = file.canRead(); |
||||
|
Log.i(TAG, "SerialPortFinder: file.canRead() = " + b); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 Drivers |
||||
|
* |
||||
|
* @return Drivers |
||||
|
* @throws IOException IOException |
||||
|
*/ |
||||
|
private ArrayList<Driver> getDrivers() throws IOException { |
||||
|
ArrayList<Driver> drivers = new ArrayList<>(); |
||||
|
LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(DRIVERS_PATH)); |
||||
|
String readLine; |
||||
|
while ((readLine = lineNumberReader.readLine()) != null) { |
||||
|
String driverName = readLine.substring(0, 0x15).trim(); |
||||
|
String[] fields = readLine.split(" +"); |
||||
|
if ((fields.length >= 5) && (fields[fields.length - 1].equals(SERIAL_FIELD))) { |
||||
|
Log.d(TAG, "Found new driver " + driverName + " on " + fields[fields.length - 4]); |
||||
|
drivers.add(new Driver(driverName, fields[fields.length - 4])); |
||||
|
} |
||||
|
} |
||||
|
return drivers; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取串口 |
||||
|
* |
||||
|
* @return 串口 |
||||
|
*/ |
||||
|
public ArrayList<Device> getDevices() { |
||||
|
ArrayList<Device> devices = new ArrayList<>(); |
||||
|
try { |
||||
|
ArrayList<Driver> drivers = getDrivers(); |
||||
|
for (Driver driver : drivers) { |
||||
|
String driverName = driver.getName(); |
||||
|
ArrayList<File> driverDevices = driver.getDevices(); |
||||
|
for (File file : driverDevices) { |
||||
|
String devicesName = file.getName(); |
||||
|
devices.add(new Device(devicesName, driverName, file)); |
||||
|
} |
||||
|
} |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
return devices; |
||||
|
} |
||||
|
} |
@ -0,0 +1,187 @@ |
|||||
|
package com.kongqw.serialportlibrary; |
||||
|
|
||||
|
import android.os.Handler; |
||||
|
import android.os.HandlerThread; |
||||
|
import android.os.Message; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.kongqw.serialportlibrary.listener.OnSerialPortDataListener; |
||||
|
import com.kongqw.serialportlibrary.thread.SerialPortReadThread; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileDescriptor; |
||||
|
import java.io.FileInputStream; |
||||
|
import java.io.FileOutputStream; |
||||
|
import java.io.IOException; |
||||
|
|
||||
|
/** |
||||
|
* Created by Kongqw on 2017/11/13. |
||||
|
* SerialPortManager |
||||
|
*/ |
||||
|
|
||||
|
public class SerialPortManager extends SerialPort { |
||||
|
private static final String TAG = SerialPortManager.class.getSimpleName(); |
||||
|
private FileInputStream mFileInputStream; |
||||
|
private FileOutputStream mFileOutputStream; |
||||
|
private FileDescriptor mFd; |
||||
|
private OnSerialPortDataListener mOnSerialPortDataListener; |
||||
|
|
||||
|
private HandlerThread mSendingHandlerThread; |
||||
|
private Handler mSendingHandler; |
||||
|
private SerialPortReadThread mSerialPortReadThread; |
||||
|
|
||||
|
/** |
||||
|
* 打开串口 |
||||
|
* |
||||
|
* @param device 串口设备 |
||||
|
* @param baudRate 波特率 |
||||
|
* @return 打开是否成功 |
||||
|
*/ |
||||
|
public boolean openSerialPort(File device, int baudRate) { |
||||
|
Log.i(TAG, "openSerialPort: " + String.format("打开串口 %s 波特率 %s", device.getPath(), baudRate)); |
||||
|
// 校验串口权限 |
||||
|
if (!device.canRead() || !device.canWrite()) { |
||||
|
boolean chmod777 = chmod777(device); |
||||
|
if (!chmod777) { |
||||
|
Log.i(TAG, "openSerialPort: 没有读写权限"); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
mFd = open(device.getAbsolutePath(), baudRate, 0); |
||||
|
mFileInputStream = new FileInputStream(mFd); |
||||
|
mFileOutputStream = new FileOutputStream(mFd); |
||||
|
Log.i(TAG, "openSerialPort: 串口已经打开 " + mFd); |
||||
|
return true; |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 关闭串口 |
||||
|
*/ |
||||
|
public void closeSerialPort() { |
||||
|
if (null != mFd) { |
||||
|
close(); |
||||
|
mFd = null; |
||||
|
} |
||||
|
// 停止发送消息的线程 |
||||
|
stopSendThread(); |
||||
|
// 停止接收消息的线程 |
||||
|
stopReadThread(); |
||||
|
|
||||
|
if (null != mFileInputStream) { |
||||
|
try { |
||||
|
mFileInputStream.close(); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
mFileInputStream = null; |
||||
|
} |
||||
|
|
||||
|
if (null != mFileOutputStream) { |
||||
|
try { |
||||
|
mFileOutputStream.close(); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
mFileOutputStream = null; |
||||
|
} |
||||
|
|
||||
|
mOnSerialPortDataListener = null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加数据通信监听 |
||||
|
* |
||||
|
* @param listener listener |
||||
|
* @return SerialPortManager |
||||
|
*/ |
||||
|
public SerialPortManager setOnSerialPortDataListener(OnSerialPortDataListener listener) { |
||||
|
mOnSerialPortDataListener = listener; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开启发送消息的线程 |
||||
|
*/ |
||||
|
public void startSendThread() { |
||||
|
// 开启发送消息的线程 |
||||
|
mSendingHandlerThread = new HandlerThread("mSendingHandlerThread"); |
||||
|
mSendingHandlerThread.start(); |
||||
|
// Handler |
||||
|
mSendingHandler = new Handler(mSendingHandlerThread.getLooper()) { |
||||
|
@Override |
||||
|
public void handleMessage(Message msg) { |
||||
|
byte[] sendBytes = (byte[]) msg.obj; |
||||
|
|
||||
|
if (null != mFileOutputStream && null != sendBytes && 0 < sendBytes.length) { |
||||
|
try { |
||||
|
mFileOutputStream.write(sendBytes); |
||||
|
if (null != mOnSerialPortDataListener) { |
||||
|
mOnSerialPortDataListener.onDataSent(sendBytes); |
||||
|
} |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止发送消息线程 |
||||
|
*/ |
||||
|
public void stopSendThread() { |
||||
|
mSendingHandler = null; |
||||
|
if (null != mSendingHandlerThread) { |
||||
|
mSendingHandlerThread.interrupt(); |
||||
|
mSendingHandlerThread.quit(); |
||||
|
mSendingHandlerThread = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 开启接收消息的线程 |
||||
|
*/ |
||||
|
public void startReadThread(int type) { |
||||
|
mSerialPortReadThread = new SerialPortReadThread(mFileInputStream, type) { |
||||
|
@Override |
||||
|
public void onDataReceived(byte[] bytes) { |
||||
|
if (null != mOnSerialPortDataListener) { |
||||
|
mOnSerialPortDataListener.onDataReceived(bytes); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
mSerialPortReadThread.startRead(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止接收消息的线程 |
||||
|
*/ |
||||
|
public void stopReadThread() { |
||||
|
if (null != mSerialPortReadThread) { |
||||
|
mSerialPortReadThread.stopRead(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送数据 |
||||
|
* |
||||
|
* @param sendBytes 发送数据 |
||||
|
* @return 发送是否成功 |
||||
|
*/ |
||||
|
public boolean sendBytes(byte[] sendBytes) { |
||||
|
if (null != mFd && null != mFileInputStream && null != mFileOutputStream) { |
||||
|
if (null != mSendingHandler) { |
||||
|
Message message = Message.obtain(); |
||||
|
message.obj = sendBytes; |
||||
|
return mSendingHandler.sendMessage(message); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
package com.kongqw.serialportlibrary.listener; |
||||
|
|
||||
|
/** |
||||
|
* Created by Kongqw on 2017/11/14. |
||||
|
* 串口消息监听 |
||||
|
*/ |
||||
|
|
||||
|
public interface OnSerialPortDataListener { |
||||
|
|
||||
|
/** |
||||
|
* 数据接收 |
||||
|
* |
||||
|
* @param bytes 接收到的数据 |
||||
|
*/ |
||||
|
void onDataReceived(byte[] bytes); |
||||
|
|
||||
|
/** |
||||
|
* 数据发送 |
||||
|
* |
||||
|
* @param bytes 发送的数据 |
||||
|
*/ |
||||
|
void onDataSent(byte[] bytes); |
||||
|
} |
@ -0,0 +1,231 @@ |
|||||
|
package com.kongqw.serialportlibrary.thread; |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.kongqw.serialportlibrary.utils.ByteUtils; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
|
||||
|
/** |
||||
|
* Created by Kongqw on 2017/11/14. |
||||
|
* 串口消息读取线程 |
||||
|
*/ |
||||
|
|
||||
|
public abstract class SerialPortReadThread extends Thread { |
||||
|
public abstract void onDataReceived(byte[] bytes); |
||||
|
|
||||
|
private InputStream mInputStream; |
||||
|
private final byte[] mReadBuffer; |
||||
|
public boolean isStopThread = false; |
||||
|
private int type = 0; |
||||
|
|
||||
|
public SerialPortReadThread(InputStream inputStream, int type) { |
||||
|
mInputStream = inputStream; |
||||
|
mReadBuffer = new byte[1024]; |
||||
|
this.type = type; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void run() { |
||||
|
super.run(); |
||||
|
if (type == 485) { |
||||
|
Log.e("ReadThread", "type 485"); |
||||
|
while (!isStopThread) { |
||||
|
try { |
||||
|
if (null == mInputStream) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Log.e("ReadThread", "type 1111"); |
||||
|
int size = mInputStream.read(mReadBuffer); |
||||
|
Log.e("ReadThread size", size + ""); |
||||
|
if (size <= 0) { |
||||
|
return; |
||||
|
} |
||||
|
Log.e("ReadThread", "type 2222"); |
||||
|
byte[] readBytes = new byte[size]; |
||||
|
System.arraycopy(mReadBuffer, 0, readBytes, 0, size); |
||||
|
onDataReceived(readBytes); |
||||
|
Log.e("ReadThread", "onDataReceived"); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
int rLen = 0; |
||||
|
int dLen = 0; |
||||
|
byte[] crc = new byte[]{0}; |
||||
|
byte[] tmp1 = new byte[]{0}; |
||||
|
byte[] tmp2 = new byte[]{0}; |
||||
|
|
||||
|
while (!isStopThread) { |
||||
|
try { |
||||
|
if (mInputStream == null) { |
||||
|
return; |
||||
|
} |
||||
|
if (!isHead()) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
crc[0] = 0x55; |
||||
|
crc[0] ^= 0xAA; |
||||
|
//接收 cmd |
||||
|
rLen = recvBuffer(tmp1, 1, 600); |
||||
|
if (rLen != 1) { |
||||
|
continue; |
||||
|
} |
||||
|
crc[0] ^= tmp1[0]; |
||||
|
if (tmp1[0] == 0x30) { |
||||
|
//接收 数据长度 |
||||
|
rLen = recvBuffer(tmp1, 1, 600); |
||||
|
if (rLen != 1) { |
||||
|
continue; |
||||
|
} |
||||
|
//接收 数据长度 |
||||
|
rLen = recvBuffer(tmp2, 1, 600); |
||||
|
if (rLen != 1) { |
||||
|
continue; |
||||
|
} |
||||
|
crc[0] ^= tmp1[0]; |
||||
|
crc[0] ^= tmp2[0]; |
||||
|
dLen = tmp1[0] & 0xff; |
||||
|
dLen |= ((tmp2[0] << 8) & 0xffff); |
||||
|
//dLen = ((uint)(tmp1[0])) | (tmp2[0] << 8); |
||||
|
byte[] data = new byte[dLen]; |
||||
|
if (dLen > 0) { |
||||
|
rLen = recvBuffer(data, dLen, 600); |
||||
|
if (rLen != dLen) { |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
//接收 crc |
||||
|
rLen = recvBuffer(tmp1, 1, 600); |
||||
|
if (rLen != 1) { |
||||
|
continue; |
||||
|
} |
||||
|
for (int i = 0; i < dLen; i++) { |
||||
|
crc[0] ^= data[i]; |
||||
|
} |
||||
|
//crc校验 |
||||
|
if (!ByteUtils.bytesToHexString(tmp1).equals(ByteUtils.bytesToHexString(crc))) { |
||||
|
continue; |
||||
|
} |
||||
|
onDataReceived(data); |
||||
|
} else { |
||||
|
Log.e("idhead", "no 0x30"); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void startRead() { |
||||
|
isStopThread = false; |
||||
|
start(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public void stopRead() { |
||||
|
isStopThread = true; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 关闭线程 释放资源 |
||||
|
*/ |
||||
|
public void release() { |
||||
|
stopRead(); |
||||
|
|
||||
|
if (null != mInputStream) { |
||||
|
try { |
||||
|
mInputStream.close(); |
||||
|
mInputStream = null; |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private int recvBuffer(byte[] buffer, int size, int msTimeout) { |
||||
|
long stime = System.currentTimeMillis(); |
||||
|
int rLen = 0, offset = 0; |
||||
|
byte[] rBuffer = new byte[1024]; |
||||
|
byte[] cache = new byte[1024]; |
||||
|
try { |
||||
|
do { |
||||
|
if (mInputStream.available() <= 0) { |
||||
|
Thread.sleep(10); |
||||
|
continue; |
||||
|
} |
||||
|
rLen = mInputStream.read(rBuffer, 0, Math.min(size, rBuffer.length)); |
||||
|
if (rLen <= 0) { |
||||
|
Thread.sleep(10); |
||||
|
continue; |
||||
|
} |
||||
|
stime = System.currentTimeMillis(); |
||||
|
|
||||
|
if (offset + rLen > cache.length) { |
||||
|
byte[] tmp = new byte[offset]; |
||||
|
System.arraycopy(cache, 0, tmp, 0, offset); |
||||
|
cache = new byte[(offset + rLen) * 2]; |
||||
|
System.arraycopy(tmp, 0, cache, 0, offset); |
||||
|
} |
||||
|
System.arraycopy(rBuffer, 0, cache, offset, rLen); |
||||
|
offset += rLen; |
||||
|
if (offset >= size) { |
||||
|
System.arraycopy(cache, 0, buffer, 0, size); |
||||
|
break; |
||||
|
} |
||||
|
} while (msTimeout > 0 && System.currentTimeMillis() - stime < msTimeout); |
||||
|
} catch (Exception e) { |
||||
|
return offset; |
||||
|
} |
||||
|
return offset; |
||||
|
} |
||||
|
|
||||
|
private boolean isHead() { |
||||
|
byte[] head1 = new byte[]{0}; |
||||
|
byte[] head2 = new byte[]{0}; |
||||
|
|
||||
|
while (true) { |
||||
|
if (head1[0] != (byte) 0x55) { |
||||
|
int rLen = recvBuffer(head1, 1, 500); |
||||
|
if (rLen != 1) { |
||||
|
try { |
||||
|
Thread.sleep(500); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
return false; |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int rLen = recvBuffer(head2, 1, 500); |
||||
|
if (rLen != 1) { |
||||
|
try { |
||||
|
Thread.sleep(10); |
||||
|
} catch (InterruptedException e) { |
||||
|
e.printStackTrace(); |
||||
|
return false; |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (head1[0] != (byte) 0x55) { |
||||
|
head1[0] = head2[0]; |
||||
|
continue; |
||||
|
} |
||||
|
if (head2[0] != (byte) 0xAA) { |
||||
|
head1[0] = head2[0]; |
||||
|
continue; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,487 @@ |
|||||
|
package com.kongqw.serialportlibrary.utils; |
||||
|
|
||||
|
import java.io.ByteArrayOutputStream; |
||||
|
import java.io.DataOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
|
||||
|
/** |
||||
|
* @author wangsir |
||||
|
* <p> |
||||
|
* 2017年9月21日 |
||||
|
*/ |
||||
|
public class ByteUtils { |
||||
|
/** |
||||
|
* 将int转为高端字节序排列的byte数组(Java内存存放顺序) |
||||
|
* |
||||
|
* @param n |
||||
|
* @return |
||||
|
*/ |
||||
|
public static byte[] int2ByteArray(int n) { |
||||
|
byte[] byteArray = null; |
||||
|
try { |
||||
|
ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); |
||||
|
DataOutputStream dataOut = new DataOutputStream(byteOut); |
||||
|
dataOut.writeInt(n); |
||||
|
byteArray = byteOut.toByteArray(); |
||||
|
Arrays.toString(byteArray); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
return byteArray; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将int转为高字节在前,低字节在后的byte数组 |
||||
|
* |
||||
|
* @param n int |
||||
|
* @return byte[] |
||||
|
*/ |
||||
|
public static byte[] int2Hbytes(int n) { |
||||
|
byte[] b = new byte[4]; |
||||
|
b[3] = (byte) (n & 0xff); |
||||
|
b[2] = (byte) (n >> 8 & 0xff); |
||||
|
b[1] = (byte) (n >> 16 & 0xff); |
||||
|
b[0] = (byte) (n >> 24 & 0xff); |
||||
|
|
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将int转为高字节在前,低字节在后的byte数组,一字节 |
||||
|
* |
||||
|
* @param n int |
||||
|
* @return byte[] |
||||
|
*/ |
||||
|
public static byte[] int2Hbytes1byte(int n) { |
||||
|
byte[] b = new byte[1]; |
||||
|
b[0] = (byte) (n & 0xff); |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将short转为高字节在前,低字节在后的byte数组 |
||||
|
* |
||||
|
* @param n short |
||||
|
* @return byte[] |
||||
|
*/ |
||||
|
public static byte[] short2Hbytes(short n) { |
||||
|
byte[] b = new byte[2]; |
||||
|
b[1] = (byte) (n & 0xff); |
||||
|
b[0] = (byte) (n >> 8 & 0xff); |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 以下 是整型数 和 网络字节序的 byte[] 数组之间的转换 |
||||
|
* |
||||
|
* @param n |
||||
|
* @return |
||||
|
*/ |
||||
|
public static byte[] long2Hbytes(long n) { |
||||
|
byte[] b = new byte[8]; |
||||
|
b[7] = (byte) (n & 0xff); |
||||
|
b[6] = (byte) (n >> 8 & 0xff); |
||||
|
b[5] = (byte) (n >> 16 & 0xff); |
||||
|
b[4] = (byte) (n >> 24 & 0xff); |
||||
|
b[3] = (byte) (n >> 32 & 0xff); |
||||
|
b[2] = (byte) (n >> 40 & 0xff); |
||||
|
b[1] = (byte) (n >> 48 & 0xff); |
||||
|
b[0] = (byte) (n >> 56 & 0xff); |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
public static byte[] long2H6bytes(long n) { |
||||
|
byte[] b = new byte[6]; |
||||
|
b[5] = (byte) (n & 0xff); |
||||
|
b[4] = (byte) (n >> 8 & 0xff); |
||||
|
b[3] = (byte) (n >> 16 & 0xff); |
||||
|
b[2] = (byte) (n >> 24 & 0xff); |
||||
|
b[1] = (byte) (n >> 32 & 0xff); |
||||
|
b[0] = (byte) (n >> 40 & 0xff); |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
public static byte[] long2H4bytes(long n) { |
||||
|
byte[] b = new byte[4]; |
||||
|
b[3] = (byte) (n & 0xff); |
||||
|
b[2] = (byte) (n >> 8 & 0xff); |
||||
|
b[1] = (byte) (n >> 16 & 0xff); |
||||
|
b[0] = (byte) (n >> 24 & 0xff); |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
public static byte[] unlong2H4bytes(long n) { |
||||
|
byte[] b = new byte[4]; |
||||
|
b[0] = (byte) (n & 0xff); |
||||
|
b[1] = (byte) (n >> 8 & 0xff); |
||||
|
b[2] = (byte) (n >> 16 & 0xff); |
||||
|
b[3] = (byte) (n >> 24 & 0xff); |
||||
|
return b; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 合并数组 |
||||
|
* |
||||
|
* @param first |
||||
|
* @param rest |
||||
|
* @return |
||||
|
*/ |
||||
|
public static byte[] concatBytes(byte[] first, byte[]... rest) { |
||||
|
int totalLength = first.length; |
||||
|
for (byte[] array : rest) { |
||||
|
if (null != array) { |
||||
|
totalLength += array.length; |
||||
|
} |
||||
|
} |
||||
|
byte[] result = Arrays.copyOf(first, totalLength); |
||||
|
int offset = first.length; |
||||
|
for (byte[] array : rest) { |
||||
|
if (null != array) { |
||||
|
System.arraycopy(array, 0, result, offset, array.length); |
||||
|
offset += array.length; |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* byte数组转为十六进制字符串 |
||||
|
* |
||||
|
* @param bytes |
||||
|
* @return |
||||
|
*/ |
||||
|
public static String byte2Hex(byte[] bytes) { |
||||
|
StringBuffer hexString = new StringBuffer(); |
||||
|
for (int i = 0; i < bytes.length; i++) { |
||||
|
String hex = Integer.toHexString(0xff & bytes[i]); |
||||
|
|
||||
|
if (hex.length() == 1) { |
||||
|
hexString.append('0'); |
||||
|
} |
||||
|
hexString.append(hex); |
||||
|
} |
||||
|
return hexString.toString(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Convert hex string to byte[] |
||||
|
* |
||||
|
* @param hexString the hex string |
||||
|
* @return byte[] |
||||
|
*/ |
||||
|
public static byte[] hex2Byte(String hexString) { |
||||
|
if (hexString == null || hexString.equals("")) { |
||||
|
return null; |
||||
|
} |
||||
|
hexString = hexString.toUpperCase().replace(" ", ""); |
||||
|
int length = hexString.length() / 2; |
||||
|
char[] hexChars = hexString.toCharArray(); |
||||
|
byte[] d = new byte[length]; |
||||
|
for (int i = 0; i < length; i++) { |
||||
|
int pos = i * 2; |
||||
|
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); |
||||
|
} |
||||
|
return d; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Convert char to byte |
||||
|
* |
||||
|
* @param c char |
||||
|
* @return byte |
||||
|
*/ |
||||
|
private static byte charToByte(char c) { |
||||
|
return (byte) "0123456789ABCDEF".indexOf(c); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 输入流转为字节数组 |
||||
|
*/ |
||||
|
public static byte[] toByteArray(InputStream input) throws IOException { |
||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream(); |
||||
|
byte[] buffer = new byte[4096]; |
||||
|
int n = 0; |
||||
|
while (-1 != (n = input.read(buffer))) { |
||||
|
output.write(buffer, 0, n); |
||||
|
} |
||||
|
return output.toByteArray(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* bytes字符串转换为Byte值 |
||||
|
* |
||||
|
* @param src src Byte字符串,每个Byte之间没有分隔符 |
||||
|
* @return byte[] |
||||
|
*/ |
||||
|
public static byte[] hexStr2Bytes(String src) { |
||||
|
int m = 0, n = 0; |
||||
|
int l = src.length() / 2; |
||||
|
byte[] ret = new byte[l]; |
||||
|
for (int i = 0; i < l; i++) { |
||||
|
m = i * 2 + 1; |
||||
|
n = m + 1; |
||||
|
ret[i] = Byte.decode("0x" + src.substring(i * 2, m) + src.substring(m, n)); |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
// /** |
||||
|
// * 字节数组转换成16进制字符串 |
||||
|
// * @param bytes 字节数组 |
||||
|
// * @return 16进制字符串 |
||||
|
// */ |
||||
|
// public static String hexEncode(byte[] bytes) { |
||||
|
// if (bytes == null || bytes.length <= 0) { |
||||
|
// return null; |
||||
|
// } |
||||
|
// return new String(Hex.encodeHex(bytes)); //Hex.encodeHex(bytes, false) |
||||
|
// } |
||||
|
// |
||||
|
// /** |
||||
|
// * 16进制字符串转换成字节数组 |
||||
|
// * @param hexStr 16进制字符串 |
||||
|
// * @return 字节数组 |
||||
|
// */ |
||||
|
// public static byte[] hexDecode(String hexStr) { |
||||
|
// if (hexStr == null || "".equals(hexStr)) { |
||||
|
// return null; |
||||
|
// } |
||||
|
// try { |
||||
|
// char[] cs = hexStr.toCharArray(); |
||||
|
// return Hex.decodeHex(cs); |
||||
|
// } catch (DecoderException e) { |
||||
|
// e.printStackTrace(); |
||||
|
// } |
||||
|
// return null; |
||||
|
// } |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 字符串转换成十六进制字符串 |
||||
|
* |
||||
|
* @param s s为待转换的ASCII字符串 |
||||
|
*/ |
||||
|
public static String str2HexStr(String s) { |
||||
|
|
||||
|
String str = ""; |
||||
|
for (int i = 0; i < s.length(); i++) { |
||||
|
int ch = (int) s.charAt(i); |
||||
|
String s4 = Integer.toHexString(ch); |
||||
|
str = str + s4; |
||||
|
} |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
public static String int2HexStr(Integer n) { |
||||
|
|
||||
|
String str = Integer.toHexString(n); |
||||
|
while (str.length() < 18) { |
||||
|
str = "0" + str; |
||||
|
} |
||||
|
return str.toUpperCase(); |
||||
|
} |
||||
|
|
||||
|
public static String binaryToHex(String bin) { |
||||
|
String str = Long.toHexString(Long.parseLong(bin, 2)); |
||||
|
while (str.length() < 4) { |
||||
|
str = "0" + str; |
||||
|
} |
||||
|
return str.toUpperCase(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 16进制的字符串转byte数组 |
||||
|
* |
||||
|
* @param src |
||||
|
* @return |
||||
|
*/ |
||||
|
public static byte[] str16ToBytes(String src) { |
||||
|
int w = 0; |
||||
|
byte[] bytes_2 = new byte[src.length() / 2]; |
||||
|
for (int i = 0; i < src.length(); i++) { |
||||
|
String zz = src.substring(i, i + 2); |
||||
|
byte aaa = (byte) Integer.parseInt(zz, 16); |
||||
|
bytes_2[w] = aaa; |
||||
|
i++; |
||||
|
|
||||
|
w = w + 1; |
||||
|
} |
||||
|
|
||||
|
return bytes_2; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 16进制转10进制数字 |
||||
|
* |
||||
|
* @param src |
||||
|
* @return |
||||
|
*/ |
||||
|
public static int str16ToInt10(String src) { |
||||
|
return Integer.parseInt(src, 16); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 16进制转10进制数字 |
||||
|
* |
||||
|
* @param src |
||||
|
* @return |
||||
|
*/ |
||||
|
public static long str16ToLong10(String src) { |
||||
|
return Long.parseLong(src, 16); |
||||
|
} |
||||
|
|
||||
|
public static String convertStringToHex(String str) { |
||||
|
char[] chars = str.toCharArray(); |
||||
|
StringBuffer hex = new StringBuffer(); |
||||
|
for (int i = 0; i < chars.length; i++) { |
||||
|
hex.append(Integer.toHexString((int) chars[i])); |
||||
|
} |
||||
|
return hex.toString(); |
||||
|
} |
||||
|
|
||||
|
public static String convertHexToString(String hex) { |
||||
|
hex = hex.replace(" ", ""); |
||||
|
StringBuilder sb = new StringBuilder(); |
||||
|
StringBuilder temp = new StringBuilder(); |
||||
|
|
||||
|
//49204c6f7665204a617661 split into two characters 49, 20, 4c... |
||||
|
for (int i = 0; i < hex.length() - 1; i += 2) { |
||||
|
|
||||
|
//grab the hex in pairs |
||||
|
String output = hex.substring(i, (i + 2)); |
||||
|
//convert hex to decimal |
||||
|
int decimal = Integer.parseInt(output, 16); |
||||
|
//convert the decimal to character |
||||
|
sb.append((char) decimal); |
||||
|
|
||||
|
temp.append(decimal); |
||||
|
} |
||||
|
|
||||
|
return sb.toString(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param |
||||
|
* @ClassName |
||||
|
* @Description : 功能说明 |
||||
|
* @Return : |
||||
|
* @Author : li |
||||
|
* @Date : 2021/3/25 13:48 |
||||
|
*/ |
||||
|
public static byte[] hexStringToByteArray(String hexString) { |
||||
|
hexString = hexString.replaceAll(" ", ""); |
||||
|
int len = hexString.length(); |
||||
|
byte[] bytes = new byte[len / 2]; |
||||
|
for (int i = 0; i < len; i += 2) { |
||||
|
// 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节 |
||||
|
bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character |
||||
|
.digit(hexString.charAt(i + 1), 16)); |
||||
|
} |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param |
||||
|
* @ClassName |
||||
|
* @Description : 功能说明 |
||||
|
* @Return : |
||||
|
* @Author : li |
||||
|
* @Date : 2021/3/25 13:48 |
||||
|
*/ |
||||
|
public static String bytesToHexString(byte[] bytes) { |
||||
|
StringBuilder sb = new StringBuilder(); |
||||
|
for (int i = 0; i < bytes.length; i++) { |
||||
|
String hex = Integer.toHexString(0xFF & bytes[i]); |
||||
|
if (hex.length() == 1) { |
||||
|
sb.append('0'); |
||||
|
} |
||||
|
sb.append(hex); |
||||
|
} |
||||
|
return sb.toString(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 从16进制字符串中获取2进制为1的序列号 |
||||
|
* |
||||
|
* @param src |
||||
|
* @return |
||||
|
*/ |
||||
|
public static ArrayList Str16Get1(String src) { |
||||
|
byte[] b_p = ByteUtils.str16ToBytes(src); |
||||
|
ArrayList list = new ArrayList<>(); |
||||
|
int w = 0; |
||||
|
for (int i = 0; i < src.length() / 2; i++) { |
||||
|
for (int j = 7; j >= 0; j--) { |
||||
|
if ((b_p[i] & (1 << j)) != 0) { |
||||
|
int pp = i * 8 + 8 - j; |
||||
|
System.out.println("pppp=======" + pp); |
||||
|
list.add(w, pp); |
||||
|
w = w + 1; |
||||
|
} else { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
return list; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 异或校验 |
||||
|
* |
||||
|
* @param bytes |
||||
|
* @return |
||||
|
*/ |
||||
|
public static byte[] bytesXorCrc(byte[] bytes) { |
||||
|
byte[] crc = new byte[1];// 异或校验 |
||||
|
crc[0] = bytes[0]; |
||||
|
for (int i = 1; i < bytes.length; i++) { |
||||
|
crc[0] ^= bytes[i]; |
||||
|
} |
||||
|
return crc; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 和校验 |
||||
|
* |
||||
|
* @param data |
||||
|
* @return |
||||
|
*/ |
||||
|
public static String makeChecksum(String data) { |
||||
|
data = data.replace(" ", ""); |
||||
|
if (data == null || data.equals("")) { |
||||
|
return ""; |
||||
|
} |
||||
|
int total = 0; |
||||
|
int len = data.length(); |
||||
|
int num = 0; |
||||
|
while (num < len) { |
||||
|
String s = data.substring(num, num + 2); |
||||
|
total += Integer.parseInt(s, 16); |
||||
|
num = num + 2; |
||||
|
} |
||||
|
/** |
||||
|
* 用256求余最大是255,即16进制的FF |
||||
|
*/ |
||||
|
int mod = total % 256; |
||||
|
String hex = Integer.toHexString(mod); |
||||
|
len = hex.length(); |
||||
|
// 如果不够校验位的长度,补0,这里用的是两位校验 |
||||
|
if (len < 2) { |
||||
|
hex = "0" + hex; |
||||
|
} |
||||
|
hex = hex.toUpperCase(); |
||||
|
if (hex.equals("7E")) { |
||||
|
hex = "7F 01"; |
||||
|
} else if (hex.equals("7F")) { |
||||
|
hex = "7F 02"; |
||||
|
} |
||||
|
return hex; |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
<resources> |
||||
|
<string name="app_name">SerialPortLibrary</string> |
||||
|
</resources> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue