Problems showing ECG signal on display (Incoming signal on A1 is good)

Problems showing ECG signal on display (Incoming signal on A1 is good)

Hello all,
I am currently working on a project where I get an ECG signal from a philips surveillance monitor coming in on pin A1 of my Arduino. The signal was initially an AC voltage that the arduino cannot read. I used a voltage divider and some capacitor to make the voltage DC voltage so that the arduino can read it. The signal I now send in on pin A1 is between 0.5v and 4v. With an oscilloscope, I checked that the signal on A1 looks neat - well clean and with clear peaks. You can see that the first curve on my screen is good and then the curve becomes less. sometimes it shows the curve 4x right and several times wrong. There is no fixed rhythm in it.
My goal is to show this ECG signal on a display, but what I get on the screen is occasionally a good ECG curve and occasionally it flips the curve. I suspect the problem is somewhere in the way I read, process or plot the analogue values on the screen.
Does anyone have an idea what this could be due to? Thinking of:

  • Too slow or too fast sampling rate?
  • Too little resolution or wrong scale?
  • Processing of the signal?
  • Error in how I control the display?
    Below I post my code:
    // Insert your full Arduino code here
    Any tips or examples are welcome!
    Thanks in advance for thinking with me :blush:
    Greetings,
    Quint
#include <Wire.h> // Voor I2C-communicatie
#include <Adafruit_GFX.h> // Grafische bibliotheek van Adafruit (voor basisvormen, lijnen, enz.)
#include <Adafruit_SH110X.h> // Driver voor SH1106 OLED-scherm

#define SCREEN_I2C_ADDR 0x3C // I2C-adres van het OLED-scherm
#define SCREEN_WIDTH 128 // Breedte van het scherm in pixels
#define SCREEN_HEIGHT 64 // Hoogte van het scherm in pixels

Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire); // Maak een displayobject aan met opgegeven resolutie en I2C

const int IN_PIN = A0; // De analoge ingang waarop het ECG-signaal wordt gemeten
const int SAMPLE_TIME = 4; // Interval (in milliseconden) tussen metingen van het ECG-signaal
const int DISPLAY_UPDATE_TIME = 8; // Interval (in milliseconden) voor het bijwerken van het display

const float SCALE_FACTOR = 12; // Versterkingsfactor voor het signaal zodat het beter zichtbaar is op het scherm
const int middleY = SCREEN_HEIGHT / 1.25; // Verticale middenlijn voor het tekenen van het signaal (schaling voor betere positie)

unsigned long lastSampleTime = 0; // Tijdstempel van de laatste meting
unsigned long lastDisplayTime = 0; // Tijdstempel van de laatste schermupdate

int ecgData[SCREEN_WIDTH]; // Array waarin alle meetpunten van het signaal worden opgeslagen om ze te tekenen
float averageVoltage = 1.5; // Startwaarde voor de gemiddelde spanning, gebruikt voor dynamisch centreren

void setup() {
  //Serial.begin(9600); // Start seriële communicatie voor debugging (voor bv. plotten via Serial Plotter)
  display.begin(SCREEN_I2C_ADDR, true); // Initialiseer het scherm met het juiste I2C-adres
  display.clearDisplay(); // Wis het scherm
  display.display(); // Toon de lege buffer op het scherm

}

void loop() {
  if (millis() - lastSampleTime >= SAMPLE_TIME) { // Controleer of het tijd is voor een nieuwe meting
    analogRead(IN_PIN); // Dummy read voor ADC-stabilisatie
    delayMicroseconds(1000); // Kleine delay om ADC te laten settelen
    int sensorValue = analogRead(IN_PIN); // Echte meting

    float voltage = (sensorValue / 1024.0) * 5.0; // Zet ADC-waarde om naar spanning (0 - 5V)

    //Serial.println(voltage); // Stuur de spanning naar de seriële monitor (voor debug of plot)

    averageVoltage = 0.9 * averageVoltage + 0.1 * voltage;

    int y = middleY - (voltage - averageVoltage) * SCALE_FACTOR;
    y = constrain(y, 5, SCREEN_HEIGHT - 5); // Zorg dat y binnen het scherm blijft

    for (int i = 0; i < SCREEN_WIDTH - 1; i++) {
      ecgData[i] = ecgData[i + 1];
    }
    ecgData[SCREEN_WIDTH - 1] = y;

    lastSampleTime = millis(); // Update tijdstempel van laatste meting
  }

  if (millis() - lastDisplayTime >= DISPLAY_UPDATE_TIME) {
    display.clearDisplay();

    for (int i = 0; i < SCREEN_WIDTH - 1; i++) {
      display.drawLine(i, ecgData[i], i + 1, ecgData[i + 1], 1);
    }

    display.display();
    lastDisplayTime = millis();
  }
}




Interesting project !

The code is pretty complex , but I’d suggest that you print a few variable values to see if their values are what you expect .

Hi @quintgiesberts ,

you might start with a consideration the following data

  • Tpp = The time between two peaks of the ECG signal
  • Npp = The number of peaks you want to see on the display in maximum
  • Res = The display resolution (width) that restricts the graphics output;

Let's assume this

  • Res = 128 (taken from your sketch)
  • For an average of 60 pulses/minute the time between two peaks Tpp = 1 [s]
  • We want to see 3 peaks so the display has to cover 3 times Tpp = 3 x 1 s = 3 s in total
  • 3 s divided by Res = 3 s/128 equals 23.4 ms

So a refresh of the display about every 23 ms would be sufficient.

However we do not know how long it takes to clear and redraw the whole display!

So I suggest you add some lines here (and of course Serial.begin(115200); in setup()) just for testing!

  if (millis() - lastDisplayTime >= DISPLAY_UPDATE_TIME) {

    unsigned long startOfRefresh = millis(); // Get the starttime

    display.clearDisplay();

    for (int i = 0; i < SCREEN_WIDTH - 1; i++) {
      display.drawLine(i, ecgData[i], i + 1, ecgData[i + 1], 1);
    }

    display.display();
    lastDisplayTime = millis();

    Serial.println(lastDisplayTime - startOfRefresh); // Print the time consumed for Refresh
  }
}

If this takes longer than your measurement interval you will quite likely lose data in your measurement function!

As the graphics resolution is the critical restriction for what you can display it does not make sense to store single values taken avery e.g. 4 ms. It would be more appropriate to average each display point value over the time between two refresh cycles.

Hope that this is of assistance...
Good luck!
ec2021

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.