Oled funktioniert nur mit delay

Hi,
ich habe wieder mal ein Problem, dessen Lösung vermutlich ziemlich einfach ist, ich aber nicht drauf komme.
I(ch habe Sensoren, die über einen MAX6675 abgefragt werden. Später mal 4, hier grad nur einen.
Ein Oled-Display soll die Werte alle 2 Sekunden anzeigen, wird ein Wert überschritten, kann man sich den zeilichen Verlauf übher eine kleine App auf einem Tablet anschauen. Die Werte werden alle Sekunde per bluetooth dorthin übertragen.

So weit so gut.
Jetzt habe ich gelernt, das Delays zu vermeiden sind. Das habe ich versucht und lass die Bluetoothwerte über eine Schleife nur alle Sekunde durchlaufen. Das Oled alle 2 Sekunden.
Das geht auch, aber dann liest er keine Werte mehr ein. Der Sensor funktioniert nicht. Füge ich das Delay im Oled-unterprogramm wieder ein und lasse alles andere wie es ist, geht es wieder.
Ist die OlED Berechnung so fordernd, das nichts mehr anderes geht? Die verwendete Bibliothek gilt als speicherschonend.

WARUM?
Es soll jetzt noch ein Durchflussmesser dazu, der Impulse zählt. Diese sollen über ein Interrupt erkannt werden. Da habe ich jetzt ein bischen Angst, das dann gar nichts mehr geht.
Nebenbei: Macht es eigentlich Sinn ein ESP32 Board zu nehmen anstatt eines Arduino Nano oder ist das bei meinen Anforderungen egal?

Hier der Code dazu.

#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <MAX6675_Thermocouple.h>
#define I2C_ADDRESS 0x3C

SSD1306AsciiWire oled;

unsigned int EGTSO = 10; //Pinbelegung Thermosensor  EGT
unsigned int EGTCS = 9; //Pinbelegung Thermosensor  EGT
unsigned int EGTCLK = 8;//Pinbelegung Thermosensor  EGT
double EGT;

char blueToothValue;
float Solltemp = 23;
unsigned long wartezeit = 2000;
unsigned long wartezeit1 = 1000;

MAX6675_Thermocouple ktcEGT(EGTCLK, EGTCS, EGTSO); // EGT

void setup() {
  Serial.begin(9600);

  pinMode(6, OUTPUT);
  Wire.begin();
  Wire.setClock(400000L);
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
}

void loop()
{
  EGT = ktcEGT.readCelsius();
  if (EGT > Solltemp) digitalWrite(6, HIGH);
  else digitalWrite(6, LOW);

  readBT();
  OLED();
}

void OLED() {
  static unsigned long lastuebertragung = 0;
  if (millis() - lastuebertragung > wartezeit) {
    oled.setFont(System5x7);
    oled.clear();
    oled.print("  EGT links  ");
    oled.print(EGT, 0);
    oled.println("  C  ");
    oled.println("  EGT rechts");
    oled.println("  CHT links");
    oled.println("  CHT rechts");
    oled.print("  Oeldruck");
    delay (500);

    lastuebertragung = millis();
  }
}

void readBT() {
  static unsigned long lastuebertragung1 = 0;
  if (millis() - lastuebertragung1 > wartezeit1) {

    if (Serial.available()) {
      blueToothValue = Serial.read();
    }

    switch (blueToothValue) {
      case 'U':
        Solltemp = Solltemp + 1;
        break;

      case 'W':
        Solltemp = Solltemp - 1;
        break;
    }

    Serial.print( "*E" );    // EGT Temperatur
    Serial.println( EGT);
    Serial.print( "*" );

    Serial.print( "*F" );    // EGT Temperatur SOLL
    Serial.println( Solltemp);
    Serial.print( "*" );

    Serial.print( "*K" );    // CHT und EGT für Diagramm
    Serial.print( EGT);
    // Serial.print( ',');
    // Serial.println( CHT);
    Serial.print( "*" );

    lastuebertragung1 = millis();
  }

}

Das delay(500) in der OLED()-Funktion sorgt dafür, dass die Startzeit für die nächste Darstellung ca. 500 ms später gesetzt wird, als der eigentliche Aufruf stattfindet. Dann sollte man auch die "wartezeit" einfach auf 2500 ms setzen können und für OLED() den gleichen Effekt erzielen ...

Wenn es aber funktional etwas ändert, bedeutet es wohl, dass eine andere Funktion ausserhalb OLED() ein Problem hat, wenn sie zu häufig aufgerufen wird.

Diese Beispiel

https://github.com/YuriiSalimov/MAX6675_Thermocouple/blob/master/examples/SerialReading/SerialReading.ino

zeigt jedoch, dass es nicht an readCelsius() liegen sollte, da dort explizit darauf hingewiesen wird, dass die dort eingetragenen delay(500) nur der seriellen Anzeige wegen eingefügt sind.

Die Funktion readBT() erschliesst sich so nicht, da dort HardwareSerial verwendet wird ...

Bei Wokwi funktioniert es:

https://wokwi.com/projects/330117511710245460

In der Doku zu SSD1306Ascii lese ich:

A small AVR only I2C driver is available when no other I2C devices are used.

Möglicherweise verträgt sich irgendwas auf dem I²C-Bus nicht oder die anderen I²C-Busteilnehmer werden nicht erkannt.

Da ich Deine Hardware nicht habe, kann ich nicht testen, daher nur spekulativ.

Meine Hardware ist recht überschaubar.

  • Arduino NANO- China clone
  • 0,96" OLED Display, standard von AZ-Delivery
  • MAX6675 mit TYP-K Thermoelement
    -HC05 Bluetooth Modul

Von denen kommuniziert doch nur der OLED über I2C oder?

Was mich wundert ist, dass es mit dem Delay funktioniert und ohne eben nicht. Der Effekt sollte ja der gleiche sein. Den Effekt sehe ich ja auch am Display und Bluetooth, aber er liest eben kaine Werte mehr ein ohne das Delay in der OLED Schleife.
Praktisch kann ich das Delay drin stehen lassen, es ist ja alles sehr träge und nichts zeitkritisch. Und wenn der Durchlussensor mit Impulszählung über Interrupt dazu kommt unterbriht er und sollte ja auch gehen.

Aber dann habe ich es immer noch nicht verstanden und würde es aber gerne. Und sauber ist es so ja auch nicht.

Cool, das kannte ich noch gar nicht.

Es funktioniert ja auch, zeigt aber einen konstanten Temperaturwert an. Wenn das Delay drin ist, zeigt es den sich aktualisierenden Temperaturwert an.

Stimmt, fälschlich hatte ich auch MAX6675 gedanklich an I²C gehängt, das Board habe ich nicht.

Das hast Du aber nicht so programmiert, denn EGT = ktcEGT.readCelsius(); steht in loop(). Ohne delay wird der Wert ständig gelesen, möglicherweise ja zu häufig. Ein Blick ins Datenblatt könnte Aufschluß bringen.

Ich habe das Lesen der Temperatur mangels Hardware durch random ersetzt, da geht die Anzeige ohne Probleme.

Probier's mal mit Gemütlichkeit:

#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include <MAX6675_Thermocouple.h>
#define I2C_ADDRESS 0x3C

SSD1306AsciiWire oled;

unsigned int EGTSO = 10; //Pinbelegung Thermosensor  EGT
unsigned int EGTCS = 9; //Pinbelegung Thermosensor  EGT
unsigned int EGTCLK = 8;//Pinbelegung Thermosensor  EGT
double EGT;

char blueToothValue;
float Solltemp = 23;
unsigned long wartezeit = 500;
unsigned long wartezeit1 = 1000;

MAX6675_Thermocouple ktcEGT(EGTCLK, EGTCS, EGTSO); // EGT

void setup() {
  Serial.begin(9600);

  pinMode(6, OUTPUT);
  Wire.begin();
  Wire.setClock(400000L);
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
}

void loop()
{
  readTemp();
  readBT();
}

void readTemp() {
  static unsigned long lastuebertragung = 0;
  if (millis() - lastuebertragung > wartezeit) {
    lastuebertragung = millis();
    EGT = ktcEGT.readCelsius();
    digitalWrite(6, EGT > Solltemp);
    OLED();
  }
}

void OLED() {
  oled.setFont(System5x7);
  oled.clear();
  oled.print("  EGT links  ");
  oled.print(EGT, 0);
  oled.println("  C  ");
  oled.println("  EGT rechts");
  oled.println("  CHT links");
  oled.println("  CHT rechts");
  oled.print("  Oeldruck");
}

void readBT() {
  static unsigned long lastuebertragung1 = 0;
  if (millis() - lastuebertragung1 > wartezeit1) {

    if (Serial.available()) {
      blueToothValue = Serial.read();
    }

    switch (blueToothValue) {
      case 'U':
        Solltemp = Solltemp + 1;
        break;

      case 'W':
        Solltemp = Solltemp - 1;
        break;
    }

    Serial.print( "*E" );    // EGT Temperatur
    Serial.println( EGT);
    Serial.print( "*" );

    Serial.print( "*F" );    // EGT Temperatur SOLL
    Serial.println( Solltemp);
    Serial.print( "*" );

    Serial.print( "*K" );    // CHT und EGT für Diagramm
    Serial.print( EGT);
    // Serial.print( ',');
    // Serial.println( CHT);
    Serial.print( "*" );

    lastuebertragung1 = millis();
  }
}

Habs grad ausprobiert. Geht wunderbar und schnurrt wie eine Katze.

Dann habe ich den Max6675 zu oft abgerufen.Denn Wert hochfrequent abzurufen und nicht zu nutzen ist ja Quatsch. Macht ja völlig Sinn es so zu machen, wie du es geschrieben hast.

Danke für die Hilfe

Dann frag ich doch gleich die Experten noch was...
Ich lösche jetzt das Display und schreibe immer neu. Das hat zur Folge, das es blinkend wirkt. Ich habe jetzt schon gelesen, dass es eleganter ist, nich zu löschen sondern das Display zu schwärzen.
Geht das auch nur für einzelne Bereiche, also in meinem Fall nur für den Teil des Displays, in dem die Messwerte stehen und sich ändern? Und wenn ja, wie?

Die Bibliothek hat im Beispiel SixAdcFieldsWire.ino eine Lösung dafür :slightly_smiling_face:

Überschreibe einfach direkt den alten Text mit neuem Text und setze nur die Differenz auf Leerzeichen. Zahlen z.B. kann man bei der Ausgabe auf eine konstante Breite mit führenden Leerzeichen formatieren. Dann flackert nichts

... ist das einzige, was sich ändert. Den Rest nur einmal, evtl. gar nur in setup().
Nun muss man nur noch berücksichtigen, dass es evtl. unterschiedlich viele Zeichen sein können.
Im einfachsten Fall hier ( nur Werte zwischen 0.0 und 99.9 ) z.B.
if (EGT < 10.0) oled.print(" "); oled.print(EGT, 1);

Dass der Hintergrund schwarz (statt gar nicht/transparent) überschrieben wird, hängt evtl. auch an Display/Library.

Wird aber vermutlich nicht das Einzige bleiben. Leider hat mir Uwe keine Glaskugel zugestanden :roll_eyes:

Das Beispiel SixAdcFieldsWire sieht auf meinem OLED gut aus.

Aber auch da gilt: festen Text nicht verändern, sondern nur verändern, wenn sich der Wert ändert. Also sowas wie if(EGT != EGTalt) {oled.print(EGT, 0); EGTalt = EGT;}.

Kleiner Tipp: Bei Wokwi habe ich den Messwert auch durch eine random-Funktion ändern lassen. Auf diese Weise wäre auch in Deiner Originalschaltung nachweisbar, dass es nicht an der Anzeige sondern der Datenerfassung liegt.

Und Du kannst Dir dort vom Sketch eine eigene Kopie erstellen und die Anpassung der Textausgabe damit entwickeln. Das spart häufig Zeit, weil das Kompilieren sehr schnell verläuft und das Hochladen entfällt.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.