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.

This commit is contained in:
Maurice Makaay 2020-07-20 02:13:10 +02:00
parent e416dab9dd
commit e48e44f287
6 changed files with 148 additions and 58 deletions

View File

@ -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

View File

@ -39,6 +39,10 @@ namespace Dough
private:
App();
Logger _logger;
bool _setupNetworking();
void _doConfigure();
void _doMeasure();
void _doCalibrate();
};
}

View File

@ -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)
if (retained)
{
char buf[16];
snprintf(buf, 16, "%d", payload);
publish(key, buf);
}
void MQTT::publish(const char *key, Measurement measurement)
{
if (measurement.ok)
{
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

View File

@ -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;

View File

@ -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];

View File

@ -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.