python Raspberry Pi double occupancy of GPIO

I have a Raspberry Pi with an Display and Buttons (here). And I have a program, that uses the Library (in the appendix) from the Display. The program creates a home screen with dynamic apps. Each app consists of a folder in an “Apps” folder. This contains a 25×25 pixel PNG file, which is required for the home screen, and a .py file, which contains the program code. The newest code from the main program is this;

from PIL import Image, ImageDraw, ImageFont
import disp # a self-built driver to solve the problems with the double assignment.
import SH1106 # the library, in the appendix
disp.DispSingleton.disp = SH1106.SH1106() # initialize the driver
Disp = disp.DispSingleton()
Read = disp.Read(Disp)

# here I have deleted some lines of code that are not important for the question. It 

def update_display(): # this creates a homescreen
    image = Image.new('1', (Disp.width, Disp.height), "WHITE")
    draw = ImageDraw.Draw(image)
    draw_icons(draw, program_files, current_page, selected_icon)
    Disp.Update(image)

def run_script(script_file):
    import subprocess
    subprocess.run(["python3", script_file])

# Event-Loop
while True:
    update_display()
    Read.update()
    
    if Read.Joystick_Up == 1:
        selected_icon = (selected_icon - 4) % ICONS_PER_PAGE
        if selected_icon < 0:
            selected_icon += ICONS_PER_PAGE
        time.sleep(0.2)
    elif Read.Joystick_Down == 1:
        # as with Read.Joystick_Up, with slightly different values. 
    elif Read.Joystick_Left == 1:
        # as with Read.Joystick_Up, with slightly different values.
    elif Read.Joystick_Right == 1:
        # as with Read.Joystick_Up, with slightly different values.
    elif Read.Joystick_Press == 1:
        program_index = current_page * ICONS_PER_PAGE + selected_icon
        if program_index < len(program_files): # start a app from the homescreen
            print('33[94m' + "program", program_files[program_index], "started" + '33[0m')

            # start program and wait
            print("start thread:", program_files[program_index]['script_file'],)
            x = threading.Thread(target=run_script, args=(program_files[program_index]['script_file'],))
            print(x)
            x.start()

            while True:
                if x.run == False:
                    break

            print('33[94m' + "program", program_files[program_index], "stopped" + '33[0m')
        time.sleep(0.2)
    elif Read.Key_1 == 1:
        current_page = (current_page - 1) % total_pages
        if current_page < 0:
            current_page += total_pages
        time.sleep(0.2)
    elif Read.Key_2 == 1:
        current_page = (current_page + 1) % total_pages
        time.sleep(0.2)

This is the disp file (disp.py), that should solve the problems with the double assignment;

class DispSingleton(): # Both the program for the homescreen and the running program should access the same data to prevent duplicate use.
    def __init__(self):
        self.disp.Init()
        self.width = self.disp.width
        self.height = self.disp.height

    def Update(self, image):
        self.disp.ShowImage(self.disp.getbuffer(image))

    def Clear(self):
        self.disp.clear()

class Read:
    def __init__(self, disp_instance):
        self.disp_instance = disp_instance
        self.update()

    def update(self):
        self.Joystick_Up = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY_UP_PIN)
        self.Joystick_Down = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY_DOWN_PIN)
        self.Joystick_Left = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY_LEFT_PIN)
        self.Joystick_Right = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY_RIGHT_PIN)
        self.Joystick_Press = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY_PRESS_PIN)
        self.Key_1 = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY1_PIN)
        self.Key_2 = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY2_PIN)
        self.Key_3 = self.disp_instance.disp.RPI.digital_read(self.disp_instance.disp.RPI.GPIO_KEY3_PIN)

and a example of the code from a app;

import os
import sys
from PIL import Image, ImageDraw
sys.path.append(os.path.expanduser('~/Display'))
import home

Disp = home.Disp
Read = home.Read

image = Image.new('1', (Disp.width, Disp.height), "WHITE")
draw = ImageDraw.Draw(image)

class Home:
    def __init__(self):
        print('33[92m' + "Home initialized" + '33[0m')
        draw.polygon([(20, 20), (30, 2), (40, 20)], outline=255, fill=0)
        Disp.Update(image)

home = Home() # initialize Home

When I run the program, everything works fine. But when I select an application and click on “Run”, this error message appears;

start thread: /home/laurin/Display/Apps/lamps/code.py
<Thread(Thread-2 (run_script), initial)>
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 408, in pin
    pin = self.pins[info]
          ~~~~~~~~~^^^^^^
KeyError: PinInfo(number=22, name='GPIO25', names=frozenset({'J8:22', '25', 'BOARD22', 'WPI6', 'GPIO25', 25, 'BCM25'}), pull='', row=11, col=2, interfaces=frozenset({'', 'sdio', 'dpi', 'jtag', 'gpio'}))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/laurin/Display/Apps/lamps/code.py", line 7, in <module>
    import home
  File "/home/laurin/Display/home.py", line 145, in <module>
    disp.DispSingleton.disp = SH1106.SH1106()
                              ^^^^^^^^^^^^^^^
  File "/home/laurin/Display/Lib/SH1106.py", line 16, in __init__
    self.RPI = config.RaspberryPi()
               ^^^^^^^^^^^^^^^^^^^^
  File "/home/laurin/Display/Lib/config.py", line 67, in __init__
    self.GPIO_RST_PIN = self.gpio_mode(RST_PIN,self.OUTPUT)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/laurin/Display/Lib/config.py", line 88, in gpio_mode
    return DigitalOutputDevice(Pin,active_high = True,initial_value =False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/gpiozero/devices.py", line 103, 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 549, in __init__
    pin = self.pin_factory.pin(pin)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 410, 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'

what I have tried:

  • Transfer the Disp file when creating the new app.
  • Both programs pull the data from the same source so that it is only created once. (Singleton, See above)
    Restrict access from Home to the GPIO pins while the program is running.

what I have found and tried:

  • What is the best way of implementing singleton in Python, method 2

How can I solve this problem?
Thank you.

The Librarys:

SH1106.py;

import config
import time
import numpy as np

Device_SPI = config.Device_SPI
Device_I2C = config.Device_I2C

LCD_WIDTH   = 128 #LCD width
LCD_HEIGHT  = 64  #LCD height

class SH1106(object):
    def __init__(self):
        self.width = LCD_WIDTH
        self.height = LCD_HEIGHT
        #Initialize DC RST pin
        self.RPI = config.RaspberryPi()
        self._dc = self.RPI.GPIO_DC_PIN
        self._rst = self.RPI.GPIO_RST_PIN
        self.Device = self.RPI.Device


    """    Write register address and data     """
    def command(self, cmd):
        if(self.Device == Device_SPI):
            self.RPI.digital_write(self._dc,False)
            self.RPI.spi_writebyte([cmd])
        else:
            self.RPI.i2c_writebyte(0x00, cmd)

    # def data(self, val):
        # GPIO.output(self._dc, GPIO.HIGH)
        # config.spi_writebyte([val])

    def Restart(self):
        # __init__
        self.width = LCD_WIDTH
        self.height = LCD_HEIGHT
        #Initialize DC RST pin
        self.RPI = config.RaspberryPi()
        self._dc = self.RPI.GPIO_DC_PIN
        self._rst = self.RPI.GPIO_RST_PIN
        self.Device = self.RPI.Device

        # Init
        if (self.RPI.module_init() != 0):
            return -1
        """Initialize dispaly"""    
        self.reset()
        self.command(0xAE);#--turn off oled panel
        self.command(0x02);#---set low column address
        self.command(0x10);#---set high column address
        self.command(0x40);#--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
        self.command(0x81);#--set contrast control register
        self.command(0xA0);#--Set SEG/Column Mapping     
        self.command(0xC0);#Set COM/Row Scan Direction   
        self.command(0xA6);#--set normal display
        self.command(0xA8);#--set multiplex ratio(1 to 64)
        self.command(0x3F);#--1/64 duty
        self.command(0xD3);#-set display offset    Shift Mapping RAM Counter (0x00~0x3F)
        self.command(0x00);#-not offset
        self.command(0xd5);#--set display clock divide ratio/oscillator frequency
        self.command(0x80);#--set divide ratio, Set Clock as 100 Frames/Sec
        self.command(0xD9);#--set pre-charge period
        self.command(0xF1);#Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
        self.command(0xDA);#--set com pins hardware configuration
        self.command(0x12);
        self.command(0xDB);#--set vcomh
        self.command(0x40);#Set VCOM Deselect Level
        self.command(0x20);#-Set Page Addressing Mode (0x00/0x01/0x02)
        self.command(0x02);#
        self.command(0xA4);# Disable Entire Display On (0xa4/0xa5)
        self.command(0xA6);# Disable Inverse Display On (0xa6/a7) 
        time.sleep(0.1)
        self.command(0xAF);#--turn on oled panel

    def Init(self):
        if (self.RPI.module_init() != 0):
            return -1
        """Initialize dispaly"""    
        self.reset()
        self.command(0xAE);#--turn off oled panel
        self.command(0x02);#---set low column address
        self.command(0x10);#---set high column address
        self.command(0x40);#--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
        self.command(0x81);#--set contrast control register
        self.command(0xA0);#--Set SEG/Column Mapping     
        self.command(0xC0);#Set COM/Row Scan Direction   
        self.command(0xA6);#--set normal display
        self.command(0xA8);#--set multiplex ratio(1 to 64)
        self.command(0x3F);#--1/64 duty
        self.command(0xD3);#-set display offset    Shift Mapping RAM Counter (0x00~0x3F)
        self.command(0x00);#-not offset
        self.command(0xd5);#--set display clock divide ratio/oscillator frequency
        self.command(0x80);#--set divide ratio, Set Clock as 100 Frames/Sec
        self.command(0xD9);#--set pre-charge period
        self.command(0xF1);#Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
        self.command(0xDA);#--set com pins hardware configuration
        self.command(0x12);
        self.command(0xDB);#--set vcomh
        self.command(0x40);#Set VCOM Deselect Level
        self.command(0x20);#-Set Page Addressing Mode (0x00/0x01/0x02)
        self.command(0x02);#
        self.command(0xA4);# Disable Entire Display On (0xa4/0xa5)
        self.command(0xA6);# Disable Inverse Display On (0xa6/a7) 
        time.sleep(0.1)
        self.command(0xAF);#--turn on oled panel
        
   
    def reset(self):
        """Reset the display"""
        self.RPI.digital_write(self._rst,True)
        time.sleep(0.1)
        self.RPI.digital_write(self._rst,False)
        time.sleep(0.1)
        self.RPI.digital_write(self._rst,True)
        time.sleep(0.1)
    
    def getbuffer(self, image):
        # print "bufsiz = ",(self.width/8) * self.height
        buf = [0xFF] * ((self.width//8) * self.height)
        image_monocolor = image.convert('1')
        imwidth, imheight = image_monocolor.size
        pixels = image_monocolor.load()
        # print "imwidth = %d, imheight = %d",imwidth,imheight
        if(imwidth == self.width and imheight == self.height):
            # print ("Vertical")
            for y in range(imheight):
                for x in range(imwidth):
                    # Set the bits for the column of pixels at the current position.
                    if pixels[x, y] == 0:
                        buf[x + (y // 8) * self.width] &= ~(1 << (y % 8))
                        # print x,y,x + (y * self.width)/8,buf[(x + y * self.width) / 8]
                        
        elif(imwidth == self.height and imheight == self.width):
            # print ("Vertical")
            for y in range(imheight):
                for x in range(imwidth):
                    newx = y
                    newy = self.height - x - 1
                    if pixels[x, y] == 0:
                        buf[(newx + (newy // 8 )*self.width) ] &= ~(1 << (y % 8))
        return buf
    
    
    # def ShowImage(self,Image):
        # self.SetWindows()
        # GPIO.output(self._dc, GPIO.HIGH);
        # for i in range(0,self.width * self.height/8):
            # config.spi_writebyte([~Image[i]])
            
    def ShowImage(self, pBuf):
        for page in range(0,8):
            # set page address #
            self.command(0xB0 + page)
            # set low column address #
            self.command(0x02); 
            # set high column address #
            self.command(0x10); 
            # write data #
            # time.sleep(0.01)
            if(self.Device == Device_SPI):
                self.RPI.digital_write(self._dc,True)
            for i in range(0,self.width):#for(int i=0;i<self.width; i++)
                if(self.Device == Device_SPI):
                    self.RPI.spi_writebyte([~pBuf[i+self.width*page]]); 
                else :
                    self.RPI.i2c_writebyte(0x40, ~pBuf[i+self.width*page])
                    
                    

    

    def clear(self):
        """Clear contents of image buffer"""
        _buffer = [0xff]*(self.width * self.height//8)
        self.ShowImage(_buffer) 
            #print "%d",_buffer[i:i+4096]

config.py;

# /*****************************************************************************
# * | File        :   config.py
# * | Author      :   Waveshare team
# * | Function    :   Hardware underlying interface,for Raspberry pi
# * | Info        :
# *----------------
# * | This version:   V1.0
# * | Date        :   2020-06-17
# * | Info        :   
# ******************************************************************************/
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to  whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

import time
from smbus import SMBus
import spidev
import ctypes
from gpiozero import *

# Pin definition
RST_PIN         = 25
DC_PIN          = 24

#GPIO define
KEY_UP_PIN     = 6 
KEY_DOWN_PIN   = 19
KEY_LEFT_PIN   = 5
KEY_RIGHT_PIN  = 26
KEY_PRESS_PIN  = 13

KEY1_PIN       = 21
KEY2_PIN       = 20
KEY3_PIN       = 16

Device_SPI = 1
Device_I2C = 0

class RaspberryPi:
    def __init__(self,spi=spidev.SpiDev(0,0),spi_freq=40000000,rst = 27,dc = 25,bl = 18,bl_freq=1000,i2c=None):
        self.INPUT = False
        self.OUTPUT = True
        
        if(Device_SPI == 1):
            self.Device = Device_SPI
            self.spi = spi
        else :
            self.Device = Device_I2C
            self.address = 0x3c
            self.bus = SMBus(1)
        
        self.GPIO_RST_PIN = self.gpio_mode(RST_PIN,self.OUTPUT)
        self.GPIO_DC_PIN = self.gpio_mode(DC_PIN,self.OUTPUT)

        self.GPIO_KEY_UP_PIN     = self.gpio_mode(KEY_UP_PIN,self.INPUT,True,None)
        self.GPIO_KEY_DOWN_PIN   = self.gpio_mode(KEY_DOWN_PIN,self.INPUT,True,None)
        self.GPIO_KEY_LEFT_PIN   = self.gpio_mode(KEY_LEFT_PIN,self.INPUT,True,None)
        self.GPIO_KEY_RIGHT_PIN  = self.gpio_mode(KEY_RIGHT_PIN,self.INPUT,True,None)
        self.GPIO_KEY_PRESS_PIN  = self.gpio_mode(KEY_PRESS_PIN,self.INPUT,True,None)

        self.GPIO_KEY1_PIN       = self.gpio_mode(KEY1_PIN,self.INPUT,True,None)
        self.GPIO_KEY2_PIN       = self.gpio_mode(KEY2_PIN,self.INPUT,True,None)
        self.GPIO_KEY3_PIN       = self.gpio_mode(KEY3_PIN,self.INPUT,True,None)

        #self.Disable_All_PIN    


    def delay_ms(self,delaytime):
        time.sleep(delaytime / 1000.0)

    def gpio_mode(self,Pin,Mode,pull_up = None,active_state = True):
        if Mode:
            return DigitalOutputDevice(Pin,active_high = True,initial_value =False)
        else:
            return DigitalInputDevice(Pin,pull_up=pull_up,active_state=active_state)


    def gpio_pwm(self,Pin):
        return PWMOutputDevice(Pin,frequency = 10000)

    def set_pwm_Duty_cycle(self,Pin,value):
        Pin.value = value

    def digital_write(self, Pin, value):
        if value:
            Pin.on()
        else:
            Pin.off()

    def digital_read(self, Pin):
        return Pin.value

    def spi_writebyte(self,data):
        self.spi.writebytes([data[0]])

    def i2c_writebyte(self,reg, value):
        self.bus.write_byte_data(self.address, reg, value)
    
    def module_init(self): 
        self.digital_write(self.GPIO_RST_PIN,False)
        if(self.Device == Device_SPI):
            self.spi.max_speed_hz = 1000000
            self.spi.mode = 0b11  
        # CS_PIN.off()
        self.digital_write(self.GPIO_DC_PIN,False)
        return 0

    def module_exit(self):
        if(self.Device == Device_SPI):
            self.spi.close()
        else :
            self.bus.close()
        self.digital_write(self.GPIO_RST_PIN,False)
        self.digital_write(self.GPIO_DC_PIN,False)

### END OF FILE ###

New contributor

Laurin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật