Aquariencomputer

Langsam komme ich an meine grenzen.
Für meinen Aquariencomputer möchte ich die Temperatur über ein Menü in 0.5 Schritten einstellen können.

Versuche es mit folgendem aber leider wird mir die Temperatur nicht mit nachkommastellen angezeigt.

Im Tab menuWertesetzen seht:

sTemp = setDisplayTemp(-1, sTemp, 18.0, 30.0, false, 7, 1);

im Tab menuWerte steht:

int setDisplayTemp (int key, float valueT, float valueTMin, float valueTMax, boolean Null, int displayColumn, int displayRow)
{
  switch(key)
  {
  case UPKEY:
      valueT = (valueT + 0.5);
      if (valueT > valueTMax)
        valueT = valueTMin;
      break;
  case DOWNKEY:
      valueT = (valueT - 0.5);
      if (valueT < valueTMin)
        valueT = valueTMax;
      break;
  case LEFTKEY:
    valueT = (valueT - 1.0);
    if (valueT < valueTMin)
      valueT = valueTMax;
    break;
  case RIGHTKEY:
    valueT = (valueT + 1.0);
    if (valueT > valueTMax)
      valueT = valueTMin;
    break;
  case SELECTKEY:
    MenuTiefe--;
    lcd.noBlink();
    break;
  }

  char sValueT[8];
  if (Null)
    sprintf(sValueT, "%03d C", valueT);
  else
    sprintf(sValueT, "%3d C", valueT);  
  lcd.setCursor(displayColumn +3 , displayRow);
  lcd.print (sValueT);
  lcd.setCursor(displayColumn +6 , displayRow);
  
  if (key == SELECTKEY)
  {
    lcd.setCursor(displayColumn +2, displayRow);
    lcd.print("      ");
  }

  return valueT;

Kann mir jemand helfen?

Ein sprintf("%03d", fvalue) gibt immer nur den ganzzahligen Teil der Fliesskommazahl aus. "%.02f" kennt die C-Bibliothek für AVR leider nicht, also bleibt nur noch das dtostrf(): avr-libc: <stdlib.h>: General utilities

pylon:
Ein sprintf("%03d", fvalue) gibt immer nur den ganzzahligen Teil der Fliesskommazahl aus. "%.02f" kennt die C-Bibliothek für AVR leider nicht, also bleibt nur noch das dtostrf(): avr-libc: <stdlib.h>: General utilities

Wenn ich da jetzt noch durchsteigen würde, wäre ich froh.
Könntest Du mir die CodeZeile mal so schreiben wie es klappen könnte?

so ists bei mir

  {
    char lcdline[17];
    /* --------- INIT --------- */
    LCDML.FuncInit();
   
    /* --------- LOOP --------- */
    if(LCDML.Timer(g_timer_wait, 1000))
    {
      lcd.clear();    
      lcd.print(F("Soll:"));// F Macro => Flashspeicherung des Strings 
      lcd.setCursor(6,0);
      dtostrf(temp1, 4, 1, lcdline); 
      lcd.print(lcdline);
      lcd.print(F("\337")); // F Macro => Flashspeicherung des Strings 
      
      lcd.setCursor(0,1);
      lcd.print(F("Ist:"));// F Macro => Flashspeicherung des Strings 
      lcd.setCursor(6,1); 
      dtostrf(Temp, 4, 1, lcdline); 
      lcd.print(lcdline);
      lcd.print(F("\337")); // F Macro => Flashspeicherung des Strings 
      
    }

Danke, habe es nun auch hin bekommen.
Bei mir sieht es nun so aus:

float setDisplayTemp (int key, float valueT, float valueTMin, float valueTMax, boolean Null, int displayColumn, int displayRow)
{
  switch(key)
  {
  case UPKEY:
      valueT = valueT + 0.5;
      if (valueT > valueTMax)
        valueT = valueTMin;
      break;
  case DOWNKEY:
      valueT = valueT - 0.5;
      if (valueT < valueTMin)
        valueT = valueTMax;
      break;
  case LEFTKEY:
    valueT = valueT - 1.0;
    if (valueT < valueTMin)
      valueT = valueTMax;
    break;
  case RIGHTKEY:
    valueT = valueT + 1.0;
    if (valueT > valueTMax)
      valueT = valueTMin;
    break;
  case SELECTKEY:
    MenuTiefe--;
    lcd.noBlink();
    break;
  }

  char sValueT[8];
  if (Null)
    dtostrf(valueT,4,2,sValueT);
  else
    dtostrf(valueT,4,2,sValueT);
    lcd.setCursor(displayColumn +3 , displayRow);
    lcd.print (sValueT);
    lcd.write ((byte)0);
    lcd.print ("C");
    lcd.setCursor(displayColumn +6 , displayRow);
  
  if (key == SELECTKEY)
  {
    lcd.setCursor(displayColumn +2, displayRow);
    lcd.print("        ");
  }

Wofür genau steht 4 ,1 in der Zeile dtostrf(temp1, 4, 1, lcdline); ?

Habe aber schon wieder ein Problem. Lese mit dieser Methode die Temperatur aus.

void loop() 
{
  sensors.requestTemperatures();
  
  iTemp = sensors.getTempCByIndex(0);

Wenn ich mich nun aber im Menü befinde reagiert das Board sehr träge auf die Tastendrücke. Wird wahrscheinlich an der Einleseverzögerung liegen.
Wenn ich die beiden Zeilen aus dem Code raus nehme ist alles flüssig. Gibt es eine andere Methode die Temperatur aus zu lesen?

Die Temperatur muss ja nicht andauernd eingelesen werden.

unsigned long g_timer_temp_measurement = 0;

void loop()
{
  if(g_timer_temp_measurement < millis()) 
  {
    g_timer_temp_measurement = millis()+500;  // alle 500 ms wird diese nun die temp abgefragt
    sensors.requestTemperatures();  
    iTemp = sensors.getTempCByIndex(0);
  }
}

Edit:
Wenn du mehrere Dinge hast die Zeitlich gesteuert ablaufen sollen geht es einfacher hiermit:
einfache Threads

Dann verliert man auch nicht mehr den Überblick

Wofür genau steht 4 ,1 in der Zeile dtostrf(temp1, 4, 1, lcdline); ?

es werden 28,45 grad angezeigt. Bei 3 wäre es dann 28,4

Dass es bei dir so langsam ist, liegt daran dass die Temperatur immer wieder ganz schnell ausgelesen wird und der Arduino es nicht schafft andere Aufgaben schneller zu erledigen.

Dies ist mein ganzer code von Temperatur Ausgabe

/* *********************************** */
/* Thread_init_and_get_temp  
/* *********************************** */
/* thread setup */
  void simpleThread_setup(Thread_init_and_get_temp)
  {       
  }
/* thread loop */
  boolean simpleThread_loop(Thread_init_and_get_temp)
  {
    if ( !ds.search(addr)) 
    {
      // No more Dallas chips present:
      ds.reset_search();            
      return false;
    }

    // Check for family code which is not DS18B20:
    if ( addr[0] != 0x28) {
      return false;
    }

    // The DallasTemperature library can do all this work for you!
    ds.reset();
    ds.select(addr);
    ds.write(0x44,0);          // start conversion, with parasite power off
    
 
    // Check whether chip is present:
    present = ds.reset();
    ds.select(addr);    
    ds.write(0xBE);             // Read Scratchpad
    for ( dsread = 0; dsread < 9; dsread++) 
    {   // we need 9 bytes of data
      data[dsread] = ds.read();
    }
 
    Temp =  ((data[1] << 8) + data[0] ) * 0.0625;  // 12Bit = 0,0625 C per Bit

    //Überprüfen ob die minimale Temperatur überschritten ist
    if (Temp < temp_min)
    {
      simpleThread_start(Thread_check_temp_warning);
    }
    //Überprüfen ob die maximale Temperatur überschritten ist
    else if (Temp > temp_max)
    {
      simpleThread_start(Thread_check_temp_warning);
    }
    else
    {
      simpleThread_stop(Thread_check_temp_warning);
      digitalWrite(alarm, HIGH);
      
    }
    if (Temp < temp1)
    {
      digitalWrite(heizung, HIGH);

    }
    else if (Temp > temp1)
    {
      digitalWrite(heizung, LOW);

    }
}

Dass was Jomelo da gebastelt hat ist ne ganz feine Sache, solltest du mal anwenden und weiter empfehlen!

Danke Jomelo, aber das klappt nicht. Am Anfang wird mir zwar die Temperatur angezeigt und auch ein paar mal aktualisiert aber dann hängt er sich auf.
hHabe mir mal mit Serial.println (g_timer_temp_measurement); den Wert anzeigen lassen.

Das kommt dabei raus:
...
27692
28490
29286
30084
30881
31678
32476
-32263
-32263
-32263
-32263
usw.

Kannst du mal den Code hier einstellen ?

Es sieht für mich so aus als hättest du nicht den Datentype unsigned long verwendet, sondern irgendetwas anderes.
Oder vielleicht die Variable in der Loop Schleife angelegt.

Du könntest auch diesen Quellcode Verwenden:

#include <simpleThread.h>   
  
simpleThread_init(1); //init one new thread 
simpleThread_new_timebased_static  (0, thread_get_temp, 500); //id, thread_name, loop_time 

simpleThread_priority {
  simpleThread(thread_get_temp)  
};

void setup() 
{   
  simpleThread_start(thread_get_temp); // startet den thread 

  //dein restlicher setup code
  
}

void loop() 
{
  simpleThread_run(_simpleThread_M_with_priority);

  //dein restlicher loop code
}

//new setup for thread
void simpleThread_setup(thread_get_temp)
{
}  
// new loop function for thread
boolean simpleThread_loop(thread_get_temp)
{   
  sensors.requestTemperatures();  
  iTemp = sensors.getTempCByIndex(0); 
}

@Jomelo,
danke nochmal für die Tips. Dein 1. Beispiel funktioniert doch. hatte die Zeile unsigned long g_timer_temp_measurement = 0; im Setup Bereich stehen und nicht bei den Konstanten.

Die Version mit dem simpleThread werde ich aber auch noch testen.

Edit:
Der SimpleThread funktioniert auch und gefällt mir etwas besser. Dadurch bekommt man noch etwas mehr Überblick. Habe es in einen extra TAB gelegt und den Interval der Abfrage auf 5000ms erhöht. Vielleicht gehe ich auch noch höher. Ich denke im Aquarium sollte es reichen den Wert jede Minute zu erfassen. Vielen Dank.

Jetzt geht es an die nächste Hürde. Würde gerne die Werte, die ich über das LCD Display ändere im EEprom abspeichern und beim Neustart von dort einlesen lassen.

Bin wieder etwas weiter gekommen.
Alle gewünschten Werte werden im EEProm gespeichert und beim Neustart auch abgerufen.
Habe für alle Werte eine Abfrage drin ob die Aktuellen Werte anders sind, als die im EEProm gespeicherten. Erst dann wird der Wert neu geschrieben. Bei den Minuten und Stundenangaben klappt das auch gut. Nur bei den Float Variablen für die Temperaturen bekomme ich das nicht hin.
Habe das speichern und einlesen so gemacht, wie von Jurs hier http://forum.arduino.cc/index.php?topic=147461.0 vorgeschlagen.

Aber wie bekomme ich da eine Abfrage rein, ob der Istwert anders ist als der EEprom Wert?

Bei Gleitkomma-Zahlen muss du beachten, dass du für einen unendlichen Bereich eine begrenzte Anzahl an Zuständen hast. Der rundet dir bei der Darstellung praktisch. Du musst daher generell bei Floats und Doubles auf einen Bereich abfragen und nicht den genauen Wert.

z.B. 2.13 < value < 2.14

Wollte es ungefähr so abfragen: if (sollTemp != EEPromsollTemp) dann schreibe in sollTemp in EEProm.

Bekomme es nur irgendwie nicht hin die Variable EEPromsollTemp mit dem Wert aus dem EEProm zu füttern.

Probier mal (sollTemp - 0.2 < EEPromsollTemp) && (EEPromsollTemp < sollTemperatur + 0.2)

(sollTemp - 0.2 < EEPromsollTemp < sollTemp + 0.2) könnte auch kompilieren

Wenn du nur in 0.5 Grad Schritten misst, sollte das zwar keine so Große Rollen spielen wie bei höheren Genauigkeiten, aber wer weiß...

Oder ist dein Problem die Konvertierung von Bytes nach Float generell? Da hilft Google. Da sind z.B. hier mehrere Lösungen:

Da kann man z.B. einfach die Pointer um-casten. Das ist der gleiche Trick der in diesem Algorithmus in die andere Richtung angewandt wird:

Habe das speichern und einlesen so gemacht, wie von Jurs hier Variablen bitweise/direkt beschreiben - Deutsch - Arduino Forum vorgeschlagen.

Und, was geht am Einlesen nicht ?

Ein Float-Wert von 0.12 ist übrigens keine "glatte Zahl", d.h. nach dem Zurücklesen hast du evtl. 0.1200000941753387451171875 oder so ähnlich.

if ( 0.12 ==  0.120000094 ) { /* Glück gehabt */ }

Entweder vergleichst du, ob die neue Zahl um einen Mindest-Wert abweicht. Oder du vergleichst die beiden 4 byte Sequenzen, die im EPROM sind / evtl. zu schreiben wären ...
Oder du siehst ein, dass float das Leben nicht einfacher, sondern komplizierter macht, und lebst mit integer oder gar byte (z.B. 215 für 21.5 °C )

War mal wieder ein simpler Programmierfehler. Hatte die Variablen für die EEPromSollTemp an der falschen Stelle im Sketch stehen. Habe einen eigenen Tab gemacht für die abfragen ob geschrieben werden muß oder nicht. Habe dann dort auch die Variablen zum einlesen der alten EEProm Temperatur reingepackt. Das war wohl der Fehler. Jetzt stehen sie mit im HauptTab und es klappt.
Die Temperaturen werden nur in 0.5°C Schritten eingestellt. Ich denke da sollten sich die rundungsdifferenzen in Grenzen halten. Werde ich aber beobachten.
Danke für die Tips.

Bei Zahlen aus Zweierpotenzen gibt es keine Rundungsfehler, wenn es nicht zuviele Ziffern sind.
Also aus ganzen Zahlen, 0.5 , 0.25 , 0.125 usw. zusammengesetzte haben keine Rundungsfehler.

Deine 0.5 Schritte sollten also sicher sein, wenn sie kleiner als 1000000.5 sind :wink: