Interference with Tiny RTC DS1307 and Analogwrite

Hello Everyone,

I am experiencing some issues with using PWM at the same time as receiving data from my DS1307 time crystal. I have had success with wiring my RGB led strips which use 3 mosfets and 3 pwm channels 3,5 and 6. Unfortunately when I try to incorporate the time of day this code, the timing has glitches in it like this:

11/1/2016 21:22:51
11/1/2016 21:22:51
11/1/2016 21:22:51
11/1/2016 21:22:51
165/165/2165 165:165:43
11/1/2016 21:22:51
11/1/2016 21:22:52
11/1/2016 21:22:52

This only happens when I have the analogwrite() code present. I am thinking there may be an interference with the two features. Any help would be great. Currently relying on a 10 second delay between the two parts of code (timing vs writing to the led strips) But I don’t really like this solution.

Please see attached code.

Thank you, Michael

First_Test_Light.ino (5.88 KB)

I'm not sure what the conflict is, but I don't see any reason you need to read the RTC each pass through loop as I'm not sure what you time to less than the minute.

You may do better to use the time library for obtaining all your time values, and sync every 5 minutes or so with the RTC. See the time library example "TimeRTC" which synchronizes the millis() timer with the RTC.

I would think that there is a bug in the RTClib. It requests 7 bytes from the RTC and reads them without checking if bytes are available to be read.

DateTime RTC_DS1307::now() {
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire._I2C_WRITE((byte)0);	
  Wire.endTransmission();

  Wire.requestFrom(DS1307_ADDRESS, 7);
  uint8_t ss = bcd2bin(Wire._I2C_READ() & 0x7F);
  uint8_t mm = bcd2bin(Wire._I2C_READ());
  uint8_t hh = bcd2bin(Wire._I2C_READ());
  Wire._I2C_READ();
  uint8_t d = bcd2bin(Wire._I2C_READ());
  uint8_t m = bcd2bin(Wire._I2C_READ());
  uint16_t y = bcd2bin(Wire._I2C_READ()) + 2000;
  
  return DateTime (y, m, d, hh, mm, ss);
}

If I compare it with the DS1307 library

  // request the 7 data fields   (secs, min, hr, dow, date, mth, yr)
  Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields);
  if (Wire.available() < tmNbrFields) return false;

  // read here
  ...
  ...

The if statement checks of there are 7 (I suspect, did not look at the TimeLib library) bytes available.

So I suspect the RTClib code to read rubbish and that is what you see. You can modify the DS1307::now() method; I do not know if the below will work but it’s worth a test.

DateTime RTC_DS1307::now() {
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire._I2C_WRITE((byte)0);	
  Wire.endTransmission();

  Wire.requestFrom(DS1307_ADDRESS, 7);

  // MODIFICATION START
  if (Wire.available() < 7)
    return NULL;
  // MODIFCATION END

  uint8_t ss = bcd2bin(Wire._I2C_READ() & 0x7F);
  uint8_t mm = bcd2bin(Wire._I2C_READ());
  uint8_t hh = bcd2bin(Wire._I2C_READ());
  Wire._I2C_READ();
  uint8_t d = bcd2bin(Wire._I2C_READ());
  uint8_t m = bcd2bin(Wire._I2C_READ());
  uint16_t y = bcd2bin(Wire._I2C_READ()) + 2000;
  
  return DateTime (y, m, d, hh, mm, ss);
}

and in loop()

void loop()
{
  DateTime now = RTC.now();

  if(now == NULL)
  {
    Serial.println("Error reading RTC");
  }

  // do whatever you want to do if no valid time was read
}

It’s a simple modification that can easily be reversed. I think I would take it a step further if needed and implement it differently (using a while and a timeout or using a statemachine).