You are on page 1of 81

Copyright © 2017,Rubén Beiroa Mosquera

Todos los derechos reservados.


Queda prohibida la reproducción total o parcial sin el permiso previo y
por escrito del titular del Copyright.

1
Índice
1. 1. Comunicación serie 3
1.1. Vectores 3
1.2. Estructuras 10
1.3. Serial Plotter 33
2. Comunicación I2C 36
3. Comunicación SPI 60
SPI en Arduino 62

2
1. Comunicación serie
En este capítulo, ampliaremos lo aprendido en el segundo tomo del
nivel inicial sobre la comunicación serie. En este tema, empezamos a
trabajar con la forma en la que almacenamos los datos; los arrays y
las estructuras de datos.

Hasta ahora, si almacenábamos datos en diferentes espacios de


memoria, era necesario declarar una variable por cada dato; con los
arrays, este proceso se agiliza, nos permite estructurar los datos. Un
array es un vector que puede almacenar más de un dato; sin
embargo, en un mismo array(vectores) solo puede haber datos del
mismo tipo.

1.1. Vectores

Al igual que una variable, los vectores deben ser declarados. Su


declaración es prácticamente idéntica a la de una variable; por
ejemplo, vamos a declarar un vector de números enteros:

Ilustración 1.

En el programa anterior, declaramos un vector en el cual se pueden


almenar datos tipo «int» y, en concreto, se pueden almacenar 6
datos.

Ilustración 2

3
La primera ventaja con la que nos encontramos es que, para
almacenar, por ejemplo, 6 datos tipo «int», ya no necesitamos
declarar 6 variables.

Ilustración 3

Para poder trabajar con los datos (modificarlos o leerlos), trabajamos


con el índice del vector; es decir, debemos saber en qué posición
hemos almacenado cada dato para, después, acceder a esa posición y
operar con él.

Ilustración 4

4
Ilustración 5

Por defecto, en cada posición, si no «escribimos» nada, muestra un


cero. Como ya explicaremos, podemos declarar de diferentes formas
un vector, pero para escribir datos en una posición determinada, solo
existe una forma: seleccionar la posición del vector y asignar un
valor.

Ilustración 6

En el primer bucle, lo que hacemos es almacenar, en cada posición,


su índice correspondiente sobre el vector. Entonces, para trabajar con
los vectores, tanto sea para leer como para escribir, siempre
debemos hacer referencia a una posición. Además, es necesario tener

5
en cuenta que, si el vector consta de 6 posiciones, estas se empiezan
a enumerar desde la posición cero; por lo tanto, en caso de contener
6 posiciones, estas irían desde la posición 0 a la 5. Otra forma de
declarar un vector es asignando valores en cada posición.

Ilustración 7

Ilustración 8

Otra opción consiste en dimensionar el vector y asignar valores en


ciertas posiciones o en todas.

6
Ilustración 9

Ilustración 10

Por último, al igual que ocurre en matemáticas, los vectores pueden


tener varias dimensiones, lo que los convierte en matrices.

7
Ilustración 11

Ilustración 12

Por lo tanto, si queremos crear tres filas, debemos crear tres campos
(de 0 a 2 posiciones) en el vector y, dentro de cada campo,
definiremos el valor de las columnas. Declararemos de la siguiente
forma, estructurándolo de manera similar a una matriz, para que se
entienda mejor:

8
Ilustración 13

Antes de continuar, tenemos que recordar que, al igual que ocurría


con las variables, los vectores (o matrices) almacenan datos
correspondientes al tipo de vector.

Ilustración 14

Ilustración 15

9
Los vectores de caracteres son de gran uso cuando necesitamos
separar los caracteres de una cadena y trabajar de forma individual
con cada uno de ellos. También disponemos de vectores de cadenas
de caracteres.

Ilustración 16

Ilustración 17

1.2. Estructuras

Con los vectores, podemos simplificar la declaración de variables; aún


así, quizás necesitemos definir, bajo un mismo identificativo,
variables de diferentes tipos, lo cual no podemos realizar con
vectores, puesto que estos solo pueden almacenar valores de un solo
tipo.

10
En consecuencia, debemos emplear estructuras de datos («struct»).
Podemos crear tantas estructuras como necesitemos. «Declarar» una
estructura significa que definimos los campos que la formarán.

Ilustración 18

Para declarar una estructura, siempre se empieza por la palabra


reservada «struct», seguida del nombre de la estructura y, entre
llaves, los datos que la forman.

En el caso anterior, en la posición 0 de «mi_estructura», podemos


almacenar un número entero; en la posición 1, un carácter y, en la
posición 2, un vector de números decimales (que, a su vez, dispone
de 3 posiciones).

Para entender el siguiente paso, vamos a considerar que, al crear una


estructura, estamos creando un tipo de dato nuevo, diferente de
byte, boolean, string, etc.

Ilustración 19

Por consiguiente, al igual que ocurría con la declaración de variables,


ahora podemos declarar estructuras bajo el prefijo de
«mi_estructura» y, cada una de ellas, internamente, dispondrá de los
campos creados anteriormente.

11
Ilustración 20

Ilustración 21

12
Podemos crear diferentes estructuras y, bajo una misma estructura,
crear diferentes variables, vectores o matrices.

Serial.flush();

Al usar «print», «println» o «write», la transmisión de datos se


realiza de forma asíncrona. Esto quiere decir que se ejecuta la
siguiente instrucción sin asegurarse de que se empiece el envío
de datos.

Esta cuestión se soluciona si, después de cualquiera de las tres


instrucciones anteriores(print , println o write) al envío,
escribimos «Serial.flush();». Esta función no devuelve nada,
pero bloquea el programa hasta que el buffer de salida se haya
vaciado, lo que significa que se han enviado todos los datos.

Ilustración 22

13
Ilustración 23

El programa tarda unos 208 𝜇𝑆𝑒𝑔𝑢𝑛𝑑𝑜𝑠 en escribir los datos en el


buffer de salida, pero esto no significa que se hayan enviado
realmente los datos; para ello, incluimos «Serial.flush();» después de
la función de envío.

Ilustración 24

14
Ilustración 25

En consecuencia, el tiempo real que tarda el Arduino en enviar los


datos es aproximadamente 32 216 𝜇𝑆𝑒𝑔𝑢𝑛𝑑𝑜𝑠.

Serial.event()

Esta función está asociada a la recepción de datos por el puerto


serie; en otras palabras, estamos hablando de una interrupción
del puerto serie. La diferencia es que no se sale de la ejecución
normal del programa, se encuentre en el punto que se
encuentre, por la recepción de datos.

Cada vez que finalice la ejecución del void loop, se evalúa si


hay datos en el buffer de entrada; de ser así, el programa salta
a la función «Serial.event()».

15
Ilustración 26

Con el programa anterior, el Arduino nos devuelve todos los


mensajes que se envíen desde el monitor serie.

Serial.availableForWrite();

Esta función devuelve el número de bytes libres en el buffer de


salida; esto nos va a permitir demostrar lo que hemos explicado
hasta ahora: el buffer dispone de 63 posiciones y, al ejecutar
las funciones «Serial.print», «println» o «write», lo que se hace
es escribir datos en el buffer, pero ello no significa que se
hayan enviado, lo que sí conseguimos con «Serial.flush();».

16
Ilustración 27

Ilustración 28

Vamos a comprobar si es cierto eso de que los datos se escriben en el


buffer, pero no es seguro que se envíen.

17
Ilustración 29

El mensaje «Arduino Entendiendo el Mundo» está formado por 28


caracteres (contando los espacios como un carácter más). Si el
«Serial.println()» enviase los datos por el puerto serie, cuando, a
continuación de su ejecución, consultásemos el buffer de salida, nos
debería indicar que dispone de 63 posiciones libres.

Ilustración 30

Como podemos observar, no es así y nos indica que dispone de 35


posiciones libres, lo cual se debe a que aún no se ha enviado ningún
carácter y aún se encuentran en la memoria de salida los 28
caracteres del mensaje.

Asimismo, como ya hemos visto, nos podemos asegurar de que se


envíe todo el mensaje antes de continuar con la ejecución del
programa gracias a «Serial.flush();».

18
Ilustración 31

Ilustración 32

Al incluir el «Serial.flush();» vemos que sí se envía todo el mensaje


antes de continuar con la ejecución del programa.

Serial.end();

Funciona a la inversa que el «Serial.begin();». Mientras el


«Serial.begin();» inicializa la comunicación serie, el «Serial.end();»
cierra la comunicación.

19
Ilustración 33

Con este programa, lo que sucede es que, una vez que se envíe un
mensaje, se inhabilita la comunicación serie, tras lo cual no
devolvería ningún mensaje que enviemos desde el IDE.

Ilustración 34

Ilustración 35

20
Ilustración 36

Serial.peek();

Es una función similar a «Serial.read();», accede al buffer de


entrada y captura el valor de la primera posición del buffer. La
diferencia con «Serial.read();» es que «Serial.peek();» no elimina el
dato del buffer.

Ilustración 37

21
Ilustración 38

Lo que sucedería con el programa anterior es que nunca saldríamos


del bucle.

Ilustración 39

Una vez que recibe datos, entra en este bucle y, como la función
«Serial.peek();» no borra los datos del buffer de entrada, cada vez
que se evalúe el bucle, este detectará que existen datos; por lo tanto,
nunca saldría.

22
Ilustración 40

Serial.setTimeout(parámetro);

Esta función permite establecer un tiempo de espera para las


funciones que veremos a continuación, que operan con la
comunicación serie. Si no se configura el tiempo, por defecto,
toma el valor de 1000, lo que equivale a 1 segundo. Por lo
tanto, al igual que los delay, la unidad de tiempo se mide en
milisegundos.

Serial.parseInt();

Lee y elimina, al igual que «Serial.read();», los datos del buffer


de entrada, hasta que encuentre un número o bien se haya
superado el tiempo de espera establecido por
«Serial.setTimeout();».

Una vez que detecte un número entero, empieza a devolver


todos los números, si los hubiese, a continuación de este, hasta
que se encuentre con un valor no válido (valor no numérico).

23
Ilustración 41

Ilustración 42

En el mensaje anterior, enviamos el número 23. La función estará


buscando a lo largo del mensaje el primer valor válido (numérico),
desechando los valores no válidos y devolviendo, si lo hubiese, el
número recibido.

24
Ilustración 43

Si realizamos la prueba, veremos que nos devuelve el número 23, sin


tener en cuenta el resto de caracteres y eliminándolos del buffer. Si,
por el contrario, enviamos un mensaje sin ningún número, la función
nos devolverá un 0.

Ilustración 44

Ilustración 45

25
Ilustración 46

Si enviamos un número en medio de una cadena de caracteres, lo


que hará la función es desechar los caracteres que se encuentren
antes del número; una vez llegue al número, lo captura y, al detectar
un carácter no numérico, deja de leer del buffer.

No obstante, aún quedan datos en el buffer; por lo tanto, al volver a


finalizar la ejecución del void loop, volverá a entrar en la interrupción,
pero en este caso, en el buffer solo encontrará caracteres no
numéricos («el mundo») y nos devolverá un 0.

Ilustración 47

Serial.parseFloat();

Similar a «Serial.parseInt();», pero busca números decimales,


desechando todo lo que sea diferente. Si se envía un numero entero,
lo considerará decimal y le añade «.00».

Ilustración 48

26
Ilustración 49

Ilustración 50

Serial.readBytes(parámetro1,parámetro2);

Esta función permite fijar el número máximo de datos que se


lee del buffer de entrada, por ejemplo: aunque dispongamos de
35, podemos indicar que solo se lean 10 datos y se almacenen
en un vector. En el primer parámetro se indica en qué vector se
almacenan los datos y, en el segundo parámetro, se define el
número de bytes que se leen del buffer de entrada.

La función devuelve el número de bytes leído, puesto que


también puede ser que se reciban cinco datos y en la función
definamos que hay que leer diez. De no contar con el número
de bytes suficiente, se leen los disponibles y se continúa con la
ejecución del programa.

Ilustración 51

27
Ilustración 52

Ejercicio propuesto:

1. Conectar dos leds al Arduino; a través de un mismo mensaje,


controlar el encendido de uno y regular el otro.

28
29
Serial.readBytesUntil(parámetro1,parámetro2,parámetro3);

A diferencia de «Serial.readBytes(,);», añade un parámetro que


permite definir un carácter por el cual se dejarían de almacenar
datos.

• Parámetro1: carácter fin de lectura.


• Parámetro2: vector en el que se almacenan los datos.
• Parámetro3: número máximo de bytes que se debe leer.

Ilustración 53

Ilustración 54

30
Serial.readString();

Lee y almacena los datos del buffer de entrada en una cadena


«interna». Una vez vaciado el buffer, devuelve todos los datos leídos.

Ilustración 55

Si cargamos el programa anterior, el Arduino devuelve todos los


mensajes que enviemos por el monitor serie.

Serial.readStringUntil(parámetro);

Lee y almacena los datos del buffer de entrada en una cadena


«interna». Deja de leer una vez vaciado el buffer o bien porque se
encuentra el carácter definido como fin de lectura introducido como
parámetro.

31
Ilustración 56

Ilustración 57

Serial.find(parámetro);

Lee del buffer de entrada los caracteres recibidos en busca de un


carácter o cadena de caracteres especificado como parámetro; de
encontrarlo, esta función devuelve un 1.

32
Ilustración 58

Ilustración 59

1.3. Serial Plotter

El IDE de Arduino dispone de una herramienta para graficar los datos


que reciba por el puerto serie.

33
Ilustración 60

Ilustración 61

Ilustración 62

34
Para graficar varios datos:

Ilustración 63

Ilustración 64

35
2. Comunicación I2C
La comunicación I2C es un protocolo presente en muchos módulos
que podemos encontrar para Arduino: reloj, LCD, etc. Por eso vamos
a analizar este protocolo de comunicaciones, desde un punto de vista
general, entendiendo el funcionamiento de este protocolo, puesto
que, para cada módulo, disponemos de una librería concreta para
facilitar su programación.

El protocolo I2C se caracteriza por utilizar dos cables para transmitir


información: SDA, que es la línea por donde se transmiten los datos,
y SCL, que es la señal que sirve para sincronizar todos los
dispositivos que se comuniquen mediante este protocolo.

Este protocolo utiliza la comunicación maestro-esclavo: este concepto


se refiere al papel que desempeña cada dispositivo en el protocolo. El
maestro es aquel que gobierna la comunicación, estableciendo la
transmisión y/o recepción de datos con cualquiera de los esclavos
que se encuentren conectados. De entre todos los dispositivos que se
comuniquen por este protocolo, únicamente puede haber uno que se
comporte como maestro. Además, un dispositivo configurado como
esclavo es aquel que transmite y/o recibe información siempre y
cuando así lo determine el maestro.

La velocidad estándar de transferencia de datos es de 100 Kbits por


segundo (se pueden alcanzar velocidades de hasta 3,4 Mbit/s). Como
ya se ha indicado, disponemos de una única línea para la transmisión
o recepción de datos entre el maestro y sus esclavos; por lo tanto, es
una transmisión «hall dúplex» o, lo que es lo mismo, la transmisión
solo se puede establecer en un sentido. En consecuencia, en el
momento en el que uno de los dispositivos reciba una comunicación,
tendrá que esperar a que finalice para responder.

Con la estructura que se sigue en este protocolo, es necesario que


cada esclavo se encuentre identificado para que el maestro pueda
establecer, en cada momento, una transferencia de datos con un
único esclavo. Con este protocolo, un maestro se puede comunicar
con hasta 128 esclavos, así que cada uno de ellos tendrá asociada
una dirección entre 0 y 127 para que, en el momento indicado, el
maestro puede inicializar la comunicación con el esclavo
correspondiente.

En el Arduino UNO los pines SDA y SCL se corresponden con el A4 y


A5, respectivamente.

36
Ilustración 65

Por lo tanto, entre dos Arduinos que se quieran comunicar por este
protocolo, deben conectarse de acuerdo con el sistema anterior; es
necesario unir las masas (GND-GND) siempre y cuando los
dispositivos trabajen a través de diferentes fuentes de alimentación.
Para operar con este protocolo, necesitamos usar la librería «wire».

Ilustración 66

Wire.begin();

Esta función nos permite inicializar la comunicación I2C.


Además, permite configurar un dispositivo como maestro o
esclavo.

• Como maestro:
«Wire.begin();». Si no pasamos parámetros a la
función, configura el dispositivo como maestro.

• Como esclavo:
«Wire.begin(4);» Si configuramos el dispositivo con
un identificativo, como es el caso, este queda
configurado como esclavo y con la dirección 4
asociada.

37
Ilustración 67

Con el programa anterior, inicializamos la comunicación I2C y


configuramos como maestro nuestro Arduino.

Ilustración 68

Por el contrario, con el programa anterior, inicializamos la


comunicación I2C configurando nuestro Arduino como esclavo, con la
dirección 4 asociada.

Wire.beginTransmission();

Esta función inicia la comunicación de un maestro con un


esclavo (especificado por parámetro).
∗ «Wire.beginTransmission(4);». Comunicación esclavo 4.
La comunicación se inicia tanto para enviar un mensaje a un
esclavo como para que este pueda enviar datos al maestro. Por
lo tanto, esta función se usa únicamente en un Arduino
configurado como maestro.

38
Wire.endTransmission();

Función sin parámetros, que finaliza la comunicación I2C del


maestro con cualquiera de sus esclavos. Por consiguiente, esta
función se usa únicamente en un Arduino configurado como
maestro.

Wire.onReceive(parámetro);

La función «Wire.onReceive();» indica qué función, pasada por


parámetro, «salta» al establecer el maestro una comunicación
con el Arduino configurado como esclavo. Por lo tanto, esta
función se usa únicamente en un Arduino configurado como
esclavo.

Wire.available();

Al igual que «Serial.available();» en la comunicación serie, que


indica si hay datos de entrada, esta función indica si hay datos
por leer en una comunicación I2C. Esta función se emplea tanto
con un maestro como con un esclavo.

Wire.read();

Al igual que «Serial.read();», lee los datos de entrada. Esta


función la utiliza tanto un maestro como un esclavo.

Wire.write();

Al igual que «Serial.write();», envía datos. Esta función la


emplea tanto un maestro como un esclavo.

El primer programa que realizaremos para empezar a trabajar con el


protocolo I2C consistirá en que un maestro incremente y envíe el
valor de un contador a un esclavo y este lo muestre por el monitor
serie.

39
Ilustración 69

Con los Arduinos conectados como ya hemos visto, cargamos el


siguiente programa al Arduino que se va a comportar como maestro.

Ilustración 70

Con el maestro programado, cargamos el siguiente programa en otro


Arduino para que se comporte como esclavo y muestre, por el
monitor serie, el valor del contador que envía el maestro.

40
Ilustración 71

Con ambos Arduinos programados y con el esclavo conectado a


nuestro PC, podemos abrir el monitor serie y comprobar si,
efectivamente, recibe algún dato del maestro.

Ilustración 72

41
Ejercicios propuestos:

2. Configurar dos Arduinos: uno como maestro y otro como


esclavo. En este caso, conectamos un botón al maestro y un led
al esclavo. Si se pulsa el botón, el led debe parpadear.

3. Configurar dos Arduinos: uno como maestro y otro como


esclavo. En este caso, conectamos un potenciómetro al maestro
y un led al esclavo. El led del esclavo debe regularse en función
del valor del potenciómetro del maestro.

42
2. Configurar dos Arduinos: uno como maestro y otro como
esclavo. En este caso, conectamos un botón al maestro y un
led al esclavo. Si se pulsa el botón, el led debe parpadear.

A
B

43
B

44
3. Configurar dos Arduinos: uno como maestro y otro como
esclavo. En este caso, conectamos un potenciómetro al
maestro y un led al esclavo. El led del esclavo debe regularse
en función del valor del potenciómetro del maestro.

B
A

45
B

46
Wire.requestFrom(parámetro1,parámetro2);

Esta función permite la transmisión de datos del esclavo al


maestro. Trabaja con dos parámetros:

• Primer parámetro: el esclavo con el cual se comunica.


• Segundo parámetro: el número de bytes que ha de
recibir.

Wire.onRequest(parametro);

Esta instrucción configura qué función se ejecuta cada vez que


el maestro «pida» datos al esclavo.

A continuación, lo que haremos será que el esclavo envíe, cada vez


que el maestro le pida datos, el mensaje «Arduino Entendiendo el
mundo».

Ilustración 73

Siempre que se pidan datos de un maestro a un esclavo, es necesario


conocer el número de bytes que se van a recibir.

47
Ilustración 74

Si conectamos el maestro a nuestro PC y abrimos el monitor serie,


debemos visualizar el mensaje que nos está enviando continuamente
el esclavo.

Ilustración 75

48
Ejercicios propuestos:

4. Con un Arduino configurado como maestro y otro como esclavo,


conectar un botón al esclavo y un led al maestro. El led debe
parpadear si se ha pulsado el botón.

5. Con un Arduino configurado como maestro y otro como esclavo,


conectar un potenciómetro al esclavo y un led al maestro. El led
debe regularse en función del potenciómetro.

6. Realizar un chat entre Arduinos a través de comunicación I2C.

7. Con un maestro y dos esclavos, conectar un potenciómetro en


uno de los esclavos y un led en el otro. Regular el led de un
esclavo en función del potenciómetro del otro.

49
4. Con un Arduino configurado como maestro y otro como
esclavo, conectar un botón al esclavo y un led al maestro. El
led debe parpadear si se ha pulsado el botón.

A
B

50
A

51
5. Con un Arduino configurado como maestro y otro como
esclavo, conectar un potenciómetro al esclavo y un led al
maestro. El led debe regularse en función del potenciómetro.

A
B

52
A

53
6. Realizar un chat entre Arduinos a través de comunicación
I2C.

A B

54
A

55
B

56
B

57
7. Con un maestro y dos esclavos, conectar un potenciómetro
en uno de los esclavos y un led en el otro. Regular el led de un
esclavo en función del potenciómetro del otro.

B C
A

58
B

59
3. Comunicación SPI
SPI (Serial Peripheral Interface): al igual que el sistema I²C, el
sistema de comunicación SPI es un estándar que permite controlar (a
cortas distancias) casi cualquier dispositivo electrónico digital que
acepte un flujo de bits serie sincronizado (es decir, regulado por un
reloj), con un sistema maestro-esclavo. Una de las principales
diferencias con el I2C es que, como mínimo, necesita cuatro líneas
para establecer una comunicación.

Ilustración 76

∗ SCLK: señal de reloj.


∗ SS: identificar el dispositivo esclavo.
∗ MOSI: línea de comunicación maestro-esclavo.
∗ MISO: línea de comunicación esclavo-maestro.

Al disponer de dos líneas diferentes para enviar información de un


maestro a un esclavo y de un esclavo a un maestro, nos encontramos
ante un protocolo «full dúplex» o, lo que es lo mismo, una
comunicación bidireccional instantánea.

Otra de las diferencias que nos encontramos con el I²C es que los
esclavos no disponen de una dirección asociada. Para establecer una
comunicación con un determinado esclavo, es necesaria una línea
independiente para cada uno que «active» la comunicación con ese
esclavo.

60
Ilustración 77

Como vemos en el caso anterior, necesitamos una línea SS por cada


esclavo para activar la comunicación con dicho esclavo.

Ventajas e inconvenientes

∗ Ventajas:
∗ Alta velocidad de transmisión (hasta 8 Mhz en Arduino) y
«full dúplex».
∗ Puede enviar secuencias de bits de cualquier tamaño sin
dividir y sin interrupciones.
∗ Desventajas:
∗ Se necesitan tres cables (SCK, MOSI, MISO) y uno más
por cada esclavo (SS).
∗ No se dispone de ningún mecanismo de control, no se
sabe si ha sido recibido ni si ha sido recibido
correctamente.
∗ La longitud de los mensajes tienen que ser conocida por
ambos dispositivos.

61
SPI en Arduino

Dispone de pines «vinculados» a comunicación SPI; se pueden


configurar otros, pero la velocidad será menor. Los pines según el
modelo de Arduino varían; en el caso del Arduino UNO:

• 10–SS.
• 11-MOSI.
• 12-MISO.
• 13-SCK.

El pin SS por hardware se emplea para configurar el Arduino como


esclavo. En caso de trabajar con el Arduino como maestro, se puede
usar cualquier pin, como SS, y varios en caso de tener varios
esclavos.

Al igual que ocurría con el protocolo I²C, disponemos de una librería


para trabajar con el protocolo SPI. Trabajaremos con él como hicimos
con el I²C. Hay que tener en cuenta que gran cantidad de módulos se
comunican por SPI y tienen librerías específicas para establecer la
transmisión de información (Ethernet, SD, TFT, etc).

Ilustración 78

SPI.begin();

Arranca la comunicación SPI, estableciendo las salidas SS


configuradas y situándolas a nivel bajo.

62
Ilustración 79

SPI.transfer();

Esta función permite enviar datos con un tamaño máximo de un


byte:
∗ boolean
∗ byte
∗ char

En el siguiente caso, vamos a encender un led conectado a un


Arduino que se encuentre configurado como esclavo, en función de
un botón conectado a otro Arduino configurado como maestro.

Ilustración 80

63
Ilustración 81

Lo primero que haremos es cargar el programa anterior al Arduino


que se comporte como maestro. Configuramos el pin 10 como salida,
puesto que es el que va a establecer la comunicación con el esclavo.
Si el pin se encuentra activado, no es posible establecer la
transferencia de información; por eso, antes de finalizar el
void setup, ponemos a nivel bajo la salida, para que quede activada
la transferencia de información con el esclavo.

Con la parte del maestro vista y, antes de continuar, debemos ver


algunas funciones necesarias para programar el esclavo:

SPCR | =_BV(SPE);

Configura el Arduino como esclavo.

SPI.attachInterrupt();

Activa la interrupción de recepción de datos.

ISR (SPI_STC_vect){}

Función asociada a la interrupción.

SPDR

Registro en el que se almacena el byte recibido (tipo


boolean, byte o char).

64
Con estas funciones definidas, podemos empezar a programar
nuestro esclavo. Lo primero que programaremos serán las
configuraciones:

Ilustración 82

Arrancamos la comunicación SPI, configuramos el pin 10 como


entrada, puesto que es el que recibe la señal de inicio y fin de
transferencia de datos. A continuación, configuramos el Arduino como
esclavo, «SPCR |= _BV(SPE);», activamos la interrupción asociada a
la recepción de datos, «SPI.attachInterrupt();» y, por último,
configuramos el pin 2 como salida, puesto que es el que se va a
utilizar para encender o apagar el led.

Ilustración 83

Con las configuraciones realizadas, podemos programar el


comportamiento del Arduino. Cada vez que el maestro envíe datos,
se ejecutan las líneas de código que se encuentren dentro de la
interrupción asociada a este protocolo, «ISR (SPI_STC_vect){}».

Una vez que se entra en la interrupción, almacenamos la información


que hemos recibido del maestro y, de manera similar a lo que ocurría
con la comunicación serie, en la que, lo que se recibe, se almacena

65
en un buffer de entrada, en este caso, se almacena en el registro
SPDR. El programa final del esclavo es el siguiente:

Ilustración 84

A continuación, vamos a controlar la regulación de un led conectado a


un Arduino configurado como esclavo, con un potenciómetro
conectado a un Arduino maestro.

66
Ilustración 85

Debemos tener en cuenta que solo podemos enviar información que


ocupe como máximo un byte; por lo tanto, no podemos enviar el
valor del potenciómetro. Lo que haremos será enviar directamente el
valor de la señal PWM.

67
Ilustración 86

Ejercicios propuestos:

8. Con tres Arduinos (dos configurados como esclavos y uno como


maestro), realizar el siguiente caso:
• El maestro dispone de un botón y un potenciómetro.
• Cada esclavo dispone de un led.
Encender el led de uno de los esclavos con el botón del maestro
y regular el led del otro Arduino con el potenciómetro del
maestro.

68
8. Con tres Arduinos (dos configurados como esclavos y uno
como maestro), realizar el siguiente caso:
• El maestro dispone de un botón y un potenciómetro.
• Cada esclavo dispone de un led.
Encender el led de uno de los esclavos con el botón del
maestro y regular el led del otro Arduino con el potenciómetro
del maestro.

69
A

70
C

Ilustración 87

71
El hecho de solo poder enviar un dato de un tamaño máximo de un
byte nos limita, pero con lo que ya sabemos sobre programación,
podemos buscar la forma de superar esta limitación.

En el siguiente caso, enviaremos un mensaje cada cierto tiempo del


maestro al esclavo y, una vez que este último reciba el mensaje al
completo, lo mostrará por en el monitor serie.

Ilustración 88

Ilustración 89

72
Si analizamos el programa anterior (que se corresponde con el
programa del maestro), recorremos la cadena de caracteres y los
enviamos uno a uno; una vez finalizado el mensaje, enviamos una
cadena «\r» para que el esclavo confirme que el mensaje ha
finalizado.

73
Ilustración 90

Mientras se reciban datos, se irán almacenando en una cadena de


caracteres; una vez que se reciba «\r», significa que se ha recibido el
mensaje completo y, cuando se vuelva a la parte principal del
programa, se envía por el puerto serie para que se visualice por el
monitor serie.

74
Ilustración 91

Ejercicio propuesto:

9. Con dos Arduinos (uno configurado como maestro y otro como


esclavo), el maestro reenvía los mensajes que recibe por el
monitor serie al esclavo y este, a su vez, almacena esos
mensajes y los muestra por el monitor serie.

75
9. Con dos Arduinos (uno configurado como maestro y otro
como esclavo), el maestro reenvía los mensajes que recibe
por el monitor serie al esclavo y este, a su vez, almacena esos
mensajes y los muestra por el monitor serie.

76
77
78
Sabemos cómo enviar datos de un maestro a un esclavo. Para
finalizar, analizaremos el proceso inverso. Para ello, repasamos las
siguientes funciones:

SPCR | =_BV(SPE);

• Configura el Arduino como esclavo.

SPI.attachInterrupt();

• Activa la interrupción de recepción de datos.

ISR (SPI_STC_vect){}

• Función asociada a la interrupción.

SPDR

• Registro en el que se almacena el byte recibido (tipo boolean,


byte o char). Si se sobrescribe, envía el byte al maestro.

A continuación, vamos a encender un led conectado a un Arduino que


se encuentre configurado como maestro, en función de un botón
conectado a otro Arduino configurado como esclavo.

Ilustración 92

79
Ilustración 93

80
Ilustración 94

81

You might also like