Gastank mit Schwimmer

Hallo Forum,

ich habe folgendes:
Gastank mit Schwimmer, 1 Anzeige (Direkt am Tank) wie voll dieser noch ist, eine die Digital umgerechnet wird, und Widerstände für Voll-Mittel-Lehr.

Z.Z. gibt die Anzeige (Digital) 4 LED´s an. Heißt, wenn alle 4 LED´s an sind ist der Tank voll, 2 ist die häflte, und keine ist logischerweise Lehr :slight_smile:
Da mir diese Anzeige zu ungenau ist, habe ich mir eine Anzeige gebaut (Arduino) die den Widerstand abgreift und auswertet in % und Balken.
Nur dieser Tank bewegt sich, so das es zu Schwankungen in der Prozentzahl sowie auch in den Balken gibt :frowning: .
Die Zeit hab ich schon verändert aber das bringt mich nicht wirklich weiter.
Erst zeigt die Anzeige z.B. 75 % an und nach 5 sec 34% und wieder 5sec. später 85% usw. .
Also dieses Schwanken geht mir auf den Keks. DAs einzige was ruhig bleibt ist der Analoge Zeiger vom Tank direkt. KA warum diese nicht schwank :frowning:
Gibt es vllt. irgendeine Idee/Lösung die mir da aushelfen kann? Z.B. ein Algoritmus der innerhalb von z.B. 5 min. die Bewegungen aufzeichnet und davon z.B. die 3. höchste Zahl (von allen aufgezeichneteten) annimmt, die aber nicht über die alte Zahl die vor 5 min angezeigt wurde drüber hinaus kommt ? Hoffentlich konnte ich mich richtig ausdrücken.

Ich schaff das einfach nicht zu realisieren. so gut bin ich da nicht :frowning:

Danke für Eure Mühe! :slight_smile:

Ich würde annehmen, dass die Anzeige immer um den richtigen Mittelwert herumschwankt. Also musst du mehrere Messungen machen und eine Mittelwertbildung vornehmen. Man könnte bspw. ein Array in gewissen Zeitabständen befüllen und wenn ein neuer Messwert anliegt den ältesten Messwert rauskicken (FIFO-Prinzip).
Alternativ spricht auch nichts dagegen, das Signal vorab mit einem RC-Glied zu glätten - das spart zumindest Speicher im Arduino.

Wie wäre es mit einem Integrator?
hier wäre einer
So ungefähr und das nur nach prüfung ob der neue Wert < Altwert ist,
Die Wert Variabelen müssen datatype long sein

Wertaktuell=analogRead(A0);       // Aktuellwert Einlesen
X=Wertneu*10;                         // Neuerwert nur 10% Einfluss auf den alten Diesen Einfluss dann 
Y=Wertalt*90;                         // Wert aus dem Vorherigien Zyklus auf 90% diesen auch zusammen müssen 100% ereben
Wertneu=X+Y/100;                 // Beide Werte addieren und Neuen Wert Bilden
Wertalt=Wertneu;                 // Für den nächsten Zyklus ist der neue (aktueller gesmoother Wert)

Somit hat pro Lesezyklus der neue Wert nur 10% Einfluss dass heisst würde der neue Gelesene Wert 0 haben so hat der nur 10% Einfluss und würde den Wert in einem Zyklus um 10% nach unten korrigieren.
Damit kannst du eine schöne Smoothesfekt machen das heisst aussreise des Schwanken beeinflussen den Wert nur gering. Und du hängst auch nicht in einer for-Schleife im einem Programm fest. Wenn dein neu eingelesener Wert noch weniger einfluss haben soll dann die Multipiktoren auf 5/95 setzten soll der neue Wert mehr einfluss haben dann z.B. auf 20/80 setzten.

Gruß
Der Dani

Hier mal mein jetziger Code mit Balkenanzeige und Prozentanzeige :wink:
Wie binde ich das da jetz ein? weil soweit bin ich echt noch nicht um euch da voll folgen zu können. :frowning:

    #include <LiquidCrystal.h>
    #include <LcdBarGraph.h>
     
    byte lcdNumCols = 16; // -- Anzahl der vollen Balken im LCD
    byte sensorPin = 1; // -- Analogeingang
     
    LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // -- Aufbau der LCD Verbindung
    LcdBarGraph lbg(&lcd, lcdNumCols);  // -- Aufbau der Balken
     
/////////////////////////////////////////////////////////////////////////////////////////////////    
    
    void setup(){

      analogReference (INTERNAL);   //  Interne Spannung 1,1 V DC am AREF Ausgang
      lcd.begin(2, lcdNumCols);   // -- Initialisierung des LCD
      lcd.clear();
      delay(1000);
    }
     
     
//////////////////////////////////////////////////////////////////////////////////////////////////     
    void loop()
    {
 
      lbg.drawValue( analogRead(sensorPin), 1024);   // -- Setzt den Wert vom Analogen Eingang
      delay(300);
         
         
 int poti = analogRead(sensorPin);                  // Poti-Wert auslesen, max. Wert 1024
 int potiVal = ((poti/4) * 100)/251;                // Potiwert in Prozent umrechnen
 lcd.setCursor(0,1);                                // 0 = Zeile  ;  1 = Spalte
 lcd.print("Tankinhalt");
 lcd.setCursor(14,1);
 lcd.print("%");
 lcd.setCursor(11,1); 
 lcd.print(potiVal);
  if( potiVal < 100 )                               // Ist potiVal kleiner als 100 setze ein leerzeichen
  lcd.print( " " );
  if( potiVal < 10 )                                // Ist potiVal kleiner als 10 setze ein leerzeichen
  lcd.print( " " );

 delay(300);
    }

Ungfähr so:

    #include <LiquidCrystal.h>
    #include <LcdBarGraph.h>
     
    byte lcdNumCols = 16; // -- Anzahl der vollen Balken im LCD
    byte sensorPin = 1; // -- Analogeingang
 
    long data = 0;

    LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // -- Aufbau der LCD Verbindung
    LcdBarGraph lbg(&lcd, lcdNumCols);  // -- Aufbau der Balken
     
/////////////////////////////////////////////////////////////////////////////////////////////////    
    
    void setup(){

      analogReference (INTERNAL);   //  Interne Spannung 1,1 V DC am AREF Ausgang
      lcd.begin(2, lcdNumCols);   // -- Initialisierung des LCD
      lcd.clear();
      delay(1000);

      //einmal initialen Wert setzen, damit nicht bei "0" angefangen wird
      data = analogRead(sensorPin);
    }
     
     

//////////////////////////////////////////////////////////////////////////////////////////////////     
    void loop()
    {
      data = ((data * 90 ) + ( analogRead(sensorPin) * 10)) / 100;
      
      lbg.drawValue( data, 1024);   // -- Setzt den Wert vom Analogen Eingang
      delay(300);
         
      int potiVal = ((data/4) * 100)/251;                // Potiwert in Prozent umrechnen
 lcd.setCursor(0,1);                                // 0 = Zeile  ;  1 = Spalte
 lcd.print("Tankinhalt");
 lcd.setCursor(14,1);
 lcd.print("%");
 lcd.setCursor(11,1); 
 lcd.print(potiVal);
  if( potiVal < 100 )                               // Ist potiVal kleiner als 100 setze ein leerzeichen
  lcd.print( " " );
  if( potiVal < 10 )                                // Ist potiVal kleiner als 10 setze ein leerzeichen
  lcd.print( " " );

 delay(300);
    }

Ich habe zwei Dinge geändert. Zum Einen wird das Wert nur einmal je loop() eingelesen und dann immer nur der gelesene Wert verarbeitet. Das sollte man grundsätzlich so machen, damit man an allen Stellen mit dem gleichen Wert rechnet. Vor allem aber, wenn die gemessenen Werte stark schwanken. Als Zweites habe ich die Formel von volvodani eingebaut, allerdings etwas mehr zusammengefasst, um Variablen zu sparen.
Der Trick dieser Formel ist, das Du den neuen Wert nicht ausschliesslich aus dem gemessenen Wert nimmst, sondern den vorherigen Messwert auch mit verwendest.
Das macht die Formel:

data = ((data * 90 ) + ( analogRead(sensorPin) * 10)) / 100;

Ein Beispiel.

Angenommen "data" hat den Wert von 730 und mittels analogRead() werden aktuell 840 gemessen und im nächsten Durchlauf wieder nur 590 und dann wieder 710, dann hättest Du die stark schwankende Anzeige die Du jetzt hast.
Setzen wir nun mal die Wert in die Formel ein.

  1. Wert: 730 (initiale Messung im setup())
  2. Wert: ((730 * 90) + (840 * 10)) / 100 = 741 ~ 72%
  3. Wert: ((741 * 90) + (590 * 10)) / 100 = 726 ~ 71%
  4. Wert: ((726 * 90) + (710 * 10)) / 100 = 724 ~ 71%

Nun das Ganze nochmal mit 95/5

  1. Wert: 730 (initiale Messung im setup())
  2. Wert: ((730 * 95) + (840 * 5)) / 100 = 735 ~ 72%
  3. Wert: ((735 * 95) + (590 * 5)) / 100 = 728 ~ 71%
  4. Wert: ((728 * 95) + (710 * 5)) / 100 = 727 ~ 71%

Man sieht, die Werte schwanken noch weniger. Das Ganze klappt natürlich nur, wenn der eigentliche Messwert ohne Fehler sich nur sehr langsam ändert. Ich hoffe jetzt ist es etwas klarer.
Mario.

Der Code ist ein bisschen Unglücklich. Ich habe mal die delays raus geschmissen und Routinen rein gebracht die einmal alle 10ms durchlaufen werden, für den Integrator. Und eine Routine mit 300ms für das Display. Dadurch verbringt die CPU keine Zeit in nichts tun.
Diese Art von "warten" bzw.. Durchlaufen in einem bestimmten Zeitpunkt findest du auch in dem Beispiel "Blink without delay".
Dadurch hast du eine Durchlaufzeit in dem ganzen Programm von weniger als 1ms und kannst viel einfacher nachher deinen Code erweitern ohne nochmal alles umbauen zu müssen

#include <LiquidCrystal.h>
#include <LcdBarGraph.h>

byte lcdNumCols = 16; // -- Anzahl der vollen Balken im LCD
byte sensorPin = 1; // -- Analogeingang

long data = 0;
unsigned long actmillis,lmillis1,lmillis2=0;
int time1=300;
int time2=10;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // -- Aufbau der LCD Verbindung
LcdBarGraph lbg(&lcd, lcdNumCols);  // -- Aufbau der Balken

/////////////////////////////////////////////////////////////////////////////////////////////////    

void setup(){

  analogReference (INTERNAL);   //  Interne Spannung 1,1 V DC am AREF Ausgang
  lcd.begin(2, lcdNumCols);   // -- Initialisierung des LCD
  lcd.clear();
  delay(1000);

  //einmal initialen Wert setzen, damit nicht bei "0" angefangen wird
  data = analogRead(sensorPin);
}



//////////////////////////////////////////////////////////////////////////////////////////////////     
void loop()
{
  actmillis=millis();
  if (actmillis-lmillis2>time2){                                      // alle 10ms durchlaufen um den Integrator ein bisschen besser zu machen
    data = ((data * 90 ) + ( analogRead(sensorPin) * 10)) / 100;
    int potiVal = ((data/4) * 100)/251;                                        // Potiwert in Prozent umrechnen
    lmillis2=millis();  
  }

  if (actmillis-lmillis1>time1){                           // alle 300ms durchlauf da 3mal in der Sekunde das Display auffrischen reicht
    lbg.drawValue( data, 1024);   // -- Setzt den Wert vom Analogen Eingang
    lcd.setCursor(0,1);                                // 0 = Zeile  ;  1 = Spalte
    lcd.print("Tankinhalt");
    lcd.setCursor(14,1);
    lcd.print("%");
    lcd.setCursor(11,1); 
    lcd.print(potiVal);
    if( potiVal < 100 )                               // Ist potiVal kleiner als 100 setze ein leerzeichen
      lcd.print( " " );
    if( potiVal < 10 )                                // Ist potiVal kleiner als 10 setze ein leerzeichen
      lcd.print( " " );
    lmillis1=millis()
    }
  }

Gruß
Der Dani

Das hät ich in 4 Monaten nicht hinbekommen, was ihr da in einer Nacht gewerkelt habt :D.

Also Mario´s konnt ich noch verfolgen aber das mit den Millis usw. das wirft mich aus der Bahn.
Ich muss halt noch viel üben und dann kommt das von alleine :wink:
Das mit den "starken Schwankungen" kann ich nicht vermeiden :frowning: werd ich merken.
Ich werde es testen und dann berichten :wink:

Frage an beide : Ist da jetz sowas eingebaut das der "neue Wert" nicht höher kommen kann wie der alte? (Ich Persönlich würde behaupten ja durch die Formel)

Danke an euch BEIDEN! :slight_smile:

KAmin:
Frage an beide : Ist da jetz sowas eingebaut das der "neue Wert" nicht höher kommen kann wie der alte? (Ich Persönlich würde behaupten ja durch die Formel)

Ähm, nein. Natürlich kann der neue Wert größer sein als der alte. Sonst dürftes Du den Tank nie wieder füllen. Außerdem mußt Du bei schwankenden Messwerten immer alle Werte berücksichtigen. Sonst würde bei zu kleinen Messwerten der Durchscnittswert auch immer kleiner werden.
Die Formel sorgt nur dafür das die aktuellen Messwerte keine großen Sprünge der Daten mehr erzeugen.
Mario.

Ja wer lesen kann und nachdenkt erkennt das auch .... :frowning: sorry. Hast ja recht :wink:

EDIT : Eben hab ich aber noch einen Fehler endekt oder vielmer das Prog.

potival ist nicht declariert :frowning: ??

Mario seins funktioniert!

deklarier einfach "int potiVal" außerhalb des if-Blocks in dem es gesetzt wird. Z.B. am Anfang der loop() oder komplett außerhalb als globale Variable, dann sollte es gehen. Die Variable ist außerhalb des if-Blocks nicht sichtbar.
Waren noch zwei kleine andere Dinge drin. Das hier sollte funktionieren:

#include <LiquidCrystal.h>
//#include <LcdBarGraph.h>

byte lcdNumCols = 16; // -- Anzahl der vollen Balken im LCD
byte sensorPin = 1; // -- Analogeingang

long data = 0;
unsigned long actmillis,lmillis1,lmillis2=0;
int time1=300;
int time2=10;

int potiVal;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // -- Aufbau der LCD Verbindung
//LcdBarGraph lbg(&lcd, lcdNumCols);  // -- Aufbau der Balken

/////////////////////////////////////////////////////////////////////////////////////////////////    

void setup(){

  analogReference (INTERNAL);   //  Interne Spannung 1,1 V DC am AREF Ausgang
  lcd.begin(2, lcdNumCols);   // -- Initialisierung des LCD
  lcd.clear();
  delay(1000);

  //einmal initialen Wert setzen, damit nicht bei "0" angefangen wird
  data = analogRead(sensorPin);
}



//////////////////////////////////////////////////////////////////////////////////////////////////     
void loop()
{
  actmillis=millis();
  if (actmillis-lmillis2>time2){                                      // alle 10ms durchlaufen um den Integrator ein bisschen besser zu machen
    data = ((data * 90 ) + ( analogRead(sensorPin) * 10)) / 100;
    potiVal = ((data/4) * 100)/251;                                        // Potiwert in Prozent umrechnen
    lmillis2=millis();  
  }

  if (actmillis-lmillis1>time1){                           // alle 300ms durchlauf da 3mal in der Sekunde das Display auffrischen reicht
    //lbg.drawValue( data, 1024);   // -- Setzt den Wert vom Analogen Eingang
    lcd.setCursor(0,1);                                // 0 = Zeile  ;  1 = Spalte
    lcd.print("Tankinhalt");
    lcd.setCursor(14,1);
    lcd.print("%");
    lcd.setCursor(11,1); 
    lcd.print(potiVal);
    if( potiVal < 100 )                               // Ist potiVal kleiner als 100 setze ein leerzeichen
      lcd.print( " " );
    if( potiVal < 10 )                                // Ist potiVal kleiner als 10 setze ein leerzeichen
      lcd.print( " " );
    lmillis1=millis();
    }
  }

Mario ich dank dir! :wink:
Ich hab da zwar keine große Veränderung gefunden :smiley: aber es geht.
Sei mir nicht böse aber die Balkenanziege habe ich wieder mit rein genommen. (Will diese auch beherschen :wink: )
Was mich gleich zu einer weiteren Frage aufwirft.

Wenn ich den Arduino starte geht alles mit den Balken i.o.
Manchmal starte ich den und es verzieht sich eine kleine Apalte in der Balkenanzeige die beim nächsten start wieder weg ist. Mhh...
Was ist das für ein Misterium?
Liest das die IC zu schnell?

#include <LiquidCrystal.h>
#include <LcdBarGraph.h>

byte lcdNumCols = 16; // -- Anzahl der vollen Balken im LCD
byte sensorPin = 1; // -- Analogeingang

long data = 0;
unsigned long actmillis,lmillis1,lmillis2=0;
int time1=300;                         // Zeit für LCD
int time2=10000;                       // Zeit für Loopdurchlauf

int potiVal;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // -- Aufbau der LCD Verbindung
LcdBarGraph lbg(&lcd, lcdNumCols);  // -- Aufbau der Balken

/////////////////////////////////////////////////////////////////////////////////////////////////    

void setup(){

  analogReference (INTERNAL);   //  Interne Spannung 1,1 V DC am AREF Ausgang
  lcd.begin(2, lcdNumCols);   // -- Initialisierung des LCD
  lcd.clear();
  delay(1000);

  //einmal initialen Wert setzen, damit nicht bei "0" angefangen wird
  data = analogRead(sensorPin);
}



//////////////////////////////////////////////////////////////////////////////////////////////////     
void loop()
{
  actmillis=millis();
  if (actmillis-lmillis2>time2){                                      // alle 10ms durchlaufen um den Integrator ein bisschen besser zu machen
    data = ((data * 95 ) + ( analogRead(sensorPin) * 5)) / 100;
    potiVal = ((data/4) * 100)/251;                                        // Potiwert in Prozent umrechnen
    lmillis2=millis();  
  }

  if (actmillis-lmillis1>time1){                           // alle 300ms durchlauf da 3mal in der Sekunde das Display auffrischen reicht
    lbg.drawValue( data, 1024);   // -- Setzt den Wert vom Analogen Eingang
    lcd.setCursor(0,1);                                // 0 = Zeile  ;  1 = Spalte
    lcd.print("Tankinhalt");
    lcd.setCursor(14,1);
    lcd.print("%");
    lcd.setCursor(11,1); 
    lcd.print(potiVal);
    if( potiVal < 100 )                               // Ist potiVal kleiner als 100 setze ein leerzeichen
      lcd.print( " " );
    if( potiVal < 10 )                                // Ist potiVal kleiner als 10 setze ein leerzeichen
      lcd.print( " " );
    lmillis1=millis();
    }
  }

Sorry, die Balkenanzeige hatte ich nur für mich rausgenommen, damit ich es cmpilieren kann. Hatte die Lib nicht zur Hand. Hab dann beim posten vergessen die Kommentare zu entfernen.
Die "kleinen" Unterschiede sind aber genau das wichtige. Daher nochmal genau schauen was sich geändert hat. Falls Du ein Linux oder einen Mac zur Hand hast, das Tool "diff" ist da sehr hilfreich.