Pumpenansteuerung für Elektrolytumwälzung und Raumbelüftung gelöst

Hallo Liebe Gemeinde,
ich bin noch komplett Neu im Thema Programmierung.
Für eine technische Umsetzung musste ich mich jetzt mit dem Thema Programmierung auseinandersetzen.

Ich möchte gerne eine Belüftungspumpe für meine Bleibatterien steuern. Die Luft sorgt für eine gleichmäßige Durchmischung der Säure im Ladezustand. Das ganze ist an einer PV-Anlage verbaut.
Der Ladestrom wird über einen induktiven Sensor gemessen und ist mit dem Arduino UNO verbunden. Die Pumpe wird dann über ein Reale gesteuert. Die Pumpe darf maximal 10min laufen und muss danach 10min Pause machen.

Der erste Entwurf der Programmierung funktionierte schon ganz gut. Leider schwanken etwas die Messwerte, und ich möchte eine gewisse Glättung in der Steuerung einbringen.
Ohne diese Glättung muss ich den Schwellenwert zu hoch setzen.
Mein Wunsch wäre, dass die Pumpe erst einschaltet, wenn der Schwellwert z.B. 3 mal hintereinander in einem bestimmten Messzyklus z.B. 5s überschritten wird. Das gleiche gilt für die Abschaltung vor Ablauf der Einschaltphase. Die Pumpe soll dann Abschalten, wenn der Schwellen z.B. 3 mal hintereinander in einem Messzyklus von z.B. 5s unterschritten wird.

Ich habe schon viel im Internet gesucht, aber leider konnte ich mir selber nicht helfen und wäre für eure Hilfe sehr dankbar.
Mit dem letzten Versuch meiner Programmierkünste bleibt die Pumpe dann leider eingeschalten.

Gruß
Meik

/*Pumpensteuerung
*Version 2.0 
*31.01.2021 
*/


const int inputPin = A0;
const int relai = 13;

int relaiState = LOW;
long myTimer = 0;
long myTimeout = 60000;

int StartMessung;                               // <3 = keine Messung, 3 = Timmer wird ausgelöst


void setup (){ 
Serial.begin(9600);
pinMode(relai,OUTPUT);

 // Grundzustand
 StartMessung = 0;                            // Am Anfang haben wir keine Messung
}

void messung() {                               //Messwerterfassung
static int oldVal = 0;
int val = analogRead(inputPin);
if (val != oldVal){
int volt = map(val,0,1023,0,502);
Serial.print(val);
Serial.print(",");
Serial.print(volt/100.0);
Serial.println(" V");  
oldVal = val;
}
delay(500);
}

void StartMe() {
 if (StartMessung != 3) {
   if (analogRead(inputPin) > 515) {         // Grenze Einschaltstrom
      StartMessung = StartMessung + 1;
   } else {
     StartMessung = 0;
   }
   delay(200);                              // der nächste Test soll erst nach z.B. 5 Sekunden gemacht werden
 }
}

void schaltung() {                            //Steuerung des Relais
if (StartMessung == 3) {                     // wir haben eine Messung!             
 digitalWrite(relai,HIGH);
if (millis() > myTimeout + myTimer ) {       // Ausschaltverzögerung
   myTimer = millis();

   if (relaiState == LOW) relaiState = HIGH;
   else relaiState = LOW;

   digitalWrite(relai, relaiState);
   delay(60000);                              //Einschaltverzögerung
 }
 } else {
  digitalWrite(relai,LOW);   
}
}
 
void loop() {
messung();
StartMe();
schaltung();
}

Hallo und willkommen im Forum.

Dein Sketch ist fast nicht zu lesen. In einem Mobilgerät überhaupt nicht.

Setze deinen Sketch bitte in Code-Tags.

Verwende dazu die Schaltfläche </> oben links im Editorfenster.
Dazu den Sketch markieren und die Schaltfläche klicken, oder [ code] davor und [ / code] dahinter ohne Leerzeichen.
Oder
im IDE-Fenster rechte Maustaste und für “Forum kopieren klicken”, danach hier einfügen.

Das kannst du in deinem Startbeitrag noch nachträglich machen.

Damit wird dieser für alle besser lesbar.

Vorweg mal Dein Sketch in Code-Tags:

*Pumpensteuerung
 *Version 2.0 
 *31.01.2021 
 */


const int inputPin = A0;
const int relai = 13;

int relaiState = LOW;
long myTimer = 0;
long myTimeout = 60000;

int StartMessung;                               // <3 = keine Messung, 3 = Timmer wird ausgelöst


void setup (){ 
Serial.begin(9600);
pinMode(relai,OUTPUT);

  // Grundzustand
  StartMessung = 0;                            // Am Anfang haben wir keine Messung
}

void messung() {                               //Messwerterfassung
static int oldVal = 0;
int val = analogRead(inputPin);
if (val != oldVal){
int volt = map(val,0,1023,0,502);
Serial.print(val);
Serial.print(",");
Serial.print(volt/100.0);
Serial.println(" V");  
oldVal = val;
}
delay(500);
}

void StartMe() {
  if (StartMessung != 3) {
    if (analogRead(inputPin) > 515) {         // Grenze Einschaltstrom
       StartMessung = StartMessung + 1;
    } else {
      StartMessung = 0;
    }
    delay(200);                              // der nächste Test soll erst nach z.B. 5 Sekunden gemacht werden
  }
}

void schaltung() {                            //Steuerung des Relais
 if (StartMessung == 3) {                     // wir haben eine Messung!             
  digitalWrite(relai,HIGH);
 if (millis() > myTimeout + myTimer ) {       // Ausschaltverzögerung
    myTimer = millis();

    if (relaiState == LOW) relaiState = HIGH;
    else relaiState = LOW;

    digitalWrite(relai, relaiState);
    delay(60000);                              //Einschaltverzögerung
  }
  } else {
   digitalWrite(relai,LOW);   
}
}
  
void loop() {
messung();
StartMe();
schaltung();
}

meikvr6:
… Leider schwanken etwas die Messwerte, und ich möchte eine gewisse Glättung in der Steuerung einbringen. …

Benutze eine Feldvariable (Array), in das Du z.B. 5 Werte schreibst und mit dem Du dann einen Durchschnitt errechnest. Schalte dann anhand dieses Durchschnitts.

Gruß

Gregor

gregors war schon so nett und hatte den Code formatiert übergeben - Danke dafür...

meikvr6:
Mein Wunsch wäre, dass die Pumpe erst einschaltet, wenn der Schwellwert
z.B. 3 mal hintereinander in einem bestimmten Messzyklus z.B. 5s überschritten wird.

    delay(60000);                              //Einschaltverzögerung

Das Eine schliesst das Andere aus.
Du solltest zuerst das mit delay ganz sein lassen. Mindestens aber, wenn die Pausezeit die von Dir geringst gewünschte Reaktionszeit übersteigt.

Und dann ist die Aufgabenstellung auch nicht ganz klar.

Willst Du, fortlaufend das innerhalb von Zeit t gemessene Temperatur T x-Mal erreicht sein muss, oder willst Du als Auslöser gemessene Temperatur T und diese ab diesem Zeitpunkt innerhalb von Zeit t x-Mal wiederholt erreicht wird?

Hintergrund ist die Frage, ob das auslösende Ereignis bei Ablauf der Zeit zwischengespeichert oder gelöscht wird.
Wenn die Zeit das bestimmende Ereignis ist, dann mit Ringspeicher, der nach Ablauf der Zeit von vorn be-/überschrieben wird.

Ich benutze gerne folgende Variante

WENN tempT >Y DANN I++ SONST I=0
oder: (Achtung Wertebereiche!)
WENN tempT >Y DANN I++ SONST WENN I>0 DANN I--

WENN I>Z DANN auslösen

Im ersten Fall wird komplett zurück gesetzt, wenn ein Wert nicht passt.
Im zweiten Fall, wird für jeden nicht passenden Wert um eins zurückgesetzt.
Also 4 passsen, einer nicht - macht drei. danach passen.... 2 also ausgelöst.

Hallo
Danke für die schnelle Rückmeldung.
Hier ist ja richtig Leben im Forum.
Ein Durchschnittswert wäre sicherlich auch eine Möglichkeit.
Trotzdem würde es mich interessieren ob man meine Idee auch umsetzen kann.
Bitte denkt daran, dass ich absoluter Anfänger bin.

Ich habe myTimer für die Einschaltphase verwendet, weil ich möchte, dass der Controler weiter die Messwerte erfasst und bei Bedarf die Pumpe wieder abschaltet.
Delay habe ich beim Abschalten genutzt, weil die Pause auf jeden Fall erfolgen muss, sonst wird die Pumpe beschädigt.

@my_xy_projekt Wenn ich dich richtig verstehe, dann würde die Zeit der Einschaltphasen gezählt werden z.B. 2min + 5 + 3min und wenn der Wert von 10min erreicht würde, dann würde eine Pause von 10min erfolgen?

Hallo
eine Schalthysterese um den Schwellenwert herum sollte das Thema handhabbarer machen.

meikvr6:
Hier ist ja richtig Leben im Forum.

Ja.
https://forum.arduino.cc/index.php?action=stats

Trotzdem würde es mich interessieren ob man meine Idee auch umsetzen kann.
Bitte denkt daran, dass ich absoluter Anfänger bin.

Ein Teil dessen was geht habe ich ja beschrieben. Die Auflistung ist nicht abschliessend.

Wenn ich dich richtig verstehe, dann würde die Zeit der Einschaltphasen gezählt werden

Nein.
Man könnte alle xx Sekunden einen TemperaturMesswert erfassen.
Was Du jetzt daraus machst wäre:
a) Messwert in einem Ringspeicher aufnehmen
aa) Regelmässig Durchschnitt aus allen Werten errechnen
Wenn Durchschnittsgrenzwert erreicht - Aktion auslösen.

ab) Maxwert nach jedem Umlauf suchen
Wenn Grenzwert erreicht - Aktion auslösen

b) Messwert mit einem Grenzwert vergleichen
ba) Grenzwert erreicht? Zähler hochsetzen
bb) Grenzwert nicht erreicht? Zähler herabsetzen
Wenn Zähler Auslösewert erreicht - Aktion auslösen

c) Alles insgesamt geht auch noch in Kombination mit einer Zeitvariablen. Also die Anzahl der Überschreitung der Grenzwerte muss in einer bestimmten Zeit erfolgen.

Dann, und erst wirklich dann, wenn Du Dich für eine Variante entschieden hast, kannst Du daran gehen die Steuerung für die Belüftung zu machen.

Hallo
Danke für deine Rückmeldung
Dann wurde ich folgende Lösung probieren:
Messwerte in einem Ringspeicher aufnehmen und dann einen regelmässigen Durchschnitt aus z.B. 5 Messwert bilden
Wenn der Durchschnittsgrenzwert erreicht wäre dann die Aktion auslösen.

Das gleiche bräuchte ich dann auch für die Abschaltung...

meikvr6:
Das gleiche bräuchte ich dann auch für die Abschaltung...

Ja, dann mach das doch. Ist gar der Unterschied zwischen digitalWrite(HIGH); und digitalWrite(LOW); nicht klar?!

Gruß

Gregor

Einfach mal machen wäre schön. :slight_smile:

Ich habe jetzt das hier gefunden…

int mittelWert(int neu) {
static int werte[3];
static byte index;
  werte[index] = neu;
  if (++index > 2) {
    index = 0;
  }
  return (int)(0L + werte[0] + werte[1] + werte[2]) / 3;
}

meikvr6:
Einfach mal machen wäre schön. :slight_smile:

Ich zumindest kann das bestätigen.

Ich habe jetzt das hier gefunden...

Ja, kann man machen.
Dann bliebe jetzt nur, in welchem Rhythmus du messen willst.
Und dann die Bedingung aus #0

Die Pumpe darf maximal 10min laufen und muss danach 10min Pause machen.

Läuft die Temperaturmessung parallel und ist diese auch Auslöser für die Abschaltung und Pausezeit?

Wir reden nicht von einer Temperaturmessung sondern vom Ladestrom der Batterien.
Ist der Ladestrom z.B. über 20A, dann soll die Pumpe angehen.

meikvr6:
Wir reden nicht von einer Temperaturmessung sondern vom Ladestrom der Batterien.
Ist der Ladestrom z.B. über 20A, dann soll die Pumpe angehen.

Aaaahhh...! Jetzt ist die Kerze an.
Vor Jahren mal sowas i.d.A. mit PzS gesehen...

Dann sollte das mit der Quasi-Hysterese eigentlich kein Problem sein.
Ringspeicher füllen, wenn oberer Durchschnitt erreicht, dann Umpumpen für 10 Minuten oder bis unterer Durchschnitt erreicht, dann Pumpe in Pause schicken. Wenn Pause um, fange an von vorn.

Einzig darauf achten, das Du im Setup() den Ringspeicher einmal füllst.
Alles andere läuft von selbst.

Wenn ich das Problem halbwegs richtig verstanden habe, dann würde das bei mir in etwa so aussehen:

#include <TaskMacro.h>

#include <CombieTypeMangling.h>
using namespace Combie::Millis;

#include <CombiePin.h>
using Pumpe = Combie::Pin::InvOutputPin<13>; // invertierender Output

#include <CombieTools.h>
// 1.0/11.0 entspricht in etwa einem Array mit 11 Zellen fuer die Berechnung des Mittelwertes
Combie::Tools::GleitenderMittelwert<uint16_t> gmw {1.0/11.0};

constexpr uint16_t obereSchwelle  {515};
constexpr uint16_t untereSchwelle {480};
Combie::Tools::SchmittTrigger<uint16_t> st {obereSchwelle,untereSchwelle};


constexpr byte sensor {A0};
bool pumpAnforderung {false}; // Uebergabe Merker

Task messen()
{
  taskBegin();
  gmw.setInitial(analogRead(sensor)); // vorbesetzen
  while(1)
  {
    taskPause(100_ms); // pause zwischen den Messungen
    pumpAnforderung = st = gmw = analogRead(sensor);
  }
  taskEnd();
}

Task pumpen()
{
  taskBegin();
  Pumpe().init();
  while(1)
  {
    taskWaitFor(pumpAnforderung)
    Pumpe() = 1;
    taskPause(10_min);
    Pumpe() = 0;
    taskPause(10_min);
  }
  taskEnd();
}



void setup() 
{
}

void loop() 
{
  messen();
  pumpen();
}

Hallo,

such mal nach "gleitender Mittelwert". Das ist letzlich ein Dreizeiler. Du wirst auch hier im Forum was dazu finden.

Heinz

Ich hoffe man kann jetzt verstehen was ich da treibe

https://www.ehbatterien.ch/produkt/elektrolytumwaelzung/

meikvr6:
Ich hoffe man kann jetzt verstehen was ich da treibe

Nein.
Die Bilder liegen bei einem externen Hoster - da komm ich nicht hin.
Ausserdem fliegen die da in ganz kurzer Zeit dort raus - dann wiess hier niemand mehr, was Du da meinst.

Editiere deinen Post und lösche die Links.
Dann unten auf attachment geklickt - ein Bild eingehangen und dann auf das nächste Attachment und so weiter.
Save und schon gehts hier weiter.

Ich hoffe man kann jetzt verstehen was ich da treibe

Das hatte ich vorher schon verstanden!
Bin ja schließlich auch kein kleiner Doofmann.

Und dennoch: Fotos sind immer gut.

Hallo
Leider kann ich im Moment nicht mehr testen mein UNO ist leider kaputt.
@my_xy_projekt ich habe leider keine attachment Funktion.
@combie ich muss erlich sagen deinem Programm kann ich als Anfänger nicht mehr folgen...
Ich werde es aber gerne mal Testen.

Könnte das funktionieren?

/*Pumpensteuerung
 *Version 1.0 
 *05.02.2021 
 */


const int inputPin = A0;
const int relai = 6;
int smoothWert = 0;

int relaiState = LOW;
long myTimer = 0;
long myTimeout = 48000;



void setup (){ 
Serial.begin(9600);
pinMode(relai,OUTPUT);
}

void messung() {                                          //Messwerterfassung
static int oldVal = 0;
int val = analogRead(inputPin);
if (val != oldVal){
int volt = map(val,0,1023,0,502);
Serial.print(val);
Serial.print(",");
Serial.print(volt/100.0);
Serial.println(" V");  
oldVal = val;
}
delay(500);
}

void schaltung() {                                          // Steuerung des Relais
smoothWert = 0.6 * smoothWert + 0.4 * analogRead(inputPin); // Glättung
  if (smoothWert)> 520){                                    // Grenze Einschaltstrom
  digitalWrite(relai,HIGH);
 if (millis() > myTimeout + myTimer ) {                     // Ausschaltverzögerung
    myTimer = millis();

    if (relaiState == LOW) relaiState = HIGH;
    else relaiState = LOW;

    digitalWrite(relai, relaiState);
    delay(60000);                                          //Einschaltverzögerung
  }
  } else {
   digitalWrite(relai,LOW);   
}
}
  
void loop() {
messung();
schaltung();
}

meikvr6:
Leider kann ich im Moment nicht mehr testen mein UNO ist leider kaputt.
@my_xy_projekt ich habe leider keine attachment Funktion.

Könnte das funktionieren?

    delay(60000);                                          //Einschaltverzögerung

Letzte Frage zuerst: Was macht da das delay? Und nein der Komentar ist keine Erklärung...

Ansonsten hast Du unter dem Editor zu stehen:
"Attachments and other options" - da klick mal drauf.

Das mit dem kaputten UNO tut weh ;(