Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Echtzeit-Herzschlag-LED-Visualisierung per EKG

Komponenten und Zubehör

Komponente Menge Hinweis
uECG-Gerät 1 Tragbares EKG, Open Source, Arduino-kompatibler Digitalausgang
Arduino Nano R3 1 Steuerung der LED-Ring
Adafruit NeoPixel Ring: WS2812 5050 RGB LED (16 LEDs) 1 Visualisierung
Li-Ion Akku 1000 mAh 1 Stromversorgung (direkt an 5V, nicht Vin!)

Benötigte Werkzeuge

Projektbeschreibung

Seit Jahren wollte ich LEDs bauen, die im Rhythmus meines Herzschlags blinken – nicht nur im Stillstand, sondern auch bei Bewegung. Das war überraschend schwierig, ich scheiterte jahrelang. Nicht mehr!

Die ganze Signalverarbeitung übernimmt das uECG – ein kleines, tragbares, Open-Source-EKG-Gerät mit einem Arduino-freundlichen Digitalausgang (Pin geht bei jedem Herzschlag High/Low). Die Auswertung dieser digitalen Zustände ist weit einfacher als die Roh-EKG-Signalverarbeitung.

Update: Zweite Iteration des Projekts nutzt Funkübertragung statt Kabel.

1. Schaltplan

Da nur digitale Signale verarbeitet werden, ist der Aufbau simpel. Für den tragbaren Einsatz sind verlötete Verbindungen zuverlässiger (und kompakter). Für schnelle Tests geht auch Steckbrett.

Echtzeit-Herzschlag-LED-Visualisierung per EKG

Wichtig: LiPo-Akku direkt an 5V anschließen – nicht an Vin! Der Spannungsregler am Vin-Pin verursacht Spannungsabfall, den wir uns nicht leisten können. Arduino ist stabil bis ~3,4 V. Voll geladener LiPo: 4,2 V, 3,4 V erst bei <15 % Restladung. Mit >200 mAh Akku gute Laufzeit. Akku per Stecker anschließen (zum Laden abnehmbar).

2. Code

Das Programm liest kontinuierlich Pin D3. Bei Flankenwechsel wird der Zeitpunkt in ein 20-Elemente-Array geschoben. Differenz zwischen erstem und letztem Element geteilt durch 20 = mittlere Zeit pro Schlag (ms). 60.000 ms / dieser Wert = BPM.

Array-Größe anpassbar: Weniger Elemente = schnellere Reaktion, aber instabiler. Mehr Elemente = stabiler, aber träger bei schnellen BPM-Änderungen.

BPM wird auf Farbe gemappt (blau → grün → gelb → rosa → rot bei steigendem Puls) und auf Anzahl leuchtender LEDs (80 BPM = 8 Segmente, 110 BPM = 11, Skalierung einstellbar).

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

#define PIN 11           // DI-Pin LED-Ring
#define NUMPIXELS 16     // Anzahl LEDs im Ring
int in_pin = 3;          // uECG Eingangspin

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define BEAT_HIST 20     // Anzahl Herzschläge für Mittelwert
long beats[BEAT_HIST];

void push_beat(long ms) {
  for(int x=0; x<BEAT_HIST-1; x++) beats[x] = beats[x+1];
  beats[BEAT_HIST-1] = ms;
}

int get_bpm() {
  long dt = beats[BEAT_HIST-1] - beats[0];
  return BEAT_HIST * 60000 / dt;
}

long last_pix_upd = 0;
int prev_in_state = 0;

void setup() {
  pixels.begin();
  pinMode(in_pin, INPUT);
  digitalWrite(in_pin, 1); // Pull-Up aktivieren – KRITISCH, uECG hat keinen internen Pull-Up
}

void loop() {
  long ms = millis();
  int in_state = digitalRead(in_pin); // 1 = kein Schlag, 0 = Schlag
  
  if (in_state == 1 && prev_in_state == 0) { // Flanke High→Low = Schlag
    push_beat(ms);
  }
  prev_in_state = in_state;
  
  if (ms - last_pix_upd > 10) { // LEDs nicht zu oft aktualisieren
    last_pix_upd = ms;
    int bpm = get_bpm();
    int max_bright = 120; // Max-Helligkeit (max 255)
    float dd = 20;        // BPM-Schritte zwischen Farbtonwechseln
    float t1 = 90, t2 = t1+dd, t3 = t2+dd, t4 = t3+dd;
    int r, g, b;
    
    // Farbmapping
    if (bpm < t1) { r=0; g=0; b=max_bright; }
    else if (bpm < t2) { r=0; g=max_bright*(bpm-t1)/dd; b=max_bright-g; }
    else if (bpm < t3) { r=max_bright*(bpm-t2)/dd; g=max_bright-r; b=r/4; }
    else if (bpm < t4) { r=max_bright; g=0; b=max_bright/2 - max_bright*(bpm-t3)/(2*dd); }
    else { r=max_bright; g=0; b=0; }
    
    // Zwischen Schlägen auf 1/4 Helligkeit dimmen (nur Schläge hervorgehoben)
    if (in_state) { r*=0.25; g*=0.25; b*=0.25; }
    
    int on_pixels = (bpm + 5) / 10; // LEDs an: 60 BPM→6, 120 BPM→12
    for (int i=0; i<NUMPIXELS; i++) {
      if (i < on_pixels) pixels.setPixelColor(i, pixels.Color(r,g,b));
      else pixels.setPixelColor(i, pixels.Color(0,0,0));
    }
    pixels.show();
  }
}

3. Aufbau als Wearable

Arduino passt fast perfekt in den LED-Ring. Akku daneben. Wichtig: uECG wird auf der Brust getragen – Kabel mit Steckern nötig! Erst uECG anbringen, dann Shirt mit Elektronik anziehen, dann Stecker verbinden. Sonst sehr fummelig.

Echtzeit-Herzschlag-LED-Visualisierung per EKG
Echtzeit-Herzschlag-LED-Visualisierung per EKG
Echtzeit-Herzschlag-LED-Visualisierung per EKG

Alles richtig verkabelt → nach ~30 Sek. startet Blinken mit BPM-Anzeige.

4. Praxistest

Getestet beim Gehen und Laufen. Problem beim Laufen: Akku hüpft auf EKG-Sensor → verfälscht Messung. Kabel zu kurz → zieht bei jedem Schritt am Sensor. Zuverlässige Erkennung nur beim Gehen/Stehen, nicht Laufen. Sensor selbst (mit anderem Shirt) zeigte beim Laufen korrekte BPM (per App geprüft).

LEDs auf der Brust sehen cool aus, sind aber unpraktisch – man schaut nicht auf die Brust für Puls. Nächste Iteration: Armband zur Anzeige.

Mehr zu uECG: Hackaday-Projektseite (Technikdetails, PCB-Designs, Logs).

Code

uECG_pixel_ring.ino (vollständig)

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 11
#define NUMPIXELS 16
int in_pin = 3;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pixels.begin();
  pinMode(in_pin, INPUT);
  digitalWrite(in_pin, 1); // Pull-Up kritisch!
}

#define BEAT_HIST 20
long beats[BEAT_HIST];

void push_beat(long ms) {
  for(int x=0; x<BEAT_HIST-1; x++) beats[x] = beats[x+1];
  beats[BEAT_HIST-1] = ms;
}

int get_bpm() {
  long dt = beats[BEAT_HIST-1] - beats[0];
  return BEAT_HIST * 60000 / dt;
}

long last_pix_upd = 0;
int prev_in_state = 0;

void loop() {
  long ms = millis();
  int in_state = digitalRead(in_pin);
  if (in_state == 1 && prev_in_state == 0) push_beat(ms);
  prev_in_state = in_state;
  if (ms - last_pix_upd > 10) {
    int r,g,b; last_pix_upd = ms;
    int bpm = get_bpm();
    int max_bright = 120;
    float dd = 20;
    float t1=90, t2=t1+dd, t3=t2+dd, t4=t3+dd;
    if (bpm < t1) { r=0; g=0; b=max_bright; }
    else if (bpm < t2) { r=0; g=max_bright*(bpm-t1)/dd; b=max_bright-g; }
    else if (bpm < t3) { r=max_bright*(bpm-t2)/dd; g=max_bright-r; b=r/4; }
    else if (bpm < t4) { r=max_bright; g=0; b=max_bright/2 - max_bright*(bpm-t3)/(2*dd); }
    else { r=max_bright; g=0; b=0; }
    if (in_state) { r*=0.25; g*=0.25; b*=0.25; }
    int on_pixels = (bpm+5)/10;
    for(int i=0;i<NUMPIXELS;i++) {
      if(i<on_pixels) pixels.setPixelColor(i, pixels.Color(r,g,b));
      else pixels.setPixelColor(i, pixels.Color(0,0,0));
    }
    pixels.show();
  }
}

Schaltplan

Echtzeit-Herzschlag-LED-Visualisierung per EKG

Herstellungsprozess

  1. Externer Defibrillator: Geschichte, Design und Fertigung
  2. Das Nudelholz: Von etruskischen Ursprüngen bis zur modernen Handwerkskunst
  3. Künstliches Herz: Technologie, Geschichte und Zukunftsperspektiven
  4. Künstliche Herzklappen: Typen, Geschichte und zukünftige Innovationen
  5. LED mit PIR-Bewegungsmelder am Raspberry Pi steuern
  6. Heimtemperatur mit Raspberry Pi Dashboard überwachen
  7. Fern-Herzfrequenz-Überwachung mit AWS IoT Alarmen
  8. Kontaktlose Herzschlagüberwachung via Smart Speaker: Ein revolutionäres System
  9. Graphen-basiertes elektronisches Tattoo ermöglicht kontinuierliche, präzise Herzüberwachung über lange Zeiträume
  10. Gelenkwelle / Knickgelenk (Knuckle Joint): Aufbau, Anwendungen & Eigenschaften