树莓派可以控制很多硬件,最常见的就是做个小车什么的了。看了网上很多都是在网页上点来点去控制,感觉有点麻烦,如果能用手柄就好了。
想到躺在箱子里玩《巫师3》时买的北通阿修罗手柄,通关之后就再也没用过,如果可以用树莓派控制的话,那想想都觉得太cooooool了!
既然想到了,自然马上在小pi上搞起。
1.树莓派3B,系统是Raspbian。性能强劲,自带无线网卡非常爽,省去了各种连线。
2.北通阿修罗TE无线手柄。支持xbox360模式,功能强大。
sudo apt-get install joystick
sudo apt-get isntall xboxdrv
我的Raspbian上,貌似已经加载了相关模块,所以无需再配置内核。
将手柄usb头查到小pi上,直接就可以驱动起来了,查看手柄的px灯同时亮,说明已经连接上了,默认是xbox360模式。
连接上手柄之后,可以在系统中查看一些信息,来确认手柄是否可以正常工作。
在shell下输入lsusb,可以看到,已经识别出来手柄 。
lsusb
Bus 001 Device 009: ID 045e:028e Microsoft Corp. Xbox360 Controller
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
进入/dev/input目录,查看多出了event0和js0两个设备文件,这两个就是手柄设备,说明已经可以开始读取数据了。
ls /dev/input/
by-id by-path event0 js0 mice
在shell下输入jstest ,然后操作手柄,可以看到,数据已经读取成功了,稍微核对一下,按键映射应该都是正确的。
jstest /dev/input/js0
Axes: 0: -2194 1: 644 2:-32767 3: -3226 4: 0 5:-32767 6: 0 7: 0 Buttons: 0:off 1:off 2:off 3:off 4:off 5:off 6:off 7:off 8:off
也可以用下面这个命令,可以查看几个关键数值。
jstest --event /dev/input/js0
Event: type 2, time 45840650, number 4, value -2711
验证到这里,手柄已经可以在我们的小pi上使用,手柄的输出也可以正常读取了。那剩下的就是怎么在代码中将手柄为我们所用了!!胜利就在彼岸!
joystick的头文件在/usr/include/linux/joystick.h 这个位置,并且只有一个头文件可以使用,里面定义了一些宏和结构,我们也只需要使用这个头文件就可以了。
最重要的结构是下面这个,手柄输出的重要数据也就是这几个数值,在jstest测试时,可以看到手柄输出的详细值。
struct js_event {
__u32 time; /* event timestamp in milliseconds */
__s16 value; /* value */
__u8 type; /* event type */
__u8 number; /* axis/button number */
};
type:1为按键,2为轴向
number:按键名字
value:实际的值。
time:一个时间值。
以下是代码:实现了一个类似jstest的功能,我额外将xbox360的按键对照number值做了映射,方便以后直接使用。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define XBOX_TYPE_BUTTON 0x01
#define XBOX_TYPE_AXIS 0x02
#define XBOX_BUTTON_A 0x00
#define XBOX_BUTTON_B 0x01
#define XBOX_BUTTON_X 0x02
#define XBOX_BUTTON_Y 0x03
#define XBOX_BUTTON_LB 0x04
#define XBOX_BUTTON_RB 0x05
#define XBOX_BUTTON_START 0x06
#define XBOX_BUTTON_BACK 0x07
#define XBOX_BUTTON_HOME 0x08
#define XBOX_BUTTON_LO 0x09 /* 左摇杆按键 */
#define XBOX_BUTTON_RO 0x0a /* 右摇杆按键 */
#define XBOX_BUTTON_ON 0x01
#define XBOX_BUTTON_OFF 0x00
#define XBOX_AXIS_LX 0x00 /* 左摇杆X轴 */
#define XBOX_AXIS_LY 0x01 /* 左摇杆Y轴 */
#define XBOX_AXIS_RX 0x03 /* 右摇杆X轴 */
#define XBOX_AXIS_RY 0x04 /* 右摇杆Y轴 */
#define XBOX_AXIS_LT 0x02
#define XBOX_AXIS_RT 0x05
#define XBOX_AXIS_XX 0x06 /* 方向键X轴 */
#define XBOX_AXIS_YY 0x07 /* 方向键Y轴 */
#define XBOX_AXIS_VAL_UP -32767
#define XBOX_AXIS_VAL_DOWN 32767
#define XBOX_AXIS_VAL_LEFT -32767
#define XBOX_AXIS_VAL_RIGHT 32767
#define XBOX_AXIS_VAL_MIN -32767
#define XBOX_AXIS_VAL_MAX 32767
#define XBOX_AXIS_VAL_MID 0x00
typedef struct xbox_map
{
int time;
int a;
int b;
int x;
int y;
int lb;
int rb;
int start;
int back;
int home;
int lo;
int ro;
int lx;
int ly;
int rx;
int ry;
int lt;
int rt;
int xx;
int yy;
}xbox_map_t;
int xbox_open(char *file_name)
{
int xbox_fd;
xbox_fd = open(file_name, O_RDONLY);
if (xbox_fd < 0)
{
perror("open");
return -1;
}
return xbox_fd;
}
int xbox_map_read(int xbox_fd, xbox_map_t *map)
{
int len, type, number, value;
struct js_event js;
len = read(xbox_fd, &js, sizeof(struct js_event));
if (len < 0)
{
perror("read");
return -1;
}
type = js.type;
number = js.number;
value = js.value;
map->time = js.time;
if (type == JS_EVENT_BUTTON)
{
switch (number)
{
case XBOX_BUTTON_A:
map->a = value;
break;
case XBOX_BUTTON_B:
map->b = value;
break;
case XBOX_BUTTON_X:
map->x = value;
break;
case XBOX_BUTTON_Y:
map->y = value;
break;
case XBOX_BUTTON_LB:
map->lb = value;
break;
case XBOX_BUTTON_RB:
map->rb = value;
break;
case XBOX_BUTTON_START:
map->start = value;
break;
case XBOX_BUTTON_BACK:
map->back = value;
break;
case XBOX_BUTTON_HOME:
map->home = value;
break;
case XBOX_BUTTON_LO:
map->lo = value;
break;
case XBOX_BUTTON_RO:
map->ro = value;
break;
default:
break;
}
}
else if (type == JS_EVENT_AXIS)
{
switch(number)
{
case XBOX_AXIS_LX:
map->lx = value;
break;
case XBOX_AXIS_LY:
map->ly = value;
break;
case XBOX_AXIS_RX:
map->rx = value;
break;
case XBOX_AXIS_RY:
map->ry = value;
break;
case XBOX_AXIS_LT:
map->lt = value;
break;
case XBOX_AXIS_RT:
map->rt = value;
break;
case XBOX_AXIS_XX:
map->xx = value;
break;
case XBOX_AXIS_YY:
map->yy = value;
break;
default:
break;
}
}
else
{
/* Init do nothing */
}
return len;
}
void xbox_close(int xbox_fd)
{
close(xbox_fd);
return;
}
int main(void)
{
int xbox_fd ;
xbox_map_t map;
int len, type;
int axis_value, button_value;
int number_of_axis, number_of_buttons ;
memset(&map, 0, sizeof(xbox_map_t));
xbox_fd = xbox_open("/dev/input/js0");
if(xbox_fd < 0)
{
return -1;
}
while(1)
{
len = xbox_map_read(xbox_fd, &map);
if (len < 0)
{
usleep(10*1000);
continue;
}
printf("\rTime:%8d A:%d B:%d X:%d Y:%d LB:%d RB:%d start:%d back:%d home:%d LO:%d RO:%d XX:%-6d YY:%-6d LX:%-6d LY:%-6d RX:%-6d RY:%-6d LT:%-6d RT:%-6d",
map.time, map.a, map.b, map.x, map.y, map.lb, map.rb, map.start, map.back, map.home, map.lo, map.ro,
map.xx, map.yy, map.lx, map.ly, map.rx, map.ry, map.lt, map.rt);
fflush(stdout);
}
xbox_close(xbox_fd);
return 0;
}
A:1 B:0 X:1 Y:0 LB:0 RB:0 start:1 back:1 home:0 LO:0 RO:0 XX:0 YY:0 LX:-388 LY:386 RX:-130 RY:1418 LT:0 RT:32767
xbox360手柄功能非常强大,wifi控制距离远,信号强,控制稳定,以后再做什么东西都可以使用手柄控制,而且是无线控制,想想就觉得充满各种可能性!
过程中查阅了大量的资料,走了很多弯路,最后实现确是很简单的。百度查到的东西都不是很靠谱,但还是要靠百度查,最后总结了很多论坛的资料找到了解决方法。
树莓派可以实现,那其他linux系统应该也是一样的道理,这里做一下记录留着自己以后方便研究学习了。