Github Repo | C Header | C source | JS source |
---|---|---|---|
mongoose-os-libs/imu | mgos_imu.h |
This library provides a simple API that describes inertial measurement units. It implements various popular I2C and SPI IMUs behind that API. Callers of the library can rely on the API returning consistently typed data regardless of the choice of sensor.
This library provides an API to expose various implementations of gyroscopes, accelerometers, and magnetometers. It offers a suitable abstraction that presents the sensor data in a consistent way, so application authors need not worry about the implementation details of the sensors.
Users create a struct mgos_imu
object, to which they add sensors (gyroscope,
acellerometer and magnetometer) either via I2C or SPI. Some chips have all
three sensor types, some have only two (typically accelerometer and gyroscope)
and some have only one type. After successfully adding the sensors to the
mgos_imu
object, reads can be performed, mostly by performing
mgos_imu_*_get()
calls.
All implementations offer the calls described below in the IMU API
.
Some implementations offer chip-specific addendums, mostly setting sensitivity
and range options.
struct mgos_imu *mgos_imu_create()
-- This call creates a new (opaque) object
which represents the IMU device. Upon success, a pointer to the object will be
returned. If the creation fails, NULL is returned.
void mgos_imu_destroy()
-- This cleans up all resources associated with with
the IMU device. The caller passes a pointer to the object pointer. If any
sensors are attached (gyroscope, accelerometer, magnetometer), they will be
destroyed in turn using mgos_imu_*_destroy()
calls.
bool mgos_imu_read()
-- This call schedules all sensors attached to the IMU
to be read from. It is not generally necessary to call this method directly,
as the mgos_imu_*_get()
calls internally schedule reads from the sensors as
well.
bool mgos_imu_accelerometer_present()
-- This returns true
if the IMU has an
attached accelerometer sensor, or false
otherwise.
bool mgos_imu_gyroscope_present()
-- This returns true
if the IMU has an
attached gyroscope sensor, or false
otherwise.
bool mgos_imu_magnetometer_present()
-- This returns true
if the IMU has an
attached magnetometer, or false
otherwise.
For each of accelerometer, gyroscope and magnetometer, the following primitives exist:
bool mgos_imu_*_create_i2c()
-- This attaches a sensor to the IMU
based on the type
enum given, using the i2c
bus and specified i2caddr
address. The function will return true
upon success and false
in case
either detection of the sensor, or creation of it failed.
bool mgos_imu_*_create_spi()
-- This attaches a sensor to the IMU
based on the type
enum given, using the spi
bus and a specified cable select
in cs_gpio
. The function will return true
upon success and false
in case
either detection of the sensor or creation of it failed.
bool mgos_imu_*_destroy()
-- This detaches a sensor from the IMU if it exists.
It takes care of cleaning up all resources associated with the sensor, and
detaches it from the i2c
or spi
bus. The higher level mgos_imu_destroy()
call uses these lower level calls to clean up sensors as well.
bool mgos_imu_*_get()
-- This call returns sensor data after
polling the sensor for the data. It returns true
if the read succeeded, in
which case the floats pointed to by x
, y
and z
are filled in. If an
error occurred, false
is returned and the contents of x
, y
and z
are
unmodified. Note the units of the return values:
Gauss
.G
.degrees per second
.const char *mgos_imu_*_get_name()
-- This returns a symbolic name of the
attached sensor, which is guaranteed to be less than or equal to 10 characters
and always exist. If there is no sensor of this type attached, VOID
will be
returned. If the sensor is not known, UNKNOWN
will be returned. Otherwise,
the chip manufacturer / type will be returned, for example MPU9250
or
ADXL345
or MAG3110
.
bool mgos_imu_*_set_orientation()
and bool mgos_imu_*_get_orientation()
--
Often times a 9DOF sensor will have multiple chips, whose axes do not line up
correctly. Even within a single chip the accelerometer, gyroscope and
magnetometer axes might not line up (for an example of this, see MPU9250
chapter 9.1).
To ensure that the x
, y
, and z
axes on all sensors are pointed in the same
direction, we can set the orientation on the gyroscope and magnetometer.
See mgos_imu.h
for more details and an example of how to do this.
This is a fairly straight forward process, taking the ADXL345
accelerometer
as example:
src/mgos_imu_adxl345.[ch]
. The
header file exposes all of the #define
's for registers and constants.detect
, create
, destroy
and read
function.bool mgos_imu_adxl345_detect()
-- this function optionally attempts
to detect the chip, often times by reading a register or set of
registers which uniquely identifies it. Not all chips can actually be
detected, so it's OK to not define this function at all.bool mgos_imu_adxl345_create()
-- this function has to perform the
initialization of the chip, typically by setting the right registers
and possibly creating a driver-specific memory structure (for, say,
coefficients or some such). If used, that memory structure is attached
to the user_data
pointer, and if so, the implementation of the
_destroy()
function must clean up and free this memory again.bool mgos_imu_adxl345_read()
-- this function performs the chip
specific read functionality. This will be called whenever the user asks
for data, either by calling mgos_imu_read()
or by calling
mgos_imu_*_get()
.bool mgos_imu_adxl345_destroy()
-- this function deinitializes the
chip, and optionally clears and frees the driver-specific memory
structure in user_data
. Not all chips need additional memory
structures or deinitialization code, in which case it's OK to not
define this function at all.detect
, create
, destroy
and read
functions in the
source code file src/mgos_imu_adxl345.c
.enum mgos_imu_*_type
in include/mgos_imu.h
.
In the example case of adxl345 (which is an accelerometer), add it to
enum mgos_imu_acc_type
.mgos_imu_*_get_name()
so that
callers can resolve the sensor to a human readable format. Make sure that
the string name is not greater than 10 characters.switch()
in mgos_imu_*_create_i2c()
(or _spi()
)
functions.It is important to note a few implementation rules when adding drivers:
mgos_imu
and mgos_imu_*
members) nor the mgos_imu_*_get()
functions.m/s/s
, deg/s
and uT
).Here's an example, for the magnetometer chip AK8975
, showing a set of commits
for each of the steps above (and honoring the driver implementation rules).
src/mgos_imu_ak8975.[ch]
commitenum mgos_imu_mag_type
in include/mgos_imu.h
commitmgos_imu_magnetometer_get_name()
in src/mgos_imu_magnetometer.c
commitmgos_imu_magnetometer_create_i2c()
in src/mgos_imu_magnetometer.c
commitAn example program that creates an IMU of type MPU9250
(which has an
accelerometer, a gyroscope and a magnetometer all in one tiny package):
#include "mgos.h"
#include "mgos_i2c.h"
#include "mgos_imu.h"
static void imu_cb(void *user_data) {
struct mgos_imu *imu = (struct mgos_imu *)user_data;
float ax, ay, az;
float gx, gy, gz;
float mx, my, mz;
if (!imu) return;
if (mgos_imu_accelerometer_get(imu, &ax, &ay, &az))
LOG(LL_INFO, ("type=%-10s Accel X=%.2f Y=%.2f Z=%.2f",
mgos_imu_accelerometer_get_name(imu), ax, ay, az));
if (mgos_imu_gyroscope_get(imu, &gx, &gy, &gz))
LOG(LL_INFO, ("type=%-10s Gyro X=%.2f Y=%.2f Z=%.2f",
mgos_imu_gyroscope_get_name(imu), gx, gy, gz));
if (mgos_imu_magnetometer_get(imu, &mx, &my, &mz))
LOG(LL_INFO, ("type=%-10s Mag X=%.2f Y=%.2f Z=%.2f",
mgos_imu_magnetometer_get_name(imu), mx, my, mz));
}
enum mgos_app_init_result mgos_app_init(void) {
struct mgos_i2c *i2c = mgos_i2c_get_global();
struct mgos_imu *imu = mgos_imu_create();
struct mgos_imu_acc_opts acc_opts;
struct mgos_imu_gyro_opts gyro_opts;
struct mgos_imu_mag_opts mag_opts;
if (!i2c) {
LOG(LL_ERROR, ("I2C bus missing, set i2c.enable=true in mos.yml"));
return false;
}
if (!imu) {
LOG(LL_ERROR, ("Cannot create IMU"));
return false;
}
acc_opts.type = ACC_MPU9250;
acc_opts.scale = 16.0; // G
acc_opts.odr = 100; // Hz
if (!mgos_imu_accelerometer_create_i2c(imu, i2c, 0x68, &acc_opts))
LOG(LL_ERROR, ("Cannot create accelerometer on IMU"));
gyro_opts.type = GYRO_MPU9250;
gyro_opts.scale = 2000; // deg/sec
gyro_opts.odr = 100; // Hz
if (!mgos_imu_gyroscope_create_i2c(imu, i2c, 0x68, &gyro_opts))
LOG(LL_ERROR, ("Cannot create gyroscope on IMU"));
mag_opts.type = MAG_AK8963;
mag_opts.scale = 12.0; // Gauss
mag_opts.odr = 10; // Hz
if (!mgos_imu_magnetometer_create_i2c(imu, i2c, 0x0C, &mag_opts))
LOG(LL_ERROR, ("Cannot create magnetometer on IMU"));
mgos_set_timer(1000, true, imu_cb, imu);
return true;
}
For a cool demo, take a look at my Demo Apps:
This project is not an official Google project. It is not supported by Google and Google specifically disclaims all warranties as to its quality, merchantability, or fitness for a particular purpose.
float odr; // Data rate, in Hz. See doc for set_odr().
float scale; // Scale. See doc for set_scale().
bool no_rst; // Do not perform reset of the device when configuring.
};
Gyroscope type.
float odr; // Data rate, in Hz. See doc for set_odr().
float scale; // Scale. See doc for set_scale().
bool no_rst; // Do not perform reset of the device when configuring.
};
Accelerometer type.
float odr; // Data rate, in Hz. See doc for set_odr().
float scale; // Scale. See doc for set_scale().
bool no_rst; // Do not perform reset of the device when configuring.
};
edit this docMagnetometer type.