From e48e44f287a56c3716527f86946c16a8db1d8d52 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Mon, 20 Jul 2020 02:13:10 +0200 Subject: [PATCH] Implemented the calibration mode and introduced a 'height' value in the mqtt stream, which is (container height - distance measurement). First implementation, so much might still change. I'm thinking about introducing a virtual sensor, but I'm not sure yet what the cleanest implementation will look like. --- src/App/App.cpp | 130 +++++++++++++++++++++++-------- src/App/App.h | 4 + src/Network/MQTT.cpp | 39 ++++++---- src/Network/MQTT.h | 6 +- src/Sensors/SensorController.cpp | 18 ++++- src/Sensors/SensorController.h | 9 ++- 6 files changed, 148 insertions(+), 58 deletions(-) diff --git a/src/App/App.cpp b/src/App/App.cpp index d63a91b..f753ed8 100644 --- a/src/App/App.cpp +++ b/src/App/App.cpp @@ -45,16 +45,8 @@ namespace Dough void App::loop() { - if (!wifi.isConnected()) + if (!_setupNetworking()) { - state.setWiFiConnected(false); - state.setWiFiConnected(wifi.connect()); - return; - } - if (!mqtt.isConnected()) - { - state.setMQTTConnected(false); - state.setMQTTConnected(mqtt.connect()); return; } @@ -64,33 +56,13 @@ namespace Dough switch (state.get()) { case CONFIGURING: - if (config.isOk()) - { - state.startMeasurements(); - } + _doConfigure(); break; case MEASURING: - if (config.isOk()) - { - if (temperatureController.loop()) - distanceSensor.setTemperature(temperatureController.getLast().value); - if (humidityController.loop()) - distanceSensor.setHumidity(humidityController.getLast().value); - distanceController.loop(); - } - else - { - state.startConfiguration(); - } + _doMeasure(); break; case CALIBRATING: - temperatureController.loop(); - humidityController.loop(); - distanceController.loop(); - - delay(2000); - state.pauseDevice(); - state.startMeasurements(); + _doCalibrate(); break; case PAUSED: temperatureController.clearHistory(); @@ -102,4 +74,98 @@ namespace Dough break; } } + + bool App::_setupNetworking() + { + if (!wifi.isConnected()) + { + state.setWiFiConnected(false); + state.setWiFiConnected(wifi.connect()); + return false; + } + if (!mqtt.isConnected()) + { + state.setMQTTConnected(false); + state.setMQTTConnected(mqtt.connect()); + return false; + } + return true; + } + + void App::_doConfigure() + { + if (config.isOk()) + { + state.startMeasurements(); + } + } + + void App::_doMeasure() + { + if (config.isOk()) + { + if (temperatureController.loop()) + distanceSensor.setTemperature(temperatureController.getLast().value); + if (humidityController.loop()) + distanceSensor.setHumidity(humidityController.getLast().value); + if (distanceController.loop()) + mqtt.publish("height", config.getContainerHeight() - distanceController.getLast().value); + } + else + { + state.startConfiguration(); + } + } + + void App::_doCalibrate() + { + int count = 0; + int runningTotal = 0; + unsigned int firstValue = 0; + unsigned int precision = distanceController.getPrecision(); + + for (;;) + { + // Read a sensor value. When this fails, then restart the calibration. + auto m = distanceController.readSensor(); + if (!m.ok) + { + _logger.log("s", "Sensor reading failed, restarting calibration"); + return; + } + _logger.log("sisis", "Calibration reading ", count, ": ", m.value, "mm"); + + count++; + runningTotal += m.value; + + // If this is the first value that is read, then use that one to compare + // the upcoming measurements against. + if (count == 1) + { + firstValue = m.value; + } + // Otherwise, check if the new value is within the precision range of the + // distance sensor. + else if (abs(firstValue - m.value) > precision) + { + _logger.log("s", "New reading exceeds sensor precision, restarting calibration"); + return; + } + + // After reading 10 values in a row that are within the sensor precision, + // then we have a winner. + if (count == 10) + { + break; + } + } + + _logger.log("sis", "Calibration completed, container height: ", firstValue, "mm"); + unsigned int container_height = int(runningTotal / 10); + config.setContainerHeight(container_height); + mqtt.publish("container_height", container_height, true); + + state.pauseDevice(); + state.startMeasurements(); + } } // namespace Dough \ No newline at end of file diff --git a/src/App/App.h b/src/App/App.h index 736c0b5..0ef958b 100644 --- a/src/App/App.h +++ b/src/App/App.h @@ -39,6 +39,10 @@ namespace Dough private: App(); Logger _logger; + bool _setupNetworking(); + void _doConfigure(); + void _doMeasure(); + void _doCalibrate(); }; } diff --git a/src/Network/MQTT.cpp b/src/Network/MQTT.cpp index cedac08..971da65 100644 --- a/src/Network/MQTT.cpp +++ b/src/Network/MQTT.cpp @@ -65,30 +65,37 @@ namespace Dough _mqttClient.subscribe(topic); } - void MQTT::publish(const char *key, const char *payload) + void MQTT::publish(const char *key, const char *payload, bool retained) { char topic[200]; snprintf(topic, sizeof(topic) / sizeof(topic[0]), "%s/%s/%s", MQTT_TOPIC_PREFIX, _mqttDeviceId, key); _logger.log("ssss", "Send message: ", topic, " = ", payload); - _mqttClient.publish(topic, payload); - } - - void MQTT::publish(const char *key, int payload) - { - char buf[16]; - snprintf(buf, 16, "%d", payload); - publish(key, buf); - } - - void MQTT::publish(const char *key, Measurement measurement) - { - if (measurement.ok) + if (retained) { - publish(key, measurement.value); + _mqttClient.publish(topic, payload, true, 2); } else { - publish(key, "null"); + _mqttClient.publish(topic, payload); + } + } + + void MQTT::publish(const char *key, int payload, bool retained) + { + char buf[16]; + snprintf(buf, 16, "%d", payload); + publish(key, buf, retained); + } + + void MQTT::publish(const char *key, Measurement measurement, bool retained) + { + if (measurement.ok) + { + publish(key, measurement.value, retained); + } + else + { + publish(key, "null", retained); } } } // namespace Dough \ No newline at end of file diff --git a/src/Network/MQTT.h b/src/Network/MQTT.h index a2beaba..042f443 100644 --- a/src/Network/MQTT.h +++ b/src/Network/MQTT.h @@ -26,9 +26,9 @@ namespace Dough bool connect(); void subscribe(const char *key); void procesIncomingsMessages(); - void publish(const char *key, const char *payload); - void publish(const char *key, int payload); - void publish(const char *key, Measurement measurement); + void publish(const char *key, const char *payload, bool retained = false); + void publish(const char *key, int payload, bool retained = false); + void publish(const char *key, Measurement measurement, bool retained = false); private: Logger _logger; diff --git a/src/Sensors/SensorController.cpp b/src/Sensors/SensorController.cpp index ebb73b1..39f70f8 100644 --- a/src/Sensors/SensorController.cpp +++ b/src/Sensors/SensorController.cpp @@ -19,6 +19,11 @@ namespace Dough return sensor->getName(); } + unsigned int SensorController::getPrecision() + { + return sensor->getPrecision(); + } + void SensorController::setup() { sensor->setup(); @@ -41,24 +46,24 @@ namespace Dough { _plugin->beforeMeasure(this); _lastMeasuredAt = millis(); - _store(sensor->read()); + _store(readSensor()); _plugin->afterMeasure(this); } - auto last = getLast(); - if (_mustPublish()) { _plugin->beforePublish(this); + auto last = getLast(); auto average = getAverage(); _lastPublishedAt = millis(); average.copyTo(&_lastPublishedAverage); last.copyTo(&_lastPublished); _plugin->doPublish(this, last, average); _plugin->afterPublish(this); + return true; } - return last.ok; + return false; } bool SensorController::_mustMeasure() @@ -162,6 +167,11 @@ namespace Dough return _index; } + Measurement SensorController::readSensor() + { + return sensor->read(); + } + Measurement SensorController::getLast() { return *_storage[_index]; diff --git a/src/Sensors/SensorController.h b/src/Sensors/SensorController.h index 3256bb7..10e0baf 100644 --- a/src/Sensors/SensorController.h +++ b/src/Sensors/SensorController.h @@ -60,10 +60,13 @@ namespace Dough // Return the name for the contained sensor. const char *getSensorName(); + // Return the measuring precision for the contained sensor. + unsigned int getPrecision(); + // Read the sensor and publish the results when needed. - // This method returns true when the last read value was an ok measurement. - // This means that after this method returns true, getLast() will return - // an ok Dough::Measurement. + // This method returns true when the last read value was an ok measurement + // and a publish operation was done. This means that after this method returns + // true, getLast() will return an ok Dough::Measurement. bool loop(); // Read a measurement from the sensor.