Comunicação I2C em Arduino

A comunicação de dados por I2C tem caracteristicas que o tornam muito usado na comunicação com vários dispositivos electrónicos usados nos projectos com microcontroladores, incluindo o Arduino.

Do ponto de vista das ligações o interface I2C é simples e facil de implementar.  Para funcionar, o I2C precisa apenas de duas linhas (dois pinos no Arduino) a SDA e a SCL, que são o bus do I2C, e duas resistências a fazer de pull up nas linhas,  ás  quais se podem ligar vários dispositivos com interface I2C.

No caso dos Arduinos, como estes pinos já tem os pull up internamente, não é necessário colocar as resistencias entre as linhas e o vcc.  No entanto, como todos os Arduinos tem esses pull ups, pelo que percebi, significa que o número de Arduinos possiveis de ligar é mais limitado que o maximo teórico.

Os dispositivos ligados ao bus I2C podem ser de dois tipos diferentes:

  • Master
  • Slave

A principal e mais notória diferença é que só os masters podem iniciar comunicações.

O bus I2C do Arduino pode ser explorado nos seguintes ambientes:

  • Unico master i2C
  • Multiplos masters no bus I2C
  • Multiplos Bus I2C

De todos os ambientes o menos problemático é o ambiente de unico master.

Estou a efectuar vários testes relacionados com a exploração estável das comunicações I2C.

Nota: Só transmite bytes e um limite de 32 bytes por comunicação.

Bibliotecas

Lista de bibliotecas
https://github.com/Testato/SoftwareWire/wiki/Arduino-I2C-libraries

Wire
https://www.arduino.cc/en/Reference/Wire

Alternativas (apenas master)

SoftI2CMaster & SlowSoftI2CMaster
https://playground.arduino.cc/Main/SoftwareI2CLibrary

https://github.com/felias-fogg/SoftI2CMaster
https://github.com/felias-fogg/SlowSoftI2CMaster

Alternativas (slave)

https://github.com/cirthix/SoftIIC
https://github.com/rambo/TinyWire

 

Formas de passar os dados numéricos por I2C

Como caracteres ascii

Passar os dados numéricos como strings. Implica a conversão do formato numérico para string.

Forma encontrada no seguinte link:

https://arduino.stackexchange.com/questions/16292/sending-and-receiving-different-types-of-data-via-i2c-in-arduino

Master

//master
#include <Wire.h>

char t[10]={};//empty array where to put the numbers comming from the slave
volatile int Val; // varaible used by the master to sent data to the slave

void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
}

void loop() {
Wire.requestFrom(8, 3); // request 3 bytes from slave device #8

//gathers data comming from slave
int i=0; //counter for each bite as it arrives
while (Wire.available()) { 
t[i] = Wire.read(); // every character that arrives it put in order in the empty array "t"
i=i+1;
}

Serial.println(t); //shows the data in the array t
delay(500); //give some time to relax

// send data to slave. here I am just sending the number 2
Val=2;
Wire.beginTransmission (8);
Wire.write (Val);
Wire.endTransmission ();
}

Slave

#include <Wire.h>

char t[10]; //empty array where to put the numbers going to the master
volatile int Val; // variable used by the master to sent data to the slave

void setup() {
Wire.begin(8); // Slave id #8
Wire.onRequest(requestEvent); // fucntion to run when asking for data
Wire.onReceive(receiveEvent); // what to do when receiving data
Serial.begin(9600); // serial for displaying data on your screen
}

void loop() {
int aRead = analogRead(A0); //plug a potentiometer or a resistor to pin A0, so you can see data being transfer
float x = aRead/1024.0*5.0; //generate a float number, with this method you can use any time of data pretty much

dtostrf(x, 3, 2, t); //convers the float or integer to a string. (floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, empty array);
Serial.println(Val); // print the character
delay(500);
}

// function: what to do when asked for data
void requestEvent() {
Wire.write(t); 
}

// what to do when receiving data from master
void receiveEvent(int howMany)
{Val = Wire.read();}

 

Como bytes

Passar os dados numéricos como bytes. Implica a reconstituição dos tipos com uma union no receptor.

Forma encontrada no seguinte link:

https://forum.arduino.cc/index.php?topic=45445.0

Master

// master reader
byte i2c_receive[32];
int i = 0;

// I2C limit write to 32 bytes
Wire.requestFrom(8, 32); // request 6 bytes from slave device #8
while (Wire.available()) { // slave may send less than requested
char c = Wire.read(); // receive a byte as character
i2c_receive[i] = c;
i++;
}

union u_tag {
byte b[4];
double d_val;
long l_val;
} u[11];

for(i=0; i < 8; i++) {
u[i].b[0] = i2c_receive[i*4 + 0];
u[i].b[1] = i2c_receive[i*4 + 1];
u[i].b[2] = i2c_receive[i*4 + 2];
u[i].b[3] = i2c_receive[i*4 + 3];
}

Serial.print(u[0].l_val); Serial.println("\t");
[...]
Serial.print(u[7].d_val); Serial.println("\t");

 

Slave

Aproveitando ao maximo o limite de 32 bytes enviamos 8 variaveis, double ou long, o que prefaz 8 vezes 4 bytes.

 // I2C limit write to 32 bytes
Wire.write((byte*)&bodyEncoderLeftTotalPulses, 4);
Wire.write((byte*)&bodyEncoderRightTotalPulses, 4);
Wire.write((byte*)&bodySpeedPidSetPoint, 4);
Wire.write((byte*)&bodySpeedPidInput, 4);
Wire.write((byte*)&bodySpeedPidOutput, 4);
Wire.write((byte*)&bodySteeringPidInput, 4);
Wire.write((byte*)&bodySteeringPidOutput, 4);
Wire.write((byte*)&bodyMoveDistanceRemain, 4);

O slave pode enviar menos bytes que o requerido pelo master.