Arduino/Příklady využití

Z WikiSkript

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

Sada pro monitor životních funkcí[upravit | editovat zdroj]

V předpřipravené sadě naleznete:

Kompletní obvod[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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ů[upravit | editovat zdroj]

  • BPM z MAX30102
  • Teplota z MAX30102

Měření krevního tlaku[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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[upravit | editovat zdroj]

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