Previous: , Up: Code integration -- extra materials   [Contents]


3.7.3 Making use of private thread scheduling to poll hardware

A foreign component is able to by-pass the dzn::pump when sending events into the System. Consequently, this means it is no longer possible to guarantee the single-thread-active convention within the System. This can be avoided by interacting with the dzn::pump within the System.

To demonstrate how you can interact with the dzn::pump in a foreign component, the final component of the AlarmSystem will be implemented and integrated: the sensor. Again, a snapshot of the Dezyne models for this stage of the application can be found at <github link>.

For the Sensor, a hardware button was used that needs to be debounced for reliable input readings (https://www.arduino.cc/en/section/debounce). The debouncing algorithm provided in the Arduino section works on a polling basis, which the event loop for the AlarmSystem does not support. On top of that, polling the foreign Sensor component outside of the safe Dezyne thread can lead to undesirable effects. Both of these challenges can be solved by making use of the dzn::pump event queue in the foreign Sensor implementation. If you store the logic that requires polling in a separate poll() function and place the poll() event in the queue while the Sensor is active, the private Dezyne thread will handle the polling and thread safety remains intact.

Below you can find a foreign Sensor implementation that implements the Arduino example debouncing algorithm by the use of dzn::pump to ensure thread safety.

Sensor.hh:

#include "skel_Sensor.hh"
#include <dzn/pump.hh>

class Sensor : public skel::Sensor {
private:
  dzn::pump& pump;
  bool sensor_value;
  bool last_sensor_value;
  unsigned long last_dbnc_time;
  bool polling;

public:
  Sensor(const dzn::locator& loc);
  void iSensor_turnOn();
  void iSensor_turnOff();
  void poll();
};

Sensor.cc:

#include <dzn/locator.hh>
#include <dzn/runtime.hh>

#include <wiringPi.h>
#include "Sensor.hh"

const int DEBOUNCE_TIME = 50;
const int PIN_SENSOR = 16;

Sensor::Sensor(const dzn::locator& loc) : skel::Sensor(loc), pump(loc.get<dzn::pump>()) {
  wiringPiSetup();
  pinMode(PIN_SENSOR, INPUT);

  pullUpDnControl(PIN_SENSOR, PUD_UP);
  this->sensor_value = false;
  this->last_sensor_value = false;
  this->last_dbnc_time = 0;
  this->polling = false;
}

void Sensor::iSensor_turnOn() {
  this->polling = true;
  this->pump( [&] { this->poll(); } );
}

void Sensor::iSensor_turnOff() {
  this->polling = false;
}

void Sensor::poll() {
  int new_sensor_value = !digitalRead(PIN_SENSOR);

  if (new_sensor_value != this->last_sensor_value) {
    this->last_dbnc_time = millis();
  }

  if((millis() - last_dbnc_time) > DEBOUNCE_TIME) {

    if(new_sensor_value != this->sensor_value) {
      this->sensor_value = new_sensor_value;
      if(this->sensor_value) {
        this->pump( [&] { this->iSensor.out.triggered(); } );
      }
    }
  }
  this->last_sensor_value = new_sensor_value;
  if(this->polling) this->pump( [&] { this->poll(); } );
}

The interactions with the dzn::pump in the System are highlighted; the Sensor class has its own reference to the dzn::pump. This reference is set in the constructor by getting the System dzn::pump from the provided dzn::locator.

The usage of dzn::pump involves providing std::function objects of events that need to be handled. The easy way to do this is by wrapping functions in lambda expressions as can be seen in the above code snippet. Finally, a way to ensure that polling is continued while the Sensor is turned on is to have the poll() method store itself in the event queue recursively.


Previous: , Up: Code integration -- extra materials   [Contents]