Another step towards the new SensorController. It is now also responsible for triggering the measurements based on a simple parameter (after how many seconds to trigger a measurement).
This commit is contained in:
parent
c5547b37ee
commit
a6e7609d66
|
@ -6,34 +6,29 @@ namespace Dough
|
|||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
DataController *DataController::_instance = nullptr;
|
||||
|
||||
DataController *DataController::Instance()
|
||||
{
|
||||
if (DataController::_instance == nullptr)
|
||||
{
|
||||
DataController::_instance = new DataController();
|
||||
}
|
||||
return DataController::_instance;
|
||||
static DataController *_instance = new DataController();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
DataController::DataController() : _temperatureMeasurements(
|
||||
"temperature",
|
||||
TemperatureSensor::Instance(),
|
||||
TEMPERATURE_AVG_LOOKBACK,
|
||||
TEMPERATURE_SIGNIFICANT_CHANGE,
|
||||
30,
|
||||
PUBLISH_INTERVAL),
|
||||
_humidityMeasurements(
|
||||
"humidity",
|
||||
HumiditySensor::Instance(),
|
||||
HUMIDITY_AVG_LOOKBACK,
|
||||
HUMIDITY_SIGNIFICANT_CHANGE,
|
||||
30,
|
||||
PUBLISH_INTERVAL),
|
||||
_distanceMeasurements(
|
||||
"distance",
|
||||
DistanceSensor::Instance(),
|
||||
DISTANCE_AVG_LOOKBACK,
|
||||
DISTANCE_SIGNIFICANT_CHANGE,
|
||||
1,
|
||||
PUBLISH_INTERVAL),
|
||||
_logger("DATA")
|
||||
{
|
||||
|
@ -114,7 +109,11 @@ namespace Dough
|
|||
{
|
||||
if (isConfigured())
|
||||
{
|
||||
_sample();
|
||||
_temperatureMeasurements.loop();
|
||||
_humidityMeasurements.loop();
|
||||
_distanceMeasurements.loop();
|
||||
// Moved logic into sensor controller code.
|
||||
//_sample();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +136,7 @@ namespace Dough
|
|||
{
|
||||
_lastSample = now;
|
||||
|
||||
// Quickly dip the LED to indicate that a measurement is started.
|
||||
// Quickly dip the LED to indicate that a measurement has started.
|
||||
// This is done synchroneously, because we suspend the timer interrupts
|
||||
// in the upcoming code.
|
||||
_ui->led3.off();
|
||||
|
@ -152,15 +151,15 @@ namespace Dough
|
|||
switch (_sampleType)
|
||||
{
|
||||
case SAMPLE_TEMPERATURE:
|
||||
_temperatureMeasurements.process();
|
||||
_temperatureMeasurements.loop();
|
||||
_sampleType = SAMPLE_HUMIDITY;
|
||||
break;
|
||||
case SAMPLE_HUMIDITY:
|
||||
_humidityMeasurements.process();
|
||||
_humidityMeasurements.loop();
|
||||
_sampleType = SAMPLE_DISTANCE;
|
||||
break;
|
||||
case SAMPLE_DISTANCE:
|
||||
_distanceMeasurements.process();
|
||||
_distanceMeasurements.loop();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,6 @@
|
|||
#define HUMIDITY_AVG_LOOKBACK 6 // making this a 3 minute average
|
||||
#define DISTANCE_AVG_LOOKBACK 28 * 2 * 3 // making this a 3 minute average
|
||||
|
||||
// When significant changes occur in the sensor measurements, they are
|
||||
// published to MQTT. These definitions specify what is considered significant.
|
||||
#define TEMPERATURE_SIGNIFICANT_CHANGE 2 // to dampen flapping between two values on transition
|
||||
#define HUMIDITY_SIGNIFICANT_CHANGE 2 // also to dampen flapping behavior.
|
||||
#define DISTANCE_SIGNIFICANT_CHANGE 3 // based on the sensor specification of 3mm resolution
|
||||
|
||||
// The minimal interval in seconds at which to forcibly publish measurements to the
|
||||
// MQTT broker. When significant changes occur in the measurements, then these will
|
||||
// be published to the MQTT broker at all times, independent from this interval.
|
||||
|
@ -63,7 +57,6 @@ namespace Dough
|
|||
|
||||
private:
|
||||
DataController();
|
||||
static DataController *_instance;
|
||||
UI *_ui;
|
||||
MQTT *_mqtt;
|
||||
SensorController _temperatureMeasurements;
|
||||
|
|
|
@ -7,15 +7,16 @@ namespace Dough
|
|||
const char *mqttKey,
|
||||
SensorBase *sensor,
|
||||
unsigned int storageSize,
|
||||
unsigned int significantChange,
|
||||
unsigned int minimumMeasureTime,
|
||||
unsigned int minimumPublishTime)
|
||||
{
|
||||
_mqttKey = mqttKey;
|
||||
_sensor = sensor;
|
||||
_storageSize = storageSize;
|
||||
_significantChange = significantChange;
|
||||
_minimumMeasureTime = minimumMeasureTime;
|
||||
_minimumPublishTime = minimumPublishTime;
|
||||
_mqtt = MQTT::Instance();
|
||||
_ui = UI::Instance();
|
||||
}
|
||||
|
||||
void SensorController::setup()
|
||||
|
@ -34,16 +35,52 @@ namespace Dough
|
|||
clearHistory();
|
||||
}
|
||||
|
||||
void SensorController::process()
|
||||
void SensorController::loop()
|
||||
{
|
||||
auto m = _sensor->read();
|
||||
_store(m);
|
||||
if (_mustMeasure())
|
||||
{
|
||||
_measure();
|
||||
}
|
||||
if (_mustPublish())
|
||||
{
|
||||
_publish();
|
||||
}
|
||||
}
|
||||
|
||||
bool SensorController::_mustMeasure()
|
||||
{
|
||||
// When no measurement was done yet, then do one now.
|
||||
if (_lastMeasuredAt == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// When enough time has passed since the last measurement,
|
||||
// then start another measurement.
|
||||
auto now = millis();
|
||||
auto delta = now - _lastMeasuredAt;
|
||||
return delta >= (_minimumMeasureTime * 1000);
|
||||
}
|
||||
|
||||
void SensorController::_measure()
|
||||
{
|
||||
_lastMeasuredAt = millis();
|
||||
|
||||
// Quickly dip the LED to indicate that a measurement has started.
|
||||
// This is done synchroneously, because we suspend the timer interrupts
|
||||
// in the upcoming code.
|
||||
_ui->led3.off();
|
||||
delay(50);
|
||||
_ui->led3.on();
|
||||
|
||||
// Read a measurement from the sensor. Suspend the user interface
|
||||
// interrupts in the meanwhile, to not disturb the timing-sensitive
|
||||
// sensor readings.
|
||||
_ui->suspend();
|
||||
_store(_sensor->read());
|
||||
_ui->resume();
|
||||
}
|
||||
|
||||
bool SensorController::_mustPublish()
|
||||
{
|
||||
Measurement lastMeasurement = getLast();
|
||||
|
@ -69,8 +106,10 @@ namespace Dough
|
|||
return _lastPublishedAt == 0 || delta >= (_minimumPublishTime * 1000);
|
||||
}
|
||||
|
||||
auto precision = _sensor->getPrecision();
|
||||
|
||||
// When there is a significant change in the sensor value, then publish.
|
||||
if (abs(_lastPublished.value - lastMeasurement.value) >= _significantChange)
|
||||
if (abs(_lastPublished.value - lastMeasurement.value) >= precision)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -78,7 +117,7 @@ namespace Dough
|
|||
auto average = getAverage();
|
||||
|
||||
// When there is a significant change in the average value, then publish.
|
||||
if (average.ok && abs(_lastPublishedAverage.value - average.value) >= _significantChange)
|
||||
if (average.ok && abs(_lastPublishedAverage.value - average.value) >= precision)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -162,4 +201,4 @@ namespace Dough
|
|||
_storage[i]->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Dough
|
|
@ -5,26 +5,31 @@
|
|||
#include "Sensors/SensorBase.h"
|
||||
#include "Data/Measurement.h"
|
||||
#include "Network/MQTT.h"
|
||||
#include "UI/UI.h"
|
||||
|
||||
namespace Dough
|
||||
{
|
||||
// This class is used to store measurements for a sensor and to keep
|
||||
// track of running totals for handling average computations.
|
||||
// It also provides functionality to decide when to publish measurements
|
||||
// to MQTT (after significant changes occur or when the last publish
|
||||
// was too long ago).
|
||||
// It also provides functionality to decide when to read a measurement
|
||||
// from a sensor and when to publish measurements to MQTT (after significant
|
||||
// changes occur or when the last publish was too long ago).
|
||||
class SensorController
|
||||
{
|
||||
public:
|
||||
// Create a new Measurements object.
|
||||
//
|
||||
// @param mqttKey
|
||||
// The key to use when publishing sensor values to MQTT.
|
||||
// The full key will be "<prefix>/<mqttKey>" for measurement values
|
||||
// and "<prefix>/<mqttKey>/average" for average values.
|
||||
// @param sensor
|
||||
// The sensor to read, implements SensorBase.
|
||||
// @param storageSize
|
||||
// Number of measurements to keep track of for computing an average.
|
||||
// @param significantChange
|
||||
// Number that describes how much a measurement value needs to change,
|
||||
// before it is considered significant and must be published to MQTT.
|
||||
// @param minimumMeasureTime
|
||||
// The number of seconds after which to read the next measurement
|
||||
// from the sensor.
|
||||
// @param minimumPublishTime
|
||||
// The number of seconds after which to forcibly publish measurements
|
||||
// to MQTT, even when no significant changes to measurements were seen.
|
||||
|
@ -32,34 +37,38 @@ namespace Dough
|
|||
const char *mqttKey,
|
||||
SensorBase *sensor,
|
||||
unsigned int storageSize,
|
||||
unsigned int significantChange,
|
||||
unsigned int minimumMeasureTime,
|
||||
unsigned int minimumPublishTime);
|
||||
void setup();
|
||||
void process();
|
||||
void loop();
|
||||
Measurement getLast();
|
||||
Measurement getAverage();
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
MQTT *_mqtt;
|
||||
UI *_ui;
|
||||
const char *_mqttKey;
|
||||
char *_mqttAverageKey;
|
||||
SensorBase *_sensor;
|
||||
Measurement **_storage;
|
||||
unsigned int _storageSize;
|
||||
unsigned int _significantChange;
|
||||
unsigned int _minimumPublishTime;
|
||||
int _averageSum = 0;
|
||||
unsigned int _averageCount = 0;
|
||||
unsigned int _index = 0;
|
||||
bool _mustMeasure();
|
||||
void _measure();
|
||||
unsigned int _minimumMeasureTime;
|
||||
unsigned long _lastMeasuredAt = 0;
|
||||
bool _mustPublish();
|
||||
void _publish();
|
||||
unsigned int _minimumPublishTime;
|
||||
unsigned long _lastPublishedAt = 0;
|
||||
Measurement _lastPublished;
|
||||
Measurement _lastPublishedAverage;
|
||||
bool _mustPublish();
|
||||
void _publish();
|
||||
void _store(Measurement measurement);
|
||||
unsigned int _next();
|
||||
};
|
||||
}
|
||||
} // namespace Dough
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,15 +7,10 @@ namespace Dough
|
|||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
MQTT *MQTT::_instance = nullptr;
|
||||
|
||||
MQTT *MQTT::Instance()
|
||||
{
|
||||
if (MQTT::_instance == nullptr)
|
||||
{
|
||||
MQTT::_instance = new MQTT();
|
||||
}
|
||||
return MQTT::_instance;
|
||||
static MQTT *_instance = new MQTT();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
MQTT::MQTT() : _logger("MQTT") {}
|
||||
|
@ -81,6 +76,8 @@ namespace Dough
|
|||
|
||||
void MQTT::procesIncomingsMessages()
|
||||
{
|
||||
// Calling loop() on the wrapped MQTT client, will fetch the
|
||||
// incoming messages and distribute them to the onMessage callback.
|
||||
_mqttClient.loop();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ namespace Dough
|
|||
|
||||
private:
|
||||
MQTT();
|
||||
static MQTT *_instance;
|
||||
MQTTClient _mqttClient;
|
||||
Logger _logger;
|
||||
MQTTConnectHandler _onConnect = nullptr;
|
||||
|
|
|
@ -6,15 +6,10 @@ namespace Dough
|
|||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
WiFi *WiFi::_instance = nullptr;
|
||||
|
||||
WiFi *WiFi::Instance()
|
||||
{
|
||||
if (WiFi::_instance == nullptr)
|
||||
{
|
||||
WiFi::_instance = new WiFi();
|
||||
}
|
||||
return WiFi::_instance;
|
||||
static WiFi *_instance = new WiFi();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
WiFi::WiFi() : _logger("WIFI") {}
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace Dough
|
|||
|
||||
private:
|
||||
WiFi();
|
||||
static WiFi *_instance;
|
||||
void _setMacAddress();
|
||||
char _macAddress[18]; // max MAC address length + 1
|
||||
Logger _logger;
|
||||
|
|
|
@ -6,15 +6,10 @@ namespace Dough
|
|||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
DistanceSensor *DistanceSensor::_instance = nullptr;
|
||||
|
||||
DistanceSensor *DistanceSensor::Instance()
|
||||
{
|
||||
if (DistanceSensor::_instance == nullptr)
|
||||
{
|
||||
DistanceSensor::_instance = new DistanceSensor();
|
||||
}
|
||||
return DistanceSensor::_instance;
|
||||
static DistanceSensor *_instance = new DistanceSensor();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
DistanceSensor::DistanceSensor() : _logger("DISTANCE")
|
||||
|
@ -59,4 +54,9 @@ namespace Dough
|
|||
return Measurement::Value(d);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int DistanceSensor::getPrecision()
|
||||
{
|
||||
return 3; // according to the sensor specifications
|
||||
}
|
||||
}
|
|
@ -14,14 +14,14 @@ namespace Dough
|
|||
{
|
||||
public:
|
||||
static DistanceSensor *Instance();
|
||||
virtual void setup();
|
||||
virtual Measurement read();
|
||||
void setTemperature(int temperature);
|
||||
void setHumidity(int humidity);
|
||||
virtual void setup();
|
||||
virtual Measurement read();
|
||||
virtual unsigned int getPrecision();
|
||||
|
||||
private:
|
||||
DistanceSensor();
|
||||
static DistanceSensor *_instance;
|
||||
Logger _logger;
|
||||
SensorHCSR04 *_hcsr04;
|
||||
};
|
||||
|
|
|
@ -6,15 +6,10 @@ namespace Dough
|
|||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
HumiditySensor *HumiditySensor::_instance = nullptr;
|
||||
|
||||
HumiditySensor *HumiditySensor::Instance()
|
||||
{
|
||||
if (HumiditySensor::_instance == nullptr)
|
||||
{
|
||||
HumiditySensor::_instance = new HumiditySensor();
|
||||
}
|
||||
return HumiditySensor::_instance;
|
||||
static HumiditySensor *_instance = new HumiditySensor();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
HumiditySensor::HumiditySensor() : _logger("HUMIDITY") {}
|
||||
|
@ -47,4 +42,9 @@ namespace Dough
|
|||
return Measurement::Value(int(t));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int HumiditySensor::getPrecision()
|
||||
{
|
||||
return 2; // prevent flapping when transitioning from value A to value B
|
||||
}
|
||||
}
|
|
@ -17,10 +17,10 @@ namespace Dough
|
|||
static HumiditySensor *Instance();
|
||||
virtual void setup();
|
||||
virtual Measurement read();
|
||||
virtual unsigned int getPrecision();
|
||||
|
||||
private:
|
||||
HumiditySensor();
|
||||
static HumiditySensor *_instance;
|
||||
Logger _logger;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,15 +6,13 @@ namespace Dough
|
|||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
SensorDHT11 *SensorDHT11::_instance = nullptr;
|
||||
|
||||
// I am using a singleton here, to make it possible to use the physical
|
||||
// DHT11 sensor from the two logical sensors TemperatureSensor and
|
||||
// HumiditySensor.
|
||||
SensorDHT11 *SensorDHT11::Instance()
|
||||
{
|
||||
if (SensorDHT11::_instance == nullptr)
|
||||
{
|
||||
SensorDHT11::_instance = new SensorDHT11();
|
||||
}
|
||||
return SensorDHT11::_instance;
|
||||
static SensorDHT11 *_instance = new SensorDHT11();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
SensorDHT11::SensorDHT11()
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace Dough
|
|||
|
||||
private:
|
||||
SensorDHT11();
|
||||
static SensorDHT11 *_instance;
|
||||
DHT *_dht;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Dough
|
|||
{
|
||||
SensorHCSR04::SensorHCSR04(int triggerPin, int echoPin) : _logger("HCSR04")
|
||||
{
|
||||
precision = 3;
|
||||
_triggerPin = triggerPin;
|
||||
_echoPin = echoPin;
|
||||
_temperature = HCSR04_INIT_TEMPERATURE;
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace Dough
|
|||
void setTemperature(int temperature);
|
||||
void setHumidity(int humidity);
|
||||
int readDistance();
|
||||
int precision;
|
||||
|
||||
private:
|
||||
Logger _logger;
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Dough
|
|||
public:
|
||||
virtual void setup();
|
||||
virtual Measurement read();
|
||||
virtual unsigned int getPrecision();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,15 +6,10 @@ namespace Dough
|
|||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
TemperatureSensor *TemperatureSensor::_instance = nullptr;
|
||||
|
||||
TemperatureSensor *TemperatureSensor::Instance()
|
||||
{
|
||||
if (TemperatureSensor::_instance == nullptr)
|
||||
{
|
||||
TemperatureSensor::_instance = new TemperatureSensor();
|
||||
}
|
||||
return TemperatureSensor::_instance;
|
||||
static TemperatureSensor *_instance = new TemperatureSensor();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
TemperatureSensor::TemperatureSensor() : _logger("TEMPERATURE") {}
|
||||
|
@ -47,4 +42,9 @@ namespace Dough
|
|||
return Measurement::Value(int(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int TemperatureSensor::getPrecision()
|
||||
{
|
||||
return 2; // prevent flapping when transitioning from value A to value B
|
||||
}
|
||||
} // namespace Dough
|
|
@ -17,10 +17,10 @@ namespace Dough
|
|||
static TemperatureSensor *Instance();
|
||||
virtual void setup();
|
||||
virtual Measurement read();
|
||||
virtual unsigned int getPrecision();
|
||||
|
||||
private:
|
||||
TemperatureSensor();
|
||||
static TemperatureSensor *_instance;
|
||||
Logger _logger;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Dough
|
||||
{
|
||||
// Constructor for a button instance.
|
||||
//
|
||||
// As a necessary evil, because of the way attachinterrupt() works in
|
||||
// Arduino, construction needs a bit of extra work to get the button
|
||||
// working. An interrupt service routine (ISR) function must be created
|
||||
|
|
Loading…
Reference in New Issue