-
Notifications
You must be signed in to change notification settings - Fork 156
Sensors & Devices Database
This document outlines some ideas for extending the devices and sensors/actuators subsystem in macchina.io.
macchina.io comes with a number of predefined device and sensor/actuator interfaces that provide a unified interface to different kinds of sensors and other devices, e.g. temperature sensors, GNSS sensors, accelerometers, switches, etc. Applications working with sensors generally don't need to know about the implementation details of a specific sensor, as long as a sensor implements one of the predefined interfaces. In addition to sensor data, the interfaces also give access to metadata, e.g. what physical quantities and unit is used.
macchina.io also provides various implementations of these interfaces for specific devices, e.g. for TI SensorTag, Bosch CISS, Tinkerforge and other kinds of devices.
Sensors and devices are implemented as OSP services and can be found via the central service registry.
For example, to find all temperature sensors available:
var sensorRefs = serviceRegistry.find('io.macchina.physicalQuantity == "temperature"');
This will return an array of ServiceRef
objects referencing to services implementing the Sensor interface.
The ServiceRef
objects also contain some metadata in the form of properties, e.g.
-
io.macchina.deviceType
- the general type of device (in this case "io.macchina.sensor"). -
io.macchina.device
- the specific device type (e.g. "io.macchina.simulation.sensor"). -
io.macchina.physicalQuantity
- the physical quantity measured by the sensor ("temperature", as used in the query above). -
io.macchina.instanceName
- the human-readable name of the device instance. -
io.macchina.composite
- the service name of the parent composite device (only available if device is part of a composite). -
name
- the service name (e.g. "io.macchina.simulation.sensor#1").
Similarly, the Sensor service interface also provides means to query a specific sensor's property, and to configure certain features of a sensor. This is depending on the specific implementation of the device.
Some of the properties available in a Sensor are:
-
displayValue
: the current value of the sensor as a string, formatted for displaying. -
valueChangedPeriod
: the minimum period between firings of thevalueChanged
event. -
valueChangedDelta
: the minimum delta the value must change for thevalueChanged
event to fire. -
physicalQuantity
: the physical quantity the sensor measures (e.g. "temperature"). -
physicalUnit
: the physical unit of the value, using Unified Code for Units of Measure (e.g. "Cel"). See also the UnitsOfMeasure service.
In addition, all Device instances also support:
-
deviceIdentifier
: an implementation-specific identifier of the underlying device (e.g. "io.macchina.simulation.sensor#1"). This corresponds to the service name. -
symbolicName
: an implementation-specific name of the sensor implementation (e.g. "io.macchina.simulation.sensor"). -
name
: a human-readable name of the device implementation (e.g. "Simulated Sensor"). -
instanceName
: a human-readable name of the device instance (e.g. "Battery Temperature"). -
type
: the type of device interface the sensor implements (e.g. "io.macchina.sensor"; see below for defined device types). -
status
: Optional device operational status (0 =DEVICE_STATUS_UNKNOWN
, 1 =DEVICE_STATUS_DISABLED
, 2 =DEVICE_STATUS_ENABLED
, 3 =DEVICE_STATUS_READY
, 4 =DEVICE_STATUS_ERROR
).
Device and sensor implementations can also define and implement their own properties.
The following devices types are defined:
-
io.macchina.accelerometer
: Accelerometer -
io.macchina.barcode
: BarcodeReader -
io.macchina.gnss
: GNSSSensor -
io.macchina.gyroscope
: Gyroscope -
io.macchina.io
: IO (GPIO) -
io.macchina.led
: LED -
io.macchina.magnetometer
: Magnetometer -
io.macchina.rotary
: RotaryEncoder -
io.macchina.sensor
: Sensor -
io.macchina.boolean
: BooleanSensor -
io.macchina.counter
: Counter -
io.macchina.serial
: SerialDevice -
io.macchina.switch
: Switch -
io.macchina.trigger
: Trigger
What is currently missing is a way to organize devices by their specific role in a device or subsystem. For example, we may want to obtain the "Motor Temperature" or the "Battery Temperature" of an electric bike. We may also want to organize devices hierarchically, i.e. create something like a "device tree". For example, for an electric bike, a subset of the device tree would be:
/
Battery/
Voltage
Temperature
ChargeLevel
Motor/
Switch
PowerUsage
RPM
Temperature
...
...
What is needed is a way to assign a logical hierarchical name, e.g. "/Battery/Temperature" to a specific Device or Sensor instance.
A new service class, CompositeDevice
, will be added to the Devices
library. This class will only provide a method to obtain all direct sub devices ("fragments"). The device type is io.macchina.composite
.
class CompositeDevice: public Device
{
public:
std::vector<std::string> fragments() const;
/// Returns the device identifiers of all direct sub devices owned by this device.
};
A new service class, DeviceTree
, will be added to the Devices library.
The DeviceTree
class will create as necessary the CompositeDevice
objects, and assign the sub devices (fragments).
struct DeviceNode
{
std::string id; /// Device identifier, e.g. "io.macchina.simulation.sensor#1"
std::string name; /// Instance name, e.g. "Temperature"
std::string composite; /// Device identifier of parent composite device, which is always a CompositeDevice.
Poco::Optional<std::vector<std::string>> fragments;
/// Device identifiers of all direct child fragment devices, only set if the device is a CompositeDevice.
};
class DeviceTree
{
public:
DeviceNode deviceByPath(const std::string& pathName);
/// Returns the DeviceNode of the device with the given path name.
/// The path name is composed of the instance names of all parent composite devices,
/// as well as the device's instance name, separated by "/".
///
/// To obtain the root device, which is always a `CompositeDevice`, specify "/" as pathName.
///
/// Throws a Poco::NotFoundException if no DeviceNode exists for the given pathName.
DeviceNode deviceById(const std::string& id);
/// Returns the DeviceNode of the device with the given device identifier.
///
/// Throws a Poco::NotFoundException if no DeviceNode exists for the given device identifier.
};
// Obtain DeviceTree service
var deviceTreeRef = serviceRegistry.findByName('io.macchina.deviceTree');
var deviceTree = deviceTreeRef.instance();
// Get Root Device
var rootNode = deviceTree.deviceByPath('/');
// Get Specific Device Node
var batteryTemperatureNode = deviceTree.deviceByPath('/Battery/Temperature');
// Get Battery Temperature Sensor
var batteryTemperatureSensorRef = serviceRegistry.findByName(batteryTemperatureNode.id);
var batteryTemperatureSensor = batteryTemperatureSensorRef.instance();
// Get Battery Temperature
var batteryTemperature = batteryTemperatureSensor.value();
Configuration of the device tree could be done via a JSON or XML document, or via a SQLite database.