Sensor Device Driver¶
A Mynewt sensor device driver uses the sensor framework abstraction and API to enable applications to access sensor data from any Mynewt sensor device using a common interface. The sensor device driver must also use the Mynewt HAL interface to communicate with and control a sensor device.
This guide describes what a sensor device driver must implement to enable a sensor device within the sensor framework. For information on using the HAL API to communicate with a sensor device, see the Hardware Layer Abstraction Guide.
The hw/drivers/sensors/<sensorname>
package implements the device
driver for the sensor named SENSORNAME
.
Note: All example excerpts are from the BNO055 sensor device driver package.
Initializing and Configuring a Sensor Device¶
A driver package for a sensor named SENSORNAME
must define and
export the following data structures and functions to initialize and
configure a device:
struct <sensorname>
: This data structure represents a sensor device. The structure must include adev
field of typestruct os_dev
and asensor
field of typestruct sensor
. For example:struct bno055 { struct os_dev dev; struct sensor sensor; struct bno055_cfg cfg; os_time_t last_read_time; };
struct <sensorname>_cfg
: This data structure defines the configuration for a sensor device. The structure fields are specific to the device. This is the data structure that a BSP, the sensor creator package, or an application sets to configure a sensor device. For example:struct bno055_cfg { uint8_t bc_opr_mode; uint8_t bc_pwr_mode; uint8_t bc_units; uint8_t bc_placement; uint8_t bc_acc_range; uint8_t bc_acc_bw; ... uint32_t bc_mask; };
<sensorname>_init()
: This is the os device initialization callback of typeint (*os_dev_init_func_t)(struct os_dev *, void *)
that theos_dev_create()
function calls to initialize the device. For example, the bno055 device driver package defines the following function:int bno055_init(struct os_dev *dev, void *arg)
The BSP, which creates a device for an onboard sensor, and the sensor creator package, which creates a device for an off-board sensor, calls the
os_dev_create()
function and passes:A pointer to a
struct <sensorname>
variable.The
<sensorname>_init()
function pointer.A pointer to a
struct sensor_itf
variable that specifies the interface the driver uses to communicate with the sensor device.
See the Creating Sensor Devices page for more details.
The
os_dev_create()
function calls the<sensorname>_init()
function with a pointer to thestruct <sensorname>
for thedev
parameter and a pointer to thestruct sensor_itf
for thearg
parameter.<sensorname>_config()
: This is the sensor configuration function that the BSP, sensor creator package, or an application calls to configure the sensor device. For example:int bno055_config(struct bno055 *bno055, struct bno055_cfg *cfg)
Defining Functions to Read Sensor Data and Get Sensor Value Type¶
A device driver must implement the following functions that the sensor API uses to read sensor data and to get the configuration value type for a sensor:
A function of type
int (*sensor_read_func_t)(struct sensor *, sensor_type_t, sensor_data_func_t, void *, uint32_t)
that the sensor framework uses to read a single value from a sensor for the specified sensor types. The device driver must implement this function such that it reads, for each sensor type set in the bit mask, a single value from the sensor and calls thesensor_data_funct_t
callback with the opaque callback argument , the sensor data, and the sensor type.A function of type
int (*sensor_get_config_func_t)(struct sensor *, sensor_type_t, struct sensor_cfg *)
that returns the value type for the specified sensor type. For example, the value type for aSENSOR_VALUE_TYPE_TEMPERATURE
sensor might beSENSOR_VALUE_TYPE_FLOAT
and the value type for aSENSOR_TYPE_ACCELEROMETER
sensor might beSENSOR_VALUE_TYPE_FLOAT_TRIPLET
.
The driver initializes a sensor_driver
structure, shown below, with
the pointers to these functions:
struct sensor_driver {
sensor_read_func_t sd_read;
sensor_get_config_func_t sd_get_config;
};
For example:
static int bno055_sensor_read(struct sensor *, sensor_type_t,
sensor_data_func_t, void *, uint32_t);
static int bno055_sensor_get_config(struct sensor *, sensor_type_t,
struct sensor_cfg *);
static const struct sensor_driver g_bno055_sensor_driver = {
bno055_sensor_read,
bno055_sensor_get_config
};
Registering the Sensor in the Sensor Framework¶
The device driver must initialize and register a struct sensor
object with the sensor manager. See the Sensor
API and the Sensor
Manager API
pages for more details.
The device driver <sensorname>_init()
function initializes and
registers a sensor object as follows:
Calls the
sensor_init()
function to initialize thestruct sensor
object.Calls the
sensor_set_driver()
function to specify the sensor types that the sensor device supports, and the pointer to thestruct sensor_driver
variable that specifies the driver functions to read the sensor data and to get the value type for a sensor.Calls the
sensor_set_interface()
function to set the interface that the device driver uses to communicate with the sensor device. The BSP, or sensor creator package for an off-board sensors, sets up thesensor_itf
and passes it to the<sensorname>_init()
function. Thesensor_set_interface()
functions saves this information in the sensor object. The device driver uses theSENSOR_GET_ITF()
macro to retrieve the sensor_itf when it needs to communicate with the sensor device.Calls the
sensor_mgr_register()
function to register the sensor with the sensor manager.
For example:
int
bno055_init(struct os_dev *dev, void *arg)
{
struct bno055 *bno055;
struct sensor *sensor;
int rc;
if (!arg || !dev) {
rc = SYS_ENODEV;
goto err;
}
bno055 = (struct bno055 *) dev;
rc = bno055_default_cfg(&bno055->cfg);
if (rc) {
goto err;
}
sensor = &bno055->sensor;
/* Code to setup logging and stats may go here */
....
rc = sensor_init(sensor, dev);
if (rc != 0) {
goto err;
}
/* Add the accelerometer/magnetometer driver */
rc = sensor_set_driver(sensor, SENSOR_TYPE_ACCELEROMETER |
SENSOR_TYPE_MAGNETIC_FIELD | SENSOR_TYPE_GYROSCOPE |
SENSOR_TYPE_TEMPERATURE | SENSOR_TYPE_ROTATION_VECTOR |
SENSOR_TYPE_GRAVITY | SENSOR_TYPE_LINEAR_ACCEL |
SENSOR_TYPE_EULER, (struct sensor_driver *) &g_bno055_sensor_driver);
if (rc != 0) {
goto err;
}
/* Set the interface */
rc = sensor_set_interface(sensor, arg);
if (rc) {
goto err;
}
rc = sensor_mgr_register(sensor);
if (rc != 0) {
goto err;
}
return (0);
err:
return (rc);
}
Configuring the Sensor Device and Setting the Configured Sensor Types¶
After the BSP, or the sensor creator package for an off-board sensor,
creates the OS device for a sensor, it calls the
<sensorname>_config()
function to configure sensor device
information such as mode, power mode, and to set the configured sensor
types. The <sensorname>_config()
function configures the settings on
the sensor device. It must also call the sensor_set_type_mask()
function to set the configured sensor types in the sensor object. The
configured sensor types are a subset of the sensor types that the sensor
device supports and the sensor framework only reads sensor data for
configured sensor types.
Notes:
The device driver uses the
SENSOR_GET_ITF()
macro to retrieve the sensor interface to communicate with the sensor.If a sensor device has a chip ID that can be queried, we recommend that the device driver read and verify the chip ID with the data sheet.
An application may call the
<sensorname>_config()
function to configure the sensor device.
For example:
int
bno055_config(struct bno055 *bno055, struct bno055_cfg *cfg)
{
int rc;
uint8_t id;
uint8_t mode;
struct sensor_itf *itf;
itf = SENSOR_GET_ITF(&(bno055->sensor));
/* Check if we can read the chip address */
rc = bno055_get_chip_id(itf, &id);
if (rc) {
goto err;
}
if (id != BNO055_ID) {
os_time_delay((OS_TICKS_PER_SEC * 100)/1000 + 1);
rc = bno055_get_chip_id(itf, &id);
if (rc) {
goto err;
}
if(id != BNO055_ID) {
rc = SYS_EINVAL;
goto err;
}
}
....
/* Other code to set the configuration on the sensor device. */
....
rc = sensor_set_type_mask(&(bno055->sensor), cfg->bc_mask);
if (rc) {
goto err;
}
bno055->cfg.bc_mask = cfg->bc_mask;
return 0;
err:
return rc;
}
Implementing a Sensor Device Shell Command¶
A sensor device driver package may optionally implement a sensor device
shell command that retrieves and sets sensor device information to aid
in testing and debugging. While the sensor framework sensor shell
command reads sensor
data for configured sensor types and is useful for testing an
application, it does not access low level device information, such as
reading register values and setting hardware configurations, that might
be needed to test a sensor device or to debug the sensor device driver
code. A sensor device shell command implementation is device specific
but should minimally support reading sensor data for all the sensor
types that the device supports because the sensor framework sensor
shell command only reads sensor data for configured sensor types.
The package should:
Name the sensor device shell command
<sensorname>
. For example, the sensor device shell command for the BNO055 sensor device isbno055
.Define a
<SENSORNAME>_CLI
syscfg setting to specify whether the shell command is enabled and disable the setting by default.Export a
<sensorname>_shell_init()
function that an application calls to initialize the sensor shell command when theSENSORNAME_CLI
setting is enabled.
For an example on how to implement a sensor device shell command, see the bno055 shell command source code. See the Enabling an Off-Board Sensor in an Existing Application Tutorial for examples of the bno055 shell command.
Defining Logs¶
A sensor device driver should define logs for testing purposes. See the
Log OS Guide for more details on how to
add logs. The driver should define a <SENSORNAME>_LOG
syscfg setting
to specify whether logging is enabled and disable the setting by
default.
Here is an example from the BNO055 sensor driver package:
#if MYNEWT_VAL(BNO055_LOG)
#include "log/log.h"
#endif
#if MYNEWT_VAL(BNO055_LOG)
#define LOG_MODULE_BNO055 (305)
#define BNO055_INFO(...) LOG_INFO(&_log, LOG_MODULE_BNO055, _VA_ARGS_)
#define BNO055_ERR(...) LOG_ERROR(&_log, LOG_MODULE_BNO055,_VA_ARGS_)
static struct log _log;
#else
#define BNO055_INFO(...)
#define BNO055_ERR(...)
#endif
...
int
bno055_init(struct os_dev *dev, void *arg)
{
...
rc = bno055_default_cfg(&bno055->cfg);
if (rc) {
goto err;
}
#if MYNEWT_VAL(BNO055_LOG)
log_register(dev->od_name, &_log, &log_console_handler, NULL, LOG_SYSLEVEL);
#endif
...
}
Defining Stats¶
A sensor device driver may also define stats for the sensor. See the
Stats OS Guide for more details on how
to add stats. The driver should define a <SENSORNAME>_STATS
syscfg
setting to specify whether stats is enabled and disable the setting by
default.
Here is an example from the BNO055 sensor driver package:
#if MYNEWT_VAL(BNO055_STATS)
#include "stats/stats.h"
#endif
#if MYNEWT_VAL(BNO055_STATS)
/* Define the stats section and records */
STATS_SECT_START(bno055_stat_section)
STATS_SECT_ENTRY(errors)
STATS_SECT_END
/* Define stat names for querying */
STATS_NAME_START(bno055_stat_section)
STATS_NAME(bno055_stat_section, errors)
STATS_NAME_END(bno055_stat_section)
/* Global variable used to hold stats data */
STATS_SECT_DECL(bno055_stat_section) g_bno055stats;
#endif
...
int
bno055_init(struct os_dev *dev, void *arg)
{
...
#if MYNEWT\_VAL(BNO055\_STATS)
/* Initialise the stats entry */
rc = stats_init(
STATS_HDR(g_bno055stats),
STATS_SIZE_INIT_PARMS(g_bno055stats, STATS_SIZE_32),
STATS_NAME_INIT_PARMS(bno055_stat_section));
SYSINIT_PANIC_ASSERT(rc == 0);
/* Register the entry with the stats registry */
rc = stats_register(dev->od_name, STATS_HDR(g_bno055stats));
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
...
}