编译注记: 本文作者Elizabeth Scott是一位女工程师,在这篇文章中,她记述了自己动手定制跳蛋遥控器的经历。以下第一人称"我"均指 Elizabeth Scott 本人。 一个跳蛋不贵,但一个能隔空进行手势操控的跳蛋可能全球你找不到几个。既然软件可以开源,为什么跳蛋不可以?极客,不仅仅是一群偏执狂,他们能用自己的智慧为自己找到性福。在完成之后,还会很乐意把各种代码和源文件在 Github 上分享出来。 性福,不仅仅靠双手。 缘起 最近我在选购遥控跳蛋,最后购买的是下面这款LELO 生产的 Lyla vibe: 这款产品优点不少:耐用、防水、舒适,可以反复充电使用。然而我对它自带的遥控器却很不满意:遥控器的控制距离太短,并且用跳蛋的时候要握着遥控器,让我感觉我在拍色情录像,而不是在尽情享受性爱。 虽然这个遥控器本身并不粗糙,甚至带有加速度传感器,但我还是觉得功能上不能尽如我意:按下遥控按键之后跳蛋的响应速度比较滞后,并且在遥控器上按键才能控制跳蛋总是让我感到很不方便。 所以,很自然的,我打算自己改善一下这颗跳蛋的功能,一开始我其实并不知道最后这个项目会有怎样的结果。 选择传感器 最初的原型设备只是给一个可变电阻器加一个把手。虽然看上去很简陋,但是它已经比跳蛋出厂自带的遥控器改善不少:遥控距离扩大了,响应速度也提高了。当时我只打算做一个简单的成品验证我的想法,还没有追加其它更复杂的功能。在此之后,我一度计划加入更多电子输入设备,做出更复杂的功能。我甚至为这个项目写了一个开源 Arduino 库放在 github 上,你可以自己在这个库的基础上给跳蛋扩展出更多功能。 在到处搜寻可用传感器的时候,我突然想到:为什么不用一个声纳呢?我快速进行了一些验证,事实证明这个方法可以带来很高的响应速度。并且我自己其实挺懒的,声纳这种不带转动部件的小传感器在结构上更加简单,更加容易做出稳定的原型设备来。 看不见的墙 到目前为止,我做出来的成品遥控器是下面这样的: 遥控器上的两个黑圈,其中一个以人耳听不到的高频发送短促的声波,另一个则接收声波被反射之后的回音。四位显示屏虽然简陋,但也已经可以用醒目的蓝色 LED 灯光给出视觉回馈。外置的天线使这个遥控器比原厂自带的那个有更大的接收范围,而左侧露出的接口则让我可以很方便地随时用 Arduino IDE 对它进行重新编程和修改。 这个使用声纳的改造方案有个十分显著的优点:你不需要用手去按动遥控器,亦无需费心保持遥控器外壳的洁净。而当我越来越多地使用这个新的遥控器,我也发现它还有更多独特的优势。 经过改造之后,这个小玩具看上去不再仅仅是一个简单的遥控器,它开始向虚拟现实靠拢——你可以通过远程触感来与机器交流。它架起了两个空间之间的桥梁——遥控器前方的一片区域,和你与跳蛋相接触的那部分身体。你伸手划过遥控器前方的一片空间,跳蛋就会带给你想要的震动感觉。 事实上,用了一段时间之后你会觉得仿佛遥控器上方的空间里存在某种肉眼不可见的物质,把身体移动过去,这种物质就会作出反应。你轻轻地按动它、拨弄它的边缘、伸手滑过它、或者画出优雅的波浪形手势,甚至整个身体向它靠过去,它都会给出合适的反馈,仿佛它熟知你身体的韵律。 你甚至可以同时使用不止一个跳蛋。不需要给每个跳蛋分配一个不同的无线频道,所有在遥控器有效范围内并且已经打开了的跳蛋都会受到控制进行震动。 这就是目前为止的成品结果。接下来,我来分享一下自制跳蛋遥控器的过程,或许会对你有所启发。 反向工程 要取代原厂自带的遥控器,首先你要理解这个原厂遥控器的工作方式。所以我先去查看FCC ID数据库,目的是了解这个遥控器的部件是否有进行破解的可能性。我运气不错,因为数据库中的信息明确地显示这个遥控器使用了 MSP430 单片机和 CC2500 无线模块。看起来破解难度并不是太大。 我没有尝试去对原装的单片机进行读取和重新编程,而是嗅探 CC2500 的 SPI 总线,以便对无线设置和无线协议进行反向工程。反向工程成功以后,我就可以把任何 CC2500 无线模块与任何单片机进行组合,并对跳蛋本身进行无线遥控。 比较悲剧的是,这个遥控器很难拆。它最外面的树脂保护层是和外壳用胶水粘贴在一起的,在打开它的过程中我不得不破坏掉了这个保护层。然后我发现外壳也是通过胶水粘贴来闭合的…… 所以,虽然我不介意拆掉自己的遥控器,但是如果你很在意遥控器的完好和防水性能的话,我不建议你拆开它——拆了以后,是没有办法完好地恢复如初的。 在终于拆开遥控器取出电路板之后,我开始尝试操作电路板。我把接插件焊到电路板的电池触点上,然后打开显微镜,用 AWG-32 规格的导线从接插件上连接到各个我需要探测的地方,例如上面说过的 SPI 总线。此外,我把遥控器上原有的振动器换成了 LED 灯,这样它既可以继续指示各种状态,又不会发出震动影响我的操作。 有不少工具可以帮助你更高效地完成类似的探测工作。如果你预算够多,愿意多买一两件专用工具的话,可以用Total Phase 的这个 SPI/I2C 嗅探器,Saleae Logic 的逻辑分析仪和Logic Shrimp之类的开源工具也很顺手。 不幸的是这些东西我手上一个也没有。我自己的 Saleae Logic 分析仪不在身边,而我常用的Bitscope分析器在对协议进行反向工程时并不那么好用。于是我需要找出一种临时解决方案,好在这一次数据量并不多,最后我把遥控器插到我的Propeller 演示板上,写了一个小程序来抓 SPI 数据流,并用 ASCII 形式把它们记录下来。这个小程序也放到了 Github 上。 完整的追踪数据可以在 Github 上看到。而其中我要找的只有两种数据:初始化序列数据、和真正传递了数据包的数据。我发现,实际上当你关掉这个遥控器时,它并没有真正的彻底停止运作,而只是进入了低耗电待机模式,并放出一个待机命令。所以,当你打开遥控器的时候也不是真的让它初始化。为了抓到真正的初始化序列数据,我只能选择反复给它切断和接上电源,就像反复放入和取出电池一样。之后我得到了下面的数据,左边的十六进制数来自 SPI 嗅探器,右边是我的注释: 实际数据比这个要长一些,不过最重要的数据都在了:基带频率 2.420 GHz,MSK 参数和 250kB 每秒的数据传输速度。此外,我还发现实际上跳蛋遥控器和跳蛋之间不是一一配对进行数据传送和接收的,所有的地址和无线频道都是硬性写死的——这意味着可以用一个遥控器来控制多个跳蛋。 现在我发现了原厂控制器遥控反应延迟的原因:数据包传输频率被定在 9Hz,这个刷新率太低了。这可能是为了省电,毕竟原厂遥控器只用两节电池来供电。而我自己做的这个则有更充足的电源,所以我完全可以把刷新率提高十倍,来大幅改善遥控反应速度。此外,我自己做的这个抗干扰能力更强,所以即便传输中会有一定的丢包,效果也几乎不会打折扣。 在每一个数据包中,都会有一个数值用于刷新跳蛋的震动马达强度,强度范围是从 0 到 128,此外,里面还有单片机本身的 9 比特负载,以及 CC2500 产生的冗余码和头文件。总之,一个典型的数据包内容如下: 你可以看到,这一次数据包传递中,震动马达强度被设在了 0x28,也就是最大震动强度的 30%,这个数值在每个数据包中重复两次,而其它的几比特数据似乎每次都不变。 到现在为止,我对协议本身已经有了足够的了解,可以尝试自己来模拟一个原厂遥控器了。当时我还没有买 CC2500 的电路板,所以我把原厂遥控器里的那个单片机强制设在重置状态,用这个遥控器作为 CC2500 无线模块用于我的原型设备中。我在这样的情况下开发出了一个 Arduino 库,可以用于正确设置 CC2500 无线模块,并发送像上面所说的那种数据包。当然,这个库现在也在 Github 上。 好了,最枯燥的协议反向工程部分完成,接下来就是真正有趣的部分了:用常见部件 DIY 一个更强大的跳蛋遥控器。 供电控制 这个时候,遥控器本身需要的东西已经很明确了——现成的 Arduino、CC2500 印刷电路板、声纳传感器,以及 LED 显示器。问题是,要怎么给这个设备供电呢?声纳传感器和 LED 显示屏都是耗电大户,上面也提到了我自己做的这个遥控器无线传输量也会更大。 我完全可以用五号或者七号电池,但是我又希望新遥控器的设计能够简单紧凑。加入塑料电池仓的话,结构上就不简单了。我甚至想过要不要直接用一个手电筒机身来装电池,但是又找不到合适的连接方法。锂电池的话,外形会更好一些,然而你需要充电器。 所以供电这边未能完全如我所愿,我至少要用到锂聚合物电池,变压器(因为要把电压变到 5V 给声纳传感器用)、充电电路和充电指示灯,并且还没有现成的组合可以直接用。Arduino Fio 板可能是最接近我需求的一个,它自带了一个锂聚合物电池充电器。但是这东西却没有 5V 变压器和充电指示灯。所以除非我自己设计制造一块电路板,否则我就只能单买各个部件然后自己组装了。最后组装出的成品是下面这样的,确实变得复杂庞大了很多。 这个供电装置的各种部件总共花费了 45 美元,最后电池容量是 3200 毫安时,只要 22 美元。整个装置基本相当于一个移动电源,可以在很多其它项目里复用,甚至用来给我的手机充电也完全没问题。 原料清单 下面就是自己做一个跳蛋遥控器需要的所有部件,几乎所有的部件都可以在亚马逊和 eBay 之类的地方找到卖家。 Arduino Pro Mini CC2500 无线模块 Anker SlimTalk 外接电池包 Parallax Ping 超声波传感器 SparkFun LED 显示屏 塑料外壳(这个可以 3D 打印出来) 两组 M3x16 螺栓和螺母 USB 插口(这个可以从旧的U盘上拆出来) 连接线和接插件 粘合剂 准备好这些之后,你需要往 Arduino 板上焊一个 FTDI 兼容接插件,然后取下 13 针 LED 的限流电阻。如果没有取掉的话,它发出的光会透过塑料壳影响到你。此外,超声波传感器上的 LED 也可以移除掉。不过我自己在做的时候忘掉了这一步,它的灯光几乎透不出塑料壳,所以……其实影响不大。 外壳打印和部件组装 很多人在进行电子项目的时候都会无意中忽视其中不带电的部分。在这个项目上,一个塑料外壳还是很重要的:它可以用来容纳电池包、维持各种电路板之间的相对位置,以及提供一个平滑的外观,更加适合在卧室里使用。 在这种情况下,3D 打印是最合适的选择。通过 3D 打印,我可以做出最符合我需求的外壳:完全与各种电路板结合,恰当地展示 LED 灯状态,给声纳传感器留出空间,同时把各种电路遮起来。 我使用著名的开源软件 Blender 进行建模,最终塑料壳上下两部分的 STL 格式文件也放在了 Github 上。接着我用 Slic3r 来把模型图转化为 3D 打印用的 G-code。下面是塑料壳一层的可视化图像,你可以看到我把塑料壳内部做成了蜂窝状,这样可以节省用料和时间。 打印工作花了几个小时,然后你还需要处理一下打印出来的各个部分。用小刀挂掉组件边缘溢出的部分,用砂纸磨掉尖锐的边缘,同时我还把各个组件的接合面也打磨了一下,以便组装时可以更加严丝合缝。现在你可以先试试看用螺丝和螺母组合一下这个外壳,如果没问题的话,那么下面就要开始对各个部件进行总装了。 总装过程主要是把各个部件放到塑料壳上为它们分隔出的空间里。有时需要一点胶水来让它们保持固定。最终成品上,天线连接器、电池包的 USB 插口和 Arduino 板的接插件是会受力的地方,所以要格外注意一些。 胶水干了以后,就可以开始接线了,塑料壳的上下两半之间总共会有六条导线。略微留长一些也没什么关系,最后拼合时会有足够的空间来容纳它们的。 刷入固件 安装完外壳之后,还需要给成品刷入固件。所用到的 Arduino Sketch 代码也在 Github 上。 这个固件的功能还很基本,它快速地获取声纳信号,然后将信号传递给中值滤波器,中值滤波器过滤掉信号中的异常值,过滤后的信号被转化成震动马达强度参数,以 80 次每秒的频率通过无线模块传递给跳蛋,并在 LED 模块上显示出来。 未来计划 到这里,这个跳蛋遥控器就已经完成了。不过,其实它还有很大的提升空间。 比如说,固件的功能可以进一步扩展,比如说根据不同的玩法和场景,信号转化为跳蛋震动强度的算法也应该有所不同;而锁定跳蛋震动强度的功能也应该可以做得更加直观一些。 除此之外,最需要改进的恐怕并不是遥控器的技术本身,而是我自己的身体。现在,这个跳蛋玩具就像一件乐器——结构简单,输入信号不多,但是通过与身体的互相配合,可以产生各式各样的技巧和乐趣。我很期待有更多的时间可以享受这颗跳蛋,探索更多不同的玩法。 最后,我也在考虑加入更多传感技术,让它更好地理解你的需求。比如说加入一个 Kinect 摄像头来远程探测你的身体动作。此外,如果加入了锁相回路(phase-locked loop)之类的系统,那么它将不仅仅是探测你的身体变化来作出回应,而是主动对你的需求进行预测,到时候任何响应上的延迟都将得到完全的弥补。 总之,"电子性玩具"的含义,远远不止一块电池和一个马达。未来的世界,会有更多、更智能、更贴心的性玩具等待着你。