增加心率回调

This commit is contained in:
peng 2025-06-25 20:02:38 +08:00
parent dd06e16646
commit 09ff19795a
5 changed files with 349 additions and 141 deletions

199
README.md
View File

@ -1,146 +1,83 @@
# ASR5515串口通信Android应用 # ASR5515 Android 串口通信应用
## 项目概述 ## 项目概述
这是一个用于与ASR5515设备进行串口通信的Android应用提供设备管理、状态监控、固件升级等功能。应用通过串口(/dev/ttyS3)与设备通信波特率115200 这是一个基于 Android 平台的串口通信应用,主要用于与 ASR5515 设备进行通信。该应用实现了完整的串口通信协议,支持设备管理、数据采集、状态监控等功能
## 主要功能 ## 主要功能
- 设备检查与状态监控 1. 串口通信
- 设备重启 - 基于 Android SerialPort API 的串口通信实现
- 日志控制 - 支持自定义波特率和串口配置
- 蓝牙版本查询 - 数据帧封装和解析
- 蓝牙固件升级
- 主机状态同步
- 心率血压血氧(HR/BP/BO)测量
- 自动测量
- 手动测量
- 动态测量功能
- Boot bin检查
- 采集频率设置
- 支持模拟模式(不实际连接硬件)
## 项目结构 2. 设备管理
``` - 设备检测和状态查询
com.ismart.ib86 - 设备重启控制
├── app/ # 主应用相关 - 固件版本管理
│ └── MainActivity.java - 蓝牙模块管理
├── common/ # 公共组件
│ └── utils/ # 工具类 3. 数据采集
└── feature/ # 功能模块 - 心率、血压、血氧自动测量
└── serial/ # 串口通信实现 - 手动测量模式支持
├── ASR5515DeviceManager.java - 动态测量功能
├── ASR5515Protocol.java - 可配置的数据采集频率
└── SerialPortHelper.java
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 1. **SerialPortHelper**
协议核心类定义ASR5515设备的通信协议格式和命令。 - 串口通信的核心类
- 实现数据的发送和接收
- 管理通信线程和数据队列
**主要功能:** 2. **ASR5515Protocol**
- 定义设备信息结构(DeviceInfo) - 实现 ASR5515 设备通信协议
- 定义各种命令(Commands) - 定义命令集和数据格式
- 定义帧结构(Frame) - 处理数据帧的封装和解析
- 提供各种请求和响应的创建与解析方法
### 2. SerialPortHelper 3. **设备管理模块**
串口通信核心实现类封装了与ASR5515设备的通信逻辑。 - 设备信息查询
- 设备控制命令
- 固件升级支持
**主要功能:** ## 通信协议
- 串口初始化和管理 - 帧格式:`[命令字(2字节) + 序列号(2字节) + 长度(2字节) + 数据 + ]`
- 数据发送和接收 - 支持的命令类型:
- ASR5515协议命令的封装 - 设备检查 (0x0065/0x0066)
- 回调机制处理各种响应 - 设备重启 (0x0067/0x0068)
- 支持模拟模式 - 状态同步 (0x0079/0x007A)
- 数据采集 (0x00C9-0x00D0)
- 佩戴检测 (0x0105/0x0106)
### 3. ASR5515DeviceManager ## 开发环境
设备管理核心类,提供高级设备管理接口。 - Android Studio
- Java
- Gradle 构建系统
**主要功能:** ## 依赖
- 设备状态检查 - Android SerialPort API
- 设备重启 - Android SDK
- 日志控制
- 蓝牙版本查询
- 蓝牙升级
- 主机状态同步
### 4. MainActivity
应用主界面,提供用户交互界面。
## 使用说明 ## 使用说明
1. 配置串口参数(波特率、设备路径)
### 初始化串口 2. 初始化 SerialPortHelper
```java 3. 实现必要的回调接口
SerialPortHelper serialPortHelper = new SerialPortHelper("/dev/ttyS1", 115200); 4. 使用设备管理和数据采集功能
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设备支持相应传感器

View File

@ -166,6 +166,20 @@ public class MainActivity extends AppCompatActivity {
deviceManager.setWearDetectionListener(response -> deviceManager.setWearDetectionListener(response ->
LogManager.d(TAG, "佩戴检测: " + response.isWearing) 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() { private void startProtocolTests() {
@ -184,11 +198,11 @@ public class MainActivity extends AppCompatActivity {
// deviceManager.queryBtVersion(); // deviceManager.queryBtVersion();
// } // }
// }, 2000); // }, 2000);
deviceManager.stopGH3220Measure((byte) 0x02);
handler.postDelayed(new Runnable() { handler.postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
deviceManager.sendWearDetectionRequest(); deviceManager.startGH3220Measure((byte) 0x02);
} }
}, 1000); }, 1000);
@ -207,10 +221,10 @@ protected void onDestroy() {
// 移至后台线程执行耗时操作避免ANR // 移至后台线程执行耗时操作避免ANR
new Thread(() -> { new Thread(() -> {
if (deviceManager != null) { // if (deviceManager != null) {
deviceManager.syncHostStatus(false); // deviceManager.syncHostStatus(false);
deviceManager.close(); // deviceManager.close();
} // }
// 释放GPIO测试资源 // 释放GPIO测试资源
if (gpioTest != null) { if (gpioTest != null) {

View File

@ -31,6 +31,8 @@ public class ASR5515DeviceManager {
private HrBpBoManualMeasureListener hrBpBoManualMeasureListener; private HrBpBoManualMeasureListener hrBpBoManualMeasureListener;
private DynamicMeasureListener dynamicMeasureListener; private DynamicMeasureListener dynamicMeasureListener;
private WearDetectionListener wearDetectionListener; private WearDetectionListener wearDetectionListener;
private GH3220MeasureListener gh3220MeasureListener;
private AlgoResultListener algoResultListener;
// 细分回调接口 // 细分回调接口
public interface DeviceCheckListener { public interface DeviceCheckListener {
@ -81,6 +83,22 @@ public class ASR5515DeviceManager {
void onWearDetectionResponse(ASR5515Protocol.WearDetectionResponse response); 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);
}
/** /**
* 构造函数 * 构造函数
* @param context 上下文 * @param context 上下文
@ -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));
}
}
});
} }
/** /**
@ -275,6 +318,17 @@ public class ASR5515DeviceManager {
this.wearDetectionListener = listener; this.wearDetectionListener = listener;
} }
public void setGH3220MeasureListener(GH3220MeasureListener listener) {
this.gh3220MeasureListener = listener;
}
/**
* 设置算法结果监听器
*/
public void setAlgoResultListener(AlgoResultListener listener) {
this.algoResultListener = listener;
}
/** /**
* 设置Boot Bin检查监听器 * 设置Boot Bin检查监听器
*/ */
@ -396,6 +450,24 @@ public class ASR5515DeviceManager {
serialPortHelper.sendWearDetectionRequest(); 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'字符 * 通过GPIO33唤醒蓝牙ASR休眠时会自动发送'w'字符

View File

@ -212,6 +212,11 @@ public class ASR5515Protocol {
public static final short CMD_DYNAMIC_MEASURE_RESP = 0x00D0; // 动态测量响应 [208 sn 0] 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 = 0x0105; // 佩戴检测查询 [261 0 0]
public static final short CMD_WEAR_DETECTION_RESP = 0x0106; // 佩戴检测响应 [262 sn len 0/1] 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 { 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) 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; byte[] frameData;
if (dataLength > 0) { if (dataLength > 0) {
frameData = new byte[dataLength]; frameData = new byte[dataLength];
System.arraycopy(data, startIndex + 7, frameData, 0, dataLength); System.arraycopy(data, startIndex + 7, frameData, 0, dataLength);
// LogManager.d(TAG,"data :"+ bytesToHexString(frameData)); LogManager.d(TAG,"data :"+ bytesToHexString(frameData));
} else { } else {
frameData = new byte[0]; frameData = new byte[0];
} }
@ -645,4 +650,115 @@ public class ASR5515Protocol {
boolean isWearing = frame.data[0] == 1; boolean isWearing = frame.data[0] == 1;
return new WearDetectionResponse(isWearing); 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;
}
}
} }

View File

@ -38,6 +38,8 @@ public class SerialPortHelper {
private HrBpBoManualMeasureCallback hrBpBoManualMeasureCallback; private HrBpBoManualMeasureCallback hrBpBoManualMeasureCallback;
private DynamicMeasureCallback dynamicMeasureCallback; private DynamicMeasureCallback dynamicMeasureCallback;
private WearDetectionCallback wearDetectionCallback; private WearDetectionCallback wearDetectionCallback;
private GH3220MeasureCallback gh3220MeasureCallback;
private AlgoResultCallback algoResultCallback;
private boolean isSimulationMode = false; private boolean isSimulationMode = false;
private final ByteBuffer frameBuffer = ByteBuffer.allocate(1024); private final ByteBuffer frameBuffer = ByteBuffer.allocate(1024);
@ -101,6 +103,22 @@ public class SerialPortHelper {
void onWearDetectionResponse(ASR5515Protocol.WearDetectionResponse response); 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) { public SerialPortHelper(String portName, int baudRate) {
this.mPortName = portName; this.mPortName = portName;
this.mBaudRate = baudRate; this.mBaudRate = baudRate;
@ -168,6 +186,18 @@ public class SerialPortHelper {
this.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); 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() { public boolean open() {
try { try {
LogManager.d(TAG, "Opening serial port: " + mPortName + " with baud rate: " + mBaudRate); LogManager.d(TAG, "Opening serial port: " + mPortName + " with baud rate: " + mBaudRate);
@ -371,7 +419,7 @@ public class SerialPortHelper {
} }
} else if (mSerialPort != null) { } else if (mSerialPort != null) {
try { 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)); LogManager.d(TAG, "ASR->5515 DATA: " + bytesToHexString(data));
mSerialPort.getOutputStream().write(data); mSerialPort.getOutputStream().write(data);
if (sendCallback != null) { if (sendCallback != null) {
@ -403,7 +451,7 @@ public class SerialPortHelper {
int bytesRead = mSerialPort.getInputStream().read(buffer); int bytesRead = mSerialPort.getInputStream().read(buffer);
if (bytesRead > 0) { if (bytesRead > 0) {
byte[] receivedData = Arrays.copyOf(buffer, bytesRead); 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)); LogManager.d(TAG, "5515->ASR DATA: " + bytesToHexString(receivedData));
receiveQueue.put(receivedData); receiveQueue.put(receivedData);
} }
@ -586,6 +634,27 @@ public class SerialPortHelper {
wearDetectionCallback.onWearDetectionResponse(wearDetectionResponse); wearDetectionCallback.onWearDetectionResponse(wearDetectionResponse);
} }
break; 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;
} }
} }
} }