co2-Ampel + Lora-Bee

Hi Philipp,

würdest Du wohl Deinen Code mit uns teilen, dann hätten wir alle eine „stabile“ Basis von der aus wir das Problem weiter eingrenzen können. Ich komme mit meinen Delay Einstellungen und dem Pi Netzteil selten über 28 Std. Laufzeit bis das ganze wieder einfriert.

Danke und grüße

Sascha

Hallo Sascha @taylor-77 ,
gerne könnt ihr meinen Code haben, den ich gerade im Einsatz habe. Meine Zu-Hause-Ampel ist gestern wieder nach ca. 90 Stunden eingeschlafen. Vielleicht ist es auch nur Zufall, dass sie mit meinen Delay-Einstellungen 90 Stunden schafft, und der Fehler liegt völlig wo anders.

Eine CO2-Ampel (Schule-Ampel) ist der fertige Bausatz. Im Code habe ich noch die Möglichkeit für eine Datenspeicherung auf SD-Karte vorgesehen, die ich gerade aber in der anderen Ampel verbaut habe. Über den seitlichen Knopf kann man ein Menü aufrufen und Wählen zwischen der Anzeige eines Mittelwertes, der Anzeige des aktuellen Wertes, der Datenspeicherung (fast ohne Anzeige für die Nacht) und der Kalibrierung.

Die andere CO2-Ampel hat noch den BME680- und einen Lichtsensor. Außerdem verwende ich einen LED-Ring statt des einzelnen LEDs.

Viele Grüße
Philipp

Hier die Codes (Sorry, ich bin sowohl Anfänger, als auch aus der Übung (mit den Sachen, die ich mal konnte)).
Ampel-Bausatz bzw. Schule-Ampel:

#include <SPI.h>
#include <Wire.h>
#include <SD.h> //für SD-Karte
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "SenseBoxMCU.h"  //für SenseBos
#include "SparkFun_SCD30_Arduino_Library.h" //für Sensor
#include <Adafruit_NeoPixel.h>  //für LED

//Variablen für das Menü

int programm = 1; //über diese Variable wird die Programm gesteuert. Vorauswahl: Datenampel
const long MENUEVERZOEGERUNG = 2000; //Zeit bis ein Programm gestartet wird

int radius = 5;   //Rückgriff der Aktivitätenanzeige auf diese Variable
boolean wachsen = true; //Rückgriff der Aktivitätenanzeige auf diese Variable

boolean anzeigeNeu = false; //für die Aktualisierung der Anzeige während der MittelwertAmpel

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
Button button(0);  //Botton wird angelegt
boolean buttonstatus = LOW; //Darüber wird gespeichert, ob der Button gedrückt wurde.
Adafruit_NeoPixel rgb_led_1= Adafruit_NeoPixel(6, 1,NEO_GRB + NEO_KHZ800);  //LED wird angelegt


SCD30 airSensor; //über dieses Objekt wird der Sensor angesprochen. Es sollte global sein, da der Sensor nur alle 0,5 s Werte liefert. 
//in Funktionen liegen also oft keine Werte vor.
float scd30_co2 = 0.0; //in dieser globalen Variable werden die abgefragten CO2-Werte des Sensors gespeichert. 
//Wenn keine neuen Werte vorliegen, wird auf die der letzten Abfrage zugegriffen.

File datei; //über dieses Objekt erfolgt der Zugriff auf die Datei



/*
 * Die Funktion gibt für 3 s eine Fehlermeldung auf dem Display aus. 
*/
void fehlermeldung(String fehlertext) {
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.println(fehlertext);
  display.display();
  delay (3000);
  }//END Fehlermeldung



/*
 * Fehlermeldung ohne Anzeigezeit. Macht nur Sinn, wenn das Programm einfriert. Zur Zeit nicht verwendet.
*/
void fehlermeldung2(String fehlertext) {
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.println(fehlertext);
  display.display();
  }//END Fehlermeldung



/*
 * Erzeugt einen kleinen Ball oben rechts, der die Aktivität anzeigt, um ein Einfrieren leichter erkennen zu können.
*/
void aktivitaet() {
  //display.clearDisplay();
  display.fillCircle(123,5,radius,1); //Kreis an x, y, mit Radius , ausgefüllt
  if ((wachsen)&&(radius>4)) {wachsen = false;}
  if ((!wachsen)&&(radius<2)) {wachsen = true;}
  if (wachsen){radius++;}else{radius--;}
  
  //display.display();
  }//END aktivitätenanzeige



/*
 * für eine fast anzeigenfreie Datenspeicherung 
*/

void nachtanzeige(int co2Wert){
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(1);
  display.setTextColor(WHITE,BLACK);
  display.println(String(co2Wert)+" ppm CO2");
  aktivitaet();
  display.display();
}//END nachtanzeige




/*
 * Die Funktion erstellt ein Menü aus einem übergebenen Array aus Strings. (max 10 Zeichen je String)
 * Sie liefert die Nr. des ausgewählten Menüpunkts zurück. (bzw. 0 als Fehler)
*/

int menue(int anzahlMenuepunkte, String menueueberschrift, String auswahltext[]) {  
  
  rgb_led_1.begin(); //LED wird während des Menüs ausgeschaltet.
  rgb_led_1.clear();
  rgb_led_1.show();
  
  if (anzahlMenuepunkte < 2) {return 0;} //Fehler, da zu wenig Menüpunkte (bei 0 oder 1 macht ein Menü auch keinen Sinn)
  long time_start = 0;
  long time_actual = 0;
  int menueauswahl = 1;
  time_actual = millis();  //Vergleichswert für die Schleife
  do { //Schleife bricht ab, wenn die Zeit der "Menüverzögerung" lang kein Button gedrück wurde.
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(BLACK,WHITE);
    display.println(menueueberschrift);
    display.setCursor(0,17);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    display.println(auswahltext[menueauswahl-1]);
    display.setCursor(0,37);
    display.setTextSize(1);
    display.setTextColor(WHITE,BLACK);
    if (menueauswahl < (anzahlMenuepunkte)) {display.println(auswahltext[menueauswahl]); }
    else {display.println(auswahltext[0]);}
    display.display();
    buttonstatus = button.wasPressed();
    if ((buttonstatus == HIGH)&&(millis()>time_actual+250)) { //durch die Zeitabfrage wird ein versehentlicher doppelklick verhindert
        time_actual = millis();   //zweiter Vergleichswert für die Schleife wird nur genommen, wenn Button gedrückt wird.
        menueauswahl++; //Das nächste Programm wird ausgewählt
        if (menueauswahl > anzahlMenuepunkte) {menueauswahl = 1;} 

    }//END if button
    time_start = millis(); //Vergleichswert für die Schleife
  } while (time_start < time_actual + MENUEVERZOEGERUNG);
   
  return menueauswahl;
}//END Menue





/*
 * die Funktion fragt die Daten ab. Sie liefert erst einen Rückgabewert, wenn es Daten gibt.
 * scd30_co2 ist global. Daher sind dort noch die letzten Werte gespeichert, wenn noch keine neuen Daten beim Sensor vorliegen.
 * 
*/
int co2SensorWert() {
  int sensorWert = 0;
  delay(50);
  if (airSensor.dataAvailable()) {delay(50); scd30_co2 = airSensor.getCO2();}
  delay(50);
  sensorWert = int(round(scd30_co2));   
  return sensorWert;
  }//end int co2SensorWert





/* 
 *  Die Funktion gibt einen Wert und seine Einheit auf dem Display aus.
 *  Wert maximal 5 Stellen
 *  Einheit maximal 10 Stellen
*/
  void wertanzeige(int wert, String text) {
      display.clearDisplay();
      display.setCursor(0,0);
      display.setTextSize(5);
      display.setTextColor(WHITE,BLACK);
      display.println(wert);
      display.setCursor(0,47);
      display.setTextSize(2);
      display.setTextColor(WHITE,BLACK);
      display.println(text);
      aktivitaet();
      display.display(); //die Vorherigen Werte werden auf das Disply geschreiben
  } //END void wertanzeige




/* 
 *  Die Ampel zeigt grün unter 1000ppm, gelb unter 1400 ppm, rot unter 2000 ppm und blinkt rot über 2000 ppm. 
*/
  void ampelanzeige(int co2wert) {
    const long BLINKINTERVALL = 1000; //Die Hälfte dieser Zeit ist das Blinkintervall über 2000 ppm
    long time_start = 0;
    long blinkzeit = 0; 
    boolean blinker = false;
    
    const int MAXLEDHELLIGKEIT = 100;
    uint32_t farbevorher;
    uint32_t farbenachher;

    time_start = millis();
    blinkzeit = time_start % BLINKINTERVALL;
    if (blinkzeit > (BLINKINTERVALL/2)) {blinker = true; } else {blinker = false;}

    rgb_led_1.begin();
    farbevorher = rgb_led_1.getPixelColor(0);
    
    if (co2wert < 1000) {
      rgb_led_1.setBrightness(MAXLEDHELLIGKEIT);
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(0,255,0));
    } else if (co2wert >= 1000 && co2wert < 1400) {
      rgb_led_1.setBrightness(MAXLEDHELLIGKEIT);
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(255,255,0));
    } else if (co2wert >= 1400 && co2wert < 2000) {
      rgb_led_1.setBrightness(MAXLEDHELLIGKEIT);
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(255,0,0));
    } else if (co2wert >= 2000) {
      if (blinker) {
        rgb_led_1.setBrightness(MAXLEDHELLIGKEIT);
        rgb_led_1.setPixelColor(0,rgb_led_1.Color(255,0,0));
      } else {
        rgb_led_1.setBrightness(0);
      }
    } else {
      rgb_led_1.setBrightness(MAXLEDHELLIGKEIT);
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(0,0,255)); //blau bei Fehler
    }
    
    rgb_led_1.show();
    farbenachher = rgb_led_1.getPixelColor(0);
    if (farbenachher != farbevorher) {anzeigeNeu = true;}else {anzeigeNeu = false;}
  } //END void ampelanzeige





/*
 * Die LED zeigt einen Regenbogen.
 * Gleichzeitig werden die Sensordaten ein paarmal abgefragt, da sie am Anfang noch keine zuverlässigen Werte liefern.
*/
void start() {
  rgb_led_1.begin();
  rgb_led_1.setBrightness(255);

  int rotzaehler = 0;
  int gruenzaehler = 0;
  int blauzaehler = 0;
  int zaehleinheit = 5;
  const int WARTEZEIT = 10;

  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.println("CO2-Ampel wird      aktiviert");
  display.display();

  int co2Wert = co2SensorWert();
  
  for (int count = 0; count < 2; count++) {
    gruenzaehler = 255;
    while (rotzaehler < 255) {
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(rotzaehler,gruenzaehler,blauzaehler));
      rgb_led_1.show();
      delay(WARTEZEIT);      
      rotzaehler = rotzaehler + zaehleinheit;
    }
    co2Wert = co2SensorWert();
    rotzaehler = 255;
    while (gruenzaehler > 0) {
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(rotzaehler,gruenzaehler,blauzaehler));
      rgb_led_1.show();
      delay(WARTEZEIT);         
      gruenzaehler = gruenzaehler - zaehleinheit;
    }
    co2Wert = co2SensorWert();
    gruenzaehler = 0;
    while (blauzaehler < 255) {
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(rotzaehler,gruenzaehler,blauzaehler));
      rgb_led_1.show();
      delay(WARTEZEIT);       
      blauzaehler = blauzaehler + zaehleinheit;
    }
    co2Wert = co2SensorWert();
    blauzaehler = 255;
    while (rotzaehler > 0) {
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(rotzaehler,gruenzaehler,blauzaehler));
      rgb_led_1.show();
      delay(WARTEZEIT); 
      rotzaehler = rotzaehler - zaehleinheit;
    }
    co2Wert = co2SensorWert();
    rotzaehler = 0;
    while (gruenzaehler < 255) {
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(rotzaehler,gruenzaehler,blauzaehler));
      rgb_led_1.show();
      delay(WARTEZEIT); 
      gruenzaehler = gruenzaehler + zaehleinheit;
    }
    co2Wert = co2SensorWert();
    gruenzaehler = 255;
    while (blauzaehler > 0) {
      rgb_led_1.setPixelColor(0,rgb_led_1.Color(rotzaehler,gruenzaehler,blauzaehler));
      rgb_led_1.show();
      delay(WARTEZEIT); 
      blauzaehler = blauzaehler - zaehleinheit;
    }
    co2Wert = co2SensorWert();
    blauzaehler = 0;
  }
    rgb_led_1.clear();
    rgb_led_1.show();
  }//END start




/*
 * Sensor ins Freie bringen.
 * Nach 4 Minuten erfolgt die Kalibrierung auf 450 ppm
 * Er benötigt konstante Bedingungen und sollte schon etwas warmgelaufen sein. 
 * Wenn man ihn von einem warmen Raum ins kalte nach draußen bringt, sind die vier Minuten u. U. nicht ausreichend.
*/
void kalibrierung(){
  long laufzeit = millis();
  long laufzeit2 = millis();
  int countdown = 60;
  
  rgb_led_1.clear();
  rgb_led_1.setPixelColor(0,0,255,255); //türkis
  rgb_led_1.show();
  
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(1);
  display.setTextColor(WHITE,BLACK);
  display.println("Zur Kalibrierung den Sensor nach draussen bringen");
  display.display();
  delay(3000); 
  do {
    if (millis() > laufzeit2 + 1000) {
      laufzeit2 = millis();
      display.clearDisplay();
      display.setCursor(0,0);
      display.println("Kalibrierung startet in " + String(countdown) + " Sekunden");
      display.display();
      countdown--;
      } //END if
    } while (millis() < laufzeit + 60000); // 1 min Zeit um nach Draußen zu bringen
    
    countdown = 240; //4 min
    laufzeit = millis(); //neuer Vergleichswert
    do {
    if (millis() > laufzeit2 + 1000) {
      laufzeit2 = millis();
      display.clearDisplay();
      display.setCursor(0,0);
      display.println("Noch " + String(countdown) + " Sekunden");
      display.setCursor(0,9);
      display.println("Kalibrierung");
      display.display();
      countdown--;
      } //END if
    } while (millis() < laufzeit + 240000); // 4 min Kalibrierung
  
  airSensor.setForcedRecalibrationFactor(450); //Kalibrierungswert wird gesetzt

  display.clearDisplay();
  display.setCursor(0,0);
  display.println("Kalibrierung abgeschlossen");
  display.display();
  delay(3000);
  
  
  }//END void kalibrierung





/*
 * CO2-Ampel mit Sofortanzeige der Messung.
 */
void co2SofortAmpel() {
  int co2Wert = co2SensorWert();    
  
  do {
    co2Wert = co2SensorWert(); 
    wertanzeige(co2Wert, "ppm CO2");
    ampelanzeige(co2Wert);
    buttonstatus = button.wasPressed();
  } while (buttonstatus == LOW);
}//END co2SofortAmpel





/*
 * Es wird jede Sekunde eine Messunge durchgeführt und ein Mittelwert aus 20 Messungen gebildet.
 * Das Puffert leichte schwankungen am, z. B. wenn man in die Richtung der Ampel atmet.
 * Die Anzweige wird ebenfalls nur alle 20 Sekunden aktuallisiert. Die erste Anzeige erfolgt daher auch erst nach 20 Sekunden.
*/
void co2MittelwertAmpel() {  
  int co2Wert = 0;
  const int MESSUNGEN = 20; //Anzahl der Messwerte für den Mittelwert
  int co2Werte[MESSUNGEN]; //Über das Array wird ein Mittelwert der Messungen von 1 Minuten gebildet.
  int co2WerteZaehler = 0; //zeigt die Position der letzten Messung an.
  int co2Mittelwert = 0;
  int co2Zwischenwert = 0;
  int co2Altwert = 0; //für die Aktivitätenanzeige benötigt
  
  const long MESSINTERVALL = 1000; //alle 1 s soll ein Messwert genommen werden
  long time_seitstart = 0;
  long time_jetzt = 0;
  
  const long ZEIGEINTERVALL = 20000; //nur alle 20 s soll die Anzeige aktualisiert werden
  long vergleichszeit = (-1)*ZEIGEINTERVALL; //sonst erfolgt keine Anzeige beim ersten mal
    
  co2Wert = co2SensorWert(); 
  co2Mittelwert = co2Wert;
  for (int i = 0; i < MESSUNGEN; i++){ co2Werte[i] = co2Wert; }//END for//Die Schleife befüllt den Array mit der ersten CO2-Messung.
  
  do { //Die Schleife läuft, bis der Button gedrückt wird
    time_seitstart = millis(); //Zeit seit dem Systemstart
    if (time_seitstart > time_jetzt + MESSINTERVALL){ //schaut, ob schon eine Sekunde um ist und ein neuer Messwert dem Array zugefügt werden kann.
      time_jetzt = millis(); //Neue Vergleichzeit wird genommen
      co2Werte[co2WerteZaehler] = (long)co2SensorWert(); //Fügt dem Array eine neue Messung hinzu.
      if (co2WerteZaehler < MESSUNGEN-1) co2WerteZaehler++; //Gibt die nächste Position der CO2-Speicherung im Array an.
      else co2WerteZaehler = 0;
      
      co2Zwischenwert = 0; //Berechnung des Mittelwertes
        for (int i = 0; i < MESSUNGEN; i++){ 
        co2Zwischenwert = co2Zwischenwert + co2Werte[i];
        }
      co2Mittelwert = co2Zwischenwert / MESSUNGEN;
      }//End if neuer Wert?
  
    if ((time_seitstart > vergleichszeit + ZEIGEINTERVALL)||(anzeigeNeu)) {    //Akualisierung des Display je nach Zeigeintervall (z. b. 20 sec) bzw. wenn die AMpel ihre Farbe geändert hat. Dann ändert dort anzeigeNeu seinen WErt.
      vergleichszeit = millis();
      wertanzeige(co2Mittelwert, "ppm CO2");
      co2Altwert = co2Mittelwert;
    } //End if neue Anzeige?
    else {wertanzeige(co2Altwert, "ppm CO2");} //diese Zeile wird nur für die Aktivitätenanzeige benötigt
    
    ampelanzeige(co2Mittelwert);
    buttonstatus = button.wasPressed();
  } while (buttonstatus == LOW);
}//END co2MittelwertAmpel




/*
 * die Funktion speichert CO2-werte auf der SD-Karte
*/
void co2Datenaufzeichnung() {
  //Menü mit Zeitintervall 
  const int MENUEZEITAUSWAHLPUNKTE = 4;
  String zeitauswahlMenuetext[] = {"1 Sekunde","15Sekunden","1 Minute","5 Minuten"};

  long time_start = 0;
  long time_actual = millis();
  long intervall = 0;
  
  int zeitauswahl = 0; 
  int datenzaehler = 1;

  int ampelAnzeige = 0;

  int co2Wert = 0;

  zeitauswahl = menue(MENUEZEITAUSWAHLPUNKTE, "Intervall", zeitauswahlMenuetext);
  switch (zeitauswahl) {
    case 1: intervall = 1000; break;
    case 2: intervall = 15000; break;
    case 3: intervall = 60000; break;
    case 4: intervall = 300000; break;
    default: fehlermeldung("Fehler im Zeitauswahl-Menue");//z. B. Fehlermeldung oder Hauptprogramm
    break;
    }//END Switch
  time_actual -= intervall; //Sonst wird erst nach der ersten Intervall-Zeit gespeichert. So aber schon beim ersten Schleifendurchlauf

  
  //Menü, ob Ampel und Display jeweils an oder aus sein sollen
  const int MENUEAMPELPUNKTE = 3;
  String ampelauswahlMenuetext[] = {"mit Ampel", "nur Werte", "winzig"};
  ampelAnzeige = menue(MENUEAMPELPUNKTE, "Anzeige", ampelauswahlMenuetext);

  
  if (SD.begin(28)){ //Weshalb 28? Vielleicht die Stelle des SD-Bees auf der SenseBox? 
    //nur SD.begin(28) funktioniert auch, aber so kann es mit einer Fehlermeldung kombiniert werden.
    datei = SD.open("CO2Daten.txt", FILE_WRITE);  //öffnet die DAtei oder legt sie neu an.
    datei.println("CO2-Aufzeichnung"); 
    datei.println("Zeitintervall: "+String(zeitauswahlMenuetext[zeitauswahl-1])); //Überschrift für die Aufzeichung;
    datei.close(); //schließt die Datei
  } else {fehlermeldung("Keine     SD-Karte"); return;} //Fehlermeldung und Abbruch der Funktion bei fehlernder SD-Karte
 
  //Display und LED werden ausgeschlatet, falls das die Menü-Auswahl ist. 
  display.clearDisplay();
  display.println("");
  display.display();
  rgb_led_1.begin();
  rgb_led_1.clear();
  rgb_led_1.show();
  
  do { //Die Schleife läuft, bis der Button gedrückt wird
    //fehlermeldung2("Position1"); 
    co2Wert = co2SensorWert();
    //fehlermeldung2("Position1a");
    time_start = millis();
    //fehlermeldung2("Position1b");
    if (time_start > (time_actual + intervall)) {
      //fehlermeldung2("Position2");
      time_actual = millis();
      if (SD.begin(28)){ 
        //fehlermeldung2("Position3");
        datei = SD.open("CO2Daten.txt", FILE_WRITE);  //öffnet die DAtei oder legt sie neu an.
        datei.println(String(datenzaehler)+": "+String(co2Wert));  //Schreibt CO2-Werte auf SD-Karte
        datei.close();
        //fehlermeldung2("Position4");
      } else {fehlermeldung("Keine     SD-Karte");} //hier kein Abbruch. Am Anfang steht man noch neben der Ampel, aber mitten drin nicht mehr. Dann bekommt man den Datenfehler nicht mit, wenn das Programm mitten drin auf "Ampel" ohne Aufzeichung umschaltet. 
      datenzaehler++;
    }//END if
    
  //fehlermeldung2("Position5");
  switch (ampelAnzeige) { //Auswahl der Anzeige während der Datenaufzeichnung
    case 1: // Ampel- und Wertanzeige (Nur Ampel nicht möglich, da in der Ampel auch die WErte aufgerufen werden)
    ampelanzeige(co2Wert);
    wertanzeige(co2Wert, "ppm CO2");
    break;
    case 2: //Nur Werteanzeige
    wertanzeige(co2Wert, "ppm CO2");
    break;
    case 3: //fast nichts (Nachtmodus ;-) )
    nachtanzeige(co2Wert);
    break;
    default: fehlermeldung("Fehler bei der Anzeige");
    break;
    }//END switch
    //fehlermeldung2("Position6");
    buttonstatus = button.wasPressed();
  } while (buttonstatus == LOW); 
  
}//END co2Datenaufzeichnung



void setup() {
  senseBoxIO.powerI2C(true);
  delay(2000);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
  display.display();
  delay(100);
  display.clearDisplay();
  button.begin();
  buttonstatus = button.wasPressed(); //Der Button muss scheinbar einmal schon im Setup abgefragt werden, sonst liefert er True zurück
  buttonstatus = LOW;

  Wire.begin(); //aktiviert den Sensor
  if (airSensor.begin() == false)  { fehlermeldung("Kein CO2-Sensor gefunden."); } //Fehlermeldung, falls Störung im CO2-Sensor
  
  start();
}//ENd Setup





void loop() {
  const int ANZAHLMENUEPUNKTE = 4;
  String MENUEPUNKTE[] = {"Ampel slow", "Ampel now", "Datenampel", "Kalibrier."}; 
  programm = menue(ANZAHLMENUEPUNKTE, "Menue", MENUEPUNKTE);
  switch (programm) {
    case 1: co2MittelwertAmpel();
    break;
    case 2: co2SofortAmpel();
    break;
    case 3: co2Datenaufzeichnung();
    break;
    case 4: kalibrierung();
    break;
    default: fehlermeldung("Funktion  nicht     vorhanden");//z. B. Fehlermeldung oder Hauptprogramm
    break;
    }//END Switch
  //}

}//END loop
1 Like

Und hier die Ampel mit mehr Sensoren und LED-Ring (Zu-Hause-Ampel):

/*
 * Das Programm misst
 * die CO2-Konzentration und gibt sie auf dem Display aus
 * weiterhin wird die Messung auf einem 24 LED-Ring angezeigt.
 * In Intervallen werden diverse Messwerte auf dem OLED ausgegeben.
 * Einige sind deaktiviert, und können ggf. noch eingebunden werden.
*/


#include <Adafruit_NeoPixel.h> //für den LED-Ring
#include "SparkFun_SCD30_Arduino_Library.h" //für CO2-Sensor
#include "SenseBoxMCU.h" //für CO2-, Licht-, Beschleunigungs-Sensor und OLED-Display
#include "bsec.h" //für Umweltsensor BME680
#include <SPI.h> //für OLDE
#include <Wire.h> //für OLED
#include <Adafruit_GFX.h> //für OLED
#include <Adafruit_SSD1306.h> //für OLED
#include <SD.h> //für SD-Karte

#define OLED_RESET 4 //für OLED

//OLED einrichten
Adafruit_SSD1306 display(OLED_RESET); //OLED-Disply initialisieren

//LED einrichten
const int LEDS = 24; //Ring mit 24 LEDs
const int LEDPIN = 5; //Der Ring wird über den Pin 5 angesteuert.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(LEDS, LEDPIN, NEO_GRB + NEO_KHZ800);  //NeoPixel als "pixels" instanziieren
int ledHelligkeit = 100; //max. 255

//SD einrichten
File datei; //über dieses Objekt erfolgt der Zugriff auf die Datei

//Sensoren einrichten
SCD30 airSensor;   //CO2-Sensor initialisieren
TSL45315 tsl; //Licht-Sensor initianlisieren
VEML6070 veml; //uv-Sensor initialisieren
Bsec iaqSensor; //Umweltsensor BME680 initialisieren

//Globale Konstanten
const int ANZEIGEINTERVALL = 2000;
char PROZENT = char(37); //%
char GRAD = char(247); //°


//Globale Variablen (für die Daten der Sensoren)
float scd30_co2 = 0.0; //CO2 in ppm. 
int co2Wert = 0; //die vom Sensor gelieferten Daten sind doppelt so hoch, wie sie sein sollten. Daher werden sie umgerechnet und hier gespeichert.
float bmeTemperatur; //in °C
float bmeHumidity; //Luftfeuchtigkeit in %
double bmePressure; //Luftdruck in Pa
float bmeIAQ; //Innenraumluftqualität IAQ
float bmeIAQAccuracy; //Kalibrierungswert
int bmeCO2; //CO2-Äquivalent 
float bmeBreathVocEquivalent; //Atemvolumen VOC Äquivalent (Übersetzungsfehler? Laut Datenblatt b-VOC = "flüchtige Organische Verbindungen")
unsigned long beleuchtungsstaerke; //in Lux
float uv;
boolean datenaufzeichnung = false; //wird true, wenn eine SD Karte eingelegt ist.
unsigned int datenzaehler = 1; //die Anzahl der Messdaten auf der SD-Karte
long time_start = 0; //für die Datenaufzeichnung
long time_actualdaten = 0; //Vergleichswert für die Datenaufzeichnung
long time_actualwertanzeige = 0; //Vergleichswert für die Anzeige
//long time_actualLedAnzeige = 0; //Vergleichswert für die ledANzeige //nicht benötigt beim Fading
long time_actualSensorenAbfrage = 0; //Vergleichswert für Sensorabfrage
boolean led24istAn = true; //für ledAktivitätenanzeige
int werteAnzeigenZaehler = 0;




/*
 * Die Funktion gibt für 3 s eine Fehlermeldung auf dem Display aus. 
*/
void fehlermeldung(String fehlertext) {
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.println(fehlertext);
  display.display();
  delay (3000);
  }//END void Fehlermeldung




/*
 * die Funktion kontrolliert die Funktionsweise des Umweltsensors BME680
*/
void checkIaqSensorStatus(void)
{
  if (iaqSensor.status != BSEC_OK) {
    if (iaqSensor.status < BSEC_OK) {
      fehlermeldung("Kein      BMW680-   Sensor");
    }
  }
  if (iaqSensor.bme680Status != BME680_OK) {
    if (iaqSensor.bme680Status < BME680_OK) {
      fehlermeldung("Kein      BMW680-   Sensor");
    }
  }
}//END void checkIaqSensorStatus





/*
 * die Funktion fragt die Daten ab. Sie liefert erst einen Rückgabewert, wenn es Daten gibt.
 * scd30_co2 ist global. Daher sind dort noch die letzten Werte gespeichert, wenn noch keine neuen Daten beim Sensor vorliegen.
 * 
*/
int co2SensorWert() {
  int sensorWert = 0;
    delay(50);
    if (airSensor.dataAvailable()) {
      delay(50);
      scd30_co2 = airSensor.getCO2();
      }
    delay(50);
    sensorWert = int(round(scd30_co2));   
  return sensorWert;
  }//end int co2SensorWert





/*
 * Fragt die Werte der Sensoren ab
*/
void sensorenAbfragen(){
  //CO2-Sensor
  co2Wert = co2SensorWert();
  //if (airSensor.dataAvailable()) {scd30_co2 = airSensor.getCO2();}  //wäre eine alternative, aber zur Zeit werden falsche Werte geliefert. Diese werden in der Funktion korrigiert.
  //Umweltsensor BME680
  delay(50);
  if (iaqSensor.run()) {
    delay(50);
      bmeTemperatur = iaqSensor.temperature;
      delay(50);
      bmeHumidity = iaqSensor.humidity;
      delay(50);
      bmePressure = iaqSensor.pressure;
      delay(50);
      bmeIAQ = iaqSensor.iaq;
      delay(50);
      bmeIAQAccuracy = iaqSensor.iaqAccuracy;
      delay(50);
      bmeCO2 = iaqSensor.co2Equivalent;
      delay(50);
      bmeBreathVocEquivalent = iaqSensor.breathVocEquivalent;
      delay(50);
    } else { checkIaqSensorStatus(); 
    }//END if - else
  delay(50);
  beleuchtungsstaerke = tsl.getIlluminance();
  delay(50);
  uv = veml.getUvIntensity();
  delay(50);
  }//END void SensorenAbfragen





/*
 * Die LED zeigt einen Regenbogen.
 * Gleichzeitig werden die Sensordaten ein paarmal abgefragt, da sie am Anfang noch keine zuverlässigen Werte liefern.
 * 
*/
void start() {

  int rotzaehler = 0;
  int gruenzaehler = 0;
  int blauzaehler = 0;
  int zaehleinheit = 5;
  const int WARTEZEIT = 10;

  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.println("CO2-Ampel wird      aktiviert");
  display.display();

  sensorenAbfragen(); //zum Aufwärmen
  
  for (int count = 0; count < 2; count++) {
    gruenzaehler = 255;
    while (rotzaehler < 255) {
      pixels.setPixelColor(0,pixels.Color(rotzaehler,gruenzaehler,blauzaehler));
      pixels.show();
      delay(WARTEZEIT);      
      rotzaehler = rotzaehler + zaehleinheit;
    }
  sensorenAbfragen(); //zum Aufwärmen
    rotzaehler = 255;
    while (gruenzaehler > 0) {
      pixels.setPixelColor(0,pixels.Color(rotzaehler,gruenzaehler,blauzaehler));
      pixels.show();
      delay(WARTEZEIT);         
      gruenzaehler = gruenzaehler - zaehleinheit;
    }
  sensorenAbfragen(); //zum Aufwärmen
    gruenzaehler = 0;
    while (blauzaehler < 255) {
      pixels.setPixelColor(0,pixels.Color(rotzaehler,gruenzaehler,blauzaehler));
      pixels.show();
      delay(WARTEZEIT);       
      blauzaehler = blauzaehler + zaehleinheit;
    }
  sensorenAbfragen(); //zum Aufwärmen
    blauzaehler = 255;
    while (rotzaehler > 0) {
      pixels.setPixelColor(0,pixels.Color(rotzaehler,gruenzaehler,blauzaehler));
      pixels.show();
      delay(WARTEZEIT); 
      rotzaehler = rotzaehler - zaehleinheit;
    }
    sensorenAbfragen();
    rotzaehler = 0;
    while (gruenzaehler < 255) {
      pixels.setPixelColor(0,pixels.Color(rotzaehler,gruenzaehler,blauzaehler));
      pixels.show();
      delay(WARTEZEIT); 
      gruenzaehler = gruenzaehler + zaehleinheit;
    }
    gruenzaehler = 255;
    while (blauzaehler > 0) {
      pixels.setPixelColor(0,pixels.Color(rotzaehler,gruenzaehler,blauzaehler));
      pixels.show();
      delay(WARTEZEIT); 
      blauzaehler = blauzaehler - zaehleinheit;
    }
  sensorenAbfragen(); //zum Aufwärmen
    blauzaehler = 0;
  }
    pixels.clear();
    pixels.show();
  }//END start





/* 
 *  Die Funktion gibt einen Wert und seine Einheit auf dem Display aus.
 *  Wert maximal 5 Stellen
 *  Einheit maximal 10 Stellen
*/
void wertanzeige(int wert, String einheit) {
  
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(5);
  display.setTextColor(WHITE,BLACK);
  display.println(wert);
  display.setCursor(0,47);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.println(einheit);
  delay(50);
  display.display(); //die Vorherigen Werte werden auf das Disply geschreiben
  delay(50);
  } //END void wertanzeige
  
void wertanzeigeKlein(int wert, String einheit) {
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(4);
  display.setTextColor(WHITE,BLACK);
  display.println(wert);
  display.setCursor(0,47);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.println(einheit);
  delay(50);
  display.display(); //die Vorherigen Werte werden auf das Disply geschreiben
  delay(50);
  } //END void wertanzeigeKlein




/*
 * Die Funktion wählt aus, was angezeigt werden soll und gibt es an die Funktion Wertanzeige weiter. 
 * Einige Anzeigeparameter wurden deaktiviert. Sie müssten ggf. noch in die Switch Case-Auswahl eingefügt werden. Dann muss auch der entsprechende Wert im loop geändert werden.
 * 
*/
void werteAnzeigen(int anzeigestelle) {  
  switch (anzeigestelle) {
    case 0: wertanzeige(co2Wert,"ppm CO2"); break;
    case 1: wertanzeige(int(bmeTemperatur), String(GRAD)+"C"); break;//in °C
    case 2: wertanzeige(int(bmeHumidity), String(PROZENT)+" Luftfeu."); break;//Luftfeuchtigkeit in % 
    default: fehlermeldung("Fehler in WertAnzeige");
    }//END Case

  //wertanzeige(int(bmePressure/100), "hPa LuftDr"); //Luftdruck in hPa
  //delay(ANZEIGEINTERVALL);
//  if (int(bmeIAQ) <= 50) {wertanzeige(int(bmeIAQ),"IAQ top");}
//  else if (int(bmeIAQ) <= 100) {wertanzeige(int(bmeIAQ),"IAQ gruen");}
//  else if (int(bmeIAQ) <= 150) {wertanzeige(int(bmeIAQ),"IAQ gelb");}
//  else if (int(bmeIAQ) <= 200) {wertanzeige(int(bmeIAQ),"IAQ orange");}
//  else if (int(bmeIAQ) <= 250) {wertanzeige(int(bmeIAQ),"IAQ rot");}
//  else if (int(bmeIAQ) <= 350) {wertanzeige(int(bmeIAQ),"IAQ violet");}
//  else if (int(bmeIAQ) > 351) {wertanzeige(int(bmeIAQ),"IAQ braun");}
  
   //Innenraumluftqualität IAQ
  //delay(ANZEIGEINTERVALL);
  //wertanzeige(int(bmeIAQAccuracy),"Kalib.Wert"); //Kalibrierungswert
  //delay(ANZEIGEINTERVALL);
  //wertanzeige(int(bmeCO2),"CO2-Äquiv."); //CO2-Äquivalent
  //delay(ANZEIGEINTERVALL);
  //wertanzeige(int(bmeBreathVocEquivalent),"ppm VOC"); //Atemvolumen VOC Äquivalent
  //delay(ANZEIGEINTERVALL);
  }//END void werteAnzeigen





/*
 * die Funktion gibt abhängig von der Beleuchtungsstärke einen Wert zwischen 5 und 250 zurück, mit dem die Helligkeit der LEDs angepasst werden kann
*/
int ledHelligkeitAnpassen() {
  int ledHelligkeit = int(beleuchtungsstaerke);
  if (ledHelligkeit > 250) {ledHelligkeit = 250;}
  if (ledHelligkeit < 5) {ledHelligkeit = 5;}

  return ledHelligkeit;
  }//END int ledHelligkeitAnpassen




/* 
 *  Die Ampel zeigt grün unter 1000ppm, gelb unter 1400 ppm, rot unter 2000 ppm und komplett rot über 2000 ppm. 
*/
void ampelanzeige() {
  
  int co2Ring = (co2Wert/100);
  int farbe = ledHelligkeitAnpassen();
  //pixels.setBrightness(ledHelligkeitAnpassen()); //sollte nur im Setup aufgerufen werden. Die Helligkeit wird dann über setPixelColor gesteuert.
  //pixels.setPixelColor(i, pixels.Color(red, green, blue));

  pixels.clear(); //setzt die Pixel zurück

  if (co2Wert < 1000) { 
    for (int i = 1; i <= co2Ring; i++) { pixels.setPixelColor(i,pixels.Color(0,farbe,0)); } //grün - END for unter 1000
    } else if (co2Wert >= 1000 && co2Wert < 1400) { 
      for (int i = 1; i < 10; i++){ pixels.setPixelColor(i,pixels.Color(0,farbe,0)); } //grün
      for (int i = 10; i <= co2Ring; i++) { pixels.setPixelColor(i,pixels.Color(farbe,farbe,0)); } //gelb 
    } else if (co2Wert >= 1400 && co2Wert < 2400) {
      for (int i = 1; i < 10; i++) { pixels.setPixelColor(i,pixels.Color(0,farbe,0)); } //grün bis max 10
      for (int i = 10; i < 14; i++) {pixels.setPixelColor(i,pixels.Color(farbe,farbe,0));} //gelb bis max 14
      for (int i = 14; i <= co2Ring; i++) { pixels.setPixelColor(i,pixels.Color(farbe,0,0)); } //rot bis 24
    } else if (co2Wert > 2400) {
      for (int i = 1; i < 24; i++) { pixels.setPixelColor(i,pixels.Color(farbe,0,0)); } //alle rot
   } //END if else
   

  for (int i = co2Ring+1; i < 24; i++) {  //deaktiviert alle übrigen Pixel
    pixels.setPixelColor(i,pixels.Color(0,0,0));
    }//END for für schwarze
  
  pixels.show();  //zeigt die neuen Werte
  
  } //END void ampelanzeige




/*
 * LED 0 wechselt die Farbe von blau zu türkis
*/

void ledAktivitaetenAnzeige(){
  int farbe = ledHelligkeitAnpassen(); //gibt den Zielwert passend zur Helligkeit vor

  uint32_t aktuellefarbe = pixels.getPixelColor(0); //holt die Farbe von led 0
  
   // Separiert die Farben in rot, grün, blau-Anteile
  uint8_t rot, gruen, blau;
  rot = (aktuellefarbe >> 16) & 0xFF; 
  gruen = (aktuellefarbe >> 8) & 0xFF; 
  blau = aktuellefarbe & 0xFF; 

  int wartezeit = 1000 / farbe; // passt das Delay an ein Fading von 1 s an.
 
  if (led24istAn) { //true wenn blau // false, wenn türkis
    for (int i = 0; i <= farbe; i++){
      pixels.setPixelColor(0,pixels.Color(0,i,farbe));
      pixels.show();
      delay(wartezeit);
    }
    led24istAn = false;
    }else {
      for (int i = gruen; i >= 0; i--) {
        pixels.setPixelColor(0,pixels.Color(0,i,farbe));
        pixels.show();
        delay(wartezeit);
        }
      led24istAn = true;
      }
  }//END void ledAktiviaetenAnzeige




/*
 * LED 0 und 23 wechseln die Farbe
 * 
 * 
void ledAktivitaetenAnzeige_alt(){
  int farbe = ledHelligkeitAnpassen();
  if (led24istAn) {
    pixels.setPixelColor(23,pixels.Color(0,farbe,farbe));
    pixels.setPixelColor(0,pixels.Color(0,0,farbe));
    led24istAn = false;
    }else {
      pixels.setPixelColor(0,pixels.Color(0,farbe,farbe));
      pixels.setPixelColor(23,pixels.Color(0,0,farbe));
      led24istAn = true;
      }
  pixels.show();
  }//END void ledAktiviaetenAnzeige
*/




/*
 * Die Messdaten werden gespeichert, sofern eine SD-Karte erkannt wird.
*/
void datenAufzeichnen(){

  if (SD.begin(28)){ 
  datei = SD.open("Daten.txt", FILE_WRITE);  //öffnet die DAtei oder legt sie neu an.
  datei.println(String(datenzaehler) + ";" + String(co2Wert) + ";" + String(bmeTemperatur) + ";" + String(bmeHumidity) + ";" + String(bmePressure/100) + ";" + String(bmeIAQ) + ";" + String(bmeCO2) + ";" + String(bmeBreathVocEquivalent) + ";" + String(beleuchtungsstaerke) + ";" + String(uv));
  datei.close();
  } else {fehlermeldung("Keine     SD-Karte"); return;} 
  datenzaehler++;
  
  }//END void datenAufzeichnen





void setup() {
  senseBoxIO.powerI2C(true); //I2C-Schnittstelle anschalten
  //Display aktivieren
  delay(2000);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
  display.display();
  delay(100);
  display.clearDisplay();
  
  pinMode (LEDPIN, OUTPUT); //Pin für LED anschalten
  pixels.begin(); //LEDs aktivieren
  pixels.setBrightness(255); //setzt die Helligkeit auf Maximum. die einzelne Helligkeit wird später über die Farbwerte gesteuert.

  Wire.begin(); //für CO2- und Umweltsensor
  Wire.setClock(50000); //setzt die Kommunikationsgeschwindigkeit für I2C auf 50kHz. Das ist die Geschwindigkeit, mit der Sensirion die Kommunikation empfiehlt.
  if (airSensor.begin() == false) {fehlermeldung("Kein      CO2-Sensor");} //CO2-Sensor aktivieren und Fehlermeldung, falls kein CO2-Sensor erkannt
  
  iaqSensor.begin(BME680_I2C_ADDR_PRIMARY, Wire); //Aktivierung des Umweltsensors
  checkIaqSensorStatus(); //Kontrolle des Umweltsensors
  bsec_virtual_sensor_t sensorList[10] = { //Einrichtung der Messwerte des Umweltsensors
    BSEC_OUTPUT_RAW_TEMPERATURE,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_RAW_HUMIDITY,
    BSEC_OUTPUT_RAW_GAS,
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_STATIC_IAQ,
    BSEC_OUTPUT_CO2_EQUIVALENT,
    BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
  };
  iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
  checkIaqSensorStatus();
  
  tsl.begin(); //Licht-Sensor aktivieren
  veml.begin(); //UV-Sensor aktivieren

  //SD-Karte aktivieren, sofern vorhanden
  if (SD.begin(28)){ //Weshalb 28? Vielleicht die Stelle des SD-Bees auf der SenseBox? 
    //nur SD.begin(28) funktioniert auch, aber so kann es mit einer Fehlermeldung kombiniert werden.
    datei = SD.open("Daten.txt", FILE_WRITE);  //öffnet die DAtei oder legt sie neu an.
    datei.println("Aufzeichnung"); 
    datei.println("Zur grafischen Auswertung in Numbers . durch , ersetzen");
    datei.println("Messung;ppm CO2;Temp in "+String(GRAD)+"C;Luftfeuchtigkeit in "+String(PROZENT)+";Luftdruck in hPa;IAQ;CO2-Aequivalent;VOC;Beleuchtung in Lux;UV in  µW/m^2");
    datei.close(); //schließt die Datei
    datenaufzeichnung = true;
  } else {fehlermeldung("Keine     SD-Karte"); datenaufzeichnung = false;} //Fehlermeldung 
 

  start();//Regenbogen und Sensoren aufwärmen
  ampelanzeige(); //Ampel wird zuerst im Setup angeschaltet, weil sie sonst erst nach einer Minute aktiviert würde
} //END void setup





void loop() {
  
  time_start = millis();
    
  if ((datenaufzeichnung)&&(time_start > (time_actualdaten+60000))) {  //daten werden ggf. aufgezeichnet, sofern 1 min vergangen und SD-Karte eingelegt.
    datenAufzeichnen(); 
    time_actualdaten = millis();
    }  //end if datenaufzeichung 

  if (time_start > (time_actualSensorenAbfrage+10000)) {//daten werden alle 10 s abgefragt
    sensorenAbfragen();
    ampelanzeige();
    time_actualSensorenAbfrage = millis();
  }//END if SensorenAbfragen

  if (time_start > (time_actualwertanzeige+2000)) { //alle 2 s wird ein neuer Wert angezeigt
    werteAnzeigen(werteAnzeigenZaehler);
    werteAnzeigenZaehler++;
    if (werteAnzeigenZaehler > 2) {werteAnzeigenZaehler = 0;}
    time_actualwertanzeige = millis();
    }//end if Wertanzeige
    
  //if (time_start > (time_actualLedAnzeige+500)) { //alle 0,5 s blinkt die LED
   // time_actualLedAnzeige = millis();
    ledAktivitaetenAnzeige(); //dauert 1 s
  //}//END if LEDAktivitätenAnzeige
  
} //END void loop
1 Like

Ich habe noch weitere Beobachtungen gemacht, die relevant sein könnten. Ich habe auch den BME680-Sensor verbaut. Er zeigte aber immer nur die gleichen Werte für IAQ und VOC an. Es stellte sich heraus, dass er auch nach fünf Tagen noch nicht mit der Selbstkalibrierung angefangen hatte, da er noch zu wenige Messwerte hatte. Ich fragte ihn immer zusammen mit dem SCD30-Sensor alle paar Sekunden ab. Also stellte ich mein Programm so um, dass die Sensoren bei jedem Schleifendurchlauf abgefragt wurden und nicht mehr nur in Sekunden-Intervallen. Jetzt wurde der BME680 zwar kalibriert, aber dafür stürzte das Programm schon nach Minuten bis wenigen Stunden ab. Also habe ich das Programm so umgestellt, dass der BME680-Sensor kontinuierlich, der SCD30-Sensor jedoch nur alle 30 Sekunden abgefragt wird. Weiterhin habe ich den Sensor auch so umprogrammiert, dass er nur noch alle 30 Sekunden einen neuen Messwert liefert. Der Befehl dazu lautet:

const int CO2ABFRAGEINTERVALL = 30;

…

airSensor.setMeasurementInterval(CO2ABFRAGEINTERVALL); //Change number of seconds between measurements: 2 to 1800 (30 minutes)

Danach lief das Programm wieder problemlos 90 Stunden, bis ich es selbst unterbrochen habe, um Anpassungen an der Darstellung in meinem LED-Ring zu programmieren.

Ich denke, dass damit der Fehler nicht behoben ist, aber man die Intervalle der Abstürze vermutlich verlängern kann.

auch ich habe nochmal ein bisschen weiter getestet — dabei kam mir noch ein verhalten „interessant“ vor: da ich die daten per Lora-Bee übertrage und mit einem eigenen Gateway auffange, in InfluxDB per node-red ablege und per grafana auswerte habe ich noch noch beobachtet, dass die daten folgendermassen aufgezeichnet werden:

über 5 bis 7 Minuten habe ich Messwerte, dann eine Minute Pause, manchmal 2, dann wieder messwerte — der Sensor lief jetzt gute 16h bis er sich heute mittag dann wohl aufgehängt hat.

1 Like

Hat schon irgendwer irgendeine Lösung für unser Problem gefunden - außer der hier:

https://www.amazon.de/exec/obidos/ASIN/B01F52E5GC/bitrep-21

Wünsche euch allen schöne Weihnachten + weiter negativ bleiben!

Ich bin von Blockly auf die Arduino IDE gewechselt und nutze als Basis diesen Sketch:


Leicht angepasst fragt der Sketch nun alle 2 s den CO2 Sensor ab, und auch nur, wenn dataAvailable true liefert.
Als Erweiterungen habe ich das Wire.SetClock(50000); drin.
Damit lief meine Box 60 h durch, bis ich sie manuell neu gestartet habe.
Zur Sicherheit habe noch einen Watchdog drin, der die Box nach 10 s neustarten würde, falls das Programm einfriert. Um das zu kontrollieren schreibe ich noch die Uptime ins Display. War aber bisher noch nicht nötig. Für den Watchdog nutze ich die SleepyDog Library.

So, und auch einer der 3 neuen Geräte ist eingefroren… Hardware: MCU (nicht Lite) CO2 Sensor, Display und RGB LED - wifi Bee

mit der Software von @swenp laufen beide Ampeln - scheinen aber zwischendrin wie oben beschrieben einen selbstständigen Neustart zu machen — die eine Ampel steht bei ~48h und die andere bei ~14 ohne das ich manuell Hand anlegen musste !!! diese Lösung von ihm finde ich prima - „muss“ nur noch WLAN /LORA eingebaut werden … .! TOP

Nachdem auch der Jahreswechsel keine Besserung des Ampel-Verhaltens gebracht hat - gibt es denn keine Aussage von „offizieller“ Seite zu diesem Problem? Tendiere dazu, das Teil zurückzusenden.
Für mich ist die Lösung von @swenp nur eine Notlösung - funktionieren sollte die Ampel schon selber.

hier nochmal eine Zwischenstand vom aktuellen Verhalten:
Bausatz 01: mit LoRa Bee: läuft Stabil OHNE LORA mit Software von @swenp
Bausatz 02: mit WLAN Bee: läuft Stabil OHNE WLAN mit Software von @swenp
Bausatz 03: mit WLAN Bee: läuft mit WLAN aber unregelmässiger freeze
Bausatz 04: mti WLAN Bee: läuft seit heute, nachdem 1 Kabel umgebaut wurde (verkehrte/verdrehte Adern) … wir haben am Stecker schwarz und rot getauscht (nun 1:1 zwischen beiden Steckern) und damit läuft der Bausatz — aktuell mit WLAN Bee aktiviert — mal schauen wann der freeze kommt …

/ Nachtrag: der freeze bei 04 kam nun zum ersten mal nach 44 Minuten … GGGGRRRRRR

Danke @mario ! — also Achtung: es sind fehlerhaft konfektionierte Kabel den Bausätzen beigelegt !

Hallo @swenp wäre es möglich deinen Teil-Code für Watchdog zur Verfügung stellen…? Und evtl. für die Uptime. Danke.

Klar, kein Problem:

Für den Watchdog:

#include <Adafruit_SleepyDog.h>

// Am Ende von Setup():
Watchdog.enable(10000);

// innerhalb der loop():
Watchdog.reset();

Für die Uptime:

// Deklaration:
unsigned long myTime;
// In loop():
myTime = millis() / 1000 / 60; // in minutes
display.println(„Uptime: " + String(myTime / 60) + " h " + String(myTime % 60) + " min“);

Ein Überlauf findet erst nach 50 Tagen statt, das stört mich daher nicht.

2 Likes

ich habe jetzt mal ein ArduinoIDE Sketch gebaut mit Lora-Unterstützung, Anzeige von Temperatur und Luftfeuchte sowie die Uptime (von @swenp ) auch der Watchdog wie oben beschrieben ist integriert – die Ampel geht heute Abend in den Dauertest — mal schauen was passiert.

https://github.com/jensileinchen/senseBox-CO2-Lora-TTN-Version

unter \SRC\ liegt die INO-Datei von heute (10.01.2021)
Achtung - die Datei ist anonymisiert, Ihr müsst natürlich Eure TTN Credentials noch eintragen !

// edit zwei Stunden später: … einige Male hat er mittlerweile neu gestartet — das einzige was daran nun positiv zu sehen ist das automatisch der Loop neu gestartet wird …

Hi @swenp, super. Danke.

@ all:
Wer hat sich von euch an den Support gewendet und eine Antwort bekommen? Bei mir herrscht Schweigen auf meine Anfrage :sleeping:
Falls jemand die Ampel bereits zurückgeschickt hat - welche Erfahrungen habt ihr mit der Rückerstattung/Ersatz gemacht?

Ich gehe davon aus, das hier keine Lösung mehr gefunden wird und werde die Ampel dann zurücksenden!

Danke euch allen für die Hilfe!

Moin - tja - @Stephan – ich habe ein Ticket - also eine offizielle Support-Anfrage wurde bereits im November gestellt – ich habe ja sogar danach noch 3 weitere Ampeln gekauft, die aber alle das gleiche Problem haben – mit dem Watchdog kann man zwar die Dinger benutzen, aber leider immer mit dem unguten Gefühl, das ja nur ein Workaround geschaffen wurde, anstatt das Problem und die Ursache zu beheben … Mir macht Testen und Fehlersuche Spaß, aber nicht, wenn Produkte offenbar beim Kunden zuende entwickelt werden …

@mario @Felix @Jan und an das Team — tut sich hier noch was ?? sonst bekommt ihr meine 4 Geräte nämlich auch zurück – ( 1x LoRa und 3x Wlan Ampel …) – Herzlichen Gruß

1 Like

Moinsen, deine Nachricht ist tatsächlich bei uns im Helpdesk hängen geblieben @Stephan! Wir nehmen uns viel Zeit für den Support und sind auch selber auf Fehlersuche. Bitte nutzt für den Moment noch den Workaround von @swenp (an dieser Stelle nochmal besten dank für die Beteiligung :muscle:). Ich verlinke die Beispiele noch bei uns in den sensebox-examples.

Moin @jensileinchen, der Fehler gibt uns nach wie vor Rätsel auf da er nur bei wenigen Bausätzen auftritt und daher schwer einzugrenzen ist. Layout von Sensor, MCU und die Stromversorgung wurden geprüft und sind in Ordnung. Software testen wir weiterhin und aktuell warte ich auf Rückmeldung von Sensirion. Umtausch der Hardware ist kein Problem, ich würd mich aber freuen wenn wir eure Fälle noch gelöst bekommen!

ganz anderer Ansatz seit heute morgen: auf dem betroffenen Board läuft nun mal eine sensebox.home Software, mit CO2Sensor, Dislay und Lora-Bee — mal schauen wie lange … !

// edit 10.02.2021
sehr spannend — das board läuft noch und sendet brav per lora messwerte ---- es liegt also eher kein hardware defekt vor sondern eher wohl ein problem mit den verrwendeten libaries ?! — testet sonst noch jemand die dinger ausser mir ?

1 Like