Around the same time I bought a cheap eCO2 sensor, I bought a MAX30100 pulse oximeter board for a few quid. I never got it working, partly because the protocol for making it work is very complex (way more complex than an air quality sensor), partly because I didn’t know what I was doing, but also (I now discover) because I was using a faulty breakout board.
Flushed with the success of getting the eCO2 sensor working, I decided I’d have a crack at getting the pulse oximeter working in Python on a BBC micro:bit. And I failed. For now, at least. I even found a MicroPython library for the MAX30100 but it doesn’t work on a micro:bit, and the protocol is too complex for me to figure out just now. I may return to it one day.
There are, of course, Arduino drivers for the MAX30100, and perhaps a little known fact is that you can program the micro:bit using the Arduino IDE. It’s not, I warn you, very easy, and I’m not offering a step-by-step guide, but I’ll explain how you can build your own and offer a HEX file you should be able to flash direct on to a micro:bit (V2 only at the moment, though I think you could compile your own for a V1). You’ll need a micro:bit breakout board that exposes the i2c pins 19 and 20, some jumper wires, a breadboard, a MAX30100 pulse oximeter, and an optional 128×64 OLED display with an i2c interface.
Wiring could not be simpler as you connect the display and sensor the same way: GND pins to micro:bit GND, VCC to micro:bit 3v, SCLs to micro:bit SCL pin 19 and SDAs to micro:bit SDA pin 20. Flash this hex file (right-click and save link or target) to the micro:bit, you may need to press reset on the back of the micro:bit and the LED on the oximeter should light up. Put your thumb on it, and off you go. It also prints readings to the serial port, so you can do some data logging over USB on a computer as well, just open up a terminal app or web browser like the Google Chromelabs one or the micro:bit Python editor.
One note of caution: if you use my HEX file, the i2c addresses of the display and sensor are hard-coded, assuming the OLED display is 0x3C (decimal 60) and the sensor is 0×57 (decimal 87). If yours have different addresses, you’ll need to modify the source code and recompile it.
How did I make it?
Why am I asking myself questions? Because it’s a useful, if jaded, rhetorical device.
Urghh. I can’t quite face doing a tutorial on using the Arduino IDE to program a micro:bit just at the moment, maybe one day. Partly because I have it all set up and it works and I’m not entirely sure how I did it. I find that with the Arduino IDE. If it works, you don’t mess with it. I mean I still have a netbook with WindowsXP on it partly because for a long time it was the only machine I could reliably program Arduinos on.
So, in short… I added the Oxullo library for the MAX30100 sensor, and the Adafruit OLED libraries. I set the target machine to the V2 BBC micro:bit which may have involved installing something… urghh… can’t quite remember, I did it ages ago when I was building my micro:bit Arduboy clone.
I added some bits to the sample MAX30100 code to make the OLED display work, and amazingly, it all seems to work. You can read the source code here.
For amusement only
I wouldn’t use this for medical purposes, but it would be interesting to compare it with an inexpensive commercial pulse oximeter to see how it compares.
Dear Sir,
I am looking for a working code for reading the MAX30100 for Micro:Bit V1.
Your HEX file simply didn’t work.
Thank you very much in advanced.
Hi Mike – yes that’s right, the HEX file was built for a V2 micro:bit so would not work on a V1. You may be able to use the Arduino IDE and the Oxullo library to target a V1 micro:bit instead of a V2 when you compile the code. I can’t test this but I had a go at recompiling it for a V1 micro:bit here: https://raw.githubusercontent.com/blogmywiki/microbit-MAX30100/main/max30100.ino.BBCmicrobit.hex
My program, including the OLED display, looked like this:
#include
#include "MAX30100_PulseOximeter.h"
#include
#include
//#include
//#include
//Adafruit_Microbit_Matrix microbit;
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (4 or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define REPORTING_PERIOD_MS 1000
// PulseOximeter is the higher level interface to the sensor
// it offers:
// * beat detection reporting
// * heart rate calculation
// * SpO2 (oxidation level) calculation
PulseOximeter pox;
uint32_t tsLastReport = 0;
// Callback (registered below) fired when a pulse is detected
void onBeatDetected()
{
Serial.println("Beat!");
display.setCursor(100, 0);
display.write(3);
display.display();
}
void setup()
{
Serial.begin(115200);
// microbit.begin();
Serial.print("Initializing pulse oximeter..");
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.clearDisplay();
delay(1000);
// Initialize the PulseOximeter instance
// Failures are generally due to an improper I2C wiring, missing power supply
// or wrong target chip
if (!pox.begin()) {
Serial.println("FAILED");
for(;;);
} else {
Serial.println("SUCCESS");
}
// The default current for the IR LED is 50mA and it could be changed
// by uncommenting the following line. Check MAX30100_Registers.h for all the
// available options.
// pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
// Register a callback for the beat detection
pox.setOnBeatDetectedCallback(onBeatDetected);
}
void loop()
{
// Make sure to call update as fast as possible
pox.update();
// Asynchronously dump heart rate and oxidation levels to the serial
// For both, a value of 0 means "invalid"
if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart rate:");
Serial.print(pox.getHeartRate());
Serial.print("bpm / SpO2:");
Serial.print(pox.getSpO2());
Serial.println("%");
display.clearDisplay();
display.cp437(true); // Use full 256 char 'Code Page 437' font
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 6);
display.println(F("SpO2 %"));
display.setCursor(0, 38);
display.println(F("Heart bpm"));
display.setTextSize(2); // Normal 1:1 pixel scale
display.setCursor(36, 0);
display.println(pox.getSpO2());
display.setCursor(36, 32);
display.println(pox.getHeartRate(),1);
display.display();
tsLastReport = millis();
}
}