Bosch Sensortec Community

    cancel
    Showing results for 
    Search instead for 
    Did you mean: 
    SOLVED

    BMX160 on Raspberry Pi

    BMX160 on Raspberry Pi

    honeybee
    New Poster

    Hello.

    I have run into difficulty using the BMI160 Bosch API in the context of a BMX160 connected to a Raspberry Pi Zero (running Raspbian) over I2C, and would be grateful for any help with this. Specifically, I have the following problems:

    1. The acc & gyro updates seem to be very rare, definitely not 100 Hz, and the data I get is mostly garbage (just zeroes, or values like 32768).
    2. If I specify -DUSE_MAG to enable the magnetometer code, the call to bmm150_init fails. I have effectively copied the github example on reading data from the auxiliary sensor, so I really don't know what I'm doing wrong here.

    I'm somewhat skeptical of my implementation of the user_i2c_read/write functions; perhaps my troubles are related to these. For what it's worth I have a little confidence in them, since bmi160_init() seems to work (i.e. can read the chip id, which I have modified to correspond to that of BMX160).

    Code:

    #include <sys/ioctl.h>
    #include <time.h>

    #include <wiringPiI2C.h>

    #include <thread>
    extern "C" {
    #include <linux/i2c-dev.h>
    #include <i2c/smbus.h>
    }

    #include "bmi160.h"
    #define BMM150_USE_FLOATING_POINT
    #include "bmm150.h"

    extern Bmx160Subscriber *ugly_hack_bmx160subscriber;

    class Bmx160Subscriber
    {
    public:
    Bmx160Subscriber()
    {
    ugly_hack_bmx160subscriber = this;
    }

    void start()
    {
    m_fd = wiringPiI2CSetup(BMI160_I2C_ADDR);
    setup_sensor();
    // start worker nnnnnn
    m_active = true;
    m_proc_thread = std::thread(&Bmx160Subscriber::process, this);
    }

    void stop()
    {
    m_active = false;
    }


    private:

    /** Initialize sensor, following https://github.com/BoschSensortec/BMI160_driver
    */
    void setup_sensor()
    {

    int8_t rslt;

    m_bmi.id = BMI160_I2C_ADDR;
    m_bmi.interface = BMI160_I2C_INTF;
    m_bmi.read = i2c_read3;
    m_bmi.write = i2c_write3b;
    m_bmi.delay_ms = user_delay_ms;
    rslt = bmi160_init(&m_bmi);
    handle_potential_error(rslt, "bmi160_init");

    /// Configure auxiliary sensor
    /* Configure the BMM150 device structure by mapping user_aux_read and user_aux_write */
    m_bmm.read = user_aux_read;
    m_bmm.write = user_aux_write;
    m_bmm.dev_id = BMM150_DEFAULT_I2C_ADDRESS;
    /* Ensure that sensor.aux_cfg.aux_i2c_addr = bmm150.id for proper sensor operation */
    m_bmm.delay_ms = user_delay_ms;
    m_bmm.intf = BMM150_I2C_INTF;

    /* Configure device structure for auxiliary sensor parameter */
    m_bmi.aux_cfg.aux_sensor_enable = BMI160_ENABLE; // auxiliary sensor enable
    m_bmi.aux_cfg.aux_i2c_addr = m_bmm.dev_id; // auxiliary sensor address
    m_bmi.aux_cfg.manual_enable = BMI160_ENABLE; // setup mode enable
    m_bmi.aux_cfg.aux_rd_burst_len = BMI160_AUX_READ_LEN_3 ;// burst read; 8 bytes
    /* Initialize the auxiliary sensor interface */
    rslt = bmi160_aux_init(&m_bmi);
    handle_potential_error(rslt, "bmi160_aux_init");
    /* Auxiliary sensor is enabled and can be accessed from this point */

    /* Configure the desired settings in auxiliary BMM150 sensor
    * using the bmm150 APIs */
    #ifdef WITH_MAG
    /* Initialising the bmm150 sensor */
    rslt = bmm150_init(&m_bmm);
    handle_potential_error(rslt, "bmm150_init");
    #endif
    /* After the above function call, accel and gyro parameters in the device structure
    a re set with default values, found in the datasheet of the sensor */

    m_bmi.accel_cfg.odr = BMI160_ACCEL_ODR_100HZ; // Output data rate
    m_bmi.accel_cfg.range = BMI160_ACCEL_RANGE_2G; // Range
    m_bmi.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; // Bandwidth
    m_bmi.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; // Power mode

    m_bmi.gyro_cfg.odr = BMI160_GYRO_ODR_100HZ; // Output data rate
    m_bmi.gyro_cfg.range = BMI160_GYRO_RANGE_2000_DPS; // Range
    m_bmi.gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE; // Bandwidth
    m_bmi.gyro_cfg.power = BMI160_GYRO_NORMAL_MODE; // Power mode
    rslt = bmi160_set_sens_conf(&m_bmi); // Set configuration
    handle_potential_error(rslt, "bmi160_set_sens_conf");

    /// Configure interrupts
    struct bmi160_int_settg int_config;
    int_config.int_channel = BMI160_INT_CHANNEL_1;// select interrupt channel/pin 1
    int_config.int_type = BMI160_ACC_GYRO_DATA_RDY_INT;// Choose DATA_READY interrupt
    /* Select the interrupt channel/pin settings */
    int_config.int_pin_settg.output_en = BMI160_ENABLE;// Enabling interrupt pins to act as output pin
    int_config.int_pin_settg.output_mode = BMI160_DISABLE;// Choosing push-pull mode for interrupt pin
    int_config.int_pin_settg.output_type = BMI160_ENABLE ;// Choosing active low output
    int_config.int_pin_settg.edge_ctrl = BMI160_ENABLE;// Choosing edge triggered output
    int_config.int_pin_settg.input_en = BMI160_DISABLE;// Disabling interrupt pin to act as input
    int_config.int_pin_settg.latch_dur = BMI160_LATCH_DUR_NONE;// non-latched output
    /* Set the interrupt */
    rslt = bmi160_set_int_config(&int_config, &m_bmi); /* sensor is an instance of the structure bmi160_dev */
    handle_potential_error(rslt, "bmi160_set_int_config");

    #ifdef WITH_MAG
    /* Set the power mode and preset mode to enable Mag data sampling */
    m_bmm.settings.pwr_mode = BMM150_NORMAL_MODE;
    rslt = bmm150_set_op_mode(&m_bmm);
    handle_potential_error(rslt, "bmm150_set_op_mode");

    m_bmm.settings.preset_mode= BMM150_PRESETMODE_LOWPOWER;
    rslt = bmm150_set_presetmode(&m_bmm);
    handle_potential_error(rslt, "bmm150_set_presetmode");

    /* In BMM150 Mag data starts from register address 0x42 */
    uint8_t aux_addr = BMM150_DATA_X_LSB;
    /* Buffer to store the Mag data from 0x42 to 0x48 */
    uint8_t mag_data[8] = {0};

    uint8_t index;

    /* Configure the Auxiliary sensor either in auto/manual modes and set the
    p olling frequency for the Auxiliary interface */
    m_bmi.aux_cfg.aux_odr = 8; /* Represents polling rate in 100 Hz*/
    rslt = bmi160_config_aux_mode(&m_bmi);
    handle_potential_error(rslt, "bmi160_config_aux_mode");
    /* Set the auxiliary sensor to auto mode */
    rslt = bmi160_set_aux_auto_mode(&aux_addr, &m_bmi);
    handle_potential_error(rslt, "bmi160_set_aux_auto_mode");
    #endif
    }


    static int8_t i2c_read3(uint8_t /*dev_addr*/, uint8_t reg_addr, uint8_t *data, uint16_t len)
    {
    write(ugly_hack_bmx160subscriber->m_fd, &reg_addr, 1);
    read(ugly_hack_bmx160subscriber->m_fd, data, len);
    return 0;
    }

    static int8_t i2c_write3b(uint8_t /*dev_addr*/, uint8_t reg_addr, uint8_t *data, uint16_t len)
    {
    uint8_t buff[len+1];
    buff[0] = reg_addr;
    for (size_t i=0; i< len; ++i) {
    buff[i+1] = data[i];
    }
    write(ugly_hack_bmx160subscriber->m_fd,buff, len+1);
    return 0;
    }


    static void user_delay_ms(uint32_t period)
    {
    struct timespec tv;
    tv.tv_sec = 0;
    tv.tv_nsec = 1000000*period;
    nanosleep(&tv, nullptr);
    }

    static int8_t user_aux_read(uint8_t /*id*/, uint8_t reg_addr, uint8_t *aux_data, uint16_t len)
    {
    int8_t rslt = bmi160_aux_read(reg_addr, aux_data, len, &ugly_hack_bmx160subscriber->m_bmi);

    return rslt;
    }

    static int8_t user_aux_write(uint8_t /*id*/, uint8_t reg_addr, uint8_t *aux_data, uint16_t len)
    {
    int8_t rslt = bmi160_aux_write(reg_addr, aux_data, len, &ugly_hack_bmx160subscriber->m_bmi);

    return rslt;
    }

    #endif
    /** Helper function to check return values from BMI160 library
    */
    void handle_potential_error(int8_t rslt, const std::string& err)
    {
    if (rslt != BMI160_OK) {
    BLT(error) << err;
    throw std::runtime_error(err);
    }
    }
    /** Worker thread that reads data from node and populates buffer
    */
    void process()
    {
    while (m_active) {
    union bmi160_int_status interrupt;
    enum bmi160_int_status_sel int_status_sel;

    /* Interrupt status selection to read all interrupts */
    int_status_sel = BMI160_INT_STATUS_ALL;
    int rslt = bmi160_get_int_status(int_status_sel, &interrupt, &m_bmi);
    handle_potential_error(rslt, "bmi160_get_int_status");
    if (interrupt.bit.drdy) {
    std::cout << "data_ready interrupt";
    struct bmi160_sensor_data accel;
    struct bmi160_sensor_data gyro;
    int8_t rslt = bmi160_get_sensor_data((BMI160_ACCEL_SEL | BMI160_GYRO_SEL), &accel, &gyro, &m_bmi);
    handle_potential_error(rslt, "bmi160_get_sensor");
    std::cout << "accelerometer data: "
    << " x:" << accel.x
    << " y:" << accel.y
    << " z:" << accel.z;
    std::cout << "gyro data: "
    << " x:" << gyro.x
    << " y:" << gyro.y
    << " z:" << gyro.z;

    #ifdef WITH_MAG
    ///* Reading data from BMI160 data registers */
    uint8_t mag_data[8] = {0};
    rslt = bmi160_read_aux_data_auto_mode(mag_data, &m_bmi);
    /* Compensating the raw mag data available from the BMM150 API */
    rslt = bmm150_aux_mag_data(mag_data, &m_bmm);

    std::cout << "compensated magnetometer data: "
    << " x:" << m_bmm.data.x
    << " y:" << m_bmm.data.y
    << " z:" << m_bmm.data.z;
    #endif
    }
    user_delay_ms(1);
    }
    }

    /// Thread object for worker thread
    std::thread m_proc_thread;
    /// Bool to control starting/stopping worker thread
    std::atomic<bool> m_active;
    /// File descriptor for IMU
    int m_fd;
    /// Sensor structure for BMI160
    struct bmi160_dev m_bmi;
    /// Sensor structure for BMM150
    struct bmm150_dev m_bmm;

    };

     

    I'll also include in my post the output of the following commands:

    pi@raspberrypi:~ $ sudo i2cdetect -F -y 1
    Functionalities implemented by /dev/i2c-1:
    I2C yes
    SMBus Quick Command yes
    SMBus Send Byte yes
    SMBus Receive Byte yes
    SMBus Write Byte yes
    SMBus Read Byte yes
    SMBus Write Word yes
    SMBus Read Word yes
    SMBus Process Call yes
    SMBus Block Write yes
    SMBus Block Read no
    SMBus Block Process Call no
    SMBus PEC yes
    I2C Block Write yes
    I2C Block Read yes

    pi@raspberrypi:~ $ sudo i2cdetect -y 1
    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
    70: -- -- -- -- -- -- -- --

    I have code that uses the wiringPiI2CReadReg8/16 functions to read acc/gyro data directly from the register, which appears to work fine, so I think the issue is with my use of the BMI160/BMM150 libraries, not a hardware one.

    3 REPLIES 3

    fish
    Community Moderator
    Community Moderator

    Hi,

    If you could read chip id and acc/gyro data register successful, means the sensor hardware is ok.

    Using wiringPiI2CReadReg8/16 function, acc/gyro odr 100hz, the data still 0 or 32768? Maybe you need make sure the configuration is right, or say the i2c write success. If you read the bmi160_get_sensor_data() function in API code, just read acc/gyro data register.

    Regarding the mag sensor data, recommend to read example code https://github.com/BoschSensortec/BMI160_driver/wiki/How-to-use-an-auxiliary-sensor-or-magnetometer-....

    Best regards.

    Thank you for your reply. I figured out that my problems were due to something entirely unrelated -- there was another device on the I2C bus that was misconfigured into master mode and was disrupting communications with the BMX160. I have fixed this problem now.

    fish
    Community Moderator
    Community Moderator

    Hi,

    Congratulations!

    Would you please select Accept as Solution to close this ticket?

    Best regards.

    Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist