[Solved] Werte mit Encoder ändern

Guten Abend,

eigentlich wollte ich ursprünglich ja anhand von Temperatur, Feuchte, ... verschiedene Dinge steuern. Irgendwie bin ich jetzt aber auf das Wetter gestoßen und möchte einen reinen Datenlogger mit dem Arduino bauen.

Der Datenlogger soll auf einer großen Wiese installiert werden und dort verschiedene Wetterparameter erfassen. Dazu noch den Pegel eines Bachs. Zu solch einer Wetterstation gehört ja auch ein gescheites Menü. Da ich vielleicht auch direkt an der Wetterstation etwas an den Einstellung ändern möchte (Messrate, ...) experimentiere ich damit, wie man mit dem Encoder Einstellungen ändern kann.

So sieht mein (zusammenkopierter) Sketch aus:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

LiquidCrystal_I2C lcd (0x27, 20, 4);
Encoder myEnc (3, 5);

int menue, encoderPos, oldEncoderPos, ergebnis, value, oldEncoderPosMenue, first;
int measure = 41;
int averaging = 10;
int menues = 2;
int Cursor = -1;
long counter;

float humidity, aIN[5];

void setup () {
  Serial.begin(9600);
  rtc.begin(DateTime(__DATE__, __TIME__));
  lcd.init();
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print(F("Wetter Datalogger"));
  lcd.setCursor(0, 1);
  lcd.print(F("Param: OK"));
  lcd.setCursor(0, 2);
  lcd.print(F("Starting"));
  for (int i = 0; i < 6; i++){
    delay(1000);
    lcd.print(F("."));
  }
  lcd.clear();
  
  // Pin-Modes
  pinMode(4, INPUT);
  pinMode(A0, INPUT);
}

void loop()
{
  readMenuePos();
  switch(menue){
    case 0:   if (editParam() == true){
                if (Cursor == 0) measure = ergebnis * 10 + measure % 10;
                if (Cursor == 1) if (ergebnis != 0) ergebnis = ergebnis - 1; measure = measure + ergebnis;
                Cursor++;
                counter = 0;
                first = 0;
                //oldEncoderPos = 0;
                //Encoder myEnc (3, 5);
                //encoderPos = 0;
                ergebnis = 0;
              }
              lcd.setCursor(0, 0);
              lcd.print(F("+ Logger V1.1"));
              lcd.setCursor(0, 1);
              lcd.print(F("Sprache: Deutsch"));
              lcd.setCursor(0, 2);
              lcd.print(F("Messrate: "));
              
              // Normaler Modus
              if (Cursor == -1){
                emptyZero(measure);
                lcd.print(F(" sek"));
                lcd.setCursor(20, 3);
                lcd.print(F("Mittelung: "));
                lcd.print(averaging);
                lcd.print(F(" min"));
              }
              
              // Messrate erste Stelle
              if (Cursor == 0){
                if (first == 0){
                  ergebnis = measure % 100;
                  ergebnis = ergebnis - (ergebnis % 10);
                  ergebnis = ergebnis / 10;
                  value = ergebnis;
                  first = 1;
                } else {
                  ergebnis = editValue();
                }
                lcd.setCursor(10, 2);
                if (counter == 0 || counter > 5){
                  lcd.write(255);
                  if (counter > 10){
                    counter = 0;
                  }
                } else {
                  lcd.print(ergebnis);
                }
                Serial.print(F("Neuer Wert Pos 1: "));
                Serial.println(ergebnis);
                counter++;
                lcd.print(measure % 10);
                lcd.print(F(" sek"));
                lcd.setCursor(20, 3);
                lcd.print(F("Mittelung: "));
                lcd.print(averaging);
                lcd.print(F(" min"));
              }
              
              // Messrate zweite Stelle
              if (Cursor == 1){
                lcd.setCursor(10, 2);
                ergebnis = measure % 100;
                ergebnis = ergebnis - (ergebnis % 10);
                ergebnis = ergebnis / 10;
                lcd.print(ergebnis);
                lcd.setCursor(11, 2);
                if (first == 0){
                  ergebnis = measure % 10;
                  value = ergebnis;
                  first = 1;
                } else {
                  ergebnis = editValue();
                }
                Serial.print(F("Neuer Wert Pos 2: "));
                Serial.println(ergebnis);
                if (counter == 0 || counter > 5){
                  lcd.write(255);
                  if (counter > 10){
                    counter = 0;
                  }
                } else {
                  lcd.print(ergebnis);
                }
                counter++;
                lcd.print(F(" sek"));
                lcd.setCursor(20, 3);
                lcd.print(F("Mittelung: "));
                lcd.print(averaging);
                lcd.print(F(" min"));
              }
              
              if (Cursor == 2) Cursor = -1;
    break;
    case 1:   meassureRoutine();
              lcd.setCursor(0, 0);
              lcd.print(F("rel. Feuchte"));
              lcd.setCursor(0, 1);
              lcd.print(F("Sensor: Haarhygro"));
              lcd.setCursor(0, 2);
              if (editParam() != true){
                lcd.print(F("Current: "));
                lcd.print(humidity, 1);
                lcd.print(F(" %"));
                lcd.setCursor(20, 3);
                lcd.print(F("Average: "));
                lcd.print(humidity, 1);
                lcd.print(F(" %"));
              }
              if (editParam() == true){
                lcd.setCursor(0, 2);
                lcd.print(F("Min: "));
                lcd.print(humidity, 1);
                lcd.print(F(" %"));
                lcd.setCursor(20, 3);
                lcd.print(F("Max: "));
                lcd.print(humidity, 1);
              }
    break;
  }
}

void meassureRoutine() 
{
  // Hier wird normalerweise gemessen
}

void readMenuePos()
{
  if (Cursor == -1){
    encoderPos = myEnc.read() / 6;
    if (encoderPos < oldEncoderPosMenue){
      menue++;
      if (menue > menues - 1){
        menue = 0;
      }
      lcd.clear();
      oldEncoderPosMenue = encoderPos;
    }
    if (encoderPos > oldEncoderPosMenue){
      menue--;
      if (menue < 0){
        menue = menues - 1;
      }
      lcd.clear();
      oldEncoderPosMenue = encoderPos;
    }
  }
}

boolean editParam()
{
 if (digitalRead(4) == 1){
   delay(500);
   if (digitalRead(4) == 1){
     return true;
   }
 }
}

void emptyZero(int number){
  if (number < 10){
    lcd.print(F("0"));
  }
  lcd.print(number);
}

int editValue()
{
  encoderPos = myEnc.read() / 4;
  if (encoderPos < oldEncoderPos){
    value++;
    if (value > 9){
        value = 0;
    }
    oldEncoderPos = encoderPos;
  }
  if (encoderPos > oldEncoderPos){
    value--;
    if (value < 0){
      value = 9;
    }
    oldEncoderPos = encoderPos;
  }
  return value;
}

Das Switchen zwischen den einzelnen "Seiten" funktioniert ganz gut.
Das Einstellen von z.B. Messrate funktioniert nur mittelmäßig. Z.B. werden manchmal die eingestellten Zahlen nicht übernommen oder wenn man gar nicht am Encoder dreht sondern nur den "Einstell-Modus" aktiviert werden die ursprünglichen Zahlen um eins verringert oder erhöht.
Das gefällt mir so nicht!! :0

Wie macht man so eine Steuerung, in der man Werte ändern kann (mit einem Encoder), professionell? Bzw. wo sind Denkfehler in meiner Steuerung?

Wäre super wenn Ihr mir mit dem "großen" Projekt helfen könntet.

Gruß,
Jan

-Jan-:
Wie macht man so eine Steuerung, in der man Werte ändern kann (mit einem Encoder), professionell? Bzw. wo sind Denkfehler in meiner Steuerung?

Wie man so eine Steuerung "professionell" macht, da gibt es wohl hunderte Möglichkeiten,

An Deinem Code fällt mir auf Anhieb zuerst das auf:

   delay(500);

Was möchtest Du mit der Blockierung des Programmablaufs für eine halbe Sekunde an dieser Stelle bewirken?

Delay-Zeiten oberhalb der menschlichen Reaktionszeit sind für interaktive Programme normalerweise tabu. Es sei denn, Du möchtest das Programm ausdrücklich während der Laufzeit des delay für sämtliche Interaktionen des Benutzers blockieren.

Kannst Du bitte mal Deine gesamte Programmlogik beschreiben, was Du mit drücken, drehen und einstellen in Deinem Sketch machen möchtest?

Wie soll die genaue Bedienerlogik aussehen, das ist mir anhand Deines Codes so nicht ganz klar?

Das sieht so aus als ob es zum Entprellen des Tasters gedacht ist. Aber selbst wenn man das mit delay() macht, sind 500ms viel, viel zu hoch. Dafür reichen meistens 10-30ms.

Hallo,

das Problem, dass das Ändern der Werte nicht richtig funktioniert hat, habe ich nun selbst in den Griff bekommen.
Das Problem ist einfach, dass wenn ich oldEncoderPos auf 0 setzet, muss ich auch den Wert von myEnc.read() auf 0 setzten, mit myEnc.write(). Außerdem darf ich die geänderte Einerstelle von der Messrate nicht direkt auf die aktuelle Messrate aufaddieren.

Die Delay-Zeit von 500 ms soll zum einen den Taster entprellen und auch verhindern, dass man in den Einstell-Modus gelangt, wenn man versehentlich beim Scrollen den Taster drückt. Da man als Maximale Abtastrate eh nur 1 Sekunde einstellen kann, sollte das Abtasten der Sensoren nicht wirklich davon beeinflusst werden.
Ziel ist es aber, dass das ganze fertige Programm ohne so eine "Bremse" läuft.

Programm- und Benutzerlogik:
Wenn man einfach nur am Encoder dreht, kann man zwischen den einzelnen Seiten umherschalten. Also von den Grundeinstellungen am Anfang bis zum aktuellen Messwert x. Am Ende angekommen, fängt das Menü wieder von vorn an.
Bei den Seiten, die mit einem "+" markiert sind, lassen sich bestimmte Werte ändern.
Wenn der Benutzer auf einer Seite ist, mit änderbaren Werten, muss er für 0,5 Sek. den Taster am Encoder drücken. Dann blinkt der Cursor am jeweiligen Wert und durch drehen am Encoder kann man den Wert erhöhen oder auch verkleinern. Ist man am letzten änderbaren Wert angekommen, drückt man nochmals auf den Taster und man gelangt wieder in den "View-Modus".

Bei den aktuellen Sensorwerten lässt sich nichts ändern. Trotzdem gibt es dort eine kleine Funktion: solange, wie man den Taster drückt, werden anstatt dem aktuellen Wert und dem Mittelwert die Extremwerte angezeigt.

Die geänderten Parameter werden dann weiter im Programm verwendet. Nach einem Reset sind sie halt verloren. In meinem Fall aber nicht tragisch.

Interessant wäre zu wissen, wie solche Menüs bei "professionellen Geräten" aufgebaut sind und funktionieren. Und wie man mein Menü verbessern kann. (1. Schritt: Delay muss raus!)

Gruß
Jan