Bu eğitim, Seeed tarafından Wio Terminal でパルスオキシメータを制作 adlı yazıdan çevrilmiştir. Yazı homemadegarbage tarafından yazılmıştır (twitter: @H0meMadeGarbage). Bunu bizimle paylaştıkları için teşekkür ederiz! Daha ilginç projeler için ana sayfalarını ziyaret edin: homemadegarbage.com.
Bu, Wio Terminal ve MAX30105 sensörünü kullanarak bir pulse oximeter nasıl yapılır hakkında basit bir eğitimdir.
MAX30105, kırmızı, yeşil ve kızılötesi LED’ler ile yüksek hassasiyetli bir foton dedektörü ile donatılmış bir modüldür ve toz ölçümü veya kan oksijen doygunluğu ölçümü (SpO2) için kullanılabilir.
1. MAX30105’i Wio Terminal’e Bağlama
MAX30105’i Wio Terminal üzerindeki 4 pinli Grove konektörüne bağlayın.

2. Pulse Oximeter’ı Kurma
MAX30105 kütüphanesini Github‘da kontrol edebilirsiniz. Diğer örnek kodlar aşağıda listelenmiştir.
#include <Wire.h>
#include "MAX30105.h"
#include"seeed_line_chart.h" //kütüphaneyi dahil et
#include "spo2_algorithm.h"
MAX30105 particleSensor;
TFT_eSPI tft;
long baseValue = 0;
long HB = 0, oldHB = 0;
int diffHB = 0;
int state = 0;
int th = -500;
#define max_size 50 //veri için maksimum boyut
doubles data; //veri depolamak için doubles türünde başlatma
TFT_eSprite spr = TFT_eSprite(&tft); // Sprite
long lastBeat = 0; //Son atışın gerçekleştiği zaman
const byte RATE_SIZE = 4; //Daha fazla ortalama almak için bunu artırın. 4 iyi.
byte rates[RATE_SIZE]; //Kalp atış hızı dizisi
byte rateSpot = 0;
float beatsPerMinute;
int beatAvg;
long delta;
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
//Arduino Uno, 32-bit formatında 100 örnek IR led verisi ve kırmızı led verisi depolamak için yeterli SRAM'e sahip değildir
//Bu sorunu çözmek için, örneklenen verilerin 16-bit MSB'si kesilecektir. Örnekler 16-bit veri haline gelir.
uint16_t irBuffer[100]; //kızılötesi LED sensör verisi
uint16_t redBuffer[100]; //kırmızı LED sensör verisi
#else
uint32_t irBuffer[100]; //kızılötesi LED sensör verisi
uint32_t redBuffer[100]; //kırmızı LED sensör verisi
#endif
int32_t bufferLength; //veri uzunluğu
int32_t spo2; //SPO2 değeri
int8_t validSPO2; //SPO2 hesaplamasının geçerli olup olmadığını gösteren gösterge
int32_t heartRate; //kalp atış hızı değeri
int8_t validHeartRate; //kalp atış hızı hesaplamasının geçerli olup olmadığını gösteren gösterge
void setup() {
pinMode(WIO_BUZZER, OUTPUT);
tft.begin();
tft.setRotation(3);
spr.createSprite(TFT_HEIGHT,TFT_WIDTH);
Serial.begin(115200);
Serial.println("Başlatılıyor...");
// Sensörü başlat
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Varsayılan I2C portunu kullan, 400kHz hızı
{
Serial.println("MAX30105 bulunamadı. Lütfen kabloları/gücü kontrol edin.");
while (1);
}
particleSensor.setup(); //Varsayılan ayarlarla sensörü yapılandır
particleSensor.setPulseAmplitudeRed(20); //Sensör çalıştığını göstermek için Kırmızı LED'i düşük seviyeye getir
particleSensor.setPulseAmplitudeGreen(0); //Yeşil LED'i kapat
}
void loop() {
bufferLength = 100; //100'lük tampon uzunluğu, 25sps hızında 4 saniyelik örnekleri depolar
//ilk 100 örneği oku ve sinyal aralığını belirle
for (byte i = 0 ; i < bufferLength ; i++)
{
while (particleSensor.available() == false) //yeni veri var mı?
particleSensor.check(); //Yeni veri için sensörü kontrol et
redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample(); //Bu örneği tamamladık, bu yüzden bir sonraki örneğe geç
Serial.print(F("kırmızı="));
Serial.print(redBuffer[i], DEC);
Serial.print(F(", ir="));
Serial.println(irBuffer[i], DEC);
}
//ilk 100 örnekten sonra kalp atış hızını ve SpO2'yi hesapla (ilk 4 saniyelik örnekler)
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
//MAX30102'den sürekli örnekler al. Kalp atış hızı ve SpO2 her 1 saniyede bir hesaplanır
while (1)
{
//hafızadaki ilk 25 örneği boşalt ve son 75 örneği yukarı kaydır
for (byte i = 25; i < 100; i++)
{
redBuffer[i - 25] = redBuffer[i];
irBuffer[i - 25] = irBuffer[i];
display();
}
//kalp atış hızını hesaplamadan önce 25 örnek al.
for (byte i = 75; i < 100; i++)
{
while (particleSensor.available() == false) //yeni veri var mı?
particleSensor.check(); //Yeni veri için sensörü kontrol et
redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample(); //Bu örneği tamamladık, bu yüzden bir sonraki örneğe geç
//örnekleri ve hesaplama sonucunu terminal programına UART üzerinden gönder
Serial.print(F("kırmızı="));
Serial.print(redBuffer[i], DEC);
Serial.print(F(", ir="));
Serial.print(irBuffer[i], DEC);
Serial.print(F(", HR="));
Serial.print(heartRate, DEC);
Serial.print(F(", HRgeçerli="));
Serial.print(validHeartRate, DEC);
Serial.print(F(", SPO2="));
Serial.print(spo2, DEC);
Serial.print(F(", SPO2Geçerli="));
Serial.println(validSPO2, DEC);
display();
}
//25 yeni örnek toplandıktan sonra HR ve SP02'yi yeniden hesapla
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
}
}
void display() {
spr.fillSprite(TFT_WHITE);
if (data.size() == max_size) {
data.pop();//ilk okunan değişkeni kaldırmak için kullanılır
}
HB = particleSensor.getIR();
diffHB = HB - oldHB;
data.push(diffHB); //değişkenleri oku ve veriye depola
if(state == 0 && diffHB < th){
delta = millis() - lastBeat;
lastBeat = millis();
beatsPerMinute = 60 / (delta / 1000.0);
rates[rateSpot++] = (byte)beatsPerMinute; //Bu okumayı dizide depola
rateSpot %= RATE_SIZE; //Değişkeni sar
//Okumaların ortalamasını al
beatAvg = 0;
for (byte x = 0 ; x < RATE_SIZE ; x++)
beatAvg += rates[x];
beatAvg /= RATE_SIZE;
Serial.println(beatAvg);
state = 1;
analogWrite(WIO_BUZZER, 128);
Serial.println("Atış!!!!!!!");
}else if(state == 1 && diffHB > th){
state = 0;
analogWrite(WIO_BUZZER, 0);
}
//SpO2 için ayarlar
String stSpO2 = " SpO2:";
if(validSPO2){
stSpO2 += spo2;
}else{
stSpO2 += "-";
}
char charSpO2[20];
stSpO2.toCharArray(charSpO2, 20);
auto header = text(0, 0)
.value(charSpO2)
.align(left)
.valign(vcenter)
.width(tft.width())
.thickness(3);
header.height(header.font_height() * 2);
header.draw(); //Başlık yüksekliği, fontun yüksekliğinin iki katıdır
//Kalp Atış Hızı için ayarlar
String stHB = " Kalp Atış Hızı:";
stHB += beatAvg;
char charHB[20];
stHB.toCharArray(charHB, 20);
auto header2 = text(0, header.height())
.value(charHB)
.align(left)
.valign(vcenter)
.width(tft.width())
.thickness(3);
header2.height(header.font_height() * 2);
header2.draw(); //Başlık yüksekliği, fontun yüksekliğinin iki katıdır
//Çizgi grafiği için ayarlar
auto content = line_chart(0, header.height() + header2.height()); //(x,y) çizgi grafiğinin başladığı yer
content
.height(tft.height() - (header.height() + header2.height()) * 1.0) //çizgi grafiğinin gerçek yüksekliği
.width(tft.width() - content.x() * 2) //çizgi grafiğinin gerçek genişliği
.based_on(0.0) //y-ekseni başlangıç noktası, float olmalıdır
.show_circle(false) //her noktada bir daire çizme, varsayılan açıktır.
.value(data) //veriyi çizgi grafiğine geçirme
.color(TFT_PURPLE) //Çizgi için rengi ayarlama
.draw();
spr.pushSprite(0, 0);
oldHB = HB;
}
