This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Getting "inactive sensor channel" when using more than one channel on heart rate sensor MAX30101 sample code

Hello, I am using:

NCS v1.5.1 and the sample code from ~/ncs/v1.5.1/zephyr/drivers/sensor/max30101/ (max30101.c, max30101.h)
nRF5340dk
MAX30101 from Sparkfun www.sparkfun.com/.../16474

Sorry for the newbie question but I am a newer programmer and have been trying to figure this out for a long time. It *should* be simple.
The sample that comes with NCS for sensor max30101 is incomplete and only gets readings from one channel (GREEN) but I am trying to figure out how to get readings from the other two (IR, RED).

The readings for those channels are always the same value. IR is always at the maximum value (doesn't change) and RED is always 0.
The second and third channels keep on throwing an error: "Inactive sensor channel" which is thrown if fifo_chan >= 3. I can verify from debugging that fifo_chan for those channels is always gets set to 3 for some reason.

Why do I keep on getting no readings for my other second and third channels?
Why does fifo_chan keep on getting set to 3 for the second and third channels?
Am I doing something wrong?

This is my main.c

#include <zephyr.h>
#include <sys/printk.h>
#include <drivers/sensor.h>
#include <stdio.h>

void main(void)
{
	struct sensor_value green;
// begin what I added
	struct sensor_value ir;
	struct sensor_value red;
// end what I added

	const struct device *dev = device_get_binding(DT_LABEL(DT_INST(0, max_max30101)));

	if (dev == NULL) {
		printf("Could not get max30101 device\n");
		return;
	}

	while (1) {
		sensor_sample_fetch(dev);
                printf("\n==============================================================================\n");
		
		sensor_channel_get(dev, SENSOR_CHAN_GREEN, &green);
		printf("GREEN=%d\n", green.val1);

// begin what I added
                sensor_channel_get(dev, SENSOR_CHAN_IR, &ir);
		printf("IR=%d\n", ir.val1);
		
                sensor_channel_get(dev, SENSOR_CHAN_RED, &red);
		printf("RED=%d\n", red.val1);

		printf("\n");
// end what I added

		k_sleep(K_MSEC(5000));
	}
}


This is the code from max30101.c. I added one line for debugging (line 81) and verified that led_chan is set correctly but I still keep on getting fifo_chan set to 3.

/*
 * Copyright (c) 2017, NXP
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT max_max30101

#include <logging/log.h>

#include "max30101.h"

LOG_MODULE_REGISTER(MAX30101, CONFIG_SENSOR_LOG_LEVEL);

static int max30101_sample_fetch(const struct device *dev,
				 enum sensor_channel chan)
{
	struct max30101_data *data = dev->data;
	const struct max30101_config *config = dev->config;
	uint8_t buffer[MAX30101_MAX_BYTES_PER_SAMPLE];
	uint32_t fifo_data;
	int fifo_chan;
	int num_bytes;
	int i;

	/* Read all the active channels for one sample */
	num_bytes = data->num_channels * MAX30101_BYTES_PER_CHANNEL;
	if (i2c_burst_read(data->i2c, config->i2c_addr,
			   MAX30101_REG_FIFO_DATA, buffer, num_bytes)) {
		LOG_ERR("Could not fetch sample");
		return -EIO;
	}

	fifo_chan = 0;
	for (i = 0; i < num_bytes; i += 3) {
		/* Each channel is 18-bits */
		fifo_data = (buffer[i] << 16) | (buffer[i + 1] << 8) |
			    (buffer[i + 2]);
		fifo_data &= MAX30101_FIFO_DATA_MASK;

		/* Save the raw data */
		data->raw[fifo_chan++] = fifo_data;
	}

	return 0;
}

static int max30101_channel_get(const struct device *dev,
				enum sensor_channel chan,
				struct sensor_value *val)
{
	struct max30101_data *data = dev->data;
	enum max30101_led_channel led_chan;
	int fifo_chan;

	switch (chan) {
	case SENSOR_CHAN_RED:
		led_chan = MAX30101_LED_CHANNEL_RED;
		break;

	case SENSOR_CHAN_IR:
		led_chan = MAX30101_LED_CHANNEL_IR;
		break;

	case SENSOR_CHAN_GREEN:
		led_chan = MAX30101_LED_CHANNEL_GREEN;
		break;

	default:
		LOG_ERR("Unsupported sensor channel");
		return -ENOTSUP;
	}

	/* Check if the led channel is active by looking up the associated fifo
	 * channel. If the fifo channel isn't valid, then the led channel
	 * isn't active.
	 */
	fifo_chan = data->map[led_chan];

// begin what I added
	LOG_ERR("led_chan: %d. fifo_chan: %d. MAX30101_MAX_NUM_CHANNELS: %d  ", led_chan, fifo_chan, MAX30101_MAX_NUM_CHANNELS);
// end what I added

	if (fifo_chan >= MAX30101_MAX_NUM_CHANNELS) {
		LOG_ERR("Inactive sensor channel\n");
		return -ENOTSUP;
	}



	/* TODO: Scale the raw data to standard units */
	val->val1 = data->raw[fifo_chan];
	val->val2 = 0;

	return 0;
}

static const struct sensor_driver_api max30101_driver_api = {
	.sample_fetch = max30101_sample_fetch,
	.channel_get = max30101_channel_get,
};

static int max30101_init(const struct device *dev)
{
	const struct max30101_config *config = dev->config;
	struct max30101_data *data = dev->data;
	uint8_t part_id;
	uint8_t mode_cfg;
	uint32_t led_chan;
	int fifo_chan;

	/* Get the I2C device */
	data->i2c = device_get_binding(config->i2c_label);
	if (!data->i2c) {
		LOG_ERR("Could not find I2C device");
		return -EINVAL;
	}

	/* Check the part id to make sure this is MAX30101 */
	if (i2c_reg_read_byte(data->i2c, config->i2c_addr,
			      MAX30101_REG_PART_ID, &part_id)) {
		LOG_ERR("Could not get Part ID");
		return -EIO;
	}
	if (part_id != MAX30101_PART_ID) {
		LOG_ERR("Got Part ID 0x%02x, expected 0x%02x",
			    part_id, MAX30101_PART_ID);
		return -EIO;
	}

	/* Reset the sensor */
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_MODE_CFG,
			       MAX30101_MODE_CFG_RESET_MASK)) {
		return -EIO;
	}

	/* Wait for reset to be cleared */
	do {
		if (i2c_reg_read_byte(data->i2c, config->i2c_addr,
				      MAX30101_REG_MODE_CFG, &mode_cfg)) {
			LOG_ERR("Could read mode cfg after reset");
			return -EIO;
		}
	} while (mode_cfg & MAX30101_MODE_CFG_RESET_MASK);

	/* Write the FIFO configuration register */
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_FIFO_CFG, config->fifo)) {
		return -EIO;
	}

	/* Write the mode configuration register */
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_MODE_CFG, config->mode)) {
		return -EIO;
	}

	/* Write the SpO2 configuration register */
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_SPO2_CFG, config->spo2)) {
		return -EIO;
	}

	/* Write the LED pulse amplitude registers */
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_LED1_PA, config->led_pa[0])) {
		return -EIO;
	}
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_LED2_PA, config->led_pa[1])) {
		return -EIO;
	}
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_LED3_PA, config->led_pa[2])) {
		return -EIO;
	}

#ifdef CONFIG_MAX30101_MULTI_LED_MODE
	uint8_t multi_led[2];

	/* Write the multi-LED mode control registers */
	multi_led[0] = (config->slot[1] << 4) | (config->slot[0]);
	multi_led[1] = (config->slot[3] << 4) | (config->slot[2]);

	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_MULTI_LED, multi_led[0])) {
		return -EIO;
	}
	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
			       MAX30101_REG_MULTI_LED + 1, multi_led[1])) {
		return -EIO;
	}
#endif

	/* Initialize the channel map and active channel count */
	data->num_channels = 0U;
	for (led_chan = 0U; led_chan < MAX30101_MAX_NUM_CHANNELS; led_chan++) {
		data->map[led_chan] = MAX30101_MAX_NUM_CHANNELS;
	}

	/* Count the number of active channels and build a map that translates
	 * the LED channel number (red/ir/green) to the fifo channel number.
	 */
	for (fifo_chan = 0; fifo_chan < MAX30101_MAX_NUM_CHANNELS;
	     fifo_chan++) {
		led_chan = (config->slot[fifo_chan] & MAX30101_SLOT_LED_MASK)-1;
		if (led_chan < MAX30101_MAX_NUM_CHANNELS) {
			data->map[led_chan] = fifo_chan;
			data->num_channels++;
		}
	}

	return 0;
}

static struct max30101_config max30101_config = {
	.i2c_label = DT_INST_BUS_LABEL(0),
	.i2c_addr = DT_INST_REG_ADDR(0),
	.fifo = (CONFIG_MAX30101_SMP_AVE << MAX30101_FIFO_CFG_SMP_AVE_SHIFT) |
#ifdef CONFIG_MAX30101_FIFO_ROLLOVER_EN
		MAX30101_FIFO_CFG_ROLLOVER_EN_MASK |
#endif
		(CONFIG_MAX30101_FIFO_A_FULL <<
		 MAX30101_FIFO_CFG_FIFO_FULL_SHIFT),

#if defined(CONFIG_MAX30101_HEART_RATE_MODE)
	.mode = MAX30101_MODE_HEART_RATE,
	.slot[0] = MAX30101_SLOT_RED_LED1_PA,
	.slot[1] = MAX30101_SLOT_DISABLED,
	.slot[2] = MAX30101_SLOT_DISABLED,
	.slot[3] = MAX30101_SLOT_DISABLED,
#elif defined(CONFIG_MAX30101_SPO2_MODE)
	.mode = MAX30101_MODE_SPO2,
	.slot[0] = MAX30101_SLOT_RED_LED1_PA,
	.slot[1] = MAX30101_SLOT_IR_LED2_PA,
	.slot[2] = MAX30101_SLOT_DISABLED,
	.slot[3] = MAX30101_SLOT_DISABLED,
#else
	.mode = MAX30101_MODE_MULTI_LED,
	.slot[0] = CONFIG_MAX30101_SLOT1,
	.slot[1] = CONFIG_MAX30101_SLOT2,
	.slot[2] = CONFIG_MAX30101_SLOT3,
	.slot[3] = CONFIG_MAX30101_SLOT4,
#endif

	.spo2 = (CONFIG_MAX30101_ADC_RGE << MAX30101_SPO2_ADC_RGE_SHIFT) |
		(CONFIG_MAX30101_SR << MAX30101_SPO2_SR_SHIFT) |
		(MAX30101_PW_18BITS << MAX30101_SPO2_PW_SHIFT),

	.led_pa[0] = CONFIG_MAX30101_LED1_PA,
	.led_pa[1] = CONFIG_MAX30101_LED2_PA,
	.led_pa[2] = CONFIG_MAX30101_LED3_PA,
};

static struct max30101_data max30101_data;

DEVICE_DT_INST_DEFINE(0, max30101_init, device_pm_control_nop,
		    &max30101_data, &max30101_config,
		    POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
		    &max30101_driver_api);


This is the output:

*** Booting Zephyr OS build v2.4.99-ncs2  ***

==============================================================================
GREEN=291
IR=29473
RED=0

[00:00:00.403,747] [1B][1;31m<err> MAX30101: led_chan: 2. fifo_chan: 0. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:00.404,724] [1B][1;31m<err> MAX30101: led_chan: 1. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:00.404,724] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m
[00:00:00.405,578] [1B][1;31m<err> MAX30101: led_chan: 0. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:00.405,578] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m

==============================================================================
GREEN=131
IR=29473
RED=0

[00:00:05.414,306] [1B][1;31m<err> MAX30101: led_chan: 2. fifo_chan: 0. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:05.415,252] [1B][1;31m<err> MAX30101: led_chan: 1. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:05.415,283] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m
[00:00:05.416,137] [1B][1;31m<err> MAX30101: led_chan: 0. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:05.416,137] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m

==============================================================================
GREEN=337
IR=29473
RED=0

[00:00:10.424,835] [1B][1;31m<err> MAX30101: led_chan: 2. fifo_chan: 0. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:10.425,781] [1B][1;31m<err> MAX30101: led_chan: 1. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:10.425,811] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m
[00:00:10.426,666] [1B][1;31m<err> MAX30101: led_chan: 0. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:10.426,666] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m

==============================================================================
GREEN=267
IR=29473
RED=0

[00:00:15.435,363] [1B][1;31m<err> MAX30101: led_chan: 2. fifo_chan: 0. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:15.436,309] [1B][1;31m<err> MAX30101: led_chan: 1. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:15.436,309] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m
[00:00:15.437,194] [1B][1;31m<err> MAX30101: led_chan: 0. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:15.437,194] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m

==============================================================================
GREEN=246
IR=29473
RED=0

[00:00:20.445,861] [1B][1;31m<err> MAX30101: led_chan: 2. fifo_chan: 0. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:20.446,838] [1B][1;31m<err> MAX30101: led_chan: 1. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:20.446,838] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m
[00:00:20.447,723] [1B][1;31m<err> MAX30101: led_chan: 0. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:20.447,723] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m

==============================================================================
GREEN=301
IR=29473
RED=0

[00:00:25.456,390] [1B][1;31m<err> MAX30101: led_chan: 2. fifo_chan: 0. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:25.457,366] [1B][1;31m<err> MAX30101: led_chan: 1. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:25.457,366] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m
[00:00:25.458,251] [1B][1;31m<err> MAX30101: led_chan: 0. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:25.458,251] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m

==============================================================================
GREEN=143
IR=29473
RED=0

[00:00:30.466,949] [1B][1;31m<err> MAX30101: led_chan: 2. fifo_chan: 0. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:30.467,895] [1B][1;31m<err> MAX30101: led_chan: 1. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:30.467,895] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m
[00:00:30.468,780] [1B][1;31m<err> MAX30101: led_chan: 0. fifo_chan: 3. MAX30101_MAX_NUM_CHANNELS: 3  [1B][0m
[00:00:30.468,780] [1B][1;31m<err> MAX30101: Inactive sensor channel
[1B][0m

Please help.  I have gone through documentation for several weeks and my brain is numb at this point.  

Parents
  • Thank-you very much for the reply, Wasan.

    I edited my prj.conf according to your suggestions, re-opened the project, re-built, and re-run, but I am getting the same output as before where RED is always 0 and IR is always 29473.

    Here is my prj.conf:

    CONFIG_SERIAL=y
    CONFIG_STDOUT_CONSOLE=y
    CONFIG_LOG=y
    CONFIG_I2C=y
    CONFIG_SENSOR=y
    CONFIG_SENSOR_LOG_LEVEL_DBG=y
    CONFIG_MAX30101=y
    CONFIG_MAX30101_MULTI_LED_MODE=y
    CONFIG_MAX30101_LED2_PA=0x0f

    In max30101.c the part that confuses me is line 78 to 87 since fifo_chan always gets set to 3 when led_chan is 1 or 0, which is what throws the "Inactive sensor channel" error.  What is the right way to activate those 2 channels correctly in code and as a result set fifo_chan to a correct value and avoid the error?

    Lines 195 to 211 Initialize the channel map and active channel count, but should I change the code there (and to what?) or is that only for initialization as the comments state?

    /*
     * Copyright (c) 2017, NXP
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #define DT_DRV_COMPAT max_max30101
    
    #include <logging/log.h>
    
    #include "max30101.h"
    
    LOG_MODULE_REGISTER(MAX30101, CONFIG_SENSOR_LOG_LEVEL);
    
    static int max30101_sample_fetch(const struct device *dev,
    				 enum sensor_channel chan)
    {
    	struct max30101_data *data = dev->data;
    	const struct max30101_config *config = dev->config;
    	uint8_t buffer[MAX30101_MAX_BYTES_PER_SAMPLE];
    	uint32_t fifo_data;
    	int fifo_chan;
    	int num_bytes;
    	int i;
    
    	/* Read all the active channels for one sample */
    	num_bytes = data->num_channels * MAX30101_BYTES_PER_CHANNEL;
    	if (i2c_burst_read(data->i2c, config->i2c_addr,
    			   MAX30101_REG_FIFO_DATA, buffer, num_bytes)) {
    		LOG_ERR("Could not fetch sample");
    		return -EIO;
    	}
    
    	fifo_chan = 0;
    	for (i = 0; i < num_bytes; i += 3) {
    		/* Each channel is 18-bits */
    		fifo_data = (buffer[i] << 16) | (buffer[i + 1] << 8) |
    			    (buffer[i + 2]);
    		fifo_data &= MAX30101_FIFO_DATA_MASK;
    
    		/* Save the raw data */
    		data->raw[fifo_chan++] = fifo_data;
    	}
    
    	return 0;
    }
    
    static int max30101_channel_get(const struct device *dev,
    				enum sensor_channel chan,
    				struct sensor_value *val)
    {
    	struct max30101_data *data = dev->data;
    	enum max30101_led_channel led_chan;
    	int fifo_chan;
    
    	switch (chan) {
    	case SENSOR_CHAN_RED:
    		led_chan = MAX30101_LED_CHANNEL_RED;
    		break;
    
    	case SENSOR_CHAN_IR:
    		led_chan = MAX30101_LED_CHANNEL_IR;
    		break;
    
    	case SENSOR_CHAN_GREEN:
    		led_chan = MAX30101_LED_CHANNEL_GREEN;
    		break;
    
    	default:
    		LOG_ERR("Unsupported sensor channel");
    		return -ENOTSUP;
    	}
    
    	/* Check if the led channel is active by looking up the associated fifo
    	 * channel. If the fifo channel isn't valid, then the led channel
    	 * isn't active.
    	 */
    	fifo_chan = data->map[led_chan];
    
    // begin what I added
    	LOG_ERR("led_chan: %d. fifo_chan: %d. MAX30101_MAX_NUM_CHANNELS: %d  ", led_chan, fifo_chan, MAX30101_MAX_NUM_CHANNELS);
    // end what I added
    
    	if (fifo_chan >= MAX30101_MAX_NUM_CHANNELS) {
    		LOG_ERR("Inactive sensor channel\n");
    		return -ENOTSUP;
    	}
    
    
    	/* TODO: Scale the raw data to standard units */
    	val->val1 = data->raw[fifo_chan];
    	val->val2 = 0;
    
    	return 0;
    }
    
    static const struct sensor_driver_api max30101_driver_api = {
    	.sample_fetch = max30101_sample_fetch,
    	.channel_get = max30101_channel_get,
    };
    
    static int max30101_init(const struct device *dev)
    {
    	const struct max30101_config *config = dev->config;
    	struct max30101_data *data = dev->data;
    	uint8_t part_id;
    	uint8_t mode_cfg;
    	uint32_t led_chan;
    	int fifo_chan;
    
    	/* Get the I2C device */
    	data->i2c = device_get_binding(config->i2c_label);
    	if (!data->i2c) {
    		LOG_ERR("Could not find I2C device");
    		return -EINVAL;
    	}
    
    	/* Check the part id to make sure this is MAX30101 */
    	if (i2c_reg_read_byte(data->i2c, config->i2c_addr,
    			      MAX30101_REG_PART_ID, &part_id)) {
    		LOG_ERR("Could not get Part ID");
    		return -EIO;
    	}
    	if (part_id != MAX30101_PART_ID) {
    		LOG_ERR("Got Part ID 0x%02x, expected 0x%02x",
    			    part_id, MAX30101_PART_ID);
    		return -EIO;
    	}
    
    	/* Reset the sensor */
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_MODE_CFG,
    			       MAX30101_MODE_CFG_RESET_MASK)) {
    		return -EIO;
    	}
    
    	/* Wait for reset to be cleared */
    	do {
    		if (i2c_reg_read_byte(data->i2c, config->i2c_addr,
    				      MAX30101_REG_MODE_CFG, &mode_cfg)) {
    			LOG_ERR("Could read mode cfg after reset");
    			return -EIO;
    		}
    	} while (mode_cfg & MAX30101_MODE_CFG_RESET_MASK);
    
    	/* Write the FIFO configuration register */
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_FIFO_CFG, config->fifo)) {
    		return -EIO;
    	}
    
    	/* Write the mode configuration register */
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_MODE_CFG, config->mode)) {
    		return -EIO;
    	}
    
    	/* Write the SpO2 configuration register */
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_SPO2_CFG, config->spo2)) {
    		return -EIO;
    	}
    
    	/* Write the LED pulse amplitude registers */
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_LED1_PA, config->led_pa[0])) {
    		return -EIO;
    	}
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_LED2_PA, config->led_pa[1])) {
    		return -EIO;
    	}
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_LED3_PA, config->led_pa[2])) {
    		return -EIO;
    	}
    
    #ifdef CONFIG_MAX30101_MULTI_LED_MODE
    	uint8_t multi_led[2];
    
    	/* Write the multi-LED mode control registers */
    	multi_led[0] = (config->slot[1] << 4) | (config->slot[0]);
    	multi_led[1] = (config->slot[3] << 4) | (config->slot[2]);
    
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_MULTI_LED, multi_led[0])) {
    		return -EIO;
    	}
    	if (i2c_reg_write_byte(data->i2c, config->i2c_addr,
    			       MAX30101_REG_MULTI_LED + 1, multi_led[1])) {
    		return -EIO;
    	}
    #endif
    
    	/* Initialize the channel map and active channel count */
    	data->num_channels = 0U;
    	for (led_chan = 0U; led_chan < MAX30101_MAX_NUM_CHANNELS; led_chan++) {
    		data->map[led_chan] = MAX30101_MAX_NUM_CHANNELS;
    	}
    
    	/* Count the number of active channels and build a map that translates
    	 * the LED channel number (red/ir/green) to the fifo channel number.
    	 */
    	for (fifo_chan = 0; fifo_chan < MAX30101_MAX_NUM_CHANNELS;
    	     fifo_chan++) {
    		led_chan = (config->slot[fifo_chan] & MAX30101_SLOT_LED_MASK)-1;
    		if (led_chan < MAX30101_MAX_NUM_CHANNELS) {
    			data->map[led_chan] = fifo_chan;
    			data->num_channels++;
    		}
    	}
    
    	return 0;
    }
    
    static struct max30101_config max30101_config = {
    	.i2c_label = DT_INST_BUS_LABEL(0),
    	.i2c_addr = DT_INST_REG_ADDR(0),
    	.fifo = (CONFIG_MAX30101_SMP_AVE << MAX30101_FIFO_CFG_SMP_AVE_SHIFT) |
    #ifdef CONFIG_MAX30101_FIFO_ROLLOVER_EN
    		MAX30101_FIFO_CFG_ROLLOVER_EN_MASK |
    #endif
    		(CONFIG_MAX30101_FIFO_A_FULL <<
    		 MAX30101_FIFO_CFG_FIFO_FULL_SHIFT),
    
    #if defined(CONFIG_MAX30101_HEART_RATE_MODE)
    	.mode = MAX30101_MODE_HEART_RATE,
    	.slot[0] = MAX30101_SLOT_RED_LED1_PA,
    	.slot[1] = MAX30101_SLOT_DISABLED,
    	.slot[2] = MAX30101_SLOT_DISABLED,
    	.slot[3] = MAX30101_SLOT_DISABLED,
    #elif defined(CONFIG_MAX30101_SPO2_MODE)
    	.mode = MAX30101_MODE_SPO2,
    	.slot[0] = MAX30101_SLOT_RED_LED1_PA,
    	.slot[1] = MAX30101_SLOT_IR_LED2_PA,
    	.slot[2] = MAX30101_SLOT_DISABLED,
    	.slot[3] = MAX30101_SLOT_DISABLED,
    #else
    	.mode = MAX30101_MODE_MULTI_LED,
    	.slot[0] = CONFIG_MAX30101_SLOT1,
    	.slot[1] = CONFIG_MAX30101_SLOT2,
    	.slot[2] = CONFIG_MAX30101_SLOT3,
    	.slot[3] = CONFIG_MAX30101_SLOT4,
    #endif
    
    	.spo2 = (CONFIG_MAX30101_ADC_RGE << MAX30101_SPO2_ADC_RGE_SHIFT) |
    		(CONFIG_MAX30101_SR << MAX30101_SPO2_SR_SHIFT) |
    		(MAX30101_PW_18BITS << MAX30101_SPO2_PW_SHIFT),
    
    	.led_pa[0] = CONFIG_MAX30101_LED1_PA,
    	.led_pa[1] = CONFIG_MAX30101_LED2_PA,
    	.led_pa[2] = CONFIG_MAX30101_LED3_PA,
    };
    
    static struct max30101_data max30101_data;
    
    DEVICE_DT_INST_DEFINE(0, max30101_init, device_pm_control_nop,
    		    &max30101_data, &max30101_config,
    		    POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
    		    &max30101_driver_api);
    

  • I took a look at the max30101 code and I think I know how to go about this.

    If you take a look at this code snippet: https://github.com/nrfconnect/sdk-zephyr/blob/v2.4.99-ncs2/drivers/sensor/max30101/max30101.c#L232-L237, you will see how the fifo channels are associated with the led channels.

    I think you can think of it like this, if I have understood it correctly:

    .mode = MAX30101_MODE_MULTI_LED,
    .slot[<fifo_chan>] = <led_chan>+1,
    .slot[<fifo_chan>] = <led_chan>+1,
    .slot[<fifo_chan>] = <led_chan>+1,
    .slot[<fifo_chan>] = <led_chan>+1,

    Most of the configs CONFIG_MAX30101_SLOTx will default to 0, which means they are disabled, and that's the reason it fails for you I think.

    So in your prj.conf, in addition to enabling CONFIG_MAX30101_MULTI_LED_MODE, try assigning a value to the configs CONFIG_MAX30101_SLOTx: https://github.com/nrfconnect/sdk-zephyr/blob/v2.4.99-ncs2/drivers/sensor/max30101/Kconfig#L141-L199 

    I don't have a max30101 sensor at hand, and haven't tested this, so I may have overlooked something.

    Best regards,

    Simon

  • Thanks very much Simon. I made the changes, got past the "Inactive Sensor Channel" errors and am now getting sensor readings! I always appreciate seeing your posts here.

    I have to ask, when I build and debug now it always stops at this line 12 of my main.c

    const struct device *dev = device_get_binding(DT_LABEL(DT_INST(0, max_max30101)));

    #include <zephyr.h>
    #include <sys/printk.h>
    #include <drivers/sensor.h>
    #include <stdio.h>
    
    void main(void)
    {
    	struct sensor_value green;
    	struct sensor_value ir;
    	struct sensor_value red;
    
    	const struct device *dev = device_get_binding(DT_LABEL(DT_INST(0, max_max30101)));
    
    	if (dev == NULL) {
    		printf("Could not get max30101 device\n");
    		return;
    	}
    
    	while (1) {
    		sensor_sample_fetch(dev);
    		printf("\n==============================================================================\n");
    		
    		sensor_channel_get(dev, SENSOR_CHAN_GREEN, &green);
    		printf("GREEN=%d\n", green.val1);
    
    		sensor_channel_get(dev, SENSOR_CHAN_IR, &ir);
    		printf("IR=%d\n", ir.val1);
    		
    		sensor_channel_get(dev, SENSOR_CHAN_RED, &red);
    		printf("RED=%d\n", red.val1);
    
    		printf("\n");
    		k_sleep(K_MSEC(5000));
    	}
    }
    

    I can click on the "Continue Execution" green play button and it continues and the rest works as expected, but just wondering if there is a fix for it always stopping there.

    Now that I can get sensor readings I know I need to perform algorithms to get the biometrics (Heart Rate, Blood Oxygen Saturation).   In all of my searching, those algorithms only exist for the Arduino.

    ie:  https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library

    If I am trying to get those readings for the nRF5340, what are the best options?

    Manually port the Arduino C++ code to Zephyr C code for the nRF5340 myself? 

    Do you know of any initiatives that would be working on this kind of porting already?  (I think it would be a fairly high demand common algorithm to work with wearables.)

  • BennyC said:

    I have to ask, when I build and debug now it always stops at this line 12 of my main.c

    const struct device *dev = device_get_binding(DT_LABEL(DT_INST(0, max_max30101)));

    It seems like this is something that SEGGER does automatically at start up, it will find main and stop there. Similar to what Ozone does, as discussed here

    The reason it stops at device_get_binding() instead of the start of main(), is because the code is optimized by default. I confirmed this by turning of optimizations (set CONFIG_DEBUG_OPTIMIZATIONS=y in prj.conf) and saw that it stopped at main().

    BennyC said:

    Now that I can get sensor readings I know I need to perform algorithms to get the biometrics (Heart Rate, Blood Oxygen Saturation).   In all of my searching, those algorithms only exist for the Arduino.

    ie:  https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library

    If I am trying to get those readings for the nRF5340, what are the best options?

    Manually port the Arduino C++ code to Zephyr C code for the nRF5340 myself? 

    This DevZone ticket may be helpful regarding this question: https://devzone.nordicsemi.com/f/nordic-q-a/57362/how-to-add-c-file-to-nrf9160-project-in-ses. It seems like C++ is not fully supported in SES. We do have more samples using C++ now, like the edge_impulse sample, which uses the library  ei_wrapper.cpp and in addition we support thread in NCS, which uses C++, so it seems like the situation has changed. I will ask internally to get more information about this.

Reply
  • BennyC said:

    I have to ask, when I build and debug now it always stops at this line 12 of my main.c

    const struct device *dev = device_get_binding(DT_LABEL(DT_INST(0, max_max30101)));

    It seems like this is something that SEGGER does automatically at start up, it will find main and stop there. Similar to what Ozone does, as discussed here

    The reason it stops at device_get_binding() instead of the start of main(), is because the code is optimized by default. I confirmed this by turning of optimizations (set CONFIG_DEBUG_OPTIMIZATIONS=y in prj.conf) and saw that it stopped at main().

    BennyC said:

    Now that I can get sensor readings I know I need to perform algorithms to get the biometrics (Heart Rate, Blood Oxygen Saturation).   In all of my searching, those algorithms only exist for the Arduino.

    ie:  https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library

    If I am trying to get those readings for the nRF5340, what are the best options?

    Manually port the Arduino C++ code to Zephyr C code for the nRF5340 myself? 

    This DevZone ticket may be helpful regarding this question: https://devzone.nordicsemi.com/f/nordic-q-a/57362/how-to-add-c-file-to-nrf9160-project-in-ses. It seems like C++ is not fully supported in SES. We do have more samples using C++ now, like the edge_impulse sample, which uses the library  ei_wrapper.cpp and in addition we support thread in NCS, which uses C++, so it seems like the situation has changed. I will ask internally to get more information about this.

Children
Related