Thanks for the responses so far - luckily I can edit the title because it turns out I was fooled by randomness and analogRead() has nothing to do with the problem, it perhaps just made it more likely to occur due to timing changes.
I managed to reproduce it with a bare minimum sketch:
#include <Wire.h>
#include <RTClib.h>
void setup() {
Serial.begin(9600);
while (!Serial);
Wire.begin();
}
void loop() {
Wire.beginTransmission(0x68);
uint8_t zero = 0;
if (Wire.write(&zero, 1) != 1) {
Serial.println("write() failed!");
while(true);
}
uint8_t result = Wire.endTransmission();
if (result != 0) {
Serial.print("endTransmission() failed with code ");
Serial.println(result, DEC);
while(true);
}
}
Post the exact processor, board, wiring, and an annotated schematic if you need a specific answer.
Components involved:
I am aware that those are cheap modules possibly prone to defects but what are the odds that both my DS3231 and DS1307 module show exactly the same behaviour?
I have also swapped the Arduino for a brand new one and observe the same behaviour. It doesn't matter if I hook up the modules via Qwiic connector or SDA/SCL, same issue. I usually have only one attached, doesn't matter if on Wire or Wire1, same issue.
One thing I notice is that it used to run for weeks without problems at first but now I can reproduce the problem within 5 minutes max, so it appears to be gradually getting worse.
I never use analogRead() on the I²C pins after the I²C interface has been initialized
I am using SDA/SCL for I2C and A0/A1 for analog input measurements, but that's moot now anyway.
A clean power supply is also critical on many of the I2C parts.
It happens when I provide power via USB cable from my PC and also when I provide power via a 12V Meanwell PSU.
what I2C device and are you using a library or have you implemented the I2C code yourself?
I use the official Wire class.
Who is holding the line low, the master or the slave? If you disable the I2C after the fault occurs does the line return high?
I have tested this by unplugging SDA from the peripheral during the error condition, connected it to VCC of the Arduino with a 3.3k Ohm resistor in between and did measure it HIGH, suggesting that it's the module that pulls it LOW. It was a bit of fiddling and I might have disturbed VCC/SCL so I don't know if that caused an I2C reset internally and if my measurement was actually probing the fault condition or its recovery.
But I will do this again with a better setup and also check on the module's end.
Looking at the waveform and the bit pattern, the only realistic scenario I see in case the module pulls it LOW is that it does so since the previous ACK, got stuck there and it's only apparent at the next ACK because the bits in between were all coincidentally zero.
There is one interesting measurement I took with a module on Wire1 and another I2C peripheral on Wire2:
Notice the gap (about 65ms) on Wire1 right when the fault on Wire occurs. No such gap was present earlier in the recording. First I thought that would speak against the peripheral being the culprit because why would it influence the other Wire1 like this. But then I thought the opposite: Arduino is likely busy-waiting until a timeout and unable to service Wire1 during that time.