With Python, how to prevent two threads from writing simultaneously to display

python thread class
python thread example
python thread pool
python multithreading for loop
python multithreading vs multiprocessing
python threading example stackoverflow
python threading event example
python stop thread

I'm working on a Raspberry Pi project where I have an OLED display that is continuously updated with information in a Python while True-loop. However, whenever I press a button (GPIO 5) I would like to display some other static information (e.g. system info) for as long as the button is depressed, or possibly for a predefined period of time. When the button is released, the "main loop" may take over again. I have tried to implement this using RPi.GPIO and a callback function for the display of system info, but the problem is of course that even during the callback function execution, the main loop continues writing to the OLED, creating garbage on the OLED if GPIO 5 is depressed: Both "threads" are writing simultaneously to the OLED...

I assume I need a way to pause the execution of the main loop during the callback function and I have experimented with Semaphore and aquire/release but with no luck. I have also considered the possibility of having two callback functions in combination with Semaphore, but since the info displayed in the main loop should be continuously updated (e.g. not interrupt-driven) I'm not so sure that is a solution for me.

At this point I am not even sure what to google next. Perhaps someone in here can enlighten me? Is this perhaps a totally wrong way of doing this kind of thing? (Limited experience with Python...)

Below is a simplified code example that simulates what I would like to do.

import time
import Adafruit_GPIO.SPI as SPI
import RPi.GPIO as GPIO
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

# Initialize
disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()
disp.clear()
disp.display()
image = Image.new('1', (disp.width, disp.height))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
GPIO.setmode(GPIO.BCM) 
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up

def clear_display():
    draw.rectangle((0,0,disp.width,disp.height), outline=0, fill=0)
    disp.image(image)
    disp.display()

# Callback function 
def display_system_info(channel):
    draw.text((0, 0), "System info displayed",  font=font, fill=255)
    draw.text((0, 9), "for five seconds.",  font=font, fill=255)
    disp.image(image)
    disp.display()
    time.sleep(5)
    clear_display()

GPIO.add_event_detect(5, GPIO.RISING, callback=display_system_info, bouncetime=200)

try:
    while True:
        for counter in range(7):
            draw.text((0,counter*9), "Printing line {0:d}".format(counter),  font=font, fill=255)
            disp.image(image)
            disp.display()
            time.sleep(1)
        clear_display()
except KeyboardInterrupt:  
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit  

Many thanks for any help.

/N

I am solving situations like this with global varialbe - buttonPressed. When you press button (GPIO 5), buttonPressed is set to True and main loop does nothing. Hope it is clear for you and will help you.

import time
import Adafruit_GPIO.SPI as SPI
import RPi.GPIO as GPIO
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

# Initialize
disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()
disp.clear()
disp.display()
image = Image.new('1', (disp.width, disp.height))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
GPIO.setmode(GPIO.BCM) 
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up

buttonPressed = False

def clear_display():
    draw.rectangle((0,0,disp.width,disp.height), outline=0, fill=0)
    disp.image(image)
    disp.display()

# Callback function 
def display_system_info(channel):
    global buttonPressed
    buttonPressed = True
    draw.text((0, 0), "System info displayed",  font=font, fill=255)
    draw.text((0, 9), "for five seconds.",  font=font, fill=255)
    disp.image(image)
    disp.display()
    time.sleep(5)
    clear_display()
    buttonPressed = False

GPIO.add_event_detect(5, GPIO.RISING, callback=display_system_info, bouncetime=200)

try:
    while True:
        if(not buttonPressed):
            for counter in range(7):
                draw.text((0,counter*9), "Printing line {0:d}".format(counter),  font=font, fill=255)
                disp.image(image)
                disp.display()
                time.sleep(1)
            clear_display()
except KeyboardInterrupt:  
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit  

Try and let me know.

Multithreading in Python, How do you run two functions at the same time in python? Threads are executed in their own system-level thread (e.g., a POSIX thread or Windows threads) that is fully managed by the host operating system. Once started, threads run independently until the target function returns. Code #2 : Querying a thread instance to see if it’s still running.

Depending on your application I'd avoid doing any real work inside of the callback. Instead I'd only set a flag which the main thread/loop can handle or add the events to a queue which is processed by the main thread/loop.

How many Python threads can I run?, are used in cases where the execution of a task involves some waiting. I'm working on a Raspberry Pi project where I have an OLED display that is continuously updated with information in a Python while True-loop. However, whenever I press a button (GPIO 5) I would lik

I realized interrupts was not a good path to walk for this particular application. Instead, I re-wrote my code so that it's only possible to push the button when the main loop is idle and waiting...which is most of the time of course. Thanks for input!

Python Programming/Threading, To guard against simultaneous access to an object, use a Lock object. In this example, the worker() function increments a Counter instance, which manages a Lock to prevent two threads from changing its internal state at the same time. Thread 2 has no idea that Thread 1 ran and updated database.value while it was sleeping. It stores its version of local_copy into database.value, also setting it to one: The two threads have interleaving access to a single shared object, overwriting each other’s results.

threading – Manage concurrent threads, You can simply create your own locking mechanism to ensure that only one thread is ever writing to a file. import threading lock = threading. Learn what is multitasking in python. It also explains multithreading how to create threads without creating a class, by extending Thread class and without extending it.

Many threads to write log file at same time in Python, Using Locks to Prevent Data Races in Threads in Python This is dangerous if we access the same objects from multiple threads simultaneously. For example, say we want to write a program that process many things in the lock; this makes it easier to see which code is executing while the lock is held. The function object passed in the above code always returns the value of the local variable stop_threads. This value is checked in the function run(), and as soon as stop_threads is reset, the run() function ends and the thread can be killed. Using traces to kill threads : This methods works by installing traces in each thread. Each trace

Using Locks to Prevent Data Races in Threads in Python, Due to this, the Python multithreading module doesn't quite behave the way you that one can still write code in Python that runs concurrently or in parallel and All subsequent code examples will only show import statements that are new Related: Become More Advanced: Avoid the 10 Most Common Mistakes That  The scripts in these threading examples have been tested with Python 3.6.4. With some changes, they should also run with Python 2—urllib is what has changed the most between these two versions of Python. Getting Started with Python Multithreading. Let us start by creating a Python module, named download.py. This file will contain all the

Comments
  • I played around with your suggestion but the strange thing is that if I add print commands to verify whether I am inside the callback function or not, I can see that even if I am inside the function, the main loop keeps printing tings to my OLED despite the fact that I added the "if not buttonPressed" check. Probably because the add_event_detect looks for edges.
  • That is a plausible solution. However, I assume I then have to constantly check whether this flag is 1/0...and then I could just as well constantly check if the button is pressed. Or did I perhaps misunderstand your suggestion?
  • Constant checking if the button is pressed (polling) might be even better. I'm not sure how the event is implemented, but if it is an interrupt you could cause all kinds of strange (timing) issues by constantly pressing the button (whatever your application is doing, it will be interrupted). Some buttons/switches also suffer from bouncing contacts, so even if you think you pressed it only once it might be detected as several very quick presses.