https://item.taobao.com/item.htm?spm=a1z09.2.0.0.6f0a2e8dl21TPE&id=573843692819&_u=o1qf7bf5d1ef
esp 手机网页心率工程
链接:https://pan.baidu.com/s/1Y7ZALvRQLYoICcAPXLvX0g?pwd=quva
提取码:quva
--来自百度网盘超级会员V5的分享





安装库
MAX30102传感器有多个库可用。 然而,在示例中,我们使用的是SparkFun Electronics提供的库。 该库公开了MAX30102的大部分特性,并提供简单易用的函数来计算脉率和SpO2。 您可以从Arduino IDE库管理器中下载此库。
要安装库,请导航到 Sketch > Include Library > Manage Libraries… 等待库管理器下载库索引并更新已安装库的列表。
输入MAX3010x筛选搜索结果。 查找SparkFun MAX3010x Pulse and Proximity Sensor Library。 单击该条目,然后选择安装。
MAX30102示例草图
SparkFun_MAX3010x库有许多示例草图。 您可以使用这些示例草图作为开发您自己的代码的基础。
要访问示例草图,请导航至File > Examples > SparkFun MAX3010x Pulse and Proximity Sensor Library。您将看到一系列示例草图。
<ignore_js_op>
测温 测心率 血氧气


#include <MAX3010x.h>
#include "filters.h"
// Sensor (adjust to your sensor type)
MAX30105 sensor;
const auto kSamplingRate = sensor.SAMPLING_RATE_400SPS;
const float kSamplingFrequency = 400.0;
// Finger Detection Threshold and Cooldown
const unsigned long kFingerThreshold = 10000;
const unsigned int kFingerCooldownMs = 500;
// Edge Detection Threshold (decrease for MAX30100)
const float kEdgeThreshold = -2000.0;
// Filters
const float kLowPassCutoff = 5.0;
const float kHighPassCutoff = 0.5;
// Averaging
const bool kEnableAveraging = true;
const int kAveragingSamples = 50;
const int kSampleThreshold = 5;
void setup() {
Serial.begin(9600);
if(sensor.begin() && sensor.setSamplingRate(kSamplingRate)) {
Serial.println("Sensor initialized");
}
else {
Serial.println("Sensor not found");
while(1);
}
}
// Filter Instances
HighPassFilter high_pass_filter(kHighPassCutoff, kSamplingFrequency);
LowPassFilter low_pass_filter(kLowPassCutoff, kSamplingFrequency);
Differentiator differentiator(kSamplingFrequency);
MovingAverageFilter<kAveragingSamples> averager;
// Timestamp of the last heartbeat
long last_heartbeat = 0;
// Timestamp for finger detection
long finger_timestamp = 0;
bool finger_detected = false;
// Last diff to detect zero crossing
float last_diff = NAN;
bool crossed = false;
long crossed_time = 0;
void loop() {
float temperature = sensor.readTemperature();
auto sample = sensor.readSample(1000);
float current_value = sample.red;
// Detect Finger using raw sensor value
if(sample.red > kFingerThreshold) {
if(millis() - finger_timestamp > kFingerCooldownMs) {
finger_detected = true;
}
}
else {
// Reset values if the finger is removed
differentiator.reset();
averager.reset();
low_pass_filter.reset();
high_pass_filter.reset();
finger_detected = false;
finger_timestamp = millis();
}
if(finger_detected) {
current_value = low_pass_filter.process(current_value);
current_value = high_pass_filter.process(current_value);
float current_diff = differentiator.process(current_value);
// Valid values?
if(!isnan(current_diff) && !isnan(last_diff)) {
// Detect Heartbeat - Zero-Crossing
if(last_diff > 0 && current_diff < 0) {
crossed = true;
crossed_time = millis();
}
if(current_diff > 0) {
crossed = false;
}
// Detect Heartbeat - Falling Edge Threshold
if(crossed && current_diff < kEdgeThreshold) {
if(last_heartbeat != 0 && crossed_time - last_heartbeat > 300) {
// Show Results
int bpm = 60000/(crossed_time - last_heartbeat);
if(bpm > 50 && bpm < 250) {
// Average?
if(kEnableAveraging) {
int average_bpm = averager.process(bpm);
// Show if enough samples have been collected
if(averager.count() > kSampleThreshold) {
Serial.print("Heart Rate (avg, bpm): ");
Serial.println(average_bpm);
Serial.print("temperatureC=");
Serial.println(temperature, 2);
}
}
else {
Serial.print("Heart Rate (current, bpm): ");
Serial.println(bpm);
Serial.print("temperatureC=");
Serial.println(temperature, 2);
}
}
}
crossed = false;
last_heartbeat = crossed_time;
}
}
last_diff = current_diff;
}
}
filters.h
#ifndef FILTERS_H
#define FILTERS_H
/**
* @brief High Pass Filter
*/
class HighPassFilter {
const float kX;
const float kA0;
const float kA1;
const float kB1;
float last_filter_value_;
float last_raw_value_;
public:
/**
* @brief Initialize the High Pass Filter
* @param samples Number of samples until decay to 36.8 %
* @remark Sample number is an RC time-constant equivalent
*/
HighPassFilter(float samples) :
kX(exp(-1/samples)),
kA0((1+kX)/2),
kA1(-kA0),
kB1(kX),
last_filter_value_(NAN),
last_raw_value_(NAN){}
/**
* @brief Initialize the High Pass Filter
* @param cutoff Cutoff frequency
* @pram sampling_frequency Sampling frequency
*/
HighPassFilter(float cutoff, float sampling_frequency) :
HighPassFilter(sampling_frequency/(cutoff*2*PI)){}
/**
* @brief Applies the high pass filter
*/
float process(float value) {
if(isnan(last_filter_value_) || isnan(last_raw_value_)) {
last_filter_value_ = 0.0;
}
else {
last_filter_value_ =
kA0 * value
+ kA1 * last_raw_value_
+ kB1 * last_filter_value_;
}
last_raw_value_ = value;
return last_filter_value_;
}
/**
* @brief Resets the stored values
*/
void reset() {
last_raw_value_ = NAN;
last_filter_value_ = NAN;
}
};
/**
* @brief Low Pass Filter
*/
class LowPassFilter {
const float kX;
const float kA0;
const float kB1;
float last_value_;
public:
/**
* @brief Initialize the Low Pass Filter
* @param samples Number of samples until decay to 36.8 %
* @remark Sample number is an RC time-constant equivalent
*/
LowPassFilter(float samples) :
kX(exp(-1/samples)),
kA0(1-kX),
kB1(kX),
last_value_(NAN){}
/**
* @brief Initialize the Low Pass Filter
* @param cutoff Cutoff frequency
* @pram sampling_frequency Sampling frequency
*/
LowPassFilter(float cutoff, float sampling_frequency) :
LowPassFilter(sampling_frequency/(cutoff*2*PI)){}
/**
* @brief Applies the low pass filter
*/
float process(float value) {
if(isnan(last_value_)) {
last_value_ = value;
}
else {
last_value_ = kA0 * value + kB1 * last_value_;
}
return last_value_;
}
/**
* @brief Resets the stored values
*/
void reset() {
last_value_ = NAN;
}
};
/**
* @brief Differentiator
*/
class Differentiator {
const float kSamplingFrequency;
float last_value_;
public:
/**
* @brief Initializes the differentiator
*/
Differentiator(float sampling_frequency) :
kSamplingFrequency(sampling_frequency),
last_value_(NAN){}
/**
* @brief Applies the differentiator
*/
float process(float value) {
float diff = (value-last_value_)*kSamplingFrequency;
last_value_ = value;
return diff;
}
/**
* @brief Resets the stored values
*/
void reset() {
last_value_ = NAN;
}
};
/**
* @brief MovingAverageFilter
* @tparam buffer_size Number of samples to average over
*/
template<int kBufferSize> class MovingAverageFilter {
int index_;
int count_;
float values_[kBufferSize];
public:
/**
* @brief Initalize moving average filter
*/
MovingAverageFilter() :
index_(0),
count_(0){}
/**
* @brief Applies the moving average filter
*/
float process(float value) {
// Add value
values_[index_] = value;
// Increase index and count
index_ = (index_ + 1) % kBufferSize;
if(count_ < kBufferSize) {
count_++;
}
// Calculate sum
float sum = 0.0;
for(int i = 0; i < count_; i++) {
sum += values_[i];
}
// Calculate average
return sum/count_;
}
/**
* @brief Resets the stored values
*/
void reset() {
index_ = 0;
count_ = 0;
}
/**
* @brief Get number of samples
* @return Number of stored samples
*/
int count() const {
return count_;
}
};
#endif // FILTERS_H
浙公网安备 33010602011771号