I2C Two Way Communication Problems

Hello, I am trying to set up communication between multiple AT-Tiny devices (slaves) and an ESP32 (master.) The AT-Tiny controls a motor and reads its own I2C encoder (using a separate software I2C bus.) I have successfully gotten 2-way communication working when a single device is connected, however when multiple are connected and I attempt to send motor commands from the master to the slave, the data sent from the AT-Tiny breaks. The data values are usually between 0-10,000 however, when I send motor commands the values are in the millions and are very random. I have no idea what the problem is so any ideas would be very much appreciated.

Master

#include <Wire.h>

union Buffer
{
  long longNumber;
  byte longBytes[4];
};

//send
byte target[5];

//receive
byte motorPos[5];
int32_t encoder[5];

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

void loop()
{
  for (int i = 1; i < 6; i++)
  {
    setTarget(i, target[i - 1]);
    requestData(i);
  }

  for (int i = 0; i < 4; i++)
  {
    Serial.print(encoder[i]);
    Serial.print(",   ");
  }
  Serial.println(encoder[4]);

  if (Serial.available() > 0)
    target[0] = Serial.readString().toInt();
}

void requestData(int ADDRESS)
{
  Buffer buffer;

  Wire.requestFrom(ADDRESS, 5);    // request bytes from slave device #8

  if (Wire.available() > 4)   // slave may send less than requested
  {
    for (int i = 0; i < 4; i++)
      buffer.longBytes[i] = Wire.read();

    motorPos[ADDRESS - 1] = Wire.read();
    encoder[ADDRESS - 1] = (int32_t)buffer.longNumber;
  }
}

void setTarget(int ADDRESS, byte pos)
{
  int data = pos + 3;

  Wire.beginTransmission(ADDRESS);
  Wire.write(data);
  Wire.endTransmission();
}

Slave

#define F_CPU 8000000                          // clock speed: 8MHz (internal crystal)
#define SLAVE_ADRESS 0x05                         //Device adress: 0x08

#include "TinyWireS.h"                          // I2C library for ATtiny84A (and other older ATtinys)

#include <avr/interrupt.h>

AS5600 dEncoder;

const int
m1 = PA2,
m2 = PA3,
aEncoder = PA1;

volatile byte motorPos;
volatile byte targetPos;

long revolutions = 0;   // number of revolutions the encoder has made
double position = 0;    // the calculated value the encoder is at
double output;          // raw value from AS5600
long lastOutput;        // last output from AS5600

union Buffer
{
  long longNumber;
  byte longBytes[4];
};

Buffer buffer; // create an instance of Buffer

void setup()
{
  pinMode(m1, OUTPUT);
  pinMode(m2, OUTPUT);

  pinMode(aEncoder, INPUT);

  TinyWireS.begin(SLAVE_ADRESS);
  TinyWireS.onRequest(requestEvent);
  TinyWireS.onReceive(receiveEvent);

  output = dEncoder.getPosition();
  lastOutput = output;
  position = output;

  targetPos = -1;

  cli();
  PCMSK0 |= (1 << aEncoder);
  GIMSK |= (1 << PCIE0);
  sei();

}

void loop()
{
  output = dEncoder.getPosition();           // get the raw value of the encoder

  if ((lastOutput - output) > 2047 )        // check if a full rotation has been made
    revolutions++;
  if ((lastOutput - output) < -2047 )
    revolutions--;

  position = revolutions * 4096 + output;   // calculate the position the the encoder is at based off of the number of revolutions

  buffer.longNumber = position;

  lastOutput = output;                      // save the last raw value for the next loop

  if (targetPos > -1)
  {
    if (motorPos > targetPos)
      reverse();
    else if (motorPos < targetPos)
      forward();
    else brake();
  }
}

volatile bool spin;
unsigned long lastInterrupt;

ISR(PCINT0_vect)
{
  if (millis() - lastInterrupt > 1)
  {
    lastInterrupt = millis();

    if (spin)
      motorPos++;
    else motorPos--;
  }

  return;
}

void requestEvent()
{
  for (int i = 0; i < 4; i++)
    TinyWireS.write(buffer.longBytes[i]); // send requested bytes

  TinyWireS.write(motorPos);
}

void receiveEvent()
{
  byte mState;                         //0 reverse, 1 = brake, 2 = forwards

  if (TinyWireS.available() > 0)   // slave may send less than requested
    mState = TinyWireS.read(); // read motor state

  if (mState > 2)
    targetPos = mState - 3;
  else
  {
    targetPos = -1;

    if (mState == 0)
      reverse();

    if (mState == 1)
      brake();

    if (mState == 2)
      forward();
  }

}

every slaves must have different address

Sorry, I should have mentioned that. I changed the address for each separate device, this is just the code for one of the devices. Also, I can read data from each slave device with no issues so the addresses aren't the issue.

where are placed pullups? on each slaves?

Hi, @samlbaker
Welcome to the forum.

What is the distances that the I2C bus is working over.

Can you please post a circuit diagram of your project?

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

No, there is one 5k resistor for each sda and scl line.

i heard this library is not good. try TinyWire.h

The distance is only about 10 or 20 cm.

I tried that library but couldn't get it to work. Also, I don't need the master functionality and I want to keep the sketch size down with the AT-Tiny's limited memory.

UPDATE:

I figured out that the issue was that the motors were creating noise when powered on which caused the data to bug out. To fix this I added stronger pullups (3.3K) and insolation to the I2C lines. If you are having a similar problem I would test your setup with a separate power supply for your motors to make sure they aren't interfering with your I2C communication.

Thanks for all your help :slight_smile:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.