Project tutorial
Portable Electrocardiograph (ECG)

Portable Electrocardiograph (ECG)

A step by step tutorial that will guide you throught the complete development process. It also includes prototype testing. A success!

  • 2,989 views
  • 2 comments
  • 24 respects

Components and supplies

Ardgen mega
Arduino Mega 2560 & Genuino Mega 2560
×1
Texas instruments lm358ap image
Texas Instruments General Purpose Dual Op-Amp
×3
Tens70
9V battery (generic)
×3
Keystone 233 image 75px
9V Battery Clip
×3
Tiga-Med Gold Electrodes
×3
Capacitor 18nF
×1
Capacitor 33nF
×1
Capacitor 1uF
×1
Capacitor 10uF
×1
Capacitor 20 uF
×1
Mfr 25frf52 1k sml
Resistor 1k ohm
×1
Mfr 25frf52 10k sml
Resistor 10k ohm
×2
Mfr 25frf52 100k sml
Resistor 100k ohm
×4
Resistor 200k ohm
×3
Resistor 1.1M ohm
×1
Resistor 3M ohm
×1

Necessary tools and machines

09507 01
Soldering iron (generic)

Apps and online services

About this project

Abstract

As a result of ageing population and a progressively greedy will of better healthcare, new medical devices have arisen. Some focus on prevention, others in treating. The prototype developed here focus in the acquisition of the heart’s electrical activity with a simple electrical circuit embedded in an Arduino microcontroller, with the possibility of early heart diseases detection. An effective and affordable solution was proposed and dozens of trials were performed with the device. These pointed to a high rate of detection of R, S and T-peaks (~100%, 78, 3% and 72, 5%, respectively) with an accurate heart rate measure. Despite of the good results obtained, this device, for now, must not be used for other purposes than monitoring.

Introduction

Big efforts to provide good healthcare at affordable prices have become a central issue in the last decades. Along with wide technological developments, it has been possible to create more efficient devices and with new available features. Wearables are a class of devices with an exponential growth in the last years, as problems with battery life get overpassed.

This electrocardiograph joins Arduino features with ananalog circuit and acquires, amplifies and filters a very weak signal obtained from the chest.

A set of algorithms were then proposed to detect arrhythmias, heart muscle necrosis, hypertrophic ventricles, variations in the heart rate depending on the position (lying down, seated or standing) of the person.

A set of trials were then carried out to evaluate the sensibility and reliability of the obtained measurements. Despite we were only able to test our device in healthy people (thus, no pathologic recordings were obtained), different and representative results were obtained among all candidates.

Given the results obtained versus the simplicity of the circuit, great improvements are expected to be achieved in a reduced time scale. Observing the world’s tendency, in a near future, it is expected to see ECG (wearable) devices at home, automatically detecting many pathologies without clinician intervention.

Physiological Basis

---------------------------- Cardiac Cycle and Electrical Activity ----------------------------

The cardiac cycle is controlled by the sinoatrial node (SA node) via electrical stimulation. The node consists of a group of very peculiar cells with the ability to spontaneously produce an electrical impulse that propagates through a network of specialized fibers (atrioventricular node, His bundle, Purkinje fibers), regulating heart muscle’s contraction. Because it is responsible for setting the heart’s rhythm, the SA node is often called the natural pacemaker.

This electrical activity of the heart is the biological fundament behind the electrocardiogram (ECG) register. In fact, each feature of the ECG is related to some phase of the cardiac cycle.At the beginning of the cardiac cycle, both the atria and ventricles are relaxed (diastole), and the blood is flowing into the right and left atria from venae cavae and the four pulmonary veins, respectively. Since both the tricuspid and mitral valves are opened, blood flows unimpeded from the atria to the ventricles.

Following atrial depolarization, represented by the P wave of the ECG, the atrial muscles contract from the superior portion of the atria toward the atrioventricular septum, pumping the blood into the ventricles through the open atrioventricular valves. At the start of atrial systole, the ventricles are usually filled with approximately 70–80% of their capacity, during the atrial systole the ventricles are completely filled. Atrial systole lasts approximately 100ms and ends before the ventricular systole.

Ventricular systole follows the depolarization of the ventricles and is represented by the QRS complex in the ECG. Initially, the pressure generated in the ventricles is not sufficient to eject the blood from the heart, the blood flowsback and closes the atrioventricular valves, it’s the isovolumetric contraction phase since blood’s volume remains constant. When the contraction of the ventricular muscle counterbalances the pressures in the pulmonary trunk and aorta, the ejection phase of theventricular systole begins and the blood is pumped from the heart.

Ventricular relaxation, or diastole, follows repolarization of the ventricles and is represented by the T-wave of the ECG.

--------------------------- ECG Features and Anomalies Detection ---------------------------

P wave – Represents atrial depolarization during atrial systole, with a usual duration smaller than 120ms. An increased or decreased P-wave can indicate hypo or hyperkalemia, respectively. Right and left atrial hypertrophy can also be detected by observing P-wave morphology.

QRS complex – Indicates ventricular depolarization and contraction during ventricular systole, with a usual duration between 60 and 100ms. Prolonged duration indicates hyperkalemia or bundle branch block and increased amplitude indicates cardiac hypertrophy, which is a common condition in athletes. Other anomalies reveal the presence of infarction.

The time between RR-peaks may be used to compute the heart rate.ST-segment – Refers to the gap between the S-wave and the T-wave, represents the time between ventricular depolarization and repolarization and has a usual duration between 80 and 120ms. Flat, down sloping, or depressed ST-segments may indicate coronary ischemia. ST-elevation is the classical indicator for myocardial infarction.T-wave – Represents ventricular repolarization. T-wave inversion (negative curvature) can be an indicator for several heart disorders.U-wave – Represents repolarization of the Purkinje fibers. It is not always visible on an ECG because it is a very small wave in comparison to the others.

Prototype Development & Design

The circuit used to measure heart’s electric activity can divided in several blocks. Signal acquisition, amplification, filtration and offsetting were the 4 main procedures used to extract a very weak signal from ones’ chest. All the connections were performed in a breadboard and then an Arduino Mega 2560 was chosen to achieve all the digital processing.

----------------------------------------------- Acquisition -----------------------------------------------

The circuit proposed in this paper was based on a standard circuit of ECG acquisition. The input electrodes are connected directly to the ampop’s positive terminals. Therefore, it was possible to amplify a very weak signal without disturbing the body voltage (due to high input impedances). To do so, ECG specific electrodes (Tiga-Med Gold) were used to detect the signal and transmit it through a set of 3 coaxial cables created to better insulate the circuit from external electromagnetic interferences. Along with this, another big advantage of these cables is the electromagnetic fields carrying the signal exist only in the space between the inner and outer conductors. This allows coaxial cable runs to be installed next to other metallic objects (e.g. - other cables).

The position of each electrode greatly influences the polarity and the shape of the electric heart activity registered. Different positions empower one to study different specific ECG curve parameters. This is why in a clinical environment, it’s expected to perform these tests with more than 3 electrodes.

---------------------------------------------- Amplification ----------------------------------------------

Taking into consideration that biosignals tend to have small and weak amplitudes, ranging from micro to milivolts, it is essential to amplify the signal before processing and display it. In order to achieve that, it was used an Instrumentation Amplifier INA126, which was supplied with ±9V. Since it is intended to get a signal ranging Volts (able to be read by the Arduino, after passing through the analog filters), it is necessary to dimension the gain in the order of magnitude of 1000, to do so we used 2 amplification stages. At the end, the amplification in our targeted frequencies (1 to 5Hz) was 2010×. We verified experimentally by amplifying a signal generated with a known amplitude.

One way of improving our circuit would be to purchase the INA126 in a chip, in order to get a better amplification with the lowest noise (the right ampops would allow a higher CMRR). Due to cost containment criteria, this wasn’t done.

The energy supplied to each ampop was provided by a set of two 9 V batteries (maximum voltage at the output of each one). To amplify the difference between the signal from each electrode (V3 and V4) we had to remove the DC component, which was done adding a capacitor in series (C1 and C2).

------------------------------------------------- Filtration -------------------------------------------------

The human body is a good conductor acting as an antenna which picks up electromagnetic radiation present in the environment. In the current project, the most common electromagnetic radiation, that was necessary to deal with, was the one coming from the power line, which has a fundamental frequency around 50Hz. To reduce its preponderance, both ampops and the Arduino were supplied with batteries. Other possible interference sources are muscular activity (apart the one from the heart), including respiration and movement. It is also important to refer the limitations of breadboards and the enormous number of connections which has interfered in the circuit stability and contributed to signal distortions. Moreover, the electrodes should also be as tighten as possible to the skin. To improve conductivity in the interface between the skin and the electrode, a conductor gel could be used (although we didn’t use it for the sake of model simplicity and low cost). To remove all these distortions sources, we designed 2 analog low-pass filters and 1 analog high-pass.

Low-Pass Filters2 low-pass filters with 2 different cutoff frequencies (24Hz and 44Hz) were used to eliminate power line frequency at a higher rate than 20 db/dec (Fig. 6). Since, we got an acceptable frequency response with these 2 passive filters, we avoid using active filtration (with ampops) to get a circuit as energetically efficient as possible, considering energy limitations present in portable devices.

High-Pass FilterThis high-pass passive (same reason as before) filter aimed to remove DCcomponent and slow undesirable oscillations in the signal (Fig. 7).ECG signal got undeniably better, although it still had some noise due to thepresence of a transition band in each frequency response. To complement these 3 filters, another 2 (digital) were added. Using ‘iirnotch’ and ‘butter’ functions inMatlab, we extracted the corresponding IIR coefficients and implemented the filters through the Arduino interface. In order to calculate each coefficient, sampling frequency had to be provided. We will explain in further detail, how we were able to sample an analog signal from the circuit with a constant and well-defined time step.

------------------------------------------------ Offsetting ------------------------------------------------

Arduino’s analog input pins are only able to read frequencies between 0 and 5V. Since our output signal varied between positive and negative voltages, a voltage divider had to be added to ensure the total signal was detected. 1 resistance of 1.1MΩ and another of 200kΩ between Vcc and the ground, allowed us to sum 1.4V (DC) to the body signal.

------------------------------------------------ Arduino ------------------------------------------------

To visualize the obtained signal, we connected the prototype's output to Arduino Mega 2560 microcontroller.The output signal (amplified and filtered) from the circuit was then received by the analogic pin A0 and the ground reference of the circuit connected to GRD pin of the hardware.

In Arduino IDE the signal was filtered by an IIR notch filter (from 40 to 60Hz) and another IIR low-pass Butterworth (cut-off frequency 100Hz) of 3rd and 4th orders, respectively:

Notch Filter

y(t) = 0.2012*x(t) − 0.3256*x(t−1) + 0.2012*x(t−2) + 0.3256*y(t−1) + 0.5975*y(t−2)

Low-Pass Filter

z(t) = 0.0495*y(t) + 0.1486*y(t−1) + 0.1486*y(t−2) + 0.0495*y(t−3) + 1.1619*z(t−1) − 0.6959*z(t−2) + 0.1378*z(t−3);

These coefficients were obtained using the Matlab function iirnotch and butter, respectively (see Matlab script attached to this report to know exactly which parameters do these functions receive and the frequency response of each one).

An important criterion for the filters’ parameter definition is the sampling frequency. To monitor this, an interrupt was set. Every time the interrupt is ON, it interrupts the loop cycle and some previously defined operations in the ISR function are performed, including the signal acquisition from pin A0. We chose a sampling period of 2ms, or equivalently, a sampling frequency of 500Hz. By the Nyquist theorem we should be able to acquire frequencies below 250Hz without aliasing (we note the low-pass filters at 24 and 44Hz plus the digital at 100Hz). We tested the sampling frequency acquiring a 50Hz generated signal and confirming we had 10 samples per period.

After collecting signal from the circuit and filtering it using 2 digital filters, some peak detection algorithms were applied to detect heart rate (4 digit 7 segment display showing it), QRS and ST time interval in the ISR function. Finally, based on this value, a simple algorithm to detect arrhythmias was proposed.

------------------------------- Complete Electric Circuit Scheme -------------------------------

After joining each block explained before, a final electric ECG circuit can be represented.On the left side, the voltage source represents the expected signal from the body, 2 electrodes are connected to the chest of the person under clinical analysis. The 3rd reference electrode (left wrist) will be in contact with the circuit’s ground.The circuit output signal is in the node V10 and is connected to an Arduino analog port.

Check PartSim reference [8], where all our ideas were brainstormed before implementation.

-------------------------------- ECG Real & Schematic Prototype --------------------------------

Schematic PrototypeAll the physical connections performed in the lab in a common breadboard are represented in Fig. 10. Due to the lack of some components in circuits.io (software used to design the circuit), assume functions generator as the electrodes connected to the body; the isolated 9V battery is connected to a specific adapter, which is plugged to the Arduino and 4 yellow cables were connected to extra digital pins which are not present in the scheme (pretend the pins used are possible digital inputs).

Real PrototypeThe actual board, with all the circuitry, is represented in Fig. 11. Because it is reasonably less perceivable, the previous scheme was added for a better understanding.

Results

In the end, we were successful in obtaining a good-looking ECG signal (Fig. 12 and 13) and plot it in the computer.

Heart Rate (HR)We were able to compute either the instantaneous heart rate (from the time between each R-peak) as the average heart rate (from the time of a 5 sequence of R-pulses). We chose to show in the 4 digits – 7 segments an average of both (so the displayed value was not as volatile as the instantaneous HR nor as static as the 5 sample HR). We also compared our HR computed valueswith the ones obtained with the plethysmography smartphone app Instant Heart Rate©, from Azumio Inc., and found them to be similar.

It was also possible to note the HR increase with deep inhalation and decrease with exhalation.

Features DetectionIn the Arduino, we implemented algorithms for R, S and T-wave detection. After trying both derivative and threshold based algorithms we went for the latter due to the better results, for the T-wave detection we experimented several empirical conditions.In a still person, in the absence of artifacts, our algorithm detects practically 100% of the R-peaks! (Thus, the very good results in HR calculation).The results, however are not so good in S and T-wave detection, in three 2 minute tests we missed 21, 7% of the S-wave and 27, 5% of the T-wave (S and T-wave misses appear to be uncorrelated). As we can see from Fig. 12, ECG varies from person to person, in one of our tests there were also some false detection of Q-wave peaks as S-wave. We had already identified some systematic errors in our S-wave detection algorithms and think that with more time we would be able to perform in S-wave detection as good as in R-wave. T-wave detection is a more complicated challenge, a literature search revealed that T-wave real-time detection is a hard problem, we think the solution would have to include derivatives (or even a conjugation of derivatives and thresholding).

QRS AmplitudeWe computed a naive algorithm for the detection of sinus arrhythmia, based on the variations of the QRS-complex amplitude. However, we did not test it because all our tests were performed on healthy subjects. Nevertheless, arrhythmia detection seems easy to achieve with our ECG prototype.

Segment DurationIn the beginning of this paper, we mentioned that segment duration was important to determine several heart disorders. With this in mind, we also designed an algorithm to compute RS-segment duration (and estimate QRS-complex duration by duplicating this value) and also ST-segment duration. However, in the latter, the obtained results are largely compromised due to the S and T-wave missed detections. Also, we only had the possibility to test the prototype in healthy subjects and thus we don’t know how helpful these could be in anomalies detection.

Discussion

The coaxial cables, the analogue and digital filters along with the reduction in the number and length of wires made possible the 50 Hz noise removal from the body’s ECG signal. Nevertheless, the output signal slightly contains its information compromised due to the noise. Its attenuation was not entirely achieved since the design and the choice of certain compounds may not be the most correct:

1. Different ampops have different common mode rejection ratios;2. Depending upon the diameter of the coaxial cables, they will better conduct low or high frequency signals.

These points were only slightly addressed due to time constraints.Nevertheless, we were able to detect practically 100% of all R-wave peaks, 78, 3% of S-wave and 72, 5% T-wave peaks, in real time, present in the signal. Last one with less accuracy due to the reduced dimensions and location in the signal, more difficult to threshold.Relatively to the processing task, we were able to develop an algorithm to measure heartbeat, the ST-segment time interval, as well as, the time across the QRS-complex. The maximum amplitude of this complex was also determined.

Conclusion

Since the world's population is ageing and particular regions are becoming depopulated, the healthcare assistance with continuous monitoring of physiological parameters (allowing preventive care, which is more effective and less costly than treating) for patients who live in remote locations can be a crucial device. Moreover, it would allow teleconsulting, patient medication, diagnosis and the prevention of unnecessary specialist consultations or laboratory examinations.

In this project, an electrocardiograph was designed and developed. A small prototype which acquires, with three electrodes, one placed in the arm and 2 in the chest, the cardiac electrical activity.Despite the tremendous difficulty in recognizing the cardiac activity from the input signal (acquired with the electrodes) we managed to greatly attenuate the noise and obtain a clean, feature recognizable, final signal.

The signal acquisition and instrumentation part of the prototype is working properly, we obtained a good-looking signal with a very simple circuit embedded in a highly portable system.However, we fell short on the signal processing part, real-time feature detection algorithms needed more time to be refined, and more controlled tests had to be performed to validate those detections, this however was out of the scope of this course.

A future improvement, besides algorithm refinement, would be to attach a wireless emitter to the system to allow signal monitoring without being connected to a computer. With the acquired signal stored on a computer, harder algorithms for feature detection that cannot be performed on real time, as Wavelet decomposition or Empirical Mode Detection, could also be used.

References

[1] “Electrocardiography Circuit Design”. Available at: http://www.egr.msu.edu/classes/ece480/capstone/spring13/group03/documents/ElectrocardiographyCircuitDesign.pdf [Consult. 06/07/2017];

[2] Instructables, “DIY EEG (and ECG) Circuit”. Available at: http://www.instructables.com/id/DIY-EEG-and-ECG-Circuit/ [Consult. 06/07/2017];

[3] Instructables, “Super Simple Electrocardiogram (ECG) Circuit “. Available at: http://www.instructables.com/id/Super-Simple-Electrocardiogram-ECG-Circuit/ [Consult. 06/07/2017];

[4] Picotech, “Electrocardiogram (ECG) circuit for use with oscilloscopes”. Available at: https://www.picotech.com/library/application-note/electrocardiogram-ecg-circuit-for-use-with-oscilloscopes [Consult. 06/07/2017];

[5] Engineers Labs, “ECG Circuit Analysis and Design”. Available at: http://engineerslabs.com/2012/01/ecg-circuit-analysis-and-design-simulation-by-multisim/ [Consult. 06/07/2017];

[6] “ECG Signal Acquisition Hardware Design”. Available at: https://courses.cs.washington.edu/courses/cse474/17wi/pdfs/lectures/Electrocardiography.pdf [Consult. 06/07/2017];

[7] “ECG Measurement System”. Available at: http://www.cisl.columbia.edu/kinget_group/student_projects/ECG%20Report/E6001%20ECG%20final%20report.htm [Consult. 06/07/2017];

[8] Partsim, ECG circuit designed. Available at: http://www.partsim.com/simulator/#79736;

[9] Autodesk Circuits, ECG prototype. Available at: https://circuits.io/circuits/5139757-iasb-ecg;

[10] Instant Heart Rate App. Available at: https://play.google.com/store/apps/details?id=si.modula.android.instantheartrate&hl=en

Code

Digital FiltersMATLAB
A IIR notch and Butterwoth high-pass filter.
No preview (download only).
ECG - PlotArduino
ECG code along with respective curve's visualization.
// -------------------------------------- Parameters ------------------------------------

const int analogInPin = A1;
int ledPin = 50;
int ledPin2 = 48;
int ledPin3 = 46;
int ledPin4 = 44;
int ledPin5 = 13;

// Originally collected signal (x, x1 and x2), notch filtered (y, y1, y2 and y3) and notch & low-pass filtered ECG signal (z, z1, z2 and z3).
// x = x(t); x1 = x(t-1); x1 = x(t-2). The same applies for y and z.
volatile double x = 0.0;
volatile double x1 = 0.0;
volatile double x2 = 0.0;

volatile double y = 0.0;
volatile double y1 = 0.0;
volatile double y2 = 0.0;
volatile double y3 = 0.0;

volatile double z = 0.0;
volatile double z1 = 0.0;
volatile double z2 = 0.0;
volatile double z3 = 0.0;

String bpm = "Normal Heart Rate";

double maxi;
double mini;

// Flag variable.
int aux = 0;

// Iterative variable.

int ii = 0;
int k = 0;
int l;

// Arrays
double timeVecto2 [512]; // Array which keeps 512 samples from the ECG signal (more or less, one cycle: sampling frequency = 500 Hz)
double timeVectorHigh2 [60]; // Saves 60 different instants (in ms) which corresponds to each R peak in the signal.
double res1;
double res2;
double resf;
double maxx = 0;
double minn = 10000;

// Defining combination of on and off pins to show each number.
const int numeral[10] = {
  B11111100, // 0
  B01100000, // 1
  B11011010, // 2
  B11110010, // 3
  B01100110, // 4
  B10110110, // 5
  B10111110, // 6
  B11100000, // 7
  B11111110, // 8
  B11110110, // 9
};

// Arduino pins used to connect all the 7 segments + dots.
const int segmentPins[] = { 22, 7, 8, 6, 5, 3, 2, 9};

// Number of digits allowed in the display.
const int numberofDigits = 4;

// Arduino pins used to connect all the 4 digits.
const int digitPins[numberofDigits] = {10, 11, 12, 4};
// --------------------------------------------------------------------------------------------------------- SETUP ---------------------------------------------------------------------------------------------------------

void setup() {

  Serial.begin(9600);

  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
  pinMode(ledPin5, OUTPUT);

  for (int i = 0; i < 8; i++)
    pinMode(segmentPins[i], OUTPUT); //set segment and DP pins to output

  //sets the digit pins as outputs
  for (int i = 0; i < numberofDigits; i++)
    pinMode(digitPins[i], OUTPUT);

  cli(); //stop interrupts

  //set timer1 interrupt at 500Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1Hz increments
  OCR1A = 3999;// = (16*10^6) / (8*500) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 bits for 8 prescaler
  TCCR1B |= (1 << CS11);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  sei(); //allow interrupts

}

// ------------------------------------------------------------------------------------------------------ INTERRUPTOR ------------------------------------------------------------------------------------------------------

ISR(TIMER1_COMPA_vect) { // commands executed at each time interrupt

  // -------------------------------------------- Acquiring Data ------------------------------------------

  x = analogRead(analogInPin);

  // ----------------------------------------------- Filtering --------------------------------------------

  // Filtering around 40 and 60 Hz (notch).
  y = 0.2012 * x - 0.3256 * x1 + 0.2012 * x2 + 0.3256 * y1 + 0.5975 * y2;

  // Low-pass Filter 100 Hz.
  z = 0.0495 * y + 0.1486 * y1 + 0.1486 * y2 + 0.0495 * y3 + 1.1619 * z1 - 0.6959 * z2 + 0.1378 * z3;

  x2 = x1;
  x1 = x;

  y3 = y2;
  y2 = y1;
  y1 = y;

  z3 = z2;
  z2 = z1;
  z1 = z;

  // Saving z value
  timeVecto2[k] = z;

  k = (k + 1) % 512;

}

// --------------------------------------------------------------------------------------------------------- LOOP ---------------------------------------------------------------------------------------------------------

void loop() {

  // ------------------------------------------ Plotting ECG Curve ----------------------------------------

  Serial.print(5 * 1000 * z / 1023);

  Serial.println();

  // ----------------------------------------- Detecting Heart Beat ---------------------------------------
  maxi = 0;
  mini = 10000;

  showNumber(round(resf));

  for (l = 0; l < 512; l++) {

    if (maxi < timeVecto2[l]) {

      maxi = timeVecto2[l]; // Signal Maximum's amplitude detected during one cycle.

    }

    if (mini > timeVecto2[l]) {

      mini = timeVecto2[l]; // Signal Minimum's amplitude detected during one cycle.

    }

  }

  // ----------------------------------------- Detecting Heart Beat ---------------------------------------

  //

  if (maxi - mini < 400 && maxi - mini > 150) {

    digitalWrite(ledPin4, HIGH);

  } else {

    digitalWrite(ledPin4, LOW);
  }

  // DETECTING R PEAK.

  if ( z > 0.9 * maxi ) {

    if (aux == 0) {

      timeVectorHigh2[ii] = millis(); // Saving 30 R peak time instants.

      aux = 1;

      res1 = 60000 / (timeVectorHigh2[ii] - timeVectorHigh2[(ii - 1) % 60]);

      res2 = 60000 * 5 / (timeVectorHigh2[ii] - timeVectorHigh2[(ii - 5) % 60]);

      ii = (ii + 1) % 60;

    }
    digitalWrite(ledPin5, HIGH);   // sets the LED on.
  } else {
    digitalWrite(ledPin5, LOW);    // sets the LED off.
    aux = 0;
  }

  // ---------------------------------------- Calculating Heart Rate --------------------------------------

  if (res1 > 40 && res1 < 200 && res2 > 40 && res2 < 200) {

    if (ii > 2) {

      resf = 0.5 * res1 + 0.5 * res2; // Weighting heart rate shown in the 4 digits 7 segments display. 30 % to Instant heart rate and 70 % to an average along 30 peaks to stabilize the output value.

    }

  }



  // ----------------------------------- Detecting Unusual Conditions ---------------------------------

  // DETECTING SINUS HEART RATE (beats per minute - bpm).

  if (resf < 60) {

    bpm = "Sinus Bradycardia";

    digitalWrite(ledPin, LOW);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, HIGH);

  } else if (resf >= 60 && resf < 100) {

    bpm = "Normal Heart Rate";

    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin, HIGH);
    digitalWrite(ledPin3, HIGH);

  } else {

    bpm = "Sinus Tachycardia";

    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin, HIGH);
    digitalWrite(ledPin2, HIGH);

  }

}

// -------------------------- 4 Digits 7 Segment Display (Auxiliar Functions) ------------------------

void showNumber (int number) {

  if (number == 0)
    showDigit (0, numberofDigits - 1); //display 0 in the rightmost digit.

  else {

    for (int digit = numberofDigits - 1; digit >= 0; digit--) {

      if (number > 0) {

        showDigit(number % 10, digit);

        number = number / 10;

      }

    }

  }

}

//Displays given number on a 7-segment display at the given digit position.
void showDigit (int number, int digit) {

  digitalWrite(digitPins[digit], HIGH);

  for (int segment = 1; segment < 8; segment++) {

    boolean isBitSet = bitRead(numeral[number], segment);

    isBitSet = ! isBitSet; // Remove this line if common cathode display.

    digitalWrite(segmentPins[segment], isBitSet);

  }

  delay(5);

  digitalWrite(digitPins[digit], LOW);

}
ECG - PrintArduino
ECG code along with respective printing.
// -------------------------------------- Parameters ------------------------------------

const int analogInPin = A1;
int ledPin = 50;
int ledPin2 = 48;
int ledPin3 = 46;
int ledPin4 = 44;
int ledPin5 = 13;

// Originally collected signal (x, x1 and x2), notch filtered (y, y1, y2 and y3) and notch & low-pass filtered ECG signal (z, z1, z2 and z3).
// x = x(t); x1 = x(t-1); x1 = x(t-2). The same applies for y and z.
volatile double x = 0.0;
volatile double x1 = 0.0;
volatile double x2 = 0.0;

volatile double y = 0.0;
volatile double y1 = 0.0;
volatile double y2 = 0.0;
volatile double y3 = 0.0;

volatile double z = 0.0;
volatile double z1 = 0.0;
volatile double z2 = 0.0;
volatile double z3 = 0.0;

String bpm = "Normal Heart Rate";

double maxi;
double maxi2;
double mini;
double sum;

// Flag variables.
int aux = 0;
int aux2 = 0;
int aux3 = 0;
double auxx;

// Iterative variable.
int ii = 0;
int k = 0;
int l;

// Arrays
double timeVecto2 [512]; // Array which keeps 512 samples from the ECG signal (more or less, one cycle: sampling frequency = 500 Hz)
double timeVectorHigh2 [60]; // Saves 30 different instants (in ms) which corresponds to each R peak in the signal.
double timeVectorHigh3 [60]; // Time duration between 30 different R peaks.
double timeVectorLow2 [60]; // Saves 30 different instants (in ms) which corresponds to each S peak in the signal.
double timeVector2Medium [60]; // Saves 30 different instants (in ms) which corresponds to each T peak in the signal.
double rt [60]; // 30 different RT intervals from the ECG signal.
double rs[60]; // 30 different RS intervals from the ECG signal.
double st[60]; // 30 different ST intervals from the ECG signal.
double rs2;
double st2;
double rsf;
double stf;
double res1;
double res2;
double resf;
double maxx = 0;
double minn = 10000;

// Defining combination of on and off pins to show each number.
const int numeral[10] = {
  B11111100, // 0
  B01100000, // 1
  B11011010, // 2
  B11110010, // 3
  B01100110, // 4
  B10110110, // 5
  B10111110, // 6
  B11100000, // 7
  B11111110, // 8
  B11110110, // 9
};

// Arduino pins used to connect all the 7 segments + dots.
const int segmentPins[] = { 22, 7, 8, 6, 5, 3, 2, 9};

// Number of digits allowed in the display.
const int numberofDigits = 4;

// Arduino pins used to connect all the 4 digits.
const int digitPins[numberofDigits] = {10, 11, 12, 4};
// --------------------------------------------------------------------------------------------------------- SETUP ---------------------------------------------------------------------------------------------------------

void setup() {

  Serial.begin(9600);

  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
  pinMode(ledPin5, OUTPUT);

  for (int i = 0; i < 8; i++)
    pinMode(segmentPins[i], OUTPUT); //set segment and DP pins to output

  //sets the digit pins as outputs
  for (int i = 0; i < numberofDigits; i++)
    pinMode(digitPins[i], OUTPUT);

  cli(); //stop interrupts

  //set timer1 interrupt at 500Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1Hz increments
  OCR1A = 3999;// = (16*10^6) / (8*500) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 bits for 8 prescaler
  TCCR1B |= (1 << CS11);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  sei(); //allow interrupts

}

// ------------------------------------------------------------------------------------------------------ INTERRUPTOR ------------------------------------------------------------------------------------------------------

ISR(TIMER1_COMPA_vect) { // commands executed at each time interrupt

  // -------------------------------------------- Acquiring Data ------------------------------------------

  x = analogRead(analogInPin);

  // ----------------------------------------------- Filtering --------------------------------------------

  // Filtering around 40 and 60 Hz (notch).
  y = 0.2012 * x - 0.3256 * x1 + 0.2012 * x2 + 0.3256 * y1 + 0.5975 * y2;

  // Low-pass Filter 100 Hz.
  z = 0.0495 * y + 0.1486 * y1 + 0.1486 * y2 + 0.0495 * y3 + 1.1619 * z1 - 0.6959 * z2 + 0.1378 * z3;

  x2 = x1;
  x1 = x;

  y3 = y2;
  y2 = y1;
  y1 = y;

  z3 = z2;
  z2 = z1;
  z1 = z;

  // Saving z value
  timeVecto2[k] = z; 

  k = (k + 1) % 512;
  
}

// --------------------------------------------------------------------------------------------------------- LOOP ---------------------------------------------------------------------------------------------------------

void loop() {

  // ------------------------------------------ Plotting ECG Curve ----------------------------------------


//  Serial.print(" ");
//  Serial.print(5 * 1000 * z / 1023);

  
//  Serial.println();

  // ----------------------------------------- Detecting Heart Beat ---------------------------------------
  maxi = 0;
  maxi2 = 0;
  mini = 10000;

  showNumber(round(resf));

  for (l = 0; l < 512; l++) {

    if (maxi < timeVecto2[l]) {

      maxi = timeVecto2[l]; // Signal Maximum's amplitude detected during one cycle.

    }

    if (mini > timeVecto2[l]) {

      mini = timeVecto2[l]; // Signal Minimum's amplitude detected during one cycle.

    }

    if (timeVecto2[l] < (maxi + mini) / 2 && timeVecto2[l] - timeVecto2[(l - 2)%512] > 0 && timeVecto2[l] - timeVecto2[(l - 2)%512] < 2 && abs(timeVecto2[l] - timeVecto2[(l - 10)%512]) < 5 ) {

      maxi2 = timeVecto2[l]; // Emprirical treshold for T peak detection.
      
    }

  }

  // ----------------------------------------- Detecting Heart Beat ---------------------------------------

  if (maxi - mini < 400 && maxi - mini > 100) {

    digitalWrite(ledPin4, HIGH);

  } else {

    digitalWrite(ledPin4, LOW);
  }

  // DETECTING R PEAK.

  if ( z > 0.9 * maxi ) {

    if (aux == 0) {

      timeVectorHigh2[ii] = millis(); // Saving 30 R peak time instants.
      timeVectorHigh3[(ii - 1)%60] = timeVectorHigh2[ii] - timeVectorHigh2[(ii - 1)%60];

      aux = 1;

      res1 = 60000 / timeVectorHigh3[(ii - 1)%60];

      res2 = 60000 * 5 / (timeVectorHigh2[ii] - timeVectorHigh2[(ii-5)%60]);

      
      
      // Print of the acquired data
      Serial.print("tS: ");
      Serial.print( timeVectorLow2[ii]);
      Serial.print("  ");
      Serial.print("tT: ");
      Serial.print( auxx);
      Serial.print("  ");
      Serial.print("tRS: ");
      Serial.print( rs[ii]);
      Serial.print("  ");
      Serial.print("tST: ");
      Serial.print( st[ii]);
      Serial.print("  ");
      Serial.print(" HR");
      Serial.print( res1);
      Serial.print("  ");
      
      Serial.println();
      Serial.print("ii: ");
      Serial.print( ii);
      Serial.print("  ");
      Serial.print("tR: ");
      Serial.print( timeVectorHigh2[ii]);
      Serial.print("  ");

      ii = (ii + 1) % 60;
    }
    digitalWrite(ledPin5, HIGH);   // sets the LED on.
  } else {
    digitalWrite(ledPin5, LOW);   // sets the LED off.     
      aux = 0;
  }

  // DETECTING S PEAK.

  if ( z < 1.3 * (mini)) {

    if(aux2 == 0){
      timeVectorLow2[ii] = millis(); // Saving 30 S peak time instants.
//      Serial.print("tS: ");
//      Serial.print( timeVectorLow2[ii]);
//      Serial.print(" ");
 
      aux2 = 1;
    } 
    
  } else {

    if(timeVectorLow2[ii] == 0){
      timeVectorLow2[ii] = timeVectorLow2[(ii-1)%60];
    }
    
    aux2 = 0;
  }

  //Serial.print(rs[3]);
  //Serial.println();


  // DETECTING T PEAK.

  if ( z > 0.95*maxi2 && z < 0.8*maxi) {

    auxx = millis();
    
    if (aux3 == 0 && (auxx - timeVectorHigh2[(ii-1)%60]) > 200) {

      timeVector2Medium[ii] = auxx;
//      Serial.print("tT: ");
//      Serial.print( auxx);
//      Serial.print(" ");
//      aux3 = 1;
      }
    aux3 = 1;
    
  } else {
    
    if(timeVector2Medium[ii] == 0){
      timeVector2Medium[ii] = timeVector2Medium[(ii-1)%60];
    }
    
    aux3 = 0;    
  }


  // ---------------------------------------- Calculating Heart Rate --------------------------------------

  if (res1 > 40 && res1 < 200 && res2 > 40 && res2 < 200) {

    resf = 0.5 * res1 + 0.5 * res2; // Weighting heart rate shown in the 4 digits 7 segments display. 50 % to Instant heart rate and 50 % to an average along 5 peaks to stabilize the output value.
  }

  // ----------------------------------- Detecting Unusual Conditions ---------------------------------

  // DETECTING SINUS HEART RATE (beats per minute - bpm).

  if (resf < 60) {

    bpm = "Sinus Bradycardia";

    digitalWrite(ledPin, LOW);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, HIGH);

  } else if (resf >= 60 && resf < 100) {

    bpm = "Normal Heart Rate";

    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin, HIGH);
    digitalWrite(ledPin3, HIGH);

  } else {

    bpm = "Sinus Tachycardia";

    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin, HIGH);
    digitalWrite(ledPin2, HIGH);

  }

  // Serial.print(bpm);
  // Serial.print('\n');

  // DETECTING SINUS ARRHYTHMIA

  if (timeVectorHigh3[(ii - 2)%60] > maxx && ii > 1 && timeVectorHigh3[(ii - 2)%60] < 1200) {

    maxx = timeVectorHigh3[(ii - 2)%60];

  }

  if (timeVectorHigh3[(ii - 2)%60] < minn && ii > 1 && timeVectorHigh3[(ii - 2)%60] > 320) {

    minn = timeVectorHigh3[(ii - 2)%60];

  }

  if (maxx - minn > 160 && maxx - minn < 300 && bpm == "Sinus Tachycardia") {

    //    Serial.print("Arrhythmia");
    //    Serial.println();

  } else {

    //    Serial.print("Normal Heart Rate");
    //    Serial.println();

  }

  // ----------------------------------- Showing Heart Rate Over Time ---------------------------------

  //Serial.println(resf);

  // ----------------------------------- RS Interval ---------------------------------

  if (abs(timeVectorHigh2 [(ii - 1) % 60] - timeVectorLow2[ii]) < 120) {

    rs [ii] = abs(timeVectorHigh2 [(ii - 1) % 60] - timeVectorLow2[ii]);

    sum = 0;

    for (l = 0; l < 60; l++) {

      sum += rs[l];

    }

    rs2 = sum / 60;

    rsf = 0.3 * rs2 + 0.7 * rs[ii];

  } else {

    rs [ii] = rs[(ii - 1) % 60];

  }

  // ----------------------------------- ST Interval ---------------------------------

  if (abs(timeVector2Medium[ii] - timeVectorLow2[ii]) < 350 && abs(timeVector2Medium[ii] - timeVectorLow2[ii]) > 100) {

    st [ii] = abs(timeVector2Medium [ii] - timeVectorLow2[ii]);

    sum = 0;

    for (l = 0; l < 60; l++) {

      sum += st[l];

    }

    st2 = sum / 60;
    stf = 0.3 * st2 + 0.7 * st[ii];

  } else {

    st [ii] = st[(ii - 1) % 60];

  }

}


// -------------------------- 4 Digits 7 Segment Display (Auxiliar Functions) ------------------------

void showNumber (int number) {

  if (number == 0)
    showDigit (0, numberofDigits - 1); //display 0 in the rightmost digit.

  else {

    for (int digit = numberofDigits - 1; digit >= 0; digit--) {

      if (number > 0) {

        showDigit(number % 10, digit);

        number = number / 10;

      }

    }

  }

}

//Displays given number on a 7-segment display at the given digit position.
void showDigit (int number, int digit) {

  digitalWrite(digitPins[digit], HIGH);

  for (int segment = 1; segment < 8; segment++) {

    boolean isBitSet = bitRead(numeral[number], segment);

    isBitSet = ! isBitSet; // Remove this line if common cathode display.

    digitalWrite(segmentPins[segment], isBitSet);

  }

  delay(5);

  digitalWrite(digitPins[digit], LOW);

}

Comments

Similar projects you might like

Portable Temperature Station V2: IOT Edition

Project tutorial by Isaac100

  • 2,659 views
  • 0 comments
  • 7 respects

$10 Portable Arduino Weather Station (AWS)

Project tutorial by Prajjwal Nag

  • 31,072 views
  • 6 comments
  • 58 respects

Portable Temperature Station

Project tutorial by Isaac100

  • 7,669 views
  • 8 comments
  • 35 respects

DIY Portable Distance Detection Device

Project tutorial by Viktor S

  • 5,598 views
  • 5 comments
  • 15 respects

Portable Environment Monitor

Project tutorial by Chathuranga Liyanage

  • 1,911 views
  • 1 comment
  • 6 respects

'Roger Bot' the Pet Rover

Project showcase by hannu_hell

  • 1,585 views
  • 0 comments
  • 12 respects
Add projectSign up / Login