Durchflusssensor mit Bedingung / #Anfänger#

Hallo zusammen,

leider habe ich sehr wenige Kenntnisse mit dem Arduino, muss aber dringend bei einer aktuellen Applikation auf diesen zurückgreifen. Bei einem Durchflusssensor verwende ich einen funktionierenden Code, den ich aber gerne etwas ungestalten müsste und nicht weiß wie. Evtl könnt ihr mir unter die Arme greifen.
Ich möchte den Durchfluss solange messen bis eine gewünschte Menge (z.B. 40ml ) erreicht ist. Das Ventil für den Durchfluss würde eine Relaisplatine vom Typ Elegoo 8 Kanal schalten. Wünsch wäre nun das Ventil freizuschalten - den Durchfluss bis 40ml zu messen und danach das Ventil wieder ausschalten. Wie bekomme ich diese Bedingung in den aktuellen Code?

float cfactor = 5.0;

byte interrupt = 0;
byte sensorPin = 2;
byte pulseCount;
float flowRate;
unsigned int f_ml;
unsigned long t_ml;
unsigned long oldTime;

void setup()
{
  Serial.begin(9600);
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH);
  attachInterrupt(interrupt, pulseCounter, FALLING);
}

void loop()
{
  if ((millis() - oldTime) > 1000)
  {
    int semic;
    detachInterrupt(interrupt);
    flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / cfactor;
    oldTime = millis();
    f_ml = (flowRate / 60) * 1000;
    t_ml += f_ml;
    Serial.print("Current flow: ");
    Serial.print(int(flowRate));
    Serial.print(".");
    semic = (flowRate - int(flowRate)) * 10;
    Serial.print(semic, DEC) ;
    Serial.print("L/min");
    Serial.print("  Total: ");
    Serial.print(t_ml);
    Serial.println("mL");
    pulseCount = 0;
    attachInterrupt(interrupt, pulseCounter, FALLING);
  }
}

void pulseCounter()
{
  pulseCount++;
}
if (t_ml >= 40)
{
  // dann tu was z.B. 
  digitalWrite(relayPin, HIGH); // in der Annahme dass die Relays LOW aktiv sind
}

aber meines erachtens passt deine Anforderung nicht.
Du musst definieren

  • wodurch wird die Messung gestartet (womit das Relais dann auch einschaltet und die Messung gestartet werden soll)
  • wodurch soll eine weitere Messung gestartet werden

okay da hast du natürlich recht. ich würde einen digitalen eingang verwenden der mir diese Messung startet und einen anderen um sie wieder zu beenden. der arduino wird in diesem projekt nur die füllstandsregelung vornehmen. ich müsste ihm natürlich noch die gewünsche menge übergeben. welche möglichkeiten hätte ich diese menge als variable zu übergeben? meine hauptanwendung läuft über access vba daher kenn ich mich hier nicht so gut aus .

d.h. die 40ml sind nicht fix sondern die willst auch noch einstellen können?

Wenn ja, dann überleg dir mal wie das interface dazu aussehen soll... also wie gibst du diesen Wert in den Arduino (außer immer wieder neu Programmieren + hochladen?)

Wünsch wäre den Prozess des Dossierens über RS232 zu starten / zu übergeben.
Bisher mache ich alles über RS232. Gibt es eine Möglichkeit den Wert per RS232 zu übertragen und damit auch die Prozedur zu starten?

Hallo,
eigentlich hast Du Dir den falschen Sketch rausgesucht. Der misst ja den Durchfluss skaliert in Volumen/Zeiteinheit.
Du willst doch eigentlich nur wissen wann eine bestimmtes Volumen durch ist. Jeder Impuls entspricht einem bestimmten Volumen, also jeden Impuls des Zählers zählen. Dazu hast Du ja bereits eine ISR function drin. Darin werden die Pulse gezählt. Jetzt musst Du mit einem Taster das Ventil einschalten und den counter auf 0 setzen. Und wenn der counter z.B >= 123 ist wieder ausschalten. Die 123 sind dann der Wert der Deinen 40ml entspricht. Das kannst Du berechnen oder ausmessen. Im Datenblatt zum Durchflussmesser findest Du die Angaben dazu.

Schau dir in den Beispielen an wie man einen Taster abfragt und die Flanke erkennt , wie einen Ausgang einschalteten und wieder ausschalteten.

Heinz

ja kann man machen.

Du hast jetzt mehrere Baustellen.
Eine ist das Auslesen einer Seriellen Schnittstelle.
Das ist hier gut erklärt:
https://forum.arduino.cc/t/serial-input-basics-updated/382007

Dann definierst du dir drei Telegramme

"Setze Werte auf xx" (PC->Arduino)
"Starte Messung" (PC->Arduino)
"Messung beendet ergebnis yy" (Arduino->PC)

das schreibt man halt nicht im Freitext sondern du musst dir Gedanken machen wie du da einen sauberen Befehl und den Wert übertragst sodass die andere Stelle das lesen kann.

Wie hängt dein Sketch mit dem von @MaxHobel gestern zusammen?

Warten bis eine bestimmte Menge erreicht ist, ok.
Aber wie startet das Ganze überhaupt?

Das ist eigentlich der Teil, der fehlt. "Wünsch" könnte evtl. ein Start-Taster sein...

Da eh das meiste noch fehlt, ein anderer Ansatz:

Du könntest dir ausrechnen, wieviele Pulse deiner Wunsch-Menge entsprechen.
Solange du keine Möglichkeit hast, die Menge im laufenden Betrieb einzustellen, gerne vor dem Programmieren per Taschenrechner.

Dann einfach (evtl. sogar ohne Interrupt-Gedöns) nach dem Öffnen des Ventils warten, bis die Anzahl Pulse erreicht ist und das Ventil wieder schließen.

Je nach Pulshäufigkeit/Anzahl könntest du jeden oder jeden zehnten Puls oder so eine Serial Testausgabe einbauen, damit das ganze beobachtbar bleibt.

Auch wenn du das vermutlich nicht hören willst:
Klar kann man Gefundenes ansehen, lernen, und sinnvolle Teile rauspicken.
Einen gefundenen Sketch mühsam an eine andere Aufgabenstellung anpassen ist meist nicht sinnvoll.

Nachtrag:
Lesen ist mühsam. Eigentlich hat @Rentner das schon so formuliert, sehe ich jetzt.

YMMD :sweat_smile: :rofl: :joy:

Hallo,
wenn zusätzlich eventuell sogar gleichzeitig die Serielle benutzt wird könnte was verloren gehen. Das hängt aber von der Frequenz des Durchflussmessers ab. Ich würde die ISR schon drin lassen , damit ist dann auch das Thema Flanken-Erkennung bereits erschlagen.

Allerdings solltest Du Dich noch mal mit dem Thema atomic und volatile Variablen beschäfigen
Du willst sicher die variable pulscount ausserhalb der ISR verändern und zum Zweiten könnte es sein das die Variable pulscount grösser als 255 wird.
Heinz

Nachtrag : falls der Sensor prellen sollte muss dann allerdings ein kleines RC mit dran.

Okay schon mal vielen Dank für die Infos.
Eine Frage hätte ich dazu noch .... Kann ich dafür den gleichen Comport nutzen mit dem ich auch die Programme auf den Arduino übertrage? wenn ich natürlich den Seriellen Monitor starte um zu Debuggen kann ich nichts mehr übertragen.

Dann muss auf jeden Fall auch das detachInterrupt raus.

Über Serial sollte sowieso nicht mehr laufen, als man lesen kann. Das dauert dann keine Zeit und so verliert man auch pulsmäßig nichts.

Das geht übrigens nur auf "richtigen" Arduinos (atmega328 basierend), wenn ich schon mal am Meckern bin.


Natürlich. Während der Übertragens (Hochladen) läuft kein Sketch, da gibt's auch nichts zu debuggen oder (in der nächsten Ausbaustufe) andere Sollwerte per SerialMonitor vorzugeben.

Das SerialMonitor-Fenster pausiert übrigens, wenn offen, während das Hochladens, und läuft danach gleich weiter.

Hallo,
ich wurde das alles erst mal mit dem Monitor testen,und erst wenn es läuft auf die VBA Schnittstelle wechseln. So komplex ist das ja auch nicht.
Heinz

fast richtig.
natürlich kämen sich deine Kommunikation mit PC-Arduino und deine Debug-Ausgaben in die Quere.
Wenn du einen externen TTL-USB Wandler verwendest und wenn du eine langsame Übertragung aktzeptierst, dann könntest du nach Soft-Serial suchen.
Ansonsten gehts auch auf einer Schnittstelle, dann musst du dein PC Programm stabil gegen "falsche" Informationen machen.
Wenn du z.B. jede Debug-Ausgabe einfach mit einem D beginnen lässt könntest du derartige Zeilen im PC einfach wegwerfen.

okay vielen Dank
ich werd mich jetzt mal an die Arbeit machen. Die Kommunikation vom PC zum Arduino funktioniert schon mal. Es kommen bestimmt noch mal Fragen aber jetzt weiß ich erstmal wos langgehen muss.

Hallo. Das Ergebnis bisher sieht schon sehr gut aus. Habe allerdings ein Problem, dass ich nicht ganz nachvollziehen kann. Zahl und t_ml sind alle als unsigned long deklariert. Beim Vergleich (t_ml >= (Zahl)) bricht die Schleife allerdings gleich beim ersten Durchlauf ab obwohl die Zahl 30 und t_ml 0 ist. Was habe ich falsch gemacht ?

Consoleninhalt:
13:38:10.876 -> 30
13:38:10.876 -> Zahl wird verarbeitet
13:38:11.890 -> Current flow: 0.0L/min Total: 0mL
13:38:11.938 -> Füllmenge erreicht

unsigned long Zahl;
char Data[5];                    //Anzahl der max Ziffernzahl -1 in diesem Fall eine 4-stellige Zahl
int i;                           //Arbeitsvariable
unsigned long Tempo;             // Startzeit für die Übertragung
int PINOUT = 8;                  // Pin 8 am Arduino festlegen
float cfactor = 5.0;
byte interrupt = 0;
byte sensorPin = 2;
byte pulseCount;
float flowRate;
unsigned int f_ml;
unsigned long t_ml;
unsigned long oldTime;


void setup()
{
  Serial.begin(9600);
  pinMode(PINOUT, OUTPUT);     // Initialisieren des Ausgangs
  digitalWrite(PINOUT, HIGH);  // Initialisiere: Relais aus
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH);
  attachInterrupt(interrupt, pulseCounter, FALLING);
}

void loop() {

  do {
    if (Serial.available()) {
      Data[i] = Serial.read();                        //liest die Zeichen nacheinander ein
      i++;
    }
    if (i < 1)Tempo = millis();                         // Startzeit abspeichern
  } while (i < 4 && (millis() - Tempo) < 500); // schleife solange nicht 4 Zeichen übertragenoder 0,5 Sekunden vorbei sind

  Data[i] = 0;                                                // Abschluß der Zeichenkette mit Null für die Umwandlung
  Zahl = atoi(Data);                                      // Umwandlung Zeichenkette zu
  i = 0;
  Serial.println(Zahl);                                   // ausgabe der Zahl zu kontrolle
  if (Zahl > 0) { // Testet, ob das Zeichen eine Zahl ist.
    Serial.println("Zahl wird verarbeitet" );
    digitalWrite(PINOUT, LOW);  // Schaltet ein
    delay(1000);
    do {
      if ((millis() - oldTime) > 1000)
      {
        int semic;
        detachInterrupt(interrupt);
        flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / cfactor;
        oldTime = millis();
        f_ml = (flowRate / 60) * 1000;
        t_ml += f_ml;
        Serial.print("Current flow: ");
        Serial.print(int(flowRate));
        Serial.print(".");
        semic = (flowRate - int(flowRate)) * 10;
        Serial.print(semic, DEC) ;
        Serial.print("L/min");
        Serial.print("  Total: ");
        Serial.print(t_ml);
        Serial.println("mL");
        pulseCount = 0;
        attachInterrupt(interrupt, pulseCounter, FALLING);
      }
    } while (t_ml >= (Zahl));
     Serial.println("Füllmenge erreicht");
    digitalWrite(PINOUT, HIGH);   // Schaltet aus
  }

  else {
    Serial.println("Das Zeichen ist keine Zahl");
  }
}

void pulseCounter()
{
  pulseCount++;
}

"solange t_ml größergleich Zahl ist"

t_ml ist 0
Zahl ist 30

damit ist t_ml nicht größer gleich - warum soll er das also wiederholen?

loop, if do while if if while else.
Spaghetti-Code und kaum mehr nachvollziehbar

Sketch alá Bolognese? :nerd_face:

okay hab das mit der while schleife falsch verstanden ... ist klar und geht.
Spaghetti Code wirds größtenteils leider bleiben weil ich den Arduino nur für dieses Projekt benötige und mir momentan keine großen Grundladen aneignen kann / will

Falls große Schnitzer drin sind wäre ich dankbar über entsprechende Tipps.
Mit der Umstellung auf while (t_ml <= (Zahl)) gehts dann auch.

Hallo,
grundsätzlich hast Du noch ein anderes Problem. Ich mach´s mal ausführlich. Derzeit misst Du ja die Frequenz, Anzahl der Impulse in einer Sekunde. Berechnet wird das jede Sekunde einmal. Damit liegt jede Sekunde ein neuer Messwert vor. Somit kannst Du auch nur jeweils nach einer Sekunde Dein Ventil abschalten. Ich weiß jetzt nicht ob Dir das reicht, aber du hast von 40ml gesprochen. Wie hoch ist denn dein Durchfluss in einer Sekunde ?

Besser wäre nur einfach die Impulse aufsummieren und normieren, und dann bei Erreichen der Sollmenge (Summe ) abzuschalten. Aber ich hatte Dir das in #6 schon mal vorgeschlagen.

ich hab da mal in meiner Bastelkiste etwas gefunden und ein Wenig angepasst. Die Frequenz des Duchflussmesser wir zum testen mittels Tone simuliert. Während des Füllvorganges wird die Summe alle 500ms aktualisiert.
läuft so auf einem UNO

Heinz

#include<util/atomic.h>

const float faktor = 1.0;     // ml/Impuls
int Summe = 100;               // Sollmenge in ml

const byte messpin = 2;
const byte startpin = 3;
const byte LED = 13;

bool btnstate;
bool btnold;
volatile uint16_t i_puls = 0; // counter für ISR
uint16_t puls = 0; // counter für Bearbeitung
uint16_t altpuls;
float ml = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(startpin, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  pinMode(messpin, INPUT_PULLUP);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(messpin), messen, FALLING);
}

void loop() {
  // put your main code here, to run repeatedly:
  tone(5, 35); // Frequenz zum testen


  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    puls = i_puls;   //umladen
  }

  // hier die Normierung
  ml = puls * faktor;

  btnstate = !digitalRead(startpin);// Invertiert einlesen

  if (btnstate && !btnold && digitalRead(LED) == LOW) { //start
    btnold = true;
    i_puls = 0;
    digitalWrite(LED, HIGH);
    Serial.println("start");
  }
  if (btnstate)btnold = false;// reset Flanke

  if (ml >= Summe) {          // Menge erreicht
    digitalWrite(LED, LOW);
  }
  if (puls != altpuls) {      // Ausgabe aktualisieren
    altpuls = puls;
    ausgabe();
  }
}

void messen() {
  i_puls++;
}

void ausgabe() {
  static uint32_t altzeit;

  if (millis() - altzeit >= 500) {
    altzeit = millis();
    Serial.print( ml); Serial.println("ml durch");
  }
}