Blocking I2C and ADS1115

I’m making a info control center for my travel trailer RV. This involves a Nextion HMI Display wired to an Arduino Mega 2560 which is wired to various input/output modules. On my I2C bus, I have an RTC(real time clock) and two ADCs (ADS1115) for reading current and voltage from my MPPT (Solar regulator).

When I read my ADS1115s the Mega sometimes locks up, randomly, sometimes after 2 minutes (about 120 loops of code) or after 15 mins or after an hour or 24 hours. I know where my Mega locks up because I get it to send location code to the Nextion so I can debug where it was last running smoothly.

I have been researching non-blocking libraries for I2C and switched from the Adafruit_ADS1015.h to the one by jrowberg (#include <I2Cdev.h> and #include <ADS1115.h>), hoping that would be able to handle errors by skipping over them involving a timeout.

My I2C wiring is too long (please work with me) at 2 ft (60cm), and before I mount an Arduino Nano inside the MPPT (adding complexity, but shortening the I2C bus, and sending serial to the Mega) I thought I was ready to ask the forum about some work around for my existing hardware setup.

To describe my hardware, I have four wires from the Mega to the MPPT (switched 5V, SCL, SDA, Gnd). I have since removed the pull up resistors from both of the ADS1115s (though the problem existed before I removed the pullups). The RTC (mounted at the Mega) provides my pull-up voltage. I use the ADS1115s in the differential mode which limits me in searching for non-blocking libraries, since I depend on finding libraries with examples that request differential voltages.

I know you’d like to see the full code, but the forum complains when I’ve exceeded 9000 characters so hopefully my file is attached.

One relevant section of my code is the subroutine called MPPT:

void MPPT() {
  MPPTtchControl =;                    //if 605 = 0 = off, 1 = auto 2, = forced on
  if ((MPPTtchControl == 1 && chargeEnable == HIGH && dayTIME == HIGH) || MPPTtchControl == 2) { //2 is override chargeEnable and dayTime
    digitalWrite(8, LOW);                           //MPPT turns on = sets the digital pin 8 on
    MPPTvoltageADS1115.setGain(ADS1115_PGA_6P144);   // 6.144V
    pvVolts = MPPTvoltageADS1115.getConversionP0N1() / 480.0;   //decimals are needed & use pins P0="+",N1="-" through voltage divider "/4"
    MPPTcurrentADS1115.setGain(ADS1115_PGA_0P256);   // 0.256V
    pvAmps = MPPTcurrentADS1115.getConversionP0N1() / 185.0;
    mpptAmps = MPPTcurrentADS1115.getConversionP2N3() / 187;
    mpptWatts = mpptAmps * voltage;
  }                //create Watts float for Amp-hours.
  else {
    digitalWrite(8, HIGH); //turns off MPPT controller

Is there a better library? Or a non-blocking millis() routine, or a “while” loop that I can make? I don’t need to have the sample work out every second. Any time I make some request of the ADS1115 a condition, the Mega might halt at the MPPT subroutine, and I am stuck.

Thanks for your help.

sketch_aug30a.ino (31.7 KB)

sketch_aug30a.ino (31.7 KB)

Do not use the i2cdevlib, it makes things worse: Wrong use of Wire library · Issue #265 · jrowberg/i2cdevlib · GitHub

Can you show a photo of the SDA and SCL wires ? Keep them as seperate wires. Crosstalk between them is a bigger problem than capacitance to ground.
The Wire.setClock() can be used to lower the speed to, for example, 50kHz.

There are many Wire libraries with timeouts, but the official Wire library has bugs removed and I don't know of all those libraries with timeout are still up to date.

What about ground currents or electrical noise that influences the I2C bus ?

Can you calculate the total pullup value ?
You can also measure it. Make a sketch with only a Wire.begin() in setup() and a empty loop(). Let it run and measure the shortcut current from SDA to GND and from SCL to GND.

You use the String object a lot and also dtostrf() with buffers. I see about 20 places that are not very safe.
The width for dstostrf() is a minimal width.
When there was something wrong and the value of the float variable is "123456.789" then memory is overwritten. Such a simple bug could crash the complete sketch.
Using the String object for AVR microcontrollers could cause a memory crash by a fragmented heap.

You have written your own code to read data from the display. That is okay, it can't be worse than the nexLoop() from the ITEAD library.
However, can't you do better than using a delay(10) ?

How is your ram usage ? You could put some texts in flash memory.

Thanks Koepel for that comprehensive reply. You’ve given me a lot to study and I am still such a newbie.
For example, all that String stuff compared to string, I know that I am absolutely supposed to avoid Strings, everyone on every forum says that, but for newbies, Strings are far easier to understand and work with and edit. Learning char C strings is way above my head, but I think I’ll end up learning it in the long run.

Any way, my arduino Mega has it’s SCL and SDA pull-up resistors at 10k. The RTC DS3231 still has it’s pullups, also at 10k each. I guess that’s why I see 1mA shorting SDA/SCA to ground. 5V/5k=1mA. I’ve attached a picture of my 2 foot I2C run, by loosening up the MPPT regulator, for the picture. I know it’s a EMI noisy environment, and it’s unshielded, but I have no ground loops at least, because it’s isolated.

But your suggestion of Wire.setclock(10000) gave me knew hope. Already after a few hours of running, I’m seeing more stability than before. Slow is good. I have no need for speed.

Arduino Mega has internal 50k + onboard 10k, the RTC has 10k.
50k // 10k // 10k = 4.5k
5V / 4.5k = 1.1 mA

You can go up to 3 mA.
A lower impedance of the I2C bus reduces electrical noise.
Try to add extra pullup resistors of 3k3 or 4k7 to 5V.

I don't know if the 10kHz for the I2C bus will work. The Wire.setClock() calls twi_setFrequency() and that function does not check the limits. I know 50 kHz works.

You could try this: MultiSpeed I2C Scanner - 50,100,200,400 KHz. - Libraries - Arduino Forum.
If it works at 400kHz then 100kHz is good and reliable.

Can you have a good look at your sketch and the buffers ? If there is a possibility that it can go wrong, then some day it will go wrong. I use a single 'currentMillis' and often a single global buffer that I use for all the conversions.