Raspberry Pi to bardzo ciekawe i ostatnio popularne urządzenie. Ten minikomputer potrafi naprawdę dużo. Dzięki złączu GPIO (General Purpose Input/Output) możemy komunikować się z innymi urządzeniami systemu komputerowego np. z mikroprocesorami czy urządzeniami peryferyjnymi. Umożliwia nam to komunikację dwustronną tzn. zapis i odczyt. Jednym z prostszych zastosowań takiego złącza jest możliwość uruchomienia ekranu LCD i kontrolowaniu go przez Raspberry Pi.

Naszym celem było wyświetlenie prostego tekstu na ekranie oraz napisanie aplikacji, która pozwoli na pokazywanie aktualnej pogody w danym mieście.

Wyświetlacz LCD

Większość z dostępnych na rynku wyświetlaczy LCD 2×16 znaków jest zgodna ze standardem Hitachi HD44780. Model jaki podłączyliśmy to: WC1602A-STBLWHTC-06. Model z niebieskim podświetleniem pasuje do kolorystki naszego serwisu ;-)

Oznaczenie pinów na wyświetlaczu LCD

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Vss Vdd V0 RS R/W E DB0 DB1 DB2 DB3 DB4 DB5 DB6 DB7 LED+ LED-
  • Vss (source supply) – służy do podłączenia GND (uziemienie)
  • Vdd (drain supply) – zasilanie układu +5V
  • RS – w zależności od stanu (wysoki, niski) wyświetlacz oczekuje na dane lub instrukcje
  • R/W – stan niski przełącza LCD w tryb odbioru informacji poprzez linie DB0..DB7. Jest to tryb zapisu. Stan wysoki przełącza moduł do wysyłania informacji do zewnętrznego urządzenia. Dzięki R/W ustalamy kierunek komunikacji.
  • E (enable) – podanie impulsu na to wejście powoduje odebranie przez mikrosterownik HD44780 informacji z linii DB0..DB7 oraz RS i R/W.
  • DB0..DB7 – szyna danych.
  • LED+/LED- – są to piny zasilające +5V oraz GND wykorzystywane w LCDkach z podświetleniem.

Do sterowania wyświetlaczem poprzez GPIO wykorzystałem bibliotekę RPi.GPIO, której proces instalacji opisaliśmy w artykule.

Pogodynka na Raspberry Pi

Schemat podłączenia LCD pod Raspberry Pi

Ekran LCD podłączamy do GPIO zgodnie ze schematem zamieszczonym poniżej. Dla wygody możemy skorzystać z płytki uniwersalnej, do której przylutujemy kable. Można również skorzystać z taśmy np. 26pin.

Schemat podłączenia LCD pod Raspberry Pi

Pin: LCD Funkcja LCD PIN: Raspberry Pi Funkcja Raspberry Pi
01 GND (uziemienie) 06 GND (uziemienie)
02 +5V 02 +5V
03 Kontrast 06 GND (uziemienie)
04 RS 26 GPIO7
05 RW 06 GND (uziemienie)
06 E GPIO8 24
07 DB0    
08 DB1    
09 DB2    
10 DB3    
11 DB4 22 GPIO25
12 DB5 18 GPIO24
13 DB6 16 GPIO23
14 DB7 12 GPIO18
15 +5V przez 560 ohm    
16 GND (uziemienie) 06 GND (uziemienie)

Pogodynka w Pythonie

Prosty program napisany w Pythonie służy do wyświetlania znaków na ekranie LCD. Źródła dostępne są również w serwisie GitHub.

#!/usr/bin/python
# -*- coding: utf-8 -*-
# HD44780 LCD Script for Raspberry Pi
#
# Author : Kamil Porembinski
# Site	 : http://osworld.pl
#
# Based on Matt's Hawkins script
# Site	 : http://www.raspberrypi-spy.co.uk
# 
# Date	 : 23/08/2012

#import
import RPi.GPIO as GPIO
import time
import urllib2
import re

# Script options
settings = {
	# City to monitor (ex. "Lodz", "Warsaw, Cracow", ...)
	'city'					 : 'Lodz',
	# Language of the conditions (ex. en, pl, fr, ...)
	'language'			 : 'pl',
}
GOOGLE_WEATHER_URL = 'http://www.google.com/ig/api?weather=%s&hl=%s&oe=utf-8'

# Define GPIO to LCD mapping
LCD_RS = 7
LCD_E  = 8
LCD_D4 = 25 
LCD_D5 = 24
LCD_D6 = 23
LCD_D7 = 18

# Define some device constants
LCD_WIDTH = 16 # Maximum characters per line
LCD_CHR = True
LCD_CMD = False

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line 

# Timing constants
E_PULSE = 0.00005
E_DELAY = 0.00005
E_TIME	= 5

def lcd_init():
	# Initialise display
	lcd_byte(0x33,LCD_CMD)
	lcd_byte(0x32,LCD_CMD)
	lcd_byte(0x28,LCD_CMD)
	lcd_byte(0x0C,LCD_CMD)	
	lcd_byte(0x06,LCD_CMD)
	lcd_byte(0x01,LCD_CMD)	

def lcd_string(message):
	# Send string to display
	message = message.ljust(LCD_WIDTH,)	

	for i in range(LCD_WIDTH):
		lcd_byte(ord(message[i]),LCD_CHR)

def lcd_byte(bits, mode):
	# Send byte to data pins
	# bits = data
	# mode = True for character
	#		 False for command

	GPIO.output(LCD_RS, mode) # RS

	# High bits
	GPIO.output(LCD_D4, False)
	GPIO.output(LCD_D5, False)
	GPIO.output(LCD_D6, False)
	GPIO.output(LCD_D7, False)
	if bits&0x10==0x10:
		GPIO.output(LCD_D4, True)
	if bits&0x20==0x20:
		GPIO.output(LCD_D5, True)
	if bits&0x40==0x40:
		GPIO.output(LCD_D6, True)
	if bits&0x80==0x80:
		GPIO.output(LCD_D7, True)

	# Toggle 'Enable' pin
	time.sleep(E_DELAY)		
	GPIO.output(LCD_E, True)	
	time.sleep(E_PULSE)
	GPIO.output(LCD_E, False)	
	time.sleep(E_DELAY)			

	# Low bits
	GPIO.output(LCD_D4, False)
	GPIO.output(LCD_D5, False)
	GPIO.output(LCD_D6, False)
	GPIO.output(LCD_D7, False)
	if bits&0x01==0x01:
		GPIO.output(LCD_D4, True)
	if bits&0x02==0x02:
		GPIO.output(LCD_D5, True)
	if bits&0x04==0x04:
		GPIO.output(LCD_D6, True)
	if bits&0x08==0x08:
		GPIO.output(LCD_D7, True)

	# Toggle 'Enable' pin
	time.sleep(E_DELAY)		
	GPIO.output(LCD_E, True)	
	time.sleep(E_PULSE)
	GPIO.output(LCD_E, False)	
	time.sleep(E_DELAY)
	
def rmpl(text):
	pl_tab = {u'ą':'a', u'ć':'c', u'ę':'e', u'ł':'l', u'ń':'n', u'ó':'o', u'ś':'s', u'ż':'z', u'ź':'z',u'Ą':'A', u'Ć':'C', u'Ę':'E', u'Ł':'L', u'Ń':'N', u'Ó':'O', u'Ś':'S', u'Ż':'Z', u'Ź':'Z'}
	return ''.join( pl_tab.get(char, char) for char in text )
	
def convert_mph(match):
	try:
		mph = int(match.group(0).split(' ')[0])
		kph = mph*1.6
		return "%s kph" % (int(round(kph)),)
	except:
		return match.group(0)

def get_weather_for_city():
	from xml.dom.minidom import parseString
	from xml.etree import ElementTree as ET
	
	url = GOOGLE_WEATHER_URL % (settings["city"].replace(' ', '+'), settings["language"])
	
	try:
		source = urllib2.urlopen(url).read()
		source = source.decode('utf-8', 'ignore')
		source = source.encode('utf-8')
		
		etree = ET.fromstring(source)
		weather = etree.find('weather')
	except:
		return("There was an error while retrieving data from the server.")

	parts = []
	out = {}
	information = weather.find('forecast_information')
	
	city = rmpl(information.find('city').get('data'))
	out["city"] = city

	conditions = weather.find('current_conditions')

	temperature = rmpl(conditions.find('temp_c').get('data') + 'C')
	out["temperature"] = "Temperatura: " + temperature

	humidity = rmpl(conditions.find('humidity').get('data'))
	out["humidity"] = humidity

	wind = rmpl(conditions.find('wind_condition').get('data'))
	wind = re.sub('\d+ mph', convert_mph, wind)
	out["wind"] = wind

	condition = rmpl(conditions.find('condition').get('data'))
	out["condition"] = condition

	return out

def main():
	
	# Main program block
	GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers
	GPIO.setup(LCD_E, GPIO.OUT)	# E
	GPIO.setup(LCD_RS, GPIO.OUT) # RS
	GPIO.setup(LCD_D4, GPIO.OUT) # DB4
	GPIO.setup(LCD_D5, GPIO.OUT) # DB5
	GPIO.setup(LCD_D6, GPIO.OUT) # DB6
	GPIO.setup(LCD_D7, GPIO.OUT) # DB7

	# Initialise display
	lcd_init()
	while 1 == 1:
		out = get_weather_for_city()
		# Send some text
		lcd_byte(LCD_LINE_1, LCD_CMD)
		lcd_string(time.strftime('    %H:%M:%S    '))
		lcd_byte(LCD_LINE_2, LCD_CMD)
		lcd_string(time.strftime('   %d-%m-%Y  '))
		time.sleep(E_TIME)
		lcd_byte(LCD_LINE_1, LCD_CMD)
		lcd_string(out["city"])
		lcd_byte(LCD_LINE_2, LCD_CMD)
		lcd_string(out["temperature"])
		time.sleep(E_TIME)
		lcd_byte(LCD_LINE_1, LCD_CMD)
		lcd_string(out["humidity"])
		lcd_byte(LCD_LINE_2, LCD_CMD)
		lcd_string(out["condition"])
		time.sleep(E_TIME)		

if __name__ == '__main__':
	main()

Z racji tego, że jest to pierwszy mój program w Pythonie, liczę na poprawki do kodu ;-)

  • Jaki jest orientacyjny koszt takiej zabawki nie licząc ceny Raspberry Pi?

  • To by było ciekawe. Pewnie można. W końcu to Open Source.

  • Rafał

    Podsyłam znaleziony w Sieci plik, może będzie inspiracją: http://sequoia.ict.pwr.wroc.pl/~kreczmer/wds/proj

  • sprae

    Ładnie napisane gratuluję :)

  • Tomasz

    Bardzo fajny pomysł. Super. Chyba wygram sobie Raspberry Pi i też takie zrobię.

  • Bardzo fajnie i przejrzyście opisane oraz zilustrowane! Szkoda, że jestem antytalentem w lutowaniu i tego typu pracach – bo chętnie bym coś takiego chciał mieć. Może sprzedacie albo zrobicie mi takie coś? :P

  • Mitras

    Zrobiłem tak jak w artykule, ale wyświetla mi się jedna linia pełna kwadratów.

  • owca

    czy zamiast rezystora 580 ohm mogę dać 560 om ?
    Właśnie taki: http://sklep.avt.pl/p/pl/70496/rezystor+560om+1w+
    będę tam robić zamówienie

    • Powinno być OK. Dla pewności możesz spytać na http://picoboard.pl/

    • Maciek

      Ja nie dałem rezystora na podświetlenie i działa o.O

    • Robert

      Nie używaj tej emotki, źle się tutaj kojarzy.

  • Rafał

    Pytanie z innej beczki, warto montować radiatory w Rasberry PI? Po zamontowaniu radiatorów podkręcaliście procek?

  • Google api nie działa już, znaleźliście jakiś zamiennik?

  • jar

    Jesteś pewny, że nie powinno tam być buforów konwertujących napięcia? RPi nie toleruje na GPIO więcej niż 3,3V. Co prawda w tej aplikacji zapisujesz dane do wyświetlacza, jednak z różnych przyczyn, na przykład zakłóceń w zasilaniu czy przypadkowym ustawieniu linii r/w na odczyt danych z LCD, może się zdarzyć, że ten wysteruje którąś z linii danych w stan wysoki (a jest zasilany z 5V) i z RPi – nie daj! – poleci "magiczny dymek".

  • Morgan Jones

    Wątek dość stary ale doszedłem do momentu kiedy przy próbie uruchomienia wyświetla się data i godzina i po dwóch sekundach pojawia się komunikat z pythona
    “Traceback (most recent call last):

    File “weather_lcd2.py”, line 200, in

    main()

    File “weather_lcd2.py”, line 189, in main

    lcd_string(out[“city”])

    TypeError: string indices must be integers, not str”

    I kompletnie nie mam pojęcia co to za babol..

    • Paweł

      Mam to samo o nie idzie się tego pozbyć…

    • Rysiek

      mam to samo pomoze ktos?

  • qwerty

    gnd to nie uziemienie to masa :/

  • Artur Dados

    Czy istnieje możliwość wyświetlania informacji na dzień kolejny? Jeśli tak, to co trzeba zmienic w kodzie ?