Hardware components | ||||||
| × | 1 | ||||
![]() |
| × | 1 | |||
| × | 11 | ||||
| × | 11 | ||||
![]() |
| × | 2 | |||
![]() |
| × | 1 | |||
![]() |
| × | 4 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
Hand tools and fabrication machines | ||||||
![]() |
|
My goal when I was looking for a project was to have something that had an interesting mechanical aspect and quite a regular software and electrical aspect. As a Mechanical Engineering major I was captivated by the mechanical intricacies of the split flap display. Even more intriguing was the perfectly timed rotation of the stepper motors to display the different characters. Also the flaps made very satisfying tapping sounds as they flipped around.
I based my project on the work that Jeff did in his project and also took some inspiration from Scott.
CodeFor the most part I wrote my own code as adaptations of the different project we did in class (ENGI 301) with Professor Welsh. For example I adapted the codes for the Button, ht16k33, SPI screen. Here is the link to the GitHub.
BuildInstructions
Mechanical Assembly:
The assembly instructions are here. Step 4 within the instructions is specific to the mechanical assembly; I used laser-cut acrylic for the frame - 1/8 '' for the outer case and 1/16'' for the flaps. There is Scott's webpage that can be used for ordering pre-made materials like the flaps, letter stickers etc. On the same webpage is a link to the laser-cut files for the frame.
Circuit Setup:
Here is a link to a YouTube video that shows how to connect the 28BYJ-48 Stepper Motor with a A4988 Stepper Motor Driver. Written instructions with pictures are linked here. Please not that the Step and Dir are connected to an Arduino in the instructions linked above, in my project these can be connected to any GPIO pins on the PocketBeagle.
"""
--------------------------------------------------------------------------
Main
--------------------------------------------------------------------------
License:
Copyright 2021 Peter Tizora
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
"""
import time
import Adafruit_BBIO.GPIO as GPIO
# to be replaced by the SPI library
import button as BUTTON
import motor as MOTOR
import spi_screen as SPI_SCREEN
class Mech_split_flap_disp():
reset_time = None
button = None # for timer
button2 = None # for stopwatch
display = None
motor1 = None
motor2 = None
def __init__(self, reset_time=1.5,
button="P2_18",button2="P2_20",
i2c_bus=1, i2c_address=0x70,
step_pin1="P2_6", dir_pin1="P2_4",
step_pin2="P2_10", dir_pin2="P2_8"):
""" Initialize variables and set up SPI display """
self.reset_time = reset_time
self.button = BUTTON.Button(button)
self.button2 = BUTTON.Button(button2)
self.display = SPI_SCREEN.SPI_Display()
self.motor1 = MOTOR.StepperMotor(step_pin1, dir_pin1)
self.motor2 = MOTOR.StepperMotor(step_pin2, dir_pin2)
self._setup()
# End def
# End class
def _setup(self):
"""Setup the hardware components."""
# Initialize Display
print("Project setup()")
self.display.blank()
# End def
def timer(self):
# run the timer while the green button has not been pressed for 2 seconds
print("timer starts")
time.sleep(0.5)
_time = 0
self.display.text([" TIMER",
" ",
"Press:",
" Green Button to set time",
" Black Button to start timer",
"Long Press Black to exit"])
loop = True
button2_press_time = self.button2.wait_for_press()[0]
if (button2_press_time > self.reset_time):
loop = False
while(loop):
if (self.button.is_pressed()):
_time = _time + 10
self.display.text(["time set", str(_time)])
elif (self.button2.is_pressed()):
loop = False
else:
time.sleep(0.1)
count = _time
while(_time):
_time -= 1
_realtime = count - _time
mins, secs = divmod(_realtime, 60)
timer = '{:02d}:{:02d}'.format(mins, secs)
# time.sleep(1)
print(timer, end="\r")
self.motor1.set_direction(1) # Set direction to clockwise
self.motor1.rotate_motor(1) # Rotate the first motor every second
# if secs == 0: # Rotate the second motor once every minute
# motor2.set_direction(1) # Set direction to clockwise
# self.motor2.rotate_motor(36,1)
self.display.text(str(timer))
if(self.button2.is_pressed()):
break
time.sleep(0.01)
time.sleep(1)
# End Def
def stopwatch(self):
print("stopwatch starts")
time.sleep(0.5)
_time2 = 0
loop2 = True
self.display.text([" STOPWATCH",
" ",
"Press:",
" Green Button to start/stop",
" Black Button to reset",
" Long Press Black to exit"])
button2_press_time = self.button2.wait_for_press()[0]
if (button2_press_time > self.reset_time):
loop2 = False
while(loop2):
if (self.button.is_pressed()):
loop3_1 = True
while(loop3_1):
_time2 = _time2 + 1
mins2, secs2 = divmod(_time2, 60)
timer2 = '{:02d}:{:02d}'.format(mins2, secs2)
print(timer2, end="\r")
# if secs2 == 0: # Rotate the second motor once every minute
# motor2.set_direction(1) # Set direction to clockwise
# self.motor2.rotate_motor(36,1)
self.display.text(str(timer2))
self.motor1.set_direction(1) # Set direction to clockwise
self.motor1.rotate_motor(1) # Rotate the first motor every second
var = 1
#time.sleep(0.5)
while(var):
var = var - 1
if (self.button.is_pressed()):
loop3_1 = False
#time.sleep(0.5)
# if (self.button.is_pressed()):
# #time.sleep(1)
# self.display.text(str(timer2))
# loop4 = True
# while(loop4):
# if (self.button.is_pressed()):
# loop4 = False
if (self.button2.is_pressed()):
_time2 = 0
mins2, secs2 = divmod(_time2, 60)
timer2 = '{:02d}:{:02d}'.format(mins2, secs2)
print(timer2, end="\r")
self.display.text(str(timer2))
time.sleep(0.1)
loop2 = False
time.sleep(1)
# End Def
def run(self):
"""Execute the main program."""
button_press_time = 0.0 # Time button was pressed (in seconds)
button2_press_time = 0.0
print("Run Class")
print("Welcome Message")
self.display.text([" Welcome to my", " Timer/Stopwatch Program!"])
time.sleep(2)
while(1):
print("In Run loop")
# Wait for button press
# button_press_time = self.button.wait_for_press()[0]
# button2_press_time = self.button2.wait_for_press()[0]
# Record time
# My own code starts here
print("SPI displays ")
self.display.text([" HOME"
" ",
"Press:",
" Green Button for Timer",
" Black Button for Stopwatch"])
wait_forpress = True
while(wait_forpress):
if (self.button.is_pressed()):
# Run timer if green button is pressed
wait_forpress = False
self.timer()
time.sleep(1.5)
if (self.button2.is_pressed()):
# Run stopwatch if black button is pressed
wait_forpress = False
self.stopwatch()
time.sleep(0.1)
time.sleep(1)
# Wait for button release
# # Compare time to increment or reset people_count
# if (button_press_time < self.reset_time):
# if people_count < HT16K33.HT16K33_MAX_VALUE:
# people_count = people_count + 1
# else:
# people_count = 0
# else:
# people_count = 0
# # Update the display
# self.display.update(people_count)
# End def
def cleanup(self):
"""Cleanup the hardware components."""
# Set Display to something unique to show program is complete
self.display.text(" IDLE", fontsize=40)
# Button does not need any cleanup code
#End def
# End class
if __name__ == '__main__':
print("Program Start")
# Create instantiation of the mech_split_flap_disp
mech_split_flap_disp = Mech_split_flap_disp()
try:
mech_split_flap_disp.run()
except KeyboardInterrupt:
# Clean up hardware when exiting
mech_split_flap_disp.cleanup()
print("Program Complete")
"""
--------------------------------------------------------------------------
Main
--------------------------------------------------------------------------
License:
Copyright 2021 Peter Tizora
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
"""
import Adafruit_BBIO.GPIO as GPIO
import time
class StepperMotor():
def __init__(self, step_pin, dir_pin, full_step=True):
self.STEP_PIN = step_pin
self.DIR_PIN = dir_pin
self.FULL_STEP = full_step
self.STEPS_PER_REVOLUTION = 2048 if self.FULL_STEP else 4096
self._time2 = 0
# Set up the GPIO pins
GPIO.setup(self.STEP_PIN, GPIO.OUT)
GPIO.setup(self.DIR_PIN, GPIO.OUT)
def set_direction(self, direction):
# Set the direction (1 for clockwise, 0 for counterclockwise)
GPIO.output(self.DIR_PIN, direction)
def rotate_motor(self, time_in):
# Calculate the number of degrees to rotate per second
degrees_per_second = 36
degrees = degrees_per_second * time_in
# Calculate the number of steps to rotate the specified number of degrees
steps = int(degrees / 360 * self.STEPS_PER_REVOLUTION)
# Calculate the delay for a 36-degree rotation per second
delay = 1 / (degrees_per_second / 360 * self.STEPS_PER_REVOLUTION)
# Perform the steps
for i in range(steps):
# Pulse the STEP pin to move the motor
GPIO.output(self.STEP_PIN, 1)
time.sleep(delay/2) # Adjusted delay
GPIO.output(self.STEP_PIN, 0)
time.sleep(delay/2) # Adjusted delay
if __name__ == '__main__':
motor1 = StepperMotor("P2_6", "P2_4")
#motor2 = StepperMotor("P2_10", "P2_8")
motor1.set_direction(1) # Set direction to clockwise
#motor2.set_direction(1)
motor1.rotate_motor(10) # Rotate the motor time_in seconds clockwise
# motor2.rotate_motor(36,10)
#!/bin/bash
# --------------------------------------------------------------------------
# People Counter - Configure Pins
# --------------------------------------------------------------------------
# License:
# Copyright 2023 Peter
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# --------------------------------------------------------------------------
#
# Configure pins for digital people counter:
# - I2C1
# - Button
#
# --------------------------------------------------------------------------
# I2C1
config-pin P2_09 i2c
config-pin P2_11 i2c
# Motors
config-pin P2_04 gpio
config-pin P2_06 gpio
config-pin P2_08 gpio
config-pin P2_10 gpio
# Buttons
config-pin P2_18 gpio # Green button
config-pin P2_20 gpio # Black button
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
SPI Display Library
--------------------------------------------------------------------------
License:
Copyright 2021 Erik Welsh
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
Software API:
SPI_DISPLAY()
- Provide spi bus that dispaly is on
- Provide spi address for the display
blank()
- Fills the display with black (i.e. color (0,0,0))
fill(color)
- Fills the display with the given (R, G, B) color tuple
image(filename, rotation=90)
- Erases display and shows image from filename
text(value, fontsize=24, fontcolor=(255,255,255), backgroundcolor=(0,0,0),
justify=LEFT, align=TOP, rotation=90):
- Erases display and shows text value on display
- Value can either be a string or list of strings for multiple lines of text
--------------------------------------------------------------------------
Background Information:
Links:
- https://learn.adafruit.com/adafruit-2-8-and-3-2-color-tft-touchscreen-breakout-v2/overview
- https://learn.adafruit.com/adafruit-2-8-and-3-2-color-tft-touchscreen-breakout-v2/spi-wiring-and-test
- https://learn.adafruit.com/adafruit-2-8-and-3-2-color-tft-touchscreen-breakout-v2/python-wiring-and-setup
- https://learn.adafruit.com/adafruit-2-8-and-3-2-color-tft-touchscreen-breakout-v2/python-usage
- https://circuitpython.readthedocs.io/projects/rgb_display/en/latest/api.html#module-adafruit_rgb_display.rgb
- https://circuitpython.readthedocs.io/projects/rgb_display/en/latest/_modules/adafruit_rgb_display/rgb.html
Software Setup:
- sudo apt-get update
- sudo pip3 install --upgrade Pillow
- sudo pip3 install adafruit-circuitpython-busdevice
- sudo pip3 install adafruit-circuitpython-rgb-display
- sudo apt-get install ttf-dejavu -y
"""
import time
import busio
import board
import digitalio
from PIL import Image, ImageDraw, ImageFont
from adafruit_rgb_display import color565
import adafruit_rgb_display.ili9341 as ili9341
# ------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------
LEFT = 0
RIGHT = 1
TOP = 2
BOTTOM = 3
CENTER = 4
PADDING = -5 # May need to adjust based on font
# ------------------------------------------------------------------------
# Functions / Classes
# ------------------------------------------------------------------------
class SPI_Display():
""" Class to manage an SPI display """
reset_pin = None
dc_pin = None
cs_pin = None
spi_bus = None
display = None
def __init__(self, clk_pin=board.SCLK, miso_pin=board.MISO, mosi_pin=board.MOSI,
cs_pin=board.P1_6, dc_pin=board.P1_4, reset_pin=board.P1_2,
baudrate=24000000, rotation=90):
""" SPI Display Constructor
:param clk_pin : Value must be a pin from adafruit board library
:param miso_pin : Value must be a pin from adafruit board library
:param mosi_pin : Value must be a pin from adafruit board library
:param cs_pin : Value must be a pin from adafruit board library
:param dc_pin : Value must be a pin from adafruit board library
:param reset_pin : Value must be a pin from adafruit board library
:param baudrate : SPI communication rate; default 24MHz
:param rotation : Rotation of display; default 90 degrees (landscape)
"""
# Configuration for CS and DC pins:
self.reset_pin = digitalio.DigitalInOut(reset_pin)
self.dc_pin = digitalio.DigitalInOut(dc_pin)
self.cs_pin = digitalio.DigitalInOut(cs_pin)
# Setup SPI bus using hardware SPI
self.spi_bus = busio.SPI(clock=clk_pin, MISO=miso_pin, MOSI=mosi_pin)
# Create the ILI9341 display:
self.display = ili9341.ILI9341(self.spi_bus, cs=self.cs_pin, dc=self.dc_pin,
baudrate=baudrate, rotation=rotation)
# Initialize Hardware
self._setup()
# End def
def _setup(self):
"""Initialize the display itself"""
# Clear the display
self.blank()
# End def
def blank(self):
"""Clear the display a black screen"""
self.fill((0,0,0))
# End def
def fill(self, color):
"""Fill the display with the given color"""
if ((color[0] < 0) or (color[0] > 255) or
(color[1] < 0) or (color[1] > 255) or
(color[2] < 0) or (color[2] > 255)):
raise ValueError("(R,G,B) must be between 0 and 255: ({0}, {1}, {2})".format(color[0], color[1], color[2]))
self.display.fill(color565(color[0], color[1], color[2]))
# End def
def _get_dimensions(self, rotation):
"""Get display dimensions"""
# Check image rotation
if rotation % 180 == 90:
height = self.display.width # Swap height/width to rotate it to landscape!
width = self.display.height
else:
width = self.display.width
height = self.display.height
return (width, height)
# End def
def image(self, filename, rotation=90):
"""Display the image on the screen"""
# Fill display with black pixels to clear the image
self.blank()
# Create image with file name
image = Image.open(filename)
# Get screen dimensions
width, height = self._get_dimensions(rotation)
# Scale the image to the smaller screen dimension
image_ratio = image.width / image.height
screen_ratio = width / height
if screen_ratio < image_ratio:
scaled_width = image.width * height // image.height
scaled_height = height
else:
scaled_width = width
scaled_height = image.height * width // image.width
image = image.resize((scaled_width, scaled_height), Image.BICUBIC)
# Crop and center the image
x = scaled_width // 2 - width // 2
y = scaled_height // 2 - height // 2
image = image.crop((x, y, x + width, y + height))
# Display image
self.display.image(image)
# End def
def text(self, value, fontsize=22, fontcolor=(255,255,255),
backgroundcolor=(0,0,0), justify=LEFT, align=TOP,
rotation=90):
""" Update the display with text
:param value : Value can be a string or list of string
:param fontsize : Size of font
:param fontcolor : (R, G, B) tuple for the color of the text
:param backgroundcolor : (R, G, B) tuple for the color of the background
:param justify : Value in [LEFT, CENTER, RIGHT]
:param align : Value in [TOP, CENTER, BOTTOM]
:param rotation : Orientation of the display
Will throw a ValueError
"""
# Debug variable
debug = False
# Check inputs:
if justify not in [LEFT, CENTER, RIGHT]:
raise ValueError("Input justify must be in [LEFT, CENTER, RIGHT]")
if align not in [TOP, CENTER, BOTTOM]:
raise ValueError("Input align must be in [TOP, CENTER, BOTTOM]")
# Determine if text value is string or list
# - Rest of function assumes value is a list of strings
if (type(value) is not list):
value = [value]
# Clear screen
self.fill(backgroundcolor)
# Get display dimensions
width, height = self._get_dimensions(rotation)
# Create a canvas for drawing
canvas = Image.new("RGB", (width, height))
# Get drawing object to draw on canvas
draw = ImageDraw.Draw(canvas)
# Load a TTF Font
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", fontsize)
# Get height of a character
font_height = font.getsize(" ")[1]
if (debug):
print("Canvas h = {0}".format(height))
print("Font h = {0}".format(font_height))
# Calculate number of lines supported on screen w/ font choice
num_line = height // font_height
if (debug):
print("Num Lines = {0}".format(num_line))
# Issue warning if too many lines
if (len(value) > num_line):
print("WARNING: Too many lines for font size. Truncating.")
print(" Required lines : {0}".format(len(value)))
print(" Available lines: {0}".format(num_line))
# Truncate list
del value[num_line:]
# Create list of positions for each line
text_height = len(value) * font_height # Number of lines * font height
# Get initial y position
if align == TOP:
y = 0
if align == BOTTOM:
y = height - text_height
if align == CENTER:
y = (height // 2) - (text_height // 2)
# Adjust y position by padding
y = y + PADDING
# Only print lines there is space for
for i, line in enumerate(value):
# Get width of line
line_width = font.getsize(line)[0]
# Issue warning if too many characters
if (line_width > width):
print("WARNING: Too many characters for the line. Truncating.")
print(" Required width : {0}".format(line_width))
print(" Available width: {0}".format(width))
# Truncate line
for i in range(len(line)):
line_width = font.getsize(line[:-(i+1)])[0]
if (line_width <= width):
line = line[:-(i+1)]
break
# Get x position
if justify == LEFT:
x = 0
if justify == RIGHT:
x = width - line_width
if align == CENTER:
x = (width // 2) - (line_width // 2)
# Draw the text
draw.text((x, y), line, font=font, fill=fontcolor)
y += font_height
# Display image
self.display.image(canvas)
# End def
# End class
# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------
if __name__ == '__main__':
import time
delay = 2
print("Test SPI Display:")
print("Create Display")
display = SPI_Display()
time.sleep(delay)
# Test Functions
print("Fill Red")
display.fill((255, 0, 0))
time.sleep(delay)
print("Fill Green")
display.fill((0, 255, 0))
time.sleep(delay)
print("Fill Blue")
display.fill((0, 0, 255))
time.sleep(delay)
print("Display blinka.jpg")
display.image("blinka.jpg")
time.sleep(delay)
print("Display Text")
display.text("This is some text!!")
time.sleep(delay)
print("Display Multi-line Text")
display.text(["This is some text", "on multiple lines!!"])
time.sleep(delay)
print("Display Multi-line Text, centered")
display.text(["This is some text", "on multiple lines!!"], justify=CENTER, align=CENTER)
time.sleep(delay)
print("Display Multi-line Text, right justify, align bottom, fontsize 30")
display.text(["This is some text", "on multiple lines!!", "asdf", "asdf", "asdf", "abcdefghijklmnopqrstuvwxyz"],
fontsize=30, justify=RIGHT, align=BOTTOM)
time.sleep(delay)
print("Test Finished.")
#!/bin/bash
# --------------------------------------------------------------------------
# People Counter - Run Script
# --------------------------------------------------------------------------
# License:
# Copyright 2020 <Name>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# --------------------------------------------------------------------------
#
# Run People Counter
#
# --------------------------------------------------------------------------
cd /var/lib/cloud9/ENGI301/python/project1
./configure_pins.sh
PYTHONPATH=/var/lib/cloud9/ENGI301/python/project1:/var/lib/cloud9/ENGI301/python/project1 python3 project01_Peter.py
"""
--------------------------------------------------------------------------
Button Driver
--------------------------------------------------------------------------
License:
Copyright 2021-2023 - Peter Tizora
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
Button Driver
This driver is built for buttons that have a pull up resistor between the
button and the processor pin (i.e. the input is "High"/"1" when the button is
not pressed) and will be connected to ground when the button is pressed (i.e.
the input is "Low" / "0" when the button is pressed)
Software API:
Button(pin)
- Provide pin that the button monitors
is_pressed()
- Return a boolean value (i.e. True/False) on if button is pressed
- Function consumes no time
wait_for_press(function=None)
- Wait for the button to be pressed
- Optionally takes in an argument "function" which is the function
to be executed when waiting for the button to be pressed
- Function consumes time
- Returns a tuple:
(<time button was pressed>, <data returned by the "function" argument>)
"""
import time
import Adafruit_BBIO.GPIO as GPIO
# ------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------
# None
# ------------------------------------------------------------------------
# Global variables
# ------------------------------------------------------------------------
# None
# ------------------------------------------------------------------------
# Functions / Classes
# ------------------------------------------------------------------------
class Button():
""" Button Class """
pin = None
unpressed_value = None
pressed_value = None
sleep_time = None
def __init__(self, pin=None):
""" Initialize variables and set up the button """
if (pin == None):
raise ValueError("Pin not provided for Button()")
else:
self.pin = pin
# By default the unpressed_value is "1" and the pressed
# value is "0". This is done to make it easier to change
# in the future
self.unpressed_value = 1
self.pressed_value = 0
# By default sleep time is "0.1" seconds
self.sleep_time = 0.1
# Initialize the hardware components
self._setup()
# End def
def _setup(self):
""" Setup the hardware components. """
GPIO.setup(self.pin, GPIO.IN)
# End def
def is_pressed(self):
""" Is the Button pressed?
Returns: True - Button is pressed
False - Button is not pressed
"""
return GPIO.input(self.pin) == self.pressed_value
# End def
def wait_for_press(self, function=None):
""" Wait for the button to be pressed. This function will
wait for the button to be pressed and released so there
are no race conditions.
Arguments:
function - Optional argument that is the functon to
executed while waiting for the button to
be pressed
Returns:
tuple - [0] Time button was pressed
- [1] Data returned by the "function" argument
"""
function_return_value = None
button_press_time = None
# Execute function if it is not None
# - This covers the case that the button is pressed prior
# to entering this function
if function is not None:
function_return_value = function()
# Wait for button press
# If the function is not None, execute the function
# Sleep for a short amount of time to reduce the CPU load
#
# HW#4 TODO: (one line of code)
# Update while loop condition to compare the input value of the
# GPIO pin of the buton (i.e. self.pin) to the "unpressed value"
# of the class (i.e. we are executing the while loop while the
# button is not being pressed)
# while(not self.is_pressed()):
# if function is not None:
# function_return_value = function()
# time.sleep(self.sleep_time)
# Record time
button_press_time = time.time()
# Wait for button release
# Sleep for a short amount of time to reduce the CPU load
#
# HW#4 TODO: (one line of code)
# Update while loop condition to compare the input value of the
# GPIO pin of the buton (i.e. self.pin) to the "pressed value"
# of the class (i.e. we are executing the while loop while the
# button is being pressed)
while(self.is_pressed()):
time.sleep(self.sleep_time)
# Compute the button_press_time
button_press_time = time.time() - button_press_time
# Return a tuple: (button press time, function return value)
return (button_press_time, function_return_value)
# End def
# End class
# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------
if __name__ == '__main__':
print("Button Test")
# Create instantiation of the button
button = Button("P2_20")
# Create an function to test the wait_for_press function
def print_time():
ret_val = time.time()
print(" Print Time = {0}".format(ret_val))
return ret_val
# End def
# Use a Keyboard Interrupt (i.e. "Ctrl-C") to exit the test
try:
# Check if the button is pressed
print("Is the button pressed?")
print(" {0}".format(button.is_pressed()))
print("Press and hold the button.")
time.sleep(4)
# Check if the button is pressed
print("Is the button pressed?")
print(" {0}".format(button.is_pressed()))
print("Release the button.")
time.sleep(4)
print("Waiting for button press ...")
value = button.wait_for_press()
print(" Button pressed for {0} seconds. ".format(value[0]))
print(" Function return value = {0}".format(value[1]))
print("Waiting for button press with optional argument ...")
value = button.wait_for_press(print_time)
print(" Button pressed for {0} seconds. ".format(value[0]))
print(" Function return value = {0}".format(value[1]))
except KeyboardInterrupt:
pass
print("Test Complete")
Comments
Please log in or sign up to comment.