Hi there, long-time lurker, first-time poster.
I have built a Pi e-ink dashboard using a Waveshare 7.5 e-paper screen and a Raspberry Pi 3b+. I am using Python for the script that renders and displays the image on the screen. I also built a Flask web app to remotely control the dashboard from other devices on my home network. The app uses buttons to send POST requests that trigger the Python scripts. The problem is, whenever the Flask server is running on the Pi and I try to run the image display script, either from the web app or via the Terminal, I get a "lgpio.error: 'GPIO busy'" error. When the server is off, the script runs fine from the Terminal, but I would like to manage the dashboard without having to SSH to it all the time.
I don't know why running the server would require interacting with the GPIO pins at all. My photo display script sleeps the display as recommended, and I have used both the Flask development server and Waitress to run the web app, with the same error popping up when running the script. Is there something I am missing? I have posted the error below, along with my Flask app and the photo display script, for reference. Any help would be greatly appreciated.
Thanks!
GPIO Error
Flask App
Photo Display script
I have built a Pi e-ink dashboard using a Waveshare 7.5 e-paper screen and a Raspberry Pi 3b+. I am using Python for the script that renders and displays the image on the screen. I also built a Flask web app to remotely control the dashboard from other devices on my home network. The app uses buttons to send POST requests that trigger the Python scripts. The problem is, whenever the Flask server is running on the Pi and I try to run the image display script, either from the web app or via the Terminal, I get a "lgpio.error: 'GPIO busy'" error. When the server is off, the script runs fine from the Terminal, but I would like to manage the dashboard without having to SSH to it all the time.
I don't know why running the server would require interacting with the GPIO pins at all. My photo display script sleeps the display as recommended, and I have used both the Flask development server and Waitress to run the web app, with the same error popping up when running the script. Is there something I am missing? I have posted the error below, along with my Flask app and the photo display script, for reference. Any help would be greatly appreciated.
Thanks!
GPIO Error
Code:
Traceback (most recent call last): File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 411, in pin pin = self.pins[info] ~~~~~~~~~^^^^^^KeyError: PinInfo(number=11, name='GPIO17', names=frozenset({'BOARD11', 17, 'WPI0', 'GPIO17', '17', 'J8:11', 'BCM17'}), pull='', row=6, col=1, interfaces=frozenset({'', 'spi', 'dpi', 'gpio', 'uart'}))During handling of the above exception, another exception occurred:Traceback (most recent call last): File "/home/tempus/tempus-db/manage/../photodisplay.py", line 8, in <module> from waveshare_epd import epd7in5_V2 File "/home/tempus/tempus-db/waveshare_epd/epd7in5_V2.py", line 32, in <module> from . import epdconfig File "/home/tempus/tempus-db/waveshare_epd/epdconfig.py", line 313, in <module> implementation = RaspberryPi() ^^^^^^^^^^^^^ File "/home/tempus/tempus-db/waveshare_epd/epdconfig.py", line 56, in __init__ self.GPIO_RST_PIN = gpiozero.LED(self.RST_PIN) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/gpiozero/devices.py", line 108, in __call__ self = super().__call__(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/gpiozero/output_devices.py", line 192, in __init__ super().__init__(pin, active_high=active_high, File "/usr/lib/python3/dist-packages/gpiozero/output_devices.py", line 74, in __init__ super().__init__(pin, pin_factory=pin_factory) File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 75, in __init__ super().__init__(*args, **kwargs) File "/usr/lib/python3/dist-packages/gpiozero/devices.py", line 553, in __init__ pin = self.pin_factory.pin(pin) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 413, in pin pin = self.pin_class(self, info) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/gpiozero/pins/lgpio.py", line 126, in __init__ lgpio.gpio_claim_input( File "/usr/lib/python3/dist-packages/lgpio.py", line 755, in gpio_claim_input return _u2i(_lgpio._gpio_claim_input(handle&0xffff, lFlags, gpio)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/lgpio.py", line 458, in _u2i raise error(error_text(v))lgpio.error: 'GPIO busy'Flask App
Code:
import subprocessfrom flask import Flask, url_for, render_template, request, jsonifyfrom markupsafe import escapefrom manager import *app = Flask(__name__)@app.route('/')def index(): # define dashboard buttons button_list = [ {"id": "dashboard", "text": "Start Dashboard", "icon": "layout-dashboard.svg", "route":""}, {"id": "photo", "text": "Display Image", "icon": "image.svg", "route":"/showimage"}, {"id": "clear", "text": "Clear Dashboard", "icon": "brush-cleaning.svg", "route":"/clear"}, {"id": "pause", "text": "Pause Dashboard", "icon": "circle-pause.svg", "route":"/dashstop"} ] return render_template('index.html', buttons=button_list)# start dashboard route@app.route('/dashstart', methods=['POST'])def dashstart(): if request.is_json: data = request.get_json() timer = data["timing"] hasjob = set_tempus_job(timer) if hasjob: subprocess.run(['python3', '../main.py']) result = f"Dashboard started successfully. Refresh time interval is {timer} minutes." print(result) return jsonify({'message': result}) else: return jsonify({'error': 'Dashboard not started'}), 400 else: return jsonify({'error': 'Request must be JSON'}), 400# display photo route@app.route('/showimage', methods=['POST'])def showimage(): stopped = clear_tempus_jobs() if stopped: subprocess.run(['python3', '../photodisplay.py']) result = "Switched to photo display mode" print(result) return jsonify({'message': result}) else: return jsonify({'error': 'Photo display failed'}), 400# clear screen route@app.route('/clear', methods=['POST'])def cleardash(): clear_display() result = "Dashboard Cleared Successfully" print(result) return jsonify({'message': result})# stop dashboard cron job route@app.route('/dashstop', methods=['POST'])def dashstop(): stopped = clear_tempus_jobs() if stopped: result = "Dashboard Paused Successfully" print(result) return jsonify({'message': result}) else: return jsonify({'error': 'Dashboard not paused'}), 400Photo Display script
Code:
import loggingimport sysimport pathlibimport timefrom PIL import Imagefrom waveshare_epd import epd7in5_V2if __name__ == '__main__':logger = logging.getLogger('tempusdash')config = {"imgW": 800,"imgH": 480,"angle": 0,"photo_path": "photo-display/photo.png","error_photo": "photo-display/error.png"}# load configuration for photo displaycurrPath = str(pathlib.Path(__file__).parent.absolute())# initialize displayepd = epd7in5_V2.EPD()img_wid = config["imgW"]img_ht = config["imgH"]angle = config["angle"]photo_path = currPath + "/" + config["photo_path"]error_photo = currPath + "/" + config["error_photo"]# create and configure loggerlogging.basicConfig(filename="logfile.log", format='%(asctime)s %(levelname)s - %(message)s', filemode='a')logger = logging.getLogger('tempusdash')logger.addHandler(logging.StreamHandler(sys.stdout)) # print logger to stdoutlogger.setLevel(logging.INFO)logger.info("Starting photo display")# get pictureif pathlib.Path(photo_path).exists():dashimage = Image.open(photo_path)log_msg = "Photo suscessfully displayed on screen"else:dashimage = Image.open(error_photo)log_msg = "Photo not found"dashimage = dashimage.convert("1")epd.init()epd.Clear()epd.display(epd.getbuffer(dashimage))logger.info(log_msg)time.sleep(2)epd.sleep()logger.info("Completed photo display update")Statistics: Posted by cealpha — Thu Aug 28, 2025 8:08 pm