Arduino/Příklady využití

V tomto článku naleznete příklady různých projektů, které využívají platformy Arduino.

Sada pro monitor životních funkcí

V předpřipravené sadě naleznete:

Kompletní obvod

Zapojením všech součástek najednou do nepájivého pole nám vznikne finální obvod. Zapojení a funčnost jednotlivých součástek je vysvětleno dále.

SchematicNano.png
Schéma obvodu[1]
Fritzing.png
Schéma obvodu z programu fritzing pro Arduino Uno[2]
Image.jpeg
Zapojený obvod s Arduino Uno[3]

Bzučák

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).

Modul pro EKG

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!

Image%202.jpeg
Ukázka špatného barevného značení - zelená místo černé elektrody pro zem[4]
ECG_limb_and_chest_electrodes_placement.png
Schéma umístění elektrod "klasického" 12svodového EKG[5]


Měření EKG

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 fitrovaní dat ze senzoru EKG

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;
}

Senzor MAX30102

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ů.

Měření SpO2

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);
  }
}

Další příklady programů

  • BPM z MAX30102
  • Teplota z MAX30102

Měření krevního tlaku

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.

Měření surových dat tlaku

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);
}

Měření tlaku v mmHg

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;
}

Zdroje

  1. Nekompletní citace webu. . 
  2. Nekompletní citace webu. . 
  3. Nekompletní citace webu. . 
  4. Nekompletní citace webu. . 
  5. Nekompletní citace webu. .