I'm in the process of making a little stock tracker using a Pi Zero 2 and an e-Paper display. At the moment I've just got 1 button that when pressed cycles to the next stock, then also updates the display with that stocks information.
This all works, but as these displays aren't meant to be left with power for long periods I need to add in a sleep call after each display refresh. I added a button using gpiozero, added a sleep function at the end of my pressed() function and it all worked... once. The first time I press the button all goes smoothly, but the 2nd time it spits out an error. After some trial and error I discovered it's the sleep call that causes the issues.
My code is as follows:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys
import os
picdir = 'pic'
libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib')
if os.path.exists(libdir):
sys.path.append(libdir)
import logging
from waveshare_epd import epd2in13_V3
import time
from PIL import Image,ImageDraw,ImageFont
import traceback
import yfinance as yf
from gpiozero import Button
import keyboard
logging.basicConfig(level=logging.DEBUG)
button = Button(2)
pageNum = 0
pageMax = 2
def pressed():
print("Pressed")
global pageNum, pageMax
if pageNum < pageMax:
pageNum += 1
elif pageNum >= pageMax:
pageNum = 0
amc = yf.Ticker("AMC")
gme = yf.Ticker("GME")
msft = yf.Ticker("MSFT")
if pageNum == 0:
print("GME selected")
print(pageNum)
stock = "GME"
buy = gme.info['ask']
sell = gme.info['bid']
high = gme.info['dayHigh']
low = gme.info['dayLow']
opening = gme.info['open']
current = gme.info['currentPrice']
elif pageNum == 1:
print("MSFT selected")
print(pageNum)
stock = "MSFT"
buy = msft.info['ask']
sell = msft.info['bid']
high = msft.info['dayHigh']
low = msft.info['dayLow']
opening = msft.info['open']
current = msft.info['currentPrice']
elif pageNum == 2:
print("AMC selected")
print(pageNum)
stock = "AMC"
buy = amc.info['ask']
sell = amc.info['bid']
high = amc.info['dayHigh']
low = amc.info['dayLow']
opening = amc.info['open']
current = amc.info['currentPrice']
buyR = round(buy, 2)
sellR = round(sell, 2)
highR = round(high, 2)
lowR = round(low, 2)
openingR = round(opening, 2)
currentR = round(current, 2)
# Blank out rectangles
draw.rectangle((2, 2, 125, 33), fill = 255)
draw.rectangle((28, 37, 123, 90), fill = 255)
draw.rectangle((151, 37, 247, 90), fill = 255)
draw.rectangle((142, 94, 247, 119), fill = 255)
draw.text((4, 0), str(stock), font = fnt_large, fill = 0)
draw.text((28, 40), str(highR), font = fnt_small, fill = 0)
draw.text((28, 66), str(lowR), font = fnt_small, fill = 0)
draw.text((151, 40), str(buyR), font = fnt_small, fill = 0)
draw.text((151, 66), str(sellR), font = fnt_small, fill = 0)
draw.text((142, 93), str(currentR), font = fnt_med, fill = 0)
epd.displayPartial(epd.getbuffer(image)) # Refresh the prices
logging.info("Sleep from pressed()")
epd.sleep()
button.when_pressed = pressed
try:
logging.info("init and Clear")
epd = epd2in13_V3.EPD()
epd.init()
# epd.Clear(0xFF)
fnt_small = ImageFont.truetype(os.path.join(picdir, 'DejaVuSans-Bold.ttf'), 20)
fnt_med = ImageFont.truetype(os.path.join(picdir, 'DejaVuSans-Bold.ttf'), 24)
fnt_large = ImageFont.truetype(os.path.join(picdir, 'DejaVuSans-Bold.ttf'), 32)
logging.info("New image")
image = Image.new('1', (epd.height, epd.width), 255) # 255 = w BG, 0 = black BG
draw = ImageDraw.Draw(image)
epd.displayPartBaseImage(epd.getbuffer(image)) # Refresh the display
# Draw the table
draw.line([(0,0),(249,0)], fill = 0,width = 1)
draw.line([(249,0),(249,121)], fill = 0,width = 1)
draw.line([(0,121),(249,121)], fill = 0,width = 1)
draw.line([(0,0),(0,121)], fill = 0,width = 1)
draw.line([(0,35),(250,35)], fill = 0,width = 1)
draw.line([(0,92),(250,92)], fill = 0,width = 1)
draw.line([(125,35),(125,92)], fill = 0,width = 1)
draw.text((4, 40), 'H:', font = fnt_small, fill = 0)
draw.text((4, 66), 'L:', font = fnt_small, fill = 0)
draw.text((129, 40), 'B:', font = fnt_small, fill = 0)
draw.text((129, 66), 'S:', font = fnt_small, fill = 0)
draw.text((6, 93), 'CURRENT:', font = fnt_med, fill = 0)
epd.display(epd.getbuffer(image))
while True:
keyboard.wait('q')
keyboard.send('ctrl+6')
logging.info("Sleep mode")
epd.sleep()
except IOError as e:
logging.info(e)
except KeyboardInterrupt:
logging.info("ctrl + c:")
epd2in13_V3.epdconfig.module_exit()
exit
And the error code that is generated when the button is pressed for a 2nd time is:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/gpiozero/pins/rpigpio.py", line 223, in _call_when_changed
super(RPiGPIOPin, self)._call_when_changed()
File "/usr/lib/python3/dist-packages/gpiozero/pins/local.py", line 130, in _call_when_changed
self.state if state is None else state)
File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 298, in _call_when_changed
method(ticks, state)
File "/usr/lib/python3/dist-packages/gpiozero/input_devices.py", line 182, in _pin_changed
self._fire_events(ticks, bool(self._state_to_value(state)))
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 401, in _fire_events
self._fire_activated()
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 447, in _fire_activated
super(HoldMixin, self)._fire_activated()
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 364, in _fire_activated
self.when_activated()
File "1.py", line 89, in pressed
epd.displayPartial(epd.getbuffer(image)) # Refresh the prices
File "/home/berry/e-Paper/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epd2in13_V3.py", line 311, in displayPa rtial
epdconfig.digital_write(self.reset_pin, 0)
File "/home/berry/e-Paper/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epdconfig.py", line 53, in digital_writ e
self.GPIO.output(pin, value)
RuntimeError: The GPIO channel has not been set up as an OUTPUT
I've tried a few different approaches to try and get this working but am all out of ideas, it seems no matter where I put it, the sleep line always goes wrong the 2nd time it's called.