Zapojený obvod s Arduino Uno[3]
Bzučák zapojíme kladným pólem do PWM výstupu. PWM znamená Pulse-width modulation a je to způsob, jak u digitálního pinu, který normálně je nastaven binárně na výstp hodnot + nebo -, nastavit určité rozpětí hodnot ale pomocí tohoto dvojhodnotového signálu. Resp. chceme docílit efektu signálu analogového pomocí digitálního tak, že generujeme pulz, u kterého měníme poměr času, kdy je signál + nebo -. Jedná se o obdélníkový signál a modifikaci tzv. střídy. Dokážeme tedy pak u bzučáku programovat vysílané tóny. Digitální piny, které pulzně šířkovou modulaci umožňují, jsou na Arduinu značeny pomocí "~". Proto pozitivní pól bzučáku zapojíme přes 100 Ohm rezistor do pinu D9 a negativní k zemi (GND).
Výstup z modulu EKG je tzv. analogový. To znamená, že hodnota napětí z výstupu se mění na základě měřené veličiny. Přesné zapojení v modulu EKG zjistíme v jeho datasheetu. Proto pin output připojíme na analogový vstupní pin Arduina značený A0. U analogových pinů pak ohledně snímaného rozlišení záleží na Analog to Digotal Converteru (X bitový).
Napětí zapojíme na 3,3V zdroje na Arduinu. Spojíme GND se zemí. LO- a LO+ zapojíme do digitálních vstupů D11 a D10. LO- detekuje jako HIGH (tj. 1 resp +), když je odpojena elektroda pravé ruky. LO+ zas u levé ruky. SDN pin necháme nepřipojený, ten kontroluje přepínání stavu napájení.
Nakonec zapojíme do jack konektoru kabel pro připojení elktrod.
Nalepte elektrody dle příslušného obrázku. Pozor!
|
Ukázka špatného barevného značení - zelená místo černé elektrody pro zem[4]
|
Schéma umístění elektrod "klasického" 12svodového EKG[5]
Kód pro EKG
#define BAUD_RATE 115200
#define LO_PLUS 10
#define LO_MINUS 11
#define ANALOG_PIN A0
void setup() {
Serial.begin(BAUD_RATE); // zahájení sériové komunikace
pinMode(LO_PLUS, INPUT); // nastavení pro detekci sejmutí svodů LO +
pinMode(LO_MINUS, INPUT); // nastavení pro detekci sejmutí svodů LO -
}
void loop() {
if((digitalRead(LO_PLUS) == 1)||(digitalRead(LO_MINUS) == 1)){
Serial.println('!');
}
else{
Serial.println(analogRead(ANALOG_PIN)); // vypíše hodnotu analogového pinu
}
delay(10);
}
Kód pro EKG s filtrem
#define SAMPLE_RATE 125
#define BAUD_RATE 115200
#define INPUT_PIN A0
#define BUZZER_PIN 9
void setup() {
Serial.begin(BAUD_RATE); // zahájení sériové komunikace
pinMode(BUZZER_PIN, OUTPUT); // nastav pin bzučáku jako výstup
}
void loop() {
// výpočet uplynulého času
static unsigned long past = 0;
unsigned long present = micros();
unsigned long interval = present - past;
past = present;
// spusť časovač
static long timer = 0;
timer -= interval;
// vzorkuj data
if(timer < 0){
timer += 1000000 / SAMPLE_RATE;
float sensor_value = analogRead(INPUT_PIN);
float signal = ECGFilter(sensor_value);
Serial.println(signal);
if(signal >= 150){
tone(BUZZER_PIN, 1000); // vytvoř 1kHz zvuk
delay(10);
noTone(BUZZER_PIN); // zastav zvuk
}
}
}
// Pásmový Butterworthův IIR digitální filtr vygenerovaný pomocí filter_gen.py.
// Vzorkovací frekvence: 125,0 Hz, frekvence: [0,5, 44,5] Hz.
// Filtr je řádu 4, implementován jako sekce druhého řádu (biquads).
float ECGFilter(float input)
{
float output = input;
{
static float z1, z2; // filter section state
float x = output - 0.70682283*z1 - 0.15621030*z2;
output = 0.28064917*x + 0.56129834*z1 + 0.28064917*z2;
z2 = z1;
z1 = x;
}
{
static float z1, z2; // filter section state
float x = output - 0.95028224*z1 - 0.54073140*z2;
output = 1.00000000*x + 2.00000000*z1 + 1.00000000*z2;
z2 = z1;
z1 = x;
}
{
static float z1, z2; // filter section state
float x = output - -1.95360385*z1 - 0.95423412*z2;
output = 1.00000000*x + -2.00000000*z1 + 1.00000000*z2;
z2 = z1;
z1 = x;
}
{
static float z1, z2; // filter section state
float x = output - -1.98048558*z1 - 0.98111344*z2;
output = 1.00000000*x + -2.00000000*z1 + 1.00000000*z2;
z2 = z1;
z1 = x;
}
return output;
}
Pro připojení modulu pro měření saturace krve použijeme ještě další metodu. Na modulu nám měření zajišťuje senzor MAX30102, ten využívá komunikaci prostřednictvím sběrnice I2C. Jedná se o sériovou sběrnici - využívá 2 piny pro přenos informací. Zde jsou to piny SDA a SCL. Výhodou této sběrnice je, že k ní můžete připojit velké množství I2C součástek k pořád stejným pinům. Jejich odlišnost se pak řeší v programu, kdy se u nich odlišují adresy. K modulu tedy připojíme napájení a piny SDA do SDA na Arduinu. Rovněž zapojíme i SCL do SCL na Arduinu.
Další používané sběrnice jsou UART a SPI. Sběrnice UART je opět sériová. Užívá se zde 2 pinů pro komunikace RX a TX. Pin RX pro příjem a TX pro odesílání. Proto bychom u druhého zařízení připojili RX do TX a TX do RX - naopad. Odeslaná data se přijímají pinem pro příjem a naopak. Proto jsou ale vždy zařízení vázaná pouze na tyto dva piny a není možné do jedné dvojice UART připojit více zařízení jak 1. Sběrnice SPI využívá 4 piny. Dva obdobně jako UART se nazívají MISO (Master In Slave Out) a MOSI (Master Out Slave In). Ty se zapojí u zařízení opačně MISO do MOSI a MOSI do MISO. Dále je zde zapotřebí zapojit pin CLK. Čtvrtý pin zde plní funkci "adres" pro rozlišování různých komponent podobně tedy jako u I2C zde můžeme připojit více komonent k jedněm SPI pinům na desce Arduino. Ovšem každému komponentu přiřazujeme konkrétní fyzický pin pro výběr senzoru, takže jsme zde omezeni počtem pinů.
Pro měření senzorem MAX30102 stačí na něj přitisknout ukazováček.
Vzorový kód pro čtení SpO2 k knihovny senzorů rodiny MAX3010x
#include <Wire.h>
#include <MAX30105.h>
#include <spo2_algorithm.h>
MAX30105 particleSensor;
#define BAUD_RATE 115200
#define MAX_BRIGHTNESS 255
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
// Arduino Uno nemá dostatek paměti SRAM pro uložení 100 vzorků dat IR led a červené led ve 32bitovém formátu
// Pro vyřešení tohoto problému bude 16bitový MSB vzorkovaných dat zkrácen. Ze vzorků se stanou 16bitová data.
uint16_t irBuffer[100]; // data z infračerveného LED senzoru
uint16_t redBuffer[100]; // data z červeného LED senzoru
#else
uint32_t irBuffer[100]; // data z infračerveného LED senzoru
uint32_t redBuffer[100]; // data z červeného LED senzoru
#endif
int32_t bufferLength; // délka dat
int32_t spo2; // hodnota SpO2
int8_t validSPO2; // indikátor, který ukazuje, zda je výpočet SPO2 platný
int32_t heartRate; // hodnota tepové frekvence
int8_t validHeartRate; // indikátor, který ukazuje, zda je výpočet tepové frekvence platný
byte pulseLED = 11; // musí být na PWM pinu
byte readLED = 13; // blikne s každým údajem červeně
void setup()
{
Serial.begin(BAUD_RATE); // zahájení sériové komunikace
pinMode(pulseLED, OUTPUT);
pinMode(readLED, OUTPUT);
// inicializace senzoru
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) // použití výchozího portu I2C, rychlost 400 kHz
{
Serial.println(F("MAX30105 nebyl nalezen. Zkontrolujte prosím zapojení/napájení."));
while (1);
}
Serial.println(F("Připevněte senzor k prstu pomocí gumičky. Stisknutím libovolné klávesy spusťte převod"));
while (Serial.available() == 0) ; // čekat, dokud uživatel nestiskne klávesu
Serial.read();
byte ledBrightness = 60; // možnosti: 0=Off to 255=50mA
byte sampleAverage = 4; // možnosti: 1, 2, 4, 8, 16, 32
byte ledMode = 2; // možnosti: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
byte sampleRate = 100; // možnosti: 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 411; // možnosti: 69, 118, 215, 411
int adcRange = 4096; // možnosti: 2048, 4096, 8192, 16384
particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
}
void loop()
{
bufferLength = 100; // délka vyrovnávací paměti 100 ukládá 4 sekundy vzorků běžících rychlostí 25sps
// přečtěte prvních 100 vzorků a určete rozsah signálu.
for (byte i = 0 ; i < bufferLength ; i++)
{
while (particleSensor.available() == false) // máme nová data?
particleSensor.check(); // kontrola senzoru pro nová data
redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample(); // skončili jsme se vzorkem dat, přechízíme k dalšímu
Serial.print(F("red="));
Serial.print(redBuffer[i], DEC);
Serial.print(F(", ir="));
Serial.println(irBuffer[i], DEC);
}
// výpočet srdeční frekvence a SpO2 po prvních 100 vzorcích (první 4 sekundy vzorků).
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
// průběžné odebírání vzorků z MAX30102, tepová frekvence a SpO2 se počítají každou 1 sekundu
while (1)
{
// vypsání prvních 25 sad vzorků do paměti a posunutí posledních 75 sad vzorků nahoru
for (byte i = 25; i < 100; i++)
{
redBuffer[i - 25] = redBuffer[i];
irBuffer[i - 25] = irBuffer[i];
}
// před výpočtem tepové frekvence pořídit 25 sad vzorků
for (byte i = 75; i < 100; i++)
{
while (particleSensor.available() == false)
particleSensor.check();
digitalWrite(readLED, !digitalRead(readLED)); // blikni LEDkou při každém čtení dat
redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample();
// odeslání vzorků a výsledku výpočtu do terminálového programu přes UART
Serial.print(F("red="));
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(", HRvalid="));
Serial.print(validHeartRate, DEC);
Serial.print(F(", SPO2="));
Serial.print(spo2, DEC);
Serial.print(F(", SPO2Valid="));
Serial.println(validSPO2, DEC);
}
// po shromáždění 25 nových vzorků přepočítejte TF a Sp02.
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
}
}
- BPM z MAX30102
- Teplota z MAX30102
Pro měření krevního tlaku využejeme senzor senzor s nátrubkem a dostatečným rozsahem měřeného tlaku. Senzor funguje jako tenzometr (snímač zatížení, load cell), kdy, zjednodušeně, deformace způsobená tlakem ovlivní vlastnosti materiálu - zejména elektrického odporu. Vhodným zapojením (zde např. ve tvaru Wheatstoneova můstku) můžele lépe měřit i velice malé změny odporu. Výsledný anapogový signál můžeme převést na digitální a dále zpracovávat. Pro naší aplikace má ale vestavěný převodník Arduina příliš malé rozlišení, proto využijeme dalšího převodníku s rozlišením 24 bitů.
Při zapojování dbejte na orientaci senzoru! Je třeba mít výřez v plastovém těle senzoru orientovaný nahoru - leží tam, kde jsou piny 1 a 6 dle schématu.
Vzorový kód pro čtení dat ze senzoru pomocí ADC převodníku zde.
#define BAUD_RATE 115200
#define OUT_PIN 2
#define SCK_PIN 3
void setup() {
pinMode(OUT_PIN, INPUT); // Připoj modul HX710 OUT pin na Arduino např. pin D2
pinMode(SCK_PIN, OUTPUT); // Připoj modul HX710 SCK pin na Arduino např. pin D3
Serial.begin(BAUD_RATE);
}
void loop() {
// čekání na konec čtení
while (digitalRead(OUT_PIN)) {}
// přečti všech 24 bitů
long result = 0;
for (int i = 0; i < 24; i++) {
digitalWrite(SCK_PIN, HIGH);
digitalWrite(SCK_PIN, LOW);
result = result << 1;
if (digitalRead(OUT_PIN)) {
result++;
}
}
// převěď na dvojkový doplněk - způsob řešení záporných čísel
result = result ^ 0x800000;
// 3x pulz na hodinový pin pro začátek měření
for (char i = 0; i < 3; i++) {
digitalWrite(SCK_PIN, HIGH);
digitalWrite(SCK_PIN, LOW);
}
// zobraz hodnotu
Serial.println(result);
}
Kód pro mapování měřených hodnot na mmHg.
#define BAUD_RATE 115200
#define OUT_PIN 2
#define SCK_PIN 3
// proměnné pro ukládání kalibračních dat
float pressure1_mmHg = 40;
float pressure2_mmHg = 200;
long adcValue1 = 9387374.36363636;
long adcValue2 = 10355154.1372549;
void setup() {
pinMode(OUT_PIN, INPUT); // připoj modul HX710 OUT pin na Arduino např. pin D2
pinMode(SCK_PIN, OUTPUT); // připoj modul HX710 SCK pin na Arduino např. pin D3
Serial.begin(BAUD_RATE);
}
void loop() {
// čekání na konec čtení
while (digitalRead(OUT_PIN)) {}
// přečti všech 24 bitů
long result = 0;
for (int i = 0; i < 24; i++) {
digitalWrite(SCK_PIN, HIGH);
digitalWrite(SCK_PIN, LOW);
result = result << 1;
if (digitalRead(OUT_PIN)) {
result++;
}
}
// převěď na dvojkový doplněk - způsob řešení záporných čísel
result = result ^ 0x800000;
// Vypočítej tlak v mmHg pomocí lineární interpolace mezi dvěma kalibračními body
float pressure_mmHg = mapCalibration(result, adcValue1, adcValue2, pressure1_mmHg, pressure2_mmHg);
// 3x pulz na hodinový pin pro začátek měření
for (char i = 0; i < 3; i++) {
digitalWrite(SCK_PIN, HIGH);
digitalWrite(SCK_PIN, LOW);
}
// zobraz hodnotu
Serial.print("Pressure: ");
Serial.print(pressure_mmHg);
Serial.println(" mmHg");
// pauza před dalším měřením
delay(10);
}
// funkce pro lineární mapování
float mapCalibration(long x, long in1, long in2, float out1, float out2) {
return (float)(x - in1) * (out2 - out1) / (float)(in2 - in1) + out1;
}
Tento kód nám pomůže zjistit hodnoty pro lineární mapování hodnot pro tlak. Budete tedy potřebovat i manometr (tlakoměr). Spusťte si "Serial Monitor". Nastavte tlak na 40 mmHg a do pole pro příkazy zadejte "start40", hodnotu tlaku 40 mmHg ponechejtze chvíli a zadejte "stop40". Přenastavte na 200 mmHg a opakujte příkazy, akorát nyní "start200" a "stop200". Po zadání příkazu "stop" se vypíše i hodnota z ADC, která tomuto tlaku odpovídá. Tu si zkopírujte a pro tlak 40 nahraďte v kódu proměnnou adcValue1 a pro 200 mmHg nahraďte adcValue2, aby byly změny trvalé a nemuseli jsme kalibraci provádět vždy po nahrání nového kódu.
#define BAUD_RATE 115200
#define OUT_PIN 2
#define SCK_PIN 3
// proměnné pro ukládání kalibračních dat
float pressure1_mmHg = 40;
float pressure2_mmHg = 200;
long adcValue1 = 9000000;
long adcValue2 = 10000000;
bool averaging1 = false;
bool averaging2 = false;
long sum1 = 0;
long sum2 = 0;
int count1 = 0;
int count2 = 0;
void setup() {
pinMode(OUT_PIN, INPUT); // připoj modul HX710 OUT pin na Arduino např. pin D2
pinMode(SCK_PIN, OUTPUT); // připoj modul HX710 SCK pin na Arduino např. pin D3
Serial.begin(BAUD_RATE);
}
void loop() {
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
if (command == "start40") {
averaging1 = true;
sum1 = 0;
count1 = 0;
Serial.println("Starting averaging for 40 mmHg...");
} else if (command == "stop40") {
averaging1 = false;
if (count1 > 0) {
adcValue1 = sum1 / count1;
Serial.print("Averaged ADC Value for 40 mmHg: ");
Serial.println(adcValue1);
} else {
Serial.println("No data to average for 40 mmHg.");
}
} else if (command == "start200") {
averaging2 = true;
sum2 = 0;
count2 = 0;
Serial.println("Starting averaging for 200 mmHg...");
} else if (command == "stop200") {
averaging2 = false;
if (count2 > 0) {
adcValue2 = sum2 / count2;
Serial.print("Averaged ADC Value for 200 mmHg: ");
Serial.println(adcValue2);
} else {
Serial.println("No data to average for 200 mmHg.");
}
}
}
// čekání na konec čtení
while (digitalRead(OUT_PIN)) {}
// přečti všech 24 bitů
long result = 0;
for (int i = 0; i < 24; i++) {
digitalWrite(SCK_PIN, HIGH);
digitalWrite(SCK_PIN, LOW);
result = result << 1;
if (digitalRead(OUT_PIN)) {
result++;
}
}
// převěď na dvojkový doplněk - způsob řešení záporných čísel
result = result ^ 0x800000;
// Průměrování hodnot pro kalibraci
if (averaging1) {
sum1 += result;
count1++;
}
if (averaging2) {
sum2 += result;
count2++;
}
// Vypočítej tlak v mmHg pomocí lineární interpolace mezi dvěma kalibračními body
float pressure_mmHg = mapCalibration(result, adcValue1, adcValue2, pressure1_mmHg, pressure2_mmHg);
// 3x pulz na hodinový pin pro začátek měření
for (char i = 0; i < 3; i++) {
digitalWrite(SCK_PIN, HIGH);
digitalWrite(SCK_PIN, LOW);
}
// zobraz hodnotu
Serial.print("Pressure: ");
Serial.print(pressure_mmHg);
Serial.println(" mmHg");
// pauza před dalším měřením
delay(10);
}
// funkce pro lineární mapování
float mapCalibration(long x, long in1, long in2, float out1, float out2) {
return (float)(x - in1) * (out2 - out1) / (float)(in2 - in1) + out1;
}
|
|
|