Controlar un display LCD 1602 para mostrar texto con la Raspberry Pi y Java

Escrito por el , actualizado el .
gnu-linux java planeta-codigo programacion
Enlace permanente Comentarios

Raspberry Pi

Java

Uno de los motivos por los que compré el kit de iniciación a la electrónica para la Raspberry Pi, además de cacharrear un poco, era en concreto controlar el display LCD de 16 columnas y 2 filas. En el kit el display viene con un adaptador con el bus de comunicación I2C. El display se puede usar sin este bus pero requiere utilizar muchos más pines GPIO de datos de los limitados 17 que ofrece la Raspberry Pi 1 y los 26 de las Raspberry Pi B+, 2 y 3. Controlar el display con I2C requiere únicamente 2 pines, por contra sin usar I2C requiere un número significativamente mayor 4 u 8 pines.

El display 1602 con su adaptador para el bus I2C que viene con el kit ya incorporado en la parte trasera es el siguiente.

Display LCD 16 columnas y 2 filas Adaptador bus I2C para display 1602

Display LCD 1602 y adaptador bus I2C

El esquema de conexionado para controlar el display requiere usar los pines de la Raspberry Pi SDA y SDL además de un pin para proporcionar un voltaje de 5V y otro pin para la tierra. El pin SDA es el número 2 según la numeración de pines de la Raspberry Pi y el SDL es el 5. El pin SDA es utilizado en el bus I2C para transmitir los datos y el SDL para la señal de reloj o sincronización. Utilizando la placa de extensión wiringPi de 26 pines los pines SDA y SDL se encuentran identificados por su nombre y el de la placa de extensión de 40 pines que viene con el kit de iniciación también, deberemos identificar estos pines y realizar las conexiones adecuadamente.

Cableado en la breadboard Esquema del cableado

Cableado en la breadboard

Hay que emplear varios cables macho-macho y hembra-hembra para conectar a los pines del adaptador I2C del display a los pines del voltaje de 5V, tierra, SDA y SDL de la placa de pruebas sin soldadura.

Unión cables macho-macho y hembra-hembra

Unión cables macho-hembra

El siguiente paso será activar el bus I2C en la Raspberry Pi que por defecto está desactivado. Esto requiere añadir unos parámetros en la configuración de arranque y cargar unos módulos del kernel que finalmente crearán un dispositivo tal que en /dev/i2c-0 o /dev/i2c-0. Si instalamos el paquete i2c-tools podremos detectar el display en el bus I2C, en la captura de pantalla en la dirección 27 que hay que usar al construir la instancia del controlador del display. Estos cambios en la configuración de inicio requieren reiniciar la Raspberry Pi. En un sistema con la distribución Arch Linux ARM los cambios son los siguientes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# /boot/config.txt
device_tree_param=i2c_arm=on

# sudo vim /boot/cmdline.txt
bcm2708.vc_i2c_override=1

$ sudo systemctl reboot

$ sudo modprobe i2c-bcm2708
$ sudo modprobe i2c-dev

$ sudo pacman -S i2c-tools
$ sudo i2cdetect -y 0
configuration.txt

Detección del display 1602 en el bus I2C

Detección del display 1602 en el bus I2C

Según la especificación del display 1602 este componente soporta varios comandos para controlarlo, algunos son para limpiar el texto, cambiar la dirección de escritura, añadir caracteres personalizados y emitir texto en la línea o posición del display que queramos. No es simple el controlar el display a bajo nivel ya que hay que trabajar en momentos con binario y usar bytes, por ello para el ejemplo usaré la librería diozero que ya trae una implementación de controlador con funciones de alto nivel I2CLcd que en versiones más recientes de la librería ha sido renombrada a HD44780Lcd mucho más cómoda que enviar bytes a bajo nivel al bus I2C, el código fuente de la clase HD44780Lcd está disponible y podemos verlo si hay curiosidad.

En mi caso con la Raspberry Pi 1 he tenido que utilizar la versión 0.9 de la librería diozero porque la 0.8 me generaba un stacktrace de una excepción java.lang.UnsupportedOperationException. Obtener esta versión de la librería como aún era de desarrollo y no estaba publicada en Maven Central la he descargado de un google drive que ha creado el autor y usado en Gradle como una dependencia del sistema de ficheros. Como librería subyacente de diozero para controlar los pines GPIO he usado pigpio.

1
2
3
4
5
Caused by: java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)
	at com.diozero.internal.board.raspberrypi.RaspberryPiBoardInfoProvider$PiBRev1BoardInfo.<clinit>(RaspberryPiBoardInfoProvider.java:281)
	... 14 more
java.lang.UnsupportedOperationException

En el ejemplo mostraré un texto en cada una de las lineas del display y usaré una de las funciones del para mostrar caracteres personalizados con los que es posible crear emojis o caracteres nuevos. El controlador de diozero ya contiene una buena colección de caracteres personalizados que definen el patrón de 5x8 puntos que siguen, los nombres de estos caracteres personalizados están en la clase interna Characters de HD44780Lcd aunque también podemos definir nuevos. El ejemplo es el siguiente donde se muestra el uso de los métodos setText y setCharacter, también el constructor donde hay que indicar la dirección asignada al dispositivo en el bus I2C que siendo la 27 corresponde con el valor definido en una constante. Pero también hay otros métodos como clear, cursorOff y cursorOn para apagar y encender el cursor, displayOff displayOn para apgar y encender el display y createChar para crear nuevos caracteres definidos como una array de 8 posiciones donde cada byte indica los pixeles encendidos de cada fila del caracter de 5x8 y con setCharacter para emitir uno de los 8 posibles que se pueden usar al mismo tiempo. Además de estos también hay otros pocos métodos más relacionados con el cursor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package io.github.picodotdev.blogbitix.javaraspberrypi;

import com.diozero.I2CLcd;
import com.diozero.api.I2CConstants;

import java.nio.ByteOrder;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class Lcd {

    public static void main(String[] args) throws Exception {
        try (I2CLcd lcd = new I2CLcd(I2CConstants.BUS_0, I2CLcd.DEFAULT_DEVICE_ADDRESS, ByteOrder.LITTLE_ENDIAN, 16, 2)) {
            lcd.setText(0, "Hello World!");
            lcd.createChar(0, I2CLcd.Characters.get("heart"));
            lcd.createChar(1, I2CLcd.Characters.get("smilie"));
            lcd.createChar(2, I2CLcd.Characters.get("space_invader"));
            lcd.setCharacter(13, 0, (char) 0);
            lcd.setCharacter(14, 0, (char) 1);
            lcd.setCharacter(15, 0, (char) 2);

            Thread messager = new Thread(() -> {
                try {
                    for (int i = 0; i < 5; i++) {
                        lcd.setText(1, " powered by Java");
                        Thread.sleep(3000);
                        lcd.setText(1, LocalDateTime.now(ZoneId.of("Europe/Madrid")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
                        Thread.sleep(3000);
                    }
                } catch(InterruptedException e){
                    e.printStackTrace();
                }
            });

            messager.start();
            messager.join();
        }
    }
}
Lcd.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apply plugin: 'java'
apply plugin: 'idea'

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile files('misc/libs/diozero-core-0.9-SNAPSHOT.jar', 'misc/libs/diozero-provider-pigpio-0.9-SNAPSHOT.jar', 'misc/libs/pigpioj-java-1.0.1.jar', 'misc/libs/commons-math3-3.6.1.jar', 'misc/libs/tinylog-1.1.jar')
}

...

task copyDependencies(type: Copy) {
    into "$buildDir/libs"
    from configurations.runtime
}

jar { dependsOn copyDependencies }

task upload(type: Exec, dependsOn: ['jar']) {
    commandLine 'rsync', '-arPL', '--delete', '-e', 'ssh', 'build/libs/', 'raspberrypi@192.168.1.101:/home/raspberrypi/scripts/javaraspberrypi/'
}

...

task executeLcd(type: Exec, dependsOn: 'upload') {
    commandLine 'ansible', '-i', 'hosts', 'raspberrypi', '--sudo', '--ask-sudo-pass', '-m', 'command', '-a', 'chdir=/home/raspberrypi/scripts/javaraspberrypi java -classpath "*" io.github.picodotdev.blogbitix.javaraspberrypi.Lcd'
    standardInput System.in
}

...
build.gradle
1
2
$ ./gradlew upload
$ ssh -t 192.168.1.101 'cd /home/raspberrypi/scripts/javaraspberrypi && sudo java -classpath "*" io.github.picodotdev.blogbitix.javaraspberrypi.Lcd'
executeSSH.sh
1
2
$ ./gradlew executeLcd

executeGradle.sh

Mensaje en LCD 1602

Mensaje en LCD 1602

Pudiendo mostrar mensajes en display es posible mostrar cualquier información que un programa sea capaz de capturar como temperatura y humedad del correspondiente sensor en el mismo kit, estado de un pulsador, espacio disponible en el disco del sistema, y memoria libre, uptime del sistema, fecha y hora, … cualquier cosa que se nos ocurra.

El ejemplo parece simple, y el programa Java lo es, pero requiere conocer varias cosas que en internet está dispersas como activar el bus I2C o conocer la librería diozero para controlar el display que simplifica enormemente el código y nos evita comunicarnos a más bajo nivel con el display, realizar las conexiones eléctricas también requiere algo de conocimiento. Averiguar todo esto me costó una buena cantidad de tiempo.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew executeLcd


Comparte el artículo: