SparkFun Pulse Oximeter and Heart Rate Sensor - not returning HR or SpO2

I have SparkFun Pulse Oximeter and Heart Rate Sensor connected to a nrf52dK board.

I am able to communicate, but I can’t seem to get the correct data that i can parse. IR and RED LED values seem ok, but algorythm output outputs zero, except for SpO2 which always outputs a number that is always increasing by 0.1%

[621107] IR=10621 RED=12548 HR=0.0 bpm Conf=0% SpO2=23.6% Status=12
[621227] IR=10621 RED=12548 HR=0.0 bpm Conf=0% SpO2=23.6% Status=12
[621347] IR=10621 RED=12548 HR=0.0 bpm Conf=0% SpO2=23.6% Status=12
[621467] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=68
[621587] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=68
[621707] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=68
[621828] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=68
[621948] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=68
[622068] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=124
[622188] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=124
[622308] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=124
[622428] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=124
[622548] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.6% Status=124
[622668] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-76
[622782] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-76
[622902] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-76
[623016] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-76
[623136] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-20
[623256] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-20
[623377] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-20
[623497] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-20
[623617] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.6% Status=-20
[623737] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.7% Status=36
[623857] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.7% Status=36
[623977] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.7% Status=36
[624097] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.7% Status=36
[624217] IR=10625 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.7% Status=36
[624337] IR=10626 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=92
[624457] IR=10626 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=92
[624577] IR=10626 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=92
[624697] IR=10626 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=92
[624817] IR=10626 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=92
[624930] FIFO count reported = 77
[625047] IR=10625 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-108
[625167] IR=10625 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-108
[625281] IR=10625 RED=12558 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-108
[625402] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-52
[625515] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-52
[625636] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-52
[625756] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-52
[625876] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.7% Status=-52
[625996] IR=10622 RED=12559 HR=0.0 bpm Conf=0% SpO2=23.8% Status=4
[626116] IR=10622 RED=12559 HR=0.0 bpm Conf=0% SpO2=23.8% Status=4
[626236] IR=10622 RED=12559 HR=0.0 bpm Conf=0% SpO2=23.8% Status=4
[626356] IR=10622 RED=12559 HR=0.0 bpm Conf=0% SpO2=23.8% Status=4
[626476] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=60
[626596] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=60
[626716] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=60
[626836] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=60
[626956] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=60
[627076] IR=10625 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=60
[627196] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.8% Status=116
[627316] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.8% Status=116
[627436] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.8% Status=116
[627550] IR=10627 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.8% Status=116
[627670] IR=10623 RED=12550 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-84
[627784] IR=10623 RED=12550 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-84
[627905] IR=10623 RED=12550 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-84
[628025] IR=10623 RED=12550 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-84
[628145] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-28
[628265] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-28
[628385] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-28
[628505] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-28
[628625] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.8% Status=-28
[628746] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=28
[628866] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=28
[628986] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=28
[629106] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=28
[629226] IR=10623 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=28
[629346] IR=10624 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.9% Status=84
[629466] IR=10624 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.9% Status=84
[629586] IR=10624 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.9% Status=84
[629706] IR=10624 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.9% Status=84
[629826] IR=10624 RED=12555 HR=0.0 bpm Conf=0% SpO2=23.9% Status=84
[629946] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-116
[630060] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-116
[630180] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-116
[630294] IR=10621 RED=12552 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-116
[630415] IR=10618 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-60
[630535] IR=10618 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-60
[630655] IR=10618 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-60
[630775] IR=10618 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-60
[630895] IR=10618 RED=12554 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-60
[631015] IR=10625 RED=12556 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-4
[631135] IR=10625 RED=12556 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-4
[631255] IR=10625 RED=12556 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-4
[631375] IR=10625 RED=12556 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-4
[631495] IR=10625 RED=12556 HR=0.0 bpm Conf=0% SpO2=23.9% Status=-4
[631615] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=52
[631736] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=52
[631856] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=52
[631976] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=52
[632096] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=52
[632216] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.0% Status=108
[632336] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.0% Status=108
[632456] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.0% Status=108
[632570] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.0% Status=108
[632690] IR=10627 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-92
[632804] IR=10627 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-92
[632924] IR=10627 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-92
[633044] IR=10627 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-92
[633164] IR=10627 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-92
[633284] IR=10622 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-36
[633404] IR=10622 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-36
[633525] IR=10622 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-36
[633645] IR=10622 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-36
[633765] IR=10622 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.0% Status=-36
[633885] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=20
[634005] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=20
[634125] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=20
[634238] FIFO count reported = 71
[634355] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=20
[634475] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=76
[634595] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=76
[634715] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=76
[634829] IR=10625 RED=12551 HR=0.0 bpm Conf=0% SpO2=24.1% Status=76
[634949] IR=10623 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-124
[635063] IR=10623 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-124
[635183] IR=10623 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-124
[635303] IR=10623 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-124
[635424] IR=10625 RED=12549 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-68
[635544] IR=10625 RED=12549 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-68
[635664] IR=10625 RED=12549 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-68
[635784] IR=10625 RED=12549 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-68
[635904] IR=10625 RED=12549 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-68
[636024] IR=10625 RED=12549 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-68
[636144] IR=10626 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-12
[636265] IR=10626 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-12
[636385] IR=10626 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-12
[636505] IR=10626 RED=12556 HR=0.0 bpm Conf=0% SpO2=24.1% Status=-12
[636625] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.2% Status=44
[636745] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.2% Status=44
[636865] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.2% Status=44
[636985] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.2% Status=44
[637105] IR=10623 RED=12553 HR=0.0 bpm Conf=0% SpO2=24.2% Status=44
[637225] IR=10625 RED=12550 HR=0.0 bpm Conf=0% SpO2=24.2% Status=100
[637339] IR=10625 RED=12550 HR=0.0 bpm Conf=0% SpO2=24.2% Status=100
[637459] IR=10625 RED=12550 HR=0.0 bpm Conf=0% SpO2=24.2% Status=100
[637573] IR=10625 RED=12550 HR=0.0 bpm Conf=0% SpO2=24.2% Status=100
[637693] IR=10626 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-100
[637813] IR=10626 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-100
[637934] IR=10626 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-100
[638054] IR=10626 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-100
[638174] IR=10626 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-100
[638294] IR=10626 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-100
[638415] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-44
[638535] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-44
[638655] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-44
[638775] IR=10621 RED=12555 HR=0.0 bpm Conf=0% SpO2=24.2% Status=-44
[638895] IR=10622 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.3% Status=12
[639015] IR=10622 RED=12552 HR=0.0 bpm Conf=0% SpO2=24.3% Status=12

/*

* main.c

* MAX30101 via MAX32664 bridge (application-mode sequences per MAX32664 app note Table 9)

*

* - Zephyr OS

* - MFIO = 18, RSTN = 19 (change to match board)

* - I2C node = i2c0 (change as needed)

*

* Key behavior:

* - For families 0x40/0x41/0x42/0x43/0x10/0x12 use write → STOP → k_busy_wait(CMD_DELAY_US) → read(status [+ payload])

* - Probe attrs, chunked dump, configure output mode, set FIFO threshold, enable sensor, enable algorithm, read status, read FIFO count, read FIFO data

*

* Tuning:

* - CMD_DELAY_US: vendor recommends ~60 us; set larger (120..500) when debugging

* - CHUNK_DUMP: small chunk size (8..24) is safe

*/

#include <zephyr/kernel.h>

#include <zephyr/device.h>

#include <zephyr/devicetree.h>

#include <zephyr/drivers/i2c.h>

#include <zephyr/drivers/gpio.h>

#include <zephyr/sys/printk.h>

#include <stdint.h>

#include <stdarg.h>

#include <string.h>

#include <errno.h>

#define I2C_DEV DEVICE_DT_GET(DT_NODELABEL(i2c0))

#define GPIO_DEV DEVICE_DT_GET(DT_NODELABEL(gpio0))

/* MAX32664 families / host commands */

#define FAMILY_STATUS 0x00 /* status read/write probe family (we use 0x00 probe as in some examples) */

#define FAMILY_DEVICE_MODE 0x02

#define FAMILY_BOOT_VER 0xFF

#define FAMILY_OUTPUT_MODE 0x10

#define FAMILY_OUTPUT_QRY 0x11

#define FAMILY_READ_FIFO_COUNT 0x12 /* 0x12 index 0x00 → num samples; index 0x01 → read fifo data */

#define FAMILY_AFE_WRITE 0x40

#define FAMILY_AFE_READ 0x41

#define FAMILY_AFE_ATTR 0x42

#define FAMILY_AFE_DUMP 0x43

#define FAMILY_ALGO_ENABLE 0x52

#define HUB_ADDR 0x55

/* MAX30101 index in bridge */

#define IDX_MAX30101 0x03

/* pins (adapt if necessary) */

#define MFIO_PIN 18

#define RSTN_PIN 19

/* timing constants */

#define CMD_DELAY_US 120 /* start conservative; vendor ~60us */

#define BOOT_POST_MS 600

#define POST_AFE_WRITE_MS 300

/* stability and fetch tuning */

#define STABLE_COUNT 6

#define STABLE_GAP_MS 8

#define FETCH_SLACK_MS 60

#define FETCH_RETRY_COUNT 6

#define FETCH_RETRY_DELAY_MS 10

/* dump */

#define MAX_REGS 36

#define MAX_DUMP_BYTES (MAX_REGS * 2)

#define DUMP_CHUNK 12

/* MAX30101 registers */

#define R_FIFO_WRPTR 0x04

#define R_FIFO_OVF 0x05

#define R_FIFO_RDPTR 0x06

#define R_FIFO_DATA 0x07

#define R_FIFO_CONF 0x08

#define R_MODE_CONF 0x09

#define R_SPO2_CONF 0x0A

#define R_LED1_PA 0x0C

#define R_LED2_PA 0x0D

/* parse helper */

static inline uint32_t parse24(const uint8_t *b) {

return ((((uint32_t)b[0] << 16) | ((uint32_t)b[1] << 8) | b[2]) & 0x03FFFF);

}

static inline uint16_t be16(const uint8_t *p) {

return (uint16_t)(((uint16_t)p[0] << 8) | p[1]); // big-endian → host

}

static inline int16_t be16s(const uint8_t *p) { // if you later parse accel

return (int16_t)(((uint16_t)p[0] << 8) | p[1]);

}

/* devices */

static const struct device *i2c;

static const struct device *gpio_dev;

/* MFIO DataReady */

static struct gpio_callback mfio_cb;

static volatile bool data_ready_flag;

static void mfio_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins) {

ARG_UNUSED(dev); ARG_UNUSED(cb); ARG_UNUSED(pins);

data_ready_flag = true;

}

/* logging */

static void tprint(const char *fmt, …) {

va_list ap; va_start(ap, fmt);

printk("[%u] ", (unsigned)k_uptime_get());

vprintk(fmt, ap);

va_end(ap);

}

/* minimal helpers */

static inline int min_i(int a, int b) { return (a < b) ? a : b; }

static void maybe_recover_bus(void) {

if (i2c) (void)i2c_recover_bus(i2c);

k_msleep(5);

}

/* Core vendor flow: write → STOP → CMD_DELAY → read(status [+payload])

* tx_buf: Family, Index, optional write-data

* expected_payload_len: number of payload bytes expected (not counting status)

* out_status: optional pointer to status byte

* out_payload: buffer for payload bytes

* returns 0 on success (status==0x00), negative on transport error or nonzero status

*/

static int vendor_write_then_read(const uint8_t *tx_buf, size_t tx_len,

uint8_t *out_payload, size_t expected_payload_len,

uint8_t *out_status)

{

int rc;

if (!i2c) return -ENODEV;

rc = i2c_write(i2c, tx_buf, tx_len, HUB_ADDR);

if (rc) {

tprint(“i2c_write failed rc=%d\n”, rc);

maybe_recover_bus();

return rc;

}

/\* STOP generated; wait CMD_DELAY_US for bridge to prepare status/payload \*/

k_busy_wait(CMD_DELAY_US);

size_t rx_len = 1 + expected_payload_len;

if (expected_payload_len > 64) return -EINVAL;

uint8_t rx_buf[1 + 64];

rc = i2c_read(i2c, rx_buf, rx_len, HUB_ADDR);

if (rc) {

tprint(“i2c_read failed rc=%d\n”, rc);

maybe_recover_bus();

return rc;

}

uint8_t st = rx_buf[0];

if (out_status) *out_status = st;

if (st != 0x00) {

tprint(“bridge status=0x%02X raw:”, st);

int to = min_i((int)rx_len, 16);

for (int i = 0; i < to; ++i) printk(" %02X", rx_buf[i]);

printk(“\n”);

return -EIO;

}

if (expected_payload_len && out_payload) memcpy(out_payload, &rx_buf[1], expected_payload_len);

return 0;

}

/* status-only probe using vendor flow: write Family=0x00, Index=0x00 (works as minimal probe) */

static int vendor_read_status(uint8_t *status_out) {

uint8_t tx[2] = { 0x00, 0x00 };

return vendor_write_then_read(tx, sizeof(tx), NULL, 0, status_out);

}

/* stability polling: require STABLE_COUNT consecutive 0x00 status reads */

static int wait_for_stable_zero(void) {

int stable = 0;

uint8_t st = 0xFF;

for (int i = 0; i < STABLE_COUNT * 10; ++i) {

int rc = vendor_read_status(&st);

if (rc == 0 && st == 0x00) {

stable++;

if (stable >= STABLE_COUNT) return 0;

k_msleep(STABLE_GAP_MS);

continue;

    }

stable = 0;

maybe_recover_bus();

k_msleep(STABLE_GAP_MS);

}

tprint(“stability wait failed last_status=0x%02X\n”, st);

return -ETIMEDOUT;

}

/* high-level vendor fetch: writes fam+idx(+opt_data), ensures stability, sleeps slack, then performs final vendor_write_then_read */

static int stability_checked_vendor_fetch(uint8_t fam, uint8_t idx,

const uint8_t *opt_data, size_t opt_len,

uint8_t *out_payload, size_t out_len)

{

int rc = wait_for_stable_zero();

if (rc) return rc;

k_msleep(FETCH_SLACK_MS);

uint8_t tx[4 + 64];

size_t tx_len = 0;

tx[tx_len++] = fam;

tx[tx_len++] = idx;

if (opt_data && opt_len) {

memcpy(&tx[tx_len], opt_data, opt_len);

tx_len += opt_len;

}

for (int attempt = 1; attempt <= FETCH_RETRY_COUNT; ++attempt) {

uint8_t st = 0xFF;

rc = vendor_write_then_read(tx, tx_len, out_payload, out_len, &st);

if (rc == 0) return 0;

tprint(“fetch attempt %d failed rc=%d status=0x%02X; retrying\n”, attempt, rc, st);

maybe_recover_bus();

k_msleep(FETCH_RETRY_DELAY_MS * attempt);

}

return -EIO;

}

/* AFE helpers built on vendor fetch */

static int read_afe_attrs(int idx, uint8_t attrs[2]) {

return stability_checked_vendor_fetch(FAMILY_AFE_ATTR, idx, NULL, 0, attrs, 2);

}

static int read_afe_reg(int idx, uint8_t reg, uint8_t *val) {

uint8_t arg[1] = { reg };

return stability_checked_vendor_fetch(FAMILY_AFE_READ, idx, arg, 1, val, 1);

}

static int write_afe_reg(int idx, uint8_t reg, uint8_t val) {

uint8_t w[2] = { reg, val };

/\* write returns only status \*/

return stability_checked_vendor_fetch(FAMILY_AFE_WRITE, idx, w, 2, NULL, 0);

}

static int read_fifo_count(void) {

uint8_t out[1];

int rc = stability_checked_vendor_fetch(FAMILY_READ_FIFO_COUNT, 0x00, NULL, 0, out, 1);

return (rc == 0) ? out[0] : -1;

}

static int read_fifo_data_chunk(uint8_t *buf, size_t len) {

/\* family 0x12 index 0x01 returns FIFO data; write index 0x01 with no opt_data, then read status + len payload \*/

return stability_checked_vendor_fetch(FAMILY_READ_FIFO_COUNT, 0x01, NULL, 0, buf, len);

}

/* Boot helper: drive MFIO as boot selection then release */

static void hard_reset_hub_once(bool mfio_high) {

if (!gpio_dev) return;

gpio_pin_configure(gpio_dev, MFIO_PIN, GPIO_OUTPUT);

gpio_pin_configure(gpio_dev, RSTN_PIN, GPIO_OUTPUT);

gpio_pin_set(gpio_dev, MFIO_PIN, mfio_high ? 1 : 0);

tprint(“boot: set MFIO=%d\n”, mfio_high ? 1 : 0);

k_msleep(5);

gpio_pin_set(gpio_dev, RSTN_PIN, 0);

tprint(“boot: assert RSTN=0\n”);

k_msleep(10);

gpio_pin_set(gpio_dev, RSTN_PIN, 1);

tprint(“boot: release RSTN=1\n”);

k_msleep(BOOT_POST_MS);

gpio_pin_configure(gpio_dev, MFIO_PIN, GPIO_INPUT | GPIO_PULL_UP);

}

/* Wait for MFIO DataReady with timeout (ms) */

static bool wait_data_ready_ms(int timeout_ms) {

int elapsed = 0;

const int step = 10;

while (elapsed < timeout_ms) {

if (data_ready_flag) { data_ready_flag = false; return true; }

k_msleep(step);

elapsed += step;

}

return false;

}

int main(void) {

i2c = I2C_DEV;

gpio_dev = GPIO_DEV;

if (!device_is_ready(i2c)) {

printk(“I2C not ready\n”);

return 0;

}

tprint(“i2c ready: %s\n”, i2c->name);

if (device_is_ready(gpio_dev)) tprint(“gpio ready: %s\n”, gpio_dev->name);

maybe_recover_bus();

/\* Boot into app mode (MFIO=1 => application) \*/

hard_reset_hub_once(true);

tprint(“Boot: application mode\n”);

/\* Configure MFIO IRQ (DataReady) \*/

if (device_is_ready(gpio_dev)) {

gpio_pin_configure(gpio_dev, MFIO_PIN, GPIO_INPUT | GPIO_PULL_UP);

gpio_init_callback(&mfio_cb, mfio_isr, BIT(MFIO_PIN));

gpio_add_callback(gpio_dev, &mfio_cb);

gpio_pin_interrupt_configure(gpio_dev, MFIO_PIN, GPIO_INT_EDGE_TO_ACTIVE);

tprint(“MFIO DataReady IRQ configured\n”);

}

/\* Probe indices 0..7 to find MAX30101 \*/

int detected = -1;

uint8_t attrs[2] = { 0xFF, 0xFF };

tprint(“Probing indices for MAX30101 attributes\n”);

for (int idx = 0; idx < 8; ++idx) {

int rc = read_afe_attrs(idx, attrs);

tprint(“probe idx=%d rc=%d attrs=%02X %02X\n”, idx, rc, attrs[0], attrs[1]);

if (rc == 0 && attrs[0] != 0x00) { detected = idx; break; }

}

int afe_idx = (detected >= 0) ? detected : IDX_MAX30101;

tprint(“using afe_idx=%d\n”, afe_idx);

/\* Read a couple of single registers to confirm comms \*/

k_msleep(POST_AFE_WRITE_MS);

uint8_t modev = 0xFF;

int rc = read_afe_reg(afe_idx, R_MODE_CONF, &modev);

tprint(“R_MODE_CONF rc=%d val=0x%02X\n”, rc, modev);

uint8_t fifo_conf = 0xFF;

rc = read_afe_reg(afe_idx, R_FIFO_CONF, &fifo_conf);

tprint(“R_FIFO_CONF rc=%d val=0x%02X\n”, rc, fifo_conf);

/\* Do chunked dump if attributes were valid \*/

if (attrs[1] != 0xFF && rc == 0) {

int regs = attrs[1];

int total_bytes = regs * 2;

tprint(“attempting chunked dump regs=%d total=%d\n”, regs, total_bytes);

uint8_t dump[MAX_DUMP_BYTES];

memset(dump, 0, sizeof(dump));

int off = 0;

while (off < total_bytes) {

int want = min_i(DUMP_CHUNK, total_bytes - off);

int d = stability_checked_vendor_fetch(FAMILY_AFE_DUMP, afe_idx, NULL, 0, &dump[off], want);

if (d == 0) {

off += want;

continue;

        }

tprint(“dump chunk failed rc=%d off=%d want=%d — falling back to per-register reads\n”, d, off, want);

        /\* fallback: read regs individually for this chunk \*/

int reg_start = off / 2;

int reg_end = reg_start + (want / 2);

for (int r = reg_start; r < reg_end; ++r) {

uint8_t v;

if (read_afe_reg(afe_idx, (uint8_t)r, &v) == 0) {

dump[off + (r - reg_start) * 2 + 0] = (uint8_t)r;

dump[off + (r - reg_start) * 2 + 1] = v;

            } else {

tprint(“per-reg read failed reg=%d\n”, r);

            }

        }

off += want;

    }

if (off == total_bytes) {

tprint(“dump collected:\n”);

for (int i = 0; i < total_bytes; ++i) {

printk("%02X ", dump[i]);

if ((i & 0x0F) == 0x0F) printk(“\n”);

        }

printk(“\n”);

    }

} else {

tprint(“skipping dump: attrs invalid or single-reads failed\n”);

}

/\* Configure per Table 9: set output mode to both raw+algo and FIFO threshold \*/

/\* Example: write Family=0x10 Index=0x00 WriteByte=0x03 to set output to both sensor+algo

 \* The vendor flow for these families uses write->STOP->CMD_DELAY->read(status).

 \*/

uint8_t tx_outmode[3] = { FAMILY_OUTPUT_MODE, 0x00, 0x03 };

(void)vendor_write_then_read(tx_outmode, 3, NULL, 0, NULL);

tprint(“Set output mode to both (requested)\n”);

uint8_t tx_thresh[3] = { FAMILY_OUTPUT_MODE, 0x01, 0x0F }; /* set FIFO almost-full threshold = 0x0F */

(void)vendor_write_then_read(tx_thresh, 3, NULL, 0, NULL);

tprint(“Set FIFO threshold to 0x0F\n”);

/\* Enable MAX30101 sensor (FAMILY 0x44 index 0x03 with write 0x01) \*/

uint8_t tx_enable_sensor[4] = { 0x44, 0x03, 0x01, 0x00 }; /* some firmwares accept 3 bytes; vendor_write_then_read will accept opt_len */

/\* Use stability flow for enabling: wait stable then write \*/

(void)stability_checked_vendor_fetch(0x44, 0x03, (uint8_t\[\]){0x01}, 1, NULL, 0);

tprint(“Enabled MAX30101\n”);

/\* Enable algorithm WHRM / MaximFast (Family 0x52, index 0x02 with 0x01) \*/

(void)stability_checked_vendor_fetch(FAMILY_ALGO_ENABLE, 0x02, (uint8_t\[\]){0x01}, 1, NULL, 0);

tprint(“Enabled WHRM/MaximFast algorithm\n”);

/\* Poll status (Family=0x00 probe) to see DataRdyInt, then read FIFO count and data per app note \*/

tprint(“Waiting for data (DataRdyInt)…\n”);

for (;:wink: {

    /\* prefer MFIO IRQ if available \*/

if (!wait_data_ready_ms(2000)) {

        /\* fallback: poll status \*/

uint8_t s = 0xFF;

if (vendor_read_status(&s) != 0) { k_msleep(100); continue; }

if ((s & 0x08) == 0) { k_msleep(100); continue; } /* DataRdyInt bit (example: bit 3) */

    }

    /\* Read FIFO count (family 0x12 index 0x00) \*/

uint8_t fifo_cnt = 0;

if (stability_checked_vendor_fetch(FAMILY_READ_FIFO_COUNT, 0x00, NULL, 0, &fifo_cnt, 1) != 0) {

tprint(“Failed to read FIFO count\n”);

k_msleep(100);

continue;

    }

tprint(“FIFO count reported = %u\n”, fifo_cnt);

if (fifo_cnt == 0) { k_msleep(20); continue; }

    /\* Read FIFO data: family 0x12 index 0x01 returns block of sample bytes.

     \* Per app note, each sample is 3 bytes IR + 3 bytes RED + algorithm bytes depending on mode.

     \* Here we read raw 6-byte samples one at a time using vendor flow (write 0x12,0x01 then read 1+6)

     \*/

const int leds = 3; // IR + RED (add more if you enable LED3)

const bool accel = false; // true if you enable the accel

const bool sample_counter = false; // true if you select 0x05/0x06/0x07

const int sensor_bytes = leds*3 + (accel ? 6 : 0);

const int algo_bytes = 6; // WHRM/MaximFast

const int sample_bytes = sensor_bytes + algo_bytes + (sample_counter ? 1 : 0);

for (int i = 0; i < fifo_cnt; ++i) {

uint8_t s[32]; // big enough for typical configs

if (stability_checked_vendor_fetch(FAMILY_READ_FIFO_COUNT, 0x01, NULL, 0, s, sample_bytes) != 0) {

tprint(“FIFO read failed\n”);

break;

    }

size_t o = 0;

if (sample_counter) { uint8_t sc = s[o++]; (void)sc; } // optional

// RAW PPG (big-endian 24-bit; mask to 18 bits for MAX30101)

uint32_t led1 = parse24(&s[o]); o += 3; // usually IR

uint32_t led2 = parse24(&s[o]); o += 3; // usually RED

// if you enabled LED3: uint32_t led3 = parse24(&s[o]); o += 3;

// if accel enabled: int16_t ax = (int16_t)be16(&s[o]); o+=2; … (ay, az)

// ALGORITHM (big-endian)

uint16_t hr10 = be16(&s[o]); o += 2; // HR in 0.1 bpm

uint8_t conf = s[o++];

uint16_t spo210 = be16(&s[o]); o += 2; // SpO2 in 0.1 %

int8_t status = (int8_t)s[o++];

tprint(“IR=%lu RED=%lu HR=%u.%u bpm Conf=%u%% SpO2=%u.%u%% Status=%d\n”,

        (unsigned long)led1, (unsigned long)led2,

hr10/10, hr10%10, conf, spo210/10, spo210%10, status);

}

}

return 0;

}

That’s quite a bit to look through lol

How is it being used? How is it mounted? Is potential sweat or something in the environment potentially a factor?