diff --git a/README.md b/README.md index e0f5ca5..c37ddd5 100644 --- a/README.md +++ b/README.md @@ -1,146 +1,83 @@ -# ASR5515串口通信Android应用 +# ASR5515 Android 串口通信应用 ## 项目概述 -这是一个用于与ASR5515设备进行串口通信的Android应用,提供设备管理、状态监控、固件升级等功能。应用通过串口(/dev/ttyS3)与设备通信,波特率115200。 +这是一个基于 Android 平台的串口通信应用,主要用于与 ASR5515 设备进行通信。该应用实现了完整的串口通信协议,支持设备管理、数据采集、状态监控等功能。 ## 主要功能 -- 设备检查与状态监控 -- 设备重启 -- 日志控制 -- 蓝牙版本查询 -- 蓝牙固件升级 -- 主机状态同步 -- 心率血压血氧(HR/BP/BO)测量 - - 自动测量 - - 手动测量 -- 动态测量功能 -- Boot bin检查 -- 采集频率设置 -- 支持模拟模式(不实际连接硬件) +1. 串口通信 + - 基于 Android SerialPort API 的串口通信实现 + - 支持自定义波特率和串口配置 + - 数据帧封装和解析 -## 项目结构 -``` -com.ismart.ib86 -├── app/ # 主应用相关 -│ └── MainActivity.java -├── common/ # 公共组件 -│ └── utils/ # 工具类 -└── feature/ # 功能模块 - └── serial/ # 串口通信实现 - ├── ASR5515DeviceManager.java - ├── ASR5515Protocol.java - └── SerialPortHelper.java +2. 设备管理 + - 设备检测和状态查询 + - 设备重启控制 + - 固件版本管理 + - 蓝牙模块管理 + +3. 数据采集 + - 心率、血压、血氧自动测量 + - 手动测量模式支持 + - 动态测量功能 + - 可配置的数据采集频率 + +4. 状态监控 + - 设备佩戴检测 + - 主机状态同步 + - 日志控制功能 + +## 技术架构 + +```mermaid +graph TD + A[Android App] --> B[串口通信层] + B --> C[协议解析层] + C --> D[设备管理] + C --> E[数据采集] + C --> F[状态监控] + + B --> |SerialPortHelper| G[串口读写] + C --> |ASR5515Protocol| H[协议实现] + D --> |DeviceManager| I[设备控制] ``` -## 核心类说明 +## 核心模块 -### 1. ASR5515Protocol -协议核心类,定义ASR5515设备的通信协议格式和命令。 +1. **SerialPortHelper** + - 串口通信的核心类 + - 实现数据的发送和接收 + - 管理通信线程和数据队列 -**主要功能:** -- 定义设备信息结构(DeviceInfo) -- 定义各种命令(Commands) -- 定义帧结构(Frame) -- 提供各种请求和响应的创建与解析方法 +2. **ASR5515Protocol** + - 实现 ASR5515 设备通信协议 + - 定义命令集和数据格式 + - 处理数据帧的封装和解析 -### 2. SerialPortHelper -串口通信核心实现类,封装了与ASR5515设备的通信逻辑。 +3. **设备管理模块** + - 设备信息查询 + - 设备控制命令 + - 固件升级支持 -**主要功能:** -- 串口初始化和管理 -- 数据发送和接收 -- ASR5515协议命令的封装 -- 回调机制处理各种响应 -- 支持模拟模式 +## 通信协议 +- 帧格式:`[命令字(2字节) + 序列号(2字节) + 长度(2字节) + 数据 + ]` +- 支持的命令类型: + - 设备检查 (0x0065/0x0066) + - 设备重启 (0x0067/0x0068) + - 状态同步 (0x0079/0x007A) + - 数据采集 (0x00C9-0x00D0) + - 佩戴检测 (0x0105/0x0106) -### 3. ASR5515DeviceManager -设备管理核心类,提供高级设备管理接口。 +## 开发环境 +- Android Studio +- Java +- Gradle 构建系统 -**主要功能:** -- 设备状态检查 -- 设备重启 -- 日志控制 -- 蓝牙版本查询 -- 蓝牙升级 -- 主机状态同步 - -### 4. MainActivity -应用主界面,提供用户交互界面。 +## 依赖 +- Android SerialPort API +- Android SDK ## 使用说明 - -### 初始化串口 -```java -SerialPortHelper serialPortHelper = new SerialPortHelper("/dev/ttyS1", 115200); -serialPortHelper.setDeviceStatusCallback(new DeviceStatusCallback() { - @Override - public void onDeviceCheckResponse(DeviceInfo deviceInfo) { - // 处理设备检查响应 - } - - // 实现其他回调方法... -}); -serialPortHelper.open(); -``` - -### 发送命令 -```java -// 设备检查 -serialPortHelper.checkDevice(); - -// 设备重启 -serialPortHelper.restartDevice(); - -// 查询蓝牙版本 -serialPortHelper.queryBtVersion(); - -// 蓝牙升级 -serialPortHelper.upgradeBt(); -``` - -### 关闭串口 -```java -serialPortHelper.close(); -``` - -## 开发环境要求 -- Android Studio 最新稳定版 -- Android SDK API 21+ -- JDK 8+ -- 支持串口的Android设备或模拟器 - -## 硬件连接指南 -1. 确保ASR5515设备已正确供电 -2. 使用USB转串口线连接设备与Android主机 -3. 确认串口设备节点(通常为/dev/ttyS3) -4. 设置波特率为115200 - -## 构建与运行 -1. 克隆项目 -```bash -git clone https://github.com/ismart/ib86.git -``` -2. 导入Android Studio -3. 配置设备连接参数(在MainActivity中修改) -4. 构建并运行应用 - -## 测试方法 -1. 单元测试 -```bash -./gradlew test -``` -2. 功能测试 -- 使用模拟模式测试基本功能 -- 连接实际设备测试通信功能 -- 测试各种测量功能(HR/BP/BO) - -## 依赖项 -- Android SerialPort API -- ASR5515通信协议(参考ASR-5515通信协议.docx) - -## 注意事项 -1. 需要设备支持串口通信 -2. 需要正确配置串口名称和波特率 -3. 模拟模式可用于开发测试 -4. 实际使用时请参考ASR5515设备的具体通信协议 -5. 测量功能需要ASR5515设备支持相应传感器 \ No newline at end of file +1. 配置串口参数(波特率、设备路径) +2. 初始化 SerialPortHelper +3. 实现必要的回调接口 +4. 使用设备管理和数据采集功能 \ No newline at end of file diff --git a/app/src/main/java/com/ismart/ib86/app/MainActivity.java b/app/src/main/java/com/ismart/ib86/app/MainActivity.java index 32042fb..5f3bf62 100644 --- a/app/src/main/java/com/ismart/ib86/app/MainActivity.java +++ b/app/src/main/java/com/ismart/ib86/app/MainActivity.java @@ -166,6 +166,20 @@ public class MainActivity extends AppCompatActivity { deviceManager.setWearDetectionListener(response -> LogManager.d(TAG, "佩戴检测: " + response.isWearing) ); + + // 设置算法结果回调 + deviceManager.setAlgoResultListener(new ASR5515DeviceManager.AlgoResultListener() { + @Override + public void onAlgoResultReceived(ASR5515Protocol.AlgoResultResponse response) { + LogManager.d(TAG, "算法结果: " + response.toString()); + // 在这里处理心率结果 + if (response.heartRate > 0) { + LogManager.d(TAG, "心率值: " + response.heartRate); + // 可以在这里更新UI或进行其他操作 + } + } + }); + } private void startProtocolTests() { @@ -184,11 +198,11 @@ public class MainActivity extends AppCompatActivity { // deviceManager.queryBtVersion(); // } // }, 2000); - + deviceManager.stopGH3220Measure((byte) 0x02); handler.postDelayed(new Runnable() { @Override public void run() { - deviceManager.sendWearDetectionRequest(); + deviceManager.startGH3220Measure((byte) 0x02); } }, 1000); @@ -207,10 +221,10 @@ protected void onDestroy() { // 移至后台线程执行耗时操作,避免ANR new Thread(() -> { - if (deviceManager != null) { - deviceManager.syncHostStatus(false); - deviceManager.close(); - } +// if (deviceManager != null) { +// deviceManager.syncHostStatus(false); +// deviceManager.close(); +// } // 释放GPIO测试资源 if (gpioTest != null) { diff --git a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java index a9ba430..ba83a9c 100644 --- a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java +++ b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515DeviceManager.java @@ -31,6 +31,8 @@ public class ASR5515DeviceManager { private HrBpBoManualMeasureListener hrBpBoManualMeasureListener; private DynamicMeasureListener dynamicMeasureListener; private WearDetectionListener wearDetectionListener; + private GH3220MeasureListener gh3220MeasureListener; + private AlgoResultListener algoResultListener; // 细分回调接口 public interface DeviceCheckListener { @@ -80,6 +82,22 @@ public class ASR5515DeviceManager { public interface WearDetectionListener { void onWearDetectionResponse(ASR5515Protocol.WearDetectionResponse response); } + + /** + * 算法结果监听器 + */ + public interface AlgoResultListener { + /** + * 接收到心率算法结果 + * @param response 心率结果响应 + */ + void onAlgoResultReceived(ASR5515Protocol.AlgoResultResponse response); + } + + public interface GH3220MeasureListener { + void onGH3220MeasureStartResponse(ASR5515Protocol.GH3220MeasureResponse response); + void onGH3220MeasureStopResponse(ASR5515Protocol.GH3220MeasureResponse response); + } /** * 构造函数 @@ -206,6 +224,31 @@ public class ASR5515DeviceManager { } } }); + + serialPortHelper.setGH3220MeasureCallback(new SerialPortHelper.GH3220MeasureCallback() { + @Override + public void onGH3220MeasureStartResponse(ASR5515Protocol.GH3220MeasureResponse response) { + if (gh3220MeasureListener != null) { + mainHandler.post(() -> gh3220MeasureListener.onGH3220MeasureStartResponse(response)); + } + } + + @Override + public void onGH3220MeasureStopResponse(ASR5515Protocol.GH3220MeasureResponse response) { + if (gh3220MeasureListener != null) { + mainHandler.post(() -> gh3220MeasureListener.onGH3220MeasureStopResponse(response)); + } + } + }); + + serialPortHelper.setAlgoResultCallback(new SerialPortHelper.AlgoResultCallback() { + @Override + public void onAlgoResultReceived(final ASR5515Protocol.AlgoResultResponse response) { + if (algoResultListener != null) { + mainHandler.post(() -> algoResultListener.onAlgoResultReceived(response)); + } + } + }); } /** @@ -274,6 +317,17 @@ public class ASR5515DeviceManager { public void setWearDetectionListener(WearDetectionListener listener) { this.wearDetectionListener = listener; } + + public void setGH3220MeasureListener(GH3220MeasureListener listener) { + this.gh3220MeasureListener = listener; + } + + /** + * 设置算法结果监听器 + */ + public void setAlgoResultListener(AlgoResultListener listener) { + this.algoResultListener = listener; + } /** * 设置Boot Bin检查监听器 @@ -396,6 +450,24 @@ public class ASR5515DeviceManager { serialPortHelper.sendWearDetectionRequest(); } + /** + * 发送GH3220开启测量请求 + * @param type 测量类型,参考ASR5515Protocol.GH3220MeasureType + */ + public void startGH3220Measure(byte type) { + wakeupDevice(); + serialPortHelper.startGH3220Measure(type); + } + + /** + * 发送GH3220停止测量请求 + * @param type 测量类型,参考ASR5515Protocol.GH3220MeasureType + */ + public void stopGH3220Measure(byte type) { + wakeupDevice(); + serialPortHelper.stopGH3220Measure(type); + } + /** * 唤醒设备 * 通过GPIO33唤醒蓝牙,ASR休眠时会自动发送'w'字符 diff --git a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515Protocol.java b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515Protocol.java index de25c28..d7be9e1 100644 --- a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515Protocol.java +++ b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/ASR5515Protocol.java @@ -212,6 +212,11 @@ public class ASR5515Protocol { public static final short CMD_DYNAMIC_MEASURE_RESP = 0x00D0; // 动态测量响应 [208 sn 0] public static final short CMD_WEAR_DETECTION = 0x0105; // 佩戴检测查询 [261 0 0] public static final short CMD_WEAR_DETECTION_RESP = 0x0106; // 佩戴检测响应 [262 sn len 0/1] + public static final short CMD_GH3220_MEASURE_START = 0x01F5; // GH3220开启测量 [501 0 len type] + public static final short CMD_GH3220_MEASURE_START_RESP = 0x01F6; // GH3220开启测量响应 [502 sn len status] + public static final short CMD_GH3220_MEASURE_STOP = 0x01F7; // GH3220停止测量 [503 0 len type] + public static final short CMD_GH3220_MEASURE_STOP_RESP = 0x01F8; // GH3220停止测量响应 [504 sn len status] + public static final short CMD_ALGO_RESULT = 0x01FE; // 主动上报算法结果(心率) [510 sn len AlgoResult] } public static class Frame { @@ -296,12 +301,12 @@ public class ASR5515Protocol { // 提取数据部分(如果有的话) int dataLength = endIndex - (startIndex + 7); // 7 = head(1) + type(2) + sn(2) + len(2) - //LogManager.d(TAG, "len: "+ len + " dataLength: " + dataLength + " end data" + data[endIndex]); + LogManager.d(TAG, "len: "+ len + " dataLength: " + dataLength + " end data" + data[endIndex]); byte[] frameData; if (dataLength > 0) { frameData = new byte[dataLength]; System.arraycopy(data, startIndex + 7, frameData, 0, dataLength); -// LogManager.d(TAG,"data :"+ bytesToHexString(frameData)); + LogManager.d(TAG,"data :"+ bytesToHexString(frameData)); } else { frameData = new byte[0]; } @@ -645,4 +650,115 @@ public class ASR5515Protocol { boolean isWearing = frame.data[0] == 1; return new WearDetectionResponse(isWearing); } + + // GH3220测量类型常量 + public static class GH3220MeasureType { + public static final byte ADT = 0x01; // ADT + public static final byte HR = 0x02; // 心率 + public static final byte LEAD = 0x03; // LEAD + public static final byte SPO2 = 0x40; // 血氧 + } + + // 创建GH3220开启测量请求帧 [501 0 len type] + public static byte[] createGH3220MeasureStartRequest(byte type) { + byte[] data = new byte[1]; + data[0] = type; // 测量类型 + return createFrame(Commands.CMD_GH3220_MEASURE_START, data); + } + + // 创建GH3220停止测量请求帧 [503 0 len type] + public static byte[] createGH3220MeasureStopRequest(byte type) { + byte[] data = new byte[1]; + data[0] = type; // 测量类型 + return createFrame(Commands.CMD_GH3220_MEASURE_STOP, data); + } + + // GH3220测量响应类 + public static class GH3220MeasureResponse { + public byte sn; + public byte len; + public byte status; + + public GH3220MeasureResponse(byte sn, byte len, byte status) { + this.sn = sn; + this.len = len; + this.status = status; + } + + @Override + public String toString() { + return "GH3220MeasureResponse{" + + "sn=" + sn + + ", len=" + len + + ", status=" + status + + '}'; + } + } + + // 解析GH3220开启测量响应帧 [502 sn len status] + public static GH3220MeasureResponse parseGH3220MeasureStartResponse(Frame frame) { + if (frame == null || frame.command != Commands.CMD_GH3220_MEASURE_START_RESP || frame.data.length < 3) { + return null; + } + return new GH3220MeasureResponse(frame.data[0], frame.data[1], frame.data[2]); + } + + // 解析GH3220停止测量响应帧 [504 sn len status] + public static GH3220MeasureResponse parseGH3220MeasureStopResponse(Frame frame) { + if (frame == null || frame.command != Commands.CMD_GH3220_MEASURE_STOP_RESP || frame.data.length < 3) { + return null; + } + return new GH3220MeasureResponse(frame.data[0], frame.data[1], frame.data[2]); + } + + // 算法结果响应类 [510 sn len AlgoResult] + public static class AlgoResultResponse { + public int heartRate; // 心率值 (snResult[0]) + public int confidence; // 置信度 (snResult[1]) + public int signalNoiseRatio; // 信噪比 (snResult[2]) + + public AlgoResultResponse(int heartRate, int confidence, int signalNoiseRatio) { + this.heartRate = heartRate; + this.confidence = confidence; + this.signalNoiseRatio = signalNoiseRatio; + } + + @Override + public String toString() { + return "AlgoResultResponse{" + + ", heartRate=" + heartRate + + ", confidence=" + confidence + + ", signalNoiseRatio=" + signalNoiseRatio + + '}'; + } + } + + // 解析算法结果响应帧 [updateFlag resultNum resultBit(2byte) heartRate(4) confidence(4) signalNoiseRatio(4)] + public static AlgoResultResponse parseAlgoResultResponse(Frame frame) { + if (frame == null || frame.command != Commands.CMD_ALGO_RESULT || frame.data.length < 4) { + return null; + } + LogManager.d(TAG," frame.data.length:"+ frame.data.length); + try { + // 解析心率值,只取第一个字节 + int heartRate = frame.data[4]; + + int confidence = 0; + int signalNoiseRatio = 0; + + // 如果数据长度足够,解析置信度,只取第一个字节 + if (frame.data.length >= 8) { + confidence = frame.data[8]; + } + + // 如果数据长度足够,解析信噪比,只取第一个字节 + if (frame.data.length >= 16) { + signalNoiseRatio = frame.data[16]; + } + return new AlgoResultResponse(heartRate, confidence, signalNoiseRatio); + } catch (Exception e) { + LogManager.e(TAG, "Parse algo result error: " + e.getMessage()); + return null; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java index ead9e09..22e5993 100644 --- a/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java +++ b/app/src/main/java/com/ismart/ib86/feature/serial/SerialPort/SerialPortHelper.java @@ -38,6 +38,8 @@ public class SerialPortHelper { private HrBpBoManualMeasureCallback hrBpBoManualMeasureCallback; private DynamicMeasureCallback dynamicMeasureCallback; private WearDetectionCallback wearDetectionCallback; + private GH3220MeasureCallback gh3220MeasureCallback; + private AlgoResultCallback algoResultCallback; private boolean isSimulationMode = false; private final ByteBuffer frameBuffer = ByteBuffer.allocate(1024); @@ -100,6 +102,22 @@ public class SerialPortHelper { public interface WearDetectionCallback { void onWearDetectionResponse(ASR5515Protocol.WearDetectionResponse response); } + + public interface GH3220MeasureCallback { + void onGH3220MeasureStartResponse(ASR5515Protocol.GH3220MeasureResponse response); + void onGH3220MeasureStopResponse(ASR5515Protocol.GH3220MeasureResponse response); + } + + /** + * 算法结果回调接口 + */ + public interface AlgoResultCallback { + /** + * 接收到心率算法结果 + * @param response 心率结果响应 + */ + void onAlgoResultReceived(ASR5515Protocol.AlgoResultResponse response); + } public SerialPortHelper(String portName, int baudRate) { this.mPortName = portName; @@ -167,6 +185,18 @@ public class SerialPortHelper { public void setWearDetectionCallback(WearDetectionCallback callback) { this.wearDetectionCallback = callback; } + + public void setGH3220MeasureCallback(GH3220MeasureCallback callback) { + this.gh3220MeasureCallback = callback; + } + + /** + * 设置算法结果回调 + * @param callback 算法结果回调 + */ + public void setAlgoResultCallback(AlgoResultCallback callback) { + this.algoResultCallback = callback; + } /** * 发送佩戴检测查询请求 @@ -256,6 +286,24 @@ public class SerialPortHelper { sendData(data, null); } + /** + * 发送GH3220开启测量请求 + * @param type 测量类型,参考ASR5515Protocol.GH3220MeasureType + */ + public void startGH3220Measure(byte type) { + byte[] data = ASR5515Protocol.createGH3220MeasureStartRequest(type); + sendData(data, null); + } + + /** + * 发送GH3220停止测量请求 + * @param type 测量类型,参考ASR5515Protocol.GH3220MeasureType + */ + public void stopGH3220Measure(byte type) { + byte[] data = ASR5515Protocol.createGH3220MeasureStopRequest(type); + sendData(data, null); + } + public boolean open() { try { LogManager.d(TAG, "Opening serial port: " + mPortName + " with baud rate: " + mBaudRate); @@ -371,7 +419,7 @@ public class SerialPortHelper { } } else if (mSerialPort != null) { try { - LogManager.d(TAG, "ASR->5515 DATA: " + Arrays.toString(data)); + //LogManager.d(TAG, "ASR->5515 DATA: " + Arrays.toString(data)); LogManager.d(TAG, "ASR->5515 DATA: " + bytesToHexString(data)); mSerialPort.getOutputStream().write(data); if (sendCallback != null) { @@ -403,7 +451,7 @@ public class SerialPortHelper { int bytesRead = mSerialPort.getInputStream().read(buffer); if (bytesRead > 0) { byte[] receivedData = Arrays.copyOf(buffer, bytesRead); - LogManager.d(TAG, "5515->ASR DATA: " + Arrays.toString(receivedData)); + //LogManager.d(TAG, "5515->ASR DATA: " + Arrays.toString(receivedData)); LogManager.d(TAG, "5515->ASR DATA: " + bytesToHexString(receivedData)); receiveQueue.put(receivedData); } @@ -586,6 +634,27 @@ public class SerialPortHelper { wearDetectionCallback.onWearDetectionResponse(wearDetectionResponse); } break; + + case ASR5515Protocol.Commands.CMD_GH3220_MEASURE_START_RESP: + ASR5515Protocol.GH3220MeasureResponse gh3220StartResponse = ASR5515Protocol.parseGH3220MeasureStartResponse(frame); + if (gh3220StartResponse != null && gh3220MeasureCallback != null) { + gh3220MeasureCallback.onGH3220MeasureStartResponse(gh3220StartResponse); + } + break; + + case ASR5515Protocol.Commands.CMD_GH3220_MEASURE_STOP_RESP: + ASR5515Protocol.GH3220MeasureResponse gh3220StopResponse = ASR5515Protocol.parseGH3220MeasureStopResponse(frame); + if (gh3220StopResponse != null && gh3220MeasureCallback != null) { + gh3220MeasureCallback.onGH3220MeasureStopResponse(gh3220StopResponse); + } + break; + + case ASR5515Protocol.Commands.CMD_ALGO_RESULT: + ASR5515Protocol.AlgoResultResponse algoResultResponse = ASR5515Protocol.parseAlgoResultResponse(frame); + if (algoResultResponse != null && algoResultCallback != null) { + algoResultCallback.onAlgoResultReceived(algoResultResponse); + } + break; } } } \ No newline at end of file