Werte aus Funktion auslesen während andere Funktion läuft

Hallo,

für ein Projekt während unserer Ausbildung haben wir uns dafür ein Arduino gekauft, dementsprechend sind wir noch ziemliche Anfänger.

Nun zu meinem Problem.
Unser Projekt ist es das wir die Temperatur und den Wasserfüllstand auslesen wollen von einer Behälter.
Zum auslesen der Sensoren habe ich zwei Funktionen geschrieben und die Werte am Ende auf einem LCD eingeblendet werden.
Jetzt ist es so das in meinem Fall eine Funktion durchläuft die ein Ladebildschirm darstellt bei dem die Werte messen gewerten. Wenn dieser durchglaufen ist werden die Werte im Script aber wirklich erst ausgelesen und auf dem Bildschirm angezeigt werden und in der Zeit ist einige Sekunden nichts auf dem LCD.
Gibt es irgendwie eine Möglichkeit das während die Funktion mit dem Ladebildschirm durchläuft die Werte ausgelesen werden können und man direkt nachdem das fertig ist die Werte bekommt.

Danke im vorraus.

Ja.
Am besten wenn du die für die Dauer deines Ladebildschirms (ein schönes Wort für Splash Screen) kein delay() verwendest.

während die Funktion mit dem Ladebildschirm durchläuft

Falscher Ansatz: Schreibe Funktionen, die keine Zeit brauchen (aber mehrfach drankommen, wenn sie einen zeitlichen Ablauf brauchen)

BlinkWithoutDelay - Prinzip

Hallo,

ich nehme mal an das Du da was falsch gemacht hast, zeig uns einfach mal Deinen Sketch damit wir das verstehen. Auf der aneren Seite gibts genug Beispiele um die Grundlagen zu lernen.So raten wir hier nur rum.

Also das ist mein Script

#include <OneWire.h>
OneWire ds(6); // Pin 4 mit 4,7 kOhm gegen 5V gelegt
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

int taster=2;
int tstatus=0;
int trigger=5;
int echo=4;
int LEDb=10;
int LEDg=11;
int LEDr=12;
long dauer=0;
long entfernung=0;

void setup() {
  // put your setup code here, to run once:
  pinMode(taster, INPUT);
  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);
  lcd.begin();
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Startet");
  lcd.setCursor(0,1);
  lcd.print(" Version  0.0.3");
  delay(5000);
  lcd.clear();
  lcd.noBacklight();
}

void loop() {
  // put your main code here, to run repeatedly:´
  tstatus=digitalRead(taster);
  if (tstatus == HIGH) {
    lcd.backlight();
    funcload();
    int celsius;
    celsius=readDS18B20();
    int fuel;
    fuel=funcfuel();
    if (celsius >= 27) {
      analogWrite(LEDr, 150);
    }
    else if (celsius >= 24) {
      analogWrite(LEDg, 150);
    }
    else {
      analogWrite(LEDb, 150);
    }
    lcd.setCursor(0,0);
    lcd.print("Temperatur:");
    lcd.setCursor(12,0);
    lcd.print(celsius);
    lcd.print((char)223);       // Grad Symbol ausgeben
    lcd.print("C  ");
    lcd.setCursor(0,1);
    lcd.print("F");
    lcd.print(char(245));
    lcd.print("llstand:");
    if (fuel <= 99) {
    lcd.setCursor(12,1);
    lcd.print(fuel);
    }
    else {
    lcd.setCursor(11,1);
    lcd.print(fuel); 
    }
    lcd.print("ml");
    delay(5000);
    analogWrite(LEDr, 0);
    analogWrite(LEDb, 0);
    analogWrite(LEDg, 0);
    lcd.clear();
  }
  else {
    lcd.noBacklight();
  }
}

int funcload() {
  int load;
  lcd.setCursor(0,0);
  lcd.print(" Ermittel Werte");
  lcd.setCursor(0,1);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.print(char(255));
  delay(100);
  lcd.clear();
  return load;
}

int readDS18B20() {
// DS18B20 Temperatur Sensor 1-wire auslesen
// Dauer ca. 1,1 Sekunden.
//
// Ausgabe: float Grad Celsius
//
// Matthias Busse Version 1.0 vom 8.6.2014

  #define DS18B20_ID 0x28
  float temp;
  byte i, present = 0, data[12], addr[8];
  
  if (!ds.search(addr)) { //Sensor finden
    ds.reset_search();
    delay(250);
  }
  if (OneWire::crc8( addr, 7) != addr[7]) { return -1000;}
  if (addr[0] != DS18B20_ID && addr[0] != DS18B20_ID) { return -1000;}
  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1); // Start konversion
  delay(850);        // warten
  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE);    // lesen
  for ( i = 0; i < 9; i++) { // 9 Byte empfangen
    data[i] = ds.read();
  }
  temp = ( (data[1] << 8) + data[0] )*0.0625; // Temperatur berechnen
  return temp;
}

int funcfuel() {
  int fuel;
  digitalWrite(trigger, LOW); 
  delay(5); 
  digitalWrite(trigger, HIGH); 
  delay(10);
  digitalWrite(trigger, LOW);
  dauer = pulseIn(echo, HIGH); 
  entfernung = (dauer/2) * 0.03432; 
  return entfernung;
}

Ich schaue mir aber gerade das BlinkWithoutDelay - Prinzip um zu versuchen das delay() wegzumachen

Hallo,

na da hast Du dir nicht gerade das beste zusammen kopiert.

was sollen die vielen delay in der fuction funcload(). Soll da ein Laufbalken dargestellt werden.? Wenn Du den balke darstellst kannst Du natürlich nichts anderes anzeigen. Dann hast Du da noch mal 5s Wartezeit drin.
Der Messvorgang selbst daurert eine knappe sekunde, mit den delay da drin. Also wann soll der Balken laufen, während der Sekunde Messzeit ? Dann kannst Du die Wartezeit während des Messvorganges nicht mit delay machen.

Die delay müssten grundsätzlich raus. Schau dir das Beispiel blink without delay an und suche hier nach Nachtwächter. Wenn Du das mal begriffen hast ist es ganz einfach.

Heinz

Ganz einfach ist es nicht. Vor allem, weil es ein komplett anderer Denk-Ansatz ist.

Wenn es dir den Spaß nicht ganz verdirbt, schau mal diesen Vorschlag an:

#include <Wire.h> // for I2C
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

#include <OneWire.h>
OneWire ds(6); // Pin 6 mit 4,7 kOhm gegen 5V gelegt

const byte taster=2;  // Pin 2 über Taster an GND (interner PullUp)

const unsigned long DAUER=60000;  // Nach 1 Minute Anzeige aus
unsigned long startzeit=0;
bool start;

void setup() {
  pinMode(taster, INPUT_PULLUP);
  lcd.begin(16,2);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Startet");
  lcd.setCursor(0,1);
  lcd.print(" Version  0.1.0");
  delay(5000);
  lcd.clear();
  lcd.noBacklight();
  start = false;
}

void loop() {
  if ( digitalRead(taster) == LOW) { // Taster gedrückt
    start = true;
    startzeit = millis();
  }
  if (start) {
    lcd.backlight();
    float celsius=readDS18B20(ds);
    if (isnan(celsius)) funcload(true);  
    else {
      funcload(false);
      lcd.setCursor(0,0);
      lcd.print("Temperatur");
      lcd.setCursor(11,0);
      if (celsius < 10.0) lcd.print(" ");  // 0 .. 99
      lcd.print(celsius,0);
      lcd.print((char)223);       // Grad Symbol ausgeben
      lcd.print("C  ");

      if (millis() - startzeit > DAUER) {
        start = false;
        lcd.noBacklight();
      }
    }
  }
}

void funcload(bool state) {
  static unsigned long lasttime;
  static byte n;
  if (!state ) n = 0;  // rücksetzen, sonst nichts tun
  else {
    if (n == 0) {  // Anzeige starten
      lcd.setCursor(0,0);
      lcd.print("Neuer Wert");
    }
    if (millis() - lasttime > 100 && n < 16) {  
      // Anzeige weiterlaufen lassen
      lcd.setCursor(n++,1);
      lcd.print(char(255));
      lasttime = millis(); 
    }
  }
}

float readDS18B20(OneWire& ds) {
// DS18B20 Temperatur Sensor 1-wire auslesen
// Dauer ca. 1,1 Sekunden.
//
// Ausgabe: float Grad Celsius oder NAN, wenn Messung noch läuft
//
// Matthias Busse Version 1.0 vom 8.6.2014
// michael_x : Version 1.1 ohne Delay

  #define DS18B20_ID 0x28
  float temp;

  static unsigned long lasttime=0; 
  static unsigned int delay=0;
  static byte phase=0;
   
  byte i, present = 0, data[12], addr[8];

  if (millis() - lasttime < delay) return NAN;

  switch (phase) {
    case 0:
      if (!ds.search(addr)) { //Sensor finden
        ds.reset_search();
        delay = 250;
        return NAN;
      }
      phase = 1;
      // break;
    case 1:
      if (OneWire::crc8( addr, 7) != addr[7]) { return 88;}
      if (addr[0] != DS18B20_ID && addr[0] != DS18B20_ID) { return 99;}
      ds.reset();
      ds.select(addr);
      ds.write(0x44, 1); // Start konversion 
      delay = 850;        // warten
      phase = 2;
      return NAN;
    case 2:
     ds.reset();
     ds.select(addr);
     ds.write(0xBE);    // lesen
     for ( i = 0; i < 9; i++) { // 9 Byte empfangen
         data[i] = ds.read();
     }
     if (data[1] >= 0x80) return 0.0; // negative Werte werden unterdrückt
     temp =  ( (data[1] << 8) + data[0] )*0.0625; // Temperatur berechnen
     phase = 0; // oder auch gleich 1 ...
     return temp;
  }
  return NAN;   
}

Übersetzt bei mir, aber ungetestet

Mein lcd.begin() braucht zwei Parameter, welche lib verwendest du ?

Eigentlich gehen folgende Messungen deutlich schneller, das ist jetzt eher ein Beispiel, wie zwei Funktionen ohne delay parallel laufen können.

Statt NAN kannst du natürlich auch einen anderen spezial - float Wert nehmen.

Entweder mach ich was mit dem Button falsch oder das funktioniert nicht. Da passiert Garnichts wenn ich auf den Knopf drücke und wenn ich starte lädt das die ganze Zeit nur die Temperatur neu.

michael_x:
Mein lcd.begin() braucht zwei Parameter, welche lib verwendest du ?

Ich benutze diese lib. Da brauch ich die Parameter nicht.
Arduino LiquidCrystal I2C library

Da passiert Garnichts wenn ich auf den Knopf drücke

Der Taster ist auf Pullup und sollte gegen GND schalten...

Hallo,

ein älteres Bsp. Man kann das auslesen noch in eine Funktion packen mit verschiedenen Parameter, je nachdem ob man alle Sensoren auf einmal lesen möchte oder nur einzelne oder was auch immer.
Schau dir auch das Bsp. der Dallas Lib "WaitForConversion2" an.

/*   
   Arduino Mega 2560
   Arduino IDE v1.8.6
*/

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 12 on the Arduino
#define ONE_WIRE_BUS 12

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// change of your sensors adress or use "getTempCByIndex" and see below
DeviceAddress sensor1 = { 0x10, 0x40, 0xDD, 0xC3, 0x02, 0x08, 0x00, 0xB4 };
DeviceAddress sensor2 = { 0x10, 0x8A, 0x0B, 0xAC, 0x02, 0x08, 0x00, 0x2C };
DeviceAddress sensor3 = { 0x28, 0xFF, 0x60, 0xAC, 0x6B, 0x14, 0x03, 0xB3 };

int resolution = 9;                         // DS18S20 only 9bit, other 9...12
unsigned long lastTempRequest;      
const unsigned int ConversionDelay = 750;   // default 750ms, okay for all DS1820 types
bool AllowDallasTempRequest = true;
float TempSensor1;                          // Zwischenspeicher zum auslesen
float TempSensor2;                          // Zwischenspeicher zum auslesen
float TempSensor3;                          // Zwischenspeicher zum auslesen


void setup(void)
{
  Serial.begin(9600);
  
  pinMode(LED_BUILTIN, OUTPUT);             // LED 13 auf Arduino Board
  
  sensors.begin();
  // set the resolution (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(sensor1, resolution);
  sensors.setResolution(sensor2, resolution);
  sensors.setResolution(sensor3, resolution);
  sensors.setWaitForConversion(false);        // makes it async
}

void loop(void)
{ 
   
  // Request a temperature conversion 
  if ( AllowDallasTempRequest == true )
    {           
     sensors.requestTemperatures();
     lastTempRequest = millis(); 
     AllowDallasTempRequest = false;
    }
  
  // readout the Dallas sensors  
  if ( millis() - lastTempRequest >= ConversionDelay )  // waited long enough?
    {
     TempSensor1 = sensors.getTempC(sensor1);  // 1. Dallas Sensor auslesen (Device Adresse)
     TempSensor2 = sensors.getTempC(sensor2);  // 2. Dallas Sensor auslesen (Device Adresse)  
     TempSensor3 = sensors.getTempC(sensor3);  // 3. Dallas Sensor auslesen (Device Adresse)  
     AllowDallasTempRequest = true;

     Serial.print(F("Temps:")); Serial.print('\t');
     Serial.print(TempSensor1); Serial.print('\t');
     Serial.print(TempSensor2); Serial.print('\t');
     Serial.print(TempSensor3);
     Serial.println();
    }
  
  // *** does other things *** //
  heartbeat(500);
  
}   // End of Loop


// ----------------------------------------------------------------------------------- //

void heartbeat (unsigned int interval)                         // Kontrolle ob Sketch blockiert
{
  static unsigned long last_ms = 0;
  static bool state = LOW;
  
  unsigned long ms = millis();
  
  if (ms - last_ms >= interval) {
    last_ms = ms;
    state = !state;
    digitalWrite(LED_BUILTIN, state);
  }
}

Okay das hab ich danke.

Wenn ich allerdings nun den Taster drücke, dann hängt es in einer schleife in der es immer zwischen "Neuer Wert" und "Temperatur" hin und her wechselt und die Temperatur ununterbrochen gemessen wird. Nach 1 Minute stoppt es dann, der LCD wird dunkel und es bleibt bei "Temperatur" stehen.

dann hängt es in einer schleife in der es immer zwischen "Neuer Wert" und "Temperatur" hin und her wechselt und die Temperatur ununterbrochen gemessen wird

Ja, das ist so programmiert.
Es wird permanent die Temperatur gemessen. Diese Messung wird künstlich 1 sec langsam gemacht. In dieser Zeit kommt immer wieder funcload dran, was alle 100 ms eine kleine LCD Ausgabe macht.

Gleichzeitig läuft noch die 1 Minute, nach der die LCD-Beleuchtung wieder abgeschaltet wird.

Kannst du gerne auch anders machen.

Ich hab nochmal eine Frage zu meinem "Ladebalken" den ich animieren will.

Ich habe jetzt das wie bekomme ich jetzt da eine Wartezeit von 100ms rein ohne delay() zu benutzen.

int funcload() {
  int load;
  //const unsigned long ms= millis();
  lcd.setCursor(0,0);
  lcd.print(" Ermittel Werte");
  lcd.setCursor(0,1);
  do  {
    a = a+1;

    lcd.print(char(255));
    //delay(100);
  } while ( a <= 16 );
  a = 0;
  return load;
};

Ich verstehe das Prinzip von BlinkWithoutDelay zwar aber weiß nicht wie ich es umsetzen soll.

ohne delay() zu benutzen

delay() durch eine andere blockierende Schleife zu ersetzen kann nicht das Ziel sein.

aber weiß nicht wie ich es umsetzen soll.

Du wirfst die Schleife raus!
So, dass die Funktion SOFORT wieder beendet werden kann.
Stattdessen merkst du dir die die jeweiligen nötigen Statusinformationen irgendwo anders.

Tipp:
Es ist völlig egal, ob die Funktion 10000 mal pro Sekunde aufgerufen wird...
Wichtig ist, dass sie selber weiß, wann sie was tun muss.

Ich verstehe das Prinzip von BlinkWithoutDelay zwar

Nein!
Wenn du es verstanden hättest, könntest du es einbauen.
Also: Weiter machen! (mit verstehen + üben)

Ich verstehe das Prinzip von BlinkWithoutDelay zwar aber weiß nicht wie ich es umsetzen soll

Der Trick ist, sich eine Startzeit zu merken und zu prüfen, wie weit man inzwischen ist.
Im einfachsten Fall macht man gar nichts, wenns noch nicht so weit ist. Aber das kann man anpassen.
Auf jeden Fall beschreiben die hintereinander geschriebenen Zeilen eines Sketches keinen zeitlichen Ablauf.

Manchmal ist es hilfreich, wenn eine Funktion zurückliefert, wie weit sie ist.
Auf jeden Fall ist es Unsinn, etwas undefiniertes zurückzuliefern ( dein int load; )

@combie: die Schleife wird hier nur benutzt um eine Anzahl Zeichen auszugeben. Funktioniert hier nur, wenn die globale Variable a direkt vor dem Aufruf von funcload() neu gesetzt wird, und ist daher nicht sehr "elegant", aber eine "blockierende Schleife" ist was Schlimmeres.

@combie: die Schleife wird hier nur benutzt um .......

Genau dort steckt das Problem!

Anforderung:

Ich hab nochmal eine Frage zu meinem "Ladebalken" den ich animieren will.

Antwort:

Du wirfst die Schleife raus!
So, dass die Funktion SOFORT wieder beendet werden kann.
Stattdessen merkst du dir die die jeweiligen nötigen Statusinformationen irgendwo anders.

Also:
Die Schleife muss weg! (dabei bleibe ich)
Alternativ: Oder geschickt umgebaut werden um das Ziel zu erreichen.
Ich würde vermutlich Goto oder Switch benutzen, wenn ich die Schleife beibehalten wollte.

:o Für den Anfang ist es wohl leichter, die Schleife zu eliminieren. :o

Genau dort steckt das Problem!

Nö!

Wenn man es falsch macht, kann es ein Problem werden. Aber das gilt ja fast immer :slight_smile:

  void ladebalken() {
  lcd.setCursor(0,1);
  do  {
    a = a+1;
    lcd.print(char(255));
  } while ( a <= 16 );
  a= 0;  // kann man eigentlich sogar weglassen
}

Das ist hässlich, aber keine "blockierende Schleife". !

Wenn a global definiert ist, müsste man diesen Schnipsel langsam mit abnehmenden a Werten aufrufen:

int a;
void loop() {
   const byte DELAY=100;
   static unsigned long lasttime;
   if (millis() - lasttime  >= DELAY) {
     // hier der 100 ms Zyklus
     lasttime = millis();
     static byte laenge = 0;
     a = 15 - laenge;             // igitt, warum das ?
     if ( a >= 0 ) { ladebalken();  laenge++; }
   }
   // ... kommt hier nicht blockierend hin
}

aber keine "blockierende Schleife". !

:o Aber auch (noch) kein Ladebalken! :o
Dort fehlt die Zeitabhandlung.

Mit delay() wäre es ein Ladebalken.
Ein blockierender.

Nö!

Doch, genau da.
In der Funktion, oder alternativ: Im Kopf des Programmierers.