BHI260AP with AUX sensors interfacing - STM32

Hello Bosh Community,

I have a custom board embedding a Dual-Core STM32H7, with a bunch of sensors, including the BHI260AP and its auxiliary sensors (BMM150, BME688 and BMP390).

I'm struggling a bit after the initialization step. Here's the code snippet that i have :

/*
 * sensor_fusion_cluster.c
 *
 *  Created on: Jun 2, 2025
 *      Author: Yassine DEHHANI
 */

#include "drivers_h/bhi260_sensor_cluster/bhy2.h"
#include "drivers_h/bhi260_sensor_cluster/bhy2_parse.h"
#include "drivers_h/bhi260_sensor_cluster/common.h"
#include "sensors_hubs/sensor_fusion_cluster.h"
#include <stdio.h>
#include <string.h>
#include <math.h>

#define WORK_BUFFER_SIZE 2048
#define FIFO_BUFFER_SIZE 2048
#define SAMPLE_RATE_HZ   100.0f
#define REPORT_LATENCY   0

#define QUAT_SENSOR_ID   BHY2_SENSOR_ID_GAMERV  // Changed from GAMERV to RV_WU
#define RAD2DEG (180.0f / M_PI)

/*#define UPLOAD_FIRMWARE_TO_FLASH */

#ifdef UPLOAD_FIRMWARE_TO_FLASH
#include "drivers_h/bhi260_sensor_cluster/firmware/bhi260ap/BHI260AP_aux_BMM150_BMP390_BME688-flash.fw.h"
#else
#include "drivers_h/bhi260_sensor_cluster/firmware/bhi260ap/BHI260AP_aux_BMM150_BMP390_BME688.fw.h"
#endif

static struct bhy2_dev bhi260;
enum bhy2_intf intf;

// Align buffer to avoid possible DMA or hard fault issues
__attribute__((aligned(32))) static uint8_t work_buffer[WORK_BUFFER_SIZE];

static void parse_quaternion(const struct bhy2_fifo_parse_data_info *callback_info, void *callback_ref)
{
    (void)callback_ref;

    // Defensive check
    if (callback_info == NULL || callback_info->data_ptr == NULL)
    {
        printf("[ERROR] parse_quaternion: Null callback info or data_ptr\r\n");
        return;
    }

    if (callback_info->data_size != 11)
    {
        printf("[WARN] Invalid quaternion size: %u\r\n", callback_info->data_size);
        return;
    }

    struct bhy2_data_quaternion data;
    bhy2_parse_quaternion(callback_info->data_ptr, &data);

    uint64_t timestamp = *callback_info->time_stamp;
    timestamp = timestamp * 15625;
    uint32_t s = (uint32_t)(timestamp / UINT64_C(1000000000));
    uint32_t ns = (uint32_t)(timestamp - (s * UINT64_C(1000000000)));

    // Convert raw values to float
    float qx = data.x / 16384.0f;
    float qy = data.y / 16384.0f;
    float qz = data.z / 16384.0f;
    float qw = data.w / 16384.0f;

    // Extract valid accuracy (low 2 bits)
    uint8_t accuracy = data.accuracy & 0x03;

//    float roll  = atan2f(2.0f * (qw * qx + qy * qz), 1.0f - 2.0f * (qx * qx + qy * qy)) * RAD2DEG;
//    float pitch = asinf(2.0f * (qw * qy - qz * qx)) * RAD2DEG;
//    float yaw   = atan2f(2.0f * (qw * qz + qx * qy), 1.0f - 2.0f * (qy * qy + qz * qz)) * RAD2DEG;

    // Optional: convert timestamp to seconds + nanoseconds already done as `s` and `ns`

    // Now you can break here and inspect qx, qy, qz, qw, accuracy
    // Or later print if needed:
    printf("SID: %u; T: %lu.%09lu; x: %f, y: %f, z: %f, w: %f; acc: %u\r\n",
           callback_info->sensor_id,
           s,
           ns,
           qx,
           qy,
           qz,
           qw,
           accuracy);
}

static void parse_meta_event(const struct bhy2_fifo_parse_data_info *callback_info, void *callback_ref)
{
    (void)callback_ref;

    // Defensive check
    if (callback_info == NULL || callback_info->data_ptr == NULL)
    {
        printf("[ERROR] parse_meta_event: Null callback info or data_ptr\r\n");
        return;
    }

    if (callback_info->data_size < 3)
    {
        printf("[WARN] Invalid meta event size: %u\r\n", callback_info->data_size);
        return;
    }

    uint8_t meta_event_type = callback_info->data_ptr[0];
    uint8_t byte1 = callback_info->data_ptr[1];
    uint8_t byte2 = callback_info->data_ptr[2];
    char *event_text = (callback_info->sensor_id == BHY2_SYS_ID_META_EVENT) ? "[META EVENT]" : "[META EVENT WAKE UP]";

    switch (meta_event_type)
    {
        case BHY2_META_EVENT_FLUSH_COMPLETE:
            printf("%s Flush complete for sensor id %u\r\n", event_text, byte1);
            break;
        case BHY2_META_EVENT_SAMPLE_RATE_CHANGED:
            printf("%s Sample rate changed for sensor id %u\r\n", event_text, byte1);
            break;
        case BHY2_META_EVENT_POWER_MODE_CHANGED:
            printf("%s Power mode changed for sensor id %u\r\n", event_text, byte1);
            break;
        case BHY2_META_EVENT_ALGORITHM_EVENTS:
            printf("%s Algorithm event\r\n", event_text);
            break;
        case BHY2_META_EVENT_SENSOR_STATUS:
            printf("%s Accuracy for sensor id %u changed to %u\r\n", event_text, byte1, byte2);
            break;
        case BHY2_META_EVENT_BSX_DO_STEPS_MAIN:
            printf("%s BSX event (do steps main)\r\n", event_text);
            break;
        case BHY2_META_EVENT_BSX_DO_STEPS_CALIB:
            printf("%s BSX event (do steps calib)\r\n", event_text);
            break;
        case BHY2_META_EVENT_BSX_GET_OUTPUT_SIGNAL:
            printf("%s BSX event (get output signal)\r\n", event_text);
            break;
        case BHY2_META_EVENT_SENSOR_ERROR:
            printf("%s Sensor id %u reported error 0x%02X\r\n", event_text, byte1, byte2);
            break;
        case BHY2_META_EVENT_FIFO_OVERFLOW:
            printf("%s FIFO overflow\r\n", event_text);
            break;
        case BHY2_META_EVENT_DYNAMIC_RANGE_CHANGED:
            printf("%s Dynamic range changed for sensor id %u\r\n", event_text, byte1);
            break;
        case BHY2_META_EVENT_FIFO_WATERMARK:
            printf("%s FIFO watermark reached\r\n", event_text);
            break;
        case BHY2_META_EVENT_INITIALIZED:
            printf("%s Firmware initialized. Firmware version %u\r\n", event_text, ((uint16_t)byte2 << 8) | byte1);
            break;
        case BHY2_META_TRANSFER_CAUSE:
            printf("%s Transfer cause for sensor id %u\r\n", event_text, byte1);
            break;
        case BHY2_META_EVENT_SENSOR_FRAMEWORK:
            printf("%s Sensor framework event for sensor id %u\r\n", event_text, byte1);
            break;
        case BHY2_META_EVENT_RESET:
            printf("%s Reset event\r\n", event_text);
            break;
        case BHY2_META_EVENT_SPACER:
            break;
        default:
            printf("%s Unknown meta event with id: %u\r\n", event_text, meta_event_type);
            break;
    }
}

int8_t SensorFusionCluster_Init(void)
{
    int8_t rslt;
    uint8_t product_id = 0;
    uint8_t boot_status = 0;
    uint8_t hintr_ctrl = BHY2_ICTL_DISABLE_STATUS_FIFO | BHY2_ICTL_DISABLE_DEBUG;

    bhi260_reset();

    rslt = bhy2_init(BHY2_SPI_INTERFACE, bhy2_spi_read, bhy2_spi_write,
                     bhy2_delay_us, WORK_BUFFER_SIZE, NULL, &bhi260);
    if (rslt != BHY2_OK) return rslt;

    rslt = bhy2_soft_reset(&bhi260);
    if (rslt != BHY2_OK) return rslt;

    rslt = bhy2_get_product_id(&product_id, &bhi260);
    if (rslt != BHY2_OK || product_id != BHY2_PRODUCT_ID) return -1;

    rslt = bhy2_set_host_interrupt_ctrl(hintr_ctrl, &bhi260);
    if (rslt != BHY2_OK) return rslt;

    rslt = bhy2_get_boot_status(&boot_status, &bhi260);
    if ((rslt != BHY2_OK) || !(boot_status & BHY2_BST_HOST_INTERFACE_READY)) return -2;

    rslt = bhy2_upload_firmware_to_ram(bhy2_firmware_image, sizeof(bhy2_firmware_image), &bhi260);
    if (rslt != BHY2_OK) return rslt;

    rslt = bhy2_boot_from_ram(&bhi260);
    if (rslt != BHY2_OK) return rslt;

    rslt = bhy2_register_fifo_parse_callback(BHY2_SYS_ID_META_EVENT, parse_meta_event, NULL, &bhi260);
    if (rslt != BHY2_OK) return rslt;
    rslt = bhy2_register_fifo_parse_callback(BHY2_SYS_ID_META_EVENT_WU, parse_meta_event, NULL, &bhi260);
    if (rslt != BHY2_OK) return rslt;
    rslt = bhy2_register_fifo_parse_callback(QUAT_SENSOR_ID, parse_quaternion, NULL, &bhi260);
    if (rslt != BHY2_OK) return rslt;

    rslt = bhy2_update_virtual_sensor_list(&bhi260);
    if (rslt != BHY2_OK) return rslt;

    rslt = bhy2_set_virt_sensor_cfg(QUAT_SENSOR_ID, SAMPLE_RATE_HZ, REPORT_LATENCY, &bhi260);
    if (rslt != BHY2_OK) return rslt;


    return BHY2_OK;
}

void SensorFusionCluster_Process(void)
{
    int8_t rslt = bhy2_get_and_process_fifo(work_buffer, WORK_BUFFER_SIZE, &bhi260);
    if (rslt != BHY2_OK)
    {
        printf("FIFO process error: %d\r\n", rslt);
    }
}

And on another file i execute everything as follow (just for test purposes) :

      res = SensorFusionCluster_Init();
      if(res != 0){
    	  RGB_LED_SetPredefinedColor(RED); // Sets the RGBW Led to OK
    	  return STATUS_ERROR;
      }

      while (1)
      {
          SensorFusionCluster_Process();
          HAL_Delay(300); // Polling every 100 ms
      }

The initialization is executed successfully without errors, but when I go through the SensorFusionCluster_Process() function, I get strange data :

At the beginning we get a BHY2_META_EVENT_SPACER (i assume it's ok for the first loop):


After that i get a BHY2_META_EVENT_INITIALIZED (also good i assume) :

Those two events occurs two times.

After that i get a BHY2_META_EVENT_FIFO_OVERFLOW (I'm not sure this is good) :

And once i get this event, the parse_quaternion() function is called, giving me those results :

Those values does not change that much, and i don't understand why. Here's the result after some loops :


Lastly, here's my common.c file where i have the spi read/write and useful functions :

#include "drivers_h/bhi260_sensor_cluster/common.h"
#include "main.h"
#include "drivers_h/bhi260_sensor_cluster/bhy2.h"

#define BHY2_SPI_TIMEOUT     1000
#define BHY2_SPI_MAX_LENGTH 256

static void delay_us(uint32_t us)
{
    uint32_t delay = (SystemCoreClock / 1000000U / 2U) * us;
    while (delay--) __NOP();
}

void bhy2_delay_us(uint32_t us, void *private_data)
{
    (void)private_data;
    delay_us(us);
}


int8_t bhy2_spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr)
{
    (void)intf_ptr;

    if (reg_data == NULL || length == 0 || length > BHY2_SPI_MAX_LENGTH)
        return BHY2_E_IO;

    uint8_t tx_buf[1] = { reg_addr | 0x80 };  // Set MSB for read
    static uint8_t rx_buf[BHY2_SPI_MAX_LENGTH + 1];  // Static prevents stack overflow

    HAL_GPIO_WritePin(BHI260_CS_PORT, BHI260_CS_PIN, GPIO_PIN_RESET);

    HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(&hspi4, tx_buf, rx_buf, length + 1, BHY2_SPI_TIMEOUT);

    HAL_GPIO_WritePin(BHI260_CS_PORT, BHI260_CS_PIN, GPIO_PIN_SET);

    if (status == HAL_OK)
    {
        memcpy(reg_data, &rx_buf[1], length);  // Skip dummy byte
        return BHY2_OK;
    }
    return BHY2_E_IO;
}

int8_t bhy2_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr)
{
    (void)intf_ptr;

    if (reg_data == NULL || length == 0 || length > BHY2_SPI_MAX_LENGTH)
        return BHY2_E_IO;

    static uint8_t tx_buf[BHY2_SPI_MAX_LENGTH + 1];
    tx_buf[0] = reg_addr & 0x7F;
    memcpy(&tx_buf[1], reg_data, length);

    HAL_GPIO_WritePin(BHI260_CS_PORT, BHI260_CS_PIN, GPIO_PIN_RESET);

    HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi4, tx_buf, length + 1, BHY2_SPI_TIMEOUT);

    HAL_GPIO_WritePin(BHI260_CS_PORT, BHI260_CS_PIN, GPIO_PIN_SET);

    return (status == HAL_OK) ? BHY2_OK : BHY2_E_IO;
}

void bhi260_reset(void)
{
    HAL_GPIO_WritePin(BHI260_RST_PORT, BHI260_RST_PIN, GPIO_PIN_RESET);
    HAL_Delay(50);
    HAL_GPIO_WritePin(BHI260_RST_PORT, BHI260_RST_PIN, GPIO_PIN_SET);
    HAL_Delay(100); // Allow time to boot
}

bool get_interrupt_status(void)
{
    return (HAL_GPIO_ReadPin(BHI260_INT_PORT, BHI260_INT_PIN) == GPIO_PIN_SET);
}

I hope I showed the complete picture about my issue.

Can someone help me debug this? It's really important, thanks in advance!

6 replies