Reconfigure sensors dynamically

IoT applications might require the reconfiguration of the target infrastructure in response to external events or under specific conditions. Huxon language supports dynamic reconfiguration of sensor parameters, such as Output Data Rate (ODR) and Full Scale (FS).

As an example, the sensitivity of a sensor can be increased at the expense of its full scale when the quantity to measure is relatively stable. Or, as another example, an anomaly detection application could increase the Output Data Rate of a sensor to collect more data when a problematic event is detected. Then, when the working conditions are back to normal, the Output Data Rate of the sensor could be decreased to reduce energy consumption.

Similarly to the initial sensor configuration, dynamic sensor reconfiguration involves two steps:
  1. create a sensor configurator for a specific sensor type

  2. register the sensor configurator to the target sensor

To create a sensor configurator for a specific sensor type we can use the HUX_SENSOR_CONFIGURATOR_BUILDER construct. This construct returns a sensor configurator builder that allows specifying one or more dynamic configurations as a chain of methods. Each method of the chain specifies which channel produces the configuration values for the given parameter. The final method of the chain must be finalize() which returns the actual sensor configurator.

The following is an example of how to create a sensor configurator for a ISM330DHCX sensor:

/* Build a sensor configurator for a ISM330DHCX sensor type */
auto sensor_ism330dhcx_configurator =
    HUX_SENSOR_CONFIGURATOR_BUILDER(hux::sensors::STMicroelectronics::ISM330DHCX)
        .add_ACC_FS_cfg_chan(fs_config_channel)
        .add_ACC_ODR_cfg_chan(odr_config_channel)
        .finalize();

In the example, fs_config_channel is a channel that produces configuration values for the FS parameter, while odr_config_channel is a channel that produces configuration values for the ODR parameter. Each sensor type has its own dynamic configuration parameters. For a full list of the available dynamic configuration parameters refer to the Sensors section.

Once we have created a sensor configurator, we can register it to a given sensor with the HUX_REGISTER_SENSOR_CONFIGURATOR construct:

/*
* sensor_x                       : the sensor label
* sensor_ism330dhcx_configurator : the dynamic configurator label
*/
HUX_REGISTER_SENSOR_CONFIGURATOR(sensor_x, sensor_ism330dhcx_configurator);

Note

A sensor configurator is sensor-type specific and can be registered to multiple sensors of the same type.

The sensor reconfiguration is triggered when any of the configuration channels produce a configuration value that differs from the active sensor configuration.

Inertial sensor reconfiguration example

In the following example we compute the maximum acceleration of an inertial sensor over a period of time and, if the value is too high, we increase the Full Scale (FS) range of the sensor and its Output Data Rate (ODR).

Note

Even if a configuration channel sends the same value multiple times, the sensor reconfiguration occurs only when the configuration value differs from the active sensor configuration.

#include <huxon/lang.hpp>
#include <huxon/sensors/STMicroelectronics.hpp>

#include <math.h>

namespace STM = hux::sensors::STMicroelectronics;

/* Declare an inertial sensor of type ISM330DHCX */
HUX_DECLARE_SENSOR(imu, STM::ISM330DHCX);

/* Fetch the X, Y and Z accelerations and compute the acceleration norm */
HUX_DECLARE_CHANNEL(acceleration_ch, zip_latest,
    imu.get_accX(),
    imu.get_accY(),
    imu.get_accZ()
);
HUX_DECLARE_PROCESSING(acc_norm, acceleration_ch, {
    float acc_x = hux::get<0>(hux_input);
    float acc_y = hux::get<1>(hux_input);
    float acc_z = hux::get<2>(hux_input);
    return std::sqrt(acc_x * acc_x + acc_y * acc_y + acc_z * acc_z);
});

/* Buffer acceleration norm samples and find the maximum value */
HUX_DECLARE_CHANNEL(channel_buffer, buffer<104>, acc_norm);
HUX_DECLARE_PROCESSING(max_acc, channel_buffer, {
    float max_acc = 0.0f;
    for(float val: hux_input) {
        max_acc = std::max(max_acc, val);
    }
    return max_acc;
});
HUX_DECLARE_CHANNEL(max_acc_ch, merge, max_acc);

/* Create a channel to dynamically configure the Output Data Rate of the imu */
HUX_DECLARE_PROCESSING(odr_switcher, max_acc_ch, {
    if (hux_input > 1.1) {
        return STM::ISM330DHCX::configs::acc_odr_104;
    } else {
        return STM::ISM330DHCX::configs::acc_odr_26;
    }
});
HUX_DECLARE_CHANNEL(odr_switcher_ch, merge, odr_switcher);

/* Create a channel to dynamically configure the Full Scale of the imu */
HUX_DECLARE_PROCESSING(fs_switcher, max_acc_ch, {
    if (hux_input > 1.5) {
        return STM::ISM330DHCX::configs::acc_fs_16;
    } else {
        return STM::ISM330DHCX::configs::acc_fs_2;
    }
});
HUX_DECLARE_CHANNEL(fs_switcher_ch, merge, fs_switcher);

/* Create a configurator for ISM330DHCX sensors, assigning:
* - channel fs_switcher_ch for configuring the FS parameter
* - channel odr_switcher_ch for configuring the ODR parameter
* */
auto imu_configurator = HUX_SENSOR_CONFIGURATOR_BUILDER(STM::ISM330DHCX)
    .add_ACC_FS_cfg_chan(fs_switcher_ch)
    .add_ACC_ODR_cfg_chan(odr_switcher_ch)
    .finalize();

/* Register the configurator to the imu sensor */
HUX_REGISTER_SENSOR_CONFIGURATOR(imu, imu_configurator);

/* Define the application output */
HUX_DECLARE_PROCESSING(node_output, max_acc_ch, {
    HUX_DECLARE_OUTPUT_VALUE(out, Float, "max_acceleration", hux_input);
    return out;
});

HUX_REGISTER_OUTPUT(node_output);