I2C Protocol - Wire library / Can one slave act as writer and sender to Master?

Dear readers,

I am learning how to use the wire library to communicate between several Arduino UNO through I2C bus.

I understand that Arduino UNO set as Master in a sketch can send data to a slave and receive from a slave.

My question is whether a slave can also act as "writer and sender" or only as "writer or sender".

All the manuals I have read and slave examples I have found refer to slave as writer or slave as sender but I haven´t found any information or example that shows as slave acting as both. As I also haven´t found this question in the forums, I wanted to ask before letting my common sense win assuming that its not possible :slight_smile:

I have built a slave skecth that picks up a value "1" or "0" from the master. I would like the slave sketch to read the data sent by the master and switch a LED on/off a depending on the incoming value. Additionally the slave skecth has an input pin connected to a temperature sensor and my idea is that the temperature is read in the slave and sent to the master; then the master will send "1" or "0" to the slave dependeing on the temperature value.

This is just a small project with 2 sketches ( master & slave) to test how I2C works. Eventually, there would be 1 slave sending data to the master + 1 slave receiving from the master and sending to it + 1 master deciding what to send to the slaves depending on what it receives from them.

But the slave code is not working and I think it´s because of this:

void setup()
{
  
  pinMode (led, OUTPUT );
  Wire.begin(2);                // join i2c bus with address #2
  Wire.onRequest(requestEvent); // register event
  Wire.onReceive(receiveEvent); // receive event
  
  Serial.begin(9600);  // start serial for output

  
}

If I comment out the whole code of "requestEvent" , the code for "receiveEvent" works, and viceversa; but not both in the same sketch. This is another strong omen that is leading me to finally admit that a slave can only go in either directions but not both at the same time... but as I am very new to I2C I thought someone might help me discover that there is a possiblity of determining within the sketch if the slave is sending or receiving data

... or maybe someone may just confirm my suspicion and help me move on and work around it! :sweat_smile:

Thank you very much in advance.

I would have to see the code for the handlers and the code on the master side BUT a slave can have handlers for BOTH types of events.

At least it works for me on a UNO.

That is possible, but you may run into trouble.
Suppose the Slave is temporary Master and is sending data to the Master. At that very moment the Master decides to send data to (or request data from) the Slave. That causes a collision and the I2C is not designed to handle collisions.

The Master can add a slave-address with: Wire.begin ( 4 ) ;
And the Master can set a onReceive handler for that.
But the collisions must be avoided at all times.
So you would have to make a protocol with timeouts and strict rules.

The most common way is to let the Master request the temperature from the Slave once a second. Or once in 10 seconds or 10 times per second, whatever you think is best.

It is much safer to avoid collisions and let the Master do all the writing and requestion data. However, that is a little slower.
When the Master requests data from the Slave, the Wire.requestFrom() waits until the I2C has finished. When the Slave is temporary Master, the Master receives it in a interrupt handler.

introulio:
But the slave code is not working and I think it´s because of this:

void setup()

{
 
  pinMode (led, OUTPUT );
  Wire.begin(2);                // join i2c bus with address #2
  Wire.onRequest(requestEvent); // register event
  Wire.onReceive(receiveEvent); // receive event
 
  Serial.begin(9600);  // start serial for output
}

That snippet?

http://snippets-r-us.com/

You can have multiple masters.

Good evening,

First of all, apologies such a late feddback from our side. After all your comments, we realised that there were some fundamentals of I2C that we hadn´t fully understood yet so, we have been studying and testing also using your advice.

*note: all comments in the code I share in this post are in Spanish and folllowed by English translation

fcwilt:
I would have to see the code for the handlers and the code on the master side BUT a slave can have handlers for BOTH types of events.

At least it works for me on a UNO.

Thanks a lot for that. It gave us the confidence to keep digging into finding how to get there! Code shared on this post.

Peter_n:
When the Master requests data from the Slave, the Wire.requestFrom() waits until the I2C has finished. When the Slave is temporary Master, the Master receives it in a interrupt handler.

Great information for us! AS you will see in our code we finally went for 3 devices processing data that they receive upon wire.requestFrom(), in order to control I2C bus traffic.

Never meant to fall under the snippet category sorry! It was more a premature attempt to play with I2C before undertsanding well the process.

We have got now multiple masters and would like to share our code wich is now working and that still rises some questions that you might be able to help with. I hope you find it interesting. To make sure I don´t exceed the lspace limit I will send a new post with the project and code of the 3 sketches.

Thanks!

So here we go:
Our I2C project communicates 3 ARDUINO UNO acting as Master: "Procesador" "Impresion" "Sensores". The 3 sketches use wire library to request and receive data from each other and act upon what is received.

The first one is "Procesador". This is the code and description:

/*
 Modulo Procesador del Proyecto I2C con 3 Arduino UNO actuando como Master para poder pedir y responder a peticiones de datos.

- PROCESADOR (ESTE SKETCH) : ENTRADAS
                             Pide a la direccion "sensores" Temperatura 1 y Luminosidad.
                             Pide a la direccion "impresion" Temperarura 2
                             SALIDAS
                             Calcula Temperatura 3 = La media entre Temperatura 1 y 2
                             Responde a la peticion de la direccion "impresion" y le envia Temperatura 3.
                             Envia a la direccion "sensores" el estado 0/1 para un LED
- Otros sketch del proyecto : "Sensores" & "Impresion"

Procesador module for I2C Project with 3 Arduino UNO acting as Master to resquest and respond to data requests
- PROCESADOR (THIS SKETCH) : INPUTS 
                             Request to address  "sensores" Temperature 1 & Luminosity.
                             Request to address "impresion" Temperarure 2
                             SALIDAS
                             Calculate Temperature 3 =  Average Temperature 1 & 2
                             Responds to request from address "impresion" and sends back Temperature 3 .
                             Sends to address "sensores" 0/1 value for a LED
- Other sketchs for this project : "Sensores" & "Impresion"

                                                         
                               
 created 01 March. 2015
 by Introulio & Arduina
  
 */

#include <Wire.h> //libreria wire

 int led1;             // estado LED -- LED status
 
 int procesador = 35;  // direccion I2C -- I2C address
 int sensores   = 42;  // direccion I2C -- I2C address
 int impresion  = 32;  // direccion I2C -- I2C address
 
 int T3 = 0; // Para calcular media de Temp1&Temp2 -- To Calculate Temp1&Temp2 avergare

//variables en byte para el tráfico I2C de Temperatura 3
// byte variables for I2C traffic of Temperature 3
 byte hiT3; 
 byte loT3; 
  
void setup()
{
  Wire.begin(procesador);        //  I2C bus  para direccion procesador  -- I2C bus for Address procesador
  Serial.begin(9600);            // inicia puerto serial -- start serial for output
  
  Wire.onRequest(requestEvent); // Función llamada por peticion I2C de Temperatura3 desde direccion "impresiones" 
                                // Function called by I2C request of Temperature3 from address "impresiones"
}

void loop()
{
  Wire.requestFrom(sensores, 4);    // Peticion I2C para la direccion "Sensores", 4 bytes (Temperatura y Luminosidad) 
                                    // I2C request from  address "sensores", 4 bytes (Temperature y Luminosity) 
 
  byte hiByteT;  // Temperatura high Byte
  byte loByteT;  // Temperatura low Byte 
  byte hiByteL;  // Luminosidad high Byte
  byte loByteL;  // Luminosidad low Byte
 

if(Wire.available() >= 4)    // Recibe los 4 bytes pedidos a  "sensores" 
                             // Recieves the 4 bytes requested to "sensores"
  { 
    hiByteT= Wire.read();
    loByteT= Wire.read();
    hiByteL= Wire.read();
    loByteL= Wire.read();
  }
 
  int val1 = (hiByteT << 8) + loByteT; // Une los 2 bytes de Temperatura -- Group the 2 bytes for Temperature
  int Temp = (5.0 * val1 * 100.0)/1024.0;  // Convierte el rango de temperatura -- Temperature range conversion

  int val2 = (hiByteL << 8) + loByteL; // Une los 2 bytes de Luminosidad -- Group the 2 bytes for Luminosity
  int lux = val2 ;
  
  //Escribe los valores de Temp y Lux en puerto serial 
  //Writes Temp & Lux values into serial port
  Serial.print("    Temp =  "); 
  Serial.print(Temp);
  Serial.print("    lux =   "); 
  Serial.print(val2);
  delay(100);
  
  Wire.requestFrom(impresion, 2 );    // Peticion I2C para la direccion "impresion", 2 bytes (Temperatura2) 
                                      // I2C request from  address "impresion", 2 bytes (Temperature2) 

  byte hiByteT2;  // Temperatura2 high Byte
  byte loByteT2;  // Temperatura2 low Byte
 

if(Wire.available() == 2)    // Recibe los 2 bytes pedidos a "impresion" 
                             // Recieves the 4 bytes requested to "impresion"
  { 
    hiByteT2= Wire.read();
    loByteT2= Wire.read();
  }
 
  int val3 = (hiByteT2 << 8) + loByteT2; // Une los 2 bytes de Temperatura2 -- Group the 2 bytes for Temperature2
  int Temp2 = val3 ; // Convierte el valor a integer en "Temp2"
  
  //Escribe el valor de Temp2 en puerto serial 
  //Writes Temp2 value into serial port
  Serial.print("    Temp2 =  "); 
  Serial.print(Temp2);
  delay(100);
  
 //Calcula Temp3 = la media de Temp y Temp2 y la escribe en puerto serial
 //Calculate Temp 3 = Temp&Temp2 average. Writes Temp3 into serial port
  T3 = (Temp + Temp2) / 2;
  Serial.print("    Temp3 =  "); 
  Serial.print(T3);
  
 //Toma de bytes de T3 para poder responder en el requestEvent a la peticion I2C "Impresiones" 
 //Divides T3 into high&low bytes to repond in requestEvent to the I2C request from "Impresiones"
  hiT3 = highByte(T3); 
  loT3 = lowByte(T3);  

 //Estado LED es 1 si la temperatura está entre 20 y 29 grados;si no, es 0
 //LED state is 1 if temperature is between 20 & 29 degrees; else it's 0
  if((Temp >= 20) && (Temp <= 29))
  {  
    led1 = 1;
  }

  else  
  {
  led1 = 0; 
  }
  
  Wire.beginTransmission(sensores);// Comienza transmision I2C a direccion "sensores"
                                    // Begins I2C Transmission to "sensores" address
  
  Wire.write (led1); // envia estado LED  -- sends LED state
  Wire.endTransmission(); // end transmission 
  
 
 //Escribe el estado del LED en puerto serial
 //Writes LED state into serial port
 Serial.print("    led1 =   "); 
 Serial.println(led1); 
  }


//Respuesta a peticion I2C de Temperatura 3 desde direccion "impresiones" 
void requestEvent()  
{
  
//buffer buf de 2 bytes compuestos por hiT3 y loT3
//buffer buf of 2 bytes composed byhiT3 y loT3
 byte buf [2];
  buf [0] = hiT3;
  buf [1] = loT3;
  
 // escribe Temperatura 3 con el buffer en el bus I2C
 // writes Temperature 3 into I2C bus
 Wire.write(buf, sizeof buf);                                      
 delay(100);
}
[code]

[/code]

The second one is "Impresion". Takes Values from "Sensores" and from "Procesador", prints LCD Screen and takes values from a analog sensor and sends them to "Procesador". This is the code:

/*
 Modulo Impresiones del Proyecto I2C con 3 Arduino UNO actuando como Master para poder pedir y responder a peticiones de datos.

- IMPRESIONES (ESTE SKETCH) : ENTRADAS
                             Pide a la direccion "sensores" Temperatura 1 y Luminosidad.
                             Pide a la direccion "procesador" Temperarura 3
                             Toma Valores de Temperatura 2 mediante sensor de Temperatura
                              SALIDAS
                             Imprime en pantalla LCD Temperatura 1 (tomada de direccion "sensores")
                             Imprime en pantalla LCD Luminosidad (tomada de direccion "sensores")
                             Imprime en pantalla LCD Temperatura 2 (tomada de sensorT2 en este sketch)
                             Imprime en pantalla LCD Temperatura 3 (tomada de direccion "procesador")
                             Responde a la peticion de la direccion "procesador" y le envia Temperatura 2.
- Otros sketch del proyecto : "Sensores" & "Procesador"

Impresiones module for I2C Project with 3 Arduino UNO acting as Master to resquest and respond to data requests
- IMPRESIONES (THIS SKETCH) : INPUTS
                             Request to address  "sensores" Temperature 1 y Luminosity.
                             Request to address "impresion" Temperarure 3
                             Reads Temperatura 2 value from input sensor
                              OUTPUTS
                             LCD Screen print of Temperature 1 (taken from address "sensores")
                             LCD Screen print of Luminosity (taken from address "sensores")
                             LCD Screen print of Temperature 2 (taken from sensorT2 in this sketch)
                             LCD Screen print of Temperature 3 (taken from address "procesador")
                             Responds to request from address "procesador" and sends back Temperature 2 .
- Other sketchs for this project : "Sensores" & "Procesador"
                                                                                    
 created 01 March. 2015
 by Introulio & Arduina
  
 */

    #include <Wire.h>          // libreria wire
    #include <LiquidCrystal.h> // libreria Pantalla LCD
     
    LiquidCrystal lcd(7, 8, 9, 10, 11 , 12); //Definimos la pantalla LCD -- LCD screen definition
    
    //Variables de temperatura
    // Temperature variables
    int Temp = 0;
    int  T2 = 0;
    int T3 = 0;
    
    //entrada analogica sensor temperatura
    //analog input temperature senson
    const int  sensorT2 = A0; 
    
    //Variables en byte para el tráfico I2C de Temperatura 2
    // Byte variables for I2C traffic of Temperature 2
    byte hiT2;
    byte loT2;
    
    // Direcciones I2C
    // I2C addresses
    int procesador = 35;
    int sensores   = 42;
    int impresion  = 32; // este sketch -- this sketch
 
    void setup(){
      
     Wire.begin(impresion);         //  I2C bus  para direccion impresion  -- I2C bus for Address impresion
     Serial.begin(9600);            // inicia puerto serial -- start serial for output
  
    lcd.begin(16,2);  // Inicia Panlalla LCD -- Initiate LCD screen
    
    pinMode(3,OUTPUT); // Salida de retroiluminacion -- Output for retro-lighting of screen
    
    Wire.onRequest(requestEvent); // Función llamada por peticion I2C de Temperatura2 desde direccion "procesador" 
                                 // Function called by I2C request of Temperature2 from address "procesador"
    }
     
     
     
    void loop(){
      Wire.requestFrom(sensores, 4);    // Peticion I2C para la direccion "Sensores", 4 bytes (Temperatura y Luminosidad) 
                                        // I2C request from  address "sensores", 4 bytes (Temperature y Luminosity) 
 
      byte hiByteT; // Temperatura high Byte
      byte loByteT; // Temperatura low Byte
      byte hiByteL; // Luminosidad high Byte
      byte loByteL;  // Luminosidad low Byte
 

     if(Wire.available() >= 4)    // Recibe los 4 bytes pedidos a "sensores" 
                                  // Recieves the 4 bytes requested to "sensores"
     { 
       hiByteT= Wire.read();
       loByteT= Wire.read();
       hiByteL= Wire.read();
       loByteL= Wire.read();
     }
 
  int val1 = (hiByteT << 8) + loByteT; // Une los 2 bytes de Temperatura -- Group the 2 bytes for Temperature
  int Temp  =  val1 *0.48828125; // Convierte el rango de temperatura -- Temperature range conversion
  
  int val2 = (hiByteL << 8) + loByteL; // Une los 2 bytes de Luminosidad -- Group the 2 bytes for Luminosity
  int lux = val2 ;
  
  
  Wire.requestFrom(procesador, 2);    // Peticion I2C para la direccion "procesador", 2 bytes (Temperatura3) 
                                      // I2C request from  address "procesador", 2 bytes (Temperature3) 
  
  byte hiByteT3; // Temperatura3 high Byte
  byte loByteT3; // Temperatura2 low Byte
  

  if(Wire.available() == 2)    // Recibe los 2 bytes pedidos a "procesador" 
                              // Recieves the 4 bytes requested to "procesador"
   { 
    hiByteT3= Wire.read();
    loByteT3= Wire.read();
   }
 
  int val4 = (hiByteT3 << 8) + loByteT3; // Une los 2 bytes de Temperatura3 -- Group the 2 bytes for Temperature3
  int Temp3  =  val4; //Temperatura3 alamacenada en Temp3
 
 //Mostramos los valores de temperatura 1,2&3 y lluminosidad en la pantalla LCD
 //Write Temperature1,2&3 and luminosity values in LCD screen
    lcd.clear(); // limpia pantalla LCD -- clear LCD screen
    
    //T1 = Temperatura 1 del sensor desde direccion "sensores"
    //T1 = Temperature 1 from sensor at address "sensores"
    lcd.setCursor(0,0); 
    lcd.print("T1:");
    lcd.setCursor(3,0);
    lcd.print(Temp);
    
    //T2 = Temperatura 2 del sensor desde direccion "impresion" (este sketch)
    //T2 = Temperature 2 from sensor at address "impresion" (this sketch)
    lcd.setCursor(6,0);
    lcd.print("T2:");
    lcd.setCursor(9,0);
    lcd.print(T2);
    
    //lux = Luminosidad del sensor desde direccion "sensores"
    //lux = Luminosity from sensor at address "sensores"
    lcd.setCursor(0,1); 
    lcd.print("lux: ");
    lcd.setCursor(4,1);
    lcd.print( lux );
 
    //T3 = Temperatura 3 del sensor desde direccion "procesador"
    //T3 = Temperature 3 from sensor at address "procesador"
    lcd.setCursor(11,1);
    lcd.print("T3:");
    lcd.setCursor(14,1);
    lcd.print(Temp3);
 
 // controla la retro iluminacion de la pantalla LCD
 // controls LCD sreen brightness 
 analogWrite(3,lux/4); 
 
 //Toma el valor de temperaura desde sensor analogico
 //Reads analog temperature sensor
  T2 = analogRead (sensorT2)*0.48828125;
  delay (100);

  //Toma de bytes de T2 para poder responder en el requestEvent a la peticion I2C "procesador" 
  //Divides T2 into high&low bytes to respond in requestEvent to the I2C request from "procesador"
  hiT2 = highByte(T2);
  loT2 = lowByte(T2);
  
  }   

//Respuesta a peticion I2C de Temperatura 2 desde direccion "procesador" 
//Response to I2C request from addres "porcesador" requesting Temperatura 2
 void requestEvent()
{
  
//buffer buf de 2 bytes compuestos por hiT2 y loT2
//buffer buf of 2 bytes composed byhiT2 y loT2
 byte buf [2];
  buf [0] = hiT2;
  buf [1] = loT2;
 
 // escribe Temperatura 2 con el buffer en el bus I2C
 // writes Temperature 2 into I2C bus
  Wire.write(buf, sizeof buf);  
 delay(100);
}

And the third one is "Sensores". Takes values from 2 analog sensors and sends them to "Impresion" and "Procesador". Receives a transmission from "Procesador" and receives the data to light a LED On/Off. This is the code:

/*
 Modulo "Sensores" del Proyecto I2C con 3 Arduino UNO actuando como Master para poder pedir y responder a peticiones de datos.

- SENSORES (ESTE SKETCH) :   ENTRADAS
                             Recibe estado LED 1/0 desde direccion "procesador"
                             Lee Temperatura 1 desde sensor analogico de temperatura
                             Lee Luminodidad dede sensor de analogico luminosidad
                             SALIDAS
                             Responde a la peticion de la direccion "procesador" y le envia Temperatura 1 y Luminosidad (tomadas desde sensorT y SensorL en este sketch) 
                             Responde a la peticion de la direccion "impresion" y le envia Temperatura 1 y Luminosidad (tomadas desde sensorT y SensorL en este sketch) 
                             Enciende/Apaga LED1 
                             
- Otros sketch del proyecto : "Sensores" & "Procesador"

"Sensores" module for I2C Project with 3 Arduino UNO acting as Master to resquest and respond to data requests
- IMPRESIONES (THIS SKETCH) : INPUTS
                              Receives LED state 1/0 from address "procesador"
                              Reads Temperatura 1 value from input sensor 
                              Reads Luminosidad value from input sensor
                              OUTPUTS
                              Responds to request from address "procesador" and sends back Temperature 1 & Luminosity (taken from sensorT y sensorL)
                              Responds to request from address "impresion" and sends back Temperature 1 & Luminosity (taken from sensorT y sensorL)
                              Lights LED1 on/off
                             
- Other sketchs for this project : "Impresion" & "Procesador"
                                                                                    
 created 01 March. 2015
 by Introulio & Arduina
  
 */


#include <Wire.h> // libreria wire para I2C

//sensores analogicos 
//analog sensors
const int  sensorT = A0;
const int  sensorL = A1 ;

//pin led 13
int  led1 = 13;

// Direcciones I2C
// I2C addresses
int sensores   = 42;
int procesador = 35;
int impresion  = 32;

//Variables en byte para el tráfico I2C de Temperatura y Luminosidad
// Byte variables for I2C traffic of Temperature & Luminosity
byte hiT;
byte loT;
byte hiL;
byte loL;


void setup()
{ 
  // declara el pin de salida para el LED1
  // declare outpin pin  LED1
  pinMode (led1, OUTPUT );  
                          
  Wire.begin(sensores);    //  I2C bus  para direccion impresion  -- I2C bus for Address sensores
    
  // Función llamada por transmision I2C de Estado LED iniciada desde direccion "procesador"
  // Function called by I2C transimission of LED State initaited by address "procesador"
  Wire.onReceive(receiveEvent); // registre  evento
  
  // Función llamada por peticion I2C de Temperatura y Luminosidad desde direcciones "procesador" y "impresion"
  // Function called by I2C request of Temperature&Luminosity from addresses "procesador" and "impresion"
  Wire.onRequest(requestEvent); // register event
}

void loop()
{
  
  //Toma el valor de temperaura desde sensor analogico
  //Reads analog temperature sensor
  int  T = analogRead (sensorT);
  
  //Toma el valor de luminosidad desde sensor analogico
  //Reads analog luminosity sensor
   int  L = analogRead (sensorL);
 
 //Toma de bytes de T y L para poder responder en el requestEvent a la peticion I2C  
  //Divides T and L into high&low bytes to respond in requestEvent to the I2C request 
  hiT = highByte(T);
  loT = lowByte(T);
  hiL = highByte(L);
  loL = lowByte(L);
}


//Respuesta a peticion I2C de Temperatura  desde direcciones "procesador" y "impresion"
//Response to I2C request from addresses "porcesador" and "impresion" requesting Temperatura 
void requestEvent()
{

  //buffer buf de 4 bytes compuestos por hiT, loT, hiL & loL
  //buffer buf of 4 bytes composed by hiT2 , loT, hiL & loL
  byte buf [4];
   buf [0] = hiT;
   buf [1] = loT;
   buf [2] = hiL;
   buf [3] = loL;
  
 // escribe Temperatura & Luminosidad con el buffer en el bus I2C
 // writes Temperature & Luminosity into I2C bus
  Wire.write(buf, sizeof buf);  // envia una respuesta de la suma de bytes
  delay(100);
}


//Recepcion de transmision  I2C de Estado LED desde direccion "procesador" 
//Reception of I2C transmission of LED State from address "procesador"  
void receiveEvent(int howMany ){
  
  for (int i = 0; i < howMany; i++)   { 
    byte c = Wire.read(); // receive byte as a character
      //recive y convierte el byte en  un numero entereo
    digitalWrite (led1,int(c));
    Serial.print("    led1 =   "); 
    Serial.println(led1);
  
   }
 
}

You can attach code to posts, thus avoiding the character limit. This includes .zip files.

How to use this forum

Thanks Nick! I will take it into account for the next one... :roll_eyes: