Raspberry Pi mit eigenem Display

28 Jan

 

Mal wieder Zeit zum Basteln!

Problem: Betreibt man einen Raspberry Pi ohne Monitor, bleibt zum Herausfinden der IP-Adresse oft nur, dass man sich durch die Konfiguration vom lokalen Router klickt.

Ziel: Sobald der Pi gestartet ist, soll er seine eigene Adresse auf einem Zusatzdisplay anzeigen.

Zum Testen sieht man hier zwei Displays: ein kleines OLED (links) vom Typ SSD1306 und ein LCD Typ HD44780 mit I2C-Konverter (rechts). Beide sind für wenige Euro bei einschlägigen Elektronik-Versandhändlern erhältlich.

Also im raspi-config den i2c-Bus aktiviert, und los geht’s. Die Verkabelung ist recht einfach, vom Pi kommen insgesamt: 5 V, 3,3 Volt, Masse, SDA und SCL.

Ans OLED gehen direkt 3,3 Volt, Masse, SDA und SCL vom Pi.

Für das LCD müssen SDA und SCL leider erst durch einen Pegelwandler.

Der wiederum braucht an seiner 3,3-Volt-Seite vom Pi: 3,3 Volt, Masse, SDA, SCL.

Auf seiner 5 Volt-Seite entsprechend: 5 Volt vom Pi (Masse ist durchgeschleift). Die Ausgänge gehen auf die Anschlüsse des I2C-Moduls am LCD: 5 Volt, Masse, SDA, SCL.

Software

Treiber für das SSD1306: https://github.com/adafruit/Adafruit_Python_SSD1306

Treiber für das HD44780 (da gibt’s viele die sich daran probiert haben, ich habe diesen hier verwendet): https://github.com/sweetpi/python-i2c-lcd

Hier ist das Python-Skript, welches die Text-Ausgabe von "ip addr show" auswertet (in bash also: "ip addr show | lcd_text.py"

 

#!/usr/bin/python

import lcddriver, re
import sys

lcd = lcddriver.lcd()

y = 1; #ausgabezeile

for line in sys.stdin:
	extrakt = re.search( 'inet ([^\s\/]{1,})', line, re.IGNORECASE )
	if extrakt and y < 3:
		aus = extrakt.group(1)
		if aus == "127.0.0.1":
			continue
		lcd.display_string( aus, y )
		y = y + 1

 

Das ist das Bash-Skript, das alles Nötige für den Aufruf zusammenfasst:

 

#!/bin/bash
cd /home/pi/bootinfo
ip addr show | ./lcd_text.py

 

Und hier noch die Zeile für den Crontab (sudo crontab -e):

* * * * * /home/pi/bootinfo/bicron.sh<br>

 

Weil mich dann noch der Ehrgeiz packte, wollte ich auch noch bei Hoch- und Herunterfahren des Pi etwas auf dem LCD sehen können. Dazu habe ich mir ein Skript für init.d gebastelt, das ein Python-Skript aufruft, welches dann das LCD ansteuert. Dazu wiederum habe ich mir alle nötigen Teile der LCD-Bibliotheken in eine Datei gesteckt.

Hier erst einmal das Skript für init.d/bootlcd.sh:

#!/bin/sh

### BEGIN INIT INFO
# Provides: my_custom_lcddisplay
# Required-Start: kmod $local_fs
# Required-Stop: kmod $local_fs
# X-Start-Before:
# X-Stop-After:
# Default-Start: 1 2 3 4 5
# Default-Stop: 0 6
# Short-Description: Calls a python script that writes a short message on I2C 1602 LCD
### END INIT INFO

case "$1" in
	start)
		if [ -e /dev/i2c-1 ]; then
			/home/pi/bootinfo/msg2lcd.py "System Start ..." "Bitte warten."
		fi

		;;

	restart|reload|force-reload)
		if [ -e /dev/i2c-1 ]; then
			/home/pi/bootinfo/msg2lcd.py "Display Restart." "----------------"
		fi
		;;

	stop)
		if [ -e /dev/i2c-1 ]; then
			/home/pi/bootinfo/msg2lcd.py "Stop in Arbeit." "Rote LED abwart."
		fi
		;;

	*)
		echo "Usage: $0 start|stop" >&2
		exit 3
		;;

esac

exit 0

 

Und hier das Python-Skript das mit dem LCD spricht:

#!/usr/bin/python
# Skript zur Textausgabe (beim Boot/Shutdown)
# zwei Parameter Text bis zu Displaybreite erforderlich
# Noetige Funktionsbiblios gleich hier eingebaut (keine Abhaengigkeiten)

import sys

# i2c_lib

import smbus
from time import *

class i2c_device:
  def __init__(self, addr, port=1):
    self.addr = addr
    self.bus = smbus.SMBus(port)   

  def write_cmd(self, cmd):
    self.bus.write_byte(self.addr, cmd)
    sleep(0.0001)

  def write_cmd_arg(self, cmd, data):
    self.bus.write_byte_data(self.addr, cmd, data)
    sleep(0.0001)

  def write_block_data(self, cmd, data):
    self.bus.write_block_data(self.addr, cmd, data)
    sleep(0.0001)

  def read(self):
    return self.bus.read_byte(self.addr)

  def read_data(self, cmd):
    return self.bus.read_byte_data(self.addr, cmd)

  def read_block_data(self, cmd):
    return self.bus.read_block_data(self.addr, cmd)




# lcd Klasse

# LCD Address
#ADDRESS = 0x3F
ADDRESS = 0x27

# I2C bus
BUS = 1

# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80

# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00

# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00

# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00

# flags for function set
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00

# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00

En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit

class lcd:
  """ 
  Class to control the 16x2 I2C LCD display from sainsmart from the Raspberry Pi
  """

  def __init__(self):
    # """Setup the display, turn on backlight and text display + ...?"""
    self.device = i2c_device(ADDRESS, BUS)

    self.write(0x03)
    self.write(0x03)
    self.write(0x03)
    self.write(0x02)

    self.write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
    self.write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
    self.write(LCD_CLEARDISPLAY)
    self.write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
    sleep(0.2)

  def strobe(self, data):
#    """clocks EN to latch command"""
    self.device.write_cmd(data | En | LCD_BACKLIGHT)
    sleep(0.0005)
    self.device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
    sleep(0.001)

  def write_four_bits(self, data):
    self.device.write_cmd(data | LCD_BACKLIGHT)
    self.strobe(data)

  def write(self, cmd, mode=0):
 #   """write a command to lcd"""
    self.write_four_bits(mode | (cmd & 0xF0))
    self.write_four_bits(mode | ((cmd << 4) & 0xF0))

  def display_string(self, string, line):
    if line == 1:
       self.write(0x80)
    if line == 2:
       self.write(0xC0)
    if line == 3:
       self.write(0x94)
    if line == 4:
       self.write(0xD4)

    for char in string:
       self.write(ord(char), Rs)

  def clear(self):
  #  """clear lcd and set to home"""
    self.write(LCD_CLEARDISPLAY)
    self.write(LCD_RETURNHOME)

  def backlight_off(self):
   # """turn off backlight, anything that calls write turns it on again"""
    self.device.write_cmd(LCD_NOBACKLIGHT)

  def display_off(self):
    #"""turn off the text display"""
    self.write(LCD_DISPLAYCONTROL | LCD_DISPLAYOFF)

  def display_on(self):
#    """turn on the text display"""
    self.write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)


lcdobj = lcd()

lcdobj.display_string( sys.argv[1], 1 )
lcdobj.display_string( sys.argv[2], 2 )

 

Das Python-Skript gehört in /home/pi/bootinfo/msg2lcd.py.

Das Init-Skript entsprechend in /etc/init.d/bootlcd.sh

Einbinden anschließend mit

update-rc.d bootlcd.sh defaults

 

Vorteile und Nachteile OLED:

– Das OLED braucht mehr Software, bis sich was auf ihm zeigt. Insbesondere hat mich überrascht, wie die Python-Funktionsbibliothek von Adafruit die Grafiken ausgibt (verglichen mit der Arduino-Variante): Alles ist Grafik. Im Raspberry wird ein Schwarzweiß-Puffer in der Größe des Displays aufgebaut und immer komplett überschrieben.

+ Dafür kann es natürlich viel flexibler Dinge darstellen, ob Text oder Bild.

+ Außerdem ist es direkt mit den 3,3 Volt vom Raspberry kompatibel. Kabel ran und bereit.

– Es soll allerdings nicht besonders gut damit klarkommen, wenn die Anzeige sich längere Zeit nicht ändert („Einbrennen“). Sicherheitshalber habe ich die Helligkeit („Kontrast“) auf Minimum gestellt und bei jedem Update wird der Textblock an einer anderen Position gezeigt.

Vorteile und Nachteile LCD:

+ Zum Ansteuern mit Python kann man alle nötigen Softwareteile in ein einziges Python-Skript packen (bis auf "import sys, smbus", "from time import *"). Das vereinfacht es, wenn man (wie ich) auch noch beim Hoch- und Runterfahren des Raspberry über ein Skript in init.d die entsprechenden Meldungen auf dem LCD zeigen will. Wirkt insgesamt „schlanker“.

– keine Grafik, nur Text. Und 16x2 Zeichen ist schon recht wenig. Wenn der Platz keine Rolle spielt, besser gleich zu einem 20x4 Zeichen Display greifen.

+ Zur Ausgabe von bis zu 2 IP-Adressen reicht es jedenfalls immer, auch mit 16x2.

+ Die LCD-Bauform „Schwarz auf Gelb“ ist auch ohne Hintergrundbeleuchtung ablesbar. Abgebildet ist zwar die Version Weiß auf Blau, die würde ich aber mittlerweile nicht mehr verwenden.

+/– Das LCD-Display ist erheblich größer als das OLED. Damit ist es natürlich leichter ablesbar, braucht aber mehr Platz.

– Zur Ansteuerung ist ein i2c-Pegelwandler von 3,3 V auf 5 V zwingend nötig (mit 3,3 Volt erreicht das LCD nicht mehr den Kontrast, dass man den Text lesen könnte). Die Wandler (2-Kanal reicht) kosten zwar auch nur noch wenig, aber es ist eindeutig mehr Verkabelungsaufwand.

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

5 × eins =