Nano hängt sich auf

Hallo zusammen,

als Neuling habe ich das Problem, dass sich mein Nano immer mal wieder aufhängt.

Kurze Beschreibung des Projekts:

  • Arduino Nano V3 von AZ Delivery
  • Über I2C ein DS3231 RTC-Modul, einen BM280 und ein 1,3 Zoll OLED mir SSH1106 angeschlossen.
  • Arduino wird über USB mit Strom versorgt, der Rest hat eine eigene Versorgung

Was will ich machen (erste Ausbaustufe, hinterher soll noch mehr kommen)

  • Datum und Kalenderwoche auf das OLED ausgeben
  • x Sekunden warten
  • Temperatur und Feuchtigkeit ausgeben
  • wieder x Sekunden warten
    - Sekunden und Druck auf OLED ausgeben
    - wieder x Sekunden
  • von vorne anfangen

Wenn ich die letzte Ausgabe von Druck und Sekunden weglasse, dann läuft der Sketch mehrere Tage durch. Sobald ich aber die Sekunden einfüge, macht er 10 .... 50 Durchgänge und stürzt ab. Wenn ich den Druck auch noch dazu gebe, dann startet er gar nicht erst.

Den Code habe ich mir zusammen kopiert aus diversen Beispielen.

Hier mein Code:


#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_BME280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <Fonts/arial7pt7b.h>
//#include <AccelStepper.h>



// OLED
#define i2c_Address 0x3c  //initialize OLED with the I2C addr 0x3C
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels
#define OLED_RESET -1     //   QT-PY / XIAO
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);



Adafruit_BME280 bme;  
RTC_DS3231 rtc;

//int wk;
uint16_t OLED_Delay;
uint32_t Unix;
float real_temp;

char wochentage[7][12] = { "7", "1", "2", "3", "4", "5", "6" }; // Wochentag für KW / Beginnend mit Sonntag=7


void setup() {
  Serial.begin(9600);
    while (!Serial);  // time to get serial running
    unsigned status;
    status = bme.begin(0x76);


    if (!rtc.begin()) {
    Serial.println("Finde keine RTC");
    while (true);  }

    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));  // Zeit vom Compiler
    //rtc.adjust(DateTime(2027, 12, 31, 23, 59, 50)); // J, M, T, Std, Min, Sek
  
    DateTime jetzt = rtc.now();
  

    display.begin(i2c_Address, true);
    display.display();
    //delay(500);         // Adafrui-Logo für 0.5sek anzeigen
    display.clearDisplay();
    display.display();

  
}


void loop() {
  showValues();
  
}


void showValues() {

  OLED_Delay = 1000;
  DateTime jetzt = rtc.now();
  char buf1[] = "DD.MM.YYYY";     //Formatierung vom Datum und der Zeit

  real_temp = (rtc.getTemperature() + bme.readTemperature()) / 2;  //Mittelwert aus BME- und RTC-Temperatur berechnen

  display.clearDisplay();

  display.setFont(&arial7pt7b);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(18, 30);
  display.print("Temp: ");
  display.print(real_temp, 1);
  display.setCursor(92, 30);
  display.print(" C");
  display.drawCircle(93, 23, 2, SH110X_WHITE);

  display.setCursor(23, 45);
  display.print("Humi: ");
  display.print(bme.readHumidity(), 1);
  display.print("%");

  display.drawRect(10, 10, 105, 46, SH110X_WHITE);

  display.display();


  delay(OLED_Delay);


  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(27, 30);
  display.print(jetzt.toString(buf1));

  display.setCursor(36, 45);
  display.print("KW ");
  if (KW(jetzt.year(), jetzt.month(), jetzt.day())<10) display.print("0");
  display.print(KW(jetzt.year(), jetzt.month(), jetzt.day()));
  display.print(".");
  display.print(wochentage[jetzt.dayOfTheWeek()]);

  display.drawRect(10, 10, 105, 46, SH110X_WHITE);

  display.display();
  
/*
  delay(OLED_Delay);

  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(27, 30);
  display.print("Sek.: ");
  display.print(jetzt.second());
  
  display.setCursor(23, 45);
  display.print("Pres: ");
  //display.print(bme.readPressure() / 100.0F);
  display.print("hPa");

  display.drawRect(10, 10, 105, 46, SH110X_WHITE);

  display.display();

*/


  delay(OLED_Delay);
}





// Kopiert von https://forum.arduino.cc/t/berechnung-der-wochennummer/458661/6
/* DIN/ISO Kalenderwoche berechnen */
long JD(int y,int m,int d)
{ //calculate and return Julian Day Number from calendar date
  long jd=  ( 1461L * ( y + 4800 + ( m - 14 ) / 12 ) ) / 4 +
          ( 367 * ( m - 2 - 12 * ( ( m - 14 ) / 12 ) ) ) / 12 -
          ( 3 * ( ( y + 4900 + ( m - 14 ) / 12 ) / 100 ) ) / 4 +
          d - 32075;
  return jd;
}

byte isoWeekDay(long J)
// Parameter ist die Julianische Tagesnummer
// Rückgabewert ist europäischer ISO Wochentag mit  Mon=1, Tue=2,Sat=6, Sun=7
{return J%7+1;}

long kw1JD(int year)
{// liefert die Julianische Tagesnummer der ersten KW des Jahres
  long result=JD(year,1,4);                     //vierter Januar gehört immer zur 1.KW
  byte weekday=isoWeekDay(result);              // Wochentag nach ISO
  return result-weekday+1;
}

int KW(int Year,byte Month, byte Day)
{
  long kwJD=JD(Year,Month,Day); 
  if(Month==12 && kwJD>=kw1JD(Year+1)) return 1;                  // im Dezember bereits erste Woche des Folgejahres
  else if(Month==1 && kwJD<kw1JD(Year)) return KW(Year-1,12,31);  //im Januar noch letzte KW  des Vorjahrs
  else return 1+(kwJD-kw1JD(Year))/7;                             // Normalfall
} 

Kann mir bitte jemand helfen und mir sagen, wo mein Fehler liegt? Ich stehe vollkommen auf dem Schlauch.

DANKE!

Was bedeutet dies. Versuche mal zu umschreiben, was passiert. Wie kommst du zu deiner Schlussfolgerung.

Das Display "friert ein" und der letzte Wert bleibt stehen.
Ich hatte auch mal eine einfach for i-Schleife im Loop mit eingebaut und mir den i-Wert im seriellen Monitor ausgegeben. Dort blieb der Wert dann auch konstant und zählte nicht mehr weiter hoch

Welche Zeilen sind denn

?

Wie lange sind die I2C-Leitungen zwischen Microcontroller und OLED-Display?
Wie lange sind die I2C-Leitungen zwischen Microcontroller und BM280-sensor?
Wie lange sind die I2C-Leitungen zwischen Microcontroller und RTC?

Und da bereiten dìr die delays große Probleme. Da werden alle Funktionen bei dir ständig verzögert aufgerufen.
Delay ist dabei total fehl am Platz. Baue es mit der Funktion "millis()" auf indem du einen Timer aufbaust, der deine einzelnen Funktioen nacheinander startet. Oder nutze eine fertigen Timer wie Interval.

Die in meinem Code mit /* .... */ auskommentierten Zeilen das.
Der Druck ist dann noch mal zusätzlich mit // auskommentiert

Mit die letzte meinte ich, dass ich dreimal den Bildschirminhalt aufbaue. Die ersten beiden klappen, wenn ich aber drei mal aufbaue, dann nicht mehr. Als beim dritten (letzten) mal

Das Board mit dem Arduino ist mit ca. 20cm Kabeln mit einem extra Board verbunden, auf dem alle I2C Sachen sind. Siehe Bild.

ja, das ist mir klar. Allerdings verstehe ich nicht, warum das zum abstürzen führen kann

Bei "millis()" hatte ich gelesen, dass man auf einen überlauf (?) achten muss. Da ich aber noch nicht genau verstanden habe, was ich beachten muss, hatte ich es erst mal bei den "delay()" gelassen.

Danke für den Hinweis, werde ich mir mal anschauen

An welcher Stelle der Sketch und warum genau hängen bleibt, wird man sicher nur mit genauer Untersuchung am "lebenden Objekt" heraus finden können. Oder du stellst den Sketch konsequent mittels millis() oder den gezeigten Timern, die auch auf millis() basieren, um.
Für millis() gibt es auch genügend Beispiele, die kein Überlaufproblem haben.

Das ist eine - meiner Meinung nach - zu kurze und deswegen nicht hilfreicher Hinweis auf nicht-blockierendes Timing.

Es gibt eine Variante wie man nicht-blockierendes Timing so programmiert,
dass der "Überlauf" überhaupt kein Problem darstellt.

Hier ist ein kleines Demo-Programm, das demonstriert das das Timing auch dann funktioniert wenn die function millis() bei ihrem Maximalwert
von 2 hoch 32 minus 1 = 4.294.967.295 ankommt und dann wieder auf 0 springt.

Das das so - völlig contra-intuitiv - funktioniert liegt an der speziellen Art und Weise wie mit Variablen vom typ unsigned long gerechnet wird.

Unsigned bedeutet: es gibt nur positive Zahlen. Es gibt keine Minuszahlen.
Es gibt selbst dann eine positive Zahl wenn nach Adam Riese eine negative herauskommen müsste.

Beispiel: 2 - 10 = positive Zahl

Deshalb merke:
alles was mit millis() zu tun hat immer als Variablen vom typ unsigned long deklarieren.
Es gibt einige Ausnahmen aber dann muss man sich mehr Regeln merken wann es geht und wann nicht.

unsigned long start;
unsigned long simMillis;

void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  // 4 before max 4294967295
  simMillis     = 4294967291; 
  start = simMillis;

  for (byte i = 0; i <= 10; i++) {
    Serial.print("simMillis=");
    Serial.print(simMillis);

    Serial.print("  start=");
    Serial.println(start);

    Serial.println("simMillis - start");
    Serial.print(simMillis);
    Serial.print(" - ");
    Serial.print(start);
    Serial.print(" = ");
    Serial.println(simMillis - start);
    Serial.println();
    simMillis++;  
  }
}

void loop() {
}

Hier die entsprechene WOKWI-Simulation

Danke, dann werde ich mich da in den nächsten Tagen doch wohl mal einlesen

Das ist doch schön, wenn du auch eine Meinung hast.
Auch das ist wenig hilfreich.
Aber schön, das du hier immer wieder Helfer kritisierst.
Das hilft den Fragestellern deutlich weiter.

Die gezeigten Timer mit "Interval" nutze ich für solche Steuerungen oft und sehr gerne, da wie der Autor auch selbst schreibt, weniger Schreibarbeit beim Programmieren anfällt und damit auch weniger Fehler auftreten.

"interval" werde ich mir in den nächsten Tagen mal durchlesen.

Eine andere Frage habe ich noch: In dem Beispiel zum BME280 stand

Serial.begin(9600);

beim RTC war dann aber

Serial.begin(115200);

Ich habe bei mir 9600 baud gewählt. kann das zu einem Problem führen?

Nein, das ist kein Problem für deinen Sketch.
Damit stellst du nur die Geschwindigkeit ein, wie schnell Informationen auf deinem Monitor angezeigt bzw. übertragen werden sollen.
Das ist für ein Fehler-Debugging nützlich.
Nimm aber besser die schnnellere, größere Baud Zahl.

Das kann ich dir nur sehr empfehlen. Das macht dir vieles leichter.

Das ist nicht analysiert sondern eine Vermutung
der c_string buf1 ist möglichweise zu kurz
Habe buf1 als deutlich länger deklariert

  //             123456789012345678901234567890
  char buf1[] = "DD.MM.YYYY12345678901234567890";     //Formatierung vom Datum und der Zeit

Hier dein Sketch aus post # 1 mit dieser Änderung und mit allen Ausgaben auf das OLED-Display

#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_BME280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <Fonts/arial7pt7b.h>
//#include <AccelStepper.h>


// OLED
#define i2c_Address 0x3c  //initialize OLED with the I2C addr 0x3C
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels
#define OLED_RESET -1     //   QT-PY / XIAO
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);



Adafruit_BME280 bme;  
RTC_DS3231 rtc;

//int wk;
uint16_t OLED_Delay;
uint32_t Unix;
float real_temp;

char wochentage[7][12] = { "7", "1", "2", "3", "4", "5", "6" }; // Wochentag für KW / Beginnend mit Sonntag=7


void setup() {
  Serial.begin(9600);
    while (!Serial);  // time to get serial running
    unsigned status;
    status = bme.begin(0x76);


    if (!rtc.begin()) {
    Serial.println("Finde keine RTC");
    while (true);  }

    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));  // Zeit vom Compiler
    //rtc.adjust(DateTime(2027, 12, 31, 23, 59, 50)); // J, M, T, Std, Min, Sek
  
    DateTime jetzt = rtc.now();
  
    display.begin(i2c_Address, true);
    display.display();
    //delay(500);         // Adafrui-Logo für 0.5sek anzeigen
    display.clearDisplay();
    display.display();  
}


void loop() {
  showValues();  
}


void showValues() {

  OLED_Delay = 1000;
  DateTime jetzt = rtc.now();
  //             123456789012345678901234567890
  char buf1[] = "DD.MM.YYYY12345678901234567890";     //Formatierung vom Datum und der Zeit

  real_temp = (rtc.getTemperature() + bme.readTemperature()) / 2;  //Mittelwert aus BME- und RTC-Temperatur berechnen

  display.clearDisplay();

  display.setFont(&arial7pt7b);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(18, 30);
  display.print("Temp: ");
  display.print(real_temp, 1);
  display.setCursor(92, 30);
  display.print(" C");
  display.drawCircle(93, 23, 2, SH110X_WHITE);

  display.setCursor(23, 45);
  display.print("Humi: ");
  display.print(bme.readHumidity(), 1);
  display.print("%");

  display.drawRect(10, 10, 105, 46, SH110X_WHITE);

  display.display();


  delay(OLED_Delay);


  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(27, 30);
  display.print(jetzt.toString(buf1));

  display.setCursor(36, 45);
  display.print("KW ");
  if (KW(jetzt.year(), jetzt.month(), jetzt.day())<10) display.print("0");
  display.print(KW(jetzt.year(), jetzt.month(), jetzt.day()));
  display.print(".");
  display.print(wochentage[jetzt.dayOfTheWeek()]);

  display.drawRect(10, 10, 105, 46, SH110X_WHITE);

  display.display();
  
  delay(OLED_Delay);

  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(27, 30);
  display.print("Sek.: ");
  display.print(jetzt.second());
  
  display.setCursor(23, 45);
  display.print("Pres: ");
  display.print(bme.readPressure() / 100.0F);
  display.print("hPa");

  display.drawRect(10, 10, 105, 46, SH110X_WHITE);

  display.display();

  delay(OLED_Delay);
}





// Kopiert von https://forum.arduino.cc/t/berechnung-der-wochennummer/458661/6
/* DIN/ISO Kalenderwoche berechnen */
long JD(int y,int m,int d)
{ //calculate and return Julian Day Number from calendar date
  long jd=  ( 1461L * ( y + 4800 + ( m - 14 ) / 12 ) ) / 4 +
          ( 367 * ( m - 2 - 12 * ( ( m - 14 ) / 12 ) ) ) / 12 -
          ( 3 * ( ( y + 4900 + ( m - 14 ) / 12 ) / 100 ) ) / 4 +
          d - 32075;
  return jd;
}

byte isoWeekDay(long J)
// Parameter ist die Julianische Tagesnummer
// Rückgabewert ist europäischer ISO Wochentag mit  Mon=1, Tue=2,Sat=6, Sun=7
{return J%7+1;}

long kw1JD(int year)
{// liefert die Julianische Tagesnummer der ersten KW des Jahres
  long result=JD(year,1,4);                     //vierter Januar gehört immer zur 1.KW
  byte weekday=isoWeekDay(result);              // Wochentag nach ISO
  return result-weekday+1;
}

int KW(int Year,byte Month, byte Day)
{
  long kwJD=JD(Year,Month,Day); 
  if(Month==12 && kwJD>=kw1JD(Year+1)) return 1;                  // im Dezember bereits erste Woche des Folgejahres
  else if(Month==1 && kwJD<kw1JD(Year)) return KW(Year-1,12,31);  //im Januar noch letzte KW  des Vorjahrs
  else return 1+(kwJD-kw1JD(Year))/7;                             // Normalfall
}

Hallo zusammen,

habe mich jetzt mal an dem CombieTimer versucht. Aber irgendwie geht jetzt gar nichts mehr.
Im Setup-Teil habe ich extra für 1sek das Ardafruit-Logo wieder rein genommen, aber selbst das macht er nicht.

Was mache ich beim CombieTimer falsch?

#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_BME280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <Fonts/arial7pt7b.h>
#include <CombieTimer.h>


// OLED
#define i2c_Address 0x3c  //initialize OLED with the I2C addr 0x3C
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels
#define OLED_RESET -1     //   QT-PY / XIAO

Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_BME280 bme;  
RTC_DS3231 rtc;
Combie::SimpleTimer timer; // timer Instanz anlegen

uint32_t Unix;
float real_temp;

char wochentage[7][12] = { "7", "1", "2", "3", "4", "5", "6" }; // Wochentag für KW / Beginnend mit Sonntag=7


void setup() {
  Serial.begin(9600);
    while (!Serial);  // time to get serial running
    unsigned status;
    status = bme.begin(0x76);


    if (!rtc.begin()) 
    {
      Serial.println("Finde keine RTC");
      while (true);
    }

    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));  // Zeit vom Compiler
    //rtc.adjust(DateTime(2027, 12, 31, 23, 59, 50)); // J, M, T, Std, Min, Sek
  
    DateTime jetzt = rtc.now();


    display.begin(i2c_Address, true);
    display.display();
    delay(1000);         // Adafrui-Logo für 1sek anzeigen
    display.clearDisplay();
    display.display();

  
}


void loop() 
{
  showValues();


}


void showValues() 
{

  DateTime jetzt = rtc.now();
  char buf1[] = "DD.MM.YYYY";     //Formatierung vom Datum und der Zeit

  real_temp = (rtc.getTemperature() + bme.readTemperature()) / 2;  //Mittelwert aus BME- und RTC-Temperatur berechnen

  timer.start();
  
  if(timer(1000))
  {
    display.clearDisplay();

    display.setFont(&arial7pt7b);
    display.setTextColor(SH110X_WHITE);
    display.setCursor(18, 30);
    display.print("Temp: ");
    display.print(real_temp, 1);
    display.setCursor(92, 30);
    display.print(" C");
    display.drawCircle(93, 23, 2, SH110X_WHITE);

    display.setCursor(23, 45);
    display.print("Humi: ");
    display.print(bme.readHumidity(), 1);
    display.print("%");

    display.drawRect(10, 10, 105, 46, SH110X_WHITE);

    display.display();
   
  }

    
  if(timer(2000))
  {
    display.clearDisplay();

    display.setTextSize(1);
    display.setTextColor(SH110X_WHITE);
    display.setCursor(27, 30);
    display.print(jetzt.toString(buf1));

    display.setCursor(36, 45);
    display.print("KW ");
    if (KW(jetzt.year(), jetzt.month(), jetzt.day())<10) display.print("0");
    display.print(KW(jetzt.year(), jetzt.month(), jetzt.day()));
    display.print(".");
    display.print(wochentage[jetzt.dayOfTheWeek()]);

    display.drawRect(10, 10, 105, 46, SH110X_WHITE);

    display.display();
    
  }
  
  
  if(timer(3000))
  {
    display.clearDisplay();

    display.setTextSize(1);
    display.setTextColor(SH110X_WHITE);
    display.setCursor(27, 30);
    display.print("Sek.: ");
    display.print(jetzt.second());
    
    display.setCursor(23, 45);
    display.print("Pres: ");
    //display.print(bme.readPressure() / 100.0F);
    display.print("hPa");

    display.drawRect(10, 10, 105, 46, SH110X_WHITE);

    display.display();
    
  }

  //timer.start();
  
}

Du hast da nicht den "Interval" genommen, den ich dir vorgeschlagen habe. Der befindet sich in einem weiteren Link des Beitrages.
Das ist dieser hier.

Allerdings sehe in deinem Sketch kein Logo, dass du anzeigen willst.

Ich glaube nicht, dass der Timer ca 10000 mal pro Sekunde gestartet werden sollte.

Nee, soweit ich mich erinnern kann, habe ich den eigentlich für OO Statemachines gebaut.

Es geht allerdings auch in Funktionen!
Du scheinst 3 Timer zu gebrauchen.
Dann mache dir auch 3

Danke für den Hinweis, dann werde ich es heute Abend noch mal mit Interval probieren.

Im Setup-Teil startet er (in meinem Verständnis) hier das Display. Wenn ich dort die Pause (Ja, ist noch mit delay) drin lasse, dann zeigt er beim Starten einmal kurz das Logo an. Momentan kommt er gar nicht so weit, sondern das Display bleibt leer.

    display.begin(i2c_Address, true);
    display.display();
    delay(1000);         // Adafrui-Logo für 1sek anzeigen
    display.clearDisplay();
    display.display();