Stage 1 setup
- Follow Adafruit instructions for PiTFT
- Get the tutorial code: git clone https://github.com/jerbly/tutorials.git
Test the setup
pi@raspberrypi ~ $ sudo python Python 2.7.3 (default, Mar 18 2014, 05:13:23) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import pygame >>> import os >>> os.putenv('SDL_FBDEV', '/dev/fb1') >>> pygame.init() (6, 0) >>> lcd = pygame.display.set_mode((320, 240)) >>> lcd.fill((255,0,0)) <rect(0, 0, 320, 240)> >>> pygame.display.update() >>> pygame.mouse.set_visible(False) 1 >>> lcd.fill((0,0,0)) <rect(0, 0, 320, 240)> >>> pygame.display.update()You can also run this test (with a one second sleep) from the pygamelcd project: sudo python test1.py
From GPIO to screen
So, we can paint colours on the screen - let's do this from GPIs!
We'll use the four tactile buttons along the bottom of the screen to draw the GPIO number and a coloured background. From left to right the buttons correspond to GPIO #23, #22, #27, and #18.
(Note: If you have a revision 1 board then #27 is #21 - you'll just have to change the code a little)
We'll use the four tactile buttons along the bottom of the screen to draw the GPIO number and a coloured background. From left to right the buttons correspond to GPIO #23, #22, #27, and #18.
(Note: If you have a revision 1 board then #27 is #21 - you'll just have to change the code a little)
import pygame import os from time import sleep import RPi.GPIO as GPIO #Note #21 changed to #27 for rev2 Pi button_map = {23:(255,0,0), 22:(0,255,0), 27:(0,0,255), 18:(0,0,0)} #Setup the GPIOs as inputs with Pull Ups since the buttons are connected to GND GPIO.setmode(GPIO.BCM) for k in button_map.keys(): GPIO.setup(k, GPIO.IN, pull_up_down=GPIO.PUD_UP) #Colours WHITE = (255,255,255) os.putenv('SDL_FBDEV', '/dev/fb1') pygame.init() pygame.mouse.set_visible(False) lcd = pygame.display.set_mode((320, 240)) lcd.fill((0,0,0)) pygame.display.update() font_big = pygame.font.Font(None, 100) while True: # Scan the buttons for (k,v) in button_map.items(): if GPIO.input(k) == False: lcd.fill(v) text_surface = font_big.render('%d'%k, True, WHITE) rect = text_surface.get_rect(center=(160,120)) lcd.blit(text_surface, rect) pygame.display.update() sleep(0.1)
You can also run this from the pygamelcd project: sudo python test2.py
From screen to GPIO
With the PiTFT installed and the 4 tactile buttons there aren't many GPIs left on the model B Raspberry Pi. So wire up #17 and #4. The software renders 4 labels on the screen and then looks for mouse events in the four quarters:
import pygame from pygame.locals import * import os from time import sleep import RPi.GPIO as GPIO #Setup the GPIOs as outputs - only 4 and 17 are available GPIO.setmode(GPIO.BCM) GPIO.setup(4, GPIO.OUT) GPIO.setup(17, GPIO.OUT) #Colours WHITE = (255,255,255) os.putenv('SDL_FBDEV', '/dev/fb1') os.putenv('SDL_MOUSEDRV', 'TSLIB') os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen') pygame.init() pygame.mouse.set_visible(False) lcd = pygame.display.set_mode((320, 240)) lcd.fill((0,0,0)) pygame.display.update() font_big = pygame.font.Font(None, 50) touch_buttons = {'17 on':(80,60), '4 on':(240,60), '17 off':(80,180), '4 off':(240,180)} for k,v in touch_buttons.items(): text_surface = font_big.render('%s'%k, True, WHITE) rect = text_surface.get_rect(center=v) lcd.blit(text_surface, rect) pygame.display.update() while True: # Scan touchscreen events for event in pygame.event.get(): if(event.type is MOUSEBUTTONDOWN): pos = pygame.mouse.get_pos() print pos elif(event.type is MOUSEBUTTONUP): pos = pygame.mouse.get_pos() print pos #Find which quarter of the screen we're in x,y = pos if y < 120: if x < 160: GPIO.output(17, False) else: GPIO.output(4, False) else: if x < 160: GPIO.output(17, True) else: GPIO.output(4, True) sleep(0.1)
Stage 2 setup
We're now going to improve the UI by introducing a widget framework PygameUI.
1. Update your version of distribute: sudo easy_install -U distribute
2. Install PygameUI: sudo pip install pygameui
PygameUI GPIOs
This example controls GPIO #17 and #4 as above but now we're using the new framework.
The widget rendering and touchscreen events are handled by PygameUI. The PiTft class defines the buttons to draw on screen and the click event to be fired when a button is pressed.
The widget rendering and touchscreen events are handled by PygameUI. The PiTft class defines the buttons to draw on screen and the click event to be fired when a button is pressed.
import pygame import os import pygameui as ui import logging import RPi.GPIO as GPIO #Setup the GPIOs as outputs - only 4 and 17 are available GPIO.setmode(GPIO.BCM) GPIO.setup(4, GPIO.OUT) GPIO.setup(17, GPIO.OUT) log_format = '%(asctime)-6s: %(name)s - %(levelname)s - %(message)s' console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter(log_format)) logger = logging.getLogger() logger.setLevel(logging.DEBUG) logger.addHandler(console_handler) os.putenv('SDL_FBDEV', '/dev/fb1') os.putenv('SDL_MOUSEDRV', 'TSLIB') os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen') MARGIN = 20 class PiTft(ui.Scene): def __init__(self): ui.Scene.__init__(self) self.on17_button = ui.Button(ui.Rect(MARGIN, MARGIN, 130, 90), '17 on') self.on17_button.on_clicked.connect(self.gpi_button) self.add_child(self.on17_button) self.on4_button = ui.Button(ui.Rect(170, MARGIN, 130, 90), '4 on') self.on4_button.on_clicked.connect(self.gpi_button) self.add_child(self.on4_button) self.off17_button = ui.Button(ui.Rect(MARGIN, 130, 130, 90), '17 off') self.off17_button.on_clicked.connect(self.gpi_button) self.add_child(self.off17_button) self.off4_button = ui.Button(ui.Rect(170, 130, 130, 90), '4 off') self.off4_button.on_clicked.connect(self.gpi_button) self.add_child(self.off4_button) def gpi_button(self, btn, mbtn): logger.info(btn.text) if btn.text == '17 on': GPIO.output(17, False) elif btn.text == '4 on': GPIO.output(4, False) elif btn.text == '17 off': GPIO.output(17, True) elif btn.text == '4 off': GPIO.output(4, True) ui.init('Raspberry Pi UI', (320, 240)) pygame.mouse.set_visible(False) ui.scene.push(PiTft()) ui.run()
Analog input
This next example uses a 10K potentiometer to provide a varying voltage. For analog to digital I normally use an MCP3008 over SPI. Unfortunately the downside to the Pi TFT touchscreen is that both SPI channels on the Pi are in use. So I've switched to an I2C ADC from Adafruit: ADS1115 16-Bit ADC - 4 Channel with Programmable Gain Amplifier.
Get the Adafruit Python library: git clone https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
If you need to enable i2c follow this guide: Configuring I2C
A few important notes about this code.
A few important notes about this code.
- A thread is used to constantly read the potentiometer. If you take the reading in-line in the scene update method then you'll slow down the screen refresh rate.
- The PotReader class is given a reference to the PiTft class in order to pass data
- The ui.Scene class (PiTft) is instantiated after the call to ui.init - if you do this the other way around it will fail.
- A signal handler is used to trap ctrl+c and terminate the PotReader thread before calling sys.exit - otherwise the program will not close.
import sys sys.path.append('/home/pi/Adafruit-Raspberry-Pi-Python-Code/Adafruit_ADS1x15') import pygame import os import pygameui as ui import logging import RPi.GPIO as GPIO import signal from Adafruit_ADS1x15 import ADS1x15 import threading import time ADS1015 = 0x00 # 12-bit ADC ADS1115 = 0x01 # 16-bit ADC # Select the gain # gain = 6144 # +/- 6.144V gain = 4096 # +/- 4.096V # gain = 2048 # +/- 2.048V # gain = 1024 # +/- 1.024V # gain = 512 # +/- 0.512V # gain = 256 # +/- 0.256V # Select the sample rate sps = 8 # 8 samples per second # sps = 16 # 16 samples per second # sps = 32 # 32 samples per second # sps = 64 # 64 samples per second # sps = 128 # 128 samples per second # sps = 250 # 250 samples per second # sps = 475 # 475 samples per second # sps = 860 # 860 samples per second # Initialise the ADC using the default mode (use default I2C address) # Set this to ADS1015 or ADS1115 depending on the ADC you are using! adc = ADS1x15(ic=ADS1115) #Setup the GPIOs as outputs - only 4 and 17 are available GPIO.setmode(GPIO.BCM) GPIO.setup(4, GPIO.OUT) GPIO.setup(17, GPIO.OUT) log_format = '%(asctime)-6s: %(name)s - %(levelname)s - %(message)s' console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter(log_format)) logger = logging.getLogger() logger.setLevel(logging.DEBUG) logger.addHandler(console_handler) os.putenv('SDL_FBDEV', '/dev/fb1') os.putenv('SDL_MOUSEDRV', 'TSLIB') os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen') MARGIN = 20 class PotReader(): def __init__(self, pitft): self.pitft = pitft self.terminated = False def terminate(self): self.terminated = True def __call__(self): while not self.terminated: # Read channel 0 in single-ended mode using the settings above volts = adc.readADCSingleEnded(0, gain, sps) / 1000 self.pitft.set_volts_label(volts) self.pitft.set_progress(volts / 3.3) class PiTft(ui.Scene): def __init__(self): ui.Scene.__init__(self) self.on17_button = ui.Button(ui.Rect(MARGIN, MARGIN, 130, 60), '17 on') self.on17_button.on_clicked.connect(self.gpi_button) self.add_child(self.on17_button) self.on4_button = ui.Button(ui.Rect(170, MARGIN, 130, 60), '4 on') self.on4_button.on_clicked.connect(self.gpi_button) self.add_child(self.on4_button) self.off17_button = ui.Button(ui.Rect(MARGIN, 100, 130, 60), '17 off') self.off17_button.on_clicked.connect(self.gpi_button) self.add_child(self.off17_button) self.off4_button = ui.Button(ui.Rect(170, 100, 130, 60), '4 off') self.off4_button.on_clicked.connect(self.gpi_button) self.add_child(self.off4_button) self.progress_view = ui.ProgressView(ui.Rect(MARGIN, 200, 280, 40)) self.add_child(self.progress_view) self.volts_value = ui.Label(ui.Rect(135, 170, 50, 30), '') self.add_child(self.volts_value) def gpi_button(self, btn, mbtn): logger.info(btn.text) if btn.text == '17 on': GPIO.output(17, False) elif btn.text == '4 on': GPIO.output(4, False) elif btn.text == '17 off': GPIO.output(17, True) elif btn.text == '4 off': GPIO.output(4, True) def set_progress(self, percent): self.progress_view.progress = percent def set_volts_label(self, volts): self.volts_value.text = '%.2f' % volts def update(self, dt): ui.Scene.update(self, dt) ui.init('Raspberry Pi UI', (320, 240)) pygame.mouse.set_visible(False) pitft = PiTft() # Start the thread running the callable potreader = PotReader(pitft) threading.Thread(target=potreader).start() def signal_handler(signal, frame): print 'You pressed Ctrl+C!' potreader.terminate() sys.exit(0) signal.signal(signal.SIGINT, signal_handler) ui.scene.push(pitft) ui.run()