Arduino 心率带测试

目前市面上能够测量心率的设备很多。有腕带腕表式的,也有夹在耳朵或者手指末端的。从准确性上来说,腕带式的容易松动因此没有胸带式的准确。同时,胸带式的对于运动统计来说也是最好的选择。

前一段入手了三根心率带和一个接收模块。其中的心率带是带有编码的,因此在接收端可以很容易的区分数据来源。当然,与之对应的还有不带编码的心率带,无法在多个的情况下使用。

先说说心率带模块:

  1. 内部使用 CR2032 纽扣电池,据说每天工作1小时可以撑9个月;
  2. 发送频率为 2.4Ghz;
  3. 平时处于睡眠模式,戴上之后才开始发送数据
  4. 发射数据每次 4 Bytes,3 Bytes ID+ 1 Byte 心率。

接收模块:

  1. 模块三个引脚,GND  VCC (特别强调是 3.3V供电) TX 。使用串口通讯。波特率 9600 bps。比如:DD 20 03 04 50(个人感觉有点低,设想如果很多根在同一个空间内使用不知道会有什么问题);
  2. 上面有一个LED,当收到有效数据后会闪动一次;
  3. 板子上的孔间距是 2.0mm 不是 2.54,使用杜邦线会很别扭,我直接焊接上导线来使用;

下面是一个完整的例子,使用 Arduino Leonardo 接收数据,将收到的数据总结为 A + 每分钟心跳数(bpm),或者  B +每分钟心跳数上传到USB串口,然后在上位机上显示出来。试验中使用的心率带心率带 A  ID :22 05 28   心率带 B  ID :22 06 58

  1. Arduino代码
void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
}

boolean status1=false;
boolean status2=false;
boolean status3=false;
byte    addr[3];
byte    pos=0;
char    outadd;
void loop() {
  byte c;
  while (Serial1.available()) 
   {
      c=Serial1.read()&0xFF;
           Serial.write(c);
      if ((status1==false)&&(c==0xdd)) {
            status1=true;
            status2=true;
        //         Serial.print("st1");
        }
      else if (status2) {
            addr[pos]=c;
            pos++;
            if (pos==3) {
                 status2=false;
                 status3=true;   
              }
          //  Serial.print("st2");  
        }
      else if (status3) {
              //Serial.print("st3");
              if ((addr[0]==0x22)&&(addr[1]==0x05)&&(addr[2]==0x28)) {
                 Serial.write('A');
                 Serial.write(c);
                 Serial.println("");
               }
              else
              if ((addr[0]==0x22)&&(addr[1]==0x06)&&(addr[2]==0x58)) {
                 Serial.write('B');
                 Serial.write(c);
                 Serial.println("");
               }
             status1=false;
             status2=false;
             status3=false;
             pos=0;  
     }   
   } //while

  • C# 编写上位机程序,在界面上绘制当前心率曲线

完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace WindowsFormsApplication9
{
    public partial class Form1 : Form
    {
        private Queue<double> dataQueue = new Queue<double>(100);
        private Queue<double> dataQueue2 = new Queue<double>(100);

        private int num = 5;//每次删除增加几个点
        private int heartA;
        private int heartB;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //textBox1.AppendText("start");
            Form2 f2 = new Form2();
            f2.ShowDialog();
            serialPort1.PortName = "COM" + f2.SelectedPort.ToString();
           // MessageBox.Show(f2.SelectedPort.ToString());
            button3.Enabled = true;
            button4.Enabled = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //设置窗体为无边框样式
            this.FormBorderStyle = FormBorderStyle.None;
            //最大化窗体    
            this.WindowState = FormWindowState.Maximized;     

            InitChart();
            chart1.Width = this.Width - 20;
            chart1.Height = this.Height - 100;
            button3.Enabled = false;
            button4.Enabled = false;

            //如果没有这句话,串口收到的只有0-128 的 ASCII
            serialPort1.Encoding = System.Text.Encoding.GetEncoding(28591);
            serialPort1.DtrEnable = true;
            serialPort1.RtsEnable = true;
        }

        /// <summary>
        /// 初始化图表
        /// </summary>
        private void InitChart()
        {
            //定义图表区域
            this.chart1.ChartAreas.Clear();
            ChartArea chartArea1 = new ChartArea("C1");
            this.chart1.ChartAreas.Add(chartArea1);
            //定义存储和显示点的容器
            this.chart1.Series.Clear();
            Series series1 = new Series("S1");
            series1.ChartArea = "C1";
            this.chart1.Series.Add(series1);

            Series series2 = new Series("S2");
            this.chart1.Series.Add(series2);
            //设置图表显示样式
            this.chart1.ChartAreas[0].AxisY.Minimum = 50;
            this.chart1.ChartAreas[0].AxisY.Maximum = 170;
            this.chart1.ChartAreas[0].AxisY.Interval = 10;
            this.chart1.ChartAreas[0].AxisX.Interval = 5;
            this.chart1.ChartAreas[0].AxisX.MajorGrid.LineColor = System.Drawing.Color.Silver;
            this.chart1.ChartAreas[0].AxisY.MajorGrid.LineColor = System.Drawing.Color.Silver;
            //设置标题
            this.chart1.Titles.Clear();
            this.chart1.Titles.Add("Red");
            this.chart1.Titles[0].Text = "Heart";
            this.chart1.Titles[0].Font = new System.Drawing.Font("Microsoft Sans Serif", 12F);
            this.chart1.Titles[0].ForeColor = Color.Red;
            //设置图表显示样式
            this.chart1.Series[0].Color = Color.Red;
            this.chart1.Series[0].ChartType = SeriesChartType.Line;
            this.chart1.Series[0].Points.Clear();

            this.chart1.Series[1].Color = Color.Blue;
            this.chart1.Series[1].ChartType = SeriesChartType.Line;

        }

        private void button2_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            UpdateQueueValue();
            this.chart1.Series[0].Points.Clear();
            this.chart1.Series[1].Points.Clear();
            for (int i = 0; i < dataQueue.Count; i++)
            {
                this.chart1.Series[0].Points.AddXY((i + 1), dataQueue.ElementAt(i));
                this.chart1.Series[1].Points.AddXY((i + 1), dataQueue2.ElementAt(i));
            }
        }

        //更新队列中的值
        private void UpdateQueueValue()
        {
            int heart=0;
            if (dataQueue.Count >= 100)
            {
                //先出列
                for (int i = 0; i < num; i++)
                {
                    dataQueue.Dequeue();
                    dataQueue2.Dequeue();
                }
            }

                //Random r = new Random();
                for (int i = 0; i < num; i++)
                {
                    //heart = r.Next(50, 170);
                    dataQueue.Enqueue(heartA);
                    //heart = r.Next(50, 170);
                    dataQueue2.Enqueue(heartB);
                }

                chart1.Titles[0].Text = "Heart"+ heart.ToString();

        }

        private void button3_Click(object sender, EventArgs e)
        {
            this.serialPort1.Open();
            this.timer1.Enabled = true;
            this.timer2.Enabled = true;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            this.timer1.Enabled = false;
            this.timer2.Enabled = false;
            serialPort1.Close();
            this.timer1.Stop();
            this.timer2.Stop();
        }

        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {

        }

        private void timer2_Tick(object sender, EventArgs e)
        {
            if (serialPort1.BytesToRead!=0) {
                
                String s=serialPort1.ReadLine();
                if (s.Length>=2) { 
                if (s[0] == 'A') {
                        heartA = s[1]; // Convert.ToInt32(s[1]);
                    textBox1.AppendText(heartA.ToString());
                }
                else
                    if (s[0] == 'B')
                      {
                        heartB = s[1];
                        //textBox1.AppendText(heartB.ToString("X2"));
                }
                }
            }
        }
    }
}

运行结果:

工作的视频在  https://zhuanlan.zhihu.com/p/56291800

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注