PWM Ansteuerung

Hallo zusammen,

ich habe einen Code entworfen und wollte euch mal fragen ob das so funktionieren würde, weil ich mir bei einigen Sachen unsicher bin.
Zur Erklärung: Ich möchte eine Pumpe per PWM ansteuern und so lange hochfahren, bis die hälfte des Sollwerts (also die Hälfte der zu liefernden Menge) erreicht ist, danach möchte ich, dass die Pumpe in der gleichen Geschwindigkeit wieder runterfährt. Dabei soll die ganze Zeit der Flowsensor den aktuellen durchfluss (Istwert) zurückgeben, sodass die Pumpe weiß wann sie zu runterzufahren hat.
Hier der Auszug aus meinem Code:

void setup() {
}

void loop() {
  if (dosierung == true){
    if (pumpe1 == true){
      FlowSensor1(istwert);
      startPumpe1(sollwert, istwert, speed);
      digitalWrite(ventil1pin, HIGH);
    }
  }
}

void startPumpe1(int sollwert, float istwert, int speed){
  int x = speed;
  for (pwm = 60, pwm > -1, pwm + x){
    analogWrite(PWMpinPumpe1, pwm);
    if (pwm == 255) x = 0;
    if ( (sollwert/2) >= (istwert/2)) x = speed*(-1);
  }  
}

float FlowSensor1(float istwert){
  int prevValue;
  int currValue = digitalRead(FlowSesnor1pin);
  if (currentValue != prevValue){
    int impulsZaehler++;

    int normistwert = (impulsZaehler - 0) * (1000 - 0) / (3800 - 0) + 0; 

    prevValue = currentValue;
    return normistwert;
  }
}

Okay keine Ahnung was ich mir bei dem ersten Code gedacht habe.
Hier eine verbesserte Version:

void loop() {
  if (dosierung == true){
    if (pumpe1 == true){
      float istwert = FlowSensor1();
      startPumpe1(sollwert, istwert, speed);
      digitalWrite(ventil1pin, HIGH);
    }
  }
}

void startPumpe1(int sollwert, float istwert, int speed){
  int x = speed;
  for (pwm = 60, pwm > -1, pwm + x){
    analogWrite(PWMpinPumpe1, pwm);
    if (pwm == 255) x = 0;
    if ( (sollwert/2) >= (istwert/2)) x = speed*(-1);
  }  
}

float FlowSensor1(){
  float normistwert;
  int impulsZaehler;
  int prevValue;
  int currValue = digitalRead(FlowSensor1pin);
  if (currentValue != prevValue){
    impulsZaehler++;

    normistwert = (impulsZaehler - 0) * (1000 - 0) / (3800 - 0) + 0; 

    prevValue = currentValue;
    return normistwert;
  }
}

Du kannst alte Posts editieren.
Grüße Uwe

Okay. danke
Ansonsten passt der Code oder gibt es Verbesserungsvorschläge?

Hallo,
ich hab da jetzt nur mal ganz kurz drüber gesehen. Wenn Du nicht weißt ob es funktioniert dann probiere es doch einfach aus. Für uns wäre es einfacher wenn wie einen code hätten der wenigstens halbwegs compilieren könnte, also warum zeigst Du uns einen Auszug.

  1. es fehlen die Deklarationen Deiner Variablen
  2. warum kein Setup ?
  3. eine for schleife von 60 bis -1 die jeweils um 1 erhöht wird ?
  4. was soll die funktion flowsensor denn machen. Sie zählt immer weiter hoch prevValue sollte sicher static sein, da bei verlassen einer function die Variablen ungültig werden.
  5. Deine Berechnung wird mit int Werten gemacht damit kommt eine ganze Zahl als Ergebnis raus. wozu sind die Nullen gut. ?

Nachtrag.
mir ist da noch was aufgefallen. Du willst die Pumpe hochfahren , dazu nutzt Du eine for Schleife. es gibt aber keine zeitliche Komponente in der schleife. Damit rast die for schleife hoch , das dauer so maximal ein paar Millisekunden.

Der ganze Code ist viel zu groß. Es geht mir nur um diesen Teilauszug, der die Pumpen ansteuert.

Ja die Pumpe soll bei einem PWM wert von 60 starten und dann höher laufen. Bis der wert 255 also max erreicht ist und dann wieder runterfahren wenn die hälfte des sollwerts erreicht wurde. dann ändert sich x auf -1. Also in der for schleife heißt es dann pwm + -1. Somit wird der pwm wert runter. solange bis pwm bei 0 ist. also dass die Pumpe wieder steht.

der Flowsensor gibt Impulse ab wenn durchfluss da ist. bei 3800 Impulsen sind 1000ml durchgelaufen.

Also wenn z.B der Sensor 485 Impulse abgegeben hat kommt man mit der Berechnung doch nciht auf eine genaue Zahl, sondern auf eine Zahlt mit mehreren zahlen nach dem komma. deswegen dachte ich dass float besser wäre.

Okay. Dachte bei static dass man die Variable nicht überschreiben kann.

stimmt. Da hast du recht. Das habe ich vergessen. Werde ich noch einbauen.

Anstelle wüster Annahmen wären ein paar Grundkenntnisse sinnvoller. Das gilt auch für die Ganzzahlrechnung. Schau für den Anfang mal hier rein.

Gruß Tommy

Hallo das kann ich nicht verstehen,
Warum zerlegst Du Deinen Code nicht in Teilaufgaben die Du besser testen kannst. Du hast einen riesigen Code an dem augenscheinlich nichts funktioniert.

Teste das mal mit dem Sketch.

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);

float a= 485*1000/3800;
Serial.println(a);
}

void loop() {
  // put your main code here, to run repeatedly:

}

probiere das mal mit 2 100 und 500 aus . du wirst dich wundern.

Warum ??
der Variabeln float a wird das Ergebniss einer Integer Berechnung zugewiesen und es kommt zu einem Überlauf.
Du kannst das einfach ändern und 1000 durch 1000.0 und 3000 durch 3000.0 ersetzen dann weiß der Compiler das er float rechnen muss.

Nachtrag zur for schleife
mach doch mal eine for schleife so wie du das vorhast ich denke die löuft nicht da die Bedingung

for (pwm = 60, pwm > -1, pwm + x){

pwm >-1 ja von Anfang an erfüllt ist damit ist das Ende sofort erreicht Aber seis drum das kannst Du ja mal selber testen. Wenn Du keine Harware hast , es gibt Simulatoren im Netz.

falsch. Wird bei jedem Schleifendurchlauf getestet. Wenn sie true ist, werden alle Statements in den Klammern ausgeführt. Wenn false dann bricht die Schleife ab. Und da ich am Anfang 60 habe ist diese condition true (60 > -1).

Stimmt da muss ich Dir recht geben

1 Like

wenigstens habe ich 1x recht ^^

zuzr Funktion

startPumpe1

ist mir folgendes aufgefallen...

wenn du beide Variablen durch 2 teilst schalltet die Pumpe erst einen Gang runter wenn der volle Sollwert erreicht ist.

Besser ist hier:

    if ( (sollwert/2) >= (istwert)) x = speed*(-1);

welche Werte kann speed annehmen ? Falls irgendwas anderes ausser 0,1 und -1 dann sollte vor dem analogWrite eine Begrenzung auf den Maximalwert vorgenommen werden.

if (pwm>255) { pwm=255;}

Die for-schleife hätte ich in drei schleifen zerlegt (hochfahren, arbeiten, runterfahren)... aber dass ist auch Ansichtssache

Aber nicht mit delay() , das mag dann Deine Durchflussmessung nicht. Eventuell machst Du das besser mit einem Interrupt, kommt allerdings auf die Frequenz an.

Aber mach mal , bisher ist das mit dem rauf und runter fahren der Drehzahl ja noch sinnfrei. Wenn man die Menge sehr genau fahren will dann stellt man ja normal kurz vor dem Ziel ehr eine kleinere Drehzahl ein und fährt langsam auf das Ziel zu.

Ha hast du Recht, dann darf das hochfahren der Pumpe aber auch nicht mit einert for-schleife erfolgen... oder "inerhalb" dieser Schleife wird der durchflusssensor abgefragt

ja ich habe das hier in meine for schleife gepackt:

if (millis()- prevmillis >= pump_pause){

Nach dem durchlaufen der schleife wird die zeit prevmillis neu gesetzt.

Vielleicht solltest du beim Coden mal eine Pause machen, dir ein kühles Getränk holen und ganz "old School" mit Papier und blei ein Statusdiagramm machen mit allen Statusübergängen und randbedingungen... Bei Meinen Projekten hat das immer sehr viel Einsicht gegeben :slight_smile:

2 Likes

C++ Anweisungen ohne Variablendefinition ist kein Code.
Die wichtigsten Kommentare sind die, in denen steht wofür die Variablen gut sind und welche Werte sie wann annehmen.
for-Schleifen, die ein zeitliches Verhalten erzeugen sollen, sind Mist ! Du hast schon eine Schleife: loop. Sorge lieber dafür, dass loop immer so schnell wie möglich fertig ist.

So. Ich habe den Code jetzt mal auf das wesentliche verkleinert um euch den ganzen Prozess zu zeigen um den es mir geht. Der Rest was drum herum passiert ist dafür irrelevant.
Kann mir jemand sagen, ob das so passen würde? (Ich kann es zur Zeit noch nicht testen, da die Teile erst bestellt wurden und auf dem weg sind, deswegen möchte ich euch Erfahrene mal fragen :wink:)

bool PROCESS, SINGLE;
int outp_speed;
int process_speed;
int process_tank1;
bool cont1_1, cont1_2, cont1_3;
int single_status;

#define pump1          2 //Pump     PWM       output pin  pump container 1
#define pump2          3 //Pump     PWM       output pin  pump container 2
#define pump3          4 //Pump     PWM       output pin  pump container 3
#define valve1        34 //Valve    DIGITAL   output pin  magnetic valve container 1
#define valve2        35 //Valve    DIGITAL   output pin  magnetic valve container 2
#define valve3        36 //Valve    DIGITAL   output pin  magnetic valve container 3
#define flow1         37
#define flow2         38
#define flow3         39

void setup() {
  pinMode(pump1, OUTPUT);
  pinMode(pump2, OUTPUT);
  pinMode(pump3, OUTPUT);
  pinMode(valve1, OUTPUT);
  pinMode(valve2, OUTPUT);
  pinMode(valve3, OUTPUT);
  pinMode(flow1, INPUT);
  pinMode(flow2, INPUT);
  pinMode(flow3, INPUT);

}

void loop() {
if (PROCESS == true){  //Wird im Menü ausgewählt. Wenn TRUE dann wird Dosiert

  //outp_speed & cont1_1, cont 1_2, cont1_3 werden in einem Menü festgelegt
  if (outp_speed == 1) process_speed = 60; //Dosing speed = slow. Zeit in ms bis pwm hochzählt
  if (outp_speed == 2) process_speed = 40; //Dosing speed = normal. Zeit in ms bis pwm hochzählt
  if (outp_speed == 3) process_speed = 20; //Dosing speed = fast. Zeit in ms bis pwm hochzählt

  if (cont1_1 == true) process_tank1 = 1; 
  if (cont1_2 == true) process_tank1 = 2;
  if (cont1_3 == true) process_tank1 = 3;


  if (SINGLE == true){ //Single Betrieb: Nur 1 Behälter wird dosiert, Wird im Menü festgelegt
    single_status = SingleProcess(process_tank1, outp_amount, process_speed);
    if (single_status >= 100){ //wenn dosierprozess fertig (100%)
      SINGLE = false;
    }
  }
}

int SingleProcess(int tank, int amount, int speed){
  int dosing = amount;
  if (tank == 1) status = startPump1(dosing, speed, pump1, valve1, flow1); //wenn Tank 1 ausgewählt, soll Pumpe 1 starten bis 'dosing' also der Sollwert erreicht ist
  if (tank == 2) status = startPump2(dosing, speed, pump2, valve2, flow2);
  if (tank == 3) status = startPump3(dosing, speed, pump2, valve3, flow3);

  return status //Bringt dauerhaft den aktuellen status des Dosierprozesses von 0-100% zurück
}

int startPump1(int target_flow, int pwm_pause, int pinPump, int pinValve, int pinFlow){
  int pwm;
  int x = 1;
  int status = 0;
  unsigned long prevmillis = 0;
  digitalWrite(pinValve, HIGH); //Magnetventil auf
  
  for (pwm = 1; pwm >= 0; pwm += x){ //for schleife wird beendet, sobald pwm auf 0 ist (Pumpe aus)
    if (x == 1 && pwm == 50) pwm_pause = pwm_pause / 2; //Verringert die Dauer die es braucht um den pwm wert zu ändern
    if (x == -1 && pwm == 50) pwm_pause = pwm_pause * 2; //Erhöht die Dauer die es braucht um den pwm wert zu ändern
    if (millis()- prevmillis >= pwm_pause){
      analogWrite(pinPump, pwm);
      actual_flow = FlowSensor1(pinFlow); //holt sich die aktuelle bisher Dosierte Menge zurück
      if (pwm == 255 || pwm == 0) x = 0; //Pumpe bleibt auf 100% bis die Hälfte des SOllwerts erreicht ist
      if ((target_flow/2) <= actual_flow) x = -1; //wenn hälfte von Sllwert erreicht fährt pumpe runter
      prevmillis = millis();
      status = (actual_flow / target_flow) * 100;
    }
  }
  digitalWrite(pinValve, LOW); //magnetventil zu
  return status;  //gibt den Status des Dosierprozesses von 0-100% zurück
}

//FlowSensor gibt Impulse raus, wenn Wasser durch ihn durchströmt. 3800 Impulse pro 1L
int FlowSensor1(int pinFlow){
  int amount;
  int counter;
  static int prevValue;
  int currValue = digitalRead(pinFlow);
  if (currentValue != prevValue){ //wird ein Impuls festgestellt, zählt der counter +1 hoch
    counter++;
    amount = (counter - 0) * (1000 - 0) / (3800 - 0) + 0; //Normierung der Impulse auf 0-1000ml
    prevValue = currentValue;
    return amount;  //gibt bisherige Dosierung in ml zurück
  }
}

Die Variablen process_speed & process_tank werden noch im MIX Process verwendet, mit dem man 2 Behälter mischen kann. Deswegen stehen die im Loop.

      actual_flow = FlowSensor1(pinFlow); //holt sich die aktuelle bisher Dosierte Menge zurück

sollte ausserhalt der if -Anweisung sein, damit auch jeder impuls des sensors registriert wird

      if (pwm == 255 || pwm == 0) x = 0; //Pumpe bleibt auf 100% bis die Hälfte des SOllwerts erreicht ist
      if ((target_flow/2) <= actual_flow) x = -1; //wenn hälfte von Sllwert erreicht fährt pumpe runter

bei pwm == 0 überschreibt die zweite if-Anweisung den x-Wert, so dass x beim Runterfahren immer auf -1 bleibt. Ist aber nicht dramatisch, da die Scheife dann ja verlassen wird.

  for (pwm = 1; pwm >= 0; pwm += x){ //for schleife wird beendet, sobald pwm auf 0 ist (Pumpe aus)

die Änderung des pwm-Wertes sollte innerhalb der if-Abfrage sein, da sonst den pwm-Wert nach oben abhaut ohne durch

      if (pwm == 255 || pwm == 0) x = 0; //Pumpe bleibt auf 100% bis die Hälfte des SOllwerts erreicht ist

gestoppt zu werden

Stimmt das "|| pwm == 0)" ist unnötig. Da wie du gesagt hast die Schleife ja verlassen wird.