Developed Pulsoximeter with 2 Arduinos.

I have developed a pulsoximter with 2 Arduinos:

Pulse oximetry is a non-invasive method for monitoring of a patient’s oxygenation. The Heartbeat BPM is detectable too.

Arduino Mega responsible for the oximetry electronics and Arduino Uno for the PPG graph. The electronics includes LED Driver, Photocurrent transformation, patient-dependent LED calibration , Active filters, controlling the Nellcor SpO2 sensor. Adafruit OLED displays Vitalparameter BPM und SPO2. Noritake VFD display GUU-100 shows the PPG(Photoplethysmograph). The boards are connected to the electronics with a Protoshield.

Compared to a clinical diagnostic device, the deviation in the average is 1 percent !!! Both Arduino are in system updatable via USB.

Looks awesome ! Are you going to share the DIY documentation ? (schematics, code , etc ...)


//code for PPG

#include <Noritake_VFD_GUU100.h> // Bibliothek Noritake GU128X64E-U100GUU-100
#include "fonts/allFonts.h"      // Bibliothek Schriftarten

static Noritake_VFD_GUU100 vfd;
#define KEY_DDR         DDRD
#define KEY_PORT        PORTD
#define KEY_PIN         PIND
#define KEY0            PORTD0
#define KEY1            PORTD1
#define KEY2            PORTD2
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1| )
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20                        // every 200ms

volatile uint8_t key_state;                                // debounced and inverted key state:
volatile uint8_t key_press;                                // key press detect
volatile uint8_t key_rpt;                                  // key long press and repeat
ISR( TIMER2_OVF_vect )                            // every 10ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;
  TCNT2 = (uint8_t)(int16_t)-(16000000 / 1024 * 10e-3 + 0.5);  // preload for 10ms
  i = key_state ^ KEY_PIN;                        // Taster HIGH AKTIV!
  ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll over ?
  key_state ^= i;                                 // then toggle debounced state
  key_press |= key_state & i;                     // 0->1: key press detect

  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
    rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
  }
}
uint8_t get_key_press( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}
uint8_t get_key_rpt( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}
uint8_t get_key_state( uint8_t key_mask )
{
  key_mask &= key_state;
  return key_mask;
}
uint8_t get_key_short( uint8_t key_mask )
{
  cli();                                          // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}
// Arduino Boot-Logo
static const uint8_t PROGMEM image_ArduinoLogo[] = {
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xc0,0xe0,0xe0,0xf0,0xf0,0xf8,0xf8,
  0xfc,0xfc,0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,0xfe,0xfe,0xfc,0xfc,0xfc,0xf8,0xf8,
  0xf0,0xf0,0xf0,0xe0,0xc0,0xc0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xc0,0xe0,0xe0,0xf0,0xf0,0xf8,0xf8,
  0xf8,0xfc,0xfc,0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,0xfe,0xfc,0xfc,0xfc,0xf8,0xf8,
  0xf8,0xf0,0xf0,0xe0,0xc0,0xc0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x80,0xe0,0xf0,0xfc,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0x1f,0x1f,0x0f,
  0x07,0x07,0x03,0x03,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x03,0x03,0x03,0x07,0x07,0x0f,0x1f,0x1f,
  0x3f,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0xc0,0xe0,
  0xf0,0xf8,0xfc,0xfc,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0x3f,0x1f,0x0f,0x0f,
  0x07,0x07,0x03,0x03,0x01,0x01,0x01,0x01,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,
  0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x03,0x03,0x03,0x07,0x07,0x0f,0x1f,0x3f,
  0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfc,0xf8,0xf0,0xc0,0x00,0x00,0xfe,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,
  0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x01,0x03,0x07,0x0f,0x9f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0x9f,0x0f,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0xf8,0xf8,0xf8,0xf8,0xf8,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,
  0xf8,0xf8,0xf8,0xf8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x07,0x3f,0x7f,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xc0,0xe0,
  0xf0,0xf8,0xfc,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0x3f,0x3f,0x7f,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xfe,0xfc,0xf0,0xe0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xc0,0xf0,
  0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0x1f,0x03,0x00,0x00,0x00,0x01,
  0x03,0x07,0x0f,0x1f,0x3f,0x3f,0x7f,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfc,0xfc,
  0xf8,0xf8,0xf8,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,
  0xf0,0xf0,0xf0,0xf0,0xf8,0xf8,0xf8,0xfc,0xfc,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,
  0x7f,0x3f,0x3f,0x1f,0x0f,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,
  0x07,0x0f,0x0f,0x1f,0x3f,0x7f,0x7f,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfc,0xfc,
  0xf8,0xf8,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,0xf0,
  0xf0,0xf0,0xf0,0xf0,0xf8,0xf8,0xfc,0xfc,0xfc,0xfe,0xff,0xff,0xff,0xff,0xff,0x7f,
  0x7f,0x3f,0x1f,0x1f,0x0f,0x07,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x03,0x03,0x03,0x07,0x07,0x07,
  0x07,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
  0x0f,0x0f,0x0f,0x0f,0x07,0x07,0x07,0x07,0x03,0x03,0x03,0x01,0x01,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x03,0x03,0x03,0x07,0x07,0x07,
  0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
  0x0f,0x0f,0x0f,0x0f,0x07,0x07,0x07,0x07,0x03,0x03,0x01,0x01,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,
  0xf8,0xfe,0xff,0x3f,0x1f,0x7f,0xff,0xfe,0xf8,0xc0,0x00,0x00,0x00,0x00,0x00,0xfe,
  0xff,0xff,0xff,0xff,0x87,0x87,0x87,0x86,0xce,0xfe,0xfe,0xfc,0x7c,0x00,0x00,0x00,
  0x00,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x06,0x0e,0x0e,0x1e,0xfe,0xfc,0xfc,0xf8,
  0xc0,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
  0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x07,0x07,0x07,0xff,0xff,0xff,0xff,0xff,0x07,
  0x07,0x07,0x07,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x7f,0xfe,0xf8,0xe0,0xc0,
  0x00,0x00,0xfe,0xff,0xff,0xff,0x00,0x00,0xe0,0xf8,0xfc,0xfe,0x7e,0x0e,0x07,0x07,
  0x07,0x07,0x07,0x0e,0xfe,0xfe,0xfc,0xf8,0xe0,0x00,0x80,0xf0,0xfc,0xff,0x7f,0x1f,
  0x1f,0x1c,0x1c,0x1c,0x1c,0x1f,0x1f,0x7f,0xff,0xff,0xf8,0xe0,0x00,0x00,0xff,0xff,
  0xff,0xff,0x7f,0x03,0x03,0x03,0x0f,0x3f,0x7f,0xfe,0xf8,0xf0,0xc0,0x00,0x00,0x00,
  0xff,0xff,0xff,0xff,0xe0,0xe0,0xe0,0xe0,0x60,0x70,0x78,0x7f,0x3f,0x1f,0x0f,0x03,
  0x00,0x00,0x3f,0x7f,0x7f,0xff,0xf0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,0x7f,0x7f,0x3f,
  0x1f,0x00,0x00,0x00,0x00,0xe0,0xe0,0xe0,0xe0,0xff,0xff,0xff,0xff,0xff,0xe0,0xe0,
  0xe0,0xe0,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x03,0x0f,0x1f,0x7f,
  0xfe,0xff,0xff,0xff,0xff,0x00,0x00,0x0f,0x1f,0x3f,0x7f,0x7e,0xf0,0xe0,0xe0,0xe0,
  0xe0,0xe0,0xf0,0x7f,0x7f,0x3f,0x1f,0x07
};

float U =0;
int i = 0;                                        // Wert X-Achse
int Pin = A0;                                     // ADC an PIN A0
int POTI10K = A1;                                 // POTI an ADC Pin A1
int PPG =0;                                       // einzulesene Spannung der PPG
int Poti=0;                                       // Potiwert um Framezeit zu variieren

// code PPG setup and loop

void setup(){
 KEY_DDR &= ~ALL_KEYS;                // configure key port for input
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
  TCCR2B = (1<<CS22)|(1<<CS20);         // divide by 1024
  TCNT2 = (uint8_t)(int16_t)-(16000000 / 1024 * 10e-3 + 0.5);  // preload for 10ms
  TIMSK2 |= 1<<TOIE2;                   // enable timer interrupt
  sei();
  vfd.reset();
  vfd.init();
  vfd.clearScreen();
  delay(200); 
  //Animation
  vfd.clearScreen();
  circleOut();
  vfd.clearScreen();
  circleOut();
  vfd.clearScreen();
  circleOut();
  vfd.reset();
  vfd.init();
  vfd.clearScreen();
  
// Startbalken
  for(int s = 0 ; s<= 127 ; s++)

  {

    vfd.setFont(thin_8x16);                         // Schriftgröße und Schriftart setzen
    vfd.setCursor(12,22);                           // Cursor setzen (Zeile 16, 21.Pixel)
    vfd.println("STARTING ...");                    // Schriebbefehl mit Inhalt
    vfd.drawLine(s,45,s,59,1); 
    delay(25);
  }
   
  vfd.clearScreen();
  delay(200);
  vfd.init();
  vfd.drawImage(image_ArduinoLogo, 0,0, 127,63);   // Bitmapdatei Arduino-Logo schreiben
  delay(3000);                                     // Wartezeit von 5 sek.
  vfd.clearScreen();                               // Display löschen
  vfd.reset();
  vfd.init();
  vfd.clearScreen();
}
//Hauptprogramm
void loop(){ 
  vfd.clearScreen();                              // Display löschen
  vfd.drawRect(0,0,127,63,1);                     // Aussenrahmen zeichnen


  for (i = 0; i <= 127; i++)                     // Zähler bis 128 ( für Länge =128 Pixel= x-Achse)
  {  
    PPG = analogRead(Pin);                       // Spannungswert  PPG einlesen
    Poti = analogRead(POTI10K);                  // Spannungswert Poti einlesen

    U=PPG/4.6;                                   // U-Variable  Vorskalierung 
    U=map(U,0,110,7,63);                         // Skalierung der Y-Achse ( 64 Pixel) 
    int Z = Poti;                                // Z-Variable deklariert für variable Frames
    Z=map(Z,0,1023,1,35);                        // Wartezeit von Z in 1-35 Millisek skaliert
    delay(Z);                                    // Wartezeit von Z in 1-35 Millisek skaliert// Wartezeit aus Z entnehmen
    if( get_key_press( 1<<KEY0 ))                     // Wenn Digitaleingang 2 HIGH hat
    {
      vfd.drawLine(i,U,i,63,1);                  // PPG Graphen integriert schreiben
    }
    else if ( !get_key_press( 1<<KEY0 ))                // Wenn Digitaleingang 2 LOW hat
    {
      vfd.fillCircle(i,U-1,0.7,1);               // PPG Graphen als Linie schreiben
      vfd.fillCircle(i,U,0.7,1);
    }
  }                                    

}

void circleOut(void)                             // Animation beim Start
{
  uint8_t x, y;

  for(x=1; x<64; x++) {
    vfd.drawCircle(64, 32, x, 1);
    vfd.drawCircle(64, 32, x, 0);
  }
}

// code attiny85 for LED Multiplexing

#include <avr/io.h>                          // Bibliothek GPIOs
#include <avr/interrupt.h>                   // Bibliothek Interrupts

volatile byte counter = 0;                   // Counter für Case-Struktur
// Zuweisungen der anzusteuernden Pins 
int main(void)
{
  DDRB = 0b00000011;                         // Port B0 & B1 aus Ausgang gesetzt
  PORTB &= ~(1<<PB0);                        // Port B0 mit LOW-Pegel
  PORTB &= ~(1<<PB1);                        // Port B1 mit LOW-Pegel
  
 
  TCCR1 = (1<<CS12)| (1<<CS11) | (1<<CS10);  
  OCR1A = 128;                               // Errechneter Zeitwert für 1000µs,124, Correktion without Ext. crystal=128
  TIMSK = (1<<OCIE1A);                       
  sei();                                     // Interrupt aktivieren
}

while(1)                                     //  Endlosschleife bleibt leer
  {}

ISR(TIMER1_COMPA_vect) {       // Aufruf  "Timer Interrupt" ISR CTC-Modus
  TCNT1 = 0;                   // Counterregister auf Null gesetzt
  counter = (counter + 1) % 6; // Counter von 0-3 für entsprechenden Schaltzustand zählen
  switch(counter) {
  case 0: 
    PORTB |=  (1<<PB0);        // 1000µs LED rot an
    PORTB &= ~(1<<PB1);        // 1000µs LED infrarot aus
    break;
  case 1:
    PORTB |=  (1<<PB0);        // 1000µs  LED rot an
    PORTB &= ~(1<<PB1);        // 1000µs  LED infrarot aus
    break;
  case 2: 
    PORTB &= ~(1<<PB0);        // 1000µs  LED rot aus
    PORTB &= ~(1<<PB1);        // 1000µs  LED infrarot aus
    break;
  case 3: 
    PORTB &= ~(1<<PB0);        // 1000µs  LED rot aus
    PORTB |=  (1<<PB1);        // 1000µs  LED infrarot an
    break; 
  case 4: 
    PORTB &= ~(1<<PB0);        // 1000µs  LED rot aus
    PORTB |=  (1<<PB1);        // 1000µs  LED infrarot an
    break; 
  case 5: 
    PORTB &= ~(1<<PB0);        // 1000µs  LED rot aus
    PORTB &= ~(1<<PB1);        // 1000µs  LED infrarot aus
    break;
  }
}

code for SPO2 still follows

THANKS !

Do you have a PDF for the schematic (just curious. Not absolutely necessary . Just makes it easier to view online.)

Is your Avatar some kind of Germanium diode ? (Is there some significance to your handle "die_diode" ?)

PS- The only part number I can't quite make out is the op amps. Are they OPA347s ?

The schematic is not printing correctly .

Overall , I would say that it looks professional enough that you should consider making it available in kit form (ala Heathkit

http://www.heathkit-museum.com/
http://www.vintage-radio.info/heathkit/

Obviously this would require making pcbs to replace the hand wired boards you have. You might want to consider a single board with everything. (using the ATmega328 chips to replace the Mega. You could take preliminary orders to see how much interest there is and if there is enough you can get quotes on the pcb in small qty. (less than 100) unless you have enough interest to warrant a larger run.
You would need an assembly drawing and instruction manual and possibly a video but from what I can see, the build should be pretty straight forward. I’ve built a lot of equipment and your box looks laid out well and easy to work with. What are the heatsinked devices ?
(regulators for the +9V and +5V supplies. (I didn’t see any other voltages)

Also,
Attached are datasheets for an analog switch and an op amp. Both are ideal for operation on 5V.
I’m not suggesting they are any better than what you used but you might take a look at their specs to see if they could be of use to you for this project.

ADG511_512_513[1].pdf (141 KB)

LT1215.pdf (339 KB)

great idea

Yes there are OPA 347, they are cheap, easy to wire, and suitable for single supply operation.
The MAX313 analog switches are great, have a very low resistance and can switch to several MHz.

The heat sinks are generously dimensioned. (For 9V and 5V), because the Noritake draws a lot of power:(

circuit.pdf (130 KB)

raschemmel: Overall , I would say that it looks professional enough that you should consider making it available in kit form (ala Heathkit http://en.wikipedia.org/wiki/Heathkit http://www.heathkit-museum.com/ http://www.vintage-radio.info/heathkit/

Obviously this would require making pcbs to replace the hand wired boards you have. You might want to consider a single board with everything. (using the ATmega328 chips to replace the Mega. You could take preliminary orders to see how much interest there is and if there is enough you can get quotes on the pcb in small qty. (less than 100) unless you have enough interest to warrant a larger run. You would need an assembly drawing and instruction manual and possibly a video but from what I can see, the build should be pretty straight forward. I've built a lot of equipment and your box looks laid out well and easy to work with. What are the heatsinked devices ? (regulators for the +9V and +5V supplies. (I didn't see any other voltages)

Also, Attached are datasheets for an analog switch and an op amp. Both are ideal for operation on 5V. I'm not suggesting they are any better than what you used but you might take a look at their specs to see if they could be of use to you for this project.

Who are you kidding?

In kit form?

You ask about regulators looking at the schematics?

I'd say, don't build it. It could be a matter of life and death.

@Janost,

What is that comment supposed to mean ? I asked if the heatsinked devices in the photo were the regulators? What does that have to do with the schematic ? The regulator ic part numbers aren't even on the schematic. Are you implying something about my knowledge of electronics or what ? How much electronics experience do you have ? You comment indicates either you are not paying attention or don't know much about electronics. There was nothing unusual about my question about the heatsinks.

It could be a matter of life and death

That's nonsense. It is certainly not a matter of life and death. If you had any hospital experience you would know that patients suffering from rapid heartbeat or low oxygenation cannot be fooled by a machine. Also, this instrument is not critical care equipment. It is used by people who want to monitor their respiratory or circulatory condition. They do not require this to know when they are in distress. They know that without any equipment. Have you worked in an ICU or a hospital ? Are you medically trained ? . That comment shows you know nothing about medical care and have no hospital experience. Patients know when their pulse is too fast and when their oxygenation is too low. It presents itself as distress,the kind that cannot be ignored. (equivilent to alarm bells ringing in their head) and it doesn't require any equipment to be aware of that. A patient in distress due to low oxygenation or rapid heartbeat cannot be fooled by a machine. They will tell you without hesitation that there is a problem and they don't give a damn what the machine says.

@ die_Diode:

Nice project. My wife is a nurse and I bought her one just to play with for about $20 off the eBay site. It is Chinese but has an OLED screen and a graph function. She has compared the results with the (seriously more expensive) office device and the two correlate.

Which brings me to the question, "Why?" I know that in my playing around that I build lots of items that are primarily just for fun. I usually take pictures, post the project, pixs, and code and then disassemble the unit. From the looks of it, your unit is here to stay and the fact that you have access to a clinical unit for comparison seems to indicate that you have a use for the device. So, is the attraction that you can interface your unit, save results, compare data on a PC, or perhaps further enhance to become an Internet thing? Your BOM is greater than $20, so no criticism, just trying to understand.

Ray

Thanks so much for posting details about this project! I'm a physician with software engineering experience and have been attempting to ascertain the minimum hardware necessary to play with a nellcor sensor and arduino uno clone. I found the nellcor 9-pin information online, but given I'm still a total novice with regard to circuitry and am having trouble understanding your schematic. I know it's a lot to ask to break this circuit down for a noob, so I'm curious if anyone has any general recommended resources for understanding the circuit components?

If anyone has the patience to answer more basic/specific questions: -is the function of all the non-display circuitry basically amplification and filtering that can't be done on the software side? -any recommended resources for wiring the nellcor to the arduino in more basic setups (i.e. bare-bones functional connection)? the ones I've seen online have not yet been very helpful... -any reason an uno or other lower-tech arduino couldn't be used instead of a mega? Same clock speed...power/input requirements?

We rely on these a lot in the hospital, and oftentimes these going off is our first notification that a patient is decompensating. Nonetheless the ones we use are often quite unreliable with many patients with e.g. motion artifacts or unique skin characteristics fuck up the sat readings, thus why we're sometimes moving the sensor from the finger to the forehead to the earlobe while waiting for an arterial blood gas sat during emergencies. Take home message is that if these are open sourced more and people come up with better hardware/algorithms we could take better care of people in the hospital.