Pulse oximeter module MAX30100 & Dallas Temperature sensor DS18B20 won't work together. I have tried using WaitForConversion.ino example from DallasTemperature.h to measure temperature asynchronously. I am using MAX30100 as a POX, measuring SpO2 and HR. I have done multiple permutations of code and delays within and between the MAX30100's code and DS18B20's code which are most generally available on the internet. The only way I have seen both sensors give out values on Serial monitor was by removing all delays, although the MAX30100's output was erronated (only 94% and 95% SpO2 values, and +110/120/130 HR values - I must mention that it works fine independently and its output values are varying from 0 to 60 up to 90 for heart rate and from 0% to 90% up to 99% SpO2).
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#define REPORTING_PERIOD_MS 1000
// Create a PulseOximeter object
PulseOximeter pox;
// Time at which the last beat occurred
uint32_t tsLastReport = 0;
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// Callback routine is executed when a pulse is detected
void onBeatDetected() {
Serial.println("Beat!");
}
void configureMax30100() {
// Configure sensor to use 7.6mA for LED drive
pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
// Register a callback routine
pox.setOnBeatDetectedCallback(onBeatDetected);
}
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature Control Library - Async Demo");
Serial.println("\nDemo shows the difference in length of the call\n\n");
// Start up the library
sensors.begin();
Serial.print("Initializing pulse oximeter..");
// Initialize sensor
if (!pox.begin()) {
Serial.println("FAILED");
for (;;);
} else {
Serial.println("SUCCESS");
}
configureMax30100();
}
void loop(void)
{
// Read from the sensor
Serial.println(" Requesting temperatures...");
sensors.setWaitForConversion(false); // makes it async
sensors.requestTemperatures(); // Send the command to get temperature readings
sensors.setWaitForConversion(true);
Serial.println("DONE");
/********************************************************************/
// 9 bit resolution by default
// Note the programmer is responsible for the right delay
// we could do something usefull here instead of the delay
int resolution = 9;
//delay(750 / (1 << (12 - resolution))); //
Serial.print("Temperature is: ");
Serial.println(sensors.getTempCByIndex(0));
pox.update();
// Grab the updated heart rate and SpO2 levels
Serial.println(millis() - tsLastReport);
if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart rate:");
Serial.print(pox.getHeartRate());
Serial.print("bpm / SpO2:");
Serial.print(pox.getSpO2());
Serial.println("%");
tsLastReport = millis();
}
}
This is the WaitForConversion code, as I've seen to be suggested as a solution on an another post
on how requestTemperature is taking too long so that the MAX30100 sensor cannot get enough samples at the same time and waitConversion flag must be disabled (FALSE):
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
void setup(void)
{
// start serial port
Serial.begin(115200);
Serial.println("Dallas Temperature Control Library - Async Demo");
Serial.println("\nDemo shows the difference in length of the call\n\n");
// Start up the library
sensors.begin();
}
void loop(void)
{
// Request temperature conversion (traditional)
Serial.println("Before blocking requestForConversion");
unsigned long start = millis();
sensors.requestTemperatures();
unsigned long stop = millis();
Serial.println("After blocking requestForConversion");
Serial.print("Time used: ");
Serial.println(stop - start);
// get temperature
Serial.print("Temperature: ");
Serial.println(sensors.getTempCByIndex(0));
Serial.println("\n");
// Request temperature conversion - non-blocking / async
Serial.println("Before NON-blocking/async requestForConversion");
start = millis();
sensors.setWaitForConversion(false); // makes it async
sensors.requestTemperatures();
sensors.setWaitForConversion(true);
stop = millis();
Serial.println("After NON-blocking/async requestForConversion");
Serial.print("Time used: ");
Serial.println(stop - start);
// 9 bit resolution by default
// Note the programmer is responsible for the right delay
// we could do something usefull here instead of the delay
int resolution = 9;
delay(750/ (1 << (12-resolution)));
// get temperature
Serial.print("Temperature: ");
Serial.println(sensors.getTempCByIndex(0));
Serial.println("\n\n\n\n");
delay(5000);
}
This is the library for MAX30100 (from oxullo) that I am using:
This problem has been mentioned a few times in different ways in the last months on this forum. One sensor (pox or heartbeat sensor) needs to be updated regularly and the other sensors needs time.
This will not work together.
If you use a example that comes with the pox sensor library, does that work ?
Then try to evolve that into your code, but do not print something every time the loop() runs. Try it first without the DS18B20 code.
There might be a library that does not wait to get the temperature from a DS18B20.
Perhaps another millis-timer is needed for the DS18B20. Read the temperature every 2 or 5 seconds or so and start a new conversion for the next time.
If the temperature does not need to be accurate, then a analog temperature sensor can be used. Maybe a temperature with SPI interface will solve the timing problem.
Keep the loop() running as fast as possible, for example hundreds times per second.
Do not use Serial.println() in the main level of the loop(), put those behind a millis-timer.
When I used the async mode for the temperature sensor, I had to control the timing myself. You appear to be setting it to async but still using delay, which defeats the point of async.
Set it to async in setup and use millis to control the time taken between requesting temperatures and going back to collect them.
I have made it work somehow following your reply and @Koepel 's. It has it's hiccups like 120 HR back to 60 HR, 70 HR some seconds ok with kind of 94% up to 99% SpO2 variations, and then down to 40 HR which makes me think that somehow there's some work left with the millis?
Here is the code - I have added POX code to WaitForConversion2.ino example from the DallasTemperature library mentioned by @dlloyd:
//
// Sample of using Async reading of Dallas Temperature Sensors
//
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#define REPORTING_PERIOD_MS 1000
// Create a PulseOximeter object
PulseOximeter pox;
// Time at which the last beat occurred
uint32_t tsLastReport = 0;
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
DeviceAddress tempDeviceAddress;
int resolution = 12;
unsigned long lastTempRequest = 0;
int delayInMillis = 0;
float temperature = 0.0;
int idle = 0;
//
// SETUP
//
// Callback routine is executed when a pulse is detected
void onBeatDetected() {
Serial.println("Beat!");
}
void configureMax30100() {
// Configure sensor to use 7.6mA for LED drive
pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
// Register a callback routine
pox.setOnBeatDetectedCallback(onBeatDetected);
}
void setup(void)
{
Serial.begin(9600);
Serial.println("Dallas Temperature Control Library - Async Demo");
Serial.print("Library Version: ");
Serial.println(DALLASTEMPLIBVERSION);
Serial.println("\n");
sensors.begin();
sensors.getAddress(tempDeviceAddress, 0);
sensors.setResolution(tempDeviceAddress, resolution);
sensors.setWaitForConversion(false);
sensors.requestTemperatures();
delayInMillis = 750 / (1 << (12 - resolution));
lastTempRequest = millis();
pinMode(13, OUTPUT);
Serial.print("Initializing pulse oximeter..");
Wire.setClock(400000UL); // I tried changing the I2C_BUS_SPEED to 100Khz, it made no difference in the output values
// Initialize sensor
if (!pox.begin()) {
Serial.println("FAILED");
for (;;);
} else {
Serial.println("SUCCESS");
}
configureMax30100();
}
void loop(void)
{
if (millis() - lastTempRequest >= 2000) // waited long enough??
{
//digitalWrite(13, LOW);
Serial.print(" Temperature: ");
temperature = sensors.getTempCByIndex(0);
Serial.println(temperature, resolution - 8);
Serial.print(" Resolution: ");
Serial.println(resolution);
Serial.print("Idle counter: ");
Serial.println(idle);
Serial.println();
idle = 0;
// immediately after fetching the temperature we request a new sample
// in the async modus
// for the demo we let the resolution change to show differences
resolution++;
if (resolution > 12) resolution = 9;
sensors.setResolution(tempDeviceAddress, resolution);
sensors.requestTemperatures();
delayInMillis = 750 / (1 << (12 - resolution));
lastTempRequest = millis();
}
//digitalWrite(13, HIGH);
// we can do usefull things here
// for the demo we just count the idle time in millis
delay(1);
idle++;
pox.update();
// Grab the updated heart rate and SpO2 levels
if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart rate:");
Serial.print(pox.getHeartRate());
Serial.print("bpm / SpO2:");
Serial.print(pox.getSpO2());
Serial.println("%");
tsLastReport = millis();
}
}
Has anybody ever measured how long one update takes ?
Does anybody know what frequency the pox / heartbeat-sensor must be updated?
so maybe a solution could be to do updating the pox with a timer-interrupt
and once every 3 seconds if the temperature-conversion has finished retrieve the temperature.
Be the change you want to see in the world
best regards Stefan
I have tried changing it, it seemed to work at 19200 then I changed to 38400 bauds as per most common baud rates, but I had weird symbols on my Serial monitor. I am using a Mega 2560.
So that would imply using the INT pin of MAX30100, right?
off: If anyone asks why is there the Wire.setClock(400000UL); line of code, is because I tried changing the I2C_BUS_SPEED value in the MAX30100.h to 100000UL, thought it might help, but no. No need (for now) to write this line, the default value is 400kHz.
Often this is due to a C++ "cast" issue where the value being assigned-to and printed is different than the function return type: example, long-integer verses float, etc.
An Arduino Mega has 3 hardware UARTS. This means you can go up to 115200 baud very easy on the hardware-serial-interfaces.
Though you have to use the right IO-pins
I don't know. Because I don't know how the MAX30100 works.
Certainly, if you have a long and assign a float, you are going to get a "converted" value ... you need to determine what the library functions return... usually obvious when using the example code.
This kind of conversion error can be due to the sensor not being completely set-up/initialized correctly. Some sensors have the ability to do their own smoothing (averaging by multiple readings.) Other sensors and not too smart and you are likely going to need to implement an appropriate averaging tech, either rolling average of store, drop high&low, average, etc.
Often, Applications Notes and/or Technical Reference PDF will give some hints.
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("H:");
Serial.println(pox.getHeartRate());
Serial.print("O:");
Serial.println(pox.getSpO2());
tsLastReport = millis();
}
}