Mehrere Pumpen gleichzeitig Steuern

Guten Abend an alle, ich würde gerne mehrer Pumpen über Bluetooth gleichzeitig ansteuern. Ich bekomme per BT einen String mit den einzelnen millis wie lange jede Pumpe laufen soll z.B.:
(5000,6800,1000,500, usw)
Jetzt möchte ich das alle Pumpen gleichzeitig an gehen und nacheinander je nach Zeit aus gehen.
Hatte danach auch schon mal vor ca. 2 Jahren gefragt LINK

jetzt brauche ich genau das aber ich bekomme das nicht hin. Hier mal mein Code:

#include <SoftwareSerial.h>   //Software Serial Port


#define DEBUG_ENABLED  1

const int Pumpe_1 = 22; //Apricot Brandy
const int Pumpe_2 = 23; //Rum
const int Pumpe_3 = 24; //Blue Curacao
const int Pumpe_4 = 25; //Gin
const int Pumpe_5 = 26; //Malibu
const int Pumpe_6 = 27; //Orangenlikör
const int Pumpe_7 = 28; //Tequila
const int Pumpe_8 = 29; //Wodka
const int Pumpe_9 = 30; //Ananssaft
const int Pumpe_10 = 31; //Maracujasaft
const int Pumpe_11 = 32; //Orangensaft
const int Pumpe_12 = 33; //Zitronensaft
const int Pumpe_13 = 34; //Grenadine


const int ANZAHL_PUMPEN = 13;
const byte pumpen[] = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 };
bool pumpenStatus[] = { false, false, false, false, false, false, false, false, false, false, false, false, false };
unsigned int pumpenZeiten[ANZAHL_PUMPEN];
unsigned long pumpenPreviousMillis[13];


int pumpenIndex = 0;


SoftwareSerial blueToothSerial(10, 11); // RX, TX

void setup()
{
  Serial.begin(9600);
  pinMode(Pumpe_1, OUTPUT);
  pinMode(Pumpe_2, OUTPUT);
  pinMode(Pumpe_3, OUTPUT);
  pinMode(Pumpe_4, OUTPUT);
  pinMode(Pumpe_5, OUTPUT);
  pinMode(Pumpe_6, OUTPUT);
  pinMode(Pumpe_7, OUTPUT);
  pinMode(Pumpe_8, OUTPUT);
  pinMode(Pumpe_9, OUTPUT);
  pinMode(Pumpe_10, OUTPUT);
  pinMode(Pumpe_11, OUTPUT);
  pinMode(Pumpe_12, OUTPUT);
  pinMode(Pumpe_13, OUTPUT);
  //Pumpen an-------------------------
  digitalWrite(Pumpe_1, HIGH);
  digitalWrite(Pumpe_2, HIGH);
  digitalWrite(Pumpe_3, HIGH);
  digitalWrite(Pumpe_4, HIGH);
  digitalWrite(Pumpe_5, HIGH);
  digitalWrite(Pumpe_6, HIGH);
  digitalWrite(Pumpe_7, HIGH);
  digitalWrite(Pumpe_8, HIGH);
  digitalWrite(Pumpe_9, HIGH);
  digitalWrite(Pumpe_10, HIGH);
  digitalWrite(Pumpe_11, HIGH);
  digitalWrite(Pumpe_12, HIGH);
  digitalWrite(Pumpe_11, HIGH);
  digitalWrite(Pumpe_13, HIGH);

  blueToothSerial.begin(9800);
}

void loop()
{

  char recvChar;
  while (1)
  {
    if (blueToothSerial.available())
    { //check if there's any data sent from the remote bluetooth shield
      for (pumpenIndex = 0; pumpenIndex  < ANZAHL_PUMPEN; pumpenIndex++)
      {
        pumpenZeiten[pumpenIndex] = blueToothSerial.read();
      }

      for (int i = 0; i < ANZAHL_PUMPEN; i++)
      {
        pumpenAn(pumpen[i]);
      }

      unsigned long currentMillis = millis();

      for (int i = 0; i < ANZAHL_PUMPEN; i++)
      {
        if ((pumpenStatus[i] == true) && (currentMillis - pumpenPreviousMillis[i] > pumpenZeiten[i]))
        {
          digitalWrite(pumpen[i], HIGH);
          pumpenStatus[i] = false;
          Serial.println(pumpen[i]);
        }
      }

      pumpenIndex = 0;

      for (int i = 0; i < ANZAHL_PUMPEN; i++)
      {
        pumpenZeiten[i] = 0;
      }

      while (blueToothSerial.read() != -1);

    }
  }
}


void pumpenAn(byte pumpe)
{
  digitalWrite(pumpen[pumpe], LOW);
  pumpenStatus[pumpe] = true;
  pumpenPreviousMillis[pumpe] = millis();

}

Würde mich über ein paar Ratschläge freuen.
LG
Der Chris

Warum schreibst du ein extra "Pumpenarray"? Die Pins sind doch bekannt. Eine weitere Frage, warum nutzt du SoftwareSerial, wenn du doch scheinbar einen Mega nutzt?

Das mit dem PumpenArray hab ich einfach so übernommen, dachte das muss sein und SoftwareSerial benutze ich weil da mein BT Shield drüber läuft. über HardwareSerial geht das irgendwie nicht.

Wenn du einen String eingibst dann musst du auch einen String einlesen. Mit read() liest du ein einzelnes Byte. Damit kannst du 5000 weder als String noch binär empfangen.

Das Array wird benötigt weil die das alles in for-Schleifen abgearbeitet wird. Die Frage ist eher was die einzelnen Pin Definitionen sollen. Das in setup() kann man auch leicht in einer for-Schleife erschlagen.

Warum schreibst du ein extra "Pumpenarray"?

13 Quasi identische Dinge... Da scheint auch mir ein Array angemessen. Vielleicht auch eine Liste. Egal, wie man es nun jetzt ausformt, aber die Alternative, die Variablen büschelweise durchzunummerieren ist suboptimal/unschön.

So besser?

#include <SoftwareSerial.h>   //Software Serial Port
#define DEBUG_ENABLED  1

const int ANZAHL_PUMPEN = 13;
const byte pumpen[] = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 };
bool pumpenStatus[] = { false, false, false, false, false, false, false, false, false, false, false, false, false };
unsigned int pumpenZeiten[ANZAHL_PUMPEN];
unsigned long pumpenPreviousMillis[13];

int pumpenIndex = 0;

SoftwareSerial blueToothSerial(10, 11); // RX, TX

void setup()
{  
  for(int i=0;i<ANZAHL_PUMPEN;i++)
  {
    pinMode(pumpe[i], OUTPUT);
    digitalWrite(pumpe[i], HIGH);
  }
  
  blueToothSerial.begin(9800);
}

void loop()
{
  if (blueToothSerial.available())
    { 
      for (pumpenIndex = 0; pumpenIndex  < ANZAHL_PUMPEN; pumpenIndex++)
      {
        pumpenZeiten[pumpenIndex] = blueToothSerial.parseFloat();
      }

      for (int i = 0; i < ANZAHL_PUMPEN; i++)
      {
        pumpenAn(pumpen[i]);
      }

      unsigned long currentMillis = millis();

      for (int i = 0; i < ANZAHL_PUMPEN; i++)
      {
        if ((pumpenStatus[i] == true) && (currentMillis - pumpenPreviousMillis[i] > pumpenZeiten[i]))
        {
          digitalWrite(pumpen[i], HIGH);
          pumpenStatus[i] = false;
          Serial.println(pumpen[i]);
        }
      }

      pumpenIndex = 0;

      for (int i = 0; i < ANZAHL_PUMPEN; i++)
      {
        pumpenZeiten[i] = 0;
      }

      while (blueToothSerial.read() != -1);

    }
  }
}


void pumpenAn(byte pumpe)
{
  digitalWrite(pumpen[pumpe], LOW);
  pumpenStatus[pumpe] = true;
  pumpenPreviousMillis[pumpe] = millis();

}

Hier habe ich gezeigt wie man Komma-separierte Zahlen in ein Array einliest:

http://forum.arduino.cc/index.php?topic=359203.msg2486412#msg2486412

Wenn du die erste Variante wählst und alles auf einmal schickst musst du den Puffer vergrößern. Da muss mindestens der ganze String + 1 reinpassen

Die Version ist aber vielleicht besser. Damit könntest du auch Index + Wert schicken wenn du willst:

1,2000

Man müsste da also nicht alle Werte auf einmal schicken sondern könnte auch als erste Zahl den Index nehmen und als zweite den Wert. Dann macht man nur zweimal strtok() + atoi():

int index = atoi(strtok(serialBuffer, ",;"));
int value = atoi(strtok(NULL, ",;"));

Da gibt es viele Möglichkeiten.

Nachtrag: wenn du Floats überträgst gibt es atof() (array to float) statt atoi() (array to integer). Dann fehlt da aber auch sowas wie * 1000 um 2.5 in 2500 umzurechnen

Die false Initalisierung bei pumpenStatus[] ist übrigens unnötig. Ich weiß das habe ich so geschrieben, aber 0 ist sowieso der Default-Wert. Macht es vielleicht deutlicher wenn man es explizit hat. Geschmackssache :)

Also zum eigentlichen Sketch. Geh mal drüber was du da machst

Das abfragen ob die Zeit abgelaufen ist muss außerhalb der Bluetooth Geschichte stehen. Nicht in dem available() Block. Wenn Daten reinkommen muss man die Pumpen anschalten.

Und dann ständig abfragen ob man sie ausschalten muss

Also bis jetzt funktionierte das immer so:

void loop()
{
    if(mySerial.available())
  {
    for(feldIndex = 0; feldIndex  < ANZAHL_FELDER; feldIndex ++)
    {
      values[feldIndex] = mySerial.parseFloat();
    }//Ende:.for(fieldIndex = 0; fieldIndex  < 5; fieldIndex ++)
           
                switch (int(values[0])) //Funktionszuordnung werden abgefragt
                {
                        case 1:
			Pumpen(int(values[1]),double(values[2]),double(values[3]),double(values[4]),
                               double(values[5]),double(values[6]),double(values[7]),double(values[8]),
                               double(values[9]),double(values[10]),double(values[11]),double(values[12]),
                               double(values[13]));
			break;                               
                }

		feldIndex  = 0;
		
                for (int i = 0; i < ANZAHL_FELDER;i++)
                {
                values[i] = 0;
                }

		while (mySerial.read() !=-1);

	}//Ende:.if( Serial.available())
}

void Pumpen(double Menge_P1,double Menge_P2,double Menge_P3,double Menge_P4,
            double Menge_P5,double Menge_P6,double Menge_P7,double Menge_P8,
            double Menge_P9,double Menge_P10,double Menge_P11,double Menge_P12,
            double Menge_P13)
{
  digitalWrite(LED_ROT,LOW);
  //Pumpen 1-13 ---------------------------
  digitalWrite(Pumpe_1,LOW);
  delay(Menge_P1);
  digitalWrite(Pumpe_1,HIGH);
  
  digitalWrite(Pumpe_2,LOW);
  delay(Menge_P2);
  digitalWrite(Pumpe_2,HIGH);
  
  digitalWrite(Pumpe_3,LOW);
  delay(Menge_P3);
  digitalWrite(Pumpe_3,HIGH);
  
  digitalWrite(Pumpe_4,LOW);
  delay(Menge_P4);
  digitalWrite(Pumpe_4,HIGH);

  digitalWrite(Pumpe_5,LOW);
  delay(Menge_P5);
  digitalWrite(Pumpe_5,HIGH);
 
  digitalWrite(Pumpe_6,LOW);
  delay(Menge_P6);
  digitalWrite(Pumpe_6,HIGH);
  
  digitalWrite(Pumpe_7,LOW);
  delay(Menge_P7);
  digitalWrite(Pumpe_7,HIGH);
  
  digitalWrite(Pumpe_8,LOW);
  delay(Menge_P8);
  digitalWrite(Pumpe_8,HIGH);
  
  digitalWrite(Pumpe_9,LOW);
  delay(Menge_P9);
  digitalWrite(Pumpe_9,HIGH);

  digitalWrite(Pumpe_10,LOW);
  delay(Menge_P10);
  digitalWrite(Pumpe_10,HIGH);
  
  digitalWrite(Pumpe_11,LOW);
  delay(Menge_P11);
  digitalWrite(Pumpe_11,HIGH);
  
  digitalWrite(Pumpe_12,LOW);
  delay(Menge_P12);
  digitalWrite(Pumpe_12,HIGH);
  
  digitalWrite(Pumpe_13,LOW);
  delay(Menge_P13);
  digitalWrite(Pumpe_13,HIGH);

  
  digitalWrite(LED_ROT,HIGH);  
}

Nur das natürlich jede Pumpe nacheinander lief.

Die Struktur muss so sein:

void loop()
{
   if (bluetooth.available())
   { 
       //Daten einlesen und Pumpen einschalten
   }

   //hier abfragen ob Pumpen ausgeschaltet werden müssen
}

Schau genau hin wo was steht. Die Abfrage muss außerhalb der Kommunikation laufen und immer statt finden.

Ja klar, voll nicht drauf geachtet, geht aber immer noch nicht.

void loop()
{
  if (blueToothSerial.available())
  {
    for (pumpenIndex = 0; pumpenIndex  < ANZAHL_PUMPEN; pumpenIndex++)
    {
      pumpenZeiten[pumpenIndex] = blueToothSerial.parseFloat();
      pumpenAn(pumpen[pumpenIndex]);
    }
  }

    unsigned long currentMillis = millis();

    for (int i = 0; i < ANZAHL_PUMPEN; i++)
    {
      if ((pumpenStatus[i] == true) && (currentMillis - pumpenPreviousMillis[i] > pumpenZeiten[i]))
      {
        digitalWrite(pumpen[i], HIGH);
        pumpenStatus[i] = false;
        Serial.println(pumpen[i]);
      }
    }

    pumpenIndex = 0;

    for (int i = 0; i < ANZAHL_PUMPEN; i++)
    {
      pumpenZeiten[i] = 0;
    }

    while (blueToothSerial.read() != -1);
}


void pumpenAn(byte pumpe)
{
  digitalWrite(pumpen[pumpe], LOW);
  pumpenStatus[pumpe] = true;
  pumpenPreviousMillis[pumpe] = millis();

}

Was soll das:

    for (int i = 0; i < ANZAHL_PUMPEN; i++)
    {
      pumpenZeiten[i] = 0;
    }

Damit setzt du die Zeiten wieder zurück!

Und was soll das:

while (blueToothSerial.read() != -1);

Das kannst du vielleicht am Ende der Datenübertragung einmal machen damit überschüssige Bytes aus dem Puffer entfernt werden. Aber nicht ständig in loop()

Außerdem passt der Datentyp nicht richtig:

pumpenZeiten[pumpenIndex] = blueToothSerial.parseFloat();

Wenn du 1000 eingeben willst, dann nimm parseInt(). Wenn du 10.5 eingeben willst. Dann nimm parseFloat() * 1000

Verstehe bitte wie nicht-blockierender Code funktioniert. loop() läuft zig mal durch und es wird ständig abgefragt ob was zu tun ist. Du betrachtest das aber als einen Durchlauf der strikt hintereinander geschieht.

Hier ist eine sehr gute Erklärung dazu:

Aber damit das geht muss loop() ständig laufen und du darfst nicht Daten überschreiben die im nächsten Durchlauf noch gebraucht werden

So hab da mal einiges geändert, danke übrigens für deine Geduld. :slight_smile:

void loop()
{
  if (blueToothSerial.available())
  {
    for (pumpenIndex = 0; pumpenIndex  < ANZAHL_PUMPEN; pumpenIndex++)
    {
      pumpenZeiten[pumpenIndex] = blueToothSerial.parseInt();
      pumpenAn(pumpen[pumpenIndex]);
    }
  }

    unsigned long currentMillis = millis();

    for (int i = 0; i < ANZAHL_PUMPEN; i++)
    {
      if ((pumpenStatus[i] == true) && (currentMillis - pumpenPreviousMillis[i] > pumpenZeiten[i]))
      {
        digitalWrite(pumpen[i], HIGH);
        pumpenStatus[i] = false;
      }
    }
}


void pumpenAn(byte pumpe)
{
  digitalWrite(pumpen[pumpe], LOW);
  pumpenStatus[pumpe] = true;
  pumpenPreviousMillis[pumpe] = millis();

}

Es gehen von 4 Relays nur 2 und 3 an und die gehen nicht mehr aus!?
Ich geb dem Ding gleich ne Kopfnuss…

Das ist noch falsch:

 pumpenAn(pumpen[pumpenIndex]);

Du darfst natürlich nicht die Pin Nummer übergeben. Sondern pumpenIndex selbst!

Schau mal wie das verwendet wird:

void pumpenAn(byte pumpe)
{
  digitalWrite(pumpen[pumpe], LOW);
  pumpenStatus[pumpe] = true;
  pumpenPreviousMillis[pumpe] = millis();

}

Also die Zahl die du übergibst wird als Array Index verwendet

Ich hab das mal geändert und jetzt gehen ALLE Relays der Reihe nach an und dann alle zeitgleich aus.

void loop()
{
  if (blueToothSerial.available())
  {
    for (pumpenIndex = 0; pumpenIndex  < ANZAHL_PUMPEN; pumpenIndex++)
    {
      pumpenZeiten[pumpenIndex] = blueToothSerial.parseInt();
      pumpenAn(pumpenIndex);
    }
  }

    unsigned long currentMillis = millis();

    for (int i = 0; i < ANZAHL_PUMPEN; i++)
    {
      if ((pumpenStatus[i] == true) && (currentMillis - pumpenPreviousMillis[i] > pumpenZeiten[i]))
      {
        digitalWrite(pumpen[i], HIGH);
        pumpenStatus[i] = false;
      }
    }
}


void pumpenAn(byte pumpe)
{
  digitalWrite(pumpen[pumpe], LOW);
  pumpenStatus[pumpe] = true;
  pumpenPreviousMillis[pumpe] = millis();
}

Ok, dann muss ich es doch mal selbst testen. Ich dachte damals du verstehst vielleicht den Code und nimmst das nur als Anregung wie es grob geht. Da bin ich wieder mal reingefallen. Obwohl es so wie es jetzt ist eigentlich gehen sollte…

Eine Sache die man ändern kann ist dass man mal auf das millis Array verzichtet. Du willst alle Pumpen gleichzeitig einschalten. Dann muss nicht unbedingt jede Pumpe ihre eigene Einschaltzeit verwalten. Sondern man nimmt eine Variable für alle Pumpen. Geht aber so oder so.

Dann werde ich auch mal auf das bool Array verzichten. Da kann man das Zeiten Array dafür nehmen und 0 = AUS

Ich habe keine Relais so rumliegen. Deshalb teste ich mal mit Serial und kommentiere die Schaltbefehle aus(!!)

Das scheint grob zu gehen:

const int ANZAHL_PUMPEN = 3;

const byte pumpen[] = { 22, 23, 24 };
unsigned int pumpenZeiten[ANZAHL_PUMPEN];
unsigned long pumpenEinschaltzeitpunkt;

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(100);
}

void loop()
{
  if (Serial.available())
  {
    for (unsigned int i = 0; i < ANZAHL_PUMPEN; i++)
    {
      pumpenZeiten[i] = Serial.parseInt();
      Serial.println(pumpenZeiten[i]);
      //digitalWrite(pumpen[i], LOW);         //Pumpe an. Low aktives Relais
    }

    pumpenEinschaltzeitpunkt = millis();    //Eine Zeit für alle Pumpen
  }

  unsigned long currentMillis = millis();
  for (unsigned int i = 0; i < ANZAHL_PUMPEN; i++)
  {
    //wenn Pumpe ein und aktuelle Zeit - Einschaltzeitpunkt > Einschaltintervall
    if ((pumpenZeiten[i] > 0) && (currentMillis - pumpenEinschaltzeitpunkt > pumpenZeiten[i]))
    {
      //digitalWrite(pumpen[i], HIGH);          //Pumpe aus. Low aktives Relais
      pumpenZeiten[i] = 0;      //Pumpe als AUS markieren

      Serial.print("Pumpe "); Serial.print(i); Serial.print(" aus -- war an fuer: "); Serial.println(currentMillis - pumpenEinschaltzeitpunkt);
    }
  }
}

Wenn ich das eingebe:
1000,5000,2000

kommt:
1000
5000
2000
Pumpe 0 aus – war an fuer: 1001
Pumpe 2 aus – war an fuer: 2001
Pumpe 1 aus – war an fuer: 5001

Man sieht auch das erst Pumpe 2 ausgeschaltet wird und dann erst Pumpe 1

Du hast Low Aktive Relais? Hast du dann auch Schließer und Öffner richtig verkabelt so dass bei Low die Pumpe angeht? Wenn nicht ändere das entweder im Code oder der Verkabelung

EDIT:
Die for-Schleife um die Relais auf OUTPUT zu schalten fehlt in meinem setup(). Das musst du ergänzen.

Bei low aktiven Relais hilft es da übrigens glaube ich den Ausgang erst auf HIGH zu schalten und dann auf OUTPUT. Dann ist er sofort HIGH.

Guten Morgen, so jetzt läuft es super. Das ging nich weil wirklich alle Pumpen angeschaltet wurden aber n ur die aus gegangen sind welche größer al 0 waren. Habe da einfach vor dem einschalten noch ne abfrage rein gemacht.

if(pumpenZeiten[i]!=0)                  //Nur Pumpen an machen die nicht 0 sind
{
    digitalWrite(pumpen[i], LOW);         //Pumpe an. Low aktives Relais
}

Vielen, vielen dank für die tolle Hilfe. :) :) :)

Ah, duh. Das hatte ich übersehen. Eigentlich logisch. Hatte ich mich wohl irgendwie darauf festgelegt dass immer alle eingeschaltet werden. Aber dass ist bei einem Cocktail-Mixer natürlich nicht so.

Man kann das auch anders lösen, indem man nur die Zeiten für Pumpen überträgt die auch wirklich eingeschaltet werden und dann die Nummer der Pumpe vor die Zeit stellt. Dann muss man vorher einmal das gesamte Zeiten Array auf 0 setzten. Man könnte auch die Zeiten völlig getrennt voneinander übertragen und am Ende ein extra "start" Kommando schicken.

Aber wenn es geht lasse es so wie es ist :)

Hab da noch eine Frage, also hier mal mein gewünschter Programmablauf:

LED an. Solange(Pumpen an) { LED aus } LED AN

bekomme das irgendwie nicht in den Code rein. Hat da evtl jemand ne idee?

if-Anweisung mit else-Verschachtelung