0

We have 7 raspberry pi's 3, each connected to the internet and an display. On each screen / raspberry pi is geeqie (image viewer_ running with an image slideshow. The goal is to have all raspberry pi's go to the next image with an mouse (scrollwheel) or any other kind of device, in syc, at the same time. An python app is running in the background to download the images (independent of this problem). All mentioned code is python 2.7.

We have 1 master raspberry which detects the mouse scroll via evdev (mousegrabber python script below) and sends GPIO.output over GPIO pins 22 and 23. GPIO pin 22 is connected to 1kΩ and after the resistor splitted to 7 and connected to each raspberry GPIO 17. For GPIO 23 the same, first an 1kΩ resistor, then splitted and connected to all 7 GPIO 18 pins. The ground is also connected of each one raspberry pi with one other.

The python detector script is below. This is all working. The input is detected via GPIO.add_event_detect() and an simulated mouse scroll event is send via uinput. Geeqie is running fullscreen and those python apps in the background, the scrolls up and down are detected by geeqie to go to the next or previous image.

The point is that all images need to switch to the next one, exactly in sync. This is not the case, there appears to be some sort of delay of about +- 50ms to 500ms of each detection. The switching of all geeqie iamges on the raspbarry are out of sync. What would be the best way to archive that all images are switching to the next one synchronous on command with some sort of input?

The is no particular reason this has to be via GPIO, the image switching just needs to be in sync. This means, when the mouse wheel is scrolled, all geeqie image slideshows needs to go to the next image. The pictures might make the image complete. The soldering is not that good, bud its working correctly. The goal is just to go to the next image at the same time on all raspberri pi's.

Any ideas? Thanks!

Extra information: We also tried this, bud it was way more lag then this above. The scroll detections were very out of sync. We also tried it with a construction of 7 mouses their rotary encoder soldered together, so the mouse itself was detected by each raspberry, only the input came from 1 mouse. This was in sync and was working, bud it broke.

Script which detects the GPIO input:

import RPi.GPIO as GPIO
import threading
import os
import random
import evdev
from evdev import UInput, AbsInfo, InputDevice, categorize, ecodes
from pprint import pprint
import sys
import time
import uinput


class InputFromMasterDetector:
    def __init__(self):
        self.config = None
        self.Enc_A = 17 # UP
        self.Enc_B = 18 # DOWN

        self.simulatedInputDevice = uinput.Device([
            uinput.REL_WHEEL,
            uinput.REL_X,
            uinput.REL_Y,
            uinput.BTN_LEFT,
            uinput.BTN_RIGHT,
        ])

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        GPIO.cleanup()
        print("InputFromMasterDetector - _exit_ cleanup\n")

    def init(self):
        GPIO.setwarnings(True)
        GPIO.setmode(GPIO.BCM) # Use BCM mode

        GPIO.setup(self.Enc_A, GPIO.IN) #, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(self.Enc_B, GPIO.IN) #, pull_up_down=GPIO.PUD_DOWN)

        #GPIO.add_event_detect(self.Enc_A, GPIO.RISING, callback=self.rotary_interrupt)                 # NO bouncetime 
        #GPIO.add_event_detect(self.Enc_B, GPIO.RISING, callback=self.rotary_interrupt)                 # NO bouncetime 

        GPIO.add_event_detect(self.Enc_A, GPIO.RISING, callback=self.rotary_interrupt_A) #, bouncetime=200)                 # NO bouncetime 
        GPIO.add_event_detect(self.Enc_B, GPIO.RISING, callback=self.rotary_interrupt_B)

        try:
            print("InputFromMasterDetector - starting input detection\n")
            #GPIO.wait_for_edge(Enc_A, GPIO.FALLING)
            while True :
                #time.sleep(1)
                #print("looipiness")
                pass

        except KeyboardInterrupt:  
            print("InputFromMasterDetector - KeyboardInterrupt \n")

        except: 
            print "InputFromMasterDetector - Other error or exception occurred! \n"

        finally:  
            GPIO.cleanup() # this ensures a clean exit
            print("InputFromMasterDetector - Cleanup\n")

    def rotary_interrupt_A(self, signalA):
        self.uinput_scroll(1, 1)
        #print("InputFromMasterDetector - rotary_interrupt_A detected - signalA: " + str(signalA) + " - ***UP*** - Switch_A:" + str(Switch_A) + " - " + str(random.randint(1,101)) + "\n")
        print("InputFromMasterDetector - ***UP***\n")

    def rotary_interrupt_B(self, signalB):
        self.uinput_scroll(-1, 1)
        #print("InputFromMasterDetector - rotary_interrupt_B detected\n")
        print("InputFromMasterDetector - ***DOWN***\n")

    def uinput_scroll(self, direction, amount=1):
        self.simulatedInputDevice.emit(uinput.REL_WHEEL, direction * amount) # direction 1 = up, -1 = down

    def start(self):
        thread = threading.Thread(target = self.init)
        thread.setDaemon(True)
        thread.start()
        print('InputFromMasterDetector - started in new thread\n')

Script which sends the GPIO signal:

import RPi.GPIO as GPIO
import threading
import os
import random
import evdev
from evdev import UInput, AbsInfo, InputDevice, categorize, ecodes
from pprint import pprint
import sys
import time

class MousegrabberStandaloneSender:
    def __init__(self):
        self.config = None
        # GPIO Ports
        #17&18 or 22&23
        self.Enc_A = 22 # UP
        self.Enc_B = 23 # DOWN

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        GPIO.cleanup()
        print("MousegrabberStandaloneSender - _exit_ cleanup\n")

    def init(self):
        GPIO.setwarnings(True)
        GPIO.setmode(GPIO.BCM) # Use BCM mode
        GPIO.setup(self.Enc_A, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(self.Enc_B, GPIO.OUT, initial=GPIO.LOW)

    def send_signal_A(self): #A == UP
        GPIO.output(self.Enc_A, GPIO.HIGH)
        time.sleep(0.1)
        GPIO.output(self.Enc_A, GPIO.LOW)
        print('MousegrabberStandaloneSender - sennd_signal_A - ***UP***' + str(random.randint(1,101)) + "\n")

    def send_signal_B(self): #B == DOWN
        GPIO.output(self.Enc_B, GPIO.HIGH)
        time.sleep(0.1)
        GPIO.output(self.Enc_B, GPIO.LOW)
        print('MousegrabberStandaloneSender - sennd_signal_B - ***DOWN***' + str(random.randint(1,101)) + "\n")


    def threaded_scrollwheeldetection(self):
        try:
            self.start_scrollwheeldetection()

        except KeyboardInterrupt:  
            print("MousegrabberStandaloneSender - KeyboardInterrupt \n")

        except:  
            print "MousegrabberStandaloneSender - Other error or exception occurred! \n"

        finally:  
            GPIO.cleanup() # this ensures a clean exit
            print("MousegrabberStandaloneSender - Cleanup\n")

    def start_scrollwheeldetection(self):
        self.init()

        devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
        foundMouse = None
        for device in devices:
            if "mouse" in device.name.lower():
                foundMouse = device
                print("MousegrabberStandaloneSender - Mouse found, using: ", device.path, device.name, device.phys)
                break

        if foundMouse is None:
            print("MousegrabberStandaloneSender - No mouse found, exiting...\n")
            GPIO.cleanup()
            sys.exit()


        dev = InputDevice(device.path)    
        #pprint(dev.capabilities(verbose=True, absinfo=True))

        simulatedUserInput = UInput()

        dev.grab()

        scrollUps = 0
        scrollDowns = 0
        startingTime = time.time()
        amountOfScrollsToReactOn = 10

        for event in dev.read_loop():
            #pprint(str(event.type) + " " + str(event.code) + " " + str(event.value))
            #print("evdev.ecodes.EV_REL: " + str(evdev.ecodes.EV_REL) + " - evdev.ecodes.REL_WHEEL: " + str(evdev.ecodes.REL_WHEEL))
            #catch the scroll wheen
            if event.type == evdev.ecodes.EV_REL and event.code == evdev.ecodes.REL_WHEEL:
            #if event.type == 0x02 and event.code == 0x08:
                currentTime = time.time()
                #print((currentTime - startingTime))
                if event.value > 0:
                    scrollUps+=1
                    scrollDowns = 0
                elif event.value < 0:
                    scrollDowns+=1
                    scrollUps = 0
                if (currentTime - startingTime) > 10:
                    startingTime = time.time()
                    scrollUps = 0
                    scrollDowns = 0
                    #print("(currentTime - startingTime) > 10")
                elif (currentTime - startingTime) < 10 and (scrollUps > amountOfScrollsToReactOn or scrollDowns > amountOfScrollsToReactOn):
                    if scrollUps > amountOfScrollsToReactOn:
                        self.send_signal_A() #up
                    elif scrollDowns > amountOfScrollsToReactOn:
                        self.send_signal_B() #down
                    startingTime = time.time()
                    scrollDowns = 0
                    scrollUps = 0
            #print(categorize(event))
            else:
                #pprint("sending: " + str(event.type) + " " + str(event.code) + " " + str(event.value))
                #dev.write(e.EV_KEY, e.KEY_A, 0)
                simulatedUserInput.write(event.type, event.code, event.value)
                simulatedUserInput.syn()

    def start(self):
        thread = threading.Thread(target = self.threaded_scrollwheeldetection)
        thread.setDaemon(True)
        thread.start()
        #thread.join() #blocks untill the first thread finished
        print('MousegrabberStandaloneSender started\n')

mousegrabberstandalonesender = MousegrabberStandaloneSender()
mousegrabberstandalonesender.threaded_scrollwheeldetection()

Images about the situation: part1 part2 GPIO headers

  • Is there any particular reason why you need this to be done through the gpio? I would set up a small python web server on each raspberry and send my signals through the network from the master raspberry. – GramThanos Jan 27 at 19:13
  • 1
    the pictured do not provide any useful information, except maybe show some really bad soldering (is that why the multi-mouse device broke?) ..... it is up to you if you want to remove the pictures ... they only clutter up the post – jsotola Jan 27 at 19:34
  • you may find a faster solution if you research how to synchronize multiple Linux computers because the answer may already be out there ..... this is not really an RPi related problem ........ maybe you need to synchronize the clocks (RTC) of the devices and run some type of a strict scheduling program – jsotola Jan 27 at 19:39
  • when you say that the screens are out of sync, does that mean that none of them are in sync? – jsotola Jan 27 at 19:46
  • The is no particular reason this has to be via GPIO, the image switching just needs to be in sync. This means, when the mouse wheel is scrolled, all geeqie image slideshows needs to go to the next image. The pictures might make the image complete. The soldering is not that good, bud its working correctly. The goal is just to go to the next image at the same time on all raspberri pi's. – BramscoChill Jan 28 at 8:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.