5

Arduino Pro mini 3.3V stucks few hours after startup. After repowered, it begins to stuck within a few seconds. If I wait long enough before turning it on it stucks little later for example now it lasted 45 seconds before stuck. Whenever it is stuck SCL pin is high and SDA pin is low so I suspect somethin about I2C is wrong. There is an 60 seconds countdown at start and the stuck usually happens there, you will see it is an extremely simple for loop. There are SHT31 sensor, 128x64 SH1106 driven OLED screen, DS3231 clock module, hc-sr501PIR sensor, LDR, Encoder and a button on the project. Only first three works with I2C and the code for those devices are very short, they are in "start(), updatescreen(), getTime(), getDew()" functions in the long script below so I hope it is not hard to diagnose the problem. I have done some experiments. I isolated SHT31 and OLED, both caused freeze even though all other devices were disconnected (I added blink code to their example test sketch and the blinking stopped after few seconds which means stuck). OLED screen works when it is connected with jumpers outside the PCB so I even suspected the PCB but it is so simple that it shouldn't cause problems. The weird thing is that this device worked perfectly for 2 days before it crushed for the first time.

Schematic: schematic

PCB (some mistakes were fixed later): enter image description here

Real images:

enter image description here enter image description here

Test script (the same freezing problem occurs but easier to read):

#include <U8g2lib.h>
#include <Wire.h>

U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
void setup() {
  u8g2.begin();
  u8g2.setFont(u8g2_font_ncenB14_tr);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  u8g2.firstPage();
  do {
    u8g2.setCursor(0, 20);
    u8g2.print(F("Hello World!"));
  } while (u8g2.nextPage());
  delay(1000);
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(1000);                      // wait for a second
  digitalWrite(LED_BUILTIN, LOW);
}

Test script 2 (this time for sht31, the same freezing problem occurs but easier to read):

#include <Wire.h>
#include "ClosedCube_SHT31D.h"

ClosedCube_SHT31D sht3xd;
void setup() {
  Wire.begin();
  sht3xd.begin(0x44);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  SHT31D result = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
}

Full Script:

#include "Wire.h"
#include "ClosedCube_SHT31D.h"
#include <DS3231.h>
#include "LowPower.h"
#include <EEPROM.h>
#include <U8g2lib.h>

#define PIR 2
#define ENCODER_BUTTON 3
#define ENC_A 4
#define ENC_B 5
#define FAN_MAIN_REL 8
#define FAN_SECOND_REL 9
#define BUZZER 10
#define LIGHT_REL 11
#define LDR_PWR 12
#define LDR_READ A0
#define FREE_RUN_BUTTON A2
#define CLOCK_INT A3

volatile bool PIR_flag, TIM1_flag, But_flag, FreeRun_flag, Settings_flag;
uint8_t Weather_flag;  // Weather_flag is special, it is set/reset by a regular function, 0: Dew is under threshold, 1: Dew is over DewThres but less than DewThresExtreme, 2: Dew is over DewThresExtreme
volatile int16_t encVal;

// Variables:
float Temp, Hum, Dew;
bool DewTrigEnabled;
uint8_t FanMotTrigEnabled;  // for fan relay
bool LightRelEnabled;       // for light relay
uint16_t waitDur;           // Setting 1
uint16_t Runtime;           // Setting 2
float DewThres;             // Setting 3
float DewThresExtreme;
uint16_t FreeRunDur;  // Setting 5
bool FreeRunStatus;
bool DNDenabled;
uint8_t DND_SH;  // DND start Hour
uint8_t DND_SM;  // DND start Minute
uint8_t DND_FH;  // DND finish Hour
uint8_t DND_FM;  // DND finish Minute
uint16_t counter;
uint8_t hour, minute;
bool LDRenabled;
uint16_t LDRThres;
bool screen_awake = true;  // screen starts "on"
uint8_t FanRelayStatus;
const uint8_t screenTime = 60;  // Screen On time without motion (seconds)
const float DewUSratio = 0.98;  // Determines the dew point which the FAN turns off if it was triggered by the Dew in the first place
const uint8_t phaseLim = 120;   // Determines LDR check rate if light is off but LDR is enabled, each phase corresponds to 5 seconds.

ClosedCube_SHT31D sht3xd;
RTClib myRTC_1;
DS3231 myRTC_2;
U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);

void swCounter(bool turn_on, uint32_t cnt = 0) {
  if (turn_on) {
    counter = cnt;
    TCNT1 = 0;                            // TIM1 counter value
    TCCR1B |= (1 << CS12) | (1 << CS10);  // Start timer by setting prescaler to 1024
  } else
    TCCR1B &= ~(1 << CS12) | ~(1 << CS10);  // Stop TIM1
}

void setup() {
  LoadPinsAndEEPROM();

  Wire.begin();

  // Temp&Hum Sensor:
  sht3xd.begin(0x44);

  // Clock Module:
  myRTC_2.setA2Time(
    0, 0, 0,
    0b01110000, false, false, false);
  myRTC_2.turnOnAlarm(2);

  myRTC_2.setA1Time(
    0, 0, 0xFF, 0,
    0b00001110, false, false, false);
  myRTC_2.turnOffAlarm(1);
  myRTC_2.checkIfAlarm(1);  // clear Alarm 1 flag

  sht3xd.heaterEnable();
  // OLED Screen:
  u8g2.begin();
  u8g2.setFont(u8g2_font_lubB12_tr);
  for (int i = 60; i > 0; i--) {
    u8g2.firstPage();
    do {
      u8g2.drawStr(6, 25, "by Prince");
      u8g2.setCursor(58, 55);
      u8g2.print(i);
      if (i == 30) sht3xd.heaterDisable();
    } while (u8g2.nextPage());
    delay(1000);
  }
  u8g2.setDrawColor(2);

  //TIM1 Configuration:
  TCCR1A = 0;
  TCCR1B = 0;
  TIMSK1 |= (1 << OCIE1A);  // Output Compare A Match Interrupt Enable
  OCR1A = 39061;            // Output Compare A
  TCCR1B |= (1 << WGM12);   // CRC (auto reset timer when it reaches OCR1A)
  TCNT1 = 0;                // TIM1 counter value

  // Attach interrupts (PinChanges and INTpin)
  attachInterrupt(digitalPinToInterrupt(PIR), ISR_PIR, RISING);
  attachInterrupt(digitalPinToInterrupt(ENCODER_BUTTON), ISR_ENCODER_BUTTON, FALLING);
  PCICR |= (1 << PCIE1);                      // A0-A6 group can create interrupt
  PCMSK1 |= (1 << PCINT10) | (1 << PCINT11);  // A2 and A3 pins can create interrupt
  PCMSK2 |= (1 << PCINT20) | (1 << PCINT21);  // D4 and D5 pins can create interrupt

  Beep(1, 100, 0);
  PIR_flag = false;
  But_flag = false;
}

void loop() {
  checkButtons();  // After execution it will return.
  if (!getDNDstatus()) {
    getDew();  // sets flag accordingly

    if (PIR_flag) {
      if (FanMotTrigEnabled == 1) {
        TIM1_flag = true;  // Update screen initially
        screenPwr(1);

        unsigned long lastMotion;
        uint8_t step = 1;
        if (waitDur <= 5) step = 2;  // PIR_flag is already true at this point
        else swCounter(1, waitDur);

        for (step = step; step <= 2; step++) {  // 1: waiting, 2: armed, 3: Running
          if (step == 2) {
            (waitDur >= 90) ? counter = 60 : counter = 30;  // Deciding how long ahead it will check for motion
            swCounter(1, counter);
            PIR_flag = false;
            updateScreen(step);
          }
          while (counter > 0) {  // Counter Loop
            if (checkButtons()) return;
            if (PIR_flag) {
              PIR_flag = false;
              lastMotion = millis();
              if (step >= 2) {    // if it's armed or Running and Motion is detected
                if (step == 2) {  // if it's armed and Motion is detected
                  if (FanRelayStatus != 2) swFanRelays(1);
                  step = 3;  // Motion..
                  updateScreen(step);
                }
                swCounter(1, Runtime);  // Restart interval
              }
            }
            if (TIM1_flag) {
              TIM1_flag = false;
              if (getDNDstatus()) return;
              if (step == 1 && (millis() - lastMotion) / 1000 >= screenTime) return;
              getDew();
              DewControl(step != 3);  // in case Dew increases while on wait
              updateScreen(step);
            }
          }
        }
      } else if (FanMotTrigEnabled != 1) {  // Mot trig is disabled but there is motion
        PIR_flag = false;
        if (FanMotTrigEnabled == 2) swFanRelays(0);
        RunScreen(false);
      }
    }

    DewControl(true);
    Sleep(true);

  } else {  // if it's DND times:
    if (PIR_flag) {
      PIR_flag = false;
      RunScreen(true);
    }
    if (getDNDstatus()) Sleep(false);  // DnD status is rechecked because it might have been stuck in RunScreen by the user when DND is over.
  }
}

void DewControl(bool cmd1) {  // 0: cannot turn the fan "off" but only "on" 1: can both turn "on" and "off"
  if (Weather_flag == 0 && cmd1) swFanRelays(0);
  else if (Weather_flag == 1) swFanRelays(1);
  else if (Weather_flag == 2) swFanRelays(2);
}

void RunScreen(bool cmd1) {  // 0: dont return based on DND, 1: return if not in DND times anymore (prevents being stuck in DND if screen is on)
  TIM1_flag = true;          // Update screen initially
  screenPwr(1);
  swCounter(1, screenTime);  // Fixed Run screen time
  while (counter > 0) {
    if (checkButtons()) return;
    if (TIM1_flag) {
      if (getDNDstatus()) {
        if (FanRelayStatus) return;
      } else {
        if (cmd1) return;
        DewControl(true);
      }
      getDew();
      updateScreen(0);  // Idle or Dew

      if (PIR_flag) {
        counter = screenTime;
        PIR_flag = false;
      }
      TIM1_flag = false;
    }
  }
  LightRel_IfNeeded(0);
}

void getDew() {
  SHT31D result = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50);  // used to be a static var
  Temp = result.t;
  Hum = result.rh;

  float a_func = log(Hum / 100) + 17.625 * Temp / (243.04 + Temp);
  Dew = (243.04 * a_func) / (17.625 - a_func);

  if (!DewTrigEnabled || (Dew <= DewThres * DewUSratio) || (Dew < DewThres && FanRelayStatus == 0)) Weather_flag = 0;
  else if (Dew < DewThresExtreme) Weather_flag = 1;
  else Weather_flag = 2;
}

bool getDNDstatus() {  // check if hours and minutes are in the interval
  if (!DNDenabled) return false;

  updateTime();
  uint16_t currentmin = hour * 60 + minute;

  uint16_t localDNDstart = DND_SH * 60 + DND_SM;
  uint16_t localDNDfinish = DND_FH * 60 + DND_FM;

  if (localDNDstart < localDNDfinish) return (currentmin >= localDNDstart && currentmin < localDNDfinish);
  else return (currentmin >= localDNDstart || currentmin < localDNDfinish);
}

void updateTime() {
  if (myRTC_2.checkIfAlarm(2)) {
    DateTime now;  // used to be a static var
    now = myRTC_1.now();
    hour = now.hour();
    minute = now.minute();
  }
}

void swFanRelays(uint8_t cmd1) {  // 0: turn off, 1: turn on
  if (FanRelayStatus == cmd1) return;
  bool PIR_flag_original = PIR_flag;
  if (cmd1 == 0) {
    digitalWrite(FAN_MAIN_REL, LOW);
    digitalWrite(FAN_SECOND_REL, LOW);
    FanRelayStatus = 0;
  } else if (cmd1 == 1) {
    digitalWrite(FAN_MAIN_REL, HIGH);
    if (FanRelayStatus != 2) {
      digitalWrite(FAN_SECOND_REL, HIGH);
      delay(500);
    }
    digitalWrite(FAN_SECOND_REL, LOW);
    FanRelayStatus = 1;
  } else if (cmd1 == 2) {
    digitalWrite(FAN_MAIN_REL, HIGH);
    digitalWrite(FAN_SECOND_REL, HIGH);
    FanRelayStatus = 2;
  }
  delay(100);
  if (!PIR_flag_original) PIR_flag = false;
  But_flag = false;
  FreeRun_flag = false;
}

void LightRel_IfNeeded(bool cmd1) {  // 0: turn OFF Light Relay, 1: turn ON Light Relay
  static bool LightRelStatus;
  bool LDRresult;
  static uint8_t phase;  // regularly check light level in case brightness changes in the room over time.

  switch (cmd1) {
    case 0:
      if (!LightRelStatus) return;
      LightRelStatus = false;
      phase = 0;
      digitalWrite(LIGHT_REL, LOW);
      delay(100);
      PIR_flag = false;
      But_flag = false;
      FreeRun_flag = false;
      break;

    case 1:
      if (!LightRelStatus && phase == 0) {
        if (LDRenabled) {
          digitalWrite(LDR_PWR, HIGH);
          delay(10);
          LDRresult = (analogRead(LDR_READ) > LDRThres);  // Darkness --> High resistance --> High voltage on LDR --> TRUE              Lit --> Low resistance --> Low voltage on LDR --> FALSE
          digitalWrite(LDR_PWR, LOW);
        } else LDRresult = true;

        if (LDRresult) {
          LightRelStatus = true;
          digitalWrite(LIGHT_REL, HIGH);
          delay(100);
          PIR_flag = false;
          But_flag = false;
          FreeRun_flag = false;
        }
      }
      phase++;
      if (phase > phaseLim) phase = 0;  // default value is 120 which means 10 minutes
      break;
  }
}

void Sleep(bool cmd1) {  // 0: Sleep normal 1: Sleep without turning off the Fan
  if (cmd1 == 0) swFanRelays(0);
  LightRel_IfNeeded(0);
  screenPwr(0);
  updateTime();
  PIR_flag = false;
  But_flag = false;
  FreeRun_flag = false;

  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}

void updateScreen(uint8_t cmd1) {  // 0: idle or Dew Trig, 1: Detected, 2: Waiting, 3: Armed, 4: Mot Trig, 5: Free run dur
  String info;
  LightRel_IfNeeded(1);  // Regularly called because brigthness of the room info is updated every phase*5/60 minutes
  updateTime();

  switch (cmd1) {
    case 0:
      if (FanRelayStatus == 0) info = "Idle";
      else if (FanRelayStatus == 1) info = "Dew";
      else info = "OverDew";
      break;
    case 1:
      info = "Waiting";
      if (FanRelayStatus == 1) info += "&Dew";
      else if (FanRelayStatus == 2) info += "&OverDew";
      break;
    case 2:
      info = "Armed";
      if (FanRelayStatus == 1) info += "&Dew";
      else if (FanRelayStatus == 2) info += "&OverDew";
      break;
    case 3:
      info = "Motion";
      if (Weather_flag == 1) info += "&Dew";
      else if (Weather_flag == 2) info += "&OverDew";
      break;
    case 4:
      info = "<";
      info += String(counter / 60 + 1);
      info.remove(info.indexOf('.'));
      info += " mins";
      break;
  }

  u8g2.firstPage();
  do {
    u8g2.setFont(u8g2_font_6x10_mr);  // u8g2_font_haxrcorp4089_tn
    u8g2.setCursor(0, 7);
    u8g2.print(info);

    u8g2.setCursor(99, 7);
    if (hour < 10) u8g2.print("0");
    u8g2.print(hour);
    u8g2.print(":");
    if (minute < 10) u8g2.print("0");
    u8g2.print(minute);

    u8g2.drawLine(0, 10, 128, 10);

    u8g2.setFont(u8g2_font_fub20_tn);
    u8g2.setCursor(0, 38);
    if (Temp < 10 && Temp >= 0) u8g2.print("0");
    u8g2.print(Temp, 1);
    u8g2.setCursor(0, 64);
    u8g2.print(Hum, 1);
    u8g2.setCursor(71, 61);
    if (Dew < 10) u8g2.print("0");
    u8g2.print(Dew, 1);
    if (Dew < DewThres) u8g2.drawFrame(70, 38, 58, 26);
    else u8g2.drawBox(71, 39, 56, 24);


    u8g2.setFont(u8g2_font_lubB12_tr);
    u8g2.drawStr(55, 30, "*C");
    u8g2.drawStr(55, 64, "%");

    if (FreeRunStatus == true) u8g2.drawStr(81, 30, "FREE");
    else if (getDNDstatus()) u8g2.drawStr(82, 30, "DND");

  } while (u8g2.nextPage());
}

void screenPwr(bool cmd1) {
  if (cmd1 == screen_awake) return;

  switch (cmd1) {
    case 0:
      u8g2.setPowerSave(1);
      u8g2.clear();
      screen_awake = false;
      break;

    case 1:
      u8g2.setPowerSave(0);
      swCounter(0);
      screen_awake = true;
      break;
  }
}

void FreeRun() {
  Beep(1, 500, 0);
  FreeRun_flag = false;
  FreeRunStatus = true;
  getDew();
  swFanRelays(1);
  swCounter(1, FreeRunDur);
  updateScreen(4);
  screenPwr(1);
  while (counter > 0) {
    if (But_flag) {
      FreeRunStatus = false;
      Settings();
      return;
    }
    if (FreeRun_flag) {
      Beep(1, 100, 0);
      delay(1000);
      if (!digitalRead(FREE_RUN_BUTTON)) {  // if the user holds the button down
        swFanRelays(0);
        Beep(2, 100, 100);
        while (!digitalRead(FREE_RUN_BUTTON)) delay(100);  // For the user to remove hand
        delay(500);                                        // prevent debouncing
        FreeRun_flag = false;
        break;
      } else {
        FreeRun_flag = false;
        if (FanRelayStatus == 1) swFanRelays(2);
        else if (FanRelayStatus == 2) swFanRelays(1);
      }
    }
    if (TIM1_flag) {
      TIM1_flag = false;
      if (getDNDstatus()) break;
      getDew();
      updateScreen(4);
    }
  }
  DewControl(true);
  FreeRunStatus = false;
  PIR_flag = false;  // Ignore the motion while in "Free Run" mode
}

bool checkButtons() {
  if (FreeRun_flag) {
    if (!getDNDstatus()) {
      FreeRun();
      return 1;
    } else {  // if Free Run is pressed during DND, deny Free Run
      Beep(3, 50, 50);
      FreeRun_flag = false;
    }
  } else if (But_flag) {
    Beep(1, 100, 0);
    delay(1000);  // If button is held down
    if (!digitalRead(ENCODER_BUTTON)) {
      Settings();
      return 1;
    }
    But_flag = false;
  }
  return 0;
}

void Beep(uint8_t rep, uint8_t dur_act, uint8_t dur_pass) {  // repetition, duration of beeping, duration of pauses
  for (int i = 0; i < rep; i++) {
    digitalWrite(BUZZER, HIGH);
    delay(dur_act);
    digitalWrite(BUZZER, LOW);
    if (i < (rep - 1)) delay(dur_pass);
  }
}

void LoadPinsAndEEPROM() {

#define FanMotTrigEnabled_Address 0
#define waitDur_Address 1
#define Runtime_Address 3
#define DewTrigEnabled_Address 5
#define DewThres_Address 6
#define DewThresExtreme_Address 10
#define LightRelEnabled_Address 14
#define LDRenabled_Address 15
#define LDRThres_Address 16
#define DND_Enabled_Address 18  // DND enabled
#define DND_SH_Address 19       // DND start Hour
#define DND_SM_Address 20       // DND start Minute
#define DND_FH_Address 21       // DND finish Hour
#define DND_FM_Address 22       // DND finish Minute
#define FreeRunDur_Address 23

  EEPROM.get(FanMotTrigEnabled_Address, FanMotTrigEnabled);
  EEPROM.get(waitDur_Address, waitDur);
  EEPROM.get(Runtime_Address, Runtime);
  EEPROM.get(DewTrigEnabled_Address, DewTrigEnabled);
  EEPROM.get(DewThres_Address, DewThres);
  EEPROM.get(DewThresExtreme_Address, DewThresExtreme);
  EEPROM.get(LightRelEnabled_Address, LightRelEnabled);
  EEPROM.get(LDRenabled_Address, LDRenabled);
  EEPROM.get(LDRThres_Address, LDRThres);
  EEPROM.get(DND_Enabled_Address, DNDenabled);
  EEPROM.get(DND_SH_Address, DND_SH);
  EEPROM.get(DND_SM_Address, DND_SM);
  EEPROM.get(DND_FH_Address, DND_FH);
  EEPROM.get(DND_FM_Address, DND_FM);
  EEPROM.get(FreeRunDur_Address, FreeRunDur);

  // DEBUG:
  /*
  FanMotTrigEnabled = 1;
  waitDur = 10;
  Runtime = 10;
  DewTrigEnabled = true;
  DewThres = 18.0;
  DewThresExtreme = 19.0;
  LightRelEnabled = true;
  LDRenabled = true;
  LDRThres = 512;
  DNDenabled = false;
  DND_SH = 23;
  DND_SM = 45;
  DND_FH = 0;
  DND_FM = 2;
  FreeRunDur = 30;
*/
  pinMode(PIR, INPUT);
  pinMode(LDR_READ, INPUT);
  pinMode(ENC_A, INPUT);
  pinMode(ENC_B, INPUT);

  pinMode(CLOCK_INT, INPUT_PULLUP);  // Datasheet states pullup resistor is necessary
  pinMode(FREE_RUN_BUTTON, INPUT_PULLUP);
  pinMode(ENCODER_BUTTON, INPUT_PULLUP);

  pinMode(FAN_MAIN_REL, OUTPUT);
  pinMode(FAN_SECOND_REL, OUTPUT);
  pinMode(LIGHT_REL, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  pinMode(LDR_PWR, OUTPUT);
}

void ISR_PIR() {
  PIR_flag = true;
}

void ISR_ENCODER_BUTTON() {
  But_flag = true;
}

ISR(PCINT1_vect) {
  if (!digitalRead(FREE_RUN_BUTTON)) FreeRun_flag = true;  // Button has pullup resistor thus "!" is used.
}

ISR(TIMER1_COMPA_vect) {
  TIM1_flag = true;
  counter -= 5;
  if (counter <= 0) swCounter(0);
}


// Horrible Settings Codes xd:
ISR(PCINT2_vect) {  // Rotary Encoder Interrupt
  static bool aLastState;
  bool aState = digitalRead(ENC_A);
  bool bState = digitalRead(ENC_B);

  if (aState != aLastState) {
    if (bState != aState) {
      encVal++;
    } else {
      encVal--;
    }
    aLastState = aState;
  }
}

void ButDebounce(bool cmd1) {  // 0: Encoder But, 1: Free Run
  if (cmd1) {
    while (!digitalRead(FREE_RUN_BUTTON)) delay(100);  // Wait until button is released
    delay(200);
    FreeRun_flag = false;
  } else {
    while (!digitalRead(ENCODER_BUTTON)) delay(100);  // Wait until button is released
    delay(200);
    But_flag = false;
  }
}

String formatTime(int input, bool type = true) {  // type = true means input is seconds, if false it means input is minutes
  int hours = input / 3600;                       // Get the number of hours
  int minutes = (input % 3600) / 60;              // Get the number of minutes
  int remainingSeconds = input % 60;              // Get the number of seconds

  if (!type) {
    hours = minutes;
    minutes = remainingSeconds;
    remainingSeconds = 0;
  }

  // Create a formatted string in the format "hour:minute:second"
  String formattedTime = "";

  if (hours < 10) {
    formattedTime += "0";  // Add leading zero for hours
  }
  formattedTime += String(hours) + ":";

  if (minutes < 10) {
    formattedTime += "0";  // Add leading zero for minutes
  }
  formattedTime += String(minutes) + ":";

  if (remainingSeconds < 10) {
    formattedTime += "0";  // Add leading zero for seconds
  }
  formattedTime += String(remainingSeconds);

  return formattedTime;
}

uint8_t setEncVal(uint16_t target, uint8_t inc1, uint8_t inc2, uint16_t inc3, uint8_t lim1, uint8_t lim2) {
  uint16_t a = 0;
  uint8_t b = 0;
  while (a < target) {
    if (b < lim1) a += inc1;
    else if (b < lim2) a += inc2;
    else a += inc3;
    b++;
  }
  return b;
}

void Settings() {} // removed because of character limit but I don't run this function before stuck anyway
9
  • Have you tried adding dedicated pullup resistors to SDA and SCL? Like 4.7kOhm. That might help in this case
    – chrisl
    Jun 21, 2023 at 14:42
  • No but the modules have pull-ups and it is 2.2k each when I measured Jun 21, 2023 at 14:58
  • 1
    the first three sentences describe a possible overheating problem
    – jsotola
    Jun 21, 2023 at 17:38
  • Last time I had super weird I2C freezes it was caused by pin change ISR without it's handler.
    – KIIV
    Jun 21, 2023 at 19:11
  • I have a pin change interrupt which I use to wake the arduino up every minute from sleep by Ds3231 and it is connected to A3 and I have a button which is conected to A2. I didn't need handler for the clock module but since they are both analog pins I have "ISR(PCINT1_vect)" for the button so I guess I can say that I have no unhandled interrupts even though pinwise A3 does nothing except waking up. Jun 22, 2023 at 1:30

2 Answers 2

1

It sounds like over ambitious pull up resistors. They should total about 3K on each bus. I will take a SWAG and say the I2C driver fet is drawing to much current and heating up. When it does that its resistance increase thereby delivering less current. Get your resistors correct and I believe it will work just fine.

If you have freeze spray chill them and they will probably start working until they warm up.

If it was left on for any period of time it is possible you damaged one of the ICs which is no longer driving the bus properly. If you can check it with a scope. If not swat parts.

2
  • This is the first answer that sounded reasonable to me. On an other forum they told me I had cold solder or connectivity problem but they didn't sound realistic. This explains maybe why the device works longer when I wait for a long time before repowering, because FETs cool down. Also it explains why it managed to run 2 days at the first time, because the I2C FETs were healthy but now they are semi broken so it only lasts few hours. It lasts few seconds when I repower without waiting. I just can't believe kiloohms of resistance can still damage FETs. I will remove some resistors form modules. Jun 22, 2023 at 1:37
  • I've removed the DS3231 because it had 4.7k pull-ups on board thus the total resistance became 5k (10k each on the SHT31 and OLED). Unfortunately the device was stuck few minutes after. Jun 22, 2023 at 10:29
1

Are you sure you are using the latest I2C library? Up until halfway through 2020, the 'official' I2C library had a known failure mode that would cause the I2C buss to hang. See my 'Paynters Palace' post on this subject for additional info.

Frank

1
  • There is a comment saying: "Modified 2020 by Greyson Christoforo ([email protected]) to implement timeouts" I guess there is a default timeout already. Also I installed Arduino IDE 2 on April 2023. Anything I am missing? Thanks. Jul 3, 2023 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.