如何通过USB连接将计算机的鼠标和键盘传输到另一台计算机
扫描二维码
随时随地手机看文章
它到底是什么?
一种通过USB连接将鼠标和键盘动作从您自己的计算机传输到另一台计算机的方法,使用树莓派pico和USB到TTL UART转换器。
为什么叫跳鼠?
跳鼠是一种跳跃的沙漠啮齿动物,这个项目允许你让你的鼠标和键盘在不同的电脑之间跳跃,就像跳鼠一样。因此得名跳鼠。
在哪里使用它?
这样的功能可以在以下用例场景中有所帮助:
同时在两台电脑上工作,在不同的键盘和鼠标之间切换变得笨拙。像跳鼠这样的设备可以让你在这些设备之间切换鼠标/键盘,这真的很舒服。
其他电脑(可能是笔记本电脑)的鼠标/键盘坏了,你不想买一套新的鼠标和键盘,直到它被修复。
其他电脑(比如安卓手机/平板电脑)的屏幕坏了,你可能想在安排维修之前紧急访问一些东西。你可以使用android的USB OTG功能(是的,几乎每个android都支持这个功能,你可以将鼠标和键盘连接到它)。
当设置sbc(单板计算机)时,它只有1x USB端口,并且像树莓派Zero 2w这样的微型USB,它需要一个USB集线器(可能是一个电源集线器)来连接鼠标和键盘。跳鼠可以很容易地帮助这样的情况下,只有一个USB 2.0到微型USB OTG电缆。
当然,上面提到的所有用例都要求您拥有Raspberry Pi Pico(或任何其他CircuitPython支持的开发板)和USB到TTL转换器。但我们中的许多人两者都已经具备了。
必需的部分
一个支持CircuitPython的开发板,支持USB HID。我已经使用Rpi Pico与rp2040,但任何其他板将工作。你可以在CircuitPython网站上找到支持的电路板(链接在最后)。
任何一个TTL UART到USB转换器。我已经使用了CP2102,但其他转换器,如PL2303, FT232, CH340将工作得很好,因为它们都支持115200波特率。请确保该转换器的逻辑电平与电路板的逻辑电平(即3.3V逻辑电平)匹配。
一台运行Windows/Linux并安装了python的PC机。同样,应该安装numpy, pyserial和pygame。
以下是我使用的设置摘要,但更高的版本应该可以正常工作:
它是如何工作的?
下面是对系统架构的简要描述。别担心,后面会详细解释每一步。
pygame窗口在本地计算机上运行,并捕获所有鼠标和键盘操作活动。
这些动作从pygame值映射到circuitpython值。
每个动作生成一个二进制数据包,并使用pyserial发送到TTL UART到USB转换器。
树莓派Pico接收这些二进制数据包,解码它们,并在远程计算机上执行相同的HID鼠标和键盘活动。
下面将进一步解释源代码细节,因此您可以同时参考github repo。
步骤1:使用pygame捕获本地计算机上的鼠标和键盘输入
在捕获鼠标和键盘输入时,需要注意以下几点:
a)当捕获活动时,鼠标和键盘的动作应该只对远程计算机有效,不应该与本地计算机交互。这意味着鼠标和键盘不应该允许在本地计算机上的任何操作,当捕获是活动的。为此,pygame允许使用以下函数完全获取输入:
b)鼠标应该允许无限运动并正确报告相对运动。这意味着当鼠标指针到达本地计算机上的某个边缘时不应该停止移动,因为同时鼠标指针可能还没有到达远程计算机上的同一边缘。Pygame允许使用以下功能,通过使鼠标指针不可见并允许在虚拟输入模式下无限移动鼠标。
c)应该有简单的方法在本地和远程计算机之间切换。为此,Ctrl+Alt+G (G表示抓取)被用作启用/禁用抓取的快捷键。启动跳跃跳鼠时,最初禁用Grab,并且鼠标/键盘在本地计算机上正常工作。通过选择pygame窗口并按Ctrl+Alt+G将启用抓取,鼠标/键盘将仅在远程计算机上工作。再次按Ctrl+Alt+G将禁用抓取,鼠标/键盘将回到本地计算机使用。请参考源代码以正确理解这一点。
跳鼠python脚本以100 Hz采样pygame事件,并查找MOUSEMOTION, MOUSEBUTTONDOWN, MOUSEBUTTONUP, MOUSEWHEEL, KEYDOWN, KEYUP事件。当观察到这些事件时,它们将被进一步发送,以便从pygame事件映射(如果需要)到circuitpython hid命令。
此外,还存在一些限制。pygame允许捕获Windows上几乎所有的键组合,除了Ctrl+Alt+Del。但是在Linux上,不允许捕获更多的键组合。然而,这并不意味着这个项目毫无用处。
步骤2:键盘键和鼠标按钮映射器
Pygame和circuitpython使用不同的整数值引用相同的键盘键或鼠标按钮。例如,pygame使用值0x97引用键盘键‘A’,而circuitpython使用值0x04引用相同的键盘键。因此,必须有一个查找表和查找函数来帮助进行键和按钮映射。
下面的映射表和功能已实现的键盘键:
同样,鼠标按钮的映射表:
注意,鼠标移动或滚轮不需要这样的映射,因为它们仅仅是基于移动/滚动的大小和方向的带符号整数值。
步骤3:简化二进制通信协议
由于我们需要发送关于鼠标和键盘动作的非常基本的信息,因此使用以下简化的二进制通信协议将不同的动作从本地计算机发送到Rpi pico。
二进制数据包的固定长度为6字节。因此不需要为数据包的大小发送额外的字节。
校验和就是报头字节和有效载荷字节的总和。它可能会溢出,但没关系,因为同样的无符号整数算法在接收端也会以同样的方式工作。无论如何,这不是一个高完整性的系统。
下面是对所有不同动作的二进制数据包内容的详细描述。
尽管长度较短,但该数据包的开销非常高,为50%(6字节总长度中的3字节报头+校验和)。但只要能达到目的,那就没问题。
下面是将鼠标X和Y运动编码为二进制数据包的函数。
115200的波特率够吗?
波特率为115200(比特/秒)和8N1(1个起始位+ 8个数据位+无奇偶校验(0位)+ 1个停止位=每个字节传输10位)
我们每秒可以传输11520字节((115200比特/秒)/(10比特/字节)= 11520字节/秒)。
这大约是每秒1920个数据包((11520字节/秒)/(6字节/数据包)= 1920个数据包/秒)
在100Hz事件采样时(在pygame中),这将导致19.2个数据包样本的带宽。((1920包/秒)/(100样本/秒)= 19.2包/样本)
19.2数据包/样本或19.2鼠标和键盘事件/样本是相当高的输入速率,我们的手不能轻易地提供这样的运动到鼠标和键盘。
因此,115200的波特率绰绰有余,并且可以为我们的TTL级UART信号提供更长的电缆长度。
步骤4:二进制数据包解码(pico)
通过向数组中添加字节来编码二进制数据包更容易,但与编码相比,解码有点困难。下面是运行在Rpi pico上的状态机对步骤3中描述的二进制协议的解码逻辑。此状态机已使用if..elseif..实现。Else控制语句与一对状态变量。
下面是对这个状态机及其状态的简单描述:
步骤5:发送USB HID命令到远程计算机
一旦接收到有效的数据包并按照步骤4中的描述进行缓冲,下面的代码将读取字节#2 (AXID),并根据AXID表示的操作调用所需的函数。
以下是通过读取字节3和4 (ACV1和ACV2)的所需值来发送鼠标运动的usb hid命令的代码。
Rpi Pico UART收到考虑
circuitpython busio api消除了实现异步接收UART数据的底层代码所需的所有艰苦工作。但是它要求应用程序代码正确地配置UART,以便应用程序在需要时及时接收到数据,并且阻塞最小,尽管数据可以随时到达。
基本上,为了更好地调度pico上的circuitpython应用程序,我们需要正确定义两件事:
一次需要等待和接收多少字节- pygame以6字节长度的块(数据包)发送数据,因此一次可以读取6字节,如下面的代码片段所示。
如果在UART上没有接收到数据,需要等待多长时间,也就是超时- pygame可以以100Hz的最大速度发送数据,因此我们可以将超时设置为10毫秒(0.01秒),参见下面的代码片段。
设置这些参数的原因是,busio.UART.read()函数将阻塞整个应用程序,直到接收到所需的字节数,或者在没有接收到任何额外字节的情况下发生超时。
虽然我们的应用程序只做一件事,即解码和对动作进行操作,但在其他应用程序中,您可能需要使用相同的code.py完成多个事情。在这种情况下,选择超时和要读取的字节数可以显著地帮助减少阻塞。
您可以通过将timeout设置为0来避免阻塞。但这将导致对read()的更多调用,从而增加开销,而不做任何实际工作。你可以使用circuitpython的asyncio来实现更好的调度。
异常处理(在本地计算机上)
Python支持异常,这是一件好事,但这意味着您必须正确处理异常,否则您的代码将在遇到异常时立即停止执行。使用异常处理程序,我们可以在不使代码崩溃的情况下处理一些异常。
例如,在初始化本地计算机脚本时,如果找不到ascii美术文件,我们可能会遇到异常。这可以通过简单地打印找不到ascii美术文件来处理,但执行仍然可以继续,因为没有任何功能依赖于ascii美术文件。
其他例外是不存在的开放串行端口,可能是因为USB到TTL UART模块未连接或连接到不同的COM端口。在这种情况下,我们可以简单地打印问题并退出应用程序,因为没有有效的串行适配器就无法进一步执行任何操作。
类似地,如果在使用之间移除USB到TTL UART适配器,则在将数据包写入串行端口时将引发异常。这可以通过打印问题并退出来处理,因为不能再做任何事情。
这些都是预期的异常,但可能会发生许多其他异常,我们可以编写一个包含整个main()调用的公共处理程序,打印异常并退出。
注意,这些都是过于简化的异常处理程序,因为我们的应用程序也非常简单。但是在更复杂的应用程序中,您可能会打开多个资源,在这种情况下,必须正确释放所有资源以避免任何损坏。
异常处理(在raspberry pi pico上)
我们应该以避免任何异常的方式编写嵌入式应用程序。这可以通过范围检查、索引检查、使用前变量初始化等措施来实现。然而,仍然有可能发生异常。
在这种情况下,我们可以编写包含整个while True的异常处理程序,并从头开始重新启动脚本执行。因此,用户甚至不会注意到异常,pico将很快恢复运行。这种方法对我们来说很有效,因为我们的应用程序不是安全关键的,因此重新启动不会导致任何问题。但对于安全关键型、高完整性或硬实时系统,这种方法可能有所不同。
安装和设置
请参考自述文件。Md文件从github的repo。
照片
限制
注意,并不是所有的键盘和鼠标操作都可以被捕获。当本地计算机是windows时,不会捕获Ctrl+Alt+Del。类似的限制也适用于Linux本地计算机。
关于键盘记录的注意事项!
在游戏的核心,跳鼠基本上就是一个键盘记录器。但与键盘记录器恶意软件不同,跳鼠python脚本在以下方面有所不同:
脚本由用户运行,不会自动启动。用户控制是否运行脚本。
脚本在执行时总是显示一个GUI pygame窗口。只有当pygame窗口处于焦点时,键盘记录操作才会激活。
它是开源的,发行版也是源代码(python脚本),而不是二进制发行版。欢迎并建议您参考源代码。
这些脚本不连接到互联网,记录(捕获)的任何密钥都不会存储在任何地方。相反,关键日志只是即时流式传输,没有来自接收器硬件的任何反馈/请求(这里是rpi pico)。
该脚本不需要安装,不会自动启动,不会自我复制,也不会试图感染任何其他系统。删除脚本等同于卸载。
用户仍然需要手动安装依赖项(python, pygame, pyserial等)才能使用此脚本。该脚本永远不会安装任何依赖项。
这只是一个概念证明,可以帮助同时使用两台计算机系统更容易一些。
进一步的可能性
为本地计算机脚本添加接收功能,rpi pico应根据其有效性确认/不确认串行数据包。未得到确认/未得到确认可以被认为是通信有问题。这样我们也可以确定rpi pico连接是否正确,因为跳线有时会变松。
将此代码移植到基于nRF52840的开发板上,以利用BLE HID功能进行无线鼠标和键盘传输。考虑到circuitpython对BLE HID也有很好的支持,就像本项目中使用的USB HID一样,这应该不是一项艰巨的工作。
本文编译自hackster.io