User Tools

Site Tools


methods:ppg:ppg_readout

Pulse detection with a microcontroller

An electronic circuit that detects the blood pulse ear clip and filters the data is described on the PPG page. The circuit has two outputs: an analog output with the raw pulses and a digital output with detected pulses. The circtui for measuring ECG or EMG also delivers an analog signal. Such signals have to be sampled for feature detection. This chapter describes how to read both outputs with a microcontroller. For hardware related issues, see the page on Simplified Microcontroller Boards.

Analog oscilloscope mode and digital timing interval mode

For the microcontroller code we have two approaches. In the first option, we sample the analog input at equidistant intervals. This is a good option to view the analog signal, hence the name “oscilloscope mode”. To sample at 500Hz, we have to use a timer interrupt.

The second approach is to use the digital signal from the comparator and create a hardware interrupt at every low-to-high zero crossing. The information is in the edge to edge interval, and this is referred to as “interval mode”.

Arduino

The Arduino platform1) is based on an Atmel AVR microcontroller. The primary board type is the Arduino UNO/Genuino and uses the ATmega328P. The advantage is that the Arduino development IDE is very simple and uses a shell around the C-language to make things easy. In the sections below, the Arduino C dialect is used when possible, although for the fast (500Hz) sampling of heart pulses envelope some low level C commands are needed to address CPU registers immediately.

Analog “oscilloscope” mode on Arduino

In the analog oscilloscope mode, we have to configure the Arduino such that every $2ms$ the timer 1 of the AVR microcontroller generates an interrupt. When the interrupt occurs, a sample is read from an analog input pin and send to a computer via a serial connection over USB.

In “Clear Timer on Compare” or “CTC mode” (WGM13:0 = 4), the OCR1A register is used to manipulate the counter resolution. In CTC mode the counter is cleared to zero when the counter value (TCNT1) matches the OCR1A. The OCR1A defines the top value for the counter, hence also its resolution. The waveform generated will have a maximum frequency of $f_{OC1A} = f_{clk\_I/O}/2$ when OCR1A is set to zero (0x0000). The waveform frequency is defined by the following equation:

\begin{equation} f_{OCnA}=\frac{f_{\text{clk_I/O}}}{2\cdot N\cdot \left ( 1+OCRnA \right )} \label{eq:Timer_fOCnA} \end{equation}

The $N$ variable represents the prescaler factor (1, 8, 64, 256, or 1024).

The code for sampling the analog signal on the analog pin 0 with a fixed frequency of $500Hz$ is as follows:

/**
 * Arduino code: Analog oscilloscope mode
 */
const int Baudrate = 19200;
const int Analog_Input_Pin = 0;               
 
void setup()
{
    Serial.begin(Baudrate);   // For sending data to the computer over USB
    cli();                    // disable interrupts while messing with their settings   
    TCCR1A = 0x00;            // clear default timer settings
    TCCR1B = 0x00;            // timer in normal mode
    TCCR1B |= (1 << WGM12);   // Configure timer 1 for CTC mode
    TCCR1B |= (0 << CS12);    // Set timer prescaling by setting 3 bits
    TCCR1B |= (1 << CS11);    // 001=1, 010=8, 011=64, 100=256, 101=1024
    TCCR1B |= (1 << CS10);
    TIMSK1 |= (1 << OCIE1A);  // Enable CTC interrupt with OCF1A flag in TIFR1
    OCR1A  = 124;             // Set CTC compare value, results into 500Hz for fI/O = 8MHz
    sei();                    // turn interrupts back on
}
 
void loop() {
  // nothing to do, its all in the interrupt handlers!
}  
 
ISR(TIMER1_COMPA_vect)            // when timer counts down it fires this interrupt for scope-mode
{  
  int val = analogRead(Analog_Input_Pin);
  Serial.write( (val >> 2));      // restrict to 8 bits
}

Digital timing interval mode on Arduino

In interval mode, the output signal of the comparator (DIGITAL_OUT in Figure 3.3) is connected to digital pin 2 of the Arduino. The concept is that this pin can generate an INT0 hardware interrupt at every low-to-high zero crossing. When doing so, the interesting information is in the time between the interrupt and the previous interrupt. This time can be measured with a resolution of 4µs which is more than accurate enough.

The code looks like:

/**
 * Arduino code: Digital timing interval mode
 */
const int Baudrate = 19200;
const int Digital_Input_Pin = 2;  // the pin that the heart-rate sensor is attached which contains INT0
 
void setup()
{
  Serial.begin(Baudrate);           // For sending data to the computer over USB
  pinMode(Digital_Input_Pin, INPUT);
  attachInterrupt(0, Send_Interval, RISING);  // Attach to INT0
}
 
void loop() {
  // nothing to do, its all in the interrupt handlers!
}  
 
unsigned long LastTime, NewTime, j;
void Send_Interval()
{ 
  NewTime = micros();  // Resolution 4us, only one overrurn in 70 minutes
  Serial.println(NewTime-LastTime, HEX); 
  LastTime = NewTime;
}

Cerebot MX4: using MPIDE

At Fontys University of Applied Sciences, the Digilent Cerebot MX4 microcontroller platform2) is one of the preferred embedded software platforms besides Arduino. The microcontroller on the Cerebot MX4 is the PIC32MX460F512L of the PIC32 family by Microchip3), and the programming platform MPLAB4). The implementation using this platform is explained here.

According to the description of the Cerebot MX4 board, it is compatible with the Arduino development platform. This gives the impression we can install the “MPIDE” environment (the Arduino equivalent for ChipKits) and upload the code of section 4.4. This is not true for two reasons:

  • The MX4 needs a bootloader to become Arduino compatible
  • The pin definitions are different

As a default, the bootloader is installed. However, if the board is once used with MPLAB to upload a program, the chipKIT boot loader is erased. To use the board with the MPIDE again, it is necessary to program the boot loader back onto the board. The programming file for the boot loader that was programmed into the board by Digilent at the factory is available for download from the ChipKIT Pro MX4 product page on the Digilent web site. Additionally, the boot loader source code is available in the chipKIT project repository at www.github.com/chipKIT32/pic32-Arduino-Bootloader. To reprogram the boot loader using MPLAB, perform the following steps:

  • Use the “Configure.Select Device …” menu to select the PIC32MX460F512L
  • Use the “Programmer.Select Programmer” menu to select the “Licensed Debugger”.
  • Use the “File Import…” dialog box to navigate to and select the boot loader programming downloaded from the Digilent web site. The file name will be something like: chipKIT_Bootloader_MX4.hex.
  • Use the “Programmer.Program” command to program all memories on the device.

For the new pin definitions there are two modifications needed. The input pin is pin number 53. This is the one with INT0 functionality on the PIC32. It can be found on header JH-08 and it is called “RD0”. The corresponding interrupt to attach to is called “EXT_INT0” and not “INT0”. As a result, the code becomes:

/**
 * PIC32MX4 Code
 * Digital timing interval mode
 * Using MPIDE
 */
const int Baudrate = 19200;
const int Digital_Input_Pin = 53;  // the pin that the heart-rate sensor is attached which contains INT0
 
void setup()
{
  Serial.begin(Baudrate);           // For sending data to the computer over USB
  pinMode(Digital_Input_Pin, INPUT);
  attachInterrupt(EXT_INT0, Send_Interval, RISING);  // Attach to INT0
}
 
void loop() {
  // nothing to do, its all in the interrupt handlers!
}  
 
unsigned long LastTime, NewTime, j;
void Send_Interval()
{ 
  NewTime = micros();  // Resolution 4us, only one overrurn in 70 minutes
  Serial.println(NewTime-LastTime, HEX); 
  LastTime = NewTime;
}

Postprocessing of interval data: communication with a PC

The Arduino sends high resolution samples over the USB bus to a computer for evaluation. It is up to the user to choose the favorite programming environment to represent the data. When Arduino sends data over the USB bus as text strings, every serial port communicator program can interact. While debugging, the best option is to use the Serial Port Monitor of the Arduino IDE. When this works, programs like Processing5) or LabVIEW6) can be used.

From LabVIEW to Arduino

The standard LabVIEW examples “Simple Serial.vi” and “Continuous Serial Write and Read.vi” are a good starting point. What is used under the hood is the Virtual Instrument Software Architecture VISA7), which gives a universal access to I/O. VISA provides the programming interface between the hardware and development environments such as LabVIEW.

A sketch that comes by default with the Arduino IDE is the “Physical Pixel” program. It makes pin 13 high when an “H” character is recieved over the serial bus (or serial virtual COM port over USB), and it makes pin 13 low when a charecter “L” is recieved. So, the only thing a LabVIEW program has to do, is to send an “H” character or an “L” character depending on the state of a button. This is shown in figure 1 which is attached as a LabVIEW vi below in the Downloads section.

Fig. 1: Example program in LabVIEW to control the "Physical Pixel" Arduino sketch. In this case, LabVIEW writes to the Arduino sketch

From Arduino to LabVIEW

Two-way communication: From Arduino to LabVIEW and back with handshakes

An example is based on the LabVIEW example in the help files: “Continuous Serial Write and Read.vi”. That one is already capable of sensing text to Arduino and recieving taxt-based responses. The next step is to implement a protocol.

The used LabVIEW code is “Continuous Serial Write and Read +get data.vi” and the Ardiuno code “Send_2_bytes.ino” as attached in the Downloads section.

In this case the communication is:

  • Arduino sends a “>” character to indicate it is ready
  • LabVIEW responds with an “S” character
  • Arduino recognises the “S” charecter and replies with two measurements in the format “123-123”, terminated with a carriage reurn and a prompt “>”
  • Now LabVIEW can decode the string with the data and ask for the next sample by sensing another “S”.

Downloads

File Program Version Description
Arduino LED switch Simple.vi LabVIEW 2015 1.0 LabVIEW example to interface with the “Physical Pixel” Arduino sketch
Continuous Serial Write and Read +get data.vi, Serial - Settings.ctl LabVIEW 2012 1.0 LabVIEW example to read two bytes from Arduino using handshakes. You may need the control as well
Send_2_bytes.ino Arduino 1.6.12 1.0 Arduino code for the use with “Continuous Serial Write and Read +get data.vi”
dg233_2012.pdf Acrobat April/May 2012 Geert Langereis, TU/e, Arduino course using mainly plain C
methods/ppg/ppg_readout.txt · Last modified: 2017/10/10 17:05 by glangereis