【DigiKey创意大赛】便携生命探测仪06+各模块整合联调
[复制链接]
上一帖介绍了如何获取心电数据并解析协议,最终显示在电脑上,实现了心电采集功能。本帖介绍如何把所有模块整合到一起,实现预期的作品功能。
一、预期功能
做一个便携生命探测仪,在发生危险时,能帮助救援人员方便找到遇险者。此方案主要采用热敏式图像传感器MLX90640ESF-BAB-000-TU检测人体红外信号,采用BME680气体,湿度,压力,温度传感器评估板记录环境信息,配合一个带屏幕的评估板,显示热成像图片。再额外添加一个心电采集模块,当发现遇险者后,能立即给遇险者采集心电图和心率并在屏幕上显示,方便救援人员判断生命体征状态,好制定下一步救援计划。
二、系统框图
整个系统框图如下图。
如上图所示,ESP32-S3评估板作为主控板,通过SPI接口连接一片480*320分辨率的触摸屏,用来显示各个检测数据、图像、心电波形。通过串口连接一片ESP32-C6评估板。ESP32-C6负责通过串口从LH001-91心电评估板接收字符串数据,然后选择心电波形、心率、导联脱落数据转换成浮点数,再从另外一个串口发给主控板。通过I2C接口连接热敏式图像传感器MLX90640ESF-BAB-000-TU检测人体红外信号,转换成热成像实时图片刷新到显示屏。通过I2C接口连接BME680气体,湿度,压力,温度传感器评估板记录环境信息,并显示在屏幕上。
三、硬件电路焊接
在之前帖子中已经详细介绍ESP32-S3主控板连接的各个传感器的焊接调试过程,在本贴中只展示一下ESP32-C6的两个串口焊接情况。如下图是焊接好的串口导线局部特写。
所有板子,传感器,屏幕等器件焊接好,并打胶加固后的效果如下图。
最终作品我计划使用硬纸板做个手枪型外壳,把这些模块全部套起来,方便使用。
四、软件编写
上一贴中介绍了如何将心电数据打包发送,此次不再赘述。此处介绍一下心电数据接收、解包过程。
为了不停从串口收数,我单开了一个任务,好在ESP32-S3有两个CPU内核,我让这个任务单独运行在另外一个内核上,防止影响屏幕刷新和热成像数据转换。
接收到的数据先是按照包头解包,然后通过数组转浮点数函数提取出来心电波形、心率、导联脱落三个独立的浮点数。其中心率和导联脱落只需简单判断一下合法性,就可以用于显示。但是心电波形是小数表达方式,需要映射成0-239范围内的整数,而且为了波形能占满全部波形区域,每显示一屏,还需要从新计算一下极值范围,保证波形不太小或太大。实际转换代码如下。
代码1:
#include <Arduino.h>
#include <ecg.h>
#include <lcd.h>
uint8_t ECG_data = 0;
uint16_t ECG_hr = 0;
bool ECG_lead = true;//导联脱落指示,true代表正常未脱落
bool ECG_flag = true;//心电数据更新指示,true代表更新了
float ecg_data = 0;
float ecg_data_max = 0;
float ecg_data_min = 0;
float ecg_data_max_temp = 0;
float ecg_data_min_temp = 0;
uint16_t ecg_data_num = 0;
float ecg_hr = 0;
float ecg_lead = 0;
uint8_t Rx_data = 0;
uint8_t Rx_buff[16] = {0};// only use 12 bytes
// float to 0,239
int map_ecg(float in, float a, float b)
{
if (in < a)
return 0;
if (in > b)
return 239;
if(b == a)
b = a + 1;
return (int)((in - a) * 239 / (b - a));
}
//函数说明:将4字节数据转成单精度浮点数据并存入指定地址
//附加说明:用户无需直接操作此函数
//target:目标单精度数据
//buf:待写入数组
//beg:指定从数组第几个元素开始写入
//函数无返回
void Byte2Float(float *target,unsigned char *buf,unsigned char beg)
{
unsigned char *point;
point = (unsigned char*)target; //得到float的地址
point[0] = buf[beg];
point[1] = buf[beg+1];
point[2] = buf[beg+2];
point[3] = buf[beg+3];
}
void task_ecg(void *ptr)
{
int i = 0;
Serial0.begin(1500000); //pin 43/44 to C6-ECG
Serial.println("uart0 init ok");
while (true)
{
if (Serial0.available() > 0)
{
// 读取串口数据
Rx_data = Serial0.read();
// Serial.println(Rx_data);
if('$' == Rx_data)//如果是包头
{
Rx_data = Serial0.read(Rx_buff,12);//连续读取12个字节
if(12 == Rx_data)
{
Byte2Float(&ecg_data,Rx_buff,0);
Byte2Float(&ecg_hr,Rx_buff,4);
Byte2Float(&ecg_lead,Rx_buff,8);
////////////////////
if(ecg_lead == 0)
{
ECG_lead = true;
}
else
{
ECG_lead = false;
}
/////////////////
if(ecg_hr != 0)
{
ECG_hr = int(ecg_hr);
}
//////////////////////
if(ecg_data_max_temp < ecg_data)
ecg_data_max_temp = ecg_data;
if(ecg_data_min_temp > ecg_data)
ecg_data_min_temp = ecg_data;
ecg_data_num++;
if(ecg_data_num > 480)
{
ecg_data_num = 0;
/////
ecg_data_max = ecg_data_max_temp;
ecg_data_min = ecg_data_min_temp;
ecg_data_max_temp = ecg_data;
ecg_data_min_temp = ecg_data;
// Serial.println(ecg_data_max);
// Serial.println(ecg_data_min);
}
ECG_data = map_ecg(ecg_data, ecg_data_min, ecg_data_max);
///////////////
ECG_flag = true;
}
}
}
vTaskDelay(1);
// ECG_flag = true;
// ECG_data = random(0, 239);
// ECG_hr = random(60, 120);
// if (ECG_hr > 110)
// ECG_lead = true;
// else
// ECG_lead = false;
}
vTaskDelete(NULL);
}
代码调试好后,整个系统只需要一个充电宝供电就能独立运行,下图是运行效果图。
实际操作体验效果见开头视频。
截止到此就基本完成了本次作品设计,后续整理材料编写作品文档和制作总结视频。
|