634|0

20

帖子

8

TA的资源

一粒金砂(中级)

楼主
 

【DigiKey“智造万物,快乐不停”创意大赛】2,Pi400 HID 键盘功能的实现 [复制链接]

 

在github上有一个zero_hid的库,可以实现使用树莓派zero模拟hid键盘。但这个库有一些问题,直接使用在组合键上会出很多的问题,因此我参考这个项目,重写了一下这个库。

首先科普一下HID协议,HID键盘协议是一种基于报文的协议,通过在USB总线上进行通信。当用户按下键盘上的按键时,键盘将生成一个HID报文,并将其发送到计算机。计算机收到报文后,根据报文的内容来模拟相应的键盘操作,例如在文本编辑器中输入字符或执行特定的功能。

HID键盘报文包含多个字段,其中最重要的是按键码(Keycode)。按键码表示按下的键的唯一标识符,例如“A”键的按键码是0x04。除了按键码外,报文还可以包含其他信息,如修饰键(如Shift、Ctrl和Alt键)的状态和组合键的状态。

因此,在合成报文前,我们先要知道我们想输入的按键哪些是修饰键,而哪些是按键,他们要分开进行处理。

在进入代码部分前,我们需要先安装一下驱动。首先先新建一个文件,命名为isticktoit_usb,添加可执行权限,并填入以下内容:

  • ···
  • #!/bin/bash
  • cd /sys/kernel/config/usb_gadget/
  • mkdir -p isticktoit
  • cd isticktoit
  • echo 0x1d6b > idVendor # Linux Foundation
  • echo 0x0104 > idProduct # Multifunction Composite Gadget
  • echo 0x0100 > bcdDevice # v1.0.0
  • echo 0x0200 > bcdUSB # USB2
  • mkdir -p strings/0x409
  • echo "fedcba9876543210" > strings/0x409/serialnumber
  • echo "Tobias Girstmair" > strings/0x409/manufacturer
  • echo "iSticktoit.net USB Device" > strings/0x409/product
  • mkdir -p configs/c.1/strings/0x409
  • echo "Config 1: ECM network" > configs/c.1/strings/0x409/configuration
  • echo 250 > configs/c.1/MaxPower
  • # Add functions here
  • mkdir -p functions/hid.usb0
  • echo 1 > functions/hid.usb0/protocol
  • echo 1 > functions/hid.usb0/subclass
  • echo 8 > functions/hid.usb0/report_length
  • 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/hid.usb0/report_desc
  • ln -s functions/hid.usb0 configs/c.1/
  • # End functions
  • ls /sys/class/udc > UDC
  • ···

接着运行以下命令,完成驱动配置:

  • ···
  • #!/bin/bash
  • echo "" | sudo tee -a /boot/config.txt
  • echo "# BEGIN HID Keyboard Simulation" | sudo tee -a /boot/config.txt
  • echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt
  • echo "# END HID Keyboard Simulation" | sudo tee -a /boot/config.txt
  • echo "" | sudo tee -a /etc/modules
  • echo "# BEGIN HID Keyboard Simulation" | sudo tee -a /etc/modules
  • echo "dwc2" | sudo tee -a /etc/modules
  • echo "libcomposite" | sudo tee -a /etc/modules
  • echo "# END HID Keyboard Simulation" | sudo tee -a /etc/modules
  • # Move to before exit 0
  • echo "" | sudo tee -a /etc/rc.local
  • echo "# BEGIN HID Keyboard Simulation" | sudo tee -a /etc/rc.local
  • echo "sudo ./isticktoit_usb" | sudo tee -a /etc/rc.local
  • echo "# END HID Keyboard Simulation" | sudo tee -a /etc/rc.local
  • ···

完成后,以后每次重启完成,只需要运行一下isticktoit_usb即可。

处理报文部分的代码如下:

  • ···
  • from typing import List
  • from .hid import hidwrite
  • from .hid.keycodes import KeyCodes
  • from time import sleep
  • import json
  • import pkgutil
  • import os
  • import pathlib
  • class Keyboard:
  •    
  •     def __init__(self, dev='/dev/hidg0') -> None:
  •         self.dev = dev
  •         self.set_layout()
  •         self.control_pressed = []
  •         self.key_pressed = []
  •    
  •     def list_layout(self):
  •         keymaps_dir = pathlib.Path(__file__).parent.absolute() / 'keymaps'
  •         keymaps = os.listdir(keymaps_dir)
  •         files = [f for f in keymaps if f.endswith('.json')]
  •         for count, fname in enumerate(files, 1):
  •             with open(keymaps_dir / fname , encoding='UTF-8') as f:
  •                 content = json.load(f)
  •                 name, desc = content['Name'], content['Description']
  •             print(f'{count}. {name}: {desc}')
  •        
  •     def set_layout(self,  language='US'):
  •         self.layout = json.loads( pkgutil.get_data(__name__, f"keymaps/{language}.json").decode() )
  •     def gen_list(self, keys = []):
  •         _control_pressed = []
  •         _key_pressed = []
  •         for key in keys:
  •             if key[:3] == "MOD":
  •                 _control_pressed.append(KeyCodes[key])
  •             else:
  •                 _key_pressed.append(KeyCodes[key])
  •         return _control_pressed, _key_pressed
  •    
  •     def gen_buf(self):
  •         self.buf = [sum(self.control_pressed),0] + self.key_pressed
  •         self.buf += [0] * (8 - len(self.buf)) # fill to lenth 8
  •     ##########################################################################
  •     # For user
  •    
  •     def press(self, keys = [], additive=False, hold=False):
  •         _control_pressed, _key_pressed = self.gen_list(keys)
  •        
  •         if not additive:
  •             self.control_pressed = []
  •             self.key_pressed = []
  •            
  •         self.control_pressed.extend(_control_pressed)
  •         self.control_pressed = list(set(self.control_pressed)) # remove repeated items
  •         self.key_pressed.extend(_key_pressed)
  •         self.key_pressed = list(set(self.key_pressed))[:6] # remove repeated items and cut until 6 items
  •        
  •         self.gen_buf()
  •         hidwrite.write_to_hid_interface(self.dev, self.buf)
  •        
  •         if not hold:
  •             self.release(keys)
  •        
  •     def release(self, keys = []):
  •         _control_pressed, _key_pressed = self.gen_list(keys)
  •         try:
  •             self.control_pressed = list(set(self.control_pressed) - set(_control_pressed))
  •         except:
  •             pass
  •         try:
  •             self.key_pressed = list(set(self.key_pressed) - set(_key_pressed))
  •         except:
  •             pass
  •        
  •         self.gen_buf()
  •         hidwrite.write_to_hid_interface(self.dev, self.buf)
  •        
  •     def release_all(self):
  •         self.control_pressed = []
  •         self.key_pressed = []
  •        
  •         self.gen_buf()
  •         hidwrite.write_to_hid_interface(self.dev, self.buf)
  •    
  •     def text(self, string, delay=0):
  •             for c in string:
  •                 key_map = self.layout['Mapping'][c]
  •                 key_map = key_map[0]
  •                 mods = key_map['Modifiers']
  •                 keys = key_map['Keys']
  •                 self.press(mods + keys)
  •                 sleep(delay)
  • ···

上面这段代码把想要输出的按键分为control(修饰按键)和key(普通按键)两块,再组合形成报文列表。使用的逻辑是输入当前想要按下的按键状态,然后程序发送对应的报文。

测试一下:

  • ···
  • import os
  • import zero_hid
  • if os.geteuid() != 0:
  •     raise ImportError('You must be root to use this library on linux.')
  • k = zero_hid.Keyboard()
  • k.press(["KEY_H"], additive=False, hold=False)
  • k.press(["KEY_E"], additive=False, hold=False)
  • k.press(["KEY_L"], additive=False, hold=False)
  • k.press(["KEY_L"], additive=False, hold=False)
  • k.press(["KEY_O"], additive=False, hold=False)
  • ···

press方法中填入的是一个list,表示当前按下的所有按键。具体的键值列表在zero_hid/keymaps/US.json中。

如果电脑成功打印,表示功能正常。

 

点赞 关注
 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
Microchip 直播|利用motorBench开发套件高效开发电机磁场定向控制方案 报名中!
直播主题:利用motorBench开发套件高效开发电机磁场定向控制方案
直播时间:2025年3月25日(星期二)上午10:30-11:30
快来报名!

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表