Raspberry pi 模拟键盘成功,但无法发送 F13 及更高版本

问题描述 投票:0回答:1

我使用树莓派 4 来接收 UDP 数据包,并使用该信息发送按键,就好像它是 USB 键盘一样。我的代码如下。

代码会跟踪 UDP 数据包告知其按下的按键,并在告知时释放它们。如果“持有”超过一定时间,它也会释放它们。它基本上是一个远程键盘/击键模拟器。

它适用于扫描下面的代码

0x65
。扫描上面不起作用的代码,特别是 F13 到 F24。当我说“不起作用”时,我的意思是它的结构似乎与其他扫描代码相同,因为它离开树莓派(除了该键码的数字稍高),但它所连接的计算机没有响应如果按键被按下(对于较低的代码确实如此)。我有一个鼠标可以成功发送F13到F24(虽然它使用特殊软件......)所以我不认为这是接收器的缺点。它停止工作的整数等效值约为 100,这可能表明某种有意的截止点。在此之前我对使用 HID 一无所知,我怀疑这是
open('/dev/hidg0', 'rb+')
导入中的问题,或者希望我只是构造了错误的字节。请帮我发送 F13,或者至少告诉我为什么不能发送。

import time
import atexit
import binascii
import signal
#!/usr/bin/env python3
keys = { #keys I can refer to by name. Some are commented out for reasons
'error'.lower():0x01,
'A'.lower():0x04,
'B'.lower():0x05,
'C'.lower():0x06,
'd':0x07,
'E'.lower():0x08,
'F'.lower():0x09,
'G'.lower():0x0A,
'H'.lower():0x0B,
'I'.lower():0x0C,
'J'.lower():0x0D,
'K'.lower():0x0E,
'L'.lower():0x0F,
'M'.lower():0x10,
'N'.lower():0x11,
'O'.lower():0x12,
'P'.lower():0x13,
'Q'.lower():0x14,
'R'.lower():0x15,
'S'.lower():0x16,
'T'.lower():0x17,
'U'.lower():0x18,
'V'.lower():0x19,
'W'.lower():0x1A,
'X'.lower():0x1B,
'Y'.lower():0x1C,
'Z'.lower():0x1D,
'1':0x1E,
'2':0x1F,
'3':0x20,
'4':0x21,
'5':0x22,
'6':0x23,
'7':0x24,
'8':0x25,
'9':0x26,
'0':0x27,
'Enter'.lower():0x28,
'Escape'.lower():0x29,
'Backspace'.lower():0x2A, #possible ambiguity with delete
'Tab'.lower():0x2B,
'Space'.lower():0x2C,
'-':0x2D,
'+':0x2E,
'[':0x2F,
']':0x30,
'\\':0x31,
#'Non-US':0x32, #???
';':0x33,
'\'':0x34,
'`':0x35,
',':0x36,
'.':0x37,
'/':0x38,
'CapsLock'.lower():0x39,
'F1'.lower():0x3A,
'F2'.lower():0x3B,
'F3'.lower():0x3C,
'F4'.lower():0x3D,
'F5'.lower():0x3E,
'F6'.lower():0x3F,
'F7'.lower():0x40,
'F8'.lower():0x41,
'F9'.lower():0x42,
'F10'.lower():0x43,
'F11'.lower():0x44,
'F12'.lower():0x45,
'PrintScreen'.lower():0x46,
'ScrollLock'.lower():0x47,
'Pause'.lower():0x48,
'Insert'.lower():0x49,
'Home'.lower():0x4A,
'PgUp'.lower():0x4B,
'Delete'.lower():0x4C,
'End'.lower():0x4D,
'PgDn'.lower():0x4E,
'Right'.lower():0x4F,
'Left'.lower():0x50,
'Down'.lower():0x51,
'Up'.lower():0x52,
'NumLock'.lower():0x53,
'NumpadDiv'.lower():0x54,
'NumpadMult'.lower():0x55,
'NumpadSub'.lower():0x56,
'NumpadAdd'.lower():0x57,
'NumpadEnter'.lower():0x58,
'Numpad1'.lower():0x59, # and End
'Numpad2'.lower():0x5A, # and Down Arrow
'Numpad3'.lower():0x5B, # and PageDn
'Numpad4'.lower():0x5C, # and Left Arrow
'Numpad5'.lower():0x5D, #
'Numpad6'.lower():0x5E, # and Right Arrow
'Numpad7'.lower():0x5F, # and Home
'Numpad8'.lower():0x60, # and Up Arrow
'Numpad9'.lower():0x61, # and PageUp
'Numpad0'.lower():0x62, # and Insert
'NumpadDot'.lower():0x63, # and Del
#'Non-US Slash Bar':0x64, #???
#'Application':0x65, #??? some programs call this "menu" <---- this is the last one that works sequentially 
#'Power':0x66, #???
#'Keypad Equals':0x67,
'F13'.lower():0x68, #<---- I first noticed that these don't work at all :(
'F14'.lower():0x69,
'F15'.lower():0x6A,
'F16'.lower():0x6B,
'F17'.lower():0x6C,
'F18'.lower():0x6D,
'F19'.lower():0x6E,
'F20'.lower():0x6F,
'F21'.lower():0x70,
'F22'.lower():0x71,
'F23'.lower():0x72,
'F24'.lower():0x73,
#'Keypad Comma':0x85,
#'International1':0x87,
#'International2':0x88,
#'International3':0x89,
#'International4':0x8A,
#'International5':0x8B,
#'International6':0x8C,
#'LANG1':0x90,
#'LANG2':0x91,
#'LANG3':0x92,
#'LANG4':0x93,
#'LANG5':0x94,
'LCtrl'.lower():0xE0,
'LShift'.lower():0xE1,
'LAlt'.lower():0xE2,
'LWin'.lower():0xE3,
'RCtrl'.lower():0xE4,
'Rshift'.lower():0xE5,
'RAlt'.lower():0xE6,
'RWin'.lower():0xE7,
#'System Power Down':0x81,
#'System Sleep':0x82,
#'System Wake Up':0x83,
#'Scan Next Track':0x00B5,
#'Scan Previous Track':0x00B6,
#'Stop':0x00B7,
#'Play/Pause':0x00CD,
#'Mute':0x00E2,
#'Volume Increment':0x00E9,
#'Volume Decrement':0x00EA,
#'AL Consumer Control Configuration':0x0183,
#'AL Email Reader':0x018A,
#'AL Calculator':0x0192,
#'AL Local Machine Browser':0x0194,
'Browser_Search':0x0221,
'Browser_Home':0x0223,
'Browser_Back':0x0224,
'Browser_Forward':0x0225,
'Browser_Stop':0x0226,
'Browser_Refresh':0x0227,
'Browser_Previous':0x022A,
    }

    
def currenttime(): #for time stamping
    return round(time.time() * 1000)
    
def write_report(report): 
    with open('/dev/hidg0', 'rb+') as fd:
        fd.write(report) #how they're sent.
        
def debug(stringin): 
    if True: #to turn off all print spam
        print(stringin)

import socket

def exit_handler():
    write_report(bytearray([0,0,0,0,0,0,0,0])) #futile attempt to prevent keys from getting stuck on

UDP_IP = "0.0.0.0"
UDP_PORT = 5005

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
sock.setblocking(False)

write_report(bytearray([0,0,0,0,0,0,0,0]))

keydata = {} #which keys are held and their time stamps
shifting = False
controlling = False
alting = False
wining = False #windows key

atexit.register(exit_handler) #none of these really solved the problem, still won't trigger on x button clicked.
signal.signal(signal.SIGINT, exit_handler)
signal.signal(signal.SIGTERM, exit_handler)
while True:
    changemade = False
    key = "no key found"
    status = -1
    try:
        data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
    except BlockingIOError:
        for eachkey in keydata: #expiring
            if keydata[eachkey] < currenttime():
                keydata.pop(eachkey)
                changemade=True
                debug("change made by way of duration expiration"+" keydata "+str(keydata))
                break #remove only one per round because dictionaries can't handle the changes
    else:
        debug("received one...")
        entered = data.decode('UTF-8')
        if entered == 'NONE':
            write_report(bytearray([0,0,0,0,0,0,0,0]))
        if entered == 'terminate':
            break
        values = entered.split(" ", 2)
        
        key = values[0] #the name of the key according to keys[]
        status = int(values[1]) #0 or 1 for up or down
        duration = int(values[2]) #duration before key is automatically set to up
        if key not in keys:
            debug(key + ' not in keys')
            continue
        if key == 'lctrl' or key == 'rctrl':
            controlling = status == '1'
            debug('controlling =' + str(controlling))
        elif key == 'lshift' or key == 'rshift':
            shifting = status == '1'
            debug('shifting =' + str(shifting))
        elif key == 'lalt' or key == 'ralt':
            alting = status == '1'
            debug('alting =' + str(alting))
        elif key == 'lwin' or key == 'rwin':
            wining = status == '1'
            debug('wining =' + str(wining))
        else:
            if status == 0: #releasing
                if key in keydata.keys():
                    keydata.pop(key)
                    changemade=True
                    debug("change made by way of pop release"+" keydata "+str(keydata))
            if status == 1: #pressing
                if key in keydata:
                    key=key
                else:
                    keydata[key] = duration+currenttime()
                    changemade=True
                    debug("change made by way of push with new duration " + key + " " + str(duration)+" keydata "+str(keydata))
    if changemade:
        boolray = [controlling,shifting,alting,wining]
        binarios = sum(map(lambda x: x[1] << x[0], enumerate(boolray)))
        bytezors = bytearray([])
        bytezors.append(binarios) #modifier keys, shift, ctrl alt etc
        bytezors.append(0) #I think this space is for the right handed version of above
        iterator = 0
        for eachkey in keydata:
            bytezors.append(keys[eachkey]) #all keys that are held down
            iterator+=1
        for x in range(6-iterator):
            bytezors.append(0) #place holders if less than 6 keys held down
        write_report(bytezors) #send it off
        debug('result was ' + str(binascii.hexlify(bytezors)) + " key:" + key)
write_report(bytearray([0,0,0,0,0,0,0,0]))

更新:

搜索其他人的尝试,我发现了这个https://forum.espruino.com/conversations/324014/

链接到此

https://www.espruino.com/modules/ble_hid_keyboard.js

这是针对 Arduino 的,但它确实包含这个有趣的评论:

听起来我使用的可能不是“最大”模式,但我不知道如何调整设置。不幸的是,屏幕截图顶部的链接出现了 404s。

再次更新:

我最初使用 “key mime pie” 来安装代码在显示

open('/dev/hidg0', 'rb+')
时与之交互的界面(?)。我找到了他们用来启用它的 bash 脚本。 在此脚本中有一行:

echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > "${FUNCTIONS_DIR}/report_desc"

这就是@aja 所描述的。我将 

x65

的相应值更改为

x73
,如 arduino 示例所示。我预计这会增加最大可能的密钥识别,但每次我尝试发送一些东西时,我只是得到一个
BlockingIOError: [Errorno 11] write could not complete without blocking
我现在的问题是需要什么十六进制组合才能使其工作。遗憾的是我没有带有 F13 及以上功能的物理键盘来监视这一点。

python-3.x raspberry-pi4 hid
1个回答
0
投票

https://randomnerdtutorials.com/raspberry-pi-zero-usb-keyboard-hid/

此外,我使用

/boot/config.txt

编辑了

sudo nano /boot/config.txt
并添加了
dtoverlay=dwc2
我没有在教程中使用供应商和硬件 ID,而是从不同的键盘复制了一些(当然得到了制造商的完全许可)

我没有使用教程的十六进制代码,而是替换了自己的十六进制代码,将键集扩展为 f13 及以上(以

echo -ne

 开头的 cmd)
我没有使用教程中的 python 代码,而是使用了我自己的上面的代码。这奏效了。阻塞错误似乎表明此设置过程中缺少某些内容,以上是我使用新安装的操作系统解决该问题的方法。

赏金将用于某人回答的最有用的附加信息,包括展示如何编辑我的代码以在每次根据

@aja

的评论发送内容时不打开流的人。

    

© www.soinside.com 2019 - 2024. All rights reserved.