發表文章

目前顯示的是 2013的文章

Bluez的藍牙配對工具程式

圖片
藍牙配對時常會困擾使用者,特別是一般的使用者。其實藍牙組織(Bluetooth SIG)也知道這個情況,所以配對的方式也有改善,以前的方式(legacy pairing, 藍牙2.0+EDR及之前)使用PIN Code,配對雙方的設備都要輸入相同的PIN Code,通常是4碼數字(規格書定義可到16位數數字)。兩個設備檢查所持有PIN Code是否相同,相同才會配對成功。但是一般使用者可能會按錯鍵,所以常常都要試很多次才會成功,對一般使用者來說經驗是不太好的。近來新的藍牙設備都已改用新的配對方式(secure simple pairing),兩個設備會先溝通一個passkey,通常是6位數的數字,然後兩個設備都會顯示這個數字,接著只要求使用者確認雙方數字是否相同即可。這樣使用者原本必須按10個鍵(4個PIN Code X 2 + 2次確認),現在只要按兩個鍵(確認鍵)就可以了。不但大量降低按錯的機會,也加快了配對的速度。 不過對於Linux的使用者來說,許多藍牙設備因為市場考量的關係,並不見得原生會支援Linux作業系統。例如,Logitech K810的鍵盤要和ubuntu配對時,就要自己費一點功夫 [1]  。這是因為設備廠商只會支援熱門的作業系統,這是很自然的事情;只是Linux系統近年來其實都有整合BlueZ這套藍牙管理程式,所以只要有依循藍牙規格書的設備,Linux就可以透過BlueZ來與之相連運作。反之,設備廠商也不需太費心專門為Linux系統做驅動,其設備就可以連上Linux系統。當然,專門做個驅動還是比較好的,會有助於使用經驗的提升。 BlueZ原生的配對工具,在Ubuntu中就是 [1] 提到的bluez-simple-agent這支程式,其實這是一支Python寫成的script,所以我們可以用more, cat, vi, gedit等指令直接看到它的內容。沒錯,它是命令列的指令用法,對大多數的使用者來說都是不易使用的。所以,其實在大部份的Linux套件中都會使用gnome-bluetooth [2] 之類的圖形界面工具。這樣一來,使用者只要點選按就可以了,自然接受度會好很多。不過,這些工具都只是界面改善(當然有些有加入其他功能),其實主要的配對工作,還是丟給BlueZ去做的。就像我在 [3] 那篇文章中提到一樣,其實只要將bluez-s

QtDBus Proxy 訪問遠端物件與回傳值 demarshall

在之前的文章 [1] 裡,我提到QtDBus在叫用DBus上的方法時,對輸入參數和輸出參數的資料型態必須做marshall和demarshall,而且有點不太容易。有些人會説,你只使用QDBusMessage來叫用方法以及接收回傳值,這不是Qt建議的作法!用QDBusMessage來做太低階了,所以參數的型態才要轉換的那麼辛苦。 因此,我要使用Qt建議的Proxy類别 [2] 做法來試試看,是不是比較容易一些。首先,之前討讑提到的輸入參數資料型態為object path時,必須自己使用QVariant::fromValue(object_path)將資料填入參數列中。使用Proxy類别做法,在使用qdbusxml2cpp工具産生需要的proxy類别後,我們可以很直觀的使用以下語法,將資料型態為QDBusObjectPath的變數,直接送入輸入參數資料型態為object path的方法中。 proxy.Method(object_path); 真是太好了,這様大家都輕鬆許多。 看一下qdbusxml2cpp工具産生的類别proxy程式,可以看到如下一段程式,原來是這個proxy類别幫我自動做了同様的轉換工作。 inline QDBusPendingReply<> Method(const QDBusObjectPath &device) { QList<QVariant> argumentList; argumentList << QVariant::fromValue(device) ; return asyncCallWithArgumentList(QLatin1String("Method"), argumentList); } 其實不管Qt是如何做到的,只要能減輕我們的工作就好了。 那麼複雜的參數型態呢?是不是也可以幫我們一點忙呢? 我們來看一下之前討讑的字典中有矩陣的問題,許多的DBus方法中,特别是取得屬性的方法,通常是叫做GetProperties(),它的回傳值都是一個字典(a{sv}),而且其中有一些值,其實是個矩陣。一様的問題,用Proxy類别的作法可以取得字典的回傳值。但我要取得其中一

QtDBus marshall and demarshall

最近在寫Qt存取DBus服務的程式,才發現要對應DBus的資料型態到Qt的資料型態,必須花點功夫,所以在此筆記一下,以免日後忘記。 首先,若要叫用DBus服務上的一個方法,基本上最簡單的方式: // 建立一個要在D-Bus上傳遞的Message QDBusMessage m = QDBusMessage::createMethodCall("org.bluez", path, interface, method); //send message QDBusConnection::systemBus().send(m); 一開始,如果很幸運地,要叫用的方法沒有輸入參數也沒有回傳值,那只要使用以上方法就可以很容易地完成。但是如果需要輸入參數的話呢?在KDE TechBase有一篇文章 [1] 提到兩個寫法: 《寫法一》 // 建立一個要在D-Bus上傳遞的Message QDBusMessage m = QDBusMessage::createMethodCall("org.bluez", path, interface, method); QList args; args.append("kde.org"); m.setArguments(args); //send message QDBusConnection::systemBus().send(m);  《寫法二》 // 建立一個要在D-Bus上傳遞的Message QDBusMessage m = QDBusMessage::createMethodCall("org.bluez", path, interface, method); m << "kde.org"; //send message QDBusConnection::systemBus().send(m); Ok, 輸入參數的資料型態是字串的時候,這様也就够了。但是如果不是字串的話呢?那就很可能會讓人找半天也不知道如何寫。例如,輸入參數的資料型態為Object Path的話,就不是

關於藍牙裝置找尋(inquiry, scan)兩三事

圖片
當我們拿到手機或平板要和藍牙耳機配對時, 配對程式做的第一件事其實是找尋(scan, inquiry)的動作, 列出周遭附近的藍牙設備名稱或MAC位址, 以供使用者選擇要和那一台配對。在Linux平台上,我們可以透過bluez來做這件事,以C語言來寫,Albert Huang的書 [1] 給了我們一個簡單的範例程式 simplescan.c : #include .... int main(int argc, char **argv) { inquiry_info *ii = NULL; int max_rsp, num_rsp; int dev_id, sock, len, flags; int i; char addr[19] = { 0 }; char name[248] = { 0 }; dev_id = hci_get_route(NULL); sock = hci_open_dev( dev_id ); if (dev_id < 0 || sock < 0) { perror("opening socket"); exit(1); } len = 8; max_rsp = 255; flags = IREQ_CACHE_FLUSH; ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info)); num_rsp = hci_inquiry (dev_id, len, max_rsp, NULL, &ii, flags); if( num_rsp < 0 ) perror("hci_inquiry"); for (i = 0; i < num_rsp; i++) { ba2str(&(ii+i)->bdaddr, addr); memset(name, 0, sizeof(name)); if ( hci_read_remote_name (sock, &(ii+i)->

python gtk+ glade and dbus work together

圖片
這篇的篇名下得有點奇怪,我也不知如何命名之,主要是想要表達整合這幾個技術: 用python語言 使用gtk+來做出圖形界面 利用glade工具來編排圖形界面的佈局,而不是用程式來寫 結合dbus來控制圖形界面 之前有看到python dbus的範例程式:參見 Python DBus教學精要 [1] 的 example-service.py 其程式主要架構為: class CLASSNAME(dbus.service.Object) @dbus.service.method("INTERFACE NAME") def METHOD_NAME(self): # do something here if __name__ == '__main__': # request dbus public name, instantiate object for dbus # gobject mainloop run 注意到我們必須建立GObject的事件處理迴圈,因為這是在寫DBus服務,而服務是不會跑完馬上停止的,而是必須一直在等待是否有客戶端的要求進來。 另外也看到可以用python配合glade來寫gtk+的圖形界面: 參見 Python Gtk glade開發GUI程式 [2] 。其中python的程式為: from gi.repository import Gtk class Handler : def onDeleteWindow ( self , * args ): Gtk . main_quit ( * args ) def onButtonPressed ( self , button ): print "Hello World!" builder = Gtk . Builder () builder . add_from_file ( "builder_example.glade" ) builder . connect_signals ( Handler ()) window = builder . get_object ( "

A2DP學習筆記

圖片
其實在PC桌面Linux已經支援藍牙的A2DP協定,包括將音樂由電腦播至藍牙耳機,也包括反方向由手機或平板將音樂傳送至電腦。 在Ubuntu 12.10的桌面環境裡,操作很容易,只要先和耳機或手機(平板)配對後,一連線成功,在電腦的設定-音效畫面就會出現設備。藍牙耳機會出現在輸出的頁籤,而手機或平板會出現在輸入頁籤。如Fig.1及Fig.2。 如果配對連線後,音效設定畫面看不到藍牙設備,請檢查/etc/bluetooth/audio.conf檔[General]區段下是否有Disable=Headset,若有請移除。 Fig.1 Fig.2 這時,打開電腦的軟體播放器,開始播放音樂,我們可以在音效設定裡(如Fig.1)選擇要將音樂由那個輸出設備播出;選到内建音效,就會由電腦的喇叭發出韾音,選到藍牙耳機,就會在藍牙耳機聽到音樂了。 而要由電腦喇叭播放手機或平板的音樂,也只要直接在手機或平板播放,在iOS裝置中會看到可以選擇那一個AirPlay裝置的小圖示。確定是放送到電腦,則在音效設定(如Fig.2)的輸入頁籤,可以看到這個手機的圖示,只要選到這個圖示,可以看到輸入等級的指示圖在左右動作。這表示手機的音樂已經傳到電腦的錄音源了,此時可以打開錄音軟體,開始錄音,然後播放錄音的結果就可以聽到由手機傳來的音樂。這好像還是有點不方便,我們要的應該是直接由電腦的喇叭發出韾音。 Ivan_wl [1] 的文章下半部份就是在討讑這個問題的解法。摘要其解決方式有二個重點: 在/etc/bluetooth/audio.conf中[General]區段要加入Enable=Source 使用Blueman套件或pulseaudio將錄音源轉至電腦喇叭  Ivan_wl比較推薦使用Blueman,因為什縻設定都不用做,但是這様一來,你原來的藍牙管理工具要留著或移除,這有時是很討厭的決定。所以來看一下怎縻用pulseaudio: 簡單版: $ pactl load-module module-loopback 20 用以上指令的意思就是載入loopback模組,它會將錄音源轉送至輸出。所以你就可以聽到由手機透過藍牙走A2DP協定過來的音樂了。你會注意到這個指令有返回一個數字,在這裡的例子是20。當你想要關掉loopback模組的時候,你會用到這個數

搞不清楚之ubuntu pulseaudio

圖片
先說了,我搞不清楚ubuntu的pulseaudio。 備忘一:如何暫時關掉pulseaudio? A: 網上有人問了也有人答了,見 how-to-temporarily-disable-pulseaudio 個人偏好第二個答案,就是去/etc/pulse/client.conf設定檔,將autospawn=yes改成no,記得前面;是備註的意思,要把它去掉。這樣當我們下指令: $ pulseaudio --kill 把pulseaudio關掉後,pulseaudio就不會再自己自動重新開啟了。 那如果我們想要打開pulseaudio,就下指令: $ pulseaudio --start 狀況一: 使用MAC機器架VirtualBox,裝ubuntu 10.04,暫時關掉pulseaudio,發現aplay還可以正常運作: $ aplay ~/音樂/01\ 慢火車.wav 正在播放 WAVE '/home/ops/音樂/01 慢火車.wav' : Signed 16 bit Little Endian, 速率 44100 Hz, 立體聲 $ aplay -Dhw:0,0 ~/音樂/01\ 慢火車.wav 正在播放 WAVE '/home/ops/音樂/01 慢火車.wav' : Signed 16 bit Little Endian, 速率 44100 Hz, 立體聲 $ aplay -L null     Discard all samples (playback) or generate zero samples (capture) default:CARD=I82801AAICH     Intel 82801AA-ICH, Intel 82801AA-ICH     Default Audio Device front:CARD=I82801AAICH,DEV=0     Intel 82801AA-ICH, Intel 82801AA-ICH     Front speakers surround40:CARD=I82801AAICH,DEV=0     Intel 82801AA-ICH, Intel 82801AA-ICH     4.0 Surround outpu

D-BUS學習筆記

圖片
我第一個看到的文章是 ali's Blog [1] , 其中提到二支程式server.c client.c,我試著去compile這二支程式, 沒什麼太大的問題就可以成功。因為這是所謂的dbus c api, 也就是low lever api,在ubuntu下只要安裝套件libdbus-1-dev, 就會安裝這個api: $ sudo apt-get install libdbus-1-dev 然後就如該文所提到的,在compile時,必須指定library: $ gcc server.c -o server -l dbus-1 此時再去compile程式,可能會發現找不到dbus.h的問題。其實檔案是存在的,只是gcc不知道去那裡找而已。我們只要加入-I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include 告訴gcc就可以了。 $ gcc -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -o server server.c -l dbus-1 如此,可以compile成功。但每次要打這麼多字,實在記不得,也容易打錯,所以使用Makefile吧! 建一個檔案,其名為Makefile,內容如下: All: server client server: server.c   gcc -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -o server server.c -l dbus-1 client: client.c   gcc -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -o client client.c -l dbus-1 我們把server.c client.c Makefile三個檔案,都放在同一個目錄,叫做dbus-test好了。這樣以後只要: $ cd dbus-test $ make 就會正確的編出server client二支可執行的程式。我們也不用去記這些很長的路徑名稱了。用過Makefile的朋友應該看得出來,其實Makefile可以再簡短一些。改成如下: All: server client