簡體   English   中英

使用 Python,我可以將 Raspberry Pi GPIO 輸入作為 HID 按鍵或命令發送嗎?

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

我正在修改現有代碼,將 Raspberry Pi 用作藍牙 HID 鍵盤和鼠標。 現有代碼可以很好地通過藍牙將按鍵發送到我的 Macbook。

我正在苦苦掙扎的是將來自 Raspberry Pi 的 GPIO 輸入轉換為通過藍牙發送的命令或按鍵。 我可以讓按鍵在本地工作,但我不知道如何更改代碼以適應 GPIO 輸入。

理想情況下,我想使用一個按鈕來按“Command+向右箭頭”或使用 AppleScript 向 Macbook 上的 Spotify 發送下一首曲目命令。 這是我唯一需要編碼的按鍵。

我想我需要導入 GPIO 並將其設置為 BCM,然后讓它將輸入從引腳 4 轉換為發送的 AppleScript 命令。 我只是想不出在哪里做。

現有代碼如下:

#!/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()

當我使用 Retrogame 或其他代碼將 Raspberry Pi 設置為從 GPIO 讀取按鍵時,它會讀取按鍵,但不會將它們轉換為鍵盤 python 程序中的按鍵。 似乎需要在代碼中完成。

編輯以添加更多信息:

該代碼還包括這個名為“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.")

編輯#2:

這是否接近輪詢 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))

是這樣的:

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)

那么你將不得不使用

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

創建和運行線程。 確保將引腳設置為“輸入”模式。 GPIO 引腳可以是其中之一,但一次只能是一個。

這是典型的 Rpi 程序——許多小線程全職運行以執行微小任務。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM