Mehrer String vergleichen und danach mehrer Pumpen ansteuern

Hallo, ich hoffe sehr Ihr könnt mir ein bisschen helfen.
Ich bau gerade eine Cocktailmaschine bestehend aus 16 Pumpen.
Ich steuere das Gerät von einem Laptop aus über den USB Port. Nach dem Tutorial von Piccus!

Meine Software sendet folgende Daten an den Arduino z.b. "1,Orangensaft,5" soll heißen "Programm 1, Getränk, Menge in cl"

Das Problem ist derzeit das ich den String also das Getränk in einer If Anweisung abfragen möchte und jedem Getränk eine Pumpe zuweisen möchte aber das klappt irgendwie nicht!
Und dann möchte ich noch das alle Pumpen gleichzeitig anlaufen, wie geht das?

Ich habe euch mal den Code hochgeladen!

int Pumpe_1 = 2;
int Pumpe_2 = 3;
int Pumpe_3 = 4;
int Pumpe_4 = 5;

const int ANZAHL_FELDER = 5;//Werte durch "," getrennt
int       feldIndex = 0;
float     values[ANZAHL_FELDER];

//---------------------------------------------------------------------------------------
void setup()
{
  pinMode(Pumpe_1, OUTPUT);
  pinMode(Pumpe_2, OUTPUT);
  pinMode(Pumpe_3, OUTPUT);
  pinMode(Pumpe_4, OUTPUT);
  
  Serial.begin(115200);
}//Ende:.void setup()
//---------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------
void loop()
{
	if( Serial.available())
	{

		for(feldIndex = 0; feldIndex  < ANZAHL_FELDER; feldIndex ++)
		{
			values[feldIndex] = Serial.parseFloat();
		}//Ende:.for(fieldIndex = 0; fieldIndex  < 5; fieldIndex ++)

		switch (int(values[0])) //Funktionszuordnung werden abgefragt
		{
                        case 1:
                        Test(String(values[1]),int(values[2]));
                        break;
		}//Ende:.switch (int(values[0]))

		feldIndex  = 0;
		values[0]  = 0;
		values[1]  = 0;
		values[2]  = 0;
		values[3]  = 0;
		values[4]  = 0;// ready to start over

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

	}//Ende:.if( Serial.available())
}//Ende:...void loop()
//---------------------------------------------------------------------------------------

void Test(String getränk, int menge)
{
  int Pumpe;
  if (getränk == "Orangensaft" Pumpe = Pumpe_1;
  else (getränk == "Maracujasaft" ) Pumpe = Pumpe_2;
  
  digitalWrite(Pumpe,HIGH);
  delay(menge*1000);
  digitalWrite(Pumpe,LOW);
}

Der_Chris:
Ich habe euch mal den Code hochgeladen!

OMG! Und was für einen!

getränk == "Orangensaft"

Erstens mal ist ein Variablenname mit einem Umlaut-Zeichen im Namen schon mal in sich fehlerhaft benannt.
Dieser Code dürfte sich überhaupt nicht fehlerfrei kompilieren lassen.
Typische Fehlermeldung: "error: stray '' in program"

Auf welcher Plattform kann denn der gepostete Code mit Umlaut-Variablennamen überhaupt kompiliert werden?

Und zweitens mal macht der Code einen Pointervergleich zwischen einer Stringvariablen und einer Textkonstanten.
Das funktioniert nicht mal mit diesen unsagbar schauderhaften String-Objekten. Zum Vergleich auf Gleichheit der Texte zweier String-Objekte dient die StringObjekt-Funktion "equals()":
http://arduino.cc/de/Reference/StringEquals

Und wenn Du in Deinem Sketch wie ein vernünftiger C-Programmierer C-Strings (char-Arrays) verwenden würdest, würde die Funktion zum Stringvergleich "strcmp()" lauten.

Es ist doch absolut unwichtig, ob Mikrocontroller weiß, ob die Flüssigkeit "Orangensaft" heißt, wenn er nur eine Pumpe bzw. einen Ventil ansteuern soll. Das Protokoll ist damit schon fast zum Scheitern verurteilt. Warum überträgst du nicht einfach 3 Byte in Folge? Das zweite Byte steht dann fürs Ventil bzw. rückwirkend fürs Getränk.

Das kann man schön mit Enums machen:

typedef enum
{
    Orangensaft,
    Maracujasaft,
    Cola,
    Gin
} drinks;

Dann wird jedem Getränk eine Zahl von 0 bis x zugewiesen. Angefangen bei Orangensaft = 0. Das ist was du seriell schicken musst. Dann kannst du einfach so ähnlich vergleichen:

byte drink = Serial.read();

switch(drink)
{
   Orangensaft: 
        ...
        break;
   Gin: 
        ....
        break;
  .
  . 
  .
}

Hierbei eventuell aufpassen ob du ASCII oder Binär-Daten schickt. Bei ASCII musst du noch -48 machen um auf Zahlen zu kommen.

Mit enums kann man auch sowas machen:

drinks currentDrink = Orangensaft;

Also, hab das nochmal überarbeitet um es einfacher zu machen. Habe es jetzt soweit das er mir von der Windows GUI keine Strings sondern stattdessen nur Zahlen gibt. Habe halt im Programm ne extra Methode welche die Strings mit den ich arbeiten muss in jeweils feste Werte ändert.

Jetzt schicke ich nur noch das zum Arduino: "1,2" die erste Zahl für die Pumpe und die zweite Zahl für die Menge. Klappt soweit auch ganz gut nur das natürlich jede Pumpe nacheinander an geht. Gibt es ne Möglichkeit alle Pumpen gleichzeitig zu starten? Hier nochmal mein Code:

const int Pumpe_1=40;
const int Pumpe_2=41;
const int Pumpe_3=42;
const int Pumpe_4=43;
//------------------------------------
const int ANZAHL_FELDER = 10;
int feldIndex = 0;
float values[ANZAHL_FELDER]; 
//------------------------------------

void setup()
{
pinMode(Pumpe_1,OUTPUT);
pinMode(Pumpe_2,OUTPUT);
pinMode(Pumpe_3,OUTPUT);
pinMode(Pumpe_4,OUTPUT);

Serial.begin(115200);

}//Ende Setup-------------------------

void loop()
{
 if( Serial.available())
 {
		for(feldIndex = 0; feldIndex  < ANZAHL_FELDER; feldIndex ++)
		{
			values[feldIndex] = Serial.parseFloat();
		}//Ende:.for(fieldIndex = 0; fieldIndex  < 5; fieldIndex ++)

		switch (int(values[0])) //Funktionszuordnung werden abgefragt
		{
			case 1:
			Pumpe1(double(values[1]));
			break;

                        case 2:
			Pumpe2(double(values[1]));
			break;

                        case 3:
			Pumpe3(double(values[1]));
			break;

                        case 4:
			Pumpe4(double(values[1]));
			break;
		}//Ende:.switch (int(values[0]))

		feldIndex  = 0;
		values[0]  = 0;
		values[1]  = 0;
		values[2]  = 0;
		values[3]  = 0;
		values[4]  = 0;

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

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

void Pumpe1(double menge)//Case 1-------------
{
  int Zeit1 = menge*1000;
  digitalWrite(Pumpe_1,HIGH);
  delay(Zeit1);
  digitalWrite(Pumpe_1,LOW);
}

void Pumpe2(double menge)//Case 2-------------
{
  int Zeit2 = menge*1000;
  digitalWrite(Pumpe_2,HIGH);
  delay(Zeit2);
  digitalWrite(Pumpe_2,LOW); 
}

void Pumpe3(double menge)//Case 3-------------
{
  int Zeit3 = menge*1000;
  digitalWrite(Pumpe_3,HIGH);
  delay(Zeit3);
  digitalWrite(Pumpe_3,LOW);
}

void Pumpe4(double menge)//Case 4-------------
{
  int Zeit4 = menge*1000;
  digitalWrite(Pumpe_4,HIGH);
  delay(Zeit4);
  digitalWrite(Pumpe_4,LOW);
}

Sicher geht das. Der Bremsklotz in deiner Programmierung ist aber das delay() in den Pumpenfunktionen. Sieh dir das Beispiel blinkwithoutdelay.ino an, das sollte vorerst helfen.
Der Code kann auch noch dahingehend optimiert werden, dass du zur Pumpenansteuerung eine Funkton ahst und nicht nur die Menge, sondern auch die Pumpennummer übergibst. Unklar ist mir auch, warum du die Menge als double-Variable übergibst, dann aber wieder bei der Multiplikation mit 1000 auf Integer castest.

sth77:
Unklar ist mir auch, warum du die Menge als double-Variable übergibst, dann aber wieder bei der Multiplikation mit 1000 auf Integer castest.

Das ist schon sinnvoll. Wenn er z.B. 2.5 übergibt wird daraus 2500ms. Oder aus 1.333 = 1333ms

Die Frage ist eher wieso die Pumpennummer ein float ist.

EDIT:
Der Cast von Float auf Double bringt nichts. Beides ist auf dem Arduino gleich groß. Selbst wenn es unterschiedlich wäre, könnte das der Compiler selbst konvertieren.

Serenifly:
Das ist schon sinnvoll. Wenn er z.B. 2.5 übergibt wird daraus 2500ms. Oder aus 1.333 = 1333ms

Du hast natürlich recht. Da war ich wohl gerade gedanklich abwesend...

sth77:
Sicher geht das. Der Bremsklotz in deiner Programmierung ist aber das delay() in den Pumpenfunktionen. Sieh dir das Beispiel blinkwithoutdelay.ino an, das sollte vorerst helfen.

Also blinkwithoutdelay.ino verstehe ich nicht ganz!? Abgesehen davon braucht mein Arduino(Mega2560) voll lange bis überhaupt mal was passiert. Ich drücke auf senden und der braucht 5 Sekunden bis da mal ne LED angeht(hab statt Pumpen erstmal LED´s zum testen genommen).
Irgendwo ist da ne bremse drin!?

So, die Bremse war die Serial.parseInt die hab ich geändert nach Serial.read jetzt ist es schon schneller.
Hab auch die untere Methode Pumpen neu geschrieben. Jetzt geht jede Lampe nacheinander für die vorbestimmte Zeit an.
Jedoch wie mache ich es das die Lampen gleichzeitig an gehen? Hat da evtl. jemand eine Idee zu?

const int Pumpe_1=40;
const int Pumpe_2=41;
const int Pumpe_3=42;
const int Pumpe_4=43;
const int Pumpe_5=44;
const int Pumpe_6=45;

const int ANZAHL_FELDER = 14;
int feldIndex = 0;
float values[ANZAHL_FELDER]; 

int ZahlenTabelle[54];
int ZahlTabIndex = 0;
void setup()
{
pinMode(Pumpe_1,OUTPUT);
pinMode(Pumpe_2,OUTPUT);
pinMode(Pumpe_3,OUTPUT);
pinMode(Pumpe_4,OUTPUT);
pinMode(Pumpe_5,OUTPUT);
pinMode(Pumpe_6,OUTPUT);

Serial.begin(115200);

}
//Ende Setup-------------------------

void loop()
{
 if( Serial.available())
 {
		for(feldIndex = 0; feldIndex  < ANZAHL_FELDER; feldIndex ++)
		{
			values[feldIndex] = Serial.parseFloat();
		}//Ende:.for(fieldIndex = 0; fieldIndex  < 5; fieldIndex ++)
           

		switch (int(values[0])) //Funktionszuordnung werden abgefragt
		{
			case 1:
			Pumpen(int(values[1]),double(values[2]),int(values[3]),double(values[4]),int(values[5]),double(values[6]),int(values[7]),double(values[8]),int(values[9]),double(values[10]),int(values[11]),double(values[12]));
			break;                        
		}//Ende:.switch (int(values[0]))

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

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

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

void Pumpen(int Pumpe1,double Menge_P1,int Pumpe2,double Menge_P2,int Pumpe3,double Menge_P3,int Pumpe4,double Menge_P4,int Pumpe5,double Menge_P5,int Pumpe6,double Menge_P6)
{
  //Pumpe_1--------------------------
    if (Pumpe1==1)
    {
        digitalWrite(Pumpe_1,HIGH);
        delay(Menge_P1*1000);
        digitalWrite(Pumpe_1,LOW);
    }
    else
    {
        digitalWrite(Pumpe_1,LOW);
    }
    //Pumpe_2--------------------------
    if (Pumpe2==1)
    {
        digitalWrite(Pumpe_2,HIGH);
        delay(Menge_P2*1000);
        digitalWrite(Pumpe_2,LOW);
    }
    else
    {
        digitalWrite(Pumpe_2,LOW);
    }
    //Pumpe_3--------------------------
    if (Pumpe3==1)
    {
        digitalWrite(Pumpe_3,HIGH);
        delay(Menge_P3*1000);
        digitalWrite(Pumpe_3,LOW);
    }
    else
    {
        digitalWrite(Pumpe_3,LOW);
    }
    //Pumpe_4--------------------------
    if (Pumpe4==1)
    {
        digitalWrite(Pumpe_4,HIGH);
        delay(Menge_P4*1000);
        digitalWrite(Pumpe_4,LOW);
    }
    else
    {
        digitalWrite(Pumpe_4,LOW);
    }
    //Pumpe_5--------------------------
    if (Pumpe5==1)
    {
        digitalWrite(Pumpe_5,HIGH);
        delay(Menge_P5*1000);
        digitalWrite(Pumpe_5,LOW);
    }
    else
    {
        digitalWrite(Pumpe_5,LOW);
    }
    //Pumpe_6--------------------------
    if (Pumpe6==1)
    {
        digitalWrite(Pumpe_6,HIGH);
        delay(Menge_P6*1000);
        digitalWrite(Pumpe_6,LOW);
    }
    else
    {
        digitalWrite(Pumpe_6,LOW);
    }
}

Keine Garantie, dass das 100%ig so funktioniert. Nicht getestet. Daher selbst mal darüber nachdenken :slight_smile:

const byte pumpen[] = { 40, 41, 42, 43, 44, 45 };
bool pumpenStatus[] = { false, false, false, false, false, false };
unsigned int pumpenZeiten[ANZAHL_PUMPEN];
unsigned long pumpenPreviousMillis[ANZAHL_PUMPEN];

void loop()
{
          //Serial hier einlesen und pumpenZeiten entsprechend der Menge setzen
 
          //Pumpen einschalten. Der Paramter der pumpeAn()-Methode muss bei 0 anfangen zu zählen! Also Pumpe1 = 0, Pumpe2 = 1, etc.

          unsigned long currentMillis = millis();

          for(int i=0; i < ANZAHL_PUMPEN, i++)
          {
                //überprüfen ob die Zeit abgelaufen ist, aber nur wenn die Pumpe läuft
                if((pumpenStatus[i] == true) && (currentMillis - pumpenPreviousMillis[i] > pumpenZeiten[i]))
                {
                        digitalWrite(pumpen[i], LOW);
                        pumpenStatus[i] = false;
                }
          }
 

}

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

Dadurch dass das jeweilige previousMillis in der pumpeAn() Methode auf den aktuellen Wert gesetzt wird, sollte das mit der Zeit hoffentlich passen.

Auf das bool Array kann man auch verzichten wenn man statt dessen pumpenZeiten[index] == 0 für AUS verwendet und >0 für EIN

Super, danke Dir werde ich morgen mal versuchen! :slight_smile:

Statt floats zu nehmen kann man die Zeit auch direkt als int in Millisekunden übergeben.

@ Serenifly: Hab da noch so ein Problem, also Serial einlesen ok hab ich verstanden aber was soll mit dem Rest passieren?

Wie meinst du das? Das muss so in dein Programm eingefügt werden. Die Arrays als globale Variablen und die for-schleife in die Loop.

Du liest über Serial ein welche Pumpen wie lange an sein müssen. Dann setzt du die Einschaltdauer in dem Array. Danach werden die Pumpen eingeschaltet. Das habe ich hier nicht im Detail implementiert, aber das hattest du ja schon in deinem Code. Du verwendest da lediglich meine pumpenAn() Methode.
Die Einschalt-Methode speichert die Zeit wenn die Pumpen eingeschaltet wurden (in Millisekunden seit dem Einschalten des Prozessors). Danach wird ständig verglichen ob die Einschaltdauer abgelaufen ist (aktuelle Zeit - Einschaltzeitpunkt > Einschaltdauer ?). Wenn ja werden die Pumpen abgeschaltet.

Da globale Variable im Gegensatz zu lokalen initialisiert werden reicht auch das (der default Wert ist false):
bool pumpenStatus[ANZAHL_PUMPEN]

Alternativ kannst das wie gesagt weglassen. Dann musst du die for-schleife statt dessen so machen:

if((pumpenZeiten[i] > 0) && (currentMillis - pumpenPreviousMillis[i] > pumpenZeiten[i]))
{
      digitalWrite(pumpen[i], LOW);
      pumpenZeiten[i] = 0;
}

Theoretisch ist auch das nicht nötig, da es nichts macht wenn man einen Ausgang der Low ist nochmal auf Low setzt. Ist aber irgendwie nicht schön und digitalWrite ist außerdem sehr langsam und ineffizient.