You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
7.0 KiB

/*
Pneumatische Zisternen-Füllstandsmessung.
Start der Messung und Übertragung der Ergebnise über WLAN.
Arduino-Bord: "NodeMCU 1.0(ESP-12E Module)"
Autor Wolfgang Neußer
Stand: 24.10.2021
Hardware:
DOIT ESP12E Motor Shield mit L293D Motortreiber
Amica NODE MCU ESP8266 12E
SparkFun Qwiic MicroPressure Sensor
Druckpumpe und Entlüftungsventil aus Oberarm-Blutdruckmesser
Messablauf:
1. Abluftventil schließen, Druckpumpe einschalten
2. Druck kontinuierlich messen
Wenn Druckanstieg beendet -> Pumpe ausschalten
3. Beruhigungszeit
4. Aktueller Druck - atmosphärischen Druck = Messdruck
Beispiel: 29810 Pa = 3040 mmH2O = 100% Füllstand
5. Abluftventil öffnen
*/
#include <Wire.h>
#include <SparkFun_MicroPressure.h>
#include <LiquidCrystal_I2C.h>
SparkFun_MicroPressure mpr;
LiquidCrystal_I2C lcdDisplay = LiquidCrystal_I2C(0x27, 16, 2);
// Zuordnung der Ein- Ausgänge
#define VENTIL 5 // GPIO5 (PWM MotorA)
#define DA 0 // GPIO0 (Richtung MotorA)
#define PUMPE 4 // GPIO4 (PWM MotorB)
#define DB 2 // GPIO2 (Richtung MotorB)
#define SDA 12 // GPIO12 I2C
#define SCL 13 // GPIO13 I2C
#define AUF LOW // Ventil öffnen
#define AUS LOW // Pumpe ausschalten
#define ZU HIGH // Ventil schliessen
#define EIN HIGH // Pumpe einschalten
// An eigene Zisterne anpassen (zur Berechnung der Füllmenge)
const int A = 3140; // Grundfläche der Zisterne in cm^2 (d * d * 3,14 / 4)
const int maxFuellhoehe = 3040; // Füllhöhe der Zisterne in mm
int atmDruck = 97400;
/*
* HELPERS FOR INPUT / OUTPUT
*/
void initDisplay() {
lcdDisplay.display();
lcdDisplay.noBlink();
lcdDisplay.noCursor();
lcdDisplay.clear();
lcdDisplay.backlight();
}
void disableDisplay() {
lcdDisplay.noDisplay();
}
void printMsgToLCD(const String& line_one, const String& line_two) {
lcdDisplay.clear();
lcdDisplay.setCursor(0, 0);
lcdDisplay.print(line_one);
lcdDisplay.setCursor(0, 1);
lcdDisplay.print(line_two);
}
void printDebugMsg(const String& type, const String& msg) {
if (Serial.available()) {
Serial.println(type + msg);
}
printMsgToLCD(type, msg);
}
void printMeasurements(const int& pressure) {
const int wassersaeule = convertPressureToHeight(currentPressure);
hoehe = String(wassersaeule / 10) + "cm";
volumen = String((wassersaeule / 10) * A / 100) + "L";
// Umrechnung Wassersäule in 0 - 100%
fuellstand = String(map(wassersaeule, 0, maxFuellhoehe, 0, 100)) + "%";
if (Serial.available()) {
Serial.println("Füllhöhe: "+ hoehe);
Serial.println("Volumen: " + volumen);
Serial.println("Füllstand: " + fuellstand);
}
printMsgToLCD("Vol: " + volumen, "Stand: " + fuellstand);
}
void handleSerialInput() {
static String inputString;
if (Serial.available()) {
char inChar = (char)Serial.read();
if ((inChar == '\r') || (inChar == '\n')) {
if (inputString == "?") {
Serial.println("Kommandos: ");
Serial.println("p1 = Pumpe EIN");
Serial.println("p0 = Pumpe AUS");
Serial.println("v1 = Ventil ZU");
Serial.println("v0 = Ventil AUF");
Serial.println("start = Messung starten");
Serial.println();
}
else if (inputString == "p1") {
Serial.println("Pumpe EIN");
digitalWrite(PUMPE, EIN);
}
else if (inputString == "p0") {
Serial.println("Pumpe AUS");
digitalWrite(PUMPE, AUS);
}
else if (inputString == "v1") {
Serial.println("Ventil ZU");
digitalWrite(VENTIL, ZU);
}
else if (inputString == "v0") {
Serial.println("Ventil AUF");
digitalWrite(VENTIL, AUF);
}
else if (inputString == "start") {
Measurement();
}
inputString = "";
} else inputString += inChar;
}
}
/*
* FUNCTIONS FOR MEASUREMENT
*/
int getPressureSensorValue() {
static int measuredPressure = 0;
static unsigned long messTakt = 0;
// Messwert in Pascal auslesen und filtern - alle 10ms bei aufruf
if (messTakt < millis()) {
measuredPressure = ((measuredPressure * 50) + int(mpr.readPressure(PA))) / 51;
messTakt = millis() + 10;
}
return measuredPressure;
}
int convertPressureToHeight(const int& pressure) {
// Umrechnung Pa in mmH2O
const int wassersaeule = (pressure - atmDruck) * 10197 / 100000;
if (wassersaeule < 0) return 0;
else return wassersaeule;
}
int setAtmosphericPressure() {
static unsigned long messTakt = 0;
if (messTakt < millis()) {
if (!digitalRead(VENTIL) && !digitalRead(PUMPE)) {
atmDruck = getPressureSensorValue();
}
messTakt = millis() + 1000;
}
}
/*
* MAIN MEASUREMENT ORCHESTRATOR
*/
void Measurement() {
initDisplay();
printDebugMsg("Info: ", "Messung begonnen");
int oldPressure = getPressureSensorValue();
digitalWrite(VENTIL, ZU);
digitalWrite(PUMPE, EIN);
delay(500);
while (true) {
const int currentPressure = getPressureSensorValue();
if (convertPressureToHeight(currentPressure) > (maxFuellhoehe + 200)) {
digitalWrite(VENTIL, AUF);
digitalWrite(PUMPE, AUS);
printDebugMsg("Fehler: ", "Leitung verstopft");
while(1);
}
if (currentPressure > oldPressure + 10) {
oldPressure = currentPressure;
delay(100);
}
else {
break;
}
}
const int finalPressure = getPressureSensorValue();
digitalWrite(VENTIL, AUF);
digitalWrite(PUMPE, AUS);
printMeasurements(finalPressure);
delay(5000);
disableDisplay();
}
/*
* SETUP AND LOOP FOR ARDUINO RUNTIME
*/
void setup() {
// Display initialisieren
lcdDisplay.init();
initDisplay();
// Richtung Motor A
pinMode(DA, OUTPUT);
digitalWrite(DA, HIGH);
// PWM Motor A
pinMode(VENTIL, OUTPUT);
digitalWrite(VENTIL, AUF);
// Richtung Motor B
pinMode(DB, OUTPUT);
digitalWrite(DB, HIGH);
// PWM Motor B
pinMode(PUMPE, OUTPUT);
digitalWrite(PUMPE, AUS);
// enable serial console
Serial.begin(115200);
delay(10);
// I2C initialisieren mit 400 kHz
Wire.begin(SDA, SCL, 400000);
// Drucksensor initialisieren
// Die Default-Adresse des Sensors ist 0x18
// Für andere Adresse oder I2C-Bus: mpr.begin(ADRESS, Wire1)
if(!mpr.begin()) {
printDebugMsg("Fehler: ", "Drucksensor");
while(1);
}
printDebugMsg("Info: ", "System OK");
delay(1000);
disableDisplay();
}
void loop() {
handleSerialInput();
setAtmosphericPressure();
// TODO: read button and start measurement
// ensure valve is open and pump turned off
digitalWrite(VENTIL, AUF);
digitalWrite(PUMPE, AUS);
}