MAX30003 configuration misunderstanding

Hello. I have this code:

#include<SPI.h>
#include "MAX30003.h"

#define IO_DELAY 50
#define MAX30003_CS_PIN PB0

uint8_t buff[4] = {0};
unsigned long etagFifoOvf = 0;
unsigned long interruptsError = 0;
boolean output_on = false;
boolean print_time = false;
long print_time_start;
long print_time_duration;

void setup() {
  Serial.begin(9600); //Serial begin

  pinMode(MAX30003_CS_PIN, OUTPUT);
  digitalWrite(MAX30003_CS_PIN, HIGH);

  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);

  MAX30003_begin();
}

void loop() {
  long c_time = millis();

  if (Serial.available()) {
char command = Serial.read();
if (command == '1') {
  printConfigs();
} else if (command == '2') {
  printStat();
} else if (command == '3') {
  output_on = true;
} else if (command == '4') {
  output_on = false;
} else if (command == '5') {
  output_on = false;
  print_time = true;
  print_time_duration  = 1000;
  print_time_start = millis();
} else if (command == '6') {
  output_on = false;
  print_time = true;
  print_time_duration = Serial.parseInt();
  print_time_start = millis();
} else if (command == '7') {
  int fcal = Serial.parseInt();
  CalConfiguration_u CNFG_CAL_r;
  readConfig(Registers_e::CNFG_ALL, CNFG_CAL_r.all);
  CNFG_CAL_r.bits.fcal = fcal;
  MAX30003_Reg_Write(Registers_e::CNFG_ALL, CNFG_CAL_r.all);
  max30003_synch();
} else if (command == '0') {
  Serial.println("0. Show menu");
  Serial.println("1. Print configs");
  Serial.println("2. Print status");
  Serial.println("3. On print output");
  Serial.println("4. Off print output");
  Serial.println("5. Print samples 1 second");
  Serial.println("6. Print samples N millisecond");
  Serial.println("7. Change FCAL to N");
} else {
  Serial.print("Unknown command: '");
  Serial.print(command);
  Serial.println("'");
}
  }

  Status_u s = readStatus();
  if (!s.bits.eint) {
interruptsError++;
delayMicroseconds(IO_DELAY);
return;
  }
  if (s.bits.eovf) {
max30003_fifo_rst();
etagFifoOvf++;
return;
  }

  byte etag;

  int_least32_t ecgdata = readEcg(etag);

  if (etag == FIFO_VALID_SAMPLE || etag == FIFO_LAST_SAMPLE) {
if (output_on || print_time && c_time - print_time_start <= print_time_duration) {
  Serial.println(ecgdata);
} else if (print_time && c_time - print_time_start > print_time_duration) {
  print_time = false;
}
  } else if (etag == FIFO_OVF) {
max30003_fifo_rst();
etagFifoOvf++;
  }
}

void printStat() {
  Serial.print("FIFO overflow: ");
  Serial.println(etagFifoOvf);
  Serial.print("Interrupts error: ");
  Serial.println(interruptsError);
}

Status_u readStatus() {
  return Status_u{ MAX30003_Read_uint32(Registers_e::STATUS) };
}

int_least32_t readEcg(byte& etag) {
  uint32_t data = MAX30003_Read_uint32(Registers_e::ECG_FIFO);
  etag = (data >> 3) & 0x7;
  return (((int_least32_t)((int_least16_t)(data >> 8))) << 2) | ((int_least32_t)(data & 0b11));
}

uint32_t MAX30003_Read_uint32(const Registers_e address) {
  MAX30003_Reg_Read(address);

  uint32_t data = (uint32_t)buff[0];
  data <<= 8;
  data |= ((uint32_t)buff[1]);
  data <<= 8;
  data |= ((uint32_t)buff[2]);

  return data;
}

void MAX30003_Reg_Write (const Registers_e address, unsigned long data) {
  byte dataToSend = (address << 1) | WREG;

  digitalWrite(MAX30003_CS_PIN, LOW);
  delayMicroseconds(IO_DELAY);

  SPI.transfer(dataToSend);
  SPI.transfer(data >> 16);
  SPI.transfer(data >> 8);
  SPI.transfer(data);

  digitalWrite(MAX30003_CS_PIN, HIGH);
  delayMicroseconds(IO_DELAY);
}

void max30003_sw_reset(void) {
  MAX30003_Reg_Write(Registers_e::SW_RST, 0x000000);
}

void max30003_synch(void) {
  MAX30003_Reg_Write(Registers_e::SYNCH, 0x000000);
}

void max30003_fifo_rst(void) {
  MAX30003_Reg_Write(Registers_e::FIFO_RST, 0x000000);
}

void MAX30003_Reg_Read(const Registers_e address) {
  digitalWrite(MAX30003_CS_PIN, LOW);
  delayMicroseconds(IO_DELAY);

  uint8_t SPI_TX_Buff = (address << 1 ) | RREG;
  SPI.transfer(SPI_TX_Buff);

  for (int i = 0; i < 3; i++) {
buff[i] = SPI.transfer(0xff);
  }

  digitalWrite(MAX30003_CS_PIN, HIGH);
  delayMicroseconds(IO_DELAY);
}

void readConfig(const Registers_e address, uint32_t& all) {
  all = MAX30003_Read_uint32(address);
}

void MAX30003_begin() {
  max30003_sw_reset();
  max30003_fifo_rst();

  GeneralConfiguration_u CNFG_GEN_r;
  setupGeneralCfg(CNFG_GEN_r);
  MAX30003_Reg_Write(Registers_e::CNFG_GEN, CNFG_GEN_r.all);

  CalConfiguration_u CNFG_CAL_r;
  setupCalCfg(CNFG_CAL_r);
  MAX30003_Reg_Write(Registers_e::CNFG_ALL, CNFG_CAL_r.all);

  MuxConfiguration_u CNFG_MUX_r;
  setupManageCfg(CNFG_MUX_r);
  MAX30003_Reg_Write(Registers_e::CNFG_EMUX, CNFG_MUX_r.all);

  ECGConfiguration_u CNFG_ECG_r;
  setupECGCfg(CNFG_ECG_r);
  MAX30003_Reg_Write(Registers_e::CNFG_ECG, CNFG_ECG_r.all);

  RtoR1Configuration_u CNFG_RTOR_r;
  setupRtoRCfg(CNFG_RTOR_r);
  MAX30003_Reg_Write( Registers_e::CNFG_RTOR1 , CNFG_RTOR_r.all);

  EnableInterrupts_u EN_INT_r;
  setupInterruptsCfg(EN_INT_r);
  MAX30003_Reg_Write( Registers_e::EN_INT , EN_INT_r.all);

  ManageDynamicModes_u MNG_DYN_r;
  setupDynamicCfg(MNG_DYN_r);
  MAX30003_Reg_Write( Registers_e::MNGR_DYN , MNG_DYN_r.all);

  ManageInterrupts_u MNG_INT_r;
  setupManageCfg(MNG_INT_r);
  MAX30003_Reg_Write( Registers_e::MNGR_INT , MNG_INT_r.all);

  max30003_synch();
}

void printConfigs() {
  GeneralConfiguration_u CNFG_GEN_r;
  setupGeneralCfg(CNFG_GEN_r);
  Serial.print("CNFG_GEN: ");
  Serial.print(CNFG_GEN_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::CNFG_GEN, CNFG_GEN_r.all);
  Serial.println(CNFG_GEN_r.all, HEX);

  CalConfiguration_u CNFG_CAL_r;
  setupCalCfg(CNFG_CAL_r);
  Serial.print("CNFG_CAL: ");
  Serial.print(CNFG_CAL_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::CNFG_ALL, CNFG_CAL_r.all);
  Serial.println(CNFG_CAL_r.all, HEX);

  MuxConfiguration_u CNFG_MUX_r;
  setupManageCfg(CNFG_MUX_r);
  Serial.print("CNFG_EMUX: ");
  Serial.print(CNFG_MUX_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::CNFG_EMUX, CNFG_MUX_r.all);
  Serial.println(CNFG_MUX_r.all, HEX);

  ECGConfiguration_u CNFG_ECG_r;
  setupECGCfg(CNFG_ECG_r);
  Serial.print("CNFG_ECG: ");
  Serial.print(CNFG_ECG_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::CNFG_ECG, CNFG_ECG_r.all);
  Serial.println(CNFG_ECG_r.all, HEX);

  RtoR1Configuration_u CNFG_RTOR_r;
  setupRtoRCfg(CNFG_RTOR_r);
  Serial.print("CNFG_RTOR: ");
  Serial.print(CNFG_RTOR_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::CNFG_RTOR1, CNFG_RTOR_r.all);
  Serial.println(CNFG_RTOR_r.all, HEX);

  ManageInterrupts_u MNG_INT_r;
  setupManageCfg(MNG_INT_r);
  Serial.print("MNGR_INT: ");
  Serial.print(MNG_INT_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::MNGR_INT, MNG_INT_r.all);
  Serial.println(MNG_INT_r.all, HEX);

  EnableInterrupts_u EN_INT_r;
  setupInterruptsCfg(EN_INT_r);
  Serial.print("EN_INT: ");
  Serial.print(EN_INT_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::EN_INT, EN_INT_r.all);
  Serial.println(EN_INT_r.all, HEX);

  ManageDynamicModes_u MNG_DYN_r;
  setupDynamicCfg(MNG_DYN_r);
  Serial.print("MNG_DYN: ");
  Serial.print(MNG_DYN_r.all, HEX);
  Serial.print(" ");
  readConfig(Registers_e::MNGR_DYN, MNG_DYN_r.all);
  Serial.println(MNG_DYN_r.all, HEX);
}

void setupGeneralCfg(GeneralConfiguration_u& CNFG_GEN_r) {
  CNFG_GEN_r.all = 0;

  CNFG_GEN_r.bits.rbiasn = 0;
  CNFG_GEN_r.bits.rbiasp = 0;
  CNFG_GEN_r.bits.rbiasv = 0b00;
  CNFG_GEN_r.bits.en_rbias = 0;

  CNFG_GEN_r.bits.vth = 0b00;
  CNFG_GEN_r.bits.imag = 0b00;
  CNFG_GEN_r.bits.ipol = 0;
  CNFG_GEN_r.bits.en_dcloff = 0;

  CNFG_GEN_r.bits.en_ecg = 1;

  CNFG_GEN_r.bits.fmstr = 0;
  CNFG_GEN_r.bits.en_ulp_lon = 0;
}

void setupCalCfg(CalConfiguration_u& CNFG_CAL_r) {
  CNFG_CAL_r.all = 0;

  CNFG_CAL_r.bits.thigh = 0;
  CNFG_CAL_r.bits.fifty = 0;
  CNFG_CAL_r.bits.fcal = 0b000;

  CNFG_CAL_r.bits.vmag = 0;
  CNFG_CAL_r.bits.vmode = 0;
  CNFG_CAL_r.bits.en_vcal = 0;
}

void setupManageCfg(MuxConfiguration_u& CNFG_MUX_r) {
  CNFG_MUX_r.all = 0;
  CNFG_MUX_r.bits.calp_sel = 0;
  CNFG_MUX_r.bits.caln_sel = 0;

  CNFG_MUX_r.bits.openn = 0;
  CNFG_MUX_r.bits.openp = 0;

  CNFG_MUX_r.bits.pol = 0;
}

void setupECGCfg(ECGConfiguration_u& CNFG_ECG_r) {
  CNFG_ECG_r.all = 0;
  CNFG_ECG_r.bits.dlpf = 0b01;
  CNFG_ECG_r.bits.dhpf = 1;
  CNFG_ECG_r.bits.gain = 0b00;
  CNFG_ECG_r.bits.rate = 0b00;
}

void setupRtoRCfg(RtoR1Configuration_u& CNFG_RTOR_r) {
  CNFG_RTOR_r.all = 0;
  CNFG_RTOR_r.bits.wndw = 0b0011;
  CNFG_RTOR_r.bits.rgain = 0b1111;
  CNFG_RTOR_r.bits.pavg = 0b10;
  CNFG_RTOR_r.bits.ptsf = 0b0011;
  CNFG_RTOR_r.bits.en_rtor = 1;
}

void setupManageCfg(ManageInterrupts_u& MNG_INT_r) {
  MNG_INT_r.all = 0;
  MNG_INT_r.bits.samp_it = 0;
  MNG_INT_r.bits.clr_samp = 1;
  MNG_INT_r.bits.clr_rrint = 1;
  MNG_INT_r.bits.clr_fast = 0;
  MNG_INT_r.bits.efit = 0;
}

void setupInterruptsCfg(EnableInterrupts_u& EN_INT_r) {
  EN_INT_r.all = 0;
  EN_INT_r.bits.intb_type = 0b11;
  EN_INT_r.bits.en_pllint = 0;
  EN_INT_r.bits.en_samp = 0;
  EN_INT_r.bits.en_rrint = 1;
  EN_INT_r.bits.en_loint = 0;
  EN_INT_r.bits.en_dcloffint = 0;
  EN_INT_r.bits.en_fstint = 0;
  EN_INT_r.bits.en_eovf = 1;
  EN_INT_r.bits.en_eint = 1;
}

void setupDynamicCfg(ManageDynamicModes_u& MNG_DYN_r) {
  MNG_DYN_r.all = 0;
  MNG_DYN_r.bits.fast_th = 0;
  MNG_DYN_r.bits.fast = 0;
}

Configs I took from here: The best configuration - Question | Mbed
In plotter I see sinusoid:


If I connect it to human, then I get something like that:

Also, I have a 47Hz signal generator, If I connect the generator to MAX30003:

When I touch contacts signal is changing, but after that, it’s stabilizing.
Can anybody clarify what I did wrong?

Hello,

How it is related to the MbedOS?
Also your code is for Ardunio.

From my point of view you will have more luck with Arduino examples on internet then here on Mbed forum. But maybe I am wrong.

Here you have some links where Maxim Integrated show examples with their configuration.
How to Interface the MAX30003WING ECG AFE with the MAX32630FTHR (maximintegrated.com)
MAX30003 ECG Recording - Question | Mbed

BR, Jan

Hello Jan,
When I google my question I found this article https://forums.mbed.com/t/heartrate-example-and-mx30003sensor-ble-service-with-sensor/9389 so I thought it’s the correct topic.

Actually, it’s not important that my code is for Arduino because in common sense it works.
I read data from MAX30003 and write in registers successfully, but ECG data looks strange.

Thanks for the links I already read them all and tried to use configs from there. The result the same as above.

BR, Maksim

Hello,

A bunch of things could lead to such a signal.
You do not tell anything about the hardware. Is it a custom made board based on MAX30003 or is the board produced by some company?
How is the board connected to the MCU?
What kind of ECG-cables/electrodes are you using?
Is there significant EMI in the room where you are recording ECG?
What is the baud-rate in case of serial communication?

Actually this old example has a configuration which should produce a proper ECG curve if you are not facing any HW issues