I2C communication goes randomly out of sync

All, suggestions please how I can figure this problem.

I have an ATtiny84 continuously monitoring some weather sensors. It is also an i2c slave, communicating the readings back to the master which happens to be a WeMos d1 mini, which is essentially an ESP8266-12E.

Every few seconds, the master requests the readings by sending a 1-byte code. The slave responds with 8 bytes of sensor data. This works well for anything from 30s to 1~2 mins before the problem occurs. Then the data gets messed up, and stays that way for a similar period, before either righting itself again, or getting even more messed up. Bytes in the data get shunted into the wrong sequence, as though one or more extra bytes have been inserted or lost

Some background: I have been using the WeMos very successfully with several other types of i2c sensor (Bmp180 etc) without and sign of this issue. The weather sensors are of a simple type, involving magnets & reed switches, so they are really just like push-buttons. I have tried 4K7 pull-ups on SDA & SCL, and without, it makes no difference. The i2c lines less than 100mm long.

Slave/tiny84 sketch:

/*
 * Weather Station i2c Slave
 * PaulRB
 * March 2016
 */

#include "TinyWireS.h"
//#include <avr/sleep.h>

#define I2C_SLAVE_ADDR  0x26
#define WIND_DIR_SENSOR   A3
#define WIND_SENSOR_ENABLE  1
#define WIND_SPEED_SENSOR  2
#define RAIN_SENSOR  0
#define LED_BUILTIN 8

long windDirTot;
int windDirCount;
int windDirPrev;
int windDir;
int rainRate;
int windSpeed;
int windSpeedCount = 0;
int windGustCount = 0;
int prevWindSpeedSensor = HIGH;
int prevRainSensor = HIGH;
unsigned long maxWindGust;
unsigned long lastWindGustTime;
unsigned long lastWindDirTime;

int rainCount;

unsigned long lastReportTime;

int readingNow;

const int reading[] = {158, 183, 221, 297, 387, 459, 556, 652, 741, 812, 850, 904, 945, 975,  985, 1023};
const int compass[] = {292, 247, 270, 337, 315,  22,   0, 202, 225,  67,  45, 157, 180, 112,  135,   90};

void setup() {

  //bitSet(PRR, PRTIM1);  // power-off Timer 1
  //set_sleep_mode(SLEEP_MODE_IDLE);

  pinMode(WIND_SENSOR_ENABLE, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  TinyWireS.begin(I2C_SLAVE_ADDR);

}

void loop() {

  unsigned long timeNow = millis();

  // Check for request from i2c master
  if (TinyWireS.available() > 0) {
    // Read the request, discard
    while (TinyWireS.available() > 0) TinyWireS.receive();
    // Send all sensor data to master
    TinyWireS.send(highByte(windDir));
    TinyWireS.send(lowByte(windDir));
    TinyWireS.send(highByte(windSpeed));
    TinyWireS.send(lowByte(windSpeed));
    TinyWireS.send(highByte(maxWindGust));
    TinyWireS.send(lowByte(maxWindGust));
    TinyWireS.send(highByte(rainRate));
    TinyWireS.send(lowByte(rainRate));
  }

  // Check windspeed sensor
  pinMode(WIND_SPEED_SENSOR, INPUT_PULLUP);
  if (digitalRead(WIND_SPEED_SENSOR) != prevWindSpeedSensor) {
    windSpeedCount++;
    windGustCount++;
    prevWindSpeedSensor = !prevWindSpeedSensor;
  }
  pinMode(WIND_SPEED_SENSOR, INPUT);

  // Check rain sensor
  pinMode(RAIN_SENSOR, INPUT_PULLUP);
  if (digitalRead(RAIN_SENSOR) != prevRainSensor) {
    rainCount++;
    prevWindSpeedSensor = !prevWindSpeedSensor;
  }
  pinMode(RAIN_SENSOR, INPUT);

  // Time to check for wind gust?
  unsigned long windGustPeriod = timeNow - lastWindGustTime;
  if (windGustPeriod >= 3000) {
    //digitalWrite(LED_BUILTIN, HIGH);
    lastWindGustTime += 3000;
    // Calculate wind gust speed, in Km/hr, over period of 6s
    // Note: 1 rotation per second = 2.4 Km/hr
    // 2 rising edges per rotation, so 1 rising edge per millisecond = 1,200 Km/hr
    unsigned long windGustSpeed;
    if (windGustPeriod > 0) windGustSpeed = 1200UL * windGustCount / windGustPeriod; else windGustSpeed = 0;
    // New highest wind gust?
    if (windGustSpeed > maxWindGust) maxWindGust = windGustSpeed;
    // Zero gust count for next period
    windGustCount = 0;
    //digitalWrite(LED_BUILTIN, LOW);
  }

  // Time to check wind direction sensor?
  if (timeNow - lastWindDirTime >= 100) {
    lastWindDirTime += 100;

    // Enable sensor only to take reading. Reduces current
    bitClear(PRR, PRADC);  // power-on ADC
    bitSet(ADCSRA, ADEN); // Enable ADC
    digitalWrite(WIND_SENSOR_ENABLE, HIGH);
    readingNow = analogRead(WIND_DIR_SENSOR);
    digitalWrite(WIND_SENSOR_ENABLE, LOW);
    bitClear(ADCSRA, ADEN); // Disable ADC
    bitSet(PRR, PRADC);  // power-off ADC
    // Translate analog reading into compass direction
    // Can be one of 16 values, but there could be some
    // analog noise, so the reading[] array contains values
    // mid-way between the possible readings.
    int i;
    for (i = 0; i < 16 && readingNow >= reading[i]; i++);
    int windDirNow = compass[i];
    // Check if sensor has swept through 0 degrees, moving in either direction
    if (windDirNow - windDirPrev > 180) windDirNow -= 360;
    if (windDirPrev - windDirNow > 180) windDirNow += 360;
    // Update total and count of data points for calculating average
    windDirTot += windDirNow;
    windDirCount++;
    windDirPrev = windDirNow;
  }

  // Time to update the report?
  unsigned long reportPeriod = timeNow - lastReportTime;
  if (reportPeriod >= 30000UL) {
    // Calculate time since last report was sent
    lastReportTime += 30000UL;

    // Calculate average wind direction over the reporting period
    if (windDirCount > 0) windDir = windDirTot / windDirCount; else windDir = 0;
    while (windDir >= 360) windDir -= 360;
    while (windDir < 0) windDir += 360;
    // Zero the count & total for next period
    windDirTot = 0;
    windDirCount = 0;

    // Calcualate average wind speed, in Km/hr, over reporting period
    // Note: 1 rotation per second = 2.4 Km/hr
    // 2 rising edges per rotation, so 1 rising edge per millisecond = 1,200 Km/hr
    if (reportPeriod > 0) windSpeed = 1200UL * windSpeedCount / reportPeriod; else windSpeed = 0;
    // Zero wind speed sensor count for next period
    windSpeedCount = 0;

    // Report highest wind gust speed, in Km/hr, over reporting period
    // Zero max wind gust for next reporting period
    maxWindGust = 0;

    // Report rainfall rate in mm/hr
    // One count = 0.011" of rain
    // One count per millisecond = 0.011 * 25.4 * 1000 * 60 * 60 = 1005840mm/hr
    if (reportPeriod > 0) rainRate = 1005840UL * rainCount / reportPeriod; else rainRate = 0;
    // Zero rain sensor count for next period
    rainCount = 0;

  }

  //sleep_enable();
  //sleep_mode();                        // System actually sleeps here
  //sleep_disable();                     // System continues execution here

}

Master/WeMos sketch:

#include <Wire.h>

void setup()
{
  Wire.begin();
  Serial.begin(115200);
}

void loop()
{
  Serial.println("Sending...");
  Wire.beginTransmission(0x26);
  Wire.write(1);
  Wire.endTransmission();
  Serial.println("Requesting...");
  Wire.requestFrom(0x26, 8);
  while (Wire.available() < 8) {
    delay(100);
  }

  int windDir = word(Wire.read(), Wire.read());
  Serial.print("WindDir=");
  Serial.print(windDir);

  int windSpeed = word(Wire.read(), Wire.read());
  Serial.print(" WindSpeed=");
  Serial.print(windSpeed);

  int windGust = word(Wire.read(), Wire.read());
  Serial.print(" WindGust=");
  Serial.print(windGust);

  int rain = word(Wire.read(), Wire.read());
  Serial.print(" rain=");
  Serial.print(rain);

  Serial.println();

  delay(5000);
}

Sample output:

Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=0 rain=0
Sending...
Requesting...
WindDir=158 WindSpeed=0 WindGust=128 rain=65535  <--- FIRST PROBLEM HERE
Sending...
Requesting...
WindDir=0 WindSpeed=158 WindGust=0 rain=0  <--- 158 FIGURE NOW IN WRONG POSITION
Sending...
Requesting...
WindDir=0 WindSpeed=158 WindGust=0 rain=0
Sending...
Requesting...
WindDir=0 WindSpeed=158 WindGust=0 rain=0
Sending...
Requesting...
WindDir=0 WindSpeed=158 WindGust=0 rain=0
Sending...
Requesting...
WindDir=0 WindSpeed=158 WindGust=0 rain=33023  <--- ANOTHER ISSUE HAPPENS
Sending...
Requesting...
WindDir=0 WindSpeed=0 WindGust=40448 rain=0 <-- BYTES IN INCORRECT SEQUENCE (158*256=40448)
Sending...
Requesting...
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
Sending...
Requesting...
WindDir=0 WindSpeed=0 WindGust=40448 rain=0

Any thought would be appreciated. Any questions, just ask.

Paul

From a link to a WeMos d1 mini, it says:
● 11 digital input/output pins, all pins have interrupt/pwm/I2C/one-wire supported(except D0)

D0 just happens to be the pin you're having trouble with.
Is it also being used fore something else?
Is there a schematic and/or pinout diagram for this?

I have tried 4K7 pull-ups on SDA & SCL, and without, it makes no difference. The i2c lines less than 100mm long.

What if you tried a 150-470 ohm series resistor for SDA & SCL at the Master WeMos? (keep the pullups)

Hi @dlloyd, thanks for the response.

No, I'm using D1& D2. They are the default i2c pins on the Wemos. You can use other pins but then you have to go editing the guts of the libraries you are using. D0 is not connected at the moment. It will be used in the final circuit for waking the chip from deep sleep, but for now this is a test sketch and it is not sleeping.

I can post a schematic later, perhaps tomorrow. Its all in my head right now, being so simple (the schematic, I mean

Update: I replaced the Wemos/ESP-12E (i2c master) with a Nano 3. It runs perfectly.

Output with Wemos/ESP:

WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=33023 rain=65535
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40192 rain=0
WindDir=0 WindSpeed=0 WindGust=40192 rain=0
WindDir=0 WindSpeed=0 WindGust=40192 rain=0
WindDir=0 WindSpeed=0 WindGust=40192 rain=0
WindDir=0 WindSpeed=0 WindGust=40192 rain=0
WindDir=0 WindSpeed=0 WindGust=40192 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=0
WindDir=0 WindSpeed=0 WindGust=40448 rain=33023
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0
WindDir=0 WindSpeed=0 WindGust=158 rain=0

With Nano:

WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0
WindDir=158 WindSpeed=0 WindGust=0 rain=0

Problem is, I need it to run on the Wemos/ESP! Any ideas anyone?

The clock speed of the Wemos/ESP-12E is 80MHz/160MHz. I'm thinking that this makes the I2C more susceptible to ringing and RFI on the signal. Even though you may only be using I2C at 400kHz, the MCU can respond much, much faster. Typically a series resistor is used to reduce this and improve noise immunity.

Have you tried a series resistor for SDA & SCL and that failed?
It did work here on the 84MHz Due.

No, never heard of that solution. Thanks, will give it a go. Does it matter if i put the series resistor on the esp side or the tiny84 side (assuming my 4K7 pullups are in the middle)?

PS just noticed you updated your earlier post with this suggestion...

I would try them at the esp side, as close to the pins as possible.

OK... 330R in series with esp pins, 4K7 pull-ups to 3.3V. Rats! It's still happening.

Thanks for that suggestion anyway. Had my hopes up for a few minutes there!

Just to clarify, the issue never appears identically-exactly the same twice, but always along very similar lines to the example outputs I have posted above.

Also, I don't know for sure what speed the i2c is. I had assumed that the esp version of Wire library would default to the same speed as an Uno, i.e. 100KHz. But maybe it isn't, and the tiny84 is struggling to keep up. Having said that, I have tried the tiny at 8MHz and it did not sort out the issue. Still, a theory to investigate.

I should also mention, in case anyone picks me up on it, that when I tried the Nano 3 as i2 master, I had to amend the master's sketch slightly. Reason was, these lines:

  int windDir = word(Wire.read(), Wire.read());

no longer executed as I wanted them to. It was because there is no guarantee which order the two bytes from the two calls of Wire.read() get given to the word() function. Turns out they happen in the opposite order for a sketch compiled for esp versus avr. I should not have relied on either order. So the master's sketch is now:

#include <Wire.h>

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Starting...");
}

int readWord() {
  byte hi = Wire.read();
  byte lo = Wire.read();
  return word(hi, lo);
}

void loop()
{
  //Serial.println("Sending...");
  Wire.beginTransmission(0x26);
  Wire.write(1);
  Wire.endTransmission();
  //Serial.println("Requesting...");
  Wire.requestFrom(0x26, 8);
  while (Wire.available() < 8) {
    delay(100);
  }

  int windDir = readWord();
  Serial.print("WindDir=");
  Serial.print(windDir);

  int windSpeed = readWord();
  Serial.print(" WindSpeed=");
  Serial.print(windSpeed);

  int windGust = readWord();
  Serial.print(" WindGust=");
  Serial.print(windGust);

  int rain = readWord();
  Serial.print(" rain=");
  Serial.print(rain);

  Serial.println();

  delay(5000);
}

Was hoping that would work. I guess there could possibly be noise issues on the power rails. In that case, additional filtering or using a ferrite core could help.

Oh, back to the I2C ... have you tried stronger pullup resistors? I haven't checked the pin ratings, but maybe something in the neighbourhood of 2.2K would make a difference (depending on max current rating of pin). Also, try shorter wire lengths if possible.

Hi everyone,
I am working on a small project which contains an Arduino Mega and 3 Arduino Nano. some switches and LED are connected to each board. Master ( Arduino Mega) should read the data from slaves( Arduino Nano), and then write the appropriate code on the assigned slave.my question is, how can I write a program which is first read data from slaves and then write data on them, Just with a 2- wire serial connection. Can you please help me? Is it a pragmatic project?

Hello nimadesert. Please start your own thread for your question. It is considered rude to hijack someone else's thread like this.

Paul