Moved measuring and publish into the Measurements class. Further code cleanup steps will follow on this, but committing since we have a nicely working version now.
This commit is contained in:
parent
cebbe092e9
commit
f1b941d964
|
@ -6,9 +6,6 @@
|
||||||
|
|
||||||
DataController *DataController::_instance = nullptr;
|
DataController *DataController::_instance = nullptr;
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the DoughData singleton.
|
|
||||||
*/
|
|
||||||
DataController *DataController::Instance()
|
DataController *DataController::Instance()
|
||||||
{
|
{
|
||||||
if (DataController::_instance == nullptr)
|
if (DataController::_instance == nullptr)
|
||||||
|
@ -18,9 +15,24 @@ DataController *DataController::Instance()
|
||||||
return DataController::_instance;
|
return DataController::_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataController::DataController() : _temperatureMeasurements(TEMPERATURE_AVG_LOOKBACK),
|
DataController::DataController() : _temperatureMeasurements(
|
||||||
_humidityMeasurements(HUMIDITY_AVG_LOOKBACK),
|
"temperature",
|
||||||
_distanceMeasurements(DISTANCE_AVG_LOOKBACK),
|
readTemperature,
|
||||||
|
TEMPERATURE_AVG_LOOKBACK,
|
||||||
|
TEMPERATURE_SIGNIFICANT_CHANGE,
|
||||||
|
PUBLISH_INTERVAL),
|
||||||
|
_humidityMeasurements(
|
||||||
|
"humidity",
|
||||||
|
readHumidity,
|
||||||
|
HUMIDITY_AVG_LOOKBACK,
|
||||||
|
HUMIDITY_SIGNIFICANT_CHANGE,
|
||||||
|
PUBLISH_INTERVAL),
|
||||||
|
_distanceMeasurements(
|
||||||
|
"distance",
|
||||||
|
readDistance,
|
||||||
|
DISTANCE_AVG_LOOKBACK,
|
||||||
|
DISTANCE_SIGNIFICANT_CHANGE,
|
||||||
|
PUBLISH_INTERVAL),
|
||||||
_logger("DATA")
|
_logger("DATA")
|
||||||
{
|
{
|
||||||
_ui = DoughUI::Instance();
|
_ui = DoughUI::Instance();
|
||||||
|
@ -100,7 +112,6 @@ void DataController::loop()
|
||||||
if (isConfigured())
|
if (isConfigured())
|
||||||
{
|
{
|
||||||
_sample();
|
_sample();
|
||||||
_publish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,52 +146,18 @@ void DataController::_sample()
|
||||||
_ui->suspend();
|
_ui->suspend();
|
||||||
|
|
||||||
// Take a sample.
|
// Take a sample.
|
||||||
Measurement m;
|
|
||||||
switch (_sampleType)
|
switch (_sampleType)
|
||||||
{
|
{
|
||||||
case SAMPLE_TEMPERATURE:
|
case SAMPLE_TEMPERATURE:
|
||||||
m = _sensors->readTemperature();
|
_temperatureMeasurements.process();
|
||||||
_temperatureMeasurements.add(m);
|
|
||||||
if (_temperatureLastPublished.ok && m.ok && _temperatureLastPublished.value != m.value)
|
|
||||||
{
|
|
||||||
if (m.value == _temperatureMeasurements.getAverage().value ||
|
|
||||||
abs(_temperatureLastPublished.value - m.value) > TEMPERATURE_SIGNIFICANT_CHANGE) {
|
|
||||||
_publishTemperature();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (_temperatureLastPublished.ok == false && m.ok) {
|
|
||||||
_publishTemperature();
|
|
||||||
}
|
|
||||||
_sampleType = SAMPLE_HUMIDITY;
|
_sampleType = SAMPLE_HUMIDITY;
|
||||||
break;
|
break;
|
||||||
case SAMPLE_HUMIDITY:
|
case SAMPLE_HUMIDITY:
|
||||||
m = _sensors->readHumidity();
|
_humidityMeasurements.process();
|
||||||
_humidityMeasurements.add(m);
|
|
||||||
if (_humidityLastPublished.ok && m.ok && _humidityLastPublished.value != m.value)
|
|
||||||
{
|
|
||||||
if (m.value == _humidityMeasurements.getAverage().value ||
|
|
||||||
abs(_humidityLastPublished.value - m.value) > HUMIDITY_SIGNIFICANT_CHANGE) {
|
|
||||||
_publishHumidity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (_humidityLastPublished.ok == false && m.ok) {
|
|
||||||
_publishHumidity();
|
|
||||||
}
|
|
||||||
_sampleType = SAMPLE_DISTANCE;
|
_sampleType = SAMPLE_DISTANCE;
|
||||||
break;
|
break;
|
||||||
case SAMPLE_DISTANCE:
|
case SAMPLE_DISTANCE:
|
||||||
m = _sensors->readDistance();
|
_distanceMeasurements.process();
|
||||||
_distanceMeasurements.add(m);
|
|
||||||
if (_distanceLastPublished.ok && m.ok && _distanceLastPublished.value != m.value)
|
|
||||||
{
|
|
||||||
if (m.value == _distanceMeasurements.getAverage().value ||
|
|
||||||
abs(_distanceLastPublished.value - m.value) > DISTANCE_SIGNIFICANT_CHANGE) {
|
|
||||||
_publishDistance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (_distanceLastPublished.ok == false && m.ok) {
|
|
||||||
_publishDistance();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,42 +171,3 @@ void DataController::_sample()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataController::_publish()
|
|
||||||
{
|
|
||||||
if (_lastPublish == 0 || millis() - _lastPublish > PUBLISH_INTERVAL)
|
|
||||||
{
|
|
||||||
_lastPublish = millis();
|
|
||||||
_publishTemperature();
|
|
||||||
_publishHumidity();
|
|
||||||
_publishDistance();
|
|
||||||
_ui->led1.dip()->fast();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataController::_publishTemperature()
|
|
||||||
{
|
|
||||||
auto m = _temperatureMeasurements.getLast();
|
|
||||||
_temperatureLastPublished.ok = m.ok;
|
|
||||||
_temperatureLastPublished.value = m.value;
|
|
||||||
_mqtt->publish("temperature", m);
|
|
||||||
_mqtt->publish("temperature/average", _temperatureMeasurements.getAverage());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataController::_publishHumidity()
|
|
||||||
{
|
|
||||||
auto m = _humidityMeasurements.getLast();
|
|
||||||
_humidityLastPublished.ok = m.ok;
|
|
||||||
_humidityLastPublished.value = m.value;
|
|
||||||
_mqtt->publish("humidity", _humidityMeasurements.getLast());
|
|
||||||
_mqtt->publish("humidity/average", _humidityMeasurements.getAverage());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataController::_publishDistance()
|
|
||||||
{
|
|
||||||
auto m = _distanceMeasurements.getLast();
|
|
||||||
_distanceLastPublished.ok = m.ok;
|
|
||||||
_distanceLastPublished.value = m.value;
|
|
||||||
_mqtt->publish("distance", _distanceMeasurements.getLast());
|
|
||||||
_mqtt->publish("distance/average", _distanceMeasurements.getAverage());
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@
|
||||||
// in the average computation.
|
// in the average computation.
|
||||||
#define TEMPERATURE_AVG_LOOKBACK 6 // making this a 3 minute average
|
#define TEMPERATURE_AVG_LOOKBACK 6 // making this a 3 minute average
|
||||||
#define HUMIDITY_AVG_LOOKBACK 6 // making this a 3 minute average
|
#define HUMIDITY_AVG_LOOKBACK 6 // making this a 3 minute average
|
||||||
#define DISTANCE_AVG_LOOKBACK 28 * 2 * 5 // making this a 5 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
|
// When significant changes occur in the sensor measurements, they are
|
||||||
// published to MQTT. These definitions specify what is considered significant.
|
// published to MQTT. These definitions specify what is considered significant.
|
||||||
|
@ -22,10 +22,10 @@
|
||||||
#define HUMIDITY_SIGNIFICANT_CHANGE 2 // also to dampen flapping behavior.
|
#define HUMIDITY_SIGNIFICANT_CHANGE 2 // also to dampen flapping behavior.
|
||||||
#define DISTANCE_SIGNIFICANT_CHANGE 3 // based on the sensor specification of 3mm resolution
|
#define DISTANCE_SIGNIFICANT_CHANGE 3 // based on the sensor specification of 3mm resolution
|
||||||
|
|
||||||
// The minimal interval at which to forcibly publish measurements to the MQTT broker.
|
// The minimal interval in seconds at which to forcibly publish measurements to the
|
||||||
// When significant changes occur in the measurements, then these will be published
|
// MQTT broker. When significant changes occur in the measurements, then these will
|
||||||
// to the MQTT broker at all times, independent from this interval.
|
// be published to the MQTT broker at all times, independent from this interval.
|
||||||
#define PUBLISH_INTERVAL 4000
|
#define PUBLISH_INTERVAL 300
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "Data/Measurements.h"
|
#include "Data/Measurements.h"
|
||||||
|
@ -62,27 +62,19 @@ public:
|
||||||
private:
|
private:
|
||||||
DataController();
|
DataController();
|
||||||
static DataController *_instance;
|
static DataController *_instance;
|
||||||
Measurements _temperatureMeasurements;
|
|
||||||
Measurement _temperatureLastPublished;
|
|
||||||
void _publishTemperature();
|
|
||||||
Measurements _humidityMeasurements;
|
|
||||||
Measurement _humidityLastPublished;
|
|
||||||
void _publishHumidity();
|
|
||||||
Measurements _distanceMeasurements;
|
|
||||||
Measurement _distanceLastPublished;
|
|
||||||
void _publishDistance();
|
|
||||||
DoughLogger _logger;
|
|
||||||
DoughUI *_ui;
|
DoughUI *_ui;
|
||||||
DoughSensors* _sensors;
|
|
||||||
DoughMQTT *_mqtt;
|
DoughMQTT *_mqtt;
|
||||||
|
DoughSensors *_sensors;
|
||||||
|
Measurements _temperatureMeasurements;
|
||||||
|
Measurements _humidityMeasurements;
|
||||||
|
Measurements _distanceMeasurements;
|
||||||
|
DoughLogger _logger;
|
||||||
unsigned long _lastSample = 0;
|
unsigned long _lastSample = 0;
|
||||||
unsigned long _lastPublish = 0;
|
|
||||||
DoughSampleType _sampleType = SAMPLE_TEMPERATURE;
|
DoughSampleType _sampleType = SAMPLE_TEMPERATURE;
|
||||||
int _sampleCounter = 0;
|
int _sampleCounter = 0;
|
||||||
int _containerHeight;
|
int _containerHeight;
|
||||||
bool _containerHeightSet;
|
bool _containerHeightSet;
|
||||||
void _sample();
|
void _sample();
|
||||||
void _publish();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,3 +15,15 @@ Measurement Measurement::Value(int value)
|
||||||
m.value = value;
|
m.value = value;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Measurement::clear()
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Measurement::copyTo(Measurement* target)
|
||||||
|
{
|
||||||
|
target->ok = ok;
|
||||||
|
target->value = value;
|
||||||
|
}
|
|
@ -13,6 +13,8 @@ public:
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
static Measurement Failed();
|
static Measurement Failed();
|
||||||
static Measurement Value(int value);
|
static Measurement Value(int value);
|
||||||
|
void clear();
|
||||||
|
void copyTo(Measurement *target);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,22 +1,111 @@
|
||||||
#include "Data/Measurements.h"
|
#include "Data/Measurements.h"
|
||||||
#include "UI/DoughUI.h"
|
#include "UI/DoughUI.h"
|
||||||
|
|
||||||
Measurements::Measurements(unsigned int avgLookback)
|
Measurements::Measurements(
|
||||||
|
const char *mqttKey,
|
||||||
|
Measurement (*measureFunc)(),
|
||||||
|
unsigned int storageSize,
|
||||||
|
unsigned int significantChange,
|
||||||
|
unsigned int minimumPublishTime)
|
||||||
{
|
{
|
||||||
_storageSize = avgLookback;
|
_mqttKey = mqttKey;
|
||||||
|
_measureFunc = measureFunc;
|
||||||
|
_storageSize = storageSize;
|
||||||
|
_significantChange = significantChange;
|
||||||
|
_minimumPublishTime = minimumPublishTime;
|
||||||
|
_mqtt = DoughMQTT::Instance();
|
||||||
|
|
||||||
_storage = new Measurement*[avgLookback];
|
// Format the key to use for publishing the average (i.e. "<mqttKey>/average").
|
||||||
for (unsigned int i = 0; i < avgLookback; i++) {
|
auto lenAverageKey = strlen(mqttKey) + 8; // +8 for the "/average" suffix
|
||||||
|
_mqttAverageKey = new char[lenAverageKey + 1]; // +1 for the ending \0
|
||||||
|
snprintf(_mqttAverageKey, lenAverageKey, "%s/average", _mqttKey);
|
||||||
|
|
||||||
|
// Initialize the storage for holding the measurements.
|
||||||
|
_storage = new Measurement *[storageSize];
|
||||||
|
for (unsigned int i = 0; i < storageSize; i++)
|
||||||
|
{
|
||||||
_storage[i] = new Measurement;
|
_storage[i] = new Measurement;
|
||||||
}
|
}
|
||||||
clearHistory();
|
clearHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Measurements::add(Measurement measurement)
|
void Measurements::process()
|
||||||
{
|
{
|
||||||
unsigned int index = _next();
|
auto m = _measureFunc();
|
||||||
_storage[index]->ok = measurement.ok;
|
_add(m);
|
||||||
_storage[index]->value = measurement.value;
|
if (_mustPublish())
|
||||||
|
{
|
||||||
|
_publish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Measurements::_mustPublish()
|
||||||
|
{
|
||||||
|
Measurement lastMeasurement = getLast();
|
||||||
|
|
||||||
|
// When the measurement failed, then there's no need to publish it.
|
||||||
|
if (lastMeasurement.ok == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When no data was published before, then this is a great time to do so.
|
||||||
|
if (_lastPublished.ok == false)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value did not change, only publish when the minimum publishing
|
||||||
|
// time has passed.
|
||||||
|
if (_lastPublished.value == lastMeasurement.value)
|
||||||
|
{
|
||||||
|
auto now = millis();
|
||||||
|
auto delta = now - _lastPublishedAt;
|
||||||
|
return _lastPublishedAt == 0 || delta >= (_minimumPublishTime * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When there is a significant change in the sensor value, then publish.
|
||||||
|
if (abs(_lastPublished.value - lastMeasurement.value) >= _significantChange)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto average = getAverage();
|
||||||
|
|
||||||
|
// When there is a significant change in the average value, then publish.
|
||||||
|
if (average.ok && abs(_lastPublishedAverage.value - average.value) >= _significantChange)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the value changed less than the significant change, but it reached
|
||||||
|
// the current average value, then publish it, since we might have reached
|
||||||
|
// a stable value.
|
||||||
|
if (average.ok && average.value == lastMeasurement.value)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Well, we're out of options. No reason to publish the data right now.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Measurements::_publish()
|
||||||
|
{
|
||||||
|
auto average = getAverage();
|
||||||
|
auto last = getLast();
|
||||||
|
|
||||||
|
_mqtt->publish(_mqttKey, last);
|
||||||
|
_mqtt->publish(_mqttAverageKey, average);
|
||||||
|
|
||||||
|
_lastPublishedAt = millis();
|
||||||
|
average.copyTo(&_lastPublishedAverage);
|
||||||
|
last.copyTo(&_lastPublished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Measurements::_add(Measurement measurement)
|
||||||
|
{
|
||||||
|
measurement.copyTo(_storage[_next()]);
|
||||||
|
|
||||||
if (measurement.ok)
|
if (measurement.ok)
|
||||||
{
|
{
|
||||||
|
@ -28,17 +117,22 @@ void Measurements::add(Measurement measurement)
|
||||||
unsigned int Measurements::_next()
|
unsigned int Measurements::_next()
|
||||||
{
|
{
|
||||||
_index++;
|
_index++;
|
||||||
|
|
||||||
|
// Wrap around at the end of the circular buffer.
|
||||||
if (_index == _storageSize)
|
if (_index == _storageSize)
|
||||||
{
|
{
|
||||||
_index = 0;
|
_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the new position contains an ok value, update the running totals.
|
||||||
if (_storage[_index]->ok)
|
if (_storage[_index]->ok)
|
||||||
{
|
{
|
||||||
_averageSum -= _storage[_index]->value;
|
_averageSum -= _storage[_index]->value;
|
||||||
_averageCount--;
|
_averageCount--;
|
||||||
}
|
}
|
||||||
_storage[_index]->ok = false;
|
|
||||||
_storage[_index]->value = 0;
|
_storage[_index]->clear();
|
||||||
|
|
||||||
return _index;
|
return _index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,13 +143,9 @@ Measurement Measurements::getLast()
|
||||||
|
|
||||||
Measurement Measurements::getAverage()
|
Measurement Measurements::getAverage()
|
||||||
{
|
{
|
||||||
Measurement result;
|
return _averageCount > 0
|
||||||
if (_averageCount > 0)
|
? Measurement::Value(round(_averageSum / _averageCount))
|
||||||
{
|
: Measurement::Failed();
|
||||||
result.ok = true;
|
|
||||||
result.value = round(_averageSum / _averageCount);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Measurements::clearHistory()
|
void Measurements::clearHistory()
|
||||||
|
@ -64,7 +154,6 @@ void Measurements::clearHistory()
|
||||||
_averageSum = 0;
|
_averageSum = 0;
|
||||||
for (unsigned int i = 0; i < _storageSize; i++)
|
for (unsigned int i = 0; i < _storageSize; i++)
|
||||||
{
|
{
|
||||||
_storage[i]->ok = false;
|
_storage[i]->clear();
|
||||||
_storage[i]->value = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,26 +3,61 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "Data/Measurement.h"
|
#include "Data/Measurement.h"
|
||||||
|
#include "Network/DoughMQTT.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to store measurements for a sensor and to keep
|
* This class is used to store measurements for a sensor and to keep
|
||||||
* track of running totals for handling average computations.
|
* 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).
|
||||||
*/
|
*/
|
||||||
class Measurements
|
class Measurements
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Measurements(unsigned int avgLookback);
|
/**
|
||||||
void add(Measurement measurement);
|
* Create a new Measurements object.
|
||||||
|
*
|
||||||
|
* @param measureFunc
|
||||||
|
* Function that reads a sensor and returns a Measurement object.
|
||||||
|
* @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 minimumPublishTime
|
||||||
|
* The number of seconds after which to forcibly publish measurements
|
||||||
|
* to MQTT, even when no significant changes to measurements were seen.
|
||||||
|
*/
|
||||||
|
Measurements(
|
||||||
|
const char *mqttKey,
|
||||||
|
Measurement (*measureFunc)(),
|
||||||
|
unsigned int storageSize,
|
||||||
|
unsigned int significantChange,
|
||||||
|
unsigned int minimumPublishTime);
|
||||||
|
void process();
|
||||||
Measurement getLast();
|
Measurement getLast();
|
||||||
Measurement getAverage();
|
Measurement getAverage();
|
||||||
void clearHistory();
|
void clearHistory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
DoughMQTT *_mqtt;
|
||||||
|
const char *_mqttKey;
|
||||||
|
char *_mqttAverageKey;
|
||||||
|
Measurement (*_measureFunc)();
|
||||||
Measurement **_storage;
|
Measurement **_storage;
|
||||||
unsigned int _storageSize;
|
unsigned int _storageSize;
|
||||||
|
unsigned int _significantChange;
|
||||||
|
unsigned int _minimumPublishTime;
|
||||||
int _averageSum = 0;
|
int _averageSum = 0;
|
||||||
unsigned int _averageCount = 0;
|
unsigned int _averageCount = 0;
|
||||||
unsigned int _index = 0;
|
unsigned int _index = 0;
|
||||||
|
unsigned long _lastPublishedAt = 0;
|
||||||
|
Measurement _lastPublished;
|
||||||
|
Measurement _lastPublishedAverage;
|
||||||
|
bool _mustPublish();
|
||||||
|
void _publish();
|
||||||
|
void _add(Measurement measurement);
|
||||||
unsigned int _next();
|
unsigned int _next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
|
|
||||||
DoughMQTT *DoughMQTT::_instance = nullptr;
|
DoughMQTT *DoughMQTT::_instance = nullptr;
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the DoughMQTT singleton.
|
|
||||||
*/
|
|
||||||
DoughMQTT *DoughMQTT::Instance()
|
DoughMQTT *DoughMQTT::Instance()
|
||||||
{
|
{
|
||||||
if (DoughMQTT::_instance == nullptr)
|
if (DoughMQTT::_instance == nullptr)
|
||||||
|
@ -123,10 +120,14 @@ void DoughMQTT::publish(const char *key, int payload)
|
||||||
publish(key, buf);
|
publish(key, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoughMQTT::publish(const char *key, Measurement measurement) {
|
void DoughMQTT::publish(const char *key, Measurement measurement)
|
||||||
if (measurement.ok) {
|
{
|
||||||
|
if (measurement.ok)
|
||||||
|
{
|
||||||
publish(key, measurement.value);
|
publish(key, measurement.value);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
publish(key, "null");
|
publish(key, "null");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,9 +6,6 @@
|
||||||
|
|
||||||
DoughWiFi *DoughWiFi::_instance = nullptr;
|
DoughWiFi *DoughWiFi::_instance = nullptr;
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the DoughWiFi singleton.
|
|
||||||
*/
|
|
||||||
DoughWiFi *DoughWiFi::Instance()
|
DoughWiFi *DoughWiFi::Instance()
|
||||||
{
|
{
|
||||||
if (DoughWiFi::_instance == nullptr)
|
if (DoughWiFi::_instance == nullptr)
|
||||||
|
|
|
@ -6,17 +6,17 @@
|
||||||
|
|
||||||
DoughSensors *DoughSensors::_instance = nullptr;
|
DoughSensors *DoughSensors::_instance = nullptr;
|
||||||
|
|
||||||
/**
|
DoughSensors *DoughSensors::Instance()
|
||||||
* Fetch the DoughSensors singleton.
|
{
|
||||||
*/
|
if (DoughSensors::_instance == nullptr)
|
||||||
DoughSensors* DoughSensors::Instance() {
|
{
|
||||||
if (DoughSensors::_instance == nullptr) {
|
|
||||||
DoughSensors::_instance = new DoughSensors();
|
DoughSensors::_instance = new DoughSensors();
|
||||||
}
|
}
|
||||||
return DoughSensors::_instance;
|
return DoughSensors::_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
DoughSensors::DoughSensors() : _logger("SENSORS") {
|
DoughSensors::DoughSensors() : _logger("SENSORS")
|
||||||
|
{
|
||||||
_dht = new DHT(DHT11_DATA_PIN, DHT11);
|
_dht = new DHT(DHT11_DATA_PIN, DHT11);
|
||||||
_hcsr04 = new HCSR04(HCSR04_TRIG_PIN, HCSR04_ECHO_PIN);
|
_hcsr04 = new HCSR04(HCSR04_TRIG_PIN, HCSR04_ECHO_PIN);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@ DoughSensors::DoughSensors() : _logger("SENSORS") {
|
||||||
// setup
|
// setup
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
void DoughSensors::setup() {
|
void DoughSensors::setup()
|
||||||
|
{
|
||||||
_dht->begin();
|
_dht->begin();
|
||||||
_hcsr04->begin();
|
_hcsr04->begin();
|
||||||
}
|
}
|
||||||
|
@ -34,12 +35,16 @@ void DoughSensors::setup() {
|
||||||
// loop
|
// loop
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
Measurement DoughSensors::readTemperature() {
|
Measurement DoughSensors::readTemperature()
|
||||||
|
{
|
||||||
float t = _dht->readTemperature();
|
float t = _dht->readTemperature();
|
||||||
if (isnan(t)) {
|
if (isnan(t))
|
||||||
|
{
|
||||||
_logger.log("s", "ERROR - Temperature measurement failed");
|
_logger.log("s", "ERROR - Temperature measurement failed");
|
||||||
return Measurement::Failed();
|
return Measurement::Failed();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_logger.log("sis", "Temperature = ", int(t), "°C ");
|
_logger.log("sis", "Temperature = ", int(t), "°C ");
|
||||||
_hcsr04->setTemperature(int(t));
|
_hcsr04->setTemperature(int(t));
|
||||||
auto m = Measurement::Value(int(t));
|
auto m = Measurement::Value(int(t));
|
||||||
|
@ -47,25 +52,52 @@ Measurement DoughSensors::readTemperature() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Measurement DoughSensors::readHumidity() {
|
Measurement DoughSensors::readHumidity()
|
||||||
|
{
|
||||||
int h = _dht->readHumidity();
|
int h = _dht->readHumidity();
|
||||||
if (h == 0) {
|
if (h == 0)
|
||||||
|
{
|
||||||
_logger.log("s", "ERROR - Humidity measurement failed");
|
_logger.log("s", "ERROR - Humidity measurement failed");
|
||||||
return Measurement::Failed();
|
return Measurement::Failed();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_logger.log("sis", "Humidity = ", h, "%");
|
_logger.log("sis", "Humidity = ", h, "%");
|
||||||
_hcsr04->setHumidity(h);
|
_hcsr04->setHumidity(h);
|
||||||
return Measurement::Value(h);
|
return Measurement::Value(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Measurement DoughSensors::readDistance() {
|
Measurement DoughSensors::readDistance()
|
||||||
|
{
|
||||||
int d = _hcsr04->readDistance();
|
int d = _hcsr04->readDistance();
|
||||||
if (d == -1) {
|
if (d == -1)
|
||||||
|
{
|
||||||
_logger.log("s", "ERROR - Distance measurement failed");
|
_logger.log("s", "ERROR - Distance measurement failed");
|
||||||
return Measurement::Failed();
|
return Measurement::Failed();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_logger.log("sis", "Distance = ", d, "mm");
|
_logger.log("sis", "Distance = ", d, "mm");
|
||||||
return Measurement::Value(d);
|
return Measurement::Value(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Function access to the sensor reading.
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Measurement readTemperature()
|
||||||
|
{
|
||||||
|
return DoughSensors::Instance()->readTemperature();
|
||||||
|
}
|
||||||
|
|
||||||
|
Measurement readHumidity()
|
||||||
|
{
|
||||||
|
return DoughSensors::Instance()->readHumidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
Measurement readDistance()
|
||||||
|
{
|
||||||
|
return DoughSensors::Instance()->readDistance();
|
||||||
|
}
|
|
@ -10,7 +10,8 @@
|
||||||
/**
|
/**
|
||||||
* This class provides access to the sensors in the device.
|
* This class provides access to the sensors in the device.
|
||||||
*/
|
*/
|
||||||
class DoughSensors {
|
class DoughSensors
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
static DoughSensors *Instance();
|
static DoughSensors *Instance();
|
||||||
void setup();
|
void setup();
|
||||||
|
@ -26,4 +27,8 @@ class DoughSensors {
|
||||||
HCSR04 *_hcsr04;
|
HCSR04 *_hcsr04;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Measurement readTemperature();
|
||||||
|
Measurement readHumidity();
|
||||||
|
Measurement readDistance();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
#include "Sensors/HCSR04.h"
|
#include "Sensors/HCSR04.h"
|
||||||
|
|
||||||
HCSR04::HCSR04(int triggerPin, int echoPin) {
|
HCSR04::HCSR04(int triggerPin, int echoPin)
|
||||||
|
{
|
||||||
_triggerPin = triggerPin;
|
_triggerPin = triggerPin;
|
||||||
_echoPin = echoPin;
|
_echoPin = echoPin;
|
||||||
_temperature = HCSR04_INIT_TEMPERATURE;
|
_temperature = HCSR04_INIT_TEMPERATURE;
|
||||||
_humidity = HCSR04_INIT_HUMIDITY;
|
_humidity = HCSR04_INIT_HUMIDITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HCSR04::begin() {
|
void HCSR04::begin()
|
||||||
|
{
|
||||||
pinMode(_triggerPin, OUTPUT);
|
pinMode(_triggerPin, OUTPUT);
|
||||||
pinMode(_echoPin, INPUT);
|
pinMode(_echoPin, INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HCSR04::setTemperature(int temperature) {
|
void HCSR04::setTemperature(int temperature)
|
||||||
|
{
|
||||||
_temperature = temperature;
|
_temperature = temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HCSR04::setHumidity(int humidity) {
|
void HCSR04::setHumidity(int humidity)
|
||||||
|
{
|
||||||
_humidity = humidity;
|
_humidity = humidity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,11 +29,13 @@ void HCSR04::setHumidity(int humidity) {
|
||||||
* When reading the distance fails, -1 is returned.
|
* When reading the distance fails, -1 is returned.
|
||||||
* Otherwise the distance in mm.
|
* Otherwise the distance in mm.
|
||||||
*/
|
*/
|
||||||
int HCSR04::readDistance() {
|
int HCSR04::readDistance()
|
||||||
|
{
|
||||||
_setSpeedOfSound();
|
_setSpeedOfSound();
|
||||||
_setEchoTimeout();
|
_setEchoTimeout();
|
||||||
_takeSamples();
|
_takeSamples();
|
||||||
if (_haveEnoughSamples()) {
|
if (_haveEnoughSamples())
|
||||||
|
{
|
||||||
_sortSamples();
|
_sortSamples();
|
||||||
return _computeAverage();
|
return _computeAverage();
|
||||||
}
|
}
|
||||||
|
@ -41,39 +47,47 @@ int HCSR04::readDistance() {
|
||||||
* and relative humidity. I derived this formula from a YouTube
|
* and relative humidity. I derived this formula from a YouTube
|
||||||
* video about the HC-SR04: https://youtu.be/6F1B_N6LuKw?t=1548
|
* video about the HC-SR04: https://youtu.be/6F1B_N6LuKw?t=1548
|
||||||
*/
|
*/
|
||||||
void HCSR04::_setSpeedOfSound() {
|
void HCSR04::_setSpeedOfSound()
|
||||||
|
{
|
||||||
_speedOfSound =
|
_speedOfSound =
|
||||||
0.3314 +
|
0.3314 +
|
||||||
(0.000606 * _temperature) +
|
(0.000606 * _temperature) +
|
||||||
(0.0000124 * _humidity);
|
(0.0000124 * _humidity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HCSR04::_setEchoTimeout() {
|
void HCSR04::_setEchoTimeout()
|
||||||
|
{
|
||||||
_echoTimeout = HCSR04_MAX_MM * 2 / _speedOfSound;
|
_echoTimeout = HCSR04_MAX_MM * 2 / _speedOfSound;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HCSR04::_takeSamples() {
|
void HCSR04::_takeSamples()
|
||||||
|
{
|
||||||
_successfulSamples = 0;
|
_successfulSamples = 0;
|
||||||
for (int i = 0; i<HCSR04_SAMPLES_TAKE; i++) {
|
for (int i = 0; i < HCSR04_SAMPLES_TAKE; i++)
|
||||||
|
{
|
||||||
// Because I notice some repeating patterns in timings when doing
|
// Because I notice some repeating patterns in timings when doing
|
||||||
// a tight loop here, I add some random waits to get a better spread
|
// a tight loop here, I add some random waits to get a better spread
|
||||||
// of sample values.
|
// of sample values.
|
||||||
if (i > 0) {
|
if (i > 0)
|
||||||
|
{
|
||||||
delay(HCSR04_SAMPLE_WAIT + random(HCSR04_SAMPLE_WAIT_SPREAD));
|
delay(HCSR04_SAMPLE_WAIT + random(HCSR04_SAMPLE_WAIT_SPREAD));
|
||||||
}
|
}
|
||||||
int distance = _takeSample();
|
int distance = _takeSample();
|
||||||
if (distance != -1) {
|
if (distance != -1)
|
||||||
|
{
|
||||||
_samples[i] = distance;
|
_samples[i] = distance;
|
||||||
_successfulSamples++;
|
_successfulSamples++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HCSR04::_haveEnoughSamples() {
|
bool HCSR04::_haveEnoughSamples()
|
||||||
|
{
|
||||||
return _successfulSamples >= HCSR04_SAMPLES_USE;
|
return _successfulSamples >= HCSR04_SAMPLES_USE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HCSR04::_takeSample() {
|
int HCSR04::_takeSample()
|
||||||
|
{
|
||||||
// Send 10μs trigger to ask sensor for a measurement.
|
// Send 10μs trigger to ask sensor for a measurement.
|
||||||
digitalWrite(HCSR04_TRIG_PIN, LOW);
|
digitalWrite(HCSR04_TRIG_PIN, LOW);
|
||||||
delayMicroseconds(2);
|
delayMicroseconds(2);
|
||||||
|
@ -86,18 +100,25 @@ int HCSR04::_takeSample() {
|
||||||
|
|
||||||
// Compute the distance, based on the echo signal length.
|
// Compute the distance, based on the echo signal length.
|
||||||
double distance = durationMicroSec / 2.0 * _speedOfSound;
|
double distance = durationMicroSec / 2.0 * _speedOfSound;
|
||||||
if (distance < HCSR04_MIN_MM || distance >= HCSR04_MAX_MM) {
|
if (distance < HCSR04_MIN_MM || distance >= HCSR04_MAX_MM)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return distance;
|
return distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HCSR04::_sortSamples() {
|
void HCSR04::_sortSamples()
|
||||||
|
{
|
||||||
int holder, x, y;
|
int holder, x, y;
|
||||||
for(x = 0; x < _successfulSamples; x++) {
|
for (x = 0; x < _successfulSamples; x++)
|
||||||
for(y = 0; y < _successfulSamples-1; y++) {
|
{
|
||||||
if(_samples[y] > _samples[y+1]) {
|
for (y = 0; y < _successfulSamples - 1; y++)
|
||||||
|
{
|
||||||
|
if (_samples[y] > _samples[y + 1])
|
||||||
|
{
|
||||||
holder = _samples[y + 1];
|
holder = _samples[y + 1];
|
||||||
_samples[y + 1] = _samples[y];
|
_samples[y + 1] = _samples[y];
|
||||||
_samples[y] = holder;
|
_samples[y] = holder;
|
||||||
|
@ -112,10 +133,12 @@ void HCSR04::_sortSamples() {
|
||||||
* When not enough samples were collected in the previous steps, then
|
* When not enough samples were collected in the previous steps, then
|
||||||
* NAN is returned.
|
* NAN is returned.
|
||||||
*/
|
*/
|
||||||
int HCSR04::_computeAverage() {
|
int HCSR04::_computeAverage()
|
||||||
|
{
|
||||||
float sum = 0;
|
float sum = 0;
|
||||||
int offset = (_successfulSamples - HCSR04_SAMPLES_USE) / 2;
|
int offset = (_successfulSamples - HCSR04_SAMPLES_USE) / 2;
|
||||||
for (int i = 0; i<HCSR04_SAMPLES_USE; i++) {
|
for (int i = 0; i < HCSR04_SAMPLES_USE; i++)
|
||||||
|
{
|
||||||
sum += _samples[i + offset];
|
sum += _samples[i + offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
/**
|
/**
|
||||||
* This class is used to get a distance reading from an HCSR04 sensor.
|
* This class is used to get a distance reading from an HCSR04 sensor.
|
||||||
*/
|
*/
|
||||||
class HCSR04 {
|
class HCSR04
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
HCSR04(int triggerPin, int echoPin);
|
HCSR04(int triggerPin, int echoPin);
|
||||||
void begin();
|
void begin();
|
||||||
|
|
Loading…
Reference in New Issue