I2C Clock Stretching

Hi, I have a pH circuit, it has an ATtiny85 and communicates to a master using I2C.

I connected it to a Raspberry Pi but there were random parse errors, turns out to be caused from clock stretching and Pi’s don’t like that, here’s an image from the analzyer.


If I slow the bus speed to 10kbit/s on the Pi the stretching stops and errors go away but I don’t want to slow it down this much.

I’ve tried the Wire and multiple TinyWire libraries, all work but same issue.

Is there something I can change on the ATtiny85 side so the clock stretching doesn’t happen.

This is the code.

#include <Wire.h>
#include <EEPROM.h>

#define PROBE_IN A3 //57   // anlaog a3  - green wire
#define PROBE_REF A2 //56   // anlaog a2  - white wire

byte ver;  // eeprom 0
byte i2cAdd = 99;  // eeprom 1

double pH;
float mVPerPH;  // calibrated mV per pH without temperature compensation

byte in_char = 0;
char inData[30];
byte w = 0;                      //counter used for ph_data array.
char computerdata[31];           //we make a 20 byte character array to hold incoming data from a pc/mac/other.

void setup()
{
  byte z = 255;
  EEPROM.get(50, z);
  
  if (z == 255)    //  Set Defaults
  {  
    EEPROM.put(0, 1);  // version
    EEPROM.put(1, 99);  // I2C address - 40, 41, 42, 43, 44, 45, 46, 47
    EEPROM.put(28, 25.0);  // calibration temperature
    EEPROM.put(50, 1);  // version
  }
  pinMode(1, OUTPUT);
  digitalWrite(1, HIGH);
  
  EEPROM.get(0, ver);
  EEPROM.get(1, i2cAdd);
  measuremV();
  Wire.begin(i2cAdd);
//  Wire.setClock(400000);
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent); // register event
}
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
unsigned long timer = 1000;
void loop()
{
  currentMillis = millis();
  if ((currentMillis - previousMillis) >= 3000) 
  {
    measuremV();
    previousMillis = millis();
  }
}
void measuremV() 
{
  double differentialmV;
  float  probeIn, probeRef, inputV, adcResolution;
  analogReference(DEFAULT);
  inputV = 3.36;  // reference voltage
  probeIn  = ((readADC(PROBE_IN) * inputV) / 1024.0);
  probeRef = ((readADC(PROBE_REF) * inputV) / 1024.0);
  differentialmV = (probeIn - probeRef);

  if (differentialmV <= -1.1)
  {
    analogReference(INTERNAL);
    adcResolution = 2.56;
  }
  else if (differentialmV <= -0)
  {
    analogReference(INTERNAL);  // set to INTERNAL for ATtiny
    adcResolution = 1.1;
  }
  else if (differentialmV <= 1.1)
  {
    analogReference(INTERNAL);
    adcResolution = 1.1;
  }
  else if (differentialmV <= 2.56)
  {
    analogReference(INTERNAL);
    adcResolution = 2.56;
  }

  // take a reading
  probeIn  = ((readADC(PROBE_IN) * adcResolution) / 1024.0);
  probeRef = ((readADC(PROBE_REF) * adcResolution) / 1024.0);

  // turn V into mV  
  differentialmV = (probeIn - probeRef) * 1000;
  mVPerPH = 59.16;

  // convert to pH
  pH = fabs(7.0 - (differentialmV / 59.16));
  dtostrf(pH, 0, 3, computerdata);  // convert float to char array so it can be sent over I2C
}
void receiveEvent(uint8_t howMany)
{
  while (Wire.available()) {    // are there bytes to receive.
    in_char = Wire.read();      // receive a byte.
    inData[w] = in_char;            // load this byte into our array.
    w += 1;                         // incur the counter for the array element.
    if (in_char == 0) {             // if we see that we have been sent a null command.
      w = 0;                        // reset the counter w to 0.
      digitalWrite(1, LOW);         // LED on circuit board
      break;                        // exit the while loop.
    }
  }
}
void requestEvent()
{
  if ((inData[0] == 'R'))           // request from reef-pi is pending
  {
    inData[0] = 0;                  // reset for next reading
    Wire.write(1);               // send byte 1
    Wire.write(computerdata,30);
    digitalWrite(1, HIGH);          // LED on circuit board
  }
}
double readADC(int channel)
{
  uint32_t total = 0UL;
  uint16_t sampleCount = 4096;
  for (uint16_t i = 0; i < sampleCount; i++) {total += analogRead(channel);}
  total = total >> 6;
  double proportional = (total * 1.0) / (0b00000001 << 6);
  return proportional;
}

Thanks

isn't clock stretching an I2C mechanism that gives the I2C device a chance to complete the operation before responding?

if the Pi code is not tolerating the stretching, it's not I2C compliant

It's sounds like it's an issue on the Pi4, was an issue on Pi3 but apparently it's fixed. Was hoping to do something so this didn't stretch the clock.