viernes, 4 de octubre de 2013

Raspberry Pi procesando eventos MIDI con Python (Parte 1)

Otro de mis pasatiempos, y para el que no soy muy bueno por cierto, es la música. He intentado aprender a tocar algunos instrumentos pero no he tenido la paciencia necesaria para desarrollar las habilidades, sin embargo gracias a familiares cercanos que viven de la música he aprendido un poco de las bases y esto me ha servido para poder ayudarles en el escenario como parte de su staff conectando cables tanto de audio como MIDI, llevandome a trasladar esos conocimientos a mi area de comfort, las computadoras.


Las computadoras y el sonido


Desde los inicios de la computación casera, las computadoras han sido capaces de generar sonido, las antiguas computadoras traian una bocinita integrada al chasis, que era incapaz de reproducir la gran gama de sonidos que actualmente son capaces de reproducirse gracias a las tarjetas de sonido y sintetizadores integrados.


Bocina integrada al chasis de la computadora

Al dia de hoy, es muy sencillo y hasta natural esperar sonido de alta definición en nuestras computadoras ya sea originado por procesamiento de ondas o por síntesis.

Esto último es importante entenderlo y diferenciarlo para el alcance de este proyecto.


Archivos de onda


La reproducción de archivos de onda es la generación de sonido teniendo como datos de entrada los valores de los muestreos de la amplitud de onda a determinada frecuencia, es decir, los valores discretos de la onda a una frecuencia y resolucion determinadas, un ejemplo de esto es la reproducción de un CD de música y archivos WAV (entre otros) que tienen valores típicos de frecuencia de muestreo de 44.1 KHz (o 44100 muestras por segundo) con una resolución de 16 bits (2^16 = 65536) para los valores de la amplitud de onda por cada canal.


Discretización de una onda análoga

Una de las ventajas de este acercamiento es que la reproducción es independiente del dispositivo y debería de sonar igual en todos lados (sin considerar variables como amplificadores o altavoces) pues la onda original se esta reconstruyendo de la manera mas fiel posible con referencia a la fuente.

Estos archivos suelen ser muy grandes en tamaño para mantener su fidelidad y son ideales para ser procesados en cualquier DAW (Digital Audio Workstation).


Archivos MIDI


MIDI es el acrónimo en ingles para Musical Instrument Digital Interface que describe todo un protocolo, interfaz digital y conectores que permiten el intermabio de información entre instrumentos musicales electrónicos, computadoras y dispositivos compatibles.

La tecnología MIDI fue estandarizada en 1983, por lo que debemos de entender que es algo un poco viejo, sin embargo es un estandar actualmente muy utilizado en la industria musical actual, a pesar de sus 30 años de vida.

Si han puesto atención habrán notado que hasta el momento jamas he hablado de sonido relacionado con MIDI, y es porque el sonido no se transmite a traves de las conexiones MIDI, lo unico que viaja entre dispositivos son mensajes MIDI que el dispositivo receptor interpretará de acuerdo a su implementación, esto puede ser algo como un tick de reloj o el evento de reproducir la nota B# de un piano de cola por 123 milisegundos, que hacer con cierto mensaje, depende totalmente de quien recibe el mensaje, nada mas.

El sonido generado por un dispositivo MIDI es una característica muy particular del dispositivo en si, es decir, si uno tiene dos sintetizadores MIDI de diferente marca y ambos reciben el mismo mensaje de reproducir una cierta nota de un instrumento especifico, no sonarán igual, y es posible que aún dos dispositivos de la misma marca y modelo, expuestos a diferentes situaciones de trabajo, temperatura y uso generen sonido diferente para el mismo evento.

La razón del comportamiento anterior es que el sonido es generado por la circuitería que sintetiza la onda del sonido solicitado, por lo que cada implementación es única. Existe también la opcion de sintetizar el sonido por software, y esta es la opción que mas adelante nosotros utilizaremos.

Ya para finalizar solo queda mencionar que el tamaño de los archivos MIDI es muy pequeño, pues solo guarda los mensajes que deben ser enviados, no el sonido en si, para escucharlos se requiere de un sintetizador, ya sea por software o por hardware y que a pesar de todo tienen grandes ventajas como poder variar los instrumentos que se reproducen, cambiar el tempo y hasta generar la partitura de la música almacenada.


Sonido en la Raspberry Pi


Ya que tenemos conocimiento de las bases sobre las que sustentaremos nuestro proyecto, comenzaremos con la configuración del sonido en la Raspberry Pi.

Dependiendo de la versión de Raspbian instalada en su computadora, el módulo de sonido puede estar o no configurado por default. Para saberlo, haremos la siguiente prueba:

lsmod | grep snd_bcm2835

Si este comando nos devuelve algo, significa que el módulo esta cargado en memoria y puedes evitar los siguientes pasos de esta sección.

Al leer esta linea, asumo que el módulo de sonido no esta instalado y que necesitas un poco de guia al respecto, asi que sigamos los siguientes pasos.

Primero, instalar ALSA que es el acrónimo de Advanced Linux Sound Architecture, para esto, hay que ejecutar los siguientes comandos para instalar ALSA y sus utilerias y posteriormente reiniciar el equipo:
$ sudo apt-get install alsa-utils
$ sudo reboot

Espera que la Raspberry Pi reinicie, una vez reiniciada, cargaremos el módulo de sonido de manera manual
  1. $ sudo modprobe snd_bcm2835

y lo configuraremos para que utilice la salida de audio de 3.5mm

  1. $ sudo amixer cset numid=3 1

En este punto deberiamos de ser capaces de escuchar algo al conectar bocinas o audifonos a la computadora. Podemos probar instalando el reproductor de linea de comando mpg321, el siguiente comando lo hará por nosotros:

$ sudo apt-get -y install mpg321

Una vez instalado mpg321 y sus dependencias, descarguemos un MP3 y reproduzcamoslo:

$ wget http://www.flashkit.com/imagesvr_ce/flashkit/soundfx/Cartoon/Laughter/demonic_-daniel_a-8513/demonic_-daniel_a-8513_hifi.mp3

$ mpg321 demonic_-daniel_a-8513_hifi.mp3

Debes de haber escuchado una risa malevola como la que cualquiera haría una vez que ha configurado el sonido por primera vez en linux :)

Por el momento dejare hasta aqui esta entrada, sabiendo que quienes estan siguiendola ya tienen el sonido configurado, en la segunda parte nos dedicaremos a la parte MIDI y a procesarlos con Python para obtener algo similar a esto



Como siempre, si tienen cualquier duda o comentario, no duden en hacerlo!

Saludos y hasta la próxima!

sábado, 28 de septiembre de 2013

Raspberry Pi ejecutando MAME controlado con un WiiMote

El día de hoy, después de muchos meses sin actualizar el blog, quiero compartirles este proyecto que puede ser bastante atractivo en mas de un sentido, pues si eres gamer retro o tienes curiosidad en entrar en el mundo del control inalámbrico e interactivo esto te puede dar algunas ideas.


Los requerimientos


Lo primero que tenemos que saber es que el control WiiMote se comunica de manera inalámbrica usando Bluetooth, lo cual nos lleva a tener como requisito un dispositivo Bluetooth USB compatible.

El segundo requerimiento es obviamente el emulador MAME, la version que se ejecuto al grabar este video fue instalada siguiendo las instrucciones de este sitio http://blog.sheasilverman.com/2012/06/raspberry-pi-and-mame-part-2/ , una vez instalado el emulador. necesitaremos algunas ROMS para jugar, en este enlace pueden descargar algunas de estas ROMS que son 100% legales http://www.mamedev.org/roms/ .

El siguiente requisito es cargar en memoria el módulo de kernel uinput con el siguiente comando:

sudo modprobe uinput

tras la ejecución del comando, si no están firmados en el sistema como root, les pedirá su contraseña, y una vez introducida, no se obtendrá mas salida. Para confirmar que el módulo esta cargado, ejecutamos el siguiente comando:


lsmod

este nos listará los módulos que esten cargados en memoria, y su salida será algo similar a esto:

Module                  Size  Used by
uinput                  6599  0 
snd_bcm2835            16432  0 
snd_pcm                77728  1 snd_bcm2835
snd_seq                53482  0 
snd_timer              20110  2 snd_pcm,snd_seq
snd_seq_device          6462  1 snd_seq
snd                    58744  5 snd_bcm2835,snd_timer,snd_pcm,snd_seq,snd_seq_device
snd_page_alloc          5169  1 snd_pcm
leds_gpio               2243  0 
led_class               3570  1 leds_gpio

MAME funciona por default con el teclado, es decir, ciertas teclas se usan para control de dirección y las diferentes acciones que se requieran en el juego, por lo que necesitaremos crear un "traductor" que reciba la información del WiiMote a traves del dispositivo Bluetooth y envie al emulador el evento de teclado adecuado de acuerdo a la acción.

El Código


De nuevo recurriremos a Python para este objetivo, la libreria cwiid es la que tiene toda la lógica para la comunicación con el WiiMote, y la libreria uinput es la que nos permitirá generar los eventos de teclado  para que MAME los use como entrada.

Antes de iniciar con el código, quiero darle credito a Brian Hensley por su script wiimotest.py y a Chris Swan por su script rpi-gpio-jstk.py en los cuales me basé para crear este código. 

#-------------------------------------------------------------------------------
# Name:        Wii Mame
# Purpose:     Script that connects a Wiimote via Bluetooth using cwiid to read
#              inputs and send keystrokes using uinput
#
# Author:      Ismael Martinez based on scripts
#                   wiimotest.py by Brian Hensley and
#                   rpi-gpio-jstk.py by Chris Swan 
#
# Created:     01/10/2012
# Copyright:   OpenSource
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import cwiid
import time
import sys
import uinput

def main():
events = (uinput.KEY_LEFT, uinput.KEY_RIGHT, uinput.KEY_UP, uinput.KEY_DOWN, uinput.KEY_1, uinput.KEY_5, uinput.KEY_LEFTCTRL, uinput.KEY_ESC, uinput.KEY_ENTER)

device = uinput.Device(events)

# Bools to keep track of movement
btn_a = False
btn_b = False
btn_one = False
btn_two = False
btn_pls = False
btn_min = False
btn_up  = False
btn_dwn = False
btn_lft = False
btn_rgt = False
debug = False

        attempt = 0
max_attempts = 3
        while attempt < max_attempts:
            attempt = attempt + 1
            print 'Press button 1 + 2 on your Wii Remote...'
            time.sleep(1)
            try:
                wm=cwiid.Wiimote()
                break
            except:
                if attempt <= max_attempts:
                    print 'Press button 1 + 2 on your Wii Remote... attempt #', attempt,' of ',max_attempts
                else:
                    print 'No Wii Remote found after ', max_attempts, ' attempts. Finishing execution.'
                    sys.exit(0)
        
print 'Wii Remote connected...'
print '\nPress [A]+[B] buttons to disconnect the Wii and end the application'
        time.sleep(1)

        wm.rpt_mode = cwiid.RPT_BTN

        while True:
            bitmask = bin(wm.state['buttons'])[2:].rjust(13, '0')
            #print bitmask

            if bitmask[-1] == "1": #wm.state['buttons'] == 1:
if debug:
print ' [2] pressed \n'
if (not btn_two) :
   btn_two = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_two:
   btn_two = False
   device.emit(uinput.KEY_LEFTCTRL, 0)
            if bitmask[-2] == "1": #wm.state['buttons'] == 2:
if debug:
print ' [1] pressed \n'
if (not btn_one) :
   btn_one = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_one:
   btn_one = False
   device.emit(uinput.KEY_LEFTCTRL, 0)
            if bitmask[-3] == "1": #wm.state['buttons'] == 4:
if debug:
print ' [B] button pressed \n'
if (not btn_b) :
   btn_b = True
   device.emit(uinput.KEY_1, 1)
   else:
if btn_b:
   btn_b = False
   device.emit(uinput.KEY_1, 0)
   if bitmask[-4] == "1": #wm.state['buttons'] == 8:
if debug:
print ' [A] button pressed \n'
if (not btn_a) :
   btn_a = True
   device.emit(uinput.KEY_5, 1)
   else:
if btn_a:
   btn_a = False
   device.emit(uinput.KEY_5, 0)
   if bitmask[-5] == "1": #wm.state['buttons'] == 16:
if debug:
print ' [-] button pressed \n'
if (not btn_min) :
   btn_min = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_min:
   btn_min = False
   device.emit(uinput.KEY_LEFTCTRL, 0)
            if bitmask[-8] == "1": #wm.state['buttons'] == 128:
if debug:
print ' [Home] button pressed \n'
device.emit(uinput.KEY_ESC, 1)
time.sleep(0.25)
device.emit(uinput.KEY_ESC, 0)
time.sleep(0.25)
device.emit(uinput.KEY_DOWN, 1)
time.sleep(0.25)
device.emit(uinput.KEY_DOWN, 0)
time.sleep(0.25)
device.emit(uinput.KEY_ENTER, 1)
time.sleep(0.25)
device.emit(uinput.KEY_ENTER, 0)
time.sleep(0.25)
            if bitmask[-9] == "1": #wm.state['buttons'] == 256:
if debug:
print ' [Down] button pressed \n'
if (not btn_dwn) :
   btn_dwn = True
   device.emit(uinput.KEY_DOWN, 1)
   else:
if btn_dwn:
   btn_dwn = False
   device.emit(uinput.KEY_DOWN, 0)
   if bitmask[-10] == "1": #wm.state['buttons'] == 512:
if debug:
print ' [Up] button pressed \n'
if (not btn_up) :
   btn_up = True
   device.emit(uinput.KEY_UP, 1)
   else:
if btn_up:
   btn_up = False
   device.emit(uinput.KEY_UP, 0)
            if bitmask[-11] == "1": #wm.state['buttons'] == 1024:
if debug:
print ' [Right] button pressed \n'
if (not btn_rgt) :
   btn_rgt = True
   device.emit(uinput.KEY_RIGHT, 1)
   else:
if btn_rgt:
   btn_rgt = False
   device.emit(uinput.KEY_RIGHT, 0)
            if bitmask[-12] == "1": #wm.state['buttons'] == 2048:
if debug:
print ' [Left] button pressed \n'
if (not btn_lft) :
   btn_lft = True
   device.emit(uinput.KEY_LEFT, 1)
   else:
if btn_lft:
   btn_lft = False
   device.emit(uinput.KEY_LEFT, 0)
   if bitmask[-13] == "1": #wm.state['buttons'] == 4096:
if debug:
print ' [+] button pressed \n'
if (not btn_pls) :
   btn_pls = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_pls:
   btn_pls = False
   device.emit(uinput.KEY_LEFTCTRL, 0)

   if wm.state['buttons'] > 0 :
if debug:
  print ' <-'
if wm.state['buttons'] == 12 :
   print 'closing Bluetooth connection. Good Bye!'
   exit(wm)

            time.sleep(.02) # Poll every 20ms (otherwise CPU load gets too high)


if __name__ == '__main__':
    main()

El resultado y algunos detalles acerca del orden de ejecución en el siguiente video.


Gracias por seguirme, Saludos!

domingo, 31 de marzo de 2013

Controlar una matriz de LEDs de 5x5 con Raspberry Pi, Python y GPIOs

Controlar una matriz de LEDs de 5x5 con Raspberry Pi, Python y GPIOs


Tras una larga ausencia que raya en el descuido de este blog, me encuentro de nuevo por aqui para platicarles como hacer una matriz de LEDs de 5x5 para el despliegue de texto.

Ya que tenemos el Cubo de LEDs de 3x3x3 pareceria que hacer un matriz de 5x5 sería ir un paso atras en cuanto a complejidad, sin embargo este proyecto nos ayudara a perfeccionar el manejo de algunos elementos de Python como lo son las listas.

En esta ocasión tomaremos ventaja de un efecto visual denominado POV (Persistencia de la Visión por sus siglas en inglés) el cual nos permite engañar a nuestro sentido de la vista haciéndonos creer que múltiples LEDs están encendidos cuando en realidad prendiendo y apagando a un ritmo muy rápido.


El material


Para armar la matriz necesitaremos 25 LEDs, 5 transistores NPN, cable (preferentemente de dos colores diferentes) cautín y soldadura.

El material seleccionado para hacer la base es una espuma recubierta de cartón delgado de 5mm que es ligero, barato y puede darnos una estructura sólida para nuestra matriz. Ademas de un cutter, bisturí o exacto para poder hacer cortes precisos y pegamento para unir las piezas.

Se corta un cuadrado de 27cm por lado que sera la base, además se cortarán 8 rectángulos de 5cm x 27 cm para las divisiones internas, 2 rectángulos de 28 cm x 5.5 cm y por ultimo 2 rectángulos de 27 cm x 5.5 cm.


La construcción


Sobre la base cuadrada se harán marcas en cada uno de los cuatro lados a las siguientes distancias de la orilla:

- 5 cm
- 5.5 cm
- 10.5 cm
- 11 cm
- 16 cm
- 16.5 cm
- 21.5 cm
- 22 cm

ahora uniremos con lápiz las lineas correspondientes de tal forma que queden definidos 25 cuadrados, ademas quedarán identificadas las lineas donde estarán las 8 divisiones (4 horizontales y 4 verticales).

Les recomiendo trazar también unas lineas diagonales que dividan todos los cuadrados en dos triángulos rectángulos y después trazar las diagonales opuestas para de esta manera tener identificado el centro de cada uno de los cuadrados, que será la posición donde colocaremos nuestros LEDs.

Los 8 rectángulos de 5 cm x 27 cm tambien tendrán las mismas marcas por el lado largo que los lados del cuadrado ademas, por el lado corto, habra que hacer una marca a 2.5 cm de cada lado y unirla con lápiz a todo lo largo.

En estos 8 rectángulos cortaremos esas pequeñas áreas de 5mm x 2.5 cm (todas por el mismo lado del rectángulo) que nos permitirán inter-conectar estas piezas para armar el entrelazado de las divisiones interiores. Una vez inter-conectadas las piezas nos aseguraremos de que esten bien alineadas para poder poner pegamento por uno de sus lados y afianzarlo a la base cuadrada en su posición.

Ya para finalizar, tenemos dos pares de rectángulos, uno de ellos ligeramente mas largo que el otro. Estos nos servirán paralos bordes de la matriz, asegúrese de pegar primero el par de rectángulos largos en lados opuestos y al final los mas cortos para terminar con la matriz.


El circuito


Para controlar la matriz, utilizaremos solo 10 pines de la Raspberry Pi, 5 para las filas y 5 para las columnas. Todas las filas estarán conectando entre sí los ánodos y al final iran a uno de los pines de la Raspberry Pi, las columnas unirán los cátodos e irán conectados a la salida de los transistores NPN, tal como lo hicimos en el cubo de 3x3x3.


El código


Como se menciono al inicio de este post, el hacer este ejercicio nos ayudara a perfeccionar nuestras habilidades para usar listas en Python, la razón de esto es que cada uno de los caracteres que desplegaremos en la matriz lo deberemos de definir nosotros en una lista de 5x5 que tendra 1 o 0 en cada una de sus posiciones, correspondiendo directamente a LED encendido o apagado. A continuación les muestro el código que se uso para generar los caracteres numéricos. Para facilitar la visualización y legibilidad, el número 0 ha sido realzado en los elementos de la lista.


#numbers
chars.append([
"01110",
"10011",
"10101",
"11001",
"01110"
])
chars.append(["00100","01100","00100","00100","11111"])
chars.append(["11110","00001","01110","10000","11111"])
chars.append(["11110","00001","00110","00001","11110"])
chars.append(["10001","10001","01111","00001","00001"])
chars.append(["11111","10000","11110","00001","11110"])
chars.append(["01111","10000","11110","10001","01110"])
chars.append(["11111","00010","00100","00100","00100"])
chars.append(["01110","10001","01110","10001","01110"])
chars.append(["01110","10001","01111","00001","00110"])

Este es el código que mostrará los caracteres en la matriz:


#!/usr/bin/env python

# Import required libraries
import time
import RPi.GPIO as GPIO

# Set up the GPIO mode
GPIO.setmode(GPIO.BCM)

#Initialize a list with the GPIO pin numbers we are going to use
gpiopins = [14,15,18,21,23,24,10,25,8,7]

# Set up pins listed as outputs
print "Setting up pins ",
for p in gpiopins :
 print str(p) + " ",
 GPIO.setup(p, GPIO.OUT)
 #GPIO.setup(p, GPIO.LOW)
print "for OUPUT completed."

cols = [14,18,23,10,8]
rows = [7,25,24,21,15]

chars = []

chars.append([...
...
...])


def getLetter(letter):
 return chars[ord(str(letter))]

def drawMatrix(mtx):
 #print mtx
 t = time.clock() + 0.4
 while t > time.clock() :
  for i in range(0,5) :
   for j in range(0,5) :
    if mtx[i][j] == "1" :
     GPIO.output(cols[j],True)
     GPIO.output(rows[i],True)
     #time.sleep(0.005)
     GPIO.output(cols[j],False)
     GPIO.output(rows[i],False)

def drawString(text):
 text = text + chr(32)
 for l in range(len(text)):
  drawMatrix(getLetter(text[l]))
 time.sleep(0.4)

def scrollText(text):
 mtx = ["","","","",""]
 text = chr(32) + text.strip() + chr(32)
 l1 = getLetter(text[0])
 for i in range(len(text)-1):
  l2 = getLetter(text[i+1])
  for j in range(0,5):
   mtx[j] = l1[j] + "000" + l2[j]
  #print mtx
  drawScrollingMatrix(mtx)
  l1 = l2
  #time.sleep(0.05)

def drawScrollingMatrix(mtx):
  for offset in range(0,8):
   t = time.clock() + 0.14
   while t > time.clock() :
    for i in range(0,5) :
     for j in range(0,5) :
      if mtx[i][j+offset] == "1" :
       GPIO.output(cols[j],True)
       GPIO.output(rows[i],True)
       GPIO.output(cols[j],False)
       GPIO.output(rows[i],False)

#initialize all LEDs to off
for i in range(0,5) :
 GPIO.output(cols[i], False)
 GPIO.output(rows[i], False)

#control variables
delay = 0.5

print "Start patterns"
drawString("RASPBERRY PI & PYTHON & GPIO")
scrollText("RASPBERRY PI & PYTHON & GPIO")

El resultado





Saludos!

sábado, 16 de febrero de 2013

Cubo de LEDs 3x3x3 controlado con Python, GPIOs y Raspberry Pi

Cubo de LEDs 3x3x3


Para quien ha seguido las publicaciones en este blog resultará normal el hecho de que incrementemos el nivel de dificultad de un proyecto a otro. Primero vimos como encender y apagar un LED, luego como manejar 4 LEDs en una secuencia determinada para obtener una secuencia binaria usando LEDs y decimal utilizando un  display de 7 segmentos.

Para este proyecto, necesitaremos el siguiente material:


  • Cautin
  • Soldadura
  • Cable
  • 27 LEDs
  • 3 transistores NPN
  • Placa fenólica perforada
  • Cable plano de 26 hilos
  • Conector IDC-26 hembra

El problema


Hasta el momento no hemos hecho mas que utilizar un pin GPIO para cada uno de los LEDs que queremos controlar, el dia de hoy tendremos que resolver una situación, ¿como controlaremos de manera individual 27 LEDs cuando la Raspberry Pi solo nos puede proveer de 17 pines GPIO?

La solución


La solución que utilizaremos para resolver la pregunta anterior es una combinación de software y la forma en la que conectaremos los LEDs entre ellos para optimizar el uso de los pines GPIO.

El cubo que construiremos tiene 3 niveles de 3 LEDs por lado, esto es 3 x 3 x 3 = 27 LEDs.

Aunque esto ya debería ser familiar, lo primero que tenemos que identificar es el ánodo (+) y el cátodo (-) de los LEDs. Al observar las conexiones de los LEDs observaremos que una es ligeramente mas larga que la otra, eso es lo que nos ayudara a identificar el ánodo (+) del LED, por consecuencia, la conexión mas corta del LED es el cátodo (-).



Lo que haremos para armar el primer nivel del cubo será tomar 9 LEDs y doblar completamente el cátodo (-) desde la base del LED hasta que quede en un ángulo de 90 grados con respecto al ánodo (+), como se muestra a continuación.


Ahora, hay que soldar los cátodos de tal forma que obtengamos una matriz de 3x3. A continuación se muestran los 3 niveles ya terminados.


Una vez terminados los 3 niveles, a continuación los apilaremos uno sobre otro, a una distancia adecuada para que los ánodos (+) se puedan soldar entre ellos. Al final tendremos 9 columnas de ánodos (+) y 3 niveles de cátodos. Por lo que cada LED individual puede ser identificado con una coordenada única en 3 dimensiones, y lo mejor de todo, hemos resulto nuestro problema inicial, pues ¡solo utilizaremos 12 pines GPIOs!

¿Tan facil?


Una vez que hemos resuelto el problema anterior hasta parece trivial, ¿cierto? pues este proyecto ahora nos presenta otro problema que debemos resolver, los GPIOs de la Raspberry Pi son pines digitales y pueden ser controlados para enviar (HIGH) o no enviar (LOW) voltaje de acuerdo a nuestras necesidades pero nuestro diseño requiere poder enviar voltaje positivo a los ánodos (las columnas del cubo) y voltaje negativo a los cátodos (cada uno de los niveles) así que ¿cómo vamos a enviar voltaje negativo a los niveles del cubo?


Aqui la solución radica en el uso de transistores utilizados como interruptores, en el cual basicamente lograremos que al enviarle corriente al transistor usando los GPIOs este permitira el flujo de la corriente negativa que tendremos conectada a una de sus terminales, tal como se muestra en el diagrama siguiente:



Habiendo hecho esto para cada uno de los 3 niveles del cubo hemos terminado con la parte del hardware, ahora voltearemos hacia el software.


El Código


#!/usr/bin/env python

# Import required libraries
import time
import RPi.GPIO as GPIO

# Set up the GPIO mode
GPIO.setmode(GPIO.BCM)

#Initialize a list with the GPIO pin numbers we are going to use
gpiopins = [0,1,4,14,15,17,18,21,22,23,24,10]

# Set up pins listed as outputs
print "Setting up pins ",
for p in gpiopins :
 print str(p) + " ",
 GPIO.setup(p, GPIO.OUT)
 GPIO.setup(p, GPIO.LOW)
print "for OUPUT - LOW completed."

levelpins = [1,4,0]
pins = [ [23,24,10] , [18,21,22] , [14,15,17] ]
delay = 0.2
loops = 10

#Initialize all LEDs to off
for y in range(0,3) :
  GPIO.output(levelpins[y],False)
  for x in range(0,3) :
   for z in range(0,3) :
    GPIO.output(pins[x][z],False)

print "Start patterns"

print "LED by LED, bottom left front to top right back"
for loopcount in range(loops/2) :
 for y in range(0,3) :
  GPIO.output(levelpins[y],True)
  for x in range(0,3) :
   for z in range(0,3) :
    GPIO.output(pins[x][z],True)
    time.sleep(delay/2)
    GPIO.output(pins[x][z],False)
  GPIO.output(levelpins[y],False)

print "Column by column, left front to right back"
for loopcount in range(loops/2) :
 for y in range(0,3) :
  GPIO.output(levelpins[y],True)
 for x in range(0,3) :
  for z in range(0,3) :
  GPIO.output(pins[x][z],True)
   time.sleep(delay)
   GPIO.output(pins[x][z],False)
 GPIO.output(levelpins[y],False)

print "Row by Row, bottom front to top back"
for loopcount in range(loops) :
 for y in range(0,3) :
  GPIO.output(levelpins[y],True)
  for z in range(0,3) :
   for x in range(0,3) :
    GPIO.output(pins[x][z],True)
   time.sleep(delay)
   for x in range(0,3) :
    GPIO.output(pins[x][z],False)
  GPIO.output(levelpins[y],False)

print "Row by Row, bottom left to top right"
for loopcount in range(loops) :
 for y in range(0,3) :
  GPIO.output(levelpins[y],True)
  for x in range(0,3) :
   for z in range(0,3) :
    GPIO.output(pins[x][z],True)
   time.sleep(delay)
   for z in range(0,3) :
    GPIO.output(pins[x][z],False)
  GPIO.output(levelpins[y],False)

print "Level by Level, bottom to top"
for x in range(0,3) :
 for z in range(0,3) :
  GPIO.output(pins[x][z],True)
for loopcount in range(loops) :
 for y in range(0,3) :
  GPIO.output(levelpins[y],True)
  time.sleep(delay)
  GPIO.output(levelpins[y],False)
for x in range(0,3) :
 for z in range(0,3) :
  GPIO.output(pins[x][z],False)

El Resultado Final



Espero que esta referencia pueda ser de su utilidad