2009年10月17日 星期六

在 Linux 下控制 Wii Remote

本文在 2009/10/17 10:15 發表於 Yahoo!奇摩部落格
因Yahoo!奇摩部落格將於2013年12月26日終止服務故遷移至此


嗯...兵單來了...囧
好吧, 夢想還沒完成, 只能暫時放下, 先輸出文件

要連線到 Wii 手把, 需要準備一個藍牙傳輸設備
大概就長這樣

叫做 bluetooth usb dongle 的樣子
類似網路卡這樣
藍牙傳輸類似網路傳輸
用一個位址找裝置
這位址可以代理,有類似DNS的查詢機制
建立連線後就像網路那樣序列傳輸



Windows 版軟體到 Johnny Chung Lee 那裡下載就有了

Johnny Chung Lee > Projects > Wii
http://johnnylee.net/projects/wii/

網路上也很多資源,很容易取得
Linux 上的就會麻煩一點了
基本需求: BlueZ 和 CWiid

BlueZ
http://www.bluez.org/

CWiid
舊站(有API文件) : http://abstrakraft.org/cwiid/
新站 : http://github.com/abstrakraft/cwiid

BlueZ 是藍牙驅動程式
分三部分
kernel driver, utilities, user space library
kernel driver 部分 linux kernel 已經有安裝
設定時如果不需要一些 sound 支援建議選沒有支援的版本
好像是 SCO 支援吧, 不太記得了
這樣才不會被 kernel message 洗版

需要自己裝的部分是 utilities 和 user space library
如果是用像 ubuntu 等系統則可跳過這部分
這些多已內建且設定好了
就算沒內建, 安裝也只是幾條指令
若要自己編就有點麻煩了
它會需要 DBus 作為散播信號的介面
對桌上型系統來說是好事
大家只要聽 DBus 信號就可以知道狀態
然而, 對於只需要有連線能力的嵌入式系統就會有點麻煩
這邊提供我改過的版本 bluez-4.18-modified.zip
把 DBus 部分拆掉了
只留下核心部分的 library 以及設定工具
編譯方法就只要 make
不過一些 makefile 裡的參數要設定一下
這就不詳述了
這是身為開發人員應該要會的!XD
完成後測試一下
插上 bluetooth usb dongle
然後用 hciconfig 設定裝置
指令大概像這樣

hciconfig hci0 up

若有看到裝置就成功了
這就像網路一樣
網路的是 ifconfig eth0 up
若有興趣可以追一下原始碼
會發現連線方式也是 socket
只是參數有些不同

不管是用官方版的或是改造過的
接著就是安裝 CWiid
一樣可以用 ubuntu 內建或是自己編譯
自己編譯的話指令如下

# CWiid 0.6.00
./configure --prefix=$PREFIX --target=$target --without-python --disable-ldconfig
# 修改 defs.mak:
CC = i686-unknown-linux-gnu-gcc
CFLAGS = $(DEBUGFLAGS) $(WARNFLAGS) -DHAVE_CONFIG_H -I$(COMMON)/include -I${prefix}/include -fno-stack-protector
LDFLAGS = -L${prefix}/lib -L../libcwiid -lbluetooth

完成後就可以開始寫控制程式
可參考API

CWiid舊站 http://abstrakraft.org/cwiid/wiki/libcwiid

如同一般裝置啟動
需要初始化,設定,然後運轉,完成後退出
初始化用這條

cwiid_wiimote_t *cwiid_connect(bdaddr_t *bdaddr, int flags);

接著設定監聽函數

typedef void cwiid_mesg_callback_t(cwiid_wiimote_t *, int, union cwiid_mesg []);
int cwiid_set_mesg_callback(cwiid_wiimote_t *wiimote, cwiid_mesg_callback_t *callback);

第一行是監聽函數的格式
官網那似乎是舊版的
0.6.00 版源碼裡附的是這:

typedef void cwiid_mesg_callback_t(cwiid_wiimote_t *, int, union cwiid_mesg [], struct timespec *);

你的函數必須長得像那樣才能用
例如:

void my_callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], struct timespec *timestamp)
{
     /* 接收訊息 */
}

然後這樣

cwiid_set_mesg_callback(wiimote, my_callback);

就完成設定監聽函數
當 CWiid 收到來自 Wii Remote 的資料
完成處理分析封包格式後會調用你設定的函數
把處理後的資料放在 mesg[] 這參數中
用這種方式把處理過的資料交給你
就像 framework 軟體一般
完成重要的部分, 然後留個坑讓你有改變的彈性

這樣設定後並不會有信號進來
所有 Wii Remote 上的裝置都要開啟後才會有信號進來
從按鈕到 Wii Remote 加速感測和前端 IR camera 都是需要另外開啟才有的
讓開發人員只啟動需要的部件
可能是省電考量
這些感測裝置運轉也是需要額外的電力
除此之外,加速感測和 IR camera 的資料量很大
處理起來會有點麻煩
設定開啟裝置用這條

int cwiid_set_rpt_mode(cwiid_wiimote_t *wiimote, uint8_t rpt_mode);

採用 bit mask 來設定狀態
可以寫一個 macro 來設定
像這樣

#define wii_toggle_bit(bf,b)    \
    (bf) = ((bf) & b)    \
           ? ((bf) & ~(b))    \
           : ((bf) | (b))

然後使用時...

unsigned char rpt_mode = 0;
wii_toggle_bit(rpt_mode, CWIID_RPT_IR);

if(cwiid_set_rpt_mode(wiimote, rpt_mode)){
     /* failed */
}

CWIID_RPT_IR 是 CWiid 的常數 macro
指定 IR camera 的參數
可參考 CWiid 的 header file 取得其他各部件的常數參數

裝置用完後, 關閉離開
用這

cwiid_close(wiimote);

關閉連線

這些資料可以透過 trace 原始碼來找到
自由世界的東西幾乎就是靠原始碼當文件
要加入自由世界一定要去除看原始碼的恐懼XD

如果是用 IR camera 擷取紅外線光點
傳回的會是 IR camera 看到的座標
亦即, 如果你的 Wii Remote 沒有正對螢幕
傳回的就是不正確的座標
所以...怎麼做?

一樣, 參考原始碼
拆開 Johnny Chung Lee 的白板軟體

Johnny Chung Lee > Projects > Wii
http://johnnylee.net/projects/wii/

Wiimote Whiteboard v0.3 裡的 source\windowsxp\WiimoteTest\Warper.cs
那是轉換座標的程式碼
把它改成用 C 寫
我改過的: wii_wrap.zip
自行比對一下就應該要會用 (這是基本!XD)

完成後, 讀取資料就可以做一些應用
像是遊戲或是電腦控制程式
寫程式讀取輸入後畫到畫面上
或是當成滑鼠來用 (這是高段應用...呵呵)
當成滑鼠的話就要把信號塞入視窗系統
讓視窗系統去散播給各個視窗
帶來的就是觸控板的效果

Wii Remote 可以同時抓多個點
不過視窗系統大部分只能有一個點當滑鼠用
要 "大大的修改" 才可以多點支援
這中間有一些 trade-off 要考慮
桌上型系統有 MPX 這專案

MPX
舊站 : http://wearables.unisa.edu.au/mpx/
新站(作者blog) : http://who-t.blogspot.com/

嵌入式系統的也有人做了 (不明)
所以想發 paper 的請避開這兩項,感謝 (謎XD)

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。