Tutorial - Comunicación Maestro / Esclavo : Parte 3 (Comunicación I2C - Introducción)

COMUNICACIÓN I2C
La comunicación I2C (Inter - Integrated Circuit ) es un protocolo de multi-maestro, multi-esclavo inventado por Philips Semiconductor, y es estándar de comunicación usado extendidamente en la electrónica. Algo a tomar en cuenta, es el uso de resistencias pull-up; ciertas aplicaciones ameritan este detalle y su debido cálculo se puede encontrar acá´: "Cálculo de resistencias pull - up para redes I2C"; sobre todo, a medida que la red crece es necesario asegurarse. 

Otro dato importante es el uso de direcciones, la red amerita un maestro que no necesita dirección, en cambio los demás elementos o esclavos si deben estar correctamente identificados; tomando esto último en consideración las direcciones pueden ser únicas o también programables y a su vez, las direcciones tambíen se pueden multiplexar.


USO DE LA LIBRERÍA "WIRE"
Para este tipo de comunicaciones en la plataforma Arduino, se usa la librería Wire que permite tanto administrar la red como configurarla. Los pines usados son los correspondientes a SDA (Línea de datos) y SCL (Línea de reloj). Algunas de las funciones se explican a continuación:
# Wire.begin() o Wire.begin(dirección) : Inicializa la librería Wire y se una a la red I2C como maestro o esclavo. La dirección es opcional para el caso del Maestro y única para el caso del Esclavo. La dirección debe ser de 7 bits como máximo.
# Wire.available() : Para uso sólo del Maestro, retorna el número de bytes disponibles.
# Wire.requestFrom(dirección, cantidad_bytes) : Para uso sólo del Maestro, pide bytes de un esclavo
# Wire.read() : Lee el byte tarnsmitido del Esclavo despúes de haberlo llamado con la función Wire.requestFrom().
# Wire.beginTransmission(dirección) : Comienza la transmisión I2C con el Esclavo de la dirección mencionada. Su uso va acompañado de la función Wire.write() y termina la comunicación con Wire.endTransmission().
# Wire.endTransmission() : Termina la transmisión inicializada con la función Wire.beginTransmission().
# Wire.write(valor) o Wire.write(string) o Wire.write(data, tamaño) : Escribe un dato y se usa entre las funciones Wire.beginTransmission() y Wire.endTransmission().
# Wire.onRequest(función) : Registra una función a ser llamada cuando el Maestro pide datos del Esclavo.
# Wire.onReceive(función) : Registra una función a ser llamada cuando el Esclavo recibe una transmisión del Maestro.

A partir de lo descrito antes, desarrollamos una aplicación simple de lectura entre un solo maestro y un esclavo. Conectamos un potenciómetro de 10 kohm al PIN A0 del Arduino que tiene el papel de esclavo y el Maestro recibirá los datos y los mostrará en el Monitor Serial. La conexión es la siguiente:

# PIN A4 (SDA) : Arduino UNO del Maestro => A4 (SDA) : Arduino UNO del Esclavo 1
# PIN A5 (SCL) : Arduino UNO del Maestro => A5 (SCL) : Arduino UNO del Esclavo 1
# PIN A0 : Arduino UNO del Esclavo => PIN CENTRAL : Potenciómetro
# GND : Los dos GND deben estar conectados => EXTREMO : Potenciómetro
# 5V : Los dos 5V deben estar conectados => EXTREMO : Potenciómetro


El programa usado para el Maestro es el siguiente:
/*
   PROGRAMA DE PRUEBA DE COMUNICACION ENTRE DOS PUNTOS: USO DE LA LIBRERIA WIRE
   (PROGRAMA PARA EL MAESTRO)
   
   MAESTRO: Arduino UNO (1)
   ESCLAVO: Arduino UNO (2)
   
   CONEXION:
             A4 (Arduino UNO (1) / SDA - MAESTRO): A4 (Arduino UNO (2) / SDA - ESCLAVO)
             A5 (Arduino UNO (1) / SCL - MAESTRO): A5 (Arduino UNO (2) / SCL - ESCLAVO)
             Potenciometro: Pin A0 (Arduino UNO(2) - ESCLAVO) 
             GND: Conectar los dos GND
             VCC: Conectar los dos 5V
   
   En el siguiente programa probaremos la comunicacion Maestro / Esclavo. El Maestro permite 
   la visualizacion de los datos obtenidos de leer el potenciometro que va conectado al Esclavo. 
   
   Para correr el programa se necesita la libreria Wire (Wire.h)
   
   Autor: Renato H.
   http://beetlecraft.blogspot.pe/
   
   El siguiente programa es de uso publico, cualquier modificacion o mal uso del mismo que pudiera 
   ocasionar el mal funcionamiento de la plataforma de uso de la misma no es responsabilidad del 
   autor
*/

#include <Wire.h> // Libreria de manejo de red I2C

int POT; // Variable de almacenamiento del valor leido del potenciometro

void setup()
{
  Wire.begin();        // Unirse a la red I2C (Maestro)
  Serial.begin(9600);  // Configuracion del puerto serial a 9600 baudios
  
  // Mensaje inicial en el Monitor Serial
  Serial.println("Los valores del potenciometro se observan de 0 a 100% ");
  Serial.println("Esperar...");
  delay(100);
}

void loop(){
  Wire.requestFrom(1,1);     // Pedido de informacion del Esclavo 1 de 1 byte
  while (Wire.available()) { // Verificacion que el puerto serial virtual recibe datos                                      
    POT = Wire.read();       // Lectura de datos del I2C
    
    // Mensaje de muestra del porcentaje del voltaje leido del potenciometro
    Serial.print("Porcentaje de voltaje: "); 
    Serial.print(POT);                       
    Serial.println(" %");         
  }
  delay(100);
}
El programa usado para el Esclavo es el siguiente:
/*
   PROGRAMA DE PRUEBA DE COMUNICACION ENTRE DOS PUNTOS: USO DE LA LIBRERIA WIRE
   (PROGRAMA PARA EL ESCLAVO)
   
   MAESTRO: Arduino UNO (1)
   ESCLAVO: Arduino UNO (2)
   
   CONEXION:
             A4 (Arduino UNO (1) / SDA - MAESTRO): A4 (Arduino UNO (2) / SDA - ESCLAVO)
             A5 (Arduino UNO (1) / SCL - MAESTRO): A5 (Arduino UNO (2) / SCL - ESCLAVO)
             Potenciometro: Pin A0 (Arduino UNO(2) - ESCLAVO) 
             GND: Conectar los dos GND
             VCC: Conectar los dos 5V
   
   En el siguiente programa probaremos la comunicacion Maestro / Esclavo. El Maestro permite 
   la visualizacion de los datos obtenidos de leer el potenciometro que va conectado al Esclavo. 
   
   Para correr el programa se necesita la libreria Wire (Wire.h)
   
   Autor: Renato H.
   http://beetlecraft.blogspot.pe/
   
   El siguiente programa es de uso publico, cualquier modificacion o mal uso del mismo que pudiera 
   ocasionar el mal funcionamiento de la plataforma de uso de la misma no es responsabilidad del 
   autor
*/

#include <Wire.h>  // Libreria de manejo de red I2C

int POT = A0;      // Pin de entrada de senal del potenciometro
int POT_valor = 0; // Variable de almacenamiento del porcentaje del potenciometro 
int porcentaje;    // Variable de conversion a porcentaje

void setup() {
  Wire.begin(1);             // Unirse a la red I2C (Esclavo con direccion "1")
  Wire.onRequest(escritura); // Registro de evento de la funcion "escritura"
}

void loop() {
  delay(100);
}

void escritura(){
  POT_valor = analogRead(POT);              // Lee el valor del sensor (Rango de 0 a 1023)
  porcentaje = map(POT_valor,0,1023,0,100); // Porcentaje del rango de 0 a 100%
                                            // 100 corresponde a 5V y 0 a 0V
  Wire.write(porcentaje);                   // Escritura por I2C del porcentaje de voltaje 
}
Los resultados obtenidos en el Monitor Serial del Maestro se muestran a continuación:


COMUNICACIÓN MAESTRO / ESCLAVO USANDO TRES ARDUINOS Y LA PC
Estableceremos una red bidireccional (Emisión y recepción de datos) usando los pines I2C de tres Arduinos UNO conectados. 

Las conexiones que se deben realizar se pueden ver en la siguiente figura:

# PIN A4 (SDA) : Arduino UNO del Maestro => A4 (SDA) : Arduino UNO del Esclavo 1
# PIN A5 (SCL) : Arduino UNO del Maestro => A5 (SCL) : Arduino UNO del Esclavo 1
# PIN A4 (SDA) : Arduino UNO del Maestro => A4 (SDA) : Arduino UNO del Esclavo 2
# PIN A5 (SCL) : Arduino UNO del Maestro => A5 (SCL) : Arduino UNO del Esclavo 2
# GND : Los dos GND deben estar conectados.
# 5V : Los dos 5V deben estar conectados


PROGRAMA DE PRUEBA
Como prueba de comunicación, realizaremos un pequeño de ejercicio de envio y recepción de datos en el cual los dos esclavos se identificarán. Para ello, el siguiente procedimiento se debe cumplir:

# El Maestro al enviar la letra "a" ingresada por "Monitor Serial" de la PC, y debe hacer que encienda el led del Pin 13 del "Esclavo 1", quien a su vez enviará el dato "1" que corresponde al identificador del Esclavo. Al recibir dicho mensaje, en el Monitor Serial deberá aparecer "Esclavo id: 1", siendo el número quien identifique el Esclavo y que recibió el mensaje.
# El Maestro al enviar la letra "b" ingresada por "Monitor Serial" de la PC, y debe hacer que encienda el led del Pin 13 del "Esclavo 2", quien a su vez enviará el dato "2" que corresponde al identificador del Esclavo. Al recibir dicho mensaje, en el Monitor Serial deberá aparecer "Esclavo id: 2", siendo el número quien identifique el Esclavo y que recibió el mensaje.

El programa del Maestro es el siguiente:
/*
   PROGRAMA DE PRUEBA DE COMUNICACION MAESTRO / ESCLAVO: MAESTRO / ESCLAVO POR I2C
   (PROGRAMA PARA EL MAESTRO)
   
   MAESTRO: Arduino UNO (1)
   ESCLAVO 1: Arduino UNO (2)
   ESCLAVO 2: Arduino UNO (3)
   
   CONEXION:
             A4 (Arduino UNO (1) / SDA - MAESTRO): A4 (Arduino UNO (2) / SDA - ESCLAVO 1)
             A5 (Arduino UNO (1) / SCL - MAESTRO): A5 (Arduino UNO (2) / SCL - ESCLAVO 1)
             A4 (Arduino UNO (1) / SDA - MAESTRO): A4 (Arduino UNO (3) / SDA - ESCLAVO 2)
             A5 (Arduino UNO (1) / SCL - MAESTRO): A5 (Arduino UNO (3) / SCL - ESCLAVO 2) 
             GND: Conectar los dos GND
             VCC: Conectar los dos 5V
   
   En el siguiente programa probaremos la comunicacion Maestro / Esclavo usando un Arduino UNO como
   Maestro y dos Arduino UNO como esclavos. El Maestro envia la letra "a" para discriminar al primer
   esclavo para que encienda el led del PIN 13 y envie A1 para identificarse; de la misma manera, al 
   enviar la letra "b" se discrimina el segundo esclavo para que encienda el led del PIN 13 y envie 
   A2
   
   Autor: Renato H.
   http://beetlecraft.blogspot.pe/
   
   El siguiente programa es de uso publico, cualquier modificacion o mal uso del mismo que pudiera 
   ocasionar el mal funcionamiento de la plataforma de uso de la misma no es responsabilidad del 
   autor
*/

#include <Wire.h>  // Libreria de manejo de red I2C

int id;       // Variable del identificador del Esclavo
int flag = 0; // Flag de control de autentificacion de mensaje

void setup() {
  Wire.begin();       // Unirse a la red I2C (Maestro)
  Serial.begin(9600); // Configuracion del puerto serial a 9600 baudios

  // Mensaje inicial en el monitor serial
  Serial.println("Envio de datos pueden ser de la siguiente manera: ");
  Serial.println("a: Para el esclavo 1");
  Serial.println("b: Para el esclavo 2"); 
}

void loop() {
  if (Serial.available()) {   // Verificacion que el puerto serial recibe datos                                  
    delay(10);                
    identificacion_esclavo(); // Rutina de identificacion de esclavos 1 y 2     
    mensaje_esclavo();        // Rutina de recepcion de mensaje de autentificacion
    while (Serial.available() > 0){Serial.read();} // Rutina de limpieza del buffer del puerto serial
  }
}

void identificacion_esclavo(){    // RUTINA DE IDENTIFICACION DE ESCLAVOS 1 Y 2

                                  // RUTINA DE IDENTIFICACION DEL ESCLAVO 1
  while (Serial.peek() == 'a'){   // Discriminacion de la letra como diferenciador del esclavo 
    Serial.println("Esperar..."); // Mensaje de espera por datos de verificacion
    flag = 1;                     // Activacion del flag 1
    Wire.beginTransmission(1);    // Comienzo de transmision al Esclavo 1
    Wire.write('a');              // Redireccionamiento del mensaje al Esclavo 1
    Wire.endTransmission();       // Fin de transmision al Esclavo 1
  break;
  }

                                  // RUTINA DE IDENTIFICACION DEL ESCLAVO 2
  while (Serial.peek() == 'b'){   // Discriminacion de la letra como diferenciador del esclavo 
    Serial.println("Esperar..."); // Mensaje de espera por datos de verificacion
    flag = 2;                     // Activacion del flag 2
    Wire.beginTransmission(2);    // Comienzo de transmision al Esclavo 2
    Wire.write('b');              // Redireccionamiento del mensaje al Esclavo 2
    Wire.endTransmission();       // Fin de transmision al Esclavo 2
  break;
  }
}

void mensaje_esclavo(){              // RUTINA DE ACREDITACION DE IDENTIFICACION
  
  while (flag == 1){                 // RUTINA DE ACREDITACION DEL ESCLAVO 1        
    Wire.requestFrom(1,1);           // Pedido de informacion del Esclavo 1 de 1 byte
     while(Wire.available()){        // Verificacion de informacion en el puerto I2C
       id = Wire.read();             // Lectura de datos del buffer   
       Serial.print("Esclavo id: "); // Mensaje de acreditacion del esclavo
       Serial.println(id);           // Numero de identificacion del esclavo
       flag = 0;                     // Desactivacion del flag 1
     }
  break;
  }

  while (flag == 2){                 // RUTINA DE ACREDITACION DEL ESCLAVO 2        
    Wire.requestFrom(2,1);           // Pedido de informacion del Esclavo 2 de 1 byte
     while(Wire.available()){        // Verificacion de informacion en el puerto I2C
       id = Wire.read();             // Lectura de datos del buffer   
       Serial.print("Esclavo id: "); // Mensaje de acreditacion del esclavo
       Serial.println(id);           // Numero de identificacion del esclavo
       flag = 0;                     // Desactivacion del flag 2
     }
  break;
  }
}

El programa de los Esclavos es el siguiente:
/*
   PROGRAMA DE PRUEBA DE COMUNICACION MAESTRO / ESCLAVO: MAESTRO / ESCLAVO POR I2C
   (PROGRAMA PARA EL ESCLAVO)
   
   MAESTRO: Arduino UNO (1)
   ESCLAVO 1: Arduino UNO (2)
   ESCLAVO 2: Arduino UNO (3)
   
   CONEXION:
             A4 (Arduino UNO (1) / SDA - MAESTRO): A4 (Arduino UNO (2) / SDA - ESCLAVO 1)
             A5 (Arduino UNO (1) / SCL - MAESTRO): A5 (Arduino UNO (2) / SCL - ESCLAVO 1)
             A4 (Arduino UNO (1) / SDA - MAESTRO): A4 (Arduino UNO (3) / SDA - ESCLAVO 2)
             A5 (Arduino UNO (1) / SCL - MAESTRO): A5 (Arduino UNO (3) / SCL - ESCLAVO 2) 
             GND: Conectar los dos GND
             VCC: Conectar los dos 5V
   
   En el siguiente programa probaremos la comunicacion Maestro / Esclavo usando un Arduino UNO como
   Maestro y dos Arduino UNO como esclavos. El Maestro envia la letra "a" para discriminar al primer
   esclavo para que encienda el led del PIN 13 y envie A1 para identificarse; de la misma manera, al 
   enviar la letra "b" se discrimina el segundo esclavo para que encienda el led del PIN 13 y envie 
   A2
   
   Autor: Renato H.
   http://beetlecraft.blogspot.pe/
   
   El siguiente programa es de uso publico, cualquier modificacion o mal uso del mismo que pudiera 
   ocasionar el mal funcionamiento de la plataforma de uso de la misma no es responsabilidad del 
   autor
*/

#include <Wire.h>  // Libreria de manejo de red I2C

const int led = 13; // Variable de almacenamiento del pin del puerto de salida del led
char id;            // Variable de almacenamiento de indentificacion

void setup() {
  pinMode(led, OUTPUT);      // Configuracion del Pin 13 como salida
  Wire.begin(1);             // Unirse a la red I2C (Esclavo con direccion "1")
  // Cambiar el 1 por 2 para el caso del Esclavo 2
  Wire.onReceive(lectura);   // Registro de funcion de lectura ante evento
  Wire.onRequest(escritura); // Registro de funcion de escritura ante evento
}

void loop() {
  delay(100);
}

void lectura(int x){           // RUTINA DE IDENTIFICACION FISICA Y AUTENTIFICACION
  while (Wire.available()){    // Verificacion de datos en el puerto I2C
    id = Wire.read();          // Lectura de datos en el puerto I2C
    if (id == 'a'){            // Discriminacion del puntero de identificacion del Esclavo
    // Cambiar 'a' por 'b' para el caso del Esclavo 2
      digitalWrite(led, HIGH); // Encendido del LED del PIN 13  
    }      
  break;
  }
}

void escritura(){ // RUTINA DE AUTENTIFICACION DE LA IDENTIFICACION DEL ESCLAVO
  Wire.write(1);  // Escritura de la identificacion del esclavo
  // Cambiar el 1 por 2 para el caso del Esclavo 2
}
NOTA: En el programa encontrarán diferentes comentarios para cambiar el programa correspondiente al Esclavo 1. El modelo final del programa del Esclavo 2 es el siguiente:
#include <Wire.h>  // Libreria de manejo de red I2C

const int led = 13; // Variable de almacenamiento del pin del puerto de salida del led
char id;            // Variable de almacenamiento de indentificacion

void setup() {
  pinMode(led, OUTPUT);      // Configuracion del Pin 13 como salida
  Wire.begin(2);             // Unirse a la red I2C (Esclavo con direccion "1")
  // Cambiar el 1 por 2 para el caso del Esclavo 2
  Wire.onReceive(lectura);   // Registro de funcion de lectura ante evento
  Wire.onRequest(escritura); // Registro de funcion de escritura ante evento
}

void loop() {
  delay(100);
}

void lectura(int x){           // RUTINA DE IDENTIFICACION FISICA Y AUTENTIFICACION
  while (Wire.available()){    // Verificacion de datos en el puerto I2C
    id = Wire.read();          // Lectura de datos en el puerto I2C
    if (id == 'b'){            // Discriminacion del puntero de identificacion del Esclavo
    // Cambiar 'a' por 'b' para el caso del Esclavo 2
      digitalWrite(led, HIGH); // Encendido del LED del PIN 13  
    }      
  break;
  }
}

void escritura(){ // RUTINA DE AUTENTIFICACION DE LA IDENTIFICACION DEL ESCLAVO
  Wire.write(2);  // Escritura de la identificacion del esclavo
  // Cambiar el 1 por 2 para el caso del Esclavo 2
}

Al abrir el Monitor Serial y colocar "a" o "b" encontraremos en pantalla lo siguiente:


Autor: 

2 comentarios:

  1. Tengo duda con el siguiente caso: Se configura una red maestro y 2 esclavos. El esclavo_1 demora 2 seg en responder mientras que el esclavo_2 demora 1 seg.

    Si el maestro realiza una consulta al esclavo_1 (2s) y luego al esclavo_2 (1s). Hay problema si ambos esclavos responden al mismo tiempo?.

    Saludos.

    ResponderEliminar
    Respuestas
    1. Hay posibilidad que la comunicación se interrumpa y tu maestro ya no reciba más datos. Lo que te recomiendo en ese caso es hacer una subrutina que permita verificar que has reciido el dato de uno para recien llamar al otro esclavo. Para verificarlo podrías usar el tamaño del dato como determinante

      Eliminar