New post about same problem: I2C does weird stuff

Hi all,

I have a weird issue. I posted before but now it looks different; more info is available. I would once again very much like your help, if anyone has any ideas. Intermittent problems are always tough.

I have a Nano-based device that has an RTC, a temp/humidity/pressure sensor, and a Sensirion SCD90 CO2 sensor, all connected via I2C. I have used most of these (and I2C, and the nano) many times before.

This one works great but it does something weird: It would hang randomly every few days - just stop. It was not a power problem, and all other obvious things have, I think, been excluded.

So I added a watchdog timer, to reboot it if it hangs. That worked. But that was not really a solution, just a fix. So as the next step in my troubleshooting I added a timeout to the Wire (I2C) use:

Wire.setWireTimeout(3000 /* us /, false / reset_on_timeout */);

But now it does REALLY weird stuff. Instead of hanging up, every day it randomly butchers time, temperature and air pressure. Almost every display value is random and entirely nonsensical: just now I got home to see 93ºC, barometric pressure of 1980 hPa, and real time at 49:79. I think CO2 level was wrong too... basically all I2C values were rubbish. And the system did not self-correct. The yellow LED for over-and under-temperature and humidity worked fine, as did the pressure change indicator ("+934"), so it was definitely the actual values, not the displays that were wrong.

ANY idea what is happening here? I have never seen this kind of problem (and I have made quite a few I2C-connected sensor-based devices).

Code below. And attached, the manual (always nice to see what the box looks like etc).
EDIT: To be clear, this manual contains description, background, circuit diagram, photos of construction, etc.

CO2meter-box.pdf (7.8 MB)

The circuit diagram has the wrong name for the sensor on the very left; it is in fact the Sensirion CO2 sensor).

/*-------------------------------------------------------------------------------
 New Environment Monitor
 --------------------------------------------------------------------------------
 - Three 7-segment LED displays
 - GY68 Air pressure/temp meter 
 - DS3231 Real-Time Clock
 - SCD30  True CO2 (&c) meter
 Code by Michael Willems  -  michael@willems.ca
 Version 1.03
 2023-03-11
 Can be done later:
  - PERHAPS AVOID DELAY AFTER SWITCH CHANGES?
---------------------------------------------------------------------------------*/


// -----------------------------------------------------------------------------------------
// INCLUDES AND DECLARATIONS:
// -----------------------------------------------------------------------------------------
#include <Wire.h>                 // for I2C communication. A4=SDA; A5=SCL on Nano
#include <DS3231.h>               // for real-time clock
#include <Adafruit_BMP085.h>      // for air pressure/temp meter
#include <Adafruit_SCD30.h>       // for CO2 detector
#include <avr/wdt.h>              // for watchdog timer xxx BUGGY ON CLONE NANO
#include <math.h>                 // for the round() function...
//
Adafruit_BMP085 bmp;              // Air P&T
Adafruit_SCD30  scd30;            // CO2 (etc) detector
//
int temperature;
int humidity;
int barometer;
int CO2level;
int oldtemperature = 0;
int oldhumidity = 0;
int oldbarometer = 0;
int oldCO2level = 0;
//
/* Code for TM1637 4 digit 7 segment display: */
#include <TM1637Display.h>
#define CLK 2  // Define the connections pins for display 1
#define DIO 3
#define CLK2 4 // Define the connections pins for display 2
#define DIO2 5
#define CLK3 6 // Define the connections pins for display 3
#define DIO3 7
//
#define buzzer 13
//
/////////////////////////////
// DEVICE TUNING SETTINGS  //
// ADJUST THESE AS NEEDED: //
/////////////////////////////
#define CO2marginal 700       // when green LED starts flashing
#define CO2yellow 1000        // when yellow LED lights
#define CO2red 1500           // when red LED lights
#define CO2alarm 3000         // when red LED flashes
#define CO2urgentAlarm 5000   // when beeper briefly beeps once a second
#define tempOffset -2         // offset to make thermometer accurate
#define humOffset 3           // offset to make humidity accurate
#define QFEoffset 5           // offset to make air pressure accurate
#define mintemp 19            // below this, top right-side yellow LED lights 
#define maxtemp 25            // above this, top right-side yellow LED lights 
#define minhum 30             // below this, bottom right-side yellow LED lights 
#define maxhum 50             // above this, bottom right-side yellow LED lights 
////////////////////////////
// 
// Create three display objects of type TM1637Display:
TM1637Display display = TM1637Display(CLK, DIO);
TM1637Display display2 = TM1637Display(CLK2, DIO2);
TM1637Display display3 = TM1637Display(CLK3, DIO3);
//
// Now create arrays and name them:
// Create array that turns all segments on:
const uint8_t data[] = {0xff, 0xff, 0xff, 0xff};
// Create array that turns all segments off:
const uint8_t blank[] = {0x00, 0x00, 0x00, 0x00};
// Create other symbols:
const uint8_t done[] = {
  SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,           // d
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,   // O
  SEG_C | SEG_E | SEG_G,                           // n
  SEG_A | SEG_D | SEG_E | SEG_F | SEG_G            // E
};
const uint8_t hPA[] = {
  0x00,                                            // 
  SEG_C | SEG_E | SEG_F | SEG_G,                   // h
  SEG_A | SEG_B | SEG_E | SEG_F | SEG_G,           // P
  SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G    // A
};
const uint8_t nd[] = {
  SEG_C | SEG_E | SEG_G,                           // n
  SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,           // d
  0x00,                                            // 
  0x00                                             // 
};
const uint8_t up[] = {
  SEG_C | SEG_D | SEG_E,                           // u
  SEG_A | SEG_B | SEG_E | SEG_F | SEG_G,           // P
  0x00,                                            // 
  0x00                                             // 
};
const uint8_t dn[] = {
  SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,           // d
  SEG_C | SEG_E | SEG_G,                           // n
  0x00,                                            // 
  0x00                                             // 
};
const uint8_t rh[] = {
  0x00,                                            // 
  0x00,                                            // 
  SEG_E | SEG_G,                                   // r
  SEG_C | SEG_E | SEG_F | SEG_G                    // h
};
const uint8_t dC[] = {
  0x00,                                            // 
  0x00,                                            // 
  SEG_A | SEG_B | SEG_F | SEG_G,                   // [deg]
  SEG_A | SEG_D | SEG_E | SEG_F                    // C
};
const uint8_t CO2[] = {
  0x00,                                            // 
  SEG_A | SEG_D | SEG_E | SEG_F,                   // C
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,   // O 
  SEG_A | SEG_B | SEG_D | SEG_E | SEG_G,           // 2 
};
//
// Misc variables etc:
unsigned long counter;            // for flashing on-board LED 13 with 1s frequency
unsigned long fastcounter;        // for flashing green with fast frequency
unsigned long measurementcounter; // for 3s interval between taking measurements
unsigned long halfhourcounter;    // for the "once every half hour" stuff
unsigned long cyclecounter = 0;   // to see how often the loop does its thing (DEBUG, UNCOMMENT PRINTLN)
//int onboardled = 9;               // for optional heartbeat LED
int greenled = A0;
int yellowled = A1;
int redled = A2;
int sideled1 = A3;
int sideled2 = 8;
int QFEswitch = 10;
int pushbutton = 11;              // for clock set 
int nightswitch = 12;             // for displaying time INSTEAD OF OTHER DATA AT NIGHT
boolean switchstate = false;
boolean oldswitchstate = true;
boolean switch2state = false;
boolean oldswitch2state = false;
boolean ledstate = false;         // for clock set 
long buttonTimer = 0;             // to time how long we have pressed the button
long longPressTime = 1000;        // how long to push button before clock enters SET mode?
long exitSetupTime = 3000;        // after clock set, how long until we exit set mode?
boolean pushbuttonActive = false;
boolean longPressActive = false;
long baromem[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} ;   // for tracking 8h barometer trend
float barodiff1h = 0;
float barodiff3h = 0;
long uptime = 0;

//clock stuff:
DS3231 Clock;
boolean Century=false;
boolean h12;
boolean PM;
byte ADay, AHour, AMinute, ASecond, ABits;
boolean ADy, A12h, Apm;
char *dayString[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
int second,minute,hour,date,month,year,val,dow,temp3; 


// -----------------------------------------------------------------------------------------
// THE SETUP (RUNS ONCE):
// -----------------------------------------------------------------------------------------
void setup() {
//  pinMode(onboardled, OUTPUT);  //the optional heartbeat LED
  pinMode(greenled, OUTPUT);
  pinMode(yellowled, OUTPUT);
  pinMode(redled, OUTPUT);
  pinMode(sideled1, OUTPUT);
  pinMode(sideled2, OUTPUT);
  pinMode(pushbutton, INPUT_PULLUP);
  pinMode(nightswitch, INPUT_PULLUP);
  pinMode(QFEswitch, INPUT_PULLUP);
  Serial.begin(9600);
  Wire.begin(); // Initiate the Wire library
  Wire.setWireTimeout(3000 /* us */, false /* reset_on_timeout */);    
  delay(10);
  counter = millis();
  measurementcounter = millis();
  halfhourcounter = millis();
  cyclecounter = 0;  //for testing loops/second
  //
  // now set up, and start, the air pressure detector:
  if (!bmp.begin()) {
	  Serial.println(F("Could not find a BMP AIR PRESSURE sensor, check wiring!"));
    digitalWrite (yellowled, HIGH);
	  while (1) {}   // stop here, since sensor not found
  }
  Serial.println(F("BMP Sensor Found!"));
  //
  // Now initialize SCD30:
  if (!scd30.begin()) {
    Serial.println(F("Failed to find SCD30 chip"));
    digitalWrite (redled, HIGH);
    while (1) { delay(10); }   // stop here, since sensor not found
  }
  Serial.println(F("SCD30 Found!"));
  //
  // Set the brightness:
  display.setBrightness(6);
  display2.setBrightness(6);
  display3.setBrightness(6);
  // All segments on:
  display.setSegments(data);
  display2.setSegments(data);
  display3.setSegments(data);
  digitalWrite (greenled, HIGH);
  digitalWrite (yellowled, HIGH);
  digitalWrite (redled, HIGH);
  digitalWrite (sideled1, HIGH);
  digitalWrite (sideled2, HIGH);
  // clear and display the units (CO2, degrees, rel.hum.)  
  display.clear();
  display2.clear();
  display3.clear();
  display.setSegments(CO2);
  display2.setSegments(dC);
  display3.setSegments(rh);
  delay(2000);
  display.clear();
  display2.clear();
  display3.clear();
  // now we set the barometer memories all the same to start:
    baromem[0] = (bmp.readPressure())+QFEoffset;
    for (int mem = 1; mem<16; mem++){
    baromem[mem] = (baromem[0]);
  }
  delay(500);
  digitalWrite (greenled, LOW);
  digitalWrite (yellowled, LOW);
  digitalWrite (redled, LOW);
  digitalWrite (sideled1, LOW);
  digitalWrite (sideled2, LOW);
}

// -----------------------------------------------------------------------------------------
// THE LOOP:
// -----------------------------------------------------------------------------------------
void loop() {
  //start the safety watchdog:
  wdt_enable(WDTO_4S); //Enable WDT with a timeout of 4 seconds (for genuine arduino nano)
  //
  // Now we do the once-per-second stuff:
  if ((millis() - counter) > 1000) {   
    counter = millis();
    //    digitalWrite (onboardled,!(digitalRead(onboardled)));   //the on-board heartbeat LED
    if (CO2level > CO2alarm) {         // high CO2 beep alert
      tone(buzzer, 1000, 10);
    }
    // check how long we have been up (8 hours is the maximum):
    uptime = (int(millis()/3600000));
    if (uptime > 8) {
      uptime = 8;
    }
//    Serial.println(cyclecounter);  //debugging: to see how many times we do the loop. each second
    cyclecounter = 0;
  } //ok, end of once-per-second stuff.  
  
  // Now we do the QUICK FLASH stuff:
  if ((millis() - fastcounter) > 500) {   
    fastcounter = millis();
    if ((CO2level > CO2marginal) && (CO2level < CO2yellow))  {    // green fast flash
      digitalWrite (greenled,!(digitalRead(greenled)));           // the green LED
    }
    if (CO2level > CO2urgentAlarm)  {                             // RED fast flash
      digitalWrite (redled,!(digitalRead(redled)));               // the red LED
    }
  } //ok, end of fast flash stuff.
  //
  // now the once every 3 seconds stuff (measurement interval):
  //
  if ((millis() - measurementcounter) > 3000) {   
    measurementcounter = millis();
    temperature = ((bmp.readTemperature()) + (tempOffset));   //read temp from the chip & adjust
    barometer = ((bmp.readPressure()/100) + (QFEoffset));     //read pressure from the chip & adjust
    if (scd30.dataReady()){
      if (!scd30.read()){ Serial.println(F("Error reading sensor data")); return; }
      humidity = (scd30.relative_humidity + humOffset);
      CO2level = int(scd30.CO2);
      if (CO2level > 9999) { CO2level = 9999; }
    } else { Serial.println(F("No data")); }
    // now display the values (+re-display if they have changed):
    if  (digitalRead(nightswitch) == LOW) {     // switch active, so go to night display
      switchstate = true;
      DisplayNightValues();
    }
    else {                                      // switch not active, so normal day display please
      switchstate = false;
      DisplayValues();
    }
  } // ok, end of the once every three second stuff.
  //
  // Now the once per half hour stuff:
  // 
  if (millis() - halfhourcounter > 1800000) { 
    //
    // every half hour, update the 16 last half-hourly readings:
    halfhourcounter = millis();
    for (int i = 15; i>0; i--) {
      baromem[i] = baromem[i-1];
    }
    baromem[0] = (bmp.readPressure());
    //
    // now here we must calculate if it's dropping or climbing in 1, 3 and 8 hours, and how fast:
    barodiff1h = ((baromem[0] - baromem[2]) / 100);
    barodiff3h = ((baromem[0] - baromem[6]) / 100);
    // (more logic here later)

  /* this section for later use:
    //
    // Display "rising now" or "falling now" line:
    //
      if (barodiff1h > 50) {
      display3.setSegments(up);
    } 
    else if (barodiff1h > 50) {
      display3.setSegments(dn);      
    }
    //
    // Display the 3 hour trend now:
    //     
    if (barodiff3h > 600) {
      //bad high
    } 
    else if (barodiff3h > 350) {
      //high
    }
    else if (barodiff3h > 150) {
      //rising
    }
    else if (barodiff3h < -600) {
      //bad low, >600
    }
    else if (barodiff3h < -350) {
      //low, 350-600
    }
    else if (barodiff3h < -150) {
      //falling, 150-350
    }
    else {
      //must be green led, 0-150
    }    
  */
  }  //end of once-per-half-hour stuff

  //
  // Now, any code that has to continously run (not just once a second):
  //

  //the "long button press detect" stuff...  
  if (digitalRead(pushbutton) == LOW) {
    if (pushbuttonActive == false) {
      pushbuttonActive = true;
      buttonTimer = millis();
    }
    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
      longPressActive = true;
      ledstate = !ledstate;
//        digitalWrite (blueled, ledstate);
      setclock();                 //the set clock mode
      }
    } else {
      if (pushbuttonActive == true) {
        if (longPressActive == true) {
          longPressActive = false;
        }
      } 
      pushbuttonActive = false;
    }

  cyclecounter = cyclecounter +1;  //to facilitate counting the cycles/second for testing
  //reset watchdog:
  wdt_reset();
} //end of loop


// -----------------------------------------------------------------------------------------
// THE FUNCTIONS:
// -----------------------------------------------------------------------------------------

//------------------------------
// Read and display time:
//------------------------------
void ReadDS3231() {
  second=Clock.getSecond();
  minute=Clock.getMinute();
  hour=Clock.getHour(h12,PM);
  date=Clock.getDate();
  month=Clock.getMonth(Century);
  year=Clock.getYear();
  temp3=Clock.getTemperature();
  dow=Clock.getDoW();
  display.showNumberDec(minute, true, 2, 2);
  display.showNumberDecEx(hour, 0b01000000, true, 2, 0);  
}


// -----------------------------------------
// Display all the (daytime) non-time stuff:
// -----------------------------------------
void DisplayValues() {
  //  set state of QFEswitch:
  if  (digitalRead(QFEswitch) == LOW) {     // switch active, so prepare for barometer display
    switch2state = true;
  }
  else {                                    // switch not active, so normal temp display please
    switch2state = false;
  }
  if ((switch2state != oldswitch2state) && (switch2state == false)) {
    display2.setSegments(dC);
    display2.showNumberDec (temperature, false, 2, 0);
    display3.setSegments(rh);
    display3.showNumberDec (humidity, false, 2, 0);
    oldswitch2state = switch2state;     
  }
  else if ((switch2state != oldswitch2state) && (switch2state == true)) {
    display2.showNumberDec (barometer, false, 4, 0);
    if (uptime > 2) {
      display3.showNumberDec (round(barodiff3h), false, 4, 0);
    }
    else {
      //display that there is not yet enough data (and how many hours so far; needs to be >2)
      display3.setSegments(nd);
      display3.showNumberDec (uptime, false, 2, 2);
    }
    oldswitch2state = switch2state;     
  }
  if (switchstate != oldswitchstate) { 
    // The switch has just been changed (startup, or back to off, else we would not get here)
    // So, do a full new display.
    //first, a hello beep:   
    tone(buzzer, 523, 100);
    delay(100);
    tone(buzzer, 587, 100);
    delay(100);
    tone(buzzer, 659, 100);
    delay(100);
    // now the displays on
    display.showNumberDec (CO2level, false, 4, 0);
    if (switch2state == true) {  
      display2.showNumberDec (barometer, false, 4, 0);
      if (uptime > 2) {
        display3.showNumberDec (round(barodiff3h), false, 4, 0);
      }
      else {
        //display that there is not yet enough data (and how many hours so far; needs to be >2)
        display3.setSegments(nd);
        display3.showNumberDec (uptime, false, 2, 2);
      }
    }
    else {
      display2.setSegments(dC);                          
      display2.showNumberDec (temperature, false, 2, 0);
      display3.setSegments(rh);
      display3.showNumberDec (humidity, false, 2, 0);
    }
    display.showNumberDec (CO2level, false, 4, 0);
    //Now the CO2 status LEDs:
    if (CO2level > CO2red) {
      digitalWrite (redled, HIGH);
      digitalWrite (yellowled, LOW);
      digitalWrite (greenled, LOW);
    } 
    else if (CO2level > CO2yellow) {
      digitalWrite (redled, LOW);
      digitalWrite (yellowled, HIGH);
      digitalWrite (greenled, LOW);
    } else {
      digitalWrite (redled, LOW);
      digitalWrite (yellowled, LOW);
      digitalWrite (greenled, HIGH);
    }
  oldCO2level = CO2level;
  oldswitchstate = switchstate;
  }
  //
  // and now, since switch was not changed, we can just display if there's a change:
  if (switch2state == false) {
    if (temperature != oldtemperature) {
    display2.setSegments(dC);
    display2.showNumberDec (temperature, false, 2, 0);
    oldtemperature = temperature;     
    }
    if (humidity != oldhumidity) {
     display3.setSegments(rh);
     display3.showNumberDec (humidity, false, 2, 0);
     oldhumidity = humidity;
   }
  }
  else {
    if (barometer != oldbarometer) {
      display2.showNumberDec (barometer, false, 4, 0);
      oldbarometer = barometer;
      if (uptime > 2) {
        display3.showNumberDec (round(barodiff3h), false, 4, 0);
      }
      else {
      //display that there is not yet enough data (and how many hours so far; needs to be >2)
        display3.setSegments(nd);
        display3.showNumberDec (uptime, false, 2, 2);
      }
    }
  }

  if (CO2level != oldCO2level) {
    display.showNumberDec (CO2level, false, 4, 0);
    if (CO2level > CO2red) {
      digitalWrite (redled, HIGH);
      digitalWrite (yellowled, LOW);
      digitalWrite (greenled, LOW);
    } else if (CO2level > CO2yellow) {
      digitalWrite (redled, LOW);
      digitalWrite (yellowled, HIGH);
      digitalWrite (greenled, LOW);
    } else {
      digitalWrite (redled, LOW);
      digitalWrite (yellowled, LOW);
      digitalWrite (greenled, HIGH);
    }
    oldCO2level = CO2level;  
  }
  if ((temperature < mintemp) || (temperature > maxtemp)) {
    digitalWrite (sideled1, HIGH);
  } else {
    digitalWrite (sideled1, LOW);    
  }
  if ((humidity < minhum) || (humidity > maxhum)) {
    digitalWrite (sideled2, HIGH);
  } else {
    digitalWrite (sideled2, LOW);
  }
}

// -------------------------------
// Display all the NIGHT stuff:
// -------------------------------
void DisplayNightValues() {
  if (switchstate != oldswitchstate) {
    // first, a good night beep:
    tone(buzzer, 659, 100);
    delay(100);
    tone(buzzer, 587, 100);
    delay(100);
    tone(buzzer, 523, 100);
    delay(100);
  }   
//    display.clear();  // not neeeded; overwriting also clears it
    display2.clear();
    display3.clear();
    ReadDS3231();         
    if (temperature != oldtemperature) {
      oldtemperature = temperature;
    }
    if (barometer != oldbarometer) {
      oldbarometer = barometer;
    }
    if (humidity != oldhumidity) {
      oldhumidity = humidity;
    }
    if (CO2level != oldCO2level) {
      if (CO2level > CO2red) {
        digitalWrite (redled, HIGH);
        digitalWrite (yellowled, LOW);
        digitalWrite (greenled, LOW);
      } else if (CO2level > CO2yellow) {
        digitalWrite (redled, LOW);
        digitalWrite (yellowled, HIGH);
        digitalWrite (greenled, LOW);
      } else {
        digitalWrite (redled, LOW);
        digitalWrite (yellowled, LOW);
        digitalWrite (greenled, HIGH);
      }
      oldCO2level = CO2level;
    }
    oldswitchstate = switchstate;
    digitalWrite (sideled1, LOW);    
    digitalWrite (sideled2, LOW);    
}


//------------------------------
// SET THE CLOCK:
//------------------------------
void setclock() {
  long settimer;
  //turn off other displays:
  display2.setBrightness(1, false);
  display3.setBrightness(1, false);  
  display2.setSegments(dC);
  display2.showNumberDec (temperature, false, 2, 0);
//  display2.showNumberDec (barometer, false, 4, 0);
  display3.setSegments(rh);
  // read the clock:
  ReadDS3231();
  second=Clock.getSecond();
  minute=Clock.getMinute();
  hour=Clock.getHour(h12,PM);
  date=Clock.getDate();
  month=Clock.getMonth(Century);
  year=Clock.getYear();
  temp3=Clock.getTemperature();
  dow=Clock.getDoW();
  //
  //-------------
  //SET THE HOUR:
  //-------------
  wdt_reset();
  display.clear();
  settimer=millis();
  pushbuttonActive = false;  
  while (millis() - settimer < exitSetupTime) {  // keep setting, unless no button pressed for 4s
  display.showNumberDec(hour, true, 2, 0);
    if (digitalRead(pushbutton) == LOW) {
      settimer=millis();
      if (pushbuttonActive == false) {
        pushbuttonActive = true;
        hour++;
        if (hour>23){
          hour=0;
        }
        Clock.setHour(hour);
        display.showNumberDec(hour, true, 2, 0);
        wdt_reset();
      }
      else{
        pushbuttonActive == false;
      }
   }
   else {
     if (pushbuttonActive = true) {
       pushbuttonActive = false;
     }
   }
  }
  //
  //---------------
  //SET THE MINUTE:
  //---------------
  wdt_reset();
  display.clear();
  settimer=millis();
  pushbuttonActive = false;  
  while (millis() - settimer < exitSetupTime) {  // here we loop to set something
  display.showNumberDec(minute, true, 2, 2);
    if (digitalRead(pushbutton) == LOW) {
      settimer=millis();
      if (pushbuttonActive == false) {
        pushbuttonActive = true;
        minute++;
        if (minute>59){
          minute=0;
        }
        Clock.setMinute(minute);
        display.showNumberDec(minute, true, 2, 2);
        wdt_reset();
      }
      else{
        pushbuttonActive == false;
      }
   }
   else {
     if (pushbuttonActive = true) {
       pushbuttonActive = false;
     }
   }
  }
  // now we timed out and it's back to displaying the time:
  display.setBrightness(6, true);
  display2.setBrightness(6, true);
  display3.setBrightness(6, true);  
  display2.setSegments(dC);
  display2.showNumberDec (temperature, false, 2, 0);
//  display2.showNumberDec (barometer, false, 4, 0);
  display3.setSegments(rh);
  display3.showNumberDec (humidity, false, 2, 0);
  //
  // housekeeping the button state etc:
  longPressActive = false;
  ledstate = !ledstate;
//  digitalWrite (blueled, ledstate);
}

If it's really just more information, but the same project, this should be attached to the original thread.

Sorry - I wasn't sure how to do that, since a lot has changed and the code (embedded in the post) has changed also: it felt like a new issue to me. Apologies.

It's not for me to decide - just a heads up.

1 Like

(nice work)

What are the indications of "Nano project hangs" to compare it with the new, "weird stuff." (Does serial monitor show no progress then and weird progress now, or do debug statements hang/get weird? Are switches unresponsive, too? Nano TX/RX LED abnormally solid? et c.)

Can you force it to unhang? (if a CO2 meter gets an abundance of my stale breath, will it go berzerk?)

Is your I2C bus configured like this Adafruit description or this Texas Instruments application shows? (in short, pullup resistors on both I2C lines high). When I worked with RS232 things, I needed one ground reference from MODEM to MUX to DTE or my comms went badly.

1 Like

Good questions.

"Hangs" means it does not respond to anything at all. Switch changes, input changes: nothing happens, no change of anything at all. It is truly stopped. No way to unhang it, whatever I do.

When I add the watchdog timer, it reboots when that happens (same frequency: roughly every day or two).

When I add the WIRE timeout, then instead of hanging or rebooting, the displays all display random stuff, but the unit still responds to switch changes, so it's not hanging.

The inputs have the internal pull-up resistor activated.

It looks like a hardware problem. Defect modules, cables, PSU...

I'd start to check operation of each module first, with the unchanged code, then add modules until the failure reoccurs. Also check the state of the SCL and SDA lines when the controller stops, e.g. in the watchdog.

1 Like

Yes. As you see, the wires are soldered in place; I test all connections and I test that there are no shorts between tracks, etc. But I'll have to start eliminating things.

Odd that it happens every day or so. Wouldn't I expect a hardware failure to be less intermittent like that?

The only new module is the CO2 detector. The rest of them I have used in many other projects. But if one of them does weird things to the I2C bus, yes, that could certainly cause trouble... and to me, with my unexperienced eye, this looks like trouble on the I2C bus. Does that sound right?

Have you waited days to see if it clears?
Does it crash at a multiple of 16*1800000 milliseconds (8 hours) and maybe you don't see two of the crashes?
Can you track the exact hours between reset and lockup (serial prints)?
Does a timer (long settimer?) or reading venture outside its max value?
Are your calculations clear of zeroes (especially zero denominator)?
Does your CO2 sensor self-reset/re-calibrate (can CO2 sensors get a "stuffy nose")?
Does your CO2 power requirement surge when it gets a lot of CO2 or too hot or too cold?
Does your physical arrival/departure coincide with the crash? (power surge at home from auto HVAC)?
Do you have a dog? A cat?!
Don't just think "code" or "hardware" - also think about everything that can interact with the box. Dare I say... Think outside the Box. =00= (sunglasses)

Good questions. I think I covered most of those, but nt all. So tomorrow I'll see what I missed, out of those.

M

How about a link to your previous posts, so instead of dragging info, etc. out of you we can catch up via the information presumably posted there? Things like a photo, schematic, description of project, etc. etc.
That's why we like to keep old topics going, so you, and we, don't rehash stuff. The volunteers here sort of expect that you'll give us easy access to the project particulars. We won't generally go digging through your past topics, looking for possibly relevant info.

FWIW, periodic issues like you describe I would attribute to one of several possibles:

  • device environment changes; for example, if it warms up in a box and crashes, it's overheating. Sun on an outdoor project can cause that.
  • electrical environment changes - did something external, or that the project controls, turn on or off and cause an electrical transient
  • cumulative code 'rot' - for example, using memory allocation sloppily, eventually causing the Arduino to run out of memory. Primary culprit would be using String for anything, but there are other malloc() culprits too
    Those are the things I'd look at, absent any real information about your project.

Hi, @michaelwillems

Can I suggest to add to your code a "heartbeat" output, using the D9 pin?
Just have the "Blink Without Delay" running as an extra function to flash a LED on pin 9, this will tell you if your code has locked up or not.
Or move the switch input from D13 to D9 and use the internal LED on the Nano.

Tom.. :smiley: :+1: :coffee: :australia:

Tom,
Yrs, I always add a heartbeat LED, except this time :frowning:
M

Oh, I repeated everything here. No need to look up the old post.

The real information is most certainly here: did you miss the PDF? That has everything: correct diagram, function description, photos, etc. Sorry, I should have been more clear about this.

To answer more: (@xfpd and @camsysca)

@xfpd

Have you waited days to see if it clears?
It does not

Does it crash at a multiple of 161800000 milliseconds (8 hours) and maybe you don't see two of the crashes? Can you track the exact hours between reset and lockup (serial prints)?*
It is not regular; it is intermittent and unpredictable

Does a timer (long settimer?) or reading venture outside its max value?
Are your calculations clear of zeroes (especially zero denominator)
I don't think this can be the case, but I am the first to admit I make mistakes. Note, though, that this code is well tested and other devices I create with basically the same kind of code run for years without locking up. Still: this is also what I was suspicious of. If you see anything in my code that might cause this, I am all ears.

Does your CO2 sensor self-reset/re-calibrate (can CO2 sensors get a "stuffy nose")?
No. But even if it did, would that lock up the device? I am not entirely clear of the WIRE library's details.

Does your CO2 power requirement surge when it gets a lot of CO2 or too hot or too cold?
No

Does your physical arrival/departure coincide with the crash? (power surge at home from auto HVAC)? Do you have a dog? A cat?!
I think I can rule out environmental factors. Certainly nothing easy.

@camsysca

device environment changes; for example, if it warms up in a box and crashes, it's overheating. Sun on an outdoor project can cause that.
I think I have ruled those out

electrical environment changes - did something external, or that the project controls, turn on or off and cause an electrical transient
I think that is also very unlikely. Three other boxes are in the same place, using the same power, etc. and they have been running for many months. Both power and environment are very stable here.

cumulative code 'rot' - for example, using memory allocation sloppily, eventually causing the Arduino to run out of memory. Primary culprit would be using String for anything, but there are other malloc() culprits too. Those are the things I'd look at, absent any real information about your project.
Entirely possible, and I hope someone will spot what I have not. I do think that is a little unlikely, because I use code that I have used in many other projects that are running well, and have been for years. But as said above, I am not above asking mistakes, obviously!
I hope you'll be willing to take a very quick look at the attached PDF and see if that gives you any ideas. Thanks!!

And thanks to all for reading and looking. This community is awesome.

As you see, this is exactly what I am doing! Good point, and I admit it is more accident than anything else that I am doing it correctly in all my code. Thanks for that link.

1 Like

To give you all some more background, here are the devices on my kitchen counter (I live alone!) all at once. Lightning detector on the left; then this device on top; environment quality monitor in the middle and predictive barometer at the bottom; then on the right a resilient timer switch. All nano-based and all using the same kind of code, and all running for many months on end. So whatever I am overlooking, it's not something simple.
Mechanical stuff always possible. When building, I always check all tracks for shorts, I shake the device to make sure it is shake-resistent, and I check each stage for reliability before adding the next stage/sensor. Obviously I overlooked something though...

I doubt it has anything to do with your problem, but...
if (pushbuttonActive = true) {

OOPS!
Otherwise, offhand I don't see anything in your code that is obviously incorrect, or deleterious; nothing at the user level that would cause memory corruption, for example. I think you may be in for a long session of adding print statements in various locations, sniffing for values that don't make sense.

1 Like

Would a duplicate, breadboard, c02box perform the same? (i am working on a simulation)

1 Like
if (pushbuttonActive = true) {

OOOPS! How did I miss that? Thanks for spotting that one... that's why it is always hard to debug your own code... it is indeed not the problem here but I will immediately fix that one... weirdly, it still all works, so I need to take a good look at that bit of logic... :slight_smile: