简体   繁体   English

使用 Python,我可以将 Raspberry Pi GPIO 输入作为 HID 按键或命令发送吗?

[英]Using Python, can I send a Raspberry Pi GPIO input as a HID keypress or command?

I am working on modifying existing code that works to use a Raspberry Pi as a bluetooth HID keyboard and mouse.我正在修改现有代码,将 Raspberry Pi 用作蓝牙 HID 键盘和鼠标。 The existing code works well to send keypresses to my Macbook via bluetooth.现有代码可以很好地通过蓝牙将按键发送到我的 Macbook。

What I am struggling with is converting GPIO inputs from the Raspberry Pi to a command or keypress that is sent via Bluetooth.我正在苦苦挣扎的是将来自 Raspberry Pi 的 GPIO 输入转换为通过蓝牙发送的命令或按键。 I can make the keypresses work locally, but I can't figure out how to alter the code to accommodate GPIO input.我可以让按键在本地工作,但我不知道如何更改代码以适应 GPIO 输入。

Ideally, I'd like to use a button to either press "Command+Right Arrow" or use AppleScript to send a Next Track command to Spotify on the Macbook.理想情况下,我想使用一个按钮来按“Command+向右箭头”或使用 AppleScript 向 Macbook 上的 Spotify 发送下一首曲目命令。 It's the only keypress I'd need to code.这是我唯一需要编码的按键。

I imagine I need to import GPIO and set it to BCM and then have it convert inputs from pin 4 to an AppleScript command that is sent.我想我需要导入 GPIO 并将其设置为 BCM,然后让它将输入从引脚 4 转换为发送的 AppleScript 命令。 I just can't figure out where to do it.我只是想不出在哪里做。

The existing code is as follows:现有代码如下:

#!/usr/bin/python3
#
# Thanhle Bluetooth keyboard emulation service
# keyboard copy client.
# Reads local key events and forwards them to the btk_server DBUS service
#
import os  # used to all external commands
import sys  # used to exit the script
import dbus
import dbus.service
import dbus.mainloop.glib
import time
import evdev  # used to get input from the keyboard
from evdev import *
import keymap  # used to map evdev input to hid keodes


# Define a client to listen to local key events
class Keyboard():

    def __init__(self):
        # the structure for a bt keyboard input report (size is 10 bytes)

        self.state = [
            0xA1,  # this is an input report
            0x01,  # Usage report = Keyboard
            # Bit array for Modifier keys
            [0,  # Right GUI - Windows Key
             0,  # Right ALT
             0,  # Right Shift
             0,  # Right Control
             0,  # Left GUI
             0,  # Left ALT
             0,  # Left Shift
             0],  # Left Control
            0x00,  # Vendor reserved
            0x00,  # rest is space for 6 keys
            0x00,
            0x00,
            0x00,
            0x00,
            0x00]

        print("setting up DBus Client")

        self.bus = dbus.SystemBus()
        self.btkservice = self.bus.get_object(
            'org.thanhle.btkbservice', '/org/thanhle/btkbservice')
        self.iface = dbus.Interface(self.btkservice, 'org.thanhle.btkbservice')
        print("waiting for keyboard")
        # keep trying to key a keyboard
        have_dev = False
        while have_dev == False:
            try:
                # try and get a keyboard - should always be event0 as
                # we're only plugging one thing in
                self.dev = InputDevice("/dev/input/event0")
                have_dev = True
            except OSError:
                print("Keyboard not found, waiting 3 seconds and retrying")
                time.sleep(3)
            print("found a keyboard")

    def change_state(self, event):
        evdev_code = ecodes.KEY[event.code]
        modkey_element = keymap.modkey(evdev_code)

        if modkey_element > 0:
            if self.state[2][modkey_element] == 0:
                self.state[2][modkey_element] = 1
            else:
                self.state[2][modkey_element] = 0
        else:
            # Get the keycode of the key
            hex_key = keymap.convert(ecodes.KEY[event.code])
            # Loop through elements 4 to 9 of the inport report structure
            for i in range(4, 10):
                if self.state[i] == hex_key and event.value == 0:
                    # Code 0 so we need to depress it
                    self.state[i] = 0x00
                elif self.state[i] == 0x00 and event.value == 1:
                    # if the current space if empty and the key is being pressed
                    self.state[i] = hex_key
                    break

    # poll for keyboard events
    def event_loop(self):
        for event in self.dev.read_loop():
            # only bother if we hit a key and its an up or down event
            if event.type == ecodes.EV_KEY and event.value < 2:
                self.change_state(event)
                self.send_input()

    # forward keyboard events to the dbus service
    def send_input(self):
        bin_str = ""
        element = self.state[2]
        for bit in element:
            bin_str += str(bit)
        a = self.state
        print(*a)
        self.iface.send_keys(int(bin_str, 2), self.state[4:10])


if __name__ == "__main__":

    print("Setting up keyboard")

    kb = Keyboard()

    print("starting event loop")
    kb.event_loop()

When I set the Raspberry Pi to read keypresses from GPIO using Retrogame or other code, it will read the keys, but it won't convert them to keypresses in the keyboard python program.当我使用 Retrogame 或其他代码将 Raspberry Pi 设置为从 GPIO 读取按键时,它会读取按键,但不会将它们转换为键盘 python 程序中的按键。 It seems like it needs to be done in the code.似乎需要在代码中完成。

Edited to add more information:编辑以添加更多信息:

The code also includes this Python file that is called "send_string.py".该代码还包括这个名为“send_string.py”的 Python 文件。

#!/usr/bin/python3
import os  # used to all external commands
import sys  # used to exit the script
import dbus
import dbus.service
import dbus.mainloop.glib
import time
# import thread
import keymap


class BtkStringClient():
    # constants
    KEY_DOWN_TIME = 0.01
    KEY_DELAY = 0.01

    def __init__(self):
        # the structure for a bt keyboard input report (size is 10 bytes)
        self.state = [
            0xA1,  # this is an input report
            0x01,  # Usage report = Keyboard
            # Bit array for Modifier keys
            [0,  # Right GUI - Windows Key
                 0,  # Right ALT
                 0,  # Right Shift
                 0,  # Right Control
                 0,  # Left GUI
                 0,  # Left ALT
                 0,  # Left Shift
                 0],  # Left Control
            0x00,  # Vendor reserved
            0x00,  # rest is space for 6 keys
            0x00,
            0x00,
            0x00,
            0x00,
            0x00]
        self.scancodes = {
            "-": "KEY_MINUS",
            "=": "KEY_EQUAL",
            ";": "KEY_SEMICOLON",
            "'": "KEY_APOSTROPHE",
            "`": "KEY_GRAVE",
            "\\": "KEY_BACKSLASH",
            ",": "KEY_COMMA",
            ".": "KEY_DOT",
            "/": "KEY_SLASH",
            "_": "key_minus",
            "+": "key_equal",
            ":": "key_semicolon",
            "\"": "key_apostrophe",
            "~": "key_grave",
            "|": "key_backslash",
            "<": "key_comma",
            ">": "key_dot",
            "?": "key_slash",
            " ": "KEY_SPACE",
        }

        # connect with the Bluetooth keyboard server
        print("setting up DBus Client")
        self.bus = dbus.SystemBus()
        self.btkservice = self.bus.get_object(
            'org.thanhle.btkbservice', '/org/thanhle/btkbservice')
        self.iface = dbus.Interface(self.btkservice, 'org.thanhle.btkbservice')

    def send_key_state(self):
        """sends a single frame of the current key state to the emulator server"""
        bin_str = ""
        element = self.state[2]
        for bit in element:
            bin_str += str(bit)
        self.iface.send_keys(int(bin_str, 2), self.state[4:10])

    def send_key_down(self, scancode, modifiers):
        """sends a key down event to the server"""
        self.state[2] = modifiers
        self.state[4] = scancode
        self.send_key_state()

    def send_key_up(self):
        """sends a key up event to the server"""
        self.state[4] = 0
        self.send_key_state()

    def send_string(self, string_to_send):
        for c in string_to_send:
            cu = c.upper()
            modifiers = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
            if cu in self.scancodes:
                scantablekey = self.scancodes[cu]
                if scantablekey.islower():
                    modifiers = [ 0, 0, 0, 0, 0, 0, 1, 0 ]
                    scantablekey = scantablekey.upper()
            else:
                if c.isupper():
                    modifiers = [ 0, 0, 0, 0, 0, 0, 1, 0 ]
                scantablekey = "KEY_" + cu

            scancode = keymap.keytable[scantablekey]
            self.send_key_down(scancode, modifiers)
            time.sleep(BtkStringClient.KEY_DOWN_TIME)
            self.send_key_up()
            time.sleep(BtkStringClient.KEY_DELAY)


if __name__ == "__main__":
    if(len(sys.argv) < 2):
        print("Usage: send_string <string to send>")
        exit()
    dc = BtkStringClient()
    string_to_send = sys.argv[1]
    print("Sending " + string_to_send)
    dc.send_string(string_to_send)
    print("Done.")

Edit #2:编辑#2:

Is this close to what could work with a way to poll for GPIO events?这是否接近轮询 GPIO 事件的方法?

    # poll for keyboard events
    def event_loop(self):
        global cmd
        for event in self.dev.read_loop():
            # only bother if we hit a key and its an up or down event
            if event.type == ecodes.EV_KEY and event.value < 2:
                self.change_state(event)
                self.send_input()
        while True:
            if event.type == GPIO.input(4):
                self.change_state(event)
                cmd = """osascript -e 'tell app "Spotify" next track'"""            
                self.nextrack(self)
                time.sleep(.25)
                
    # forward keyboard events to the dbus service

    def nextrack(self):
        global cmd
        bin_str = ""
        element = self.state[2]
        for bit in element:
            bin_str += str(bit)
        a = self.state
        print("Play next track")
        cmd = """osascript -e 'tell app "Spotify" next track'"""
        self.iface.send_keys(os.system(cmd))

Something like this:是这样的:

def gpioThread():
    state = GPIO.input(4)
    while True:
        now = GPIO.input(4)
        if now != state:
            # Take action here based on whether "now" is 0 or 1.
            state = now
        time.sleep(0.25)

Then you'll have to use那么你将不得不使用

t = Threading.thread(target=gpioThread)
t.run()

to create and run the thread.创建和运行线程。 Make sure you set the pin to "input" mode.确保将引脚设置为“输入”模式。 GPIO pins can be one or the other, but only one at a time. GPIO 引脚可以是其中之一,但一次只能是一个。

This is typical of an Rpi program -- lots of little threads running full time doing tiny tasks.这是典型的 Rpi 程序——许多小线程全职运行以执行微小任务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM