Cómo conectar Arduino y Raspberry Pi por comunicación SPI

En este pequeño tutorial veremos cómo conectar una tarjeta Arduino a una tarjeta Raspberry Pi por comunicación SPI, encontrarás códigos de programación para tus primeras pruebas.

La comunicación SPI se implementa con 4 señales digitales:

  • SCLK (Clock): Es el pulso que marca la sincronización. Con cada pulso de este reloj, se lee o se envía un bit.
  • MOSI (Master Output Slave Input): Salida de datos del Maestro y entrada de datos al Esclavo.
  • MISO (Master Input Slave Output): Salida de datos del Esclavo y entrada al Master.
  • SS/Select: Para seleccionar un Esclavo, o para que el Maestro le diga al Esclavo que se active. Se requiere uno por cada Esclavo.

Tanto la tarjeta Arduino Uno como las tarjetas Raspberry Pi cuentan con hardware integrado para implementar el protocolo. Como lo indica la descripción de las señales, los dispositivos comunicados por SPI pueden ser «Maestros» o «Esclavos», siempre habrá un solo Maestro mientras que el número de esclavos puede ir desde 1 hasta el número de pines con los que podamos implementar señales SS

El ejercicio que realizaremos consiste, entonces, en controlar un LED conectado al pin #4 del Arduino desde la Raspberry Pi. Para ello ejecutaremos un script de python que solicite al usuario insertar un comando para ser enviado al Arduino a través del protocolo SPI, éste a su vez, estará programado para recibir todos los comandos respondiendo solo a 2: «on» para encender el LED y «off» para apagar el LED.

Componentes necesarios

  • Raspberry Pi, cualquier modelo
  • Arduino UNO
  • Protoboard mini o chica
  • Resistor de 330 ohms
  • Led 5mm
  • Cables de conexion M-M y H-M
  • Convertidor de nivel lógico bidireccional – 3.3V <-> 5V TTL

Conexiones

Utilizamos un convertidor de nivel lógico para conectar las señales de ambas tarjetas, esto es importante porque trabajan con distintos niveles lógicos de voltaje, el Arduino tienen un nivel lógico de 5V y la Raspberry Pi de 3.3V. Conectar las señales directamente podría resultar en un daño a la tarjeta con nivel lógico menor, es decir, la Raspberry Pi.

Raspberry Pi – Arduino Uno – SPI

Pasos previos a ejecutar el código

Para que podamos ejecutar un programa que intercambie información a través del protocolo SPI en nuestra Raspberry Pi, debemos habilitar el hardware correspondiente en dicha tarjeta. Esto lo podemos hacer de manera sencilla con ayuda de la herramienta de configuración raspi-config, si no has utilizado esta herramienta antes da click aquí para ver el paso a paso. Una vez que termines regresa para continuar con este tutorial.

Código de Arduino – Ardiuno Uno

Cargamos el siguiente código a nuestra tarjeta Arduino Uno:



#include <SPI.h>

char buf [100];//Definimos el arreglo de caracteres que será nuestro buffer de recepción 
volatile byte pos; //Indice auxiliar para administrar el índice del arreglo "buf"
volatile boolean process_it;  //Bandera auxiliar para saber cuando hemos recibido un comando 
char on[] = {111,110,10};   //Arreglo de carácteres correspondientes a la cadena "on\n"
char off[] = {111,102,102,10}; //Arreglo de carácteres correspondientes a la cadena "off\n"
int pin_led = 14; //Pin al cual se conecto la señal del led

void setup (void)
{
  Serial.begin (9600);   
  pinMode(pin_led, OUTPUT);
  
  // Configuramos como salida el pin correspondiente a la salida de datos por parte del esclavo
  pinMode(MISO, OUTPUT);
  
  // Establecemos el modo de esclavo
  SPCR |= _BV(SPE);
 
  pos = 0;             // buffer vacio
  process_it = false;  //bandera auxiliar para indicar cuando un comando completo se encuentra en buffer

  // Habilitamos interrupciones por SPI
  SPI.attachInterrupt();

}  // fin de setup


// Rutina de interrupción SPI
ISR (SPI_STC_vect)
{
   byte c = SPDR;  // tomamos el byterecibido que se encuentra en el registro de datos SPI
  
  //si contamos con espacio agregamos el byte a la cadena de caracteres
  if (pos < sizeof buf)
    {
    buf [pos++] = c; //Agregamos el caracter recibido 
    
    // Si el caracter actual es un salto de línea sabemos que hemos recibido un comando completo 
    if (c == '\n')
      process_it = true;//Levantamos la bandera
      
    }  
}  // fin de la rutina de interupción SPI

void loop (void)
{
  if (process_it)//Si la bandera esta levantada procesamos el comando recibido
    {
    if(array_cmp(on,buf,3,pos)) digitalWrite(pin_led, 1);
    if(array_cmp(off,buf,4,pos)) digitalWrite(pin_led, 0);
    pos = 0;
    process_it = false;
    }  // fin del procesamiento del comando recibido
    
}  // fin del loop


boolean array_cmp(char *a, char *b, int len_a, int len_b){
     int n;

     // Si la longitud es distinta regresa FALSO y terminamos la función
     if (len_a != len_b) return false;

     // Comparramos pares de bytes formados por los los elementos que tienen el mismo índice en sus respectivos arreglos
     //Si algún par está formado por bytes distintos, regresa FALSO y termina la función
     for (n=0;n<len_a;n++) if (a[n]!=b[n]) return false;

     //Si llegamos hast aqui es porque los arreglos son identicos
     return true;
}//fin de la función

Código de Python3 – Raspberry Pi

Copiamos el siguiente código a un script de python en nuestra Raspberry Pi y lo ejecutamos con Python3. Al ejecutarlo, el programa solicitará ingresar un comando, el comando será enviado al Arduino al oprimir la tecla «Enter». Con el comando «on» se enciende el led integrado del Arduino y con el comando «off» se apaga.


# Importamos los paquetes necesarios
import RPi.GPIO as GPIO #Para controlar pines de la tarj$
import spidev #Para implementar comunicación SPI
import time

GPIO.setmode(GPIO.BCM) #Definimos el modo para referirnos a los pines de la Raspberry Pi

spi = spidev.SpiDev() #Creamos el objeto spi
spi.open(0,0) #Abrimos el puerto SPI - Módulo 0, Dispositivo 0
spi.max_speed_hz = 5000 #Establecemos la velocidad máxima -->muy importante<--
try:
    while True:
        comando = input("Ingresar comando (on/off): ") #Solicitamos ingresar comando
        comando = comando + "\n" #Agregamos salto de línea al final del comando ingresado
        comando = comando.encode() #Convertimos el comando a un arreglo de bytes
        spi.xfer(comando) #Mandamos el comando
        time.sleep(0.25) #Esperamos 0.25

except KeyboardInterrupt:
    # Ctrl+C
    print ("Interrupción por teclado")
finally:
    spi.close()
    GPIO.cleanup()
    print ("GPIO.cleanup() y spi.close() ejecutados ")

¡Sigue explorando!