sipower 发表于 2024-10-20 21:25

【DigiKey创意大赛】便携生命探测仪06+各模块整合联调

<div>d3ebd2761ecd76c87286f8e09232ebab<br />
&nbsp;</div>

<div>上一帖介绍了如何获取心电数据并解析协议,最终显示在电脑上,实现了心电采集功能。本帖介绍如何把所有模块整合到一起,实现预期的作品功能。</div>

<h4>一、预期功能</h4>

<div>做一个便携生命探测仪,在发生危险时,能帮助救援人员方便找到遇险者。此方案主要采用热敏式图像传感器MLX90640ESF-BAB-000-TU检测人体红外信号,采用BME680气体,湿度,压力,温度传感器评估板记录环境信息,配合一个带屏幕的评估板,显示热成像图片。再额外添加一个心电采集模块,当发现遇险者后,能立即给遇险者采集心电图和心率并在屏幕上显示,方便救援人员判断生命体征状态,好制定下一步救援计划。</div>

<h4>二、系统框图</h4>

<div>整个系统框图如下图。</div>

<div></div>

<div>如上图所示,ESP32-S3评估板作为主控板,通过SPI接口连接一片480*320分辨率的触摸屏,用来显示各个检测数据、图像、心电波形。通过串口连接一片ESP32-C6评估板。ESP32-C6负责通过串口从LH001-91心电评估板接收字符串数据,然后选择心电波形、心率、导联脱落数据转换成浮点数,再从另外一个串口发给主控板。通过I2C接口连接热敏式图像传感器MLX90640ESF-BAB-000-TU检测人体红外信号,转换成热成像实时图片刷新到显示屏。通过I2C接口连接BME680气体,湿度,压力,温度传感器评估板记录环境信息,并显示在屏幕上。</div>

<h4>三、硬件电路焊接</h4>

<div>在之前帖子中已经详细介绍ESP32-S3主控板连接的各个传感器的焊接调试过程,在本贴中只展示一下ESP32-C6的两个串口焊接情况。如下图是焊接好的串口导线局部特写。</div>

<div></div>

<div>所有板子,传感器,屏幕等器件焊接好,并打胶加固后的效果如下图。</div>

<div></div>

<div>最终作品我计划使用硬纸板做个手枪型外壳,把这些模块全部套起来,方便使用。</div>

<h4>四、软件编写</h4>

<div>上一贴中介绍了如何将心电数据打包发送,此次不再赘述。此处介绍一下心电数据接收、解包过程。</div>

<div>为了不停从串口收数,我单开了一个任务,好在ESP32-S3有两个CPU内核,我让这个任务单独运行在另外一个内核上,防止影响屏幕刷新和热成像数据转换。</div>

<div>接收到的数据先是按照包头解包,然后通过数组转浮点数函数提取出来心电波形、心率、导联脱落三个独立的浮点数。其中心率和导联脱落只需简单判断一下合法性,就可以用于显示。但是心电波形是小数表达方式,需要映射成0-239范围内的整数,而且为了波形能占满全部波形区域,每显示一屏,还需要从新计算一下极值范围,保证波形不太小或太大。实际转换代码如下。</div>

<div>代码1:</div>

<div>
<pre>
<code class="language-cpp">#include &lt;Arduino.h&gt;
#include &lt;ecg.h&gt;
#include &lt;lcd.h&gt;

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 = {0};// only use 12 bytes

// float to 0,239
int map_ecg(float in, float a, float b)
{
    if (in &lt; a)
      return 0;

    if (in &gt; 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 = buf;
    point = buf;
    point = buf;
    point = buf;
}


void task_ecg(void *ptr)
{
    int i = 0;
    Serial0.begin(1500000);//pin 43/44to C6-ECG
    Serial.println("uart0 init ok");
    while (true)
    {
      if (Serial0.available() &gt; 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(&amp;ecg_data,Rx_buff,0);
                  Byte2Float(&amp;ecg_hr,Rx_buff,4);
                  Byte2Float(&amp;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 &lt; ecg_data)
                        ecg_data_max_temp = ecg_data;
                  if(ecg_data_min_temp &gt; ecg_data)
                        ecg_data_min_temp = ecg_data;

                  ecg_data_num++;
                  if(ecg_data_num &gt; 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 &gt; 110)
      //   ECG_lead = true;
      // else
      //   ECG_lead = false;
    }
    vTaskDelete(NULL);
}</code></pre>

<p>&nbsp;</p>
</div>

<div>代码调试好后,整个系统只需要一个充电宝供电就能独立运行,下图是运行效果图。</div>

<div></div>

<div>实际操作体验效果见开头视频。</div>

<div>截止到此就基本完成了本次作品设计,后续整理材料编写作品文档和制作总结视频。</div>

<p>&nbsp;
<p><!--importdoc--></p>
</p>

wangerxian 发表于 2024-10-21 15:32

<p>我感觉那个心电图可以画的频率低一些,要不显示的不是很好看。</p>

<p> &nbsp;</p>

sipower 发表于 2024-10-24 00:03

wangerxian 发表于 2024-10-21 15:32
我感觉那个心电图可以画的频率低一些,要不显示的不是很好看。

&nbsp;

<p>是的,这个采样率低,处理的时候还丢点了,心电波就显得比较密集,要是想显示清楚,需要增加采样点和MCU处理能力,目前选的这个板板改起来比较费劲,当前这个波形主要是看心律情况。</p>

wangerxian 发表于 2024-10-24 09:02

sipower 发表于 2024-10-24 00:03
是的,这个采样率低,处理的时候还丢点了,心电波就显得比较密集,要是想显示清楚,需要增加采样点和MCU ...

<p>那确实,采样率不够,心电波形就会有点问题。</p>

sipower 发表于 2024-10-24 10:39

wangerxian 发表于 2024-10-24 09:02
那确实,采样率不够,心电波形就会有点问题。

<p>对于单导的心电,主要还是看心律,计算心律不齐、室早房早啥的,看形态的少些。</p>

wangerxian 发表于 2024-10-24 16:13

sipower 发表于 2024-10-24 10:39
对于单导的心电,主要还是看心律,计算心律不齐、室早房早啥的,看形态的少些。

<p>嗯嗯,单导只能看心率,我以前问过医生,单导其实看不出什么东西。</p>

sipower 发表于 2024-10-28 09:03

wangerxian 发表于 2024-10-24 16:13
嗯嗯,单导只能看心率,我以前问过医生,单导其实看不出什么东西。

<p>感觉您也是专业人士哈</p>

wangerxian 发表于 2024-10-28 09:06

sipower 发表于 2024-10-28 09:03
感觉您也是专业人士哈

<p>只是之前研究过这方面的技术。</p>
页: [1]
查看完整版本: 【DigiKey创意大赛】便携生命探测仪06+各模块整合联调