pw_sensor#

This is the main documentation file for pw_sensor. It is under construction.

Defining types#

Pigweed provides a data-definition layer for sensors. This allows the properties of a sensor to be declared once and shared across multiple languages or runtimes. More information is available in pw_sensor Python package.

Once we define our units, measurements, attributes, and triggers we can import them and use them in our language-specific sensor code.

Here’s an example sensor definition YAML file for a custom sensor made by a company called “MyOrg” with a part ID “MyPaRt12345”. This sensor supports reading acceleration and its internal die temperature. We can also configure the sample rate for the acceleration readings.

deps:
   - "third_party/pigweed/pw_sensor/attributes.yaml"
   - "third_party/pigweed/pw_sensor/channels.yaml"
   - "third_party/pigweed/pw_sensor/units.yaml"
compatible:
   org: "MyOrg"
   part: "MyPaRt12345"
channels:
   acceleration: []
   die_temperature: []
attributes:
   -  attribute: "sample_rate"
      channel: "acceleration"
      units: "frequency"
      representation: "unsigned"

Now that we have our sensor spec in a YAML file we can use it in our C++ code:

Compiling one or more sensor YAML files into a header is done by a call to the pw_sensor_library rule. It looks like:

load("@pigweed//pw_sensor:sensor.bzl", "pw_sensor_library")

pw_sensor_library(
   name = "my_sensor_lib",
   out_header = "my_app/generated/sensor_constants.h",
   srcs = [
      "my_sensor0.yaml",
      "my_sensor1.yaml",
   ],
   inputs = [
      "@pigweed//pw_sensor:attributes.yaml",
      "@pigweed//pw_sensor:channels.yaml",
      "@pigweed//pw_sensor:triggers.yaml",
      "@pigweed//pw_sensor:units.yaml",
   ],
   generator_includes = ["@pigweed//"],
   deps = [
      "@pigweed//pw_sensor:pw_sensor_types",
      "@pigweed//pw_containers:flag_map",
      "@pigweed//pw_tokenizer:pw_tokenizer",
   ],
)

Compiling one or more sensor YAML files into a header is done by a call to the pw_sensor_library template. It looks like:

import("$dir_pw_sensor/sensor.gni")

pw_sensor_library("my_sensor_lib") {
  out_header = "my_app/generated/sensor_constants.h"
  sources = [
   "my_sensor0.yaml",
   "my_sensor1.yaml",
  ]
  inputs = [
   "$dir_pw_sensor/attributes.yaml",
   "$dir_pw_sensor/channels.yaml",
   "$dir_pw_sensor/triggers.yaml",
   "$dir_pw_sensor/units.yaml",
  ]
  generator_includes = [ getenv["PW_ROOT"] ]
  public_deps = [
   "$dir_pw_sensor:pw_sensor_types",
   "$dir_pw_containers:flag_map",
   "$dir_pw_tokenizer:pw_tokenizer",
  ]
}

Compiling one or more sensor YAML files into a header is done by a call to the pw_sensor_library function. It looks like:

include($ENV{PW_ROOT}/pw_sensor/sensor.cmake)

# Generate an interface library called my_sensor_lib which exposes a
# header file that can be included as
# "my_app/generated/sensor_constants.h".
pw_sensor_library(my_sensor_lib
   OUT_HEADER
      my_app/generated/sensor_constants.h
   INPUTS
      $ENV{PW_ROOT}/attributes.yaml
      $ENV{PW_ROOT}/channels.yaml
      $ENV{PW_ROOT}/triggers.yaml
      $ENV{PW_ROOT}/units.yaml
   GENERATOR_INCLUDES
      $ENV{PW_ROOT}
   SOURCES
      my_sensor0.yaml
      my_sensor1.yaml
   PUBLIC_DEPS
      pw_sensor.types
      pw_containers
      pw_tokenizer
)

The final product is an interface library that can be linked and used in your application. As an example:

#include "my_app/generated/sensor_constants.h"

int main() {
  PW_LOG_INFO(
    PW_LOG_TOKEN_FMT() " is measured in " PW_LOG_TOKEN_FMT(),
    pw::sensor::channels::kAcceleration::kMeasurementName,
    pw::sensor::GetMeasurementUnitNameFromType(
      pw::sensor::channels::kAcceleration::kUnitType
    )
  );
}

Under the hood#

In order to communicate with Pigweed’s sensor stack, there are a few type definitions that are used:

  • Unit types - created with PW_SENSOR_UNIT_TYPE. These can be thought of as defining things like “meters”, “meters per second square”, “radians per second”, etc.

  • Measurement types - created with PW_SENSOR_MEASUREMENT_TYPE. These are different things you can measure with a given unit. Examples: “height”, “width”, and “length” would all use “meters” as a unit but are different measurement types.

  • Attribute types - created with PW_SENSOR_ATTRIBUTE_TYPE. These are configurable aspects of the sensor. They are things like sample rates, tigger thresholds, etc. Attributes are unitless until they are paired with the measurement type that they modify. As an example “range” attribute for acceleration measurements would be in “m/s^2”, while a “range” attribute for rotational velocity would be in “rad/s”.

  • Attribute instances - created with PW_SENSOR_ATTRIBUTE_INSTANCE. These lump together an attribute with the measurement it applies to along with a unit to use. Example: Attribute(“sample rate”) + Measurement(“acceleration”) + Unit(“frequency”).

  • Trigger types - created with PW_SENSOR_TRIGGER_TYPE. These are events that affect the streaming API. These can be events like “fifo full”, “tap”, “double tap”

Developers don’t need to actually touch these, as they’re automatically called from the generated sensor library above. The important thing from the input YAML file is that our final generated header will include the following types: attributes::kSampleRate, channels::kAcceleration, channels::kDieTemperature, and units::kFrequency. All of these are used by our sensor.

A later change will also introduce the PW_SENSOR_ATTRIBUTE_INSTANCE.