arduino esp32 通过局域网控制和获取max30102心率血氧代码

@Ta 2023-04-01发布,2023-04-01修改 59点击

代码都是用ai和网上教程做的,用esp32c3测试能用,目前可以通过局域网控制开关,通过WebSocket读取数据,还可以在本地局域网自建echarts网页,用WebSocket获取实时心率血氧图表。

现在就是检测的心率和血氧不准,我还看不懂是哪里的问题...

微信截图_20230401171305.png(95.19 KB)微信截图_20230401171412.png(79.39 KB)微信截图_20230401170915.png(88.57 KB)微信图片_20230401172215.jpg(287.26 KB)

arduino代码:

#include <Wire.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"
#include <WiFi.h> // 引入WiFi 库
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>

AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
// WiFi 参数
const char* ssid = "wifi名";
const char* password = "WiFi密码";


//WiFiServer server(80); // 在端口80上创建一个web服务器

MAX30105 particleSensor;

#define MAX_BRIGHTNESS 255

#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
//Arduino Uno不具备足够的SRAM来存储32位格式的50个红外LED数据和红色LED数据。
//为了解决这个问题,采样数据的16位MSB将被截断。样品变成16位数据。
uint16_t irBuffer[50]; //红外LED传感器数据
uint16_t redBuffer[50];  //红色LED传感器数据
#else
uint32_t irBuffer[50]; //红外LED传感器数据
uint32_t redBuffer[50];  //红色LED传感器数据
#endif

int32_t spo2; //SPO2值
int8_t validSPO2; //指示SPO2计算是否有效
int32_t heartRate; //心率值
int8_t validHeartRate; //指示心率计算是否有效

bool sensorEnabled = false; //标志位,表示传感器是否启用

void setup()
{
  Serial.begin(115200); // 以115200比特/秒初始化串行通信:

  // 初始化WIFI连接
  Serial.println();
  Serial.println();
  Serial.print("连接到 ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi 连接成功");
  Serial.println(WiFi.localIP());


  // 初始化Web服务器
  server.begin();
  Serial.println("Web服务器已启动");

  // 初始化传感器
  Wire.begin(4, 5); // SDA = GPIO 4, SCL = GPIO 5
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //使用默认的I2C端口,400kHz的速度
  {
    Serial.println(F("未找到MAX30105。 请检查布线/电源。"));
    while (1);
  }

  particleSensor.setup(55, 4, 2, 200, 411, 4096); //使用这些设置配置传感器

  // 初始化结束后关闭传感器模块
  particleSensor.shutDown();
  sensorEnabled = false;
  
// 启动 Web 服务器
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String content = "<html><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta charset=\"UTF-8\">";
    content += "<head><title>MAX30102 心率检测</title></head>";
    content += "<body>";
    content += "<h1>MAX30102 心率检测</h1>";
    content += "<div id='data-box'></div>";  //增加数据展示框
    content += "<p>当前状态: " + String(sensorEnabled ? "已打开" : "已关闭") + "</p>";
    if (sensorEnabled) {
      content += "<a href=\"/setSensorState?state=off\"><button class=\"button\">关闭模块</button></a>";
    } else {
      content += "<a href=\"/setSensorState?state=on\"><button class=\"button\">开启模块</button></a>";
    }
    //增加WebSocket连接
    content += "<script>var socket = new WebSocket('ws://' + window.location.hostname + ':80/ws');";
    content += "socket.onmessage = function(event) { document.getElementById('data-box').innerHTML = event.data; };";
    content += "</script>";
    content += "</body>";
    content += "</html>";
    request->send(200, "text/html", content);
  });

  server.on("/setSensorState", HTTP_GET, [](AsyncWebServerRequest *request){
    if (request->hasParam("state")) {
      String state = request->getParam("state")->value();
      if (state == "on" && !sensorEnabled) {
        // 打开传感器模块
        particleSensor.wakeUp();
        delay(500);
        sensorEnabled = true;
        Serial.print(F("打开"));
      } else if (state == "off" && sensorEnabled) {
        // 关闭传感器模块
        shutDownSensorModule();
        Serial.print(F("关闭"));
      }
    }
    request->redirect("/");
  });
  
  //启动WebSocket服务
  ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len){
    if(type == WS_EVT_CONNECT){
      Serial.println("WebSocket client connected");
    } else if(type == WS_EVT_DISCONNECT){
      Serial.println("WebSocket client disconnected");
    }
  });
  
  server.addHandler(&ws);

  server.begin();
  
}

long lastActiveTime = 0;

void loop()
{

  // 检查传感器是否需要关闭
  if (sensorEnabled && (millis() - lastActiveTime > 60000)) {
    shutDownSensorModule();
  }

  //读取前50个样本,并确定信号范围
  for (byte i = 0 ; i < 50 ; i++)
  {
    while (particleSensor.available() == false) //是否有新数据?
      particleSensor.check(); //检查传感器是否有新数据

    redBuffer[i] = particleSensor.getRed();
    irBuffer[i] = particleSensor.getIR();
    particleSensor.nextSample(); //处理完当前样本后,转到下一个样本
    Serial.print(F("红光="));
    Serial.print(redBuffer[i], DEC);
    Serial.print(F(", 红外="));
    Serial.println(irBuffer[i], DEC);
  }

  //在前50个样本之后计算心率和SpO2(前4秒的样本)
  maxim_heart_rate_and_oxygen_saturation(irBuffer, 50, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

  //无线循环地从MAX30102中采样。每1秒计算一次心率和SpO2
  while (1)
  {
    //将内存中的前25个样本丢弃,并将最后25个样本移动到顶部
    for (byte i = 25; i < 50; i++)
    {
      redBuffer[i - 25] = redBuffer[i];
      irBuffer[i - 25] = irBuffer[i];
    }

    //在计算心率之前,采取25个样本集。
    for (byte i = 25; i < 50; i++)
    {
      while (particleSensor.available() == false) //是否有新数据?
        particleSensor.check(); //检查传感器是否有新数据

      redBuffer[i] = particleSensor.getRed();
      irBuffer[i] = particleSensor.getIR();
      particleSensor.nextSample(); //处理完当前样本后,转到下一个样本
      Serial.print(F("红光="));
      Serial.print(redBuffer[i], DEC);
      Serial.print(F(", 红外="));
      Serial.print(irBuffer[i], DEC);

      Serial.print(F(", 心率="));
      Serial.print(heartRate, DEC);

      Serial.print(F(", HRvalid="));
      Serial.print(validHeartRate, DEC);

      Serial.print(F(", 血氧饱和度="));
      Serial.print(spo2, DEC);

      Serial.print(F(", SPO2Valid="));
      Serial.println(validSPO2, DEC);
 
    }

    //收集25个新样本后,重新计算HR和SP02
    maxim_heart_rate_and_oxygen_saturation(irBuffer, 50, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
    sendDataToWebSocket();  //将数据推送到客户端
  }
}

void sendDataToWebSocket() {
  if(validSPO2 && validHeartRate) {
    String data = "{\"心率\":" + String(heartRate, DEC) + ",\"血氧饱和度\":" + String(spo2, DEC) + "}";
    ws.textAll(data);  //调用WebSocket推送数据到客户端
  } else {
    Serial.println(F("无效"));
  }
}



void printToSerial() {
  if(validSPO2 && validHeartRate) {
    Serial.print(F("心率: ")); Serial.println(heartRate, DEC);
    Serial.print(F("血氧饱和度: ")); Serial.println(spo2, DEC);
  } else {
    Serial.println(F("无效"));
  }
}

// 如果需要关闭传感器模块,则可以使用以下函数
void shutDownSensorModule() {
  if (sensorEnabled) {
    particleSensor.shutDown();
    sensorEnabled = false;
  }
}

局域网echarts心率血氧实时监测页面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>心率血氧实时监测</title>
    <script src="../sjwsd/style/echarts.min.js"></script>
<script src="../sjwsd/style/echarts.common.min.js"></script>
</head>
<body>
    <div id="chart" style="width: 800px; height: 600px;"></div>
<script>
    // 初始化 ECharts 实例
    var myChart = echarts.init(document.getElementById('chart'));

    // 定义初始数据
    var heartRateData = [];
    var spo2Data = [];
    var timestampData = [];

    // 初始化图表参数
    myChart.setOption({
        title: { text: '心率血氧实时监测' },
        tooltip: { trigger: 'axis' },
        legend: { data: ['心率', '血氧饱和度'] },
        xAxis: {
            type: 'category',
            boundaryGap: false,
            data: []
        },
        yAxis: [
            {
                type: 'value',
                name: '心率'
            },
            {
                type: 'value',
                name: '血氧饱和度'
            }
        ],
        series: [
            {
                name: '心率',
                type: 'line',
                yAxisIndex: 0,
                showSymbol: false,
                hoverAnimation: false,
                data: heartRateData
            },
            {
                name: '血氧饱和度',
                type: 'line',
                yAxisIndex: 1,
                showSymbol: false,
                hoverAnimation: false,
                data: spo2Data
            }
        ]
    });

    // 建立 WebSocket 连接
    var socket = new WebSocket('ws://192.168.41.10/ws');

    // 接收并处理从后端发送来的新数据,并更新图表
    socket.onmessage = function (event) {
        var data = JSON.parse(event.data);
        var timestamp = new Date().toLocaleTimeString('en-US', { hour12: false });
        timestampData.push(timestamp);
        heartRateData.push(data.心率);
        spo2Data.push(data.血氧饱和度);
        if (timestampData.length >= 30) {
            timestampData.shift();
            heartRateData.shift();
            spo2Data.shift();
        }
        myChart.setOption({
            xAxis: {
                data: timestampData
            },
            series: [
                {
                    data: heartRateData
                },
                {
                    data: spo2Data
                }
            ]
        });
    };
</script>
</body>
</html>

手机

回复列表(4|隐藏机器人聊天)
  • @Ta / 2023-04-01 / /
    基于esp32的chatgpt语音助手

    视频链接:VID_20230401_001318.mp4(47.16 MB)

  • @Ta / 2023-04-01 / /

    @冰封,有意思,壳子也挺好看
    手机

  • @Ta / 2023-04-01 / /

    服了我靠,把串口调试器开着运行一切正常,秒出数据,串口调试器关掉后数据就要一分钟以上才出来一条
    手机

  • @Ta / 2023-04-02 / /
    我第一眼把这个aardio看成了aiduino

    视频链接



    来自【aardio编程语言作者:因妻子患癌,再无精力维护项目-哔哩哔哩】 https://b23.tv/fsU67Na


添加新回复
回复需要登录