Adafruit_Sensor in Detail

The unified sensor drivers system is based around a single base class named Adafruit_Sensor (source code). This base class defines two functions and and a few shared 'types', which are described below.

Essentially, 'Adafruit_Sensor' is a contract that every driver signs to make sure it plays well with other unified sensor drivers and programs that make use of the system. Anything the inherits from Adafruit_Sensor can obscure away almost all of it's technical particularities, and instead present this common facade.

When a new fancy sensor comes out at half the price of your current one, you don't have to worry about recreating the wheel since the new driver snaps nicely into place because it was created with the same common mold in mind! All you need to care about is that it follows the contract set down by the base class, and when you call those pre-defined functions, you get back the expected results in the expected units and scale.

Standardised SI Units for Sensor Data

A key part of the unified sensor driver system layer is the standardisation of values on SI units of a particular scale. This following SI units and scales are used for the appropriate sensor type:
  • acceleration: values are in meter per second per second (m/s^2)
  • magnetic: values are in micro-Tesla (uT)
  • orientation: values are in degrees
  • gyro: values are in rad/s
  • temperature: values in degrees centigrade (Celsius)
  • distance: values are in centimeters
  • light: values are in SI lux units
  • pressure: values are in hectopascal (hPa)
  • relative_humidity: values are in percent
  • current: values are in milliamps (mA)
  • voltage: values are in volts (V)
  • color: values are in 0..1.0 RGB channel luminosity and 32-bit RGBA format
This is one of the key benefits of the unified sensor driver system. By using standard SI units in drivers, any accelerometer can be switched out with any other accelerometer with minimal impact on the rest of the system because they all produce exactly the same units and scale (m/s^2)!

No more wondering what 0 or 1023 means, since the necessary conversions and calculations are already done for you behind the scene!

Supported Sensor Types

While this list may expand in the future, the following sensor types are currently supported in the Adafruit Unified Sensor System (based on 'sensors_type_t' in Adafruit_Sensor.h):
Download: file
/** Sensor types */
typedef enum
{
  SENSOR_TYPE_ACCELEROMETER         = (1),   /**< Gravity + linear acceleration */
  SENSOR_TYPE_MAGNETIC_FIELD        = (2),
  SENSOR_TYPE_ORIENTATION           = (3),
  SENSOR_TYPE_GYROSCOPE             = (4),
  SENSOR_TYPE_LIGHT                 = (5),
  SENSOR_TYPE_PRESSURE              = (6),
  SENSOR_TYPE_PROXIMITY             = (8),
  SENSOR_TYPE_GRAVITY               = (9),
  SENSOR_TYPE_LINEAR_ACCELERATION   = (10),  /**< Acceleration not including gravity */
  SENSOR_TYPE_ROTATION_VECTOR       = (11),
  SENSOR_TYPE_RELATIVE_HUMIDITY     = (12),
  SENSOR_TYPE_AMBIENT_TEMPERATURE   = (13),
  SENSOR_TYPE_VOLTAGE               = (15),
  SENSOR_TYPE_CURRENT               = (16),
  SENSOR_TYPE_COLOR                 = (17)
} sensors_type_t;

Key Functions in Adafruit_Sensor

Anything that works with the unified sensor driver system uses two basic 'types', with each type having an associated read function for it. These data types are describe in a lot more detail below, but they're summarised below for convenience sake:
  • sensors_event_t - This type is used to encapsulate a specific sensor reading, called an 'event', and contains a data from the sensor from a specific moment in time.
  • sensor_t - This type is used to describe some basic details about this specific sensor, including the sensor name, the resolution and range of the sensor (in the SI units described above), as well as a unique ID that you can assign to this sensor so that you can identify dozens of identical sensors if you are logging your sensor data in a central database.
To fill an object with the right data, you simply need to call one of the two mandatory functions in the unified driver system:

void getEvent(sensors_event_t*)

This function will read a new set of values from you sensor (a sensor 'event'), convert them to the appropriate SI units and scale, and then assign the results to a specific sensors_event_t object.

This is the function you call to 'read' your sensor!
A pointer to sensors_event_t object is used here to save memory, so that you only ever need one sensor_event_t object since they use 36 bytes of memory each.
After creating an instance of your driver ('tsl' below), you would call this function as follows:
Download: file
 /* Create an instance of our sensor (only do this once!) */
 Adafruit_TSL2561 tsl = Adafruit_TSL2561(TSL2561_ADDR_FLOAT, 12345);
 
 ...

 /* Create a sensors_event_t object in memory to hold our results */
 sensors_event_t event;

 /* Get a new sensor event, passing in our 'event' placeholder */ 
 tsl.getEvent(&event);

 /* Now do something with the sensor event data ... */

 /* Display the results (light is measured in lux) */
 if (event.light)
 {
   Serial.print(event.light); Serial.println(" lux");
 }
 else
 {
   /* If event.light = 0 lux the sensor is probably saturated
      and no reliable data could be generated! */
   Serial.println("Sensor overload");
 }

void getSensor(sensor_t*)

This function returns information about the sensor itself, such as the sensor name, the min and max values that the sensor can return, and a unique ID for this specific sensor that can help you distinguish different copies of the exact same sensor in your sensor network or project. If you have 50 light sensors on 50 tomato plants, it's useful to know which data is from which plant, and the unique ID is a great way to solve this problem in your data logs.

You can call this function with the following code (using the TSL2561 from above as an example):
Download: file
 /* Create a sensor_t object in memory to filled below */
 sensor_t sensor;

 /* Get the sensor details and place them in 'sensor' */
 tsl.getSensor(&sensor);

 /* Display the sensor details */
 Serial.println("------------------------------------");
 Serial.print  ("Sensor:       "); Serial.println(sensor.name);
 Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
 Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
 Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" lux");
 Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" lux");
 Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" lux");  
 Serial.println("------------------------------------");
 Serial.println("");

Data Type Implementation Details

The two functions defined above manipulate specific data 'types'. These types (sensors_event_t and sensor_t) allow us to abstract any type of sensor data into a single object that we can easily log, transmit, manipulate, etc., without requiring any knowledge of the type of sensor being used:
  • sensors_event_t - This 'type' contains sensor data from a single read at a specific moment in time. The same event type is used for any sensor, with details on how this is made possible below. Sensor data is generally defined as 32-bit floating point values, but two exceptions are the data types defined below:
    • sensors_vec_t - This is a type used by sensors_event_t to encapsulate X/Y/Z or similar data in a generic way.
    • sensors_color_t - This is another type used by sensors_event_t to encapsulate basic color data.
  • sensor_t - Data about the sensor itself.

sensors_event_t (sensor data)

'sensors_event_t' (defined in Adafruit_Sensor.h) encapsulates sensor data from any type of sensor.

The way this works is that a fancy feature of C called a 'union' is employed to pack a variety of fields into a single struct, and you can simply use the field that is appropriate for your specific requirements and ignore the rest.

The raw source code for the typedef is below:
Download: file
/* Sensor event (36 bytes) */
/** struct sensor_event_s is used to provide a single sensor event in a common format. */
typedef struct
{
    int32_t version;                          /**< must be sizeof(struct sensors_event_t) */
    int32_t sensor_id;                        /**< unique sensor identifier */
    int32_t type;                             /**< sensor type */
    int32_t reserved0;                        /**< reserved */
    int32_t timestamp;                        /**< time is in milliseconds */
    union
    {
        float           data[4];
        sensors_vec_t   acceleration;         /**< acceleration values are in meter per second per second (m/s^2) */
        sensors_vec_t   magnetic;             /**< magnetic vector values are in micro-Tesla (uT) */
        sensors_vec_t   orientation;          /**< orientation values are in degrees */
        sensors_vec_t   gyro;                 /**< gyroscope values are in rad/s */
        float           temperature;          /**< temperature is in degrees centigrade (Celsius) */
        float           distance;             /**< distance in centimeters */
        float           light;                /**< light in SI lux units */
        float           pressure;             /**< pressure in hectopascal (hPa) */
        float           relative_humidity;    /**< relative humidity in percent */
        float           current;              /**< current in milliamps (mA) */
        float           voltage;              /**< voltage in volts (V) */
        sensors_color_t color;                /**< color in RGB component values */
    };
} sensors_event_t;
The advantage of handling things this way is that you can completely abstract away the different sensor types. If you are using a temperature sensor, you simply read the 'event.temperature' field, for a light sensor you read the 'event.light' field, etc.

Certain sensor types also use the sensors_vec_t or sensors_color_t data types. These are used for sensors that return more than one numeric value, such as X/Y/Z for an accelerometer or gyroscope, or r/g/b color data for a color sensor.

sensors_vec_t
Download: file
/** struct sensors_vec_s is used to return a vector in a common format. */
typedef struct {
    union {
        float v[3];
        struct {
            float x;
            float y;
            float z;
        };
        /* Orientation sensors */
        struct {
            float azimuth;
            float pitch;
            float roll;
        };
    };
    int8_t status;
    uint8_t reserved[3];
} sensors_vec_t;
sensors_color_t
Download: file
/** struct sensors_color_s is used to return color data in a common format. */
typedef struct {
    union {
        float c[3];
        /* RGB color space */
        struct {
            float r;
            float g;
            float b;
        };
    };
    uint32_t rgba;         /* 24-bit RGB value */
} sensors_color_t;
You can access these extended types in the same manner ... for acceleration data, for example, we could read the data with: 'event.acceleration.x', 'event.acceleration.y', etc.

sensor_t (Sensor details)

The other important type is sensor_t, which encapsulates all of the key information about this sensor, such as the sensor type, the name of this particular sensor, the driver version, the unique ID for this specific sensor instance (in case multiple sensors of the same type are used), etc.

The raw typedef for sensor_t is defined in Adafruit_Sensor.h, but can be seen below:
Download: file
/* Sensor details (40 bytes) */
/** struct sensor_s is used to describe basic information about a specific sensor. */
typedef struct
{
    char     name[12];                        /**< sensor name */
    int32_t  version;                         /**< version of the hardware + driver */
    int32_t  sensor_id;                       /**< unique sensor identifier */
    int32_t  type;                            /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
    float    max_value;                       /**< maximum value of this sensor's value in SI units */
    float    min_value;                       /**< minimum value of this sensor's value in SI units */
    float    resolution;                      /**< smallest difference between two values reported by this sensor */
    int32_t  min_delay;                       /**< min delay in microseconds between events. zero = not a constant rate */
} sensor_t;
The most important field here is type, which identifies the type of sensor and allows us to know what type of data to read from the sensors_event_t typedef.
This guide was first published on Feb 28, 2013. It was last updated on Feb 28, 2013. This page (Adafruit_Sensor in Detail) was last updated on May 22, 2019.