在 Arduino 中使用 Wii 双节棍
组件和用品
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
关于这个项目
前言
最初,这篇文章发表在 www.xarg.org。
使用带有 Arduino 的 Wii 双节棍
我们都是在手中握着游戏手柄长大的,这使得它们非常适合将它们与几乎任何可能的应用程序结合起来。任天堂的一项伟大发明是 Nunchuk,它是 Wii U 遥控器的廉价扩展。由于它使用 I2C 作为传输协议,因此很容易访问控制器的原始数据。由于它是如此简单,我认为必须有一个标准的解决方案,但找不到稳定的实现,只有大量的代码片段。这就是为什么我专注于填补这个空白,它就在这里。在本文中,我将引导您了解详细信息并在 Arduino 上实现它。
硬件
Nunchuk 有两个薄膜按钮,称为按钮 C 和按钮 Z。设备顶部是一个模拟两轴操纵杆,整个设备对运动很敏感,因为加速度计测量作用在所有三个维度上的力。建议使用 3v3 操作 Nunchuk,如果您无法提供该电压以延长其使用寿命,则绝对应该使用电平转换器。此设置至少需要以下部件:
零件
- 任天堂双节棍(或复制品)
- 阿杜诺
- 电平转换器(可选)
- Nunchuk 适配器(可选)
原始双节棍或复制品
最大的问题是,您应该购买原始的任天堂双节棍还是中国的复制品?为了测试我的 Nunchuk 驱动程序,我购买了原版和便宜的副本。两个版本都远不贵,但相差大约10-15欧元。我可以说的是,原始 Nunchuk 的电缆线是屏蔽的,这使得它们更强大地抵抗干扰。原版的摇杆质量更高,可以解析所有数值范围内的所有动作,而副本不会对最小动作做出反应。原厂的加速度计质量也更好。但是,我应用了卡尔曼滤波器来平衡这些差异。所以,如果你想要一个长寿命的控制器,我会选择原版,如果你只需要一个便宜的输入设备,你可能会幸运地拥有一个 Nunchuk 副本。
双节棍连接器
任天堂使用专有插头连接所有外围设备。因为我没有 Wii 并且不会将控制器用于与游戏相关的事情,所以我撕开了连接器。你可以花几块钱买一个 Nunchuk 适配器,如果你不想做这个激烈的步骤,但是有了一些焊接技巧,无论如何你应该能够修理插头。我建议打开插头,因为您可以看到电线是如何连接到引脚的。这对于 Nunchuk 复制品尤其重要,因为电线可以随机连接。另一个奇怪的是,原来的控制器有4根线,复制品有5根,而连接器实际上有6针。好的,让我们开始工作,打开连接器:
并切断电线:
从插头正面看时,您会看到这些引脚:
对于原版的 Nunchuk,以下是连接器的颜色和引脚布局:
- 绿色 → SDA
- nc
- 红色 → 3v3
- 白色 → GND
- nc
- 黄色 → SCL
对于我得到的 Nunchuk 克隆,以下是连接器的颜色和引脚布局:
- 黄色 → SDA
- 黑色 → nc
- 绿色 → 3v3
- 红色 → 接地
- nc
- 白色 → SCL
正如我所说,您绝对应该查看电线的布局方式,而不是通过颜色连接任何东西。使用的首字母缩写词是:nc
对于未连接,SDA-数据线(必须与Arduino板上的A4连接)和SCL-时钟(与A5连接)。 Arduino 有一个电压调节器,它允许我们直接使用 3v3。如果您想使用 WiiChuck 或类似的 Nunchuk 适配器,请将 3v3 与 A2 连接,将 GND 与 A1 连接 - 您稍后需要调用 nunchuk_init_power()
初始化这些电源连接器。以下 Fritzing 草图说明了这种联系:
软件
我写了一个小的头库,实现了与Nunchuk的通信。事实上,您不必担心幕后发生的事情,它应该是开箱即用的。如果你对细节感兴趣,我会在一分钟内解释这个。
下载双节棍图书馆
与 Nunchuk 的通信是通过 TWI 总线进行的,这只是 I2C 的另一个名称(好吧,有些细节是不同的,但这对我们的目的并不重要)。 I2C 总线的速度默认设置为快速模式 (400kHz)。为了在您的 Arduino 中使用 Nunchuk,只需要以下内容:
#include #include "nunchuk.h"void setup() { Serial.begin(9600); Wire.begin(); // nunchuk_init_power(); // A1 和 A2 是电源 nunchuk_init();}void loop() { if (nunchuk_read()) { // 使用 nunchuk_data nunchuk_print(); } 延迟(10);}
启动库附带的处理文件时,您可以用 Nunchuk 平衡这个锥体:
而不是 nunchuk_print()
很多函数可以用来访问实际数据:
双节棍函数
nunchuk_buttonZ():
1 或 0,是否按下 Z 按钮
nunchuk_buttonC():
1 或 0,是否按下 C 键
nunchuk_joystickX_raw() / nunchuk_joystickX() nunchuk_joystickY_raw() / nunchuk_joystickY():
操纵杆的 x 或 y 值。每个函数都有一个原始版本,无需校准即可访问数据。
nunchuk_joystick_angle():
以弧度为单位计算操纵杆的角度。
nunchuk_accelX_raw() / nunchuk_accelX() nunchuk_accelY_raw() / nunchuk_accelY() nunchuk_accelZ_raw() / nunchuk_accelZ():
x-、y- 或 z-
加速度计的值。每个函数都有一个原始版本,无需校准即可访问数据。
nunchuk_pitch():
以弧度为单位计算控制器的俯仰角。
nunchuk_roll():
以弧度为单位计算控制器的滚动角。
注意: 没有 nunchuk_yaw()
功能,因为加速度计只能测量方向力而不能测量旋转速度。这种情况也将俯仰限制为仅 180°,并在俯仰大于 180°时迫使横滚角错误。
俯仰角和滚转角的计算也在一个简化的假设下进行,即作用在控制器上的唯一速度是重力。这是有效的,因为 Nunchuk 要么保持静止,要么在外力作用下保持恒定速度。
校准
如功能概述中所述,操纵杆和加速度计的每个功能都带有校准版本和原始版本。在库文件的开头,一些常量定义了零位置。最好的办法是读取控制器的原始测量值,当您将其保持在中立位置并相应地调整常量时。
I2C 协议
此处的最后一部分讨论了实际的 I2C 协议。如果你只是想使用控制器,这个信息是不需要的,但是如果你想了解协议,我希望我能节省你一些时间。
我通过使用 I2C 扫描器扫描 Nunchuk 的实际地址(0x52)开始开发。通过 I2C 总线从 Nunchuk 的存储器读取参数与与普通 I2C EEPROM 的通信非常相似。在可以读取来自 Nunchuk 的数据之前,需要发送一个初始化序列。
为了从 Nunchuk 读取数据,必须发送要读取的地址,因为 Nunchuk 的控制器会在每次读取时递增地址。我们真正感兴趣的数据位于地址 0x00 处,长度为 6 个字节。在地址 0x20 和 0x30(似乎是 0x20 字节的精确副本),存储了 16 字节的校准数据。在地址 0xFA 处,您可以找到设备的标识号,Nunchuck 为 0xA4200000,Classic Controller 为 0xA4200101,Balance 为 0xA4200402 等等。
当需要将数据写入 Nunchuk 时,格式类似于读取命令。第一个字节是地址,后面是实际数据。
让我们来看看 Nunchuk 的实际字节序列,用于描述的功能:
1. 初始化双节棍:
开始,0x40,0x00,停止
这个序列是正常的初始化序列,它将加密算法设置为默认值。从 Nunchuk 读取的每个字节都必须用 (x ^ 0x17) + 0x17
解密 .更好的方法是使用此序列禁用加密:
2. 不加密初始化双节棍:
START, 0xF0, 0x55, STOPSTART, 0xFB, 0x00, STOP
这样做的好处是无需解密公式即可使用实际数据,并且也适用于 Nunchuk-clone。
3.从扩展寄存器中读取设备标识:
START, 0xFA, STOPREAD 6 字节
如果连接的设备是 Nunchuk 或 Classic 控制器等,数据就是我已经提到的 ident。
4. 从设备读取测量值:
START, 0x00, STOPREAD 6 字节
此概述中描述了您获得的回报:
- 位字节 7 6 5 4 3 2 1 0
- 1 个操纵杆 X 轴 [7:0]
- 2 操纵杆 Y 轴 [7:0]
- 3 加速度计 X 轴 [9:2]
- 4 加速度计 Y 轴 [9:2]
- 5 加速度计 Z 轴 [9:2]
- 6 Az [1:0] Ay [1:0] Ax [1:0] ¬Bc ¬Bz
因此,按钮值被反转,加速度计的 LSB 位包含在字节 6 中。在库中,我将 LSB 位与其余位组合在一起。我见过有人使用没有 LSB 位的值,这是让信号更稳定的一种过于简单的方法。对我来说,我更喜欢来自 ADC 的噪声信号,它可以使用互补或卡尔曼滤波器轻松过滤,而不是通过舍入来截断数字 - 这样会丢失大量信息。
5.从设备读取实际校准数据:
START, 0x20, STOP READ 16byte
此概述中描述了您获得的回报:
字节
说明
- X 轴的 1 0G 值 [9:2]
- 2 Y 轴的 0G 值 [9:2]
- 3 Z 轴的 0G 值 [9:2]
- X、Y、Z 轴零值的 4 LSB
- 5 X 轴的 1G 值 [9:2]
- 6 Y 轴的 1G 值 [9:2]
- 7 Z 轴的 1G 值 [9:2]
- X、Y、Z 轴的 1G 值的 8 LSB
- 9 操纵杆 X 轴最大值
- 至少 10 个操纵杆 X 轴
- 11 操纵杆 X 轴中心
- 最大 12 个操纵杆 Y 轴
- 13 操纵杆 Y 轴最小值
- 14 摇杆 Y 轴中心
- 15 校验和
- 16 校验和
字节 0-3 存储 X、Y 和 Z 轴的零值,而字节 4-7 存储 1G(地球引力)的值。校准数据尚未用于软件实现。
代码
- 代码片段 #1
代码片段 #1纯文本
#include#include "nunchuk.h"void setup() { Serial.begin(9600); Wire.begin(); // nunchuk_init_power(); // A1 和 A2 是电源 nunchuk_init();}void loop() { if (nunchuk_read()) { // 使用 nunchuk_data nunchuk_print(); }延迟(10);}
Github
https://github.com/infusion/Fritzing/tree/master/Nunchukhttps://github.com/infusion/Fritzing/tree/master/Nunchuk定制零件和外壳
制造工艺