Windows 下有現成的藍牙程式可以控制 NXT,例如 NXT Remote--使用電腦鍵盤遙控NXT。我平常用 Linux 電腦較多,NXT 在 Linux 的資源沒有 Windows 那麼豐富,不過找到了這份簡報,裡面有一個透過藍牙讀取 NXT 電池電壓的 C 程式,測試紀錄如後。
- 電腦安裝藍牙及編譯程式:我的電腦跑 Ubuntu 12.04,但不是標準的,很多套件被我移除了。bluez 包括 Linux 的藍牙程式及工具,libbluetooth-dev 含編譯藍牙用的標頭檔,gcc 是 C compiler。
sudo apt-get install bluez libbluetooth-dev gcc
- 開啟 NXT 藍牙功能
- 搜尋藍牙設備:使用 hcitool scan,會顯示 NXT 的 MAC 位址及名稱。MAC 位址在下一步寫程式時會用到。
$ hcitool scan Scanning ... 00:16:53:17:7F:CF NXT
- 電腦藍牙程式:藍牙通訊也是用 socket,通訊協定依照 NXT Communication Protocol,可到 Bluetooth Developer Kit 下載,Mindstorms NXT 與 Arduino之間的藍牙通訊 有一些說明。複製簡報中的程式,填上 NXT 的 MAC 位址,並修正一些錯誤,如下。
/* nxt_getbattery.c */ #include <stdio.h> // Socket, used for Bluetooth socket #include <sys/socket.h> #include <sys/types.h> // Bluetooth headers #include <bluetooth/bluetooth.h> #include <bluetooth/rfcomm.h> // Global Variables int nxtSocket; int init_bluetooth(char *btAddress) { struct sockaddr_rc addr={0}; int status; /*----------------------------------------------------------- * SOCK_STREAM * Provides sequenced, reliable, two-way, connection-based * byte streams. An out-of-band data transmission * mechanism may be supported. *----------------------------------------------------------*/ // Allocate a socket nxtSocket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); // Set what type and who to connect to addr.rc_family = AF_BLUETOOTH; addr.rc_channel = (uint8_t) 1; str2ba(btAddress, &addr.rc_bdaddr); // Connect to the NXT status = connect(nxtSocket, (struct sockaddr *)&addr, sizeof(addr) ); if (status < 0) { perror("Error connecting Bluetooth"); return status; } return 0; } /**************************************************************** * * nxt get battery level * * this will get the battery level on the nxt * * returns: the battery level as an integer * ***************************************************************/ #define MAX_MESSAGE_SIZE 64 int nxt_getbattery(void) { /*------------------------------------------------------ * * direct command format: * * {length/lsb, length/msb, byte 0, byte 1... byte n} * * * * for getbatterylevel (see direct commands): * * byte 0: 0x00 * * byte 1: 0x0b * * length/lsb: 0x02, the command length is 2 bytes * *----------------------------------------------------*/ char cmd[4]={0x02, 0x00, 0x00, 0x0b}; char reply[MAX_MESSAGE_SIZE]; int result; int blevel; int replylength; int error = 0; //- send request ----------------------------------------- if ( (result = write(nxtSocket, cmd, 4)) < 0 ) { perror("error sending getbatterylevel command "); return result; } //- read reply ----------------------------------------- // get bluetooth message length if ( (result = read(nxtSocket, reply, 2)) < 0) { perror("error receiving getbatterylevel reply "); return result; } replylength = reply[0] + (reply[1] * 256); // get return package if ( (result = read(nxtSocket, reply, replylength)) < 0) { perror("error receiving getbatterylevel reply "); return result; } // quick check to make sure we got everything if (replylength != result) { fprintf(stderr, "getbatterylevel : lengths do not match : %d != %d\n", replylength, result); } /*------------------------------------------------------ * * return package format: * * {length/lsb, length/msb, byte0, byte1..., byten} * * for getbatterylevel: * * byte0: 0x02 * * byte1: 0x0b * * byte2: status byte * * byte3-4: voltage in millivolts (uword) * * length/lsb: 0x05 * *----------------------------------------------------*/ // byte 0 if (reply[0] != 0x02) { fprintf(stderr, "getbatterylevel : byte 0 : %hhx != 0x02\n", reply[0]); error = 1; } // byte 1 //if (reply[1] != 0x13) { if (reply[1] != 0x0b) { fprintf(stderr, "getbatterylevel : byte 1 : %hhx != 0x0b\n", reply[1]); error = 1; } // byte 2 if (reply[2] != 0x00) { fprintf(stderr, "getbatterylevel : byte 2, status : %hhx \n", reply[2]); error = 1; } if (error) { return -1; } // byte 3-4 blevel = reply[3] + (reply[4] * 256); return blevel; } int main (void) { // nxt brick alpha bluetooth address //char btaddress[18] = "00:16:53:01:2c:84"; char btaddress[18] = "00:16:53:17:7F:CF"; int blevel; // initiate bluetooth connection if (init_bluetooth(btaddress) < 0) { close(nxtSocket); return 1; } printf("bluetooth connected to %s \n", btaddress); // get battery level (direct command) blevel = nxt_getbattery(); if (blevel < 0) { close(nxtSocket); return 1; } printf("battery level: %.3f\n", blevel/1000.0); close(nxtSocket); return 0; } - 編譯
gcc nxt_getbattery.c -o nxt_getbattery -lm -lbluetooth
- 執行程式,NXT 會「ㄅㄧ」一聲顯示 Passkey: 1234,按橘色鈕確認會發生 Permission denied 錯誤,原來藍牙需要先配對一次。
$ ./nxt_getbattery Error connecting Bluetooth: Permission denied
- 藍牙配對 (只有第一次需要,配對過後就不需要了):正常安裝的 Linux 可能會出現小視窗讓你輸入就解決了,可是我的電腦較慢,用較陽春的 LXDE 桌面,並移除了許多套件,並不會自動出現輸入 Passkey 的小視窗。找到這篇,裡面提到許多方式,但我偏好指令行,開另一個終端機,執行:
bluetooth-agent 1234
- 最後執行成功,電壓只有 6.9 V,有點低。
$ ./nxt_getbattery bluetooth connected to 00:16:53:xx:xx:xx battery level: 6.903
沒有留言:
張貼留言