"Schmutzzeichen" am unteren OLED Rand

Liebe Forummitglieder, ich habe ein eigenartiges Phänomen:
bei einigen Textausgaben auf dem OLED 128 x 64 SSD1306 habe ich am unteren Rand immer so komische sichtbare Pixel. Immer dann, wenn ich nicht nur Text sondern Werte ausgebe.
Damit ihr euch nicht durch den elend langen sketch durchackern müßt (der im Wesentlichen von my_xy_projekt stammt und unter "Simpler Dosierspender" hier im Forum zu finden ist), habe ich euch die dazugehörigen sketch-Schnippsel zu den Fotos angehängt.

Hat da jemand eine Erklärung und eine Lösung dafür?
Ich habe schon display.println() als letzte Zeile hinzugefügt - ändert nix.

Foto: Ansaugen = korrekt

 Serial.println("Ansaugen gestoppt");
      Serial.println("");
        display.clearDisplay();
        display.setCursor(0,9);
        display.println("ANSAUGEN:");
        display.println("  STOPP");
        display.display();
      ausloeser = false;

Foto: Dauer = "verschmutzt"

 status = uebernahme;
          lastmillis = millis();
            display.clearDisplay();
            display.setCursor(0,9);
            display.println("DAUER:");
            display.print((float)zeitsammler /1000);
            display.println(" Sek");
            display.display();

Foto: Dosieren = korrekt

 Serial.println("Dosieren gestartet...");
        startTime = millis();                         // Zeit merken
        lastmillis = millis();
         display.clearDisplay();
         display.setCursor(0,9);
         display.println("DOSIEREN:");
         display.print("  START");
         display.display();
      }

Foto: Vorgabe = "verschmutzt"

 Serial.println();
            display.clearDisplay();
            display.setCursor(0,5);
            display.println("Vorgabe:");
            display.print((float)zeitsammler/1000);
            display.println("  Sek");
            display.print("\201bernommen");
            display.display();
          zeitsammler = 0;
          status = warten;

Danke schon mal vorab für eure wertvoll Hilfe.

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

vielen Dank, habs gerade gemerkt und versucht zu verschieben, aber nix gefunden :-(.
Danke daher!

Das Verschmutzt scheint mir eine interpretation der Zahl als Bit-Pixel also grafisch.
Hast Du irgendwo (ungewollt) eine Umschaltung von Text nach Grafikmodus?

Grüße Uwe

Hi Uwe,
nicht das ich wüßte. Weiß gar nicht wie das geht, bin newbe :smirk:
Es hängt auch nicht mit ev. Umlauten zusammen. Bloß wenn Werte ausgelesen werden, dann tauchen sie auf.

Ich weiß auch nicht ob's geht es sieht nur so aus.
Könnte mir vorstellen daß Du ein Ram-Problem hast wo dieser Bereich des "Bildschirmspeichers" überschrieben wird.

Ohne Sketch könnenwir Dir nichts sagen. Ich glaube Du mußt uns den gesamten Sketch geben. Bitte links auf die verwendeten Bibliotheken.

Welchen Arduino verwendest Du? Hast Du es mit einem Arduino Mega schon versucht?

Grüße Uwe

ok. Ich verwende einen Arduino Nano.
Eine Bitte: lacht nicht über den sketch, man könnte den sicher kompakter und professioneller machen, aber ich bin am Lernen und da beginne ich simple by simple :wink:
Also hier der Sketch:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Servo.h>
#include <AccelStepper.h>

//Taster:
    const byte SaugT =12;    //Taste auf D12: Ansaugen ohne Zeitnehmung
    const byte DosierT = 11;    // Taste auf D11: Vorgabezeit programmieren
    const byte StopT = 10;    // Set / Stopp Taste = Abbruch
    const byte FuellT = 9;  // Taste: Fülle bis Vorgabewert
    const byte DrehT = 5;     // zum Drehen des Motors zur nächsten Flasche
    const byte FCS = 6;       // checkt, ob Flasche vorhanden
    const byte Relais = 4;   // Relais
    const unsigned long bounceTime = 200;   //Entprellzeit
    unsigned long FuellTime;
    unsigned long vorgabezeit;  //gemerkte Zeit als Vorgabe für autom. Befüllen
    unsigned long duration;             // Variable für die Dauer des Tastendrucks
    unsigned long fuellStartTime = 0; // millis-Wert beim ersten Drücken der Taste
    
    #define OLED_RESET 0
    //#define SSD1306_128_64
    Adafruit_SSD1306 display(128, 64);  // Breite, Höhe
    //Adafruit_SSD1306 display(OLED_RESET);
 
//Servomotor   
    Servo myservo;
    int pos = 0;
     
//Steppermotor 17HS19
    const int dirPin = 2;   // Richtung StepperMotor
    const int stepPin = 3;  // Ansteuerung StepperMotor
    #define motorInterfaceType 1
   
    AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin); // Create a new instance of the AccelStepper class:
    void  AccelStepper::disableOutputs ();  //Spulen stromlos machen um Strom zu sparen
    
void setup() {
  
//Eingänge:
    pinMode (SaugT, INPUT_PULLUP);  // Pullup-Widerstand aktivieren
    pinMode (DosierT, INPUT_PULLUP);  // Pullup-Widerstand aktivieren
    pinMode (FuellT, INPUT_PULLUP);  // Pullup-Widerstand aktivieren
    pinMode (StopT, INPUT_PULLUP);   // Pullup-Widerstand aktivieren
    pinMode (DrehT, INPUT_PULLUP);   //dreht Motor zur nächsten Flasche
    pinMode (FCS, INPUT_PULLUP);    //checkt ob Flasche vorhanden
        
//Ausgänge:
    pinMode(stepPin, OUTPUT);
    pinMode(dirPin, OUTPUT); 
    pinMode(Relais, OUTPUT);
    
    digitalWrite(Relais, LOW); //Relais standardmäßig abgefallen
   
    myservo.attach(7);  //Servomotor auf PIN 7
    #define MIN_PULSE_WIDTH       544     // the shortest pulse sent to a servo  
    #define MAX_PULSE_WIDTH      2400     // the longest pulse sent to a servo
    #define DEFAULT_PULSE_WIDTH  1500     // default pulse width when servo is attached
    myservo.write(0);
  

//StepperMotor:
     stepper.setMaxSpeed(1000);  //Set maximum speed in steps per second
     stepper.setAcceleration(400);
     stepper.setCurrentPosition(0);
  
 //Serieller Monitor
    Serial.begin(115200);
    Serial.println(F("Start..."));

//OLED Display Anzeige
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    //display.display();
    display.clearDisplay();
    display.setCursor(0,17);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.println("R E A D Y");
    display.display();
}

void loop()
{
       if (!ansaugen())
        if (!programmieren())
       ablauf();
       drehen();
}
//=====================================================================
bool ansaugen()
{
  static uint32_t lastmillis = 0;
  static bool ausloeser = false;
  if ((!digitalRead(FCS)) && (!digitalRead(SaugT)))    // FCS UND SaugTaste gedrueckt? ...
   { 
    ausloeser = true; 
    
    if (!digitalRead(Relais))                       // ... und Relais ist aus? ...
    {
      lastmillis = millis();
      myservo.write(70);
      delay (500);
      digitalWrite (Relais , HIGH);                 // Relais an
      Serial.println(F("Ansaugen gestartet..."));
      Serial.println("");
        display.clearDisplay();
        display.setCursor(0,9);
        display.println("ANSAUGEN:");
        display.print("  START");
        display.display();
    }
  }
  else if (ausloeser)                               // ausloeser gesetz?
    if (millis() - lastmillis > bounceTime)         // Taste losgelassen und deboncetime
    {
      digitalWrite (Relais , LOW);  // mache Relais wieder aus
      delay(500);
      myservo.write(0);
      
      Serial.println("Ansaugen gestoppt");
      Serial.println("");
        display.clearDisplay();
        display.setCursor(0,9);
        display.println("ANSAUGEN:");
        display.println("  STOPP");
        display.display();
      ausloeser = false;
    }
  return ausloeser;
}
//======================================================================
bool programmieren()
{
  enum {warten, dosieren, uebernahme};
  static uint32_t zeitsammler = 0;
  static uint32_t startTime = 0;
  static uint32_t lastmillis = 0;
  static byte status = warten;
  switch (status)
  {
    case warten:
     if ((!digitalRead(FCS)) && (!digitalRead(DosierT)))   // Programmiertaste ausgelöst
      {
        status = dosieren;
        myservo.write(70);
        delay (500);
        digitalWrite (Relais , HIGH);                 // zieht Relais an
       
        Serial.println("Dosieren gestartet...");
        startTime = millis();                         // Zeit merken
        lastmillis = millis();
         display.clearDisplay();
         display.setCursor(0,9);
         display.println("DOSIEREN:");
         display.print("  START");
         display.display();
      }
      break;
    case dosieren:
      if (millis() - lastmillis > bounceTime)
      {
        if (digitalRead(DosierT))
        {
          digitalWrite (Relais , LOW);  // mache Relais wieder aus
          delay(500);
          myservo.write(0);
          zeitsammler += (millis() - startTime);        // berechne Zeit
          Serial.print(F("Dauer =    "));
          Serial.print((float)zeitsammler / 1000);
          Serial.println(F("  Sek"));
          Serial.println();
          status = uebernahme;
          lastmillis = millis();
            display.clearDisplay();
            display.setCursor(0,9);
            display.println("DAUER:");
            display.print((float)zeitsammler /1000);
            display.println(" Sek");
            display.display();
        }
      }
      break;
    case uebernahme:
      if (millis() - lastmillis > bounceTime)
      {
        if (!digitalRead(StopT))
        {
          duration = zeitsammler;
          Serial.print(F("Vorgabezeit = "));
          Serial.print((float)duration/1000);
          Serial.print(F("  Sek"));
          Serial.println(" übernommen");
          Serial.println();
            display.clearDisplay();
            display.setCursor(0,5);
            display.println("Vorgabe:");
            display.print((float)zeitsammler/1000);
            display.println("  Sek");
            display.print("\201bernommen");
            display.display();
          zeitsammler = 0;
          status = warten;
        }
        if (!digitalRead(DosierT))
        {
          Serial.println(F(" zähle hinzu"));
          Serial.println();
          display.clearDisplay();
          display.setCursor(0,15);
          display.println("z\204hle");
          display.print("hinzu:");
          display.display();
          status = warten;
        }
      }
      break;
  }
  return status;
  }
 //======================================================================
void ablauf()
{
  static uint32_t startTime = 0;
  stepper.setCurrentPosition(0);
  if ((!digitalRead(FCS)) && (!digitalRead(FuellT)))
  {
    if (!digitalRead(Relais))
    {
      Serial.println(F("Starte Relais...."));
        display.clearDisplay();
        display.setCursor(0,9);
        display.println("BEF\201LLEN:");
        display.print("  START");
        display.display();
     
        startTime = millis();
        myservo.write(70);
        delay(500);
        digitalWrite (Relais , HIGH);                 // zieht Relais an
    }
  }
  if (!digitalRead(StopT) && digitalRead(Relais))
  {
    digitalWrite(Relais, LOW);
    Serial.print("GESTOPPT =  ");
        display.clearDisplay();
        display.setCursor(0,15);
        display.print("GESTOPPT");
        display.display();
        delay(500);
        myservo.write(0);
  }
  if ((millis() - startTime >= duration) && digitalRead(Relais))
  {
    digitalWrite (Relais , LOW);  // mache Relais wieder aus
      
    Serial.print(F("FÜLLZEIT =  "));
    Serial.print((float)duration / 1000);
    Serial.println(F("  Sek"));
    Serial.println();
      display.clearDisplay();
      display.setCursor(0,9);
      display.println("F\201LLZEIT:");
      display.print((float)duration/1000);
      display.print(" Sek");
      display.display();

      delay(500);
      myservo.write(0);
      delay(500);
      
      stepper.setSpeed(400);
      stepper.moveTo (325);
      stepper.runToPosition();  
      stepper.setCurrentPosition(0);
      
      }
drehen();
}
//======================================================================  
void drehen()
{
 if (!digitalRead(DrehT))
  {

    display.clearDisplay();
    display.setCursor(0,9);
    display.println("Motor dreht");
    display.display();
    
     stepper.moveTo (325);
     stepper.runToPosition();
     stepper.setCurrentPosition(0);
  }
}

Fang mal bei den "verschmutzten" mit

            display.setCursor(0,0);

an.
Ändert sich was?

außer dass die Anzeige nun in der 1. Zeile beginnt, hat sich nichts geändert. Die "Schmutzzeichen" bleiben an der gleichen Stelle rechts unten.

Oh hallo my_xy_projekt :-). Erst jetzt bermerkt, dass du wieder dabei bist. Du erkennst deinen sketch, gell ? :wink:

Hallo,
mach mal einen mini Sketch der nur auf einer Seite das eine Bild ausgibt, sonst alles weg. ich vermute ein Speicherproblem
Heinz

Naja... ist schon verschoben...
Zumal ich das

/tmp/arduino_modified_sketch_715182/sketch_feb15h.ino:37:37: warning: declaration of 'virtual void AccelStepper::disableOutputs()' outside of class is not definition [-fpermissive]
 void  AccelStepper::disableOutputs ();  //Spulen stromlos machen um Strom zu sparen
                                     ^
Compiling libraries.

nicht durchgelassen hätte.

Wir sprechen vom UNO?

Ich denke, ich habe was entdeckt: wenn ich in den Definitionen folgendes ändere:

statt:

#define OLED_RESET 0
    //#define SSD1306_128_64
    Adafruit_SSD1306 display(128, 64);  // Breite, Höhe
    //Adafruit_SSD1306 display(OLED_RESET);
 #define OLED_RESET 0
    #define SSD1306_128_64
    // Adafruit_SSD1306 display(128, 64);  // Breite, Höhe
    Adafruit_SSD1306 display(OLED_RESET);

unter weiter unten die TextSize auf (1) setze,
dann habe ich zwar eine kleinere Schrift, aber keine Schmutzzeichen mehr.
Schöner wäre natürlich die größere Schrift, aber bei TExtSize (2) ist sie wieder zu groß.

Hilft das für eine Lösung?

nein, ich verwende einen Nano.

Hallo Heinz,
lt Speicheranzeige verbraucht der sketch nur 75%. Und wenn ich die SSD1306 definition ändere dann passt die TextSize (2) wenn ich gleich in der 1. Zeile beginne.
Ich lass es jetzt mal so. Der Aufwand für eine andere Lösung ist (für alle die mitmachen) einfach zu groß.

DANKE an alle, die mitgedacht haben.

75% ist schon Anschlag mach alles was in ("XXXccc") ausgegeben wird mit FMakro.
Aus :

mach

display.println(F("Motor dreht"));

und schön die Klammer zählen;

Der Sketch verwendet 23128 Bytes (75%) des Programmspeicherplatzes. Das Maximum sind 30720 Bytes.
Globale Variablen verwenden 894 Bytes (43%) des dynamischen Speichers, 1154 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

Nach F: sind es immer noch 75% :slight_smile:

Der Sketch verwendet 23228 Bytes (75%) des Programmspeicherplatzes. Das Maximum sind 30720 Bytes.
Globale Variablen verwenden 696 Bytes (33%) des dynamischen Speichers, 1352 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes

SCNR

@bertbek bei solchen Angaben unbedingt alles mitgeben.
Das führt sonst wie hier zu Verwirrungen und Fehlinterpretationen.

Dem Nano kannst Du auch den UNO-Bootloader aufspielen.
Dann gewinnst Du noch Speicherplatz....

Der Sketch verwendet 23228 Bytes (72%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.

wow! Habe jetzt, wie fony vorgeschlagen, alle display.println mit FMacro versehen und ... siehe da, obwohl sich der Speicherplatz nicht geändert hat, sind die Schmutzzeichen WEG :crazy_face: :grin:
Crazy...

Warum auch immer...
Jedenfalls DANKE nochmal an alle. Bin happy..


Bin davon ausgegangen das es um RAM geht
Programmspeicher hat im dem Fall nix zu sagen kann 100% sein :wink:
So manche gucken auf Prog statt auf Ram :wink:

@SCNR alles klar. Mach ich in Zukunft..

Das gleiche Problem hatte ich mit LCD 2004 auch Geister Zeichen :wink:
Das passiert aber nur mit I2C warum auch immer