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.
# 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
# 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.
/* 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:
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.
ResponderEliminarSi 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.
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