MAX30001 Driver Init Failure (ID 0x52 Mismatch) on nRF52DK - Works on ESP32

Description: Hello,

I am integrating a Protocentral MAX30001 ECG sensor with the nRF52DK (nRF52832) using the nRF Connect SDK v3.1.1.

The Issue: The sensor hardware is functional, but the Zephyr driver fails to initialize it because of a strict Chip ID check. The sensor returns a Silicon Revision ID of 0x52, but the driver hard-codes a check for 0x54, causing it to abort.

1. Evidence of Working Hardware (ESP32 Context): This exact sensor breakout board works perfectly with an ESP32-S3 using the Arduino Protocentral library. On the ESP32, the library accepts the sensor regardless of the minor revision number, and I can visualize clear ECG data. This confirms the wiring and sensor health are good.

2. The Failure on nRF52 (Serial Monitor Logs): On the nRF52DK, the SPI communication succeeds (I can read the ID), but the driver throws an error immediately after reading the ID.

Serial Output:

Plaintext
MAX30001 ID: 52 0 0
[00:00:00.549,804] <err> SENSOR_MAX30001: MAX30001 not found
*** Booting nRF Connect SDK v3.1.1 ***
device is 0x99d0, name is max30001@0
[00:00:00.550,140] <inf> app: Current sensor value = 0

As seen above, the ID is read as 0x52, but the driver logs MAX30001 not found and stops, leaving the sensor value at 0.

3. The Responsible Code (Root Cause): I located the specific blocking code in the driver file modules/protocentral_max30001/drivers/sensor/max30001/max30001.c. The initialization function strictly demands 0x54:

C
/* In max30001_chip_init */
if (chip_id[0] != 0x54) // <--- FAILS HERE because my ID is 0x52
{
    LOG_ERR("MAX30001 not found");
    return -ENODEV;
}

4. The Build System Issue: I am attempting to patch this locally to allow 0x52 (Revision 2), but west build does not seem to pick up my changes to the driver file. It appears to be pulling the module from a location different than my local project structure (likely due to how west.yml handles remote modules).

My Questions:

  1. Is there a recommended way to override this specific driver file in NCS without forking the entire remote module?

  2. How can I force the build system to use my local patch so the driver accepts ID 0x52?Any assistance in resolving this build/path issue would be appreciated.

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/sensor.h>
#include "max30001.h"
#include <zephyr/logging/log.h>

#define LOG_MODULE_NAME app
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS   2000

/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)

/*
 * A build error on this line means your board is unsupported.
 * See the sample documentation for information on how to fix this.
 */
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);


#define THREAD_STACKSIZE        1024
#define THREAD_PRIORITY         7

K_THREAD_STACK_DEFINE(sensor_thread_stack, THREAD_STACKSIZE);
static struct k_thread sensor_thread;
K_MUTEX_DEFINE(sensor_mutex);
static int32_t sensor_value;

static void thread_entry(void *p1, void *p2, void *p3)
{
	int ret;
	struct sensor_value my_sensor_value;

	const struct device *dev = (const struct device *)p1;
	struct k_mutex *temp_mutex = (struct k_mutex *)p2;
	int32_t *temp = (int32_t *)p3;

	while (1) {
        LOG_INF("Attempting to fetch sample...");
		ret = sensor_sample_fetch(dev); // Try to get data

		if (ret != 0) {
			// Fetch failed, print error and DO NOT blink
			LOG_INF("sensor_sample_fetch FAILED (ret %d). Check wiring.\n", ret);
		} else {
			// Fetch was OK! Now get the channel data.
    		ret = sensor_channel_get(dev, SENSOR_CHAN_ECG_UV, &my_sensor_value);
    		if (ret != 0) {
    			LOG_INF("sensor_channel_get failed ret %d\n", ret);
    		} else {
                // SUCCESS! We got a value.
        		LOG_INF("Fetch SUCCESS! Toggling LED.");
				
				// 1. Update the global value
				if (k_mutex_lock(temp_mutex, K_MSEC(2000)) == 0) {
        			*temp = my_sensor_value.val1;
        			k_mutex_unlock(temp_mutex);
        		} else {
        			LOG_INF("Unable to lock mutex\n");
        		}
                
                // 2. Toggle the LED to signal success
                gpio_pin_toggle_dt(&led);
            }
        }
        
		// Wait 2 seconds before trying again
		k_sleep(K_MSEC(2000));
	}
}

void sensor_init(void)
{
	const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(max30001));	    
    __ASSERT(dev != NULL, "Failed to get device binding");
	__ASSERT(device_is_ready(dev), "Device %s is not ready", dev->name);
	printk("device is %p, name is %s\n", dev, dev->name);
	
    k_thread_create(&sensor_thread, sensor_thread_stack,
			K_THREAD_STACK_SIZEOF(sensor_thread_stack),
			thread_entry, (void *)dev, (void *)&sensor_mutex, (void*)&sensor_value,
			THREAD_PRIORITY, 0, K_FOREVER);
	k_thread_start(&sensor_thread);
}

int32_t value_get(void)
{
	int32_t val = 0;

	if (k_mutex_lock(&sensor_mutex, K_MSEC(100)) == 0) {
		val = sensor_value;
		k_mutex_unlock(&sensor_mutex);
	}

	return val;
}

int main(void)
{
	int ret;
    int32_t current_sensor_value;
    sensor_init(); // This starts the sensor thread

	if (!gpio_is_ready_dt(&led)) {
		return 0;
	}

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (ret < 0) {
		return 0;
	}

    // Set the LED to OFF at the start.
	// It will only turn on if the sensor thread is successful.
    gpio_pin_set_dt(&led, 0); 
	LOG_INF("LED set to OFF. Waiting for successful sensor read...");

	while (1) {
        // The main loop will now just print the sensor value
        current_sensor_value = value_get();
        LOG_INF("Current sensor value = %d", current_sensor_value);
		k_msleep(SLEEP_TIME_MS);
	}
	return 0;
}
7416.nrf52dk_nrf52832.overlay
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(blinky)

target_sources(app PRIVATE src/main.c)

Parents
  • Hello,

    Is there a recommended way to override this specific driver file in NCS without forking the entire remote module?

    I think it’s better to treat the driver as a module and point west.yml to your repository branch where you modify the C file. See this older DevZone ticket,it explains the same.

    How can I force the build system to use my local patch so the driver accepts ID 0x52?Any assistance in resolving this build/path issue would be appreciated.

    Creating your own fork of the MAX30001 module and pointing your west.yml entry to your fork/branch is the best option, in my opinion. Otherwise, you can edit the module directly and modify the C file inside the module directory. From the next build onward, the edited/patched source file will be used by the build system.

    Kind Regards,

    Abhijith

  • thank you for the assistance, now i have solved the issue, i manually changed the C file of the driver and the sensor is getting detected, but i do have some issues with the data which i am getting from the sensor, so can you assist with that? 

Reply Children
No Data
Related