Realtime ECG-weergave op OLED met Arduino UNO
Onderdelen en benodigdheden
| Arduino UNO | 1 |
| uECG apparaat | 1 |
| nRF24 Module (algemeen) | 1 |
| ElectroPeak 0.96" OLED 64x128 Display Module | 1 |
Over dit project
Enkele tijd geleden postte ik een paar projecten die lieten zien hoe je data van het uECG-apparaat kunt krijgen – maar die hadden veel rommelige code en gebruikten nog steeds alleen basisdata. Uiteindelijk schreef ik een Arduino-bibliotheek die dit veel eenvoudiger en betrouwbaarder maakt, hier is hij: https://github.com/ultimaterobotics/uECG_library (let op: je moet ook de RF24-bibliotheek installeren via Library Manager, en als je data op OLED wilt tonen zoals in dit project – ook Adafruit's SSD1306-bibliotheek).
1. Schema's
Het schema is hetzelfde als voor elk ander project met nRF24-module en OLED: nRF24 is aangesloten op Arduino's SPI-bus (D13, D12, D11) en twee willekeurige pinnen voor module CS en CE lijnen – ik koos D10 en D9 voor gemak. Het belangrijke punt: de nRF24-module moet aangesloten worden op de 3.3V lijn, niet op 5V! Het helpt ook om een 1uF of 10uF condensator tussen 3.3V en GND te plaatsen – die nRF24-modules vereisen een stabiele spanning die de Arduino niet altijd op de 3.3V-lijn kan leveren, condensators helpen daarbij.
De OLED is via I2C aangesloten – SDA aan A4, SCL aan A5, en gevoed van de 5V-lijn. In mijn geval had de OLED-module ingebouwde weerstanden voor het I2C-protocol. Als jouw module die niet heeft, moet je 4.7k weerstanden toevoegen van SDA naar 3.3V en van SCL naar 3.3V, hoewel de meeste modules die ik recentelijk zag dit al hebben.
Je kunt het schema hieronder zien, en hier is een foto van het gemonteerde project:

2. Code
De uECG-bibliotheek vereist een aantal regels code voor correcte werking:
- In
setup()roep jeuECG.begin(pin_cs, pin_ce)aan – je moet aangeven welke pinnummers gebruikt worden voor nRF24 CS en CE lijnen; de bibliotheek zet de module aan en in de juiste modus. - In
loop()moet jeuECG.run()zo vaak mogelijk aanroepen: het uECG-apparaat stuurt veel data – één pakket elke paar milliseconden – en als jeuECG.run()niet aanroept voordat het volgende pakket aankomt, gaat de data verloren. Dus nooitdelay()gebruiken in de loop, enmillis()gebruiken voor taken die timing vereisen (ik heb een voorbeeld in de bibliotheekvoorbeelden toegevoegd).
Deze projectcode is beschikbaar als voorbeeld in de bibliotheek, en ook hieronder bijgevoegd (als het te complex lijkt – houd er rekening mee dat hier 95% van de code gewijd is aan geoptimaliseerde display-tekening, voor simpele waarden printen naar de seriële monitor heb je maar een paar regels nodig):
#include <uECG.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int rf_cen = 9;
int rf_cs = 10;
void setup() {
Serial.begin(115200);
while(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
}
display.display();
delay(100);
uECG.begin(rf_cs, rf_cen);
delay(100);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.cp437(true);
display.display();
delay(100);
Serial.println("after display");
}
uint32_t prev_data_count = 0;
uint32_t prev_displ = 0;
uint8_t ecg_screen[128];
int ecg_screen_len = 128;
float ecg_avg = 0;
float ecg_max = 1;
float ecg_min = -1;
int ecg_size = 40;
int displ_phase = 0;
void loop() {
uECG.run();
uint32_t data_count = uECG.getDataCount();
int new_data = data_count - prev_data_count;
prev_data_count = data_count;
if(new_data > 0) {
uint32_t ms = millis();
int16_t ecg_data[8];
uECG.getECG(ecg_data, new_data);
for(int x = 0; x < new_data; x++)
Serial.println(ecg_data[x]);
for(int x = new_data; x < ecg_screen_len; x++)
ecg_screen[x-new_data] = ecg_screen[x];
for(int x = 0; x < new_data; x++) {
ecg_avg *= 0.99;
ecg_avg += 0.01*ecg_data[x];
ecg_max = ecg_max*0.995 + ecg_avg*0.005;
ecg_min = ecg_min*0.995 + ecg_avg*0.005;
if(ecg_data[x] > ecg_max) ecg_max = ecg_data[x];
if(ecg_data[x] < ecg_min) ecg_min = ecg_data[x];
int ecg_y = 63-ecg_size*(ecg_data[x] - ecg_min) / (ecg_max - ecg_min + 1);
ecg_screen[ecg_screen_len-1-new_data+x] = ecg_y;
}
if(ms - prev_displ > 30) {
prev_displ = ms;
if(displ_phase == 0) {
display.clearDisplay();
display.setCursor(0, 0);
display.print("BPM: ");
display.println(uECG.getBPM());
display.print(" RR: ");
display.println(uECG.getLastRR());
display.print("steps: ");
display.print(uECG.getSteps());
int batt_mv = uECG.getBattery();
int batt_perc = (batt_mv - 3300)/8;
if(batt_perc < 0) batt_perc = 0;
if(batt_perc > 100) batt_perc = 100;
display.drawLine(110, 0, 127, 0, WHITE);
display.drawLine(110, 10, 127, 10, WHITE);
display.drawLine(110, 0, 110, 10, WHITE);
display.drawLine(127, 0, 127, 10, WHITE);
int bat_len = batt_perc / 6;
for(int x = 1; x < 10; x++)
display.drawLine(110, x, 110+bat_len, x, WHITE);
}
if(displ_phase == 1) {
for(int x = 1; x < ecg_screen_len/2; x++)
display.drawLine(x-1, ecg_screen[x-1], x, ecg_screen[x], WHITE);
}
if(displ_phase == 2) {
for(int x = ecg_screen_len/2; x < ecg_screen_len-1; x++)
display.drawLine(x-1, ecg_screen[x-1], x, ecg_screen[x], WHITE);
}
if(displ_phase == 3)
display.display();
displ_phase++;
if(displ_phase > 3) displ_phase = 0;
}
}
}
3. Data verwerken
Er wordt veel verwerking on-board uitgevoerd en je kunt diverse statistieken opvragen die door het apparaat berekend worden: BPM, GSR, laatste RR-interval, HRV-parameter en 16 HRV-bins (eerste bin = aantal slagen met variatie <1%, tweede bin = variatie tussen 1-2%, etc.), aantal stappen, versnellingsmeter-waarden (hoewel ververingsfrequentie laag is, dus alleen goed voor houdingschatting).
Je kunt ook ruwe ECG-metingen ophalen – de datastroom is niet perfect, ab en toe gaan pakketten verloren, toch krijg je iets bruikbaars:

Nou, dat is het – als je dit apparaat in een hoek had liggen stoven, werkt het nu echt zonder te veel gedoe :)
Code
uECG Arduino-bibliotheek (met OLED en simpele seriële voorbeelden):
https://github.com/ultimaterobotics/uECG_library
Schema's

Productieproces
- Een robuuste temperatuur- en sensor-API bouwen op Raspberry Pi met de GY-91-module
- Aan de slag met TJBot: Bouw, Configureer en Stem-bedien Je Eigen AI-robot
- Bouw een Klassiek Pong-spel op Arduino UNO met OLED-display – Stap-voor-stap Tutorial
- JX Wave Generator – Arduino-compatibele DDS-oscillator met OLED-display
- Bouw Real-Time Cellulaire Automaten op Arduino met 128x64 OLED Display
- DIY Arduino LCD-project: Bouw met weerstanden, drukknop en 16x2 display
- Arduino Beginner Tutorial 01: Start met Je Eerste Project
- Maxim's MAX86150: geïntegreerde PPG & ECG module voor mobiele gezondheidsmonitoring
- Verilog module basis: Structuur & syntaxis
- Verilog Parameters beheersen: herbruikbare, schaalbare modules bouwen
- Java 9 Modulesysteem: Verbeteringen, Link-time en Custom Runtime Images