Arduino Durchfluss/ Flow Meter

Hallo, bin neu hier.

Ich habe einen Arduino UNO, ein LCD und ein Flow Meter:

LCD: 20 x 4 inkl. Lib

Flow Meter: https://www.robotics.org.za/image/data/Sensors/Flow%20Meter/flow_arduino.jpg


Ich habe diesen Quellcode genommen, um den Durchfluss anzeigen zu lassen (also wieviel l/min durchkommen):

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

long Imp;
long starttime;

void setup() {
lcd.begin(20,4);
pinMode(2, INPUT); //Pin 2
}

void loop() {
Imp = 0; // Zähler wird zurückgesetzt
starttime = micros(); // und die Startzeit gespeichert

do {if (pulseIn(2, HIGH) > 0 ) Imp++; } // Die pos. Impulse werden für eine
while( micros() < (starttime + 1e6)); // Torzeit von 1s gezählt
lcd.setCursor(0,0);
lcd.print("flow = ");
lcd.print(Imp); lcd.print(" Imp/s ");
lcd.setCursor(0, 1);
float z= Imp;
z = 24*z/1000; // Die gemessene Impulszahl pro s wird in l/min umgerechnet
if (z < 1) {lcd.print(z,2); }
if (z >= 1 && z<=3.5) {lcd.print(z,1); }
if (z > 3.5) { lcd.print("-> Q > Q(max) "); }
lcd.print (" l/min ");
}

So bekomme ich aber 0,47 l/min angezeigt, was ja nicht stimmt. Also mache ich aus "z = 24*z/1000" die Rechnung "z = z/7.5" und komme auf ca. 2,5 l/min (reeller Wert). Allerdings hängt es sich nach ca 30 min. auf und läuft danach wieder....warum?

Wie kann ich die Pulse ausmessen? Mein Durchflusszähler schafft angeblich 400 Pulse/min. Hab allerdings nicht die Kenntnis, den Puls auszulesen, umzurechnen, anzeigen lassen (l/min) und danach liter gesamt anzeigen zu lassen.

Ich habe hier schon geschaut, mehrmals ohne Erfolg. Deswegen hab ich mich hier angemeldet. Ich hoffe, ihr könnt mir helfen.

Wenn du die Anzahl der Pulse zählen willst, dann kannst du das auch direkt tun! Du musst nicht die Länge messen. Sondern kannst entweder Interrupts nehmen oder die Flanken per Hand abfragen.

Und für eine Sekunde nimmst du besser millis() und nicht micros(). micros() läuft nach ca. 70 Minuten über. Du hast nur long statt unsigned long. Also die Hälfte. Das sind deine 30 Minuten!

Normal macht man es so:

if (millis() - vorherigeMillis > intervall)
{
   vorherigeMillis = millis();

   //hier was tun
}

Dann spielt ein Überlauf keine Rolle

Danke! Macht so, wenn du das jetzt schreibst, Sinn...wäre aber nicht allein drauf gekommen :confused:

Allerdings muss ich bestimmt bei:

void loop() {
Imp = 0; // Zähler wird zurückgesetzt
starttime = millis(); // und die Startzeit gespeichert

do {if (pulseIn(2, HIGH) > 0 ) Imp++; }

// IN dieser Zeile:

while( micros() < (starttime + 1e6));

die starttime + "1e6" ändern oder? Ändere ich in der while- Schleife "millis", statt micros, seh ich auf dem LCD nix

Ja, zwischen micros und millis ist der Faktor 1000, der bei der Berechnung einbezogen werden muß.

unsigned long aktMillis, altMillis, Imp;
const unsigned int torzeit = 1000;
bool aktStatus, altStatus;

void setup() {
  Serial.begin(9600);
  Serial.println("Programmanfang");
  pinMode(2, INPUT); //Pin 2
  pinMode(13, OUTPUT);
  aktStatus = digitalRead(2);
  altStatus = aktStatus;
}

void loop() {
  aktMillis = millis();
  aktStatus = digitalRead(2);
  if (altStatus && !aktStatus) Imp++;
  altStatus = aktStatus;
  if (aktMillis - altMillis >= torzeit) {
    altMillis = aktMillis;
    Serial.print("Impulse = ");
    Serial.print(Imp); Serial.println(" Imp/s    ");
    Imp = 0;
  }
}

do
{
}
while( micros() < (starttime + 1e6));

Loop() ist schon eine Schleife! Du kannst du Funktion immer durchlaufen lassen und nur dann was machen wenn 1s vorbei ist. Wenn du dann subtrahierst statt addierst wird der Überlauf der Zeit korrekt behandelt. Wenn man addiert ist das nicht der Fall!

Außerdem musst du deine Zeit-Variablen als unsigned long und nicht als long definieren.

agmue:
Ja, zwischen micros und millis ist der Faktor 1000, der bei der Berechnung einbezogen werden muß.

unsigned long aktMillis, altMillis, Imp;

const unsigned int torzeit = 1000;
bool aktStatus, altStatus;

void setup() {
 Serial.begin(9600);
 Serial.println("Programmanfang");
 pinMode(2, INPUT); //Pin 2
 pinMode(13, OUTPUT);
 aktStatus = digitalRead(2);
 altStatus = aktStatus;
}

void loop() {
 aktMillis = millis();
 aktStatus = digitalRead(2);
 if (altStatus && !aktStatus) Imp++;
 altStatus = aktStatus;
 if (aktMillis - altMillis >= torzeit) {
   altMillis = aktMillis;
   Serial.print("Impulse = ");
   Serial.print(Imp); Serial.println(" Imp/s    ");
   Imp = 0;
 }
}

Danke! Das funktioniert soweit. Die Ausgabe auf dem LCD auch. Nun kommt die Rechnung: Angezeigt werden ca. 19 Imp/s. Also 19 * 7,5 => l/h => /60 => l/min.

also im Code:

//ab hier stimmt was nicht:

funktioniert nicht =(

Möglicherweise 19 * 60 / 400 = 2,85 l/min

jetzt "springt" er ziemlich schnell, also auf dem LCD steht: 0.00, 1.00, 2.00, 0.00, 1.00, 2.00 usw.

Die Display Ausgabe musst du auch mit millis() verzögern! Hier muss man eigentlich nur aktualisieren wenn neue Daten vorliegen.

In anderen Anwendung reicht es wenn man z.B. alle 100-200ms aktualisiert. Aber es ist Unsinn alle paar µs auf das Display zu schreiben. So schnell kommt das gar nicht mit.

Und wenn der Wert vor dem Komma auch mal zwei-stellig sein kann musst du das auf eine konstante Breite formatieren:
https://www.mikrocontroller.net/topic/86391
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga060c998e77fb5fc0d3168b3ce8771d42

Uff...sorry, da bin ich raus. Das macht die Sache für mich noch komplizierter =(

Ich hab einen anderen Quellcode gefunden, Liter werden insgesamt durchgezählt, nur wie viel Liter aktuell durchlaufen, zeigt es nicht an.

Kann mir bitte jemand bei diesem Code helfen? Ich weiß, typisch Anfänger, aber ich hänge seit 2 Tagen intensiv dran und langsam geht mir die Kraft aus.

Es hängt eigentlich nur noch an der Rechnung, die der "Autor"(?) zwar kommentiert hat, aber ich peil das eben nicht.

Es geht um meine Rechnung (als z deklariert)

hier der CODE

Versuch mal z = flowrate * 60.0 / 400.0; Bei flowrate=19 kommt 2,85 raus.

weiter unten im programm habe ich es so gerechnet, da komm ich auf 5-7. das liegt aber daran, dass flowrate anders misst, sprich im code in Hz auf dem display angegeben werden sollen und nun stehen werte von 37 - 4 drauf und wechseln schnell (300 ms) und

flowrate * 60 / 400 sind 40 * 60 / 400 = 5,77 - 7,14 (l/min) geht nicht, die pumpe muss so um die 2,7 l/min sein, dein prog ist geil, aber ich komm nicht weiter damit

EDIT: Super. Er überschlägt sich bei 140 liter....kann ein arduino "endlos" zählen?

kann ein arduino "endlos" zählen?

Natürlich nicht. Das kommt auf die Datentypen an

Auch bei den Rechnungen musst du aufpassen. Das passieren eher Überlaufen. Erst mal kommt es auf die Datentypen rechts vom = an. Der Standard Datentyp für Integer-Literale und Rechnungen mit diesen ist int. Das gehen 2 ^ 15 - 1 rein. Geht also maximal bis 32767

Wenn du ein Zwischenergebnis hast das größer wird hast du zwei Optionen:
1.) Gleich eine Variable unsigned long machen. unsigned long * int = unsigned long
2.) UL an das Literal anhängen. z.B. val1 = val2 * 60UL / 400. Dann wird in unsigned long gerechnet auch wenn val2 nur ein int/unsigned int ist

Auch daran denken dass / 400 eine Integer Division wenn die anderen Datentypen in der Rechnung auch Integer sind. / 400.0 ist eine Gleitkomma-Division

Aber:
Wenn eine der Variablen rechts vom = float ist wird gleich alles float gerechnet, sowohl bei Division als auch Multiplikation

Also:
int und int -> int
unsigned long und int -> unsigned long
float und irgendwas -> float

Ich hatte hier letztens ein Tacho-Programm reingestellt, das 2 mal / sec. das LCD aktualisiert, vielleicht kannst du das umstricken von km/h in L/min
Tacho-Programm

Lotti-Karrotti:
flowrate * 60 / 400 sind 40 * 60 / 400 = 5,77 - 7,14 (l/min) geht nicht, die pumpe muss so um die 2,7 l/min sein, ...

Es werden nach meinem Eindruck zwei Flanken gemessen, wo es nur eine sein dürfte, daher eine kleine Klammerverschiebung als Vorschlag:

 if (x == HIGH) {   //low to high transition!
    pulses++;
    flowrate = 1000.0;
    flowrate /= lastflowratetimer; // in hertz
    lastflowratetimer = 0;
  }
  lastflowpinstate = x;