06-27-2020 07:28 PM
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, ®_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.
Solved! Go to Solution.
06-29-2020 06:06 AM
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.
06-30-2020 08:24 PM
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.
07-01-2020 05:39 AM
Hi,
Congratulations!
Would you please select Accept as Solution to close this ticket?
Best regards.