Para alimentar externamente los motores usaré un pack de 8 baterías recargables que compré hace tiempo para alimentar una tira de leds que aun no he puesto. Este pack ofrece 1.2*8=9.6V y 2.3mAh cuando está cargado totalmente. Le soldé un jack de alimentación para alimentar el Arduino, pero me es más útil alimentando las baterías, así que busqué un jack de alimentación macho que tenía por ahi para tiras led y lo conecté, de forma que a la protoboard llegarán 2 cables pelados.
El montaje es el que este usuario reporta en el foro de arduino https://forum.arduino.cc/index.php?topic=256290.0. Aunque dice que no le funciona, a mi me esta funcionando perfectamente y no parece calentarse el transistor ni el diodo, así que tiraré con esto para hacer pruebas 😀
El código será similar al código de prueba del pdf del ejercicio 17 del Arduino starter pack que he usado en otras iteraciones. Simplemente permite controlar la velocidad de los dos motores con el valor que se emita por el puerto serie (de 0 a 254).
//Global variables
int motor1Pin = 3;
int motor2Pin = 5;
//Initial setup
void setup()
{
//Initializing output
pinMode(motor1Pin, OUTPUT);
pinMode(motor2Pin, OUTPUT);
//Begin serial port communication
Serial.begin(9600);
while (! Serial);
Serial.println("Speed 0 to 254");
}
//Main loop
void loop()
{
if (Serial.available())
{
int speed = Serial.parseInt();
if (speed >= 0 && speed < 255)
{
analogWrite(motor1Pin, speed);
analogWrite(motor2Pin, speed);
}
}
}
Ya tengo 2 ruedas moviéndose, ahora estaría bien que puedan cambiar el sentido e ir hacia atrás, cosa que podría conseguir con un integrado L293D que casualmente me venía en el Arduino starter kit, así que esa será la siguiente iteración
Con el tema del transistor PN2222A solucionado pude por fin empezar a programar, que es lo que buscaba desde el principio. Le puse una rueda al motor y analicé su voltaje (4.54V máximo con una resistencia de 1KOhm). Va muy lento, esto no mueve los 500g del kit de coche entero.
Seguí el esquema del proyecto 17 del pdf del Arduino starter kit de aliexpress que ya cité en la anterior iteración, pero esta vez cambiando el orden de la base, colector y emisor para adaptarse a mi transistor especial. Como más o menos funcionaba, le puse también otra rueda y otro circuito con otro transistor para moverla.
Con una sola rueda empezaba a moverse a partir de 1.5V, que equivalía al valor 100-110 por PWM, pero al poner dos ruedas necesitaba el doble de voltaje, así que probé con 200. Si movía las dos ruedas a la vez no tenía suficiente fuerza, así que creé este programa para alternar primero una rueda y luego otra. El funcionamiento exacto sería el siguiente: empieza la primera rueda a moverse desde el valor 120 hasta el máximo (254) tardando 5 segundos en este proceso, los siguientes 2 segundos continuará a máxima velocidad, luego se parará esta rueda y repetirá el proceso la siguiente rueda, repitiéndose el proceso completo cuando esta segunda rueda acabe.
//Global variables
const int motor1Pin = 3;
const int motor2Pin = 5;
const int maxSpeed = 254;
//Initial setup
void setup()
{
//Initializing output
pinMode(motor1Pin, OUTPUT);
pinMode(motor2Pin, OUTPUT);
}
//Main loop
void loop()
{
accelerate(motor1Pin, 120, maxSpeed, 5); //Accelerate the motor 1, from 120 to maxSpeed in 5 seconds
delay(2000);
analogWrite(motor1Pin, 0);
accelerate(motor2Pin, 120, maxSpeed, 5); //Accelerate the motor 2, from 120 to maxSpeed in 5 seconds
delay(2000);
analogWrite(motor2Pin, 0);
}
//Accelerate the motor from currentSpeed to maxSpeed in n seconds
void accelerate(int motor, int fromSpeed, int toSpeed, int sec)
{
int initialFromSpeed = fromSpeed;
for (; fromSpeed <= toSpeed; fromSpeed++)
{
analogWrite(motor, fromSpeed);
delay((sec * 1000)/(toSpeed - initialFromSpeed));
}
}
Bueno, funciona, sí, pero no, en la siguiente iteración alimentaré los motores por separado y el Arduino sólo variará las bases de los transistores.
He estado a nada de dejar el proyecto entero y volverme a mi mundo de unos y ceros donde TODO FUNCIONA. Resulta que los transistores PN2222A que tengo son de algún fabricante raro que no respeta el datasheet y tiene el emisor-base-colector en otro orden, pero empecemos por lo primero.
Después de tener el kit de coche montado, me dispuse felizmente a probar a mover una rueda con un transistor y alimentación directa del Arduino. Para ello consulté varios montajes, en todos ellos me pasaba lo mismo: el motor iba siempre a máxima velocidad, obviando lo que le pasara por la patilla de la base, ya fuera masa, 5V o PWM al Arduino. Probé de todo:
Montajes donde el orden con la parte plana del transistor hacia arriba era emisor-base-colector, como el que había de ejemplo (ejercicio 17) en el pdf que incluía el Arduino starter pack que le compré a mi padre. Por cierto, en el datasheet del PN2222A pone que el orden es emisor-base-colector.
Montajes con un transistor 2N2222 donde el orden era colector-base-emisor (como en este link).
Probé más cosas que no puedo ni quiero recordar, ya me daba igual todo.
Muchas cosas podían haber fallado o haberse quemado, así que empecé a comprobar lo más básico hasta encontrar la fuente del problema. Aprendí mucho sobre transistores con esta web y después de 2 hojas de cálculos entendí mucho mejor como debería funcionar. También probé un circuito básico de PWM para alimentar un led y descartar que me hubiera cargado el pin PWM que usaba del Arduino o sus 5V/GND, por suerte funcionaba perfectamente, así que el problema NO estaba en lo que estaba enviando por la base del transistor.
Cuando todo estaba perdido me puse a investigar cómo comprobar si un transistor está quemado. Encontré varias webs que decían lo mismo: si pones la punta del voltímetro en una patilla y mides resistencia a las otras dos y te dan valores diferentes de «Over Limit», entonces esa primera patilla era la base. Hice todas las combinaciones posibles y no había duda: la base resultaba estar en la primera patilla!, además según esta web el transistor sería PNP.
Conclusión: el datasheet dice que el transistor PN2222A es NPN y el orden es emisor-base-colector, pero el PN2222A que me venía con el Arduino starter pack es PNP y con orden base-colector-emisor.
Este es mi primer proyecto grande con Arduino. Después de muchas dudas sobre qué versión comprar: 2 ruedas, 4 ruedas o incluso no aventurarme a tocar nada de Arduino, al final me decidí por el kit de 4 ruedas porque el de 2 me parecía demasiado cutre xD. Tardé unos días en darme cuenta de que el kit de 4 ruedas tiene casi todo lo necesario para montar 2 robots de 2 ruedas a falta de la rueda loca de detrás, así que creo que es la mejor opción pensando en el futuro.
2WD car kit
4WD car kit
Este kit solo contiene elementos básicos del coche y un pack de pilas, pero nada de electrónica:
4 ruedas de plástico con goma
4 motores CC modelo 130 de 3-6V y 70-250mA
4 discos de plástico para el velocímetro (faltaría el sistema de led-fotoresistor para medir la luz que pasa por las ranuras)
2 tiras de 4 cables cada una para conectar los motores
1 pack de pilas para agrupar pilas de 1.5V, en total serían 6V
2 placas de metacrilato que servirán como base de los componentes
8 elementos de sujeción para los motores (2 por cada motor)
Tornillos, tuercas y espaciadores metálicos para afianzar los componentes
Lo que me interesa es controlar el software de lo que se me vaya ocurriendo, la parte electrónica es un mero trámite (para mí desagradable) y para ello cuento con la ayuda puntual de dos ingenieros: mi padre y mi primo. Tengo nociones de electrónica por mi carrera y por mi padre, pero será éste quien evite que fría más componentes de los que debiera. El proyecto es muy escalable: primero conseguir que el coche se mueva recto hacia delante con una velocidad aceptable. Luego podría ampliar su funcionalidad para que también girara y cambiara de sentido. Cuando funcione todo esto básico, sería interesante ahondar en:
Afinar el control básico. Calibrar las ruedas para que todas vayan a la misma velocidad aplicándoles distinto voltaje a cada una, porque la vida real no es tan perfecta como en el papel y habrá roces o cargas en unas ruedas que difieran de otras.
Ampliar funcionalidades. Control remoto por bluetooth/wifi. Sensor de ultrasonidos con servo para analizar el entorno en sus 180º frontales. Cámara wifi para acceder desde el móvil. Roomba casero con mapeado del entorno.
En la siguiente iteración haré (o intentaré hacer) que se mueva un motor con un transistor y alimentación 5V de Arduino.
– Instalamos Apache con los siguientes comandos: sudo apt-get update
sudo apt-get install apache2
A continuación podremos comprobar que funciona el servidor Apache accediendo con un navegador al puerto 80 de la Raspberry Pi (en mi caso http://192.168.1.20)
– Instalamos php con el siguiente comando: sudo apt-get update php5 libapache2-mod-php5
Podemos comprobar que funciona PHP creando el siguiente archivo y accediendo con un navegador a él (en mi caso http://192.168.1.20/index.php). echo "<?php phpinfo(); ?>" > index.php
sudo mv index.php /var/www/html
– Instalamos MySQL con el siguiente comando: sudo apt-get install mysql-server mysql-client php5-mysql
Para comprobar que funciona MySQL ejecutaremos el siguiente comando e iniciaremos sesión con la contraseña que elegimos en el proceso de instalación: mysql -u root -p
y ya dentro de MySQL listaremos las bases de datos con: show databases;
exit
– Instalamos phpmyadmin con el siguiente comando: sudo apt-get install phpmyadmin
Para comprobar que funciona phpmyadmin, añadiremos la siguiente linea: Include /etc/phpmyadmin/apache.conf
al archivo apache2.conf, con el siguiente comando: sudo nano /etc/apache2/apache2.conf
y reiniciaremos el servicio de apache2: sudo /etc/init.d/apache2 restart
Podremos consultar las bases de datos accediendo a «phpmyadmin» desde un navegador (en mi caso http://192.168.1.20/phpmyadmin)
A continuación explicaré cómo instalar y configurar una carpeta compartida en la Raspberry Pi para acceder a ella desde nuestro PC Windows usando Samba.
Instalamos el paquete samba con el siguiente comando: sudo apt-get install samba samba-common-bin
Hacemos backup del archivo de configuracion: sudo cp /etc/samba/smb.conf /etc/samba/smb.conf2
Editamos el archivo smb.conf: workgroup = <GrupoDeTrabajo>
wins support = yes
read only = no
Por defecto se comparten las carpetas en home, pero si quieres añadir otra carpeta, habrá que agregar lo siguiente: [home]
comment = Home en Rasp3
path =/home/pi
browseable = yes
writeable = yes
only guest = no
public = no
Añadimos un password al usuario pi: sudo smbpasswd -a pi
Reiniciar el servicio para cargar la nueva configuración: sudo /etc/init.d/samba restart
A veces hay problemas a la hora de resolver los DNS porque alguna aplicación ha hecho de las suyas. Podemos consultar la lista de servidores DNS con el siguiente comando: cat /etc/resolv.conf
Podemos añadir los servidores de Google para resolver DNS y nos aseguraremos de que el problema de la conexión ya no es por DNS. sudo nano /etc/resolv.conf y añadiremos nameserver 8.8.8.8
Para seguir descartando problemas de conectividad, podemos hacer ping a Google (ping google.es) y debería resolverse con los servidores que acababamos de incluir. Pero si además tampoco hay respuesta a ping 8.8.8.8 entonces es que no hay salida a internet. Para descartar también un problema de cableado, haremos ping a la ip local más cercana (el router suele ser 192.168.1.1)
Otro proyecto con el LCD HD44780! Ahora vamos a crear una animación utilizando la CGRAM, que nos ofrece 8 huecos libres para customizar nuestros propios caracteres (o píxeles en nuestro caso). Todos los detalles del pin layout, configuraciones, etc están en el anterior artículo del LCD HD44780, aquí
He hecho 3 pruebas, las dos primeras están comentadas y solo se ejecutará la tercera:
pruebaCustomFont(). Primera prueba para dibujar un 0 y un 1 con una fuente personalizada pixel a pixel.
bolaRebotando(). Se trata de un cuadrado de 4 píxeles que traza una trayectoria hacia abajo y luego hacia arriba, rebotando en la pared derecha de este caracter de 5×7 puntos.
naveEspacial(). Es una animación de un cohete espacial con dos propulsores en su base, animada pixel a pixel.
Recordad que hay que instalar los siguientes paquetes:
sudo apt-get install python-setuptools
sudo apt-get install python-pip
sudo pip install rpi.gpio
Dibujando con el LCD HD44780
He usado el LCD como si fuera un display de matriz de puntos, dibujando un cohete con dos propulsores. Para ello he creado «nuevos caracteres» en la memoria CGRAM y los he ido mostrando y sobreescribiendo. Como solo tengo 8 huecos para custom characters, he agrupado mi figura en 3 partes: primero dos cuadrados de 4 píxeles cada uno (los propulsores), luego la parte externa del cohete con 8 píxeles más, y finalmente la parte interior con 8 píxeles más. Para dibujar las partes me ha sido de mucha utilidad esta web https://www.quinapalus.com/hd44780udg.html, que permite marcar los píxeles que quieres en tu caracter y te ofrece el array hexadecimal que lo define.
Y aquí el código megacomentado:
#!/usr/bin/python
import RPi.GPIO as GPIO
from time import sleep
class HD44780:
'''
Inicializador
'''
def __init__(self):
#Establecer pines GPIO de la Raspberry Pi para los pines RS, E y datos
self.pin_rs = 2
self.pin_e = 3
self.pins_db = [25, 8, 9, 11]
self.dinamico = False #Modo estático activado (el texto aparece escrito instantáneamente)
#Establecer los pines como salida
GPIO.setmode(GPIO.BCM) #Numeración BCM (numeración GPIO en lugar de numeración física de la placa)
GPIO.setup(self.pin_e, GPIO.OUT) #Establecer pin Enable como salida
GPIO.setup(self.pin_rs, GPIO.OUT) #Establecer pin Commando/Dato como salida
for pin in self.pins_db: #Establecer pines de datos como salida
GPIO.setup(pin, GPIO.OUT)
self.clear() #Llamar a función local para vaciar la pantalla y setearla
'''
Vaciar pantalla y configurarla
'''
def clear(self):
self.cmd(0x28) # Function set: Modo 4 bits, 2 líneas, 5x7 pixels (DL = 0: 4 bits, N = 1: 2 lines, F = 0: 5x7 dots, # = 0: Not 24x4 module)
self.cmd(0x0C) # Display ON/OFF Control: Display activado, cursor y parpadeo desactivado (D = 1: Display On, C = 0: Cursor Off, B: Cursor blink off)
self.cmd(0x06) # Entry Mode Set: Dirección del cursor (I/D = 1: Increment, S = 0: Do not accompany display shift)
self.cmd(0x01) # Clear Display: Vaciar pantalla
'''
Enviar comando
bits: comando a enviar
char_mode: modo comando/dato. Por defecto en false (comando)
'''
def cmd(self, bits, char_mode=False):
sleep(0.001) #Esperar 1ms
bits = bin(bits) #Castear a binario
bits = bits[2:] #Descartar los primeros 2 bits (0b)
zeros = (8 - len(bits)) * "0" #Generar "00"
bits = zeros + bits #Insertar los dos "00" a la izquierda de bits
GPIO.output(self.pin_rs, char_mode) #Establecer pin RS = modo comando / modo dato
#Primer paquete
self.inicializarDatos() #Inicializar datos a 0
for i in range(4): #Se envian los datos, de 0 a 3
if bits[i] == "1":
GPIO.output(self.pins_db[::-1][i], True) #Enviar solo los 1, los demás son 0 por defecto
self.refrescarEnable() #Refrescar enable
#Segundo paquete
self.inicializarDatos() #Inicializar datos a 0
for i in range(4, 8): #Se envian los datos, de 4 a 7
if bits[i] == "1":
GPIO.output(self.pins_db[::-1][i-4], True) #Enviar solo los 1, los demás son 0 por defecto
self.refrescarEnable() #Refrescar enable
'''
Inicializar datos
'''
def inicializarDatos(self):
for pin in self.pins_db:
GPIO.output(pin, False) #Inicializar todos los pines de datos a 0
'''
Refrescar enable
'''
def refrescarEnable(self):
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
GPIO.output(self.pin_e, True) #Enable = 1
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
GPIO.output(self.pin_e, False) #Enable = 0
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
'''
Establecer fuente personalizada
'''
def customfont(self, posicion, cero, uno, dos, tres, cuatro, cinco, seis, siete):
self.cmd(0x40 + posicion * 8) #(0100, 0000) Acceder 0x00+posicion en CGRAM
self.cmd(cero, True) #Enviar posición 0
self.cmd(uno, True) #Enviar posición 1
self.cmd(dos, True) #Enviar posición 2
self.cmd(tres, True) #Enviar posición 3
self.cmd(cuatro, True) #Enviar posición 4
self.cmd(cinco, True) #Enviar posición 5
self.cmd(seis, True) #Enviar posición 6
self.cmd(siete, True) #Enviar posición 7
'''
Resetear un caracter en CGRAM
'''
def resetear(self, posicion):
self.cmd(0x40 + posicion * 8) #Acceder a CGRAM en la posición dada
for i in range(8):
self.cmd(0x0, True) #Setear a 0 todas las líneas del caracter
'''
Resetear todos los caracter en CGRAM
'''
def resetearCGRAM(self):
for i in range(8):
self.resetear(i)
'''
Mostrar CGRAM
'''
def mostrarCGRAM(self):
for i in range(8):
self.cmd(0x80 + i) #Posicionar
self.cmd(0x00 + i, True) #Cargar elementos entre 0x00 y 0x07
'''
Mostrar caracter
'''
def mostrar(self, posicion):
lcd.cmd(0x80) #Mostrarlo en la primera línea
lcd.cmd(0x0 + posicion, True) #Mostrar caracter
'''
Setear y mostrar un 0 y un 1 con fuente custom
'''
def pruebaCustomFont():
lcd.resetearCGRAM()
lcd.customfont(0, 0x1f, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1f) #Fuente personalizada de posición 0 (Un número 0)
lcd.customfont(1, 0x4, 0xc, 0x14, 0x4, 0x4, 0x4, 0x4, 0x1f) #Fuente personalizada en posición 1 (Un número 1)
lcd.mostrarCGRAM()
'''
Setear y mostrar un cuadrado de 4 píxeles que empieza en la esquina superior izquierda y se desplaza 45 grados hacia abajo-derecha trazando un > y vuelve al origen
'''
def bolaRebotando():
#lcd.resetearCGRAM()
lcd.customfont(0, 0x18, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
lcd.customfont(1, 0x0, 0xc, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0)
lcd.customfont(2, 0x0, 0x0, 0x6, 0x6, 0x0, 0x0, 0x0, 0x0)
lcd.customfont(3, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x0)
lcd.customfont(4, 0x0, 0x0, 0x0, 0x0, 0x6, 0x6, 0x0, 0x0)
lcd.customfont(5, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0)
lcd.customfont(6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x18)
while True:
for i in range(6):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
for j in range(6):
lcd.cmd(0x01)
lcd.mostrar(6-j)
sleep(0.1)
'''
Setear y mostrar nave espacial
'''
def naveEspacial():
#Primera parte - Propulsores. Dos cuadros en la base de 4 píxeles cada uno en los extremos
lcd.customfont(0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0)
lcd.customfont(1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x0)
lcd.customfont(2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x10)
lcd.customfont(3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x18)
lcd.customfont(4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x18)
lcd.customfont(5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x18)
lcd.customfont(6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x19)
lcd.customfont(7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1b)
#Mostrar parte 1
for i in range(8):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
#Segunda parte - Parte izquierda. Línea izquierda desde los propulsores hacia arriba, con parte de la derecha
lcd.customfont(0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x1b, 0x1b)
lcd.customfont(1, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(2, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(3, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(4, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(5, 0x4, 0x8, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(6, 0x4, 0xa, 0x8, 0x8, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(7, 0x4, 0xa, 0xa, 0x8, 0x8, 0x8, 0x1b, 0x1b)
#Mostrar parte 2
for i in range(8):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
#Tercera parte - Parte derecha y relleno. Acaba línea derecha y rellena el centro.
lcd.customfont(0, 0x4, 0xa, 0xa, 0xa, 0x8, 0x8, 0x1b, 0x1b)
lcd.customfont(1, 0x4, 0xa, 0xa, 0xa, 0xa, 0x8, 0x1b, 0x1b)
lcd.customfont(2, 0x4, 0xa, 0xa, 0xa, 0xa, 0xa, 0x1b, 0x1b)
lcd.customfont(3, 0x4, 0xa, 0xa, 0xa, 0xa, 0xe, 0x1b, 0x1b)
lcd.customfont(4, 0x4, 0xa, 0xa, 0xa, 0xe, 0xe, 0x1b, 0x1b)
lcd.customfont(5, 0x4, 0xa, 0xa, 0xe, 0xe, 0xe, 0x1b, 0x1b)
lcd.customfont(6, 0x4, 0xa, 0xe, 0xe, 0xe, 0xe, 0x1b, 0x1b)
lcd.customfont(7, 0x4, 0xe, 0xe, 0xe, 0xe, 0xe, 0x1b, 0x1b)
#Mostrar parte 3
for i in range(8):
lcd.cmd(0x01)
lcd.mostrar(i)
sleep(0.1)
'''
Función principal
'''
if __name__ == '__main__':
lcd = HD44780() #Cargar clase principal
#lcd.clear()
#lcd.resetearCGRAM()
#pruebaCustomFont() #Primera prueba con CustomFont
#bolaRebotando() #Segunda prueba con CustomFont
while True: naveEspacial() #Tercera prueba con CustomFOnt
GPIO.cleanup()
En este proyecto voy a controlar una pantallita LCD HD44780 para mostrar un texto scrolleando.
Necesitaremos, a parte de la Raspberry Pi, lo siguiente:
LCD HD44780 o similar (habría que adaptar el pinout si usáis otro)
Placa board para sostener los componentes
8 cables con pines hembra-macho de colores
Varios trozos de cable para puentear
Potenciómetro 10k Ohm (103) o resistencia de 470 Ohm (opcional)
El LCD HD44780 tiene 16 pines (Vss, Vdd, V0, RS, RW, E, D0, D1, D2, D3, D4, D5, D6, D7, A, K) y usaremos la siguiente asignación de pines:
Vss hacia GND
Vdd hacia +5V
V0 hacia GND o hacia la patilla negativa del potenciómetro si lo usamos (la otra patilla del potenciómetro iría a +5V)
RS hacia pin GPIO2 para conmutar entre enviar comando o enviar dato
RW hacia GND porque solo vamos a escribir en el LCD, nunca a leer
E hacia pin GPIO3 para habilitar/deshabilitar el LCD
D4..D7 para datos en modo 4 bits (los conectaremos, por orden, a GPIO25, GPIO8, GPIO9, GPIO11)
A a +5V (es el ánodo)
K a GND (es el cátodo)
Para organizar mejor los cables he utilizado unos adaptadores de placa base para USB/Firewire que he cortado y adaptado, así que nos quedarán 2 grupos: el rojo para los datos y el azul para alimentación, RS y E.
La numeración que seguiremos será la GPIO, para ello estableceremos el modo BCM con GPIO.setmode(BCM). Por ejemplo, en modo BCM el pin E sería 3 (GPIO3), en modo BOARD el pin E sería el 5 como se puede observar en la siguiente imagen.
Y ahora el código! hay que instalar los siguientes paquetes para poder ejecutar el script python:
sudo apt-get install python-setuptools
sudo apt-get install python-pip
sudo pip install rpi.gpio
Mostrando el Lorem Ipsum con scroll (dinámico) o mensaje estático (comentado)
#!/usr/bin/python
import RPi.GPIO as GPIO
from time import sleep
class HD44780:
'''
Inicializador
'''
def __init__(self):
#Establecer pines GPIO de la Raspberry Pi para los pines RS, E y datos
self.pin_rs = 2
self.pin_e = 3
self.pins_db = [25, 8, 9, 11]
self.dinamico = False #Modo estático activado (el texto aparece escrito instantáneamente)
#Establecer los pines como salida
GPIO.setmode(GPIO.BCM) #Numeración BCM (numeración GPIO en lugar de numeración física de la placa)
GPIO.setup(self.pin_e, GPIO.OUT) #Establecer pin Enable como salida
GPIO.setup(self.pin_rs, GPIO.OUT) #Establecer pin Commando/Dato como salida
for pin in self.pins_db: #Establecer pines de datos como salida
GPIO.setup(pin, GPIO.OUT)
self.clear() #Llamar a función local para vaciar la pantalla y setearla
'''
Vaciar pantalla y configurarla
'''
def clear(self):
self.cmd(0x28) # Function set: Modo 4 bits, 2 líneas, 5x7 pixels (DL = 0: 4 bits, N = 1: 2 lines, F = 0: 5x7 dots, # = 0: Not 24x4 module)
self.cmd(0x0C) # Display ON/OFF Control: Display activado, cursor y parpadeo desactivado (D = 1: Display On, C = 0: Cursor Off, B: Cursor blink off)
self.cmd(0x06) # Entry Mode Set: Dirección del cursor (I/D = 1: Increment, S = 0: Do not accompany display shift)
self.cmd(0x01) # Clear Display: Vaciar pantalla
'''
Enviar comando
bits: comando a enviar
char_mode: modo comando/dato. Por defecto en false (comando)
'''
def cmd(self, bits, char_mode=False):
sleep(0.001) #Esperar 1ms
bits = bin(bits) #Castear a binario
bits = bits[2:] #Descartar los primeros 2 bits (0b)
zeros = (8 - len(bits)) * "0" #Generar "00"
bits = zeros + bits #Insertar los dos "00" a la izquierda de bits
GPIO.output(self.pin_rs, char_mode) #Establecer pin RS = modo comando / modo dato
#Primer paquete
self.inicializarDatos() #Inicializar datos a 0
for i in range(4): #Se envian los datos, de 0 a 3
if bits[i] == "1":
GPIO.output(self.pins_db[::-1][i], True) #Enviar solo los 1, los demás son 0 por defecto
self.refrescarEnable() #Refrescar enable
#Segundo paquete
self.inicializarDatos() #Inicializar datos a 0
for i in range(4, 8): #Se envian los datos, de 4 a 7
if bits[i] == "1":
GPIO.output(self.pins_db[::-1][i-4], True) #Enviar solo los 1, los demás son 0 por defecto
self.refrescarEnable() #Refrescar enable
'''
Mensaje a enviar (Soporte para cadenas con y sin salto de linea \n incluido)
text: texto a enviar en string
dinamico: modo dinámico. Por defecto en false (estático)
'''
def message(self, text, dinamico=False):
self.dinamico = dinamico
caracter = 0 #Contador de caracter (0 a 31)
for i in range(len(text)):
if text[i] == '\n': #Salto de línea explícito
self.cmd(0xC0) #Segunda línea (Mover cursor a posición 0x40)
if self.dinamico:
if caracter == 16: #Salto de linea implícito
self.cmd(0xC0) #Segunda línea (Mover cursor a posición 0x40)
elif caracter == 31: #Fin de la segunda línea
self.cmd(0x0F) #Poner cursor parpadeando
sleep(3) #Esperar 3 segundos
self.cmd(0x0C) #Segunda linea y quitar cursor parpadeando
self.cmd(0x01) #Borrar display y cursor a home
caracter = 0 #Resetear contador de caracter
if text[i] != '\n': #Enviar caracter si no es un salto de línea
self.cmd(ord(text[i]), True)
caracter += 1 #Incrementar contador de caracter
'''
Inicializar datos
'''
def inicializarDatos(self):
for pin in self.pins_db:
GPIO.output(pin, False) #Inicializar todos los pines de datos a 0
'''
Refrescar enable
'''
def refrescarEnable(self):
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
GPIO.output(self.pin_e, True) #Enable = 1
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
GPIO.output(self.pin_e, False) #Enable = 0
if(self.dinamico):
sleep(0.01) #Esperar 10ms solo en modo dinamico
'''
Función principal
'''
if __name__ == '__main__':
lcd = HD44780() #Cargar clase principal
#lcd.clear()
#lcd.message(" Welcome to\nUltimateRaspbian") #Mensaje estático
lcd.message("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam metus justo, eleifend et quam eget, congue consectetur lacus", True) #Mensaje dinámico
GPIO.cleanup()
He necesitado leer el datasheet del LCD HD44780 https://www.sparkfun.com/datasheets/LCD/HD44780.pdf para asegurarme del voltaje, lo que hace cada pin, qué comandos enviar para configurarlo a mi manera, etc.
Voy a emparejar mi teclado+touchpad bluetooth (Bluetooth iPazzPort), para ello utilizaré la herramienta bluetoothctl.
Primero instalaremos los paquetes necesarios con el siguiente comando: sudo apt-get install pi-bluetooth bluez bluez-tools
Ejecutaremos bluetoothctl -a para entrar en el programa. Aquí tuve un problema con el demonio bluetooth (el comando systemctl status bluetooth devolvía error en la ejecución) que se solucionó dando permisos a este archivo con el siguiente comando: sudo chmod +x /usr/lib/bluetooth/bluetoothd
A continuación ejecutaremos por orden los siguientes comandos:
power on <- Para encender el controlador
agent KeyboardOnly <- Para especificar el tipo de emparejamiento
default-agent <- Para establecer como agente por defecto
pairable on <- Para que sea visible y se pueda emparejar
scan on <- Para escanear dispositivos a emparejar
pair XX:XX:XX:XX:XX:XX <- Para emparejar con el dispositivo cuya MAC es XX:XX:XX:XX:XX:XX
trust XX:XX:XX:XX:XX:XX <- Para marcar como dispositivo de confianza
connect XX:XX:XX:XX:XX:XX <- Para conectar con el dispositivo
quit <- Para salir del programa
Si se desvincula el dispositivo, se puede volver a conectar con la utilidad hciconfig y hcitool hciconfig hci0 up
hcitool cc XX:XX:XX:XX:XX:XX