Pull up resistors for I2C devices

I have three I2C devices connected to Uno and powered independently. The setup randomly fails (Arduino just stops running and needs to be reset, sometimes, LCD back-light goes out as well until the reset, etc.

Trough trial and error I discovered the issue with pull-up resistors (see details below) that improved things quite a bit (it used to freeze up within seconds of resetting Arduino, now it fails randomly but not all the time).

I am guessing there is another issue I am not seeing and any advice will be appreciated.

thank you

P.S. I also have an issue with uploading a program (I have to disconnect separate power to relay in order to upload a program - details are described here).

P.S. issue with pull-up resistors - I2C pins on arduino (SDA/SCL have internal pull ups and any pull-up resistor on I2C devices should be disabled. I did this for both of my MCP4725 DACs and it worked fine during prototyping when used separately. But the set up wouldn't work when three I2C devices connected simultaneously AND powered through a separate 5V supply (grounds connected with Arduino). I re-enabled pull-up on one of the DAC and set-up is working much better (no idea why) but still freezes occasionally. Should I enable pull-ups on all three? is there anything else missing?

#include <Wire.h>  
#include <LiquidCrystal_I2C.h> // Using version 1.2.1
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 
#include <Adafruit_MCP4725.h>

Adafruit_MCP4725 dac_R; // constructor
Adafruit_MCP4725 dac_L;

//CURRENT READINGS
const int numReadings = 10; //number of current readings
int readings_1[numReadings]; int readings_2[numReadings];       // the readings from the analog input acs715
int readIndex = 0; // the index of the current reading
int total_1 = 0; int total_2 = 0; // the running total current from acs715

int analogInPin1 = A0; int analogInPin2 = A1;  // Analog input pin that acs715 is connected to
float amps_1 =0; float amps_2 =0; //amps calculation from average
float pramps_1 =0; float pramps_2 =0; //previous amps
unsigned long lastampreading = 0; // last time acs715 was read
unsigned long ampreadinginterval = 10; // interval for acs715 reading
float amplimit = 12; // maximum amps allowed per motor 

//DAC OPERATIONS
unsigned long lastVupdate = 0; // last time POT was read
unsigned long Vupdateinterval = 200; // how often POT should be read
uint32_t dac_value = 0;
int adcValueRead = 0;
float voltageRead = 0;
float dac_expected_output;
int dac_increment = 100;
int speedpercent = 0; //speed as percent

//INPUT SPEED OPERATIONS
int speedinput = A2; // POT input
int speedincrement = 50; // POT input is mapped to smaller resolution (from 1023)
int input_speed = 0; // input speed that would be anything between 0 and speedincrement above. 

//Motor pins
int turnon = 5; //receives signal to turn motors on or off (set as input_pullup pin in void setup section)
int Lmotor = 11; int Rmotor = 12; // activates relay to turn on PWM controller (set as output in void setup section)
boolean MotorsOn = false; // tracks whether motors are on 

int reversesense = 4; //receives signal to reverse direction of motors (set as input_pullup pin in void setup section)
int reverser = 13; //activates DSDP relay to reverse direction
boolean reverse = false; // tracks whether direction is forward or reversed

unsigned long buttoninterval = 500; //these are for button debouncing. 
unsigned long buttonlastread = 0;


    
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
  for (int thisReading = 0; thisReading < numReadings; thisReading++) { //creates placeholder variables for tracking average current readings from each motor. 
    readings_1[thisReading] = 0;
    readings_2[thisReading] = 0;}
dac_R.begin(0x62);
dac_L.begin(0x61);
lcd.begin(16,2); // sixteen characters across - 2 lines
lcd.backlight();
lcd.setCursor(0,0);

dac_R.setVoltage(0, false); //sets DAC output to zero
dac_L.setVoltage(0, false);

pinMode(turnon, INPUT_PULLUP);
pinMode(Rmotor, OUTPUT);
pinMode(Lmotor, OUTPUT);
digitalWrite(Rmotor, HIGH);
digitalWrite(Lmotor, HIGH);

pinMode(reversesense, INPUT_PULLUP);
pinMode(reverser, OUTPUT);
digitalWrite(reverser, HIGH);
}
void loop() {
//IF MOTOR TURN ON BUTTON IS PUSHED
if (digitalRead(turnon)== LOW) {
  if ((millis() - buttonlastread) > buttoninterval) {
    buttonlastread = millis();
    FTurnMotorsOnOff(); //call function to turn motor on or off
  }
}
//IF REVERSE BUTTON IS PUSHED
if (digitalRead(reversesense)== LOW) {
  if ((millis() - buttonlastread) > buttoninterval) {
    buttonlastread = millis();
    if (MotorsOn == true){ //shut down the motors before engaging in reverse. 
      Fsettledown(); 
    }
      Freverse(); //call function to reverse motor
  }
}
if (MotorsOn == true) {
//MONITOR THROTTLE INPUT AND SEND SIGNAL TO MOTOR CONTROLS
if ((millis() - lastVupdate) > Vupdateinterval){
  lastVupdate=millis();
  input_speed = map(analogRead(speedinput),0,1023,speedincrement, 0); //map POT input to 1-50 resolution
   int currspeed = map(dac_value,0,4000, 0, speedincrement); //also map DAC output (not actual output but what it is set to output) to the same range (1-50). 
  if (input_speed != currspeed) { 
      if (input_speed > currspeed && input_speed > 2)   { //if POT is higher than DAC, then its accellerating. ignore pot reading lower than 2 
        dac_value += (1000/speedincrement); //so, if speed is increasing, don't set DAC all the way to POT reading at once
        // Instead, increase by 1000/speedincrement that would increase of 20 every 200ms (200 is Vupdateinterval) and it would take 40,000ms (40 seconds) to reach full speed of about 6mph. 

      }
      else  if (input_speed < currspeed) {
        dac_value += -(4000/speedincrement); // if speed is decreasing, deceleration is faster
      } 
    dac_R.setVoltage(dac_value, false); //set DAC output to each motor. 
    dac_L.setVoltage(dac_value, false);
    speedpercent = (currspeed*100/speedincrement); //calculate percent value of the speed. 
    LCDUpdate(6);
  }
//MONITOR CURRENT DRAW FOR EACH MOTOR
if ((millis() - lastampreading) > ampreadinginterval) { //amps should be read once every 10ms
  lastampreading = millis(); 
  //Read and maintain the total for the last numReadings
  total_1 = total_1 - readings_1[readIndex];         
  readings_1[readIndex] = analogRead(analogInPin1);
  total_1 = total_1 + readings_1[readIndex];
  
//second ACS715 code is commented out for testing purposes

  readIndex = readIndex + 1;
  if (readIndex >= numReadings) { // once 10 readings are done, calculate average amps 
    readIndex = 0;
    amps_1 = ampsreading(total_1); //call function below to calculate average amps over numReadings 
    if (abs(amps_1-pramps_1) > 0.2) { // update amp reading if the chage is more than 0.2.
      pramps_1 = amps_1;
      LCDUpdate(5); 
      // pramps_2 = amps_2;
      //IF CURRENT EXCEEDS ALLOWABLE LIMIT, INITIATE SHUTDOWN
      //if ((amps_1 >= amplimit) || (amps_2 >= amplimit)) {
      if (amps_1 >= amplimit) {
            Fsettledown(); // Initiate shutdown if the current is exceeded. 
            return;
      }
    }
}
  }
}
}
}

void FTurnMotorsOnOff() {
//removed code to meet the forum 9000 character limit requirement
}

void Freverse() {
//removed code to meet the forum 9000 character limit requirement
}


float ampsreading (int total) {//calculates amp value from total readings and number of readings. 
  int average = total / numReadings;
  float amps = (float) (((long)average * 5000 / 1024) - 500 ) / 133;
  return amps; 
}

void Fsettledown(){ //slows everything down quickly and shut down. 
  for (int i=dac_value; i >= 0; i=i-(4000/speedincrement)){
    dac_R.setVoltage(i, false);
    dac_L.setVoltage(i, false);  
    delay(100); // delay used here since this function rarely is called for and nothing else should be happening when this function is being executed 
  }
  digitalWrite(Rmotor, HIGH);
  digitalWrite(Lmotor, HIGH);
  MotorsOn = false;
  dac_value=0;
  LCDUpdate(4);
}

void LCDUpdate (int scenario) {
//removed LCD update code to meet the forum 9000 character limit requirement
}

I'd 1/2 split your issue or cut 1/2 the stuff out to see if that 1/2 of the stuff causes issue. I suspect you are running out of program memory space, after looking over the code.

Thank you

Idahowalker:
I'd 1/2 split your issue or cut 1/2 the stuff out to see if that 1/2 of the stuff causes issue. I suspect you are running out of program memory space, after looking over the code.

Here is very basic question (I just don't know):

When you say "program memory" does that mean the actual length of the code or number of variables that are being manipulated? Would reducing array of variables that used to calculate the amps help (I keep roughly 20 variables for current to calculate average load)?

The reason for asking is that this code has 6,600 characters (9,400 with comments) vs. another code that has 6,700 characters (below) which runs without a problem; trying to figure out where and what to cut if the issue is a program memory. thank you.

#include <Wire.h>  // Comes with Arduino IDE

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
//on board YGB
//on arduino YGB
int latchPin = 10;
int clockPin = 9;
int dataPin = 8;
int keypadpass = 52;
int wrongpassword = 53;
int wrongpasswordreset = 51;
int functionstatus;
int wrongpasswordcount;

int deflector=22; boolean deflectorstatus; //1.2
int hyperdrive=23;boolean hyperdrivestatus; //2.2
int targeting=24;boolean targetingstatus; //2.3
int repair=25;boolean repairstatus; //4.1
int laserleft=26;int laserleftstatus; //2.1
int laserright=27;int laserrightstatus;//3.1
int torpedos=28;boolean torpedosstatus; //4.2
int viewfront=29; //3.2
int viewback=30; //3.3
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 200;


int numOfRegisters = 2;
byte* registerState;

long effectId = 0;
long prevEffect = 0;
long effectRepeat = 0;
long effectSpeed = 30;

#include <ShiftOutX.h>
#include <ShiftPinNo.h>

shiftOutX regOne(10, 8, 9, MSBFIRST, 4); 

void setup() {
  //Initialize array
  registerState = new byte[numOfRegisters];
  for (size_t i = 0; i < numOfRegisters; i++) {
    registerState[i] = 0;
  }


  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(wrongpasswordreset, OUTPUT);
  pinMode(keypadpass,INPUT_PULLUP);
  pinMode(wrongpassword,INPUT_PULLUP);
  pinMode(deflector,INPUT_PULLUP);deflectorstatus = false;
  pinMode(hyperdrive,INPUT_PULLUP); hyperdrivestatus = LOW;  
  pinMode(targeting,INPUT_PULLUP); targetingstatus = LOW;
  pinMode(repair,INPUT_PULLUP); repairstatus = LOW;
  pinMode(laserleft,INPUT_PULLUP); laserleftstatus = 0;
  pinMode(laserright,INPUT_PULLUP); laserrightstatus =0;
  pinMode(torpedos,INPUT_PULLUP); torpedosstatus = LOW; 
  pinMode(viewfront,INPUT_PULLUP); 
  pinMode(viewback,INPUT_PULLUP);
  functionstatus = 0; 
  wrongpasswordcount = 0;
   lcd.begin(20,4);
     for(int i = 0; i< 3; i++)
  {
    lcd.backlight();
    delay(250);
    lcd.noBacklight();
    delay(250);
  }
  lcd.backlight();

  lcd.setCursor(3,0); //Start at character 4 on line 0
  lcd.print("Hello, Jedi!");
  delay(1000);
  lcd.setCursor(2,1);
  lcd.print("Enter the passcode");
  delay(1000);  
  lcd.setCursor(0,2);
  lcd.print("on the keypad");
  lcd.setCursor(0,3);
  delay(1000);   
  lcd.print("to start the T-65");
  delay(1000);
}

void loop() {  
if (functionstatus == 0) {
    ledintro();
    if (digitalRead(keypadpass) == LOW) {
      functionstatus = 1;wrongpasswordcount=0;
      lcd.clear();  lcd.setCursor(3,0); lcd.print("Great job, Jedi");
      lcd.setCursor(0,1); lcd.print("Get to know the");
      lcd.setCursor(0,2); lcd.print("X-Wing before");
      lcd.setCursor(0,3); lcd.print("engaging the engines");
      regOne.allOn();
      delay(1000);
      regOne.allOff();
          
    if (digitalRead(wrongpassword) == LOW) {
      wrongpasswordcount++;
      digitalWrite(wrongpasswordreset, LOW);
      wrongpasswordresponse();
      }
    }
}
else {
  if ((millis() - lastDebounceTime) > debounceDelay) {
  if (digitalRead(deflector) == LOW){
    if (deflectorstatus == false){deflectorstatus = true; regOne.pinOn(shPin17);
      lcd.clear();  lcd.setCursor(3,0); lcd.print("Deflectors enabled");
      //lcd.setCursor(0,1); lcd.print("been en");
      lcd.setCursor(0,2); lcd.print("Remember, they");
      lcd.setCursor(0,3); lcd.print("effect velocity");}
    else {deflectorstatus = false; regOne.pinOff(shPin17);
      lcd.clear();  lcd.setCursor(3,0); lcd.print("Deflectors disabled");
      //lcd.setCursor(0,1); lcd.print("been en");
      lcd.setCursor(0,2); lcd.print("Be cautious");
      //lcd.setCursor(0,3); lcd.print("effect velocity");
      }
      lastDebounceTime = millis();}
  if (digitalRead(hyperdrive) == LOW){
    if (hyperdrivestatus == false){hyperdrivestatus = true; regOne.pinOn(shPin18);
      lcd.clear();  lcd.setCursor(3,0); lcd.print("Hyperdrive enabled");
      lcd.setCursor(0,1); lcd.print("Dont wonder too far");
      lcd.setCursor(0,2); lcd.print("Imperials are around");
      //lcd.setCursor(0,3); lcd.print("effect velocity");
      }
    else {hyperdrivestatus = false; regOne.pinOff(shPin18);
      lcd.clear();  lcd.setCursor(0,0); lcd.print("Hyperdrive disabled");
      lcd.setCursor(0,1); lcd.print("Return to base");
      lcd.setCursor(0,2); lcd.print("immediately");
     // lcd.setCursor(0,3); lcd.print("effect velocity");
      }
      lastDebounceTime = millis();}
  if (digitalRead(targeting) == LOW){
    if (targetingstatus== LOW){targetingstatus = HIGH; regOne.pinOn(shPin19);}
    else {targetingstatus = LOW; regOne.pinOff(shPin19);}}  
  if (digitalRead(repair) == LOW){
    if (repairstatus == LOW){repairstatus = HIGH; regOne.pinOn(shPin20);}
    else {repairstatus = LOW; regOne.pinOff(shPin20);}}
  if (digitalRead(laserleft) == LOW){
    //
}
  if (digitalRead(laserright ) == LOW){
  //
}
  if (digitalRead(torpedos) == LOW){
    if (torpedosstatus == LOW){torpedosstatus = HIGH; regOne.pinOn(shPin23);}
    else {torpedosstatus = LOW; regOne.pinOff(shPin23);}}

  if (digitalRead(viewfront) == LOW){
  //
}
  if (digitalRead(viewback) == LOW){
  //
}}}}

void ledintro(){
      int prevI = 0;
  for (int i = 0; i < 16; i++){
    regWrite(prevI, LOW);
    regWrite(i, HIGH);
    prevI = i;
    delay(30);
  }
  for (int i = 15; i >= 0; i--){
    regWrite(prevI, LOW);
    regWrite(i, HIGH);
    prevI = i;
    delay(30);
  }
  }

void wrongpasswordresponse(){
  wrongpasswordcount = 1;
}
void regWrite(int pin, bool state){
  //Determines register
  int reg = pin / 8;
  //Determines pin for actual register
  int actualPin = pin - (8 * reg);

  //Begin session
  digitalWrite(latchPin, LOW);

  for (int i = 0; i < numOfRegisters; i++){
    //Get actual states for register
    byte* states = &registerState[i];

    //Update state
    if (i == reg){
      bitWrite(*states, actualPin, state);
    }

    //Write
    shiftOut(dataPin, clockPin, MSBFIRST, *states);
  }

  //End session
  digitalWrite(latchPin, HIGH);
}
 void fourRegister(){
 regOne.pinOn(1);
  delay(200);
  regOne.pinOn(shPin2);//PIN 2
  delay(200);   
 regOne.pinOn(4);
  delay(200);
 regOne.pinOn(8);// PIN 4
  delay(200); 
 regOne.pinOn(5);
  delay(200);
  regOne.pinOn(shPin6);//PIN 6
  delay(200);
  regOne.pinOff(shPin7);
  delay(200);
  regOne.pinOn(shPin8);// PIN 8
  delay(200);
   regOne.pinOn(shPin1);
  delay(200);
  regOne.pinOn(shPin10);//PIN 2-2
  delay(200);   
 regOne.pinOff(shPin33);
  delay(200);
 regOne.pinOn(shPin12);// PIN 2-4
  delay(200); 
 regOne.pinOff(shPin34);
  delay(200);
  regOne.pinOn(shPin14);//PIN 2-6
  delay(200);
  regOne.pinOff(shPin35);
  delay(200);
  regOne.pinOn(shPin16);// PIN 2-8
  delay(200);


  if (regOne.pinState(shPin20) == true)
  {
    regOne.pinOn(shPin32);
    delay(1000);
    regOne.pinOff(shPin32);
  }
  
  
  regOne.allOn();
  delay(1000);

  regOne.allOff();
  delay(1000);
  

}

First, I'd prove if the SRAM is getting full by cutting out 1/2 the code, commenting out, and see how well it runs, then add in a bit more code, uncomment, till failure point.

The ram I mean is the ram your program will use to run from; SRAM: 2 KB (ATmega328P).

Number of characters in your C++ code has not much relationship to the size of the compiled code. Quite a few times I added code only to see the compiled size decrease. Or the other way around. Have a look at what the compiler tells you after the code has been compiled, and definitely heed "low memory" warnings.

There are quite a few optimisations that I spotted for SRAM savings, mostly by not declaring everything int but use byte if the value is never more than 255, or add the const keyword to make it a constant for further optimisation. Furthermore, use the F() macro for all string literals. See this tutorial on how to use it.

You have quite a mix of voltages in that circuit.
You should definitely not use a pull up resistor on an I2C device which is driven at a higher voltage than the Arduino it is connected to. Disconnect all the pull-up resistors on the I2C devices (as you apparently have) and try adding say 4.7k pull-up (or maybe a bit stronger if necessary but not less than 1k) to the Arduino’s I2C lines. The internal pull-ups on the Arduino may not be sufficient with multiple I2C devices connected.
Can you draw a schematic of that circuit?

wvmarle:
Number of characters in your C++ code has not much relationship to the size of the compiled code. Quite a few times I added code only to see the compiled size decrease. Or the other way around. Have a look at what the compiler tells you after the code has been compiled, and definitely heed "low memory" warnings.

There are quite a few optimisations that I spotted for SRAM savings, mostly by not declaring everything int but use byte if the value is never more than 255, or add the const keyword to make it a constant for further optimisation. Furthermore, use the F() macro for all string literals. See this tutorial on how to use it.

Thank you. After playing with it some more, it seems that the code is working now without a problem (it may also have been a minor hardware problem (connection, etc.). Below is the output when compiling and it seems the memory is not exhausted (unless I am misreading it) but change the variable types per your suggestion to make it more optimized. Thank you.

P.S. My only outstanding issue is with the weird relationship between the relay being powered and ability to upload a program.

Sketch uses 8344 bytes (25%) of program storage space. Maximum is 32256 bytes.
Global variables use 635 bytes (31%) of dynamic memory, leaving 1413 bytes for local variables. Maximum is 2048 bytes.

6v6gt:
You have quite a mix of voltages in that circuit.
You should definitely not use a pull up resistor on an I2C device which is driven at a higher voltage than the Arduino it is connected to.

HI, sorry, my description may not have been clear and I am still learning how to do schematic and what tools to use. All I3Cs operate at 5v, just like Arduino. 12 volt only goes to Arduino (that is being stepped down with the Uno's built-in regulator to 5 volts). Relays, current readers and DACs interface with higher voltage but they operate on 5v.

6v6gt:
Disconnect all the pull-up resistors on the I2C devices (as you apparently have) and try adding say 4.7k pull-up (or maybe a bit stronger if necessary but not less than 1k) to the Arduino’s I2C lines. The internal pull-ups on the Arduino may not be sufficient with multiple I2C devices connected.

I just got this code running and really afraid to experiment since it took me about 2 full days to find the culprit - Like you said, I had disabled internal pull-ups on I2Cs and they worked fine but, once connected three of them, i started having problems. I guess the issue was exactly what you described (internal pull-ups not strong enough for 3 devices). After re-enabling pull up on one (out of three) I3C devices did the trick (probably same would happen if I just added the pull up per your suggestion).

Like i mentioned in the previous response, the only outstanding issue is with the weird relationship between the relay being powered and ability to upload a program.

thanks again.