From a6e7609d6675e5587a807b6e2c7ea88fa4fcba49 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Mon, 13 Jul 2020 15:48:13 +0200 Subject: [PATCH] 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). --- src/Data/DataController.cpp | 29 +++++++------- src/Data/DataController.h | 7 ---- src/Data/SensorController.cpp | 55 +++++++++++++++++++++++---- src/Data/SensorController.h | 35 ++++++++++------- src/Network/MQTT.cpp | 11 ++---- src/Network/MQTT.h | 1 - src/Network/WiFi.cpp | 9 +---- src/Network/WiFi.h | 1 - src/Sensors/DistanceSensor.cpp | 14 +++---- src/Sensors/DistanceSensor.h | 6 +-- src/Sensors/HumiditySensor.cpp | 14 +++---- src/Sensors/HumiditySensor.h | 2 +- src/Sensors/LowLevel/SensorDHT11.cpp | 12 +++--- src/Sensors/LowLevel/SensorDHT11.h | 1 - src/Sensors/LowLevel/SensorHCSR04.cpp | 1 + src/Sensors/LowLevel/SensorHCSR04.h | 1 + src/Sensors/SensorBase.h | 1 + src/Sensors/TemperatureSensor.cpp | 16 ++++---- src/Sensors/TemperatureSensor.h | 2 +- src/UI/Button.cpp | 1 + 20 files changed, 125 insertions(+), 94 deletions(-) diff --git a/src/Data/DataController.cpp b/src/Data/DataController.cpp index 95f6ad6..4fd07ff 100644 --- a/src/Data/DataController.cpp +++ b/src/Data/DataController.cpp @@ -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; } diff --git a/src/Data/DataController.h b/src/Data/DataController.h index aa9bad9..172e351 100644 --- a/src/Data/DataController.h +++ b/src/Data/DataController.h @@ -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; diff --git a/src/Data/SensorController.cpp b/src/Data/SensorController.cpp index c58f64c..12218f4 100644 --- a/src/Data/SensorController.cpp +++ b/src/Data/SensorController.cpp @@ -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(); } } -} \ No newline at end of file +} // namespace Dough \ No newline at end of file diff --git a/src/Data/SensorController.h b/src/Data/SensorController.h index d10063c..879442c 100644 --- a/src/Data/SensorController.h +++ b/src/Data/SensorController.h @@ -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 "/" for measurement values + // and "//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 diff --git a/src/Network/MQTT.cpp b/src/Network/MQTT.cpp index 74c4aaa..1a1d2bc 100644 --- a/src/Network/MQTT.cpp +++ b/src/Network/MQTT.cpp @@ -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(); } diff --git a/src/Network/MQTT.h b/src/Network/MQTT.h index 974ef6c..29e290b 100644 --- a/src/Network/MQTT.h +++ b/src/Network/MQTT.h @@ -34,7 +34,6 @@ namespace Dough private: MQTT(); - static MQTT *_instance; MQTTClient _mqttClient; Logger _logger; MQTTConnectHandler _onConnect = nullptr; diff --git a/src/Network/WiFi.cpp b/src/Network/WiFi.cpp index e4b6de4..a160126 100644 --- a/src/Network/WiFi.cpp +++ b/src/Network/WiFi.cpp @@ -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") {} diff --git a/src/Network/WiFi.h b/src/Network/WiFi.h index 9342704..a441ea1 100644 --- a/src/Network/WiFi.h +++ b/src/Network/WiFi.h @@ -21,7 +21,6 @@ namespace Dough private: WiFi(); - static WiFi *_instance; void _setMacAddress(); char _macAddress[18]; // max MAC address length + 1 Logger _logger; diff --git a/src/Sensors/DistanceSensor.cpp b/src/Sensors/DistanceSensor.cpp index 02af09a..3f6b020 100644 --- a/src/Sensors/DistanceSensor.cpp +++ b/src/Sensors/DistanceSensor.cpp @@ -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 + } } \ No newline at end of file diff --git a/src/Sensors/DistanceSensor.h b/src/Sensors/DistanceSensor.h index e527ef3..65bfd69 100644 --- a/src/Sensors/DistanceSensor.h +++ b/src/Sensors/DistanceSensor.h @@ -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; }; diff --git a/src/Sensors/HumiditySensor.cpp b/src/Sensors/HumiditySensor.cpp index 7420344..89ac573 100644 --- a/src/Sensors/HumiditySensor.cpp +++ b/src/Sensors/HumiditySensor.cpp @@ -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 + } } \ No newline at end of file diff --git a/src/Sensors/HumiditySensor.h b/src/Sensors/HumiditySensor.h index ff67084..8ed660c 100644 --- a/src/Sensors/HumiditySensor.h +++ b/src/Sensors/HumiditySensor.h @@ -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; }; } diff --git a/src/Sensors/LowLevel/SensorDHT11.cpp b/src/Sensors/LowLevel/SensorDHT11.cpp index f2837d7..b2c1418 100644 --- a/src/Sensors/LowLevel/SensorDHT11.cpp +++ b/src/Sensors/LowLevel/SensorDHT11.cpp @@ -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() diff --git a/src/Sensors/LowLevel/SensorDHT11.h b/src/Sensors/LowLevel/SensorDHT11.h index 7146d4d..1c39786 100644 --- a/src/Sensors/LowLevel/SensorDHT11.h +++ b/src/Sensors/LowLevel/SensorDHT11.h @@ -17,7 +17,6 @@ namespace Dough private: SensorDHT11(); - static SensorDHT11 *_instance; DHT *_dht; }; } diff --git a/src/Sensors/LowLevel/SensorHCSR04.cpp b/src/Sensors/LowLevel/SensorHCSR04.cpp index c8d6032..ed58f59 100644 --- a/src/Sensors/LowLevel/SensorHCSR04.cpp +++ b/src/Sensors/LowLevel/SensorHCSR04.cpp @@ -4,6 +4,7 @@ namespace Dough { SensorHCSR04::SensorHCSR04(int triggerPin, int echoPin) : _logger("HCSR04") { + precision = 3; _triggerPin = triggerPin; _echoPin = echoPin; _temperature = HCSR04_INIT_TEMPERATURE; diff --git a/src/Sensors/LowLevel/SensorHCSR04.h b/src/Sensors/LowLevel/SensorHCSR04.h index 084e469..c130520 100644 --- a/src/Sensors/LowLevel/SensorHCSR04.h +++ b/src/Sensors/LowLevel/SensorHCSR04.h @@ -43,6 +43,7 @@ namespace Dough void setTemperature(int temperature); void setHumidity(int humidity); int readDistance(); + int precision; private: Logger _logger; diff --git a/src/Sensors/SensorBase.h b/src/Sensors/SensorBase.h index b45b98b..873b9ab 100644 --- a/src/Sensors/SensorBase.h +++ b/src/Sensors/SensorBase.h @@ -11,6 +11,7 @@ namespace Dough public: virtual void setup(); virtual Measurement read(); + virtual unsigned int getPrecision(); }; } diff --git a/src/Sensors/TemperatureSensor.cpp b/src/Sensors/TemperatureSensor.cpp index 68ee33b..855eac2 100644 --- a/src/Sensors/TemperatureSensor.cpp +++ b/src/Sensors/TemperatureSensor.cpp @@ -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)); } } -} \ No newline at end of file + + unsigned int TemperatureSensor::getPrecision() + { + return 2; // prevent flapping when transitioning from value A to value B + } +} // namespace Dough \ No newline at end of file diff --git a/src/Sensors/TemperatureSensor.h b/src/Sensors/TemperatureSensor.h index ed3a728..b46de0a 100644 --- a/src/Sensors/TemperatureSensor.h +++ b/src/Sensors/TemperatureSensor.h @@ -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; }; } diff --git a/src/UI/Button.cpp b/src/UI/Button.cpp index 16c2d44..ab2d2ff 100644 --- a/src/UI/Button.cpp +++ b/src/UI/Button.cpp @@ -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