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%

Serial Output

[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

Code (main.c)
/*
* 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=%dn”, 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=%dn”, 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%02Xn”, 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; retryingn”, 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=%dn”, mfio_high ? 1 : 0);
	k_msleep(5);
	gpio_pin_set(gpio_dev, RSTN_PIN, 0);
	tprint(“boot: assert RSTN=0n”);
	k_msleep(10);
	gpio_pin_set(gpio_dev, RSTN_PIN, 1);
	tprint(“boot: release RSTN=1n”);
	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 readyn”);
		return 0;
	}
	
	tprint(“i2c ready: %sn”, i2c->name);
	if (device_is_ready(gpio_dev)) tprint(“gpio ready: %sn”, gpio_dev->name);
	maybe_recover_bus();
	
	/* Boot into app mode (MFIO=1 => application) */
	hard_reset_hub_once(true);
	tprint(“Boot: application moden”);
	
	/* 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 configuredn”);
	}
	
	/* Probe indices 0..7 to find MAX30101 */
	int detected = -1;
	uint8_t attrs[2] = { 0xFF, 0xFF };
	tprint(“Probing indices for MAX30101 attributesn”);
	for (int idx = 0; idx < 8; ++idx) {
		int rc = read_afe_attrs(idx, attrs);
		tprint(“probe idx=%d rc=%d attrs=%02X %02Xn”, 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=%dn”, 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%02Xn”, 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%02Xn”, 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=%dn”, 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 readsn”, 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=%dn”, 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 failedn”);
	}
	
	/* 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 0x0Fn”);
	
	/* 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 MAX30101n”);
	
	/* 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 algorithmn”);
	
	/* 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 countn”);
			k_msleep(100);
			continue;
		}
		
		tprint(“FIFO count reported = %un”, 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 failedn”);
				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=%dn”,
				(unsigned long)led1, (unsigned long)led2,
				hr10/10, hr10%10, conf, spo210/10, spo210%10, status);
		}
	}
	
	return 0;
}

Hello, welcome to the TechForum!

It looks like there is a troubleshooting page for issues with getting a pulse reading, do the tips they provide solve your issue?

I have looked into it and no luck… I2C communication is happening ok, and so is the reset/init (with MFIO) procedure… i checked with a logic analyzer
but the issue persists

Hi @manuel.reiscarneiro,

I would argue that the IR and Red LED values do not look OK as they are not oscillating as you would expect them to with a finger held to the sensor. Unless I’m missing something, the Status values appear to be nonsensical as well.

To make it easier to follow along with your code, can you post the startup/initialization portion of the serial output as well? Also, did you write this code from scratch or is it ported from another source?

Hi @manuel.reiscarneiro ,

There may be a design mistake on the Sparkfun board. The I2C bus is pulled to 3.3V although the chips are powered from 1.8V. The MAX32664 datasheet does not say if that is permitted or not.

Cheers, heke

Hi @Matt_Mielke
I can see them changing from near 0 when no finger is on the sensor, to high values when i put my finger on the sensor. i agree that they should fluctuate but at least i get some kind of reaction

@Matt_Mielke here is the output on the startup until start getting values from after MFIO interrupts. I made the code. I used the arduino library for this sensor as the reference for the commands and all

*** Using Zephyr OS v4.1.99-ff8f0c579eeb ***
[275] i2c ready: i2c@40003000n[278] gpio ready: gpio@50000000
[286] boot: set MFIO=1
[293] boot: assert RSTN=0
[305] boot: release RSTN=1
[908] Boot: application mode
[910] MFIO DataReady IRQ configured
[913] Probing indices for MAX30101 attributes
[921] bridge status=0xFF raw: FF
[941] bridge status=0xFF raw: FF
[957] i2c_write failed rc=-5
[978] i2c_write failed rc=-5
[1000] i2c_write failed rc=-5
[1021] i2c_write failed rc=-5
[1042] i2c_write failed rc=-5
[1170] bridge status=0x02 raw: 02 00 00
[1173] fetch attempt 1 failed rc=-5 status=0x02; retrying
[1195] bridge status=0x02 raw: 02 00 00
[1198] fetch attempt 2 failed rc=-5 status=0x02; retrying
[1230] bridge status=0x02 raw: 02 00 00
[1233] fetch attempt 3 failed rc=-5 status=0x02; retrying
[1274] bridge status=0x02 raw: 02 00 00
[1278] fetch attempt 4 failed rc=-5 status=0x02; retrying
[1329] bridge status=0x02 raw: 02 00 00
[1333] fetch attempt 5 failed rc=-5 status=0x02; retrying
[1394] bridge status=0x02 raw: 02 00 00
[1398] fetch attempt 6 failed rc=-5 status=0x02; retrying
[1468] probe idx=0 rc=-5 attrs=FF FF
[1578] bridge status=0x02 raw: 02 00 00
[1582] fetch attempt 1 failed rc=-5 status=0x02; retrying
[1603] bridge status=0x02 raw: 02 00 00
[1606] fetch attempt 2 failed rc=-5 status=0x02; retrying
[1638] bridge status=0x02 raw: 02 00 00
[1641] fetch attempt 3 failed rc=-5 status=0x02; retrying
[1683] bridge status=0x02 raw: 02 00 00
[1686] fetch attempt 4 failed rc=-5 status=0x02; retrying
[1738] bridge status=0x02 raw: 02 00 00
[1741] fetch attempt 5 failed rc=-5 status=0x02; retrying
[1803] bridge status=0x02 raw: 02 00 00
[1806] fetch attempt 6 failed rc=-5 status=0x02; retrying
[1877] probe idx=1 rc=-5 attrs=FF FF
[1986] bridge status=0x02 raw: 02 00 00
[1990] fetch attempt 1 failed rc=-5 status=0x02; retrying
[2011] bridge status=0x02 raw: 02 00 00
[2015] fetch attempt 2 failed rc=-5 status=0x02; retrying
[2046] bridge status=0x02 raw: 02 00 00
[2050] fetch attempt 3 failed rc=-5 status=0x02; retrying
[2091] bridge status=0x02 raw: 02 00 00
[2095] fetch attempt 4 failed rc=-5 status=0x02; retrying
[2146] bridge status=0x02 raw: 02 00 00
[2150] fetch attempt 5 failed rc=-5 status=0x02; retrying
[2211] bridge status=0x02 raw: 02 00 00
[2215] fetch attempt 6 failed rc=-5 status=0x02; retrying
[2285] probe idx=2 rc=-5 attrs=FF FF
[2395] probe idx=3 rc=0 attrs=01 24
[2398] using afe_idx=3
[2807] R_MODE_CONF rc=0 val=0x80
[2916] R_FIFO_CONF rc=0 val=0x2F
[2919] attempting chunked dump regs=36 total=72
[3031] bridge status=0xFE raw: FE 00 24 00 00 00 00 00 00 00 00 00 00
[3037] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[3060] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[3066] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[3098] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[3104] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[3147] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[3153] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[3205] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[3211] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[3273] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[3279] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[3350] dump chunk failed rc=-5 off=0 want=12 — falling back to per-register reads
[4105] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[4111] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[4133] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[4139] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[4172] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[4178] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[4220] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[4226] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[4279] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[4285] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[4347] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[4353] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[4424] dump chunk failed rc=-5 off=12 want=12 — falling back to per-register reads
[5179] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[5185] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[5207] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[5213] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[5246] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[5252] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[5294] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[5300] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[5352] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[5359] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[5421] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[5427] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[5497] dump chunk failed rc=-5 off=24 want=12 — falling back to per-register reads
[6253] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[6259] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[6281] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[6287] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[6319] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[6326] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[6368] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[6374] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[6426] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[6432] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[6495] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[6501] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[6571] dump chunk failed rc=-5 off=36 want=12 — falling back to per-register reads
[7326] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[7332] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[7355] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[7361] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[7393] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[7399] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[7442] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[7448] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[7500] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[7506] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[7568] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[7575] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[7645] dump chunk failed rc=-5 off=48 want=12 ��� falling back to per-register reads
[8400] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[8406] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[8429] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[8435] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[8467] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[8473] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[8515] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[8522] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[8574] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[8580] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[8642] bridge status=0xFE raw: FE 00 01 01 00 02 40 03 00 04 00 05 00
[8648] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[8719] dump chunk failed rc=-5 off=60 want=12 �� falling back to per-register reads
[9366] dump collected:
00 01 01 00 02 40 03 00 04 00 05 00 06 00 07 00
08 2F 09 80 0A 00 0B 00 0C 00 0D 19 0E 00 0F 00
10 00 11 00 12 00 13 0F 14 FF 15 FF 16 FF 17 FF
18 00 19 00 1A 00 1B 00 1C 00 1D 00 1E 00 1F 00
20 00 21 00 22 00 23 00 n[9389] Set output mode to both (requested)
[9394] Set FIFO threshold to 0x0F
[9503] bridge status=0xFE raw: FE
[9506] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[9528] bridge status=0xFE raw: FE
[9531] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[9562] bridge status=0xFE raw: FE
[9565] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[9606] bridge status=0xFE raw: FE
[9609] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[9661] bridge status=0xFE raw: FE
[9664] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[9725] bridge status=0xFE raw: FE
[9728] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[9798] Enabled MAX30101
[9907] bridge status=0xFE raw: FE
[9910] fetch attempt 1 failed rc=-5 status=0xFE; retrying
[9931] bridge status=0xFE raw: FE
[9934] fetch attempt 2 failed rc=-5 status=0xFE; retrying
[9966] bridge status=0xFE raw: FE
[9969] fetch attempt 3 failed rc=-5 status=0xFE; retrying
[10010] bridge status=0xFE raw: FE
[10013] fetch attempt 4 failed rc=-5 status=0xFE; retrying
[10064] bridge status=0xFE raw: FE
[10068] fetch attempt 5 failed rc=-5 status=0xFE; retrying
[10129] bridge status=0xFE raw: FE
[10132] fetch attempt 6 failed rc=-5 status=0xFE; retrying
[10202] Enabled WHRM/MaximFast algorithm
[10206] Waiting for data (DataRdyInt)…
[10316] FIFO count reported = 4
[10427] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[10540] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[10653] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[10766] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[10878] FIFO count reported = 18
[10989] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[11102] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[11215] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[11328] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[11441] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[11554] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4
[11667] IR=971 RED=2 HR=0.0 bpm Conf=0% SpO2=0.0% Status=4

with the following code, I can set and read application, mode, i can read version, set algorythm only, and set interrup threshold - always returns 0x00

When I enable the AFE max30101, i get 0xFE as response, but the red LED lights up (prints at the end)

/*

* 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 5

#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);

}

/* 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;

}

/* 1. Set the application mode (Family=0x01, Index=0x00, Write=0x01) */

static int set_application_mode(void) {

uint8_t tx[3] = { 0x01, 0x00, 0x00 }; // Family=SET_DEVICE_MODE, Index=0x00, Write=0x01 (application)

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

}

/* 2. Read current mode (Family=0x02, Index=0x00) */

static int read_device_mode(uint8_t *mode_out) {

return stability_checked_vendor_fetch(0x02, 0x00, NULL, 0, mode_out, 1);

// mode_out[0] will be 0x00 (application) or 0x08 (bootloader)

}

/* 3. Read sensor hub version (Family=0xFF, Index=0x03) */

static int read_hub_version(uint8_t version[3]) {

return stability_checked_vendor_fetch(0xFF, 0x03, NULL, 0, version, 3);

// version[0] = major, version[1] = minor, version[2] = patch

// Example: 0x01 0x09 0x00 → v1.9.0

}

/* 4. Get MAX30101 register attributes (Family=0x42, Index=0x03) */

static int get_max30101_attrs(uint8_t attrs[2]) {

return stability_checked_vendor_fetch(0x42, 0x03, NULL, 0, attrs, 2);

// attrs[0] = register size (bytes per register)

// attrs[1] = number of registers available (e.g. 0x24 = 36 registers)

}

/* 5. Set output mode to sensor + algorithm data (Family=0x10, Index=0x00, Write=0x03) */

static int set_output_mode_sensor_algo(void) {

uint8_t tx[3] = { 0x10, 0x00, 0x03};

// Family=0x10, Index=0x00, Write=0x03

// Some implementations expect 3 bytes (family, index, write),

// others pad with a trailing 0x00. Adjust if needed.

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

}

/* 6. Set sensor hub interrupt threshold (Family=0x10, Index=0x01) */

static int set_interrupt_threshold(uint8_t threshold) {

uint8_t tx[3] = { 0x10, 0x01, threshold };

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

}

/* 7. Enable AGC algorithm (Family=0x52, Index=0x00, Write=0x01) */

static int enable_agc(void) {

uint8_t tx[3] = { 0x52, 0x00, 0x01 };

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

}

/* 8. Enable the MAX30101 AFE (Family=0x44, Index=0x03, Write=0x01) */

static int enable_max30101(void) {

uint8_t tx[3] = { 0x44, 0x03, 0x01 };

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

}

/* Get sensor hub attributes (Family=0x42, Index=0x00) */

static int get_sensor_hub_attrs(uint8_t *buf, size_t len, uint8_t *status){

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

return vendor_write_then_read(tx, sizeof(tx), buf, len, status);

}

/* later on: Dump all MAX30101 registers (Family=0x43, Index=0x03)

* Returns address/value pairs for all registers.

*/

static int dump_max30101_registers(void) {

uint8_t buf[72]; // 36 registers * 2 bytes (addr+val)

int rc = stability_checked_vendor_fetch(0x43, 0x03, NULL, 0, buf, sizeof(buf));

if (rc == 0) {

printk(“MAX30101 register dump (bulk):\n”);

for (int i = 0; i < sizeof(buf); i += 2) {

printk(“Reg 0x%02X = %3u (raw: 0x%02X)\n”, buf[i], buf[i+1], buf[i+1]);

    }

return 0;

}

printk(“Bulk dump not supported (rc=%d, status=0xFF). Falling back to individual reads.\n”, rc);

// Fallback: read registers one by one

uint8_t val;

for (uint8_t reg = 0; reg < 36; reg++) {

if (stability_checked_vendor_fetch(0x41, reg, NULL, 0, &val, 1) == 0) {

printk(“Reg 0x%02X = %3u (raw: 0x%02X)\n”, reg, val, val);

    } else {

printk(“Failed to read Reg 0x%02X\n”, reg);

return -1;

    }

}

return 0;

}

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: %sn”, 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”);

k_msleep(1200);

/\* 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”);

}

int aux;

uint8_t mode;

aux = set_application_mode();

printk(“set_application_mode() rc=%d\n”, aux);

k_msleep(100);

gpio_pin_configure(gpio_dev, MFIO_PIN, GPIO_INPUT | GPIO_PULL_UP);

aux = read_device_mode(&mode);

printk(“read_device_mode() rc=%d mode=0x%02X\n”, aux, mode);

uint8_t ver[3];

if (read_hub_version(ver) == 0) {

printk(“Hub version %u.%u.%u (raw: %02X %02X %02X)\n”,ver[0], ver[1], ver[2],ver[0], ver[1], ver[2]);

    }

uint8_t attrs[2];

if (get_max30101_attrs(attrs) == 0) {

printk(“MAX30101 attrs: size=%u, registers=%u (raw: %02X %02X)\n”,attrs[0], attrs[1],attrs[0], attrs[1]);

    }

int rc = set_output_mode_sensor_algo();

printk(“set_output_mode_sensor_algo() rc=%d\n”, rc);

if (rc == 0) {

printk(“Output mode set: sensor + algorithm data\n”);

    } else {

printk(“Failed to set output mode rc=%d\n”, rc);

    }

int rc1 = set_interrupt_threshold(0x0F); // threshold = 15 samples

printk(“set_interrupt_threshold(0x0F) rc=%d\n”, rc1);

if (rc1 == 0) {

printk(“Interrupt threshold set to %u samples (raw: 0x%02X)\n”, 0x0F, 0x0F);

    } else {

printk(“Failed to set interrupt threshold rc=%d\n”, rc1);

    }

k_msleep(5000);

int rc3 = enable_max30101();

printk(“enable_max30101() rc=%d\n”, rc3);

if (rc3 == 0) {

printk(“MAX30101 AFE enabled (raw: 0x01)\n”);

    } else {

printk(“Failed to enable MAX30101 rc=%d\n”, rc3);

    }

k_msleep(500);

printk(“read_device_mode() rc=%d mode=0x%02X\n”, aux, mode);

for (;:wink: {

}

return 0;

}

*** Booting nRF Connect SDK v3.1.1-e2a97fe2578a ***
*** Using Zephyr OS v4.1.99-ff8f0c579eeb ***
[270] i2c ready: i2c@40003000n[273] gpio ready: gpio@50000000
[281] boot: set MFIO=1
[288] boot: assert RSTN=0
[301] boot: release RSTN=1
[903] Boot: application mode
[2106] MFIO DataReady IRQ configured
set_application_mode() rc=0
read_device_mode() rc=0 mode=0x00
Hub version 10.1.0 (raw: 0A 01 00)
MAX30101 attrs: size=1, registers=36 (raw: 01 24)
set_output_mode_sensor_algo() rc=0
Output mode set: sensor + algorithm data
set_interrupt_threshold(0x0F) rc=0
Interrupt threshold set to 15 samples (raw: 0x0F)
[7560] bridge status=0xFE raw: FE
enable_max30101() rc=-5
Failed to enable MAX30101 rc=-5
read_device_mode() rc=0 mode=0x00

Thanks for the update @manuel.reiscarneiro. Since your code is so long, though, please just attach the main.c file rather than copying and pasting it’s contents. That will save us a lot of trouble in having to manually fix the formatting to make it readable. Once you do this, I’ll take a closer look at your changes and see if we can help you zero in on the issue.

main.c (15.0 KB)Things I see happening:

i get 0xFE as response for all commands after i set the interrup threshold (enable afe, enable algorythm, disable AGC)

Yet despite the weird response (should be 0x00), when i enable the AFE, the red led lights up; and after i disable the AGC, the light intensity of the red led stopx changing when i move my finger closer, which leads me to thing that the hub/AFE are acepting the commands despite the weird responses

Sounds like you’re looking at the older version (Rev 0) of “UG6806: MAX32664 User Guide” hosted by SparkFun. You should check out the latest version (Rev 3) of UG6806 available from ADI.

There you’ll find in Table 5:
image

You’ll also find updated timing information:
image