WP8 - Bluetooth (A2A, A2D)

Windows Phone 8–Bluetooth (A2A, A2D)

在WP 7.1時,對於Bluetooth的API僅提供藍芽耳機的連結,無法像其他系統支援度這麼完整,

對於用戶或開發人員均是比較不好的體驗與處境。但到了WP8支援度更完整了,可透過藍芽

來進行檔案的傳輸、裝置連線之外,也可以連接指定的應用程式…等。因此,該篇將加以

說明如何操作藍芽API。

 

簡單談論一下Bluetooth,它支援10公尺內的傳輸,各設備之間透過互相被搜尋、配對、列出各設備支援的

Services,進一步取得交易連線實行Services的功能。透過這樣無線的技術,設備之間即可以交易(溝通),不

用實體連線,例如:無線藍芽耳機、無線控制器、多媒體播放控制器、控制遊戲…等。

 

 

〉WP 7.1與WP8對於Bluetooth的支援度

   在WP 8主要支持Bluetooth 3.1,支援WP 8與其他藍芽設備進行連接,包括:電話耳機、汽車音響系統、揚聲器底座,

以及NFC配對…等,以下先看一下目前WP 7與WP 8在Bluetooth支援度:

  Windows Phone 7 Windows Phone 8

Advanced Audio Distribution Profile
(audio streaming)

Yes (1.2) Yes (1.2)

Audio/Video Remote Control Profile
(controlling A/V equipment)

Yes (1.3) Yes (1.4)

Hands Free Profile
(e.g. car systems for hands-free calls)

Yes (1.5) Yes (1.5)

Headset Profile
(basic headset features)

Yes (1.1) No*

Phone Book Access Profile
(exchange of phone book data)

Yes (1.1) Yes (1.1)

Object Push Profile
(pushing files, images etc. to others)

No Yes (1.1)
Out of Band (OOB) and Near Field Communications (NFC) No Yes

Human Interface Device Profile
(mice, joysticks, keyboards etc.)

No No

參考來源:<Windows Phone 8: Bluetooth - End User Bluetooth Features in Windows Phone>。

 

 

〉WP 8支援Bluetooth二大情境

(1) App to App

     在App to App的通訊裡,一個App使用Bluetooth來探尋另一個設備上的App,當有App被發現,該App向對方發起連線的請求

     當二個App互相接受建立起連線時,一個Stream Socket被打開以支援二個App之間的溝通。

     簡單來說,即是一個App被安裝於多個設備裡,這些設備開啟了Bluetooth並且也執行該App,此時一個請求者透過該App進行搜尋

     Bluetooth無線範圍內有開啟該App的設備,選擇其中一個設備進行App資訊/訊息的傳遞。

 

(2) App to Device

     在App to Device的通訊裡,一個App使用Bluetooth來探尋配對過的Device,以發現設備後,該App向對方發連線的請求

     當App與Device互相接受建立起連線時,一個Stream Socket被打開以支援雙方之間的溝通。

     簡單來說,App to Device事先需要透過Bluetooth Panel進行Devices的配對,配對完成後,即可以在自己的App裡與這些配對過的設備

     進行交互。

     

 

那麼對於探尋的處理機制(Peer discovery)又是怎麼認定的呢?

 

〉Peer discovery

    Discovery代表在搜尋過程裡找到想要進行交互的設備或應用程式,以進行配對並完成對應的服務。

配對的過程裡,使用系統的Bluetooth Panel(藍芽控制面板)找到對應的Devices並與他們連接。配對透過Shared PIN或雙方互相同意連線。

App-to-Device情境下,只能發現已配對的Devices且它正在執行相同App

App-to-App情境下,一個App正在另一個電話的另一個instance,屬這種情境的手機彼此不需要配對即可以交互

 

 

大略了解了二種WP 8支援Bluetooth情境後,往下便加以說明如何在這二個情境下操作API,以達到服務的交互。

〉Bluetooth APIs

    WP8為了支援第三方Bluetooth開發者,延伸了二個在Windows Runtime APIs的重要類別:StreamSocketPeerFinder

更包括以下幾個重要的類別:

類別 說明

PeerFinder

Gives you the ability to discover another instance of your app on a nearby device, and create a socket connection between these peer apps. A peer app is another instance of an app running on another device.

PeerInformation

Contains info that identifies a peer app or device.

StreamSocket

Supports network communication using a TCP stream socket.

ConnectionRequestedEventArgs

Contains properties that are passed to an app by the ConnectionRequested event.

往下一一說明各類別的用途與主要方法與屬性。

 

a. PeerFinder

    該API用於允許應用程式搜尋附近也有安裝該應用程式的設備(或稱發現你的應用程式在另外設備的另一個Instance),

以建立二個Peer之間的Socket連線進行溝通,並且透過Tap gesture或Browsering

 

a-1. 重要的事件、屬性與方法

類型 名稱 說明
Event ConnectionRequested 發生於當一個遠端Peer請求連線,使用了ConnectAsync()方法。
Event TriggeredConnectionStateChanged 發生於一個遠端Peer的tap gesture期間。
Method ConnectAsync 經由呼叫FindAllPeerAsync()方法後,指定一個Peear發出ConnectAsync()請求。
Method FindAllPeersAsync 非同步瀏覽無線範圍內,正在執行相同App的對等設備。
Method Start() 啟動搜尋,以發現對等的App與使用程式發現遠端的Peers。
Method Start(String) 提供一個訊息至一個接近(proximate)設備上的App。
Method Stop 停止搜尋過程。
Attribute AllowBluetooth R/W,指定該PeerFinder類別可能使用Bluetooth連接一個StreamSocket物件。
Attribute AllowInfrastructure R/W,指定該PeerFinder類別可能使用TCP/IP連接一個StreamSocket物件。
Attribute AllowWiFiDirect R/W,指定該PeerFinder類別可能使用WiFi-Direct連接一個StreamSocket物件。
Attribute AlternateIdentities R,獲取可選用APPID值的列表,以配合其他平台上的點對點應用。
Attribute DisplayName R/W,設定/取得讓遠端Peers識別您的名稱。
Attribute SupportedDiscoveryTypes R,取得PeerFinder可使用Discovery任務的類型。

該類別是最主要操作Bluetooth APIs達成二個情境的應用,因此,需要特別注意如何使用該類別。

 

 

b. PeerInformation

    儲存可識別一個Peer的所有資訊。包括:

Property Access type Description
DisplayName Read-only Gets the display name of the peer.
HostName Read-only Windows Phone only. Gets the hostname or IP address of the peer.
ServiceName Read-only Windows Phone only. Gets the service name or TCP port number of the peer.

該類別的Instance在呼叫FindAllPeersAysnc()時被建立,等到ConnectionRequested事件被觸發時才被傳遞出來。你可以透過該Instance去進行

ConnectAsync()的方法來接受遠端Peer的請求。

 

 

c. StreamSocket

    使用TCP Stream socket提供網路通訊。在Bluetooth與其他設備/App連線之後,APIs會自動建立該Socket來進行溝通。

 

c-1. 重要事件、屬性與方法

類型 名稱 說明
Method Close 關閉StreamSocket物件。
Method ConnectAsync(EndpointPair) 在StreamSocket物件啟動一個非同步運作以連線至遠端網路目標指定的EndpointPair物件。
Method ConnectAsync(EndpointPair, SocketProtectionLevel) 在StreamSocket物件啟動一個非同步運作以連線至遠端網路目標指定的EndpointPair物件,並指定SocketProtectionLevel列舉值。這方法不支援JavaScript呼叫。
Method ConnectAsync(HostName, String) 在StreamSocket物件啟動一個非同步運作以連線至遠端網路目標指定的Host name與Service name。
Method ConnectAsync(HostName, String, SocketProtectionLevel) 在StreamSocket物件啟動一個非同步運作以連線至遠端網路目標指定的Host name與Service name,以及SocketProtectedLevel列舉值。
Method Dispose 執行釋放已佔用或被管理的資源。
Method UpgradeToSslAsync 在StreamSocket物件啟動一個非同步運作以更新一個已連線的socket支援SSL。
Property Control R,在StreamSocket物件取得socket control data。
Property Information R,在StreamSocket物件取得socket資訊。
Property InputStream R,在StreamSocket物件取得input stream來讀取來自遠端host送出的資料。
Property OutputStream R,在StreamSocket物件取得output stream來寫入資料至遠端host。

由於StreamSocket類別使用TCP Stream Socket進行網路上的溝通,因此,它與StreamSocketListener物件有所關聯,協助監聽TCP連線。

另外,當StreamSocketListener收到TCP連線請求時,即透過ConnectionReceived事件上的Socket屬性回傳StreamSocket物件。

 

典型操作StreamSocket物件的方法順序如下:

1. 建立一個StreamSocket物件;

2. 透過StreamSocket.Control屬性取得StreamSocketControl物件,並且在呼叫ConnectAsync()方法前設定該物件所需要的屬性;

3. 呼叫ConnectAsync()方法與遠端endpoint建立連線。如果需要是SSL/TLS連線方式,可透過其他ConnectAsync()方法呼叫;

    如果在傳送/接收訊息之後想要加上SSL/TLS連線,可搭配UpgradeToSslAsync()方法更新連線的方式,之後就變成SSL了。

4. 取得StreamSocket.OutputStream屬性來寫入資料至遠端host;

5. 取得StreamSocket.InputStream屬性來讀取遠端host送來的資料;

6. 呼叫Close()方法來取消或拒絕任何已配對的設備,並且釋放所有管理的資源與相關StreamSocket物件。

 

 

d. ConnectionRequestedEventArgs

    包括傳送給應用程式的所有資訊與ConnectionRequested 事件。該類別配合PeerInformation類別一起使用,用於交易過程中,

得知遠端hosti請求連接的交互狀態與處理結果。主要發生於當其他App要求連線至該設備的App時所觸發的事件

 

 

〉在WMAppManifest.xml中加入ID_CAP_PROXIMITY、ID_CAP_NETWORKING

    要操作Bluetooth API,記得在WMAppManifest.xml中宣告該App具有這二個特性的需求。

Scenario

Required capabilities

App to app

ID_CAP_PROXIMITY

App to device

ID_CAP_PROXIMITY, ID_CAP_NETWORKING

 

 

以上說明了建立一個WP8專案操作Bluetooth APIs涉及的類別與特性,透過下方範例來說明會更清楚。

[範例說明]

〉Connect to a peer app

    使用於App與App之間的連接,以監聽連線的請求、確認Bluetooth功能是否被啟用。

   1: // 執行App to App的任務。
   2: async void AppToApp()
   3: {        
   4:     // PeerFinder.Start() 用來宣告本身的存在,使開啟相同App的人可以搜尋到自己;
   5:     // 該方法必須在FindAllPeersAsync()之前被呼叫。
   6:     PeerFinder.Start();
   7:  
   8:     var peers = await PeerFinder.FindAllPeersAsync();
   9:  
  10:     if (peers.Count == 0)
  11:     {
  12:         Debug.WriteLine("Peer not found.");
  13:     }
  14:     else
  15:     {
  16:         // 選擇一個Peer進行交互。
  17:         PeerInformation selectedPeer = peers[0];
  18:  
  19:         // 請求連線。
  20:         var streamSocket = await PeerFinder.ConnectAsync(selectedPeer);
  21:  
  22:         // 取得StreamSocket後,往下進行必要的Send或Read。
  23:         DoSomethingUseful(streamSocket);
  24:     }
  25: }

如果想要監聽連線的請求,記得加上「Windows.Networking.Proximity.PeerFinder.ConnectionRequested += ConnectionRequested; 」的監聽。

配合如下範例:

   1: private void ConnectionRequested(object sender,
   2:     Windows.Networking.Proximity.ConnectionRequestedEventArgs e)
   3: {
   4:     // 取得目前交易連線的Remote host name與相關PeerInformation物件。
   5:     WriteMessageText("Connection requested by " + 
   6:         requestingPeer.DisplayName + ". " +
   7:         "Click 'Accept Connection' to connect.");
   8: }

 

 

〉Connect to a device

   與App to App不同在於to device需要事先配對,所以記得先透過Bluetooth panel(設定/藍芽)找到需要配對的設備進行配備。

接著才透過PeerFinder.FindAllPeersAsync()取得已配對的設備,進行交互。可透過設定選擇Peer識別的類型:

AlternateIdenties setting

Notes

PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";

列出所有已配對的設備。將影響 PeerInformation.ServiceName 值均為空白。

對於許多的設備,Bluetooth port是hard code的,所以可直接透過ConnectAsync()進行請求。

如果設備是advertising service,可以列舉出配對選項,但呼叫ConnectAsyc()需搭配Serivce的GUID識別,以建立連線。

PeerFinder.AlternateIdentities["Bluetooth:SDP"] = "<SDP service guid>";

利用Service discovery protocol (SDP)找尋設備與Serivce的GUID識別值。

如果有設備被找到,PeerInformation.ServiceName 將等於指定的GUID值。

 

   1: // Note: 只能瀏覽與連接被配對過的設備。
   2: private async void AppToDevice()
   3: {
   4:    // 設定PeerFinder要搜尋所有配對過的設備。
   5:    PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
   6:  
   7:    var pairedDevices = await PeerFinder.FindAllPeersAsync();
   8:  
   9:    if (pairedDevices.Count == 0)
  10:    {
  11:       Debug.WriteLine("No paired devices were found.");
  12:    }
  13:    else
  14:    {
  15:       // 選擇已配對過的設備。
  16:       PeerInformation selectedDevice = pairedDevices[0];
  17:  
  18:       // 企圖建立連線。
  19:       StreamSocket socket = new StreamSocket();
  20:       // 確認 ID_CAP_NETWORKING 被宣告於 WMAppManifest.xml,否則將有Exception。
  21:       // 往下範例呼叫ConnectAsycn()指定第二個參數,
  22:       // 第二個參數為 RFCOMM port number,值的範圍: 1 to 30.
  23:       await socket.ConnectAsync(selectedDevice.HostName,"1");
  24:  
  25:       // 取得連線後進行傳送或讀取資訊
  26:       DoSomethingUseful(socket);
  27:    }
  28: }

需注意ConnectAsync()的方法,支援多載的方式,第二個參數的使用可依照需求來搭配RFCOMM使用。

 

RFCOMM:擷取來源<RFCOMM - 百度百科>

一个基于欧洲电信标准协会ETSI07.10规程的串行线性仿真协议。此协议提供RS232控制和状态信号,如基带上的损坏,CTS以及数据信号等,

为上层业务(如传统的串行线缆应用)提供了传送能力。

RFCOMM是一个简单传输协议,其目的为了解决如何在两个不同设备上的应用程序之间保证一条完整的通信路径,并在它们之间保持一通信段的问题。

 

〉識別是否有開啟Bluetooth

    使用PeerFinder.FindAllPeersAsync()記得加上try…catch,以擷取如果未開啟Bluetooth,將會收到 0x8007048F 的錯誤代碼。

   1: private async void FindPaired()
   2: { 
   3:    // 指定搜尋所有配對過的設備
   4:    PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
   5:  
   6:    try
   7:    {
   8:        var peers = await PeerFinder.FindAllPeersAsync();
   9:    }
  10:    catch (Exception ex)
  11:    {
  12:        // 如果未開啟Bluetooth,將會收到 0x8007048F 的錯誤代碼。
  13:        if ((uint)ex.HResult == 0x8007048F)
  14:        {
  15:            MessageBox.Show("Bluetooth is turned off");
  16:        }
  17:    }
  18: }

 

上述已介紹完如果操作PeerFinder完成App2App與App2Device,往下即透過實際的代碼來進行介紹。

 

 

[範例程式]

(A) 實作App to App:由於Emulator不支援Bluetooth,請準備二台WP 8的設備

(A-1). 設定一個易識別的名稱,註冊ConnectionRequested事件,啟動PeerFinder.Start()

   1: protected override void OnNavigatedTo(NavigationEventArgs e)
   2: {
   3:     // 維護Peer資料清單,並且Binding至List控件
   4:     _peerApps = new ObservableCollection<PeerAppInfo>();
   5:     PeerList.ItemsSource = _peerApps;
   6:  
   7:     // 註冊ConnectionRequested事件,以監聽連線進來的請求
   8:     PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;
   9:  
  10:     // 開始宣傳自己,讓其他人可以找到自己
  11:     PeerFinder.DisplayName = App.ChatName;
  12:     PeerFinder.Start();
  13:  
  14:     RefreshPeerAppList();
  15:  
  16:     base.OnNavigatedTo(e);
  17: }

 

(A-2). 取得搜尋的結果,並且更新於畫面的ListBox中

   1: #region 處理發現到的Peers資訊
   2: /// <summary>
   3: /// 非同步呼叫任務,負責重新填寫ListBox的項目。
   4: /// </summary>
   5: private async void RefreshPeerAppList()
   6: {
   7:     try
   8:     {
   9:         StartProgress("finding peers ...");
  10:         var peers = await PeerFinder.FindAllPeersAsync();
  11:  
  12:         // 經由清除後端資訊,以有效地清掉ListBox中的項目
  13:         _peerApps.Clear();
  14:  
  15:         if (peers.Count == 0)
  16:         {
  17:             tbPeerList.Text = "沒有找到Peers。請點擊'更新peers'進行更新!";
  18:         }
  19:         else
  20:         {
  21:             tbPeerList.Text = String.Format("Peers found: {0}. 請點擊'更新peers'進行更新!", peers.Count);
  22:             // 增加每一個Peers至清單中
  23:             foreach (var peer in peers)
  24:             {
  25:                 _peerApps.Add(new PeerAppInfo(peer));
  26:             }
  27:  
  28:             // 判斷如果只有一個Peer,預設選擇第一個項目
  29:             if (PeerList.Items.Count == 1)
  30:                 PeerList.SelectedIndex = 0;
  31:         }
  32:     }
  33:     catch (Exception ex)
  34:     {
  35:         if ((uint)ex.HResult == ERR_BLUETOOTH_OFF)
  36:         {
  37:             var result = MessageBox.Show("未開啟藍芽連線能力", "錯誤", MessageBoxButton.OKCancel);
  38:             if (result == MessageBoxResult.OK)
  39:             {
  40:                 ConnectionSettingsTask connectionSettingsTask = new ConnectionSettingsTask();
  41:                 connectionSettingsTask.ConnectionSettingsType = ConnectionSettingsType.Bluetooth;
  42:                 connectionSettingsTask.Show();
  43:             }
  44:         }
  45:         else if ((uint)ex.HResult == ERR_MISSING_CAPS)
  46:         {
  47:             MessageBox.Show("缺少必要的Capability");
  48:         }
  49:         else if ((uint)ex.HResult == ERR_NOT_ADVERTISING)
  50:         {
  51:             MessageBox.Show("You are currently not advertising yourself, i.e., a call to PeerFinder.Start() must proceed FindAllPeersAsync()");
  52:         }
  53:         else
  54:         {
  55:             MessageBox.Show(ex.Message);
  56:         }
  57:     }
  58:     finally
  59:     {
  60:         StopProgress();
  61:     }
  62: }
  63: #endregion

     由於操作PeerFinder.FindPeersAll(),可能會有些額外的Exception要記得處理;

 

(A-3). 接收其他設備請求連結的處理

   1: void PeerFinder_ConnectionRequested(object sender, ConnectionRequestedEventArgs args)
   2: {
   3:     try
   4:     {
   5:         this.Dispatcher.BeginInvoke(() =>
   6:         {
   7:             // 如果用戶想要連接進來的請求,先詢問看看
   8:             var result = MessageBox.Show(
   9:                 String.Format("Do you want to chat with {0}?", args.PeerInformation.DisplayName),
  10:                 "Incoming Chat Request", MessageBoxButton.OKCancel);
  11:             if (result == MessageBoxResult.OK)
  12:             {
  13:                 // 請求連線至Peer
  14:                 ConnectToPeer(args.PeerInformation);
  15:             }
  16:             else
  17:             {
  18:                 // Currently no method to tell the sender that the connection was rejected.
  19:             }
  20:         });
  21:     }
  22:     catch (Exception ex)
  23:     {
  24:         MessageBox.Show(ex.Message);
  25:         CloseConnection(true);
  26:     }
  27: }

 

(A-4). 建立連線的處理

   1: async void ConnectToPeer(PeerInformation peer)
   2: {
   3:     try
   4:     {
   5:         _socket = await PeerFinder.ConnectAsync(peer);
   6:  
   7:         // 關閉宣傳自己,已保護電池的壽命
   8:         PeerFinder.Stop();
   9:  
  10:         _peerName = peer.DisplayName;
  11:         UpdateChatBox("Joined Conversation", true);
  12:  
  13:         // 啟動對話後,開始監聽進入與送出的訊息. 
  14:         // 監聽進入的訊息.
  15:         ListenForIncomingMessage();
  16:     }
  17:     catch (Exception ex)
  18:     {
  19:         MessageBox.Show(ex.Message);
  20:         CloseConnection(false);
  21:     }
  22: }

 

(A-5). 處理收到的訊息

     透過取得StreamSocket物件的InputStream後,進行訊息內容的拆解。

訊息結構分成二個部分:第一個部分為訊息長度;第二個部分為訊息內容。透過DataReader類別根據訊息bytes的位移處理,取得內容與長度。

   1: #region 讀取訊息
   2: private DataReader _dataReader;
   3: /// <summary>
   4: /// 監聽Peer送進入的訊息。
   5: /// </summary>
   6: private async void ListenForIncomingMessage()
   7: {
   8:     try
   9:     {
  10:         var message = await GetMessage();
  11:  
  12:         // Add to chat
  13:         UpdateChatBox(message, true);
  14:  
  15:         // 開始監聽下一個訊息,透過遞回的方式來進行監聽
  16:         ListenForIncomingMessage();
  17:     }
  18:     catch (Exception)
  19:     {
  20:         UpdateChatBox("Chat Ended", true);
  21:         CloseConnection(true);
  22:     }
  23: }
  24:  
  25: /// <summary>
  26: /// 讀取訊息內容。
  27: /// </summary>
  28: /// <returns></returns>
  29: private async Task<string> GetMessage()
  30: {
  31:     if (_dataReader == null)
  32:         _dataReader = new DataReader(_socket.InputStream);
  33:  
  34:     // 每一個訊息包括二個部分:
  35:     // 第一部分:是訊息的長度;
  36:     // 第二部分:為訊息的本身;            
  37:     //var len = await GetMessageSize();
  38:     await _dataReader.LoadAsync(4);
  39:     uint messageLen = (uint)_dataReader.ReadInt32();
  40:     await _dataReader.LoadAsync(messageLen);
  41:     return _dataReader.ReadString(messageLen);
  42: }
  43: #endregion

 

(A-6). 處理發送訊息給指定的Peer

     取得StreamSocket.OutputStream,透過DataWriter將要送出的訊息,透過固定的格式輸出。

訊息結構分成二個部分:第一個部分為訊息長度;第二個部分為訊息內容。讀取分二段,輸出一樣分成二段。

   1: #region 發送訊息
   2: private DataWriter _dataWriter;
   3: /// <summary>
   4: /// 發送訊息至指定的Peer。
   5: /// </summary>
   6: /// <param name="message"></param>
   7: private async void SendMessage(string message)
   8: {
   9:     if (message.Trim().Length == 0)
  10:     {
  11:         MessageBox.Show("Please enter a message to send.", "Can't send", MessageBoxButton.OK);
  12:         return;
  13:     }
  14:  
  15:     if (_socket == null)
  16:     {
  17:         MessageBox.Show("You are not connected to a Peer.", "Can't send", MessageBoxButton.OK);
  18:         return;
  19:     }
  20:  
  21:     if (_dataWriter == null)
  22:         // 取得StreamSocket的OutputStream進行傳送
  23:         _dataWriter = new DataWriter(_socket.OutputStream);
  24:  
  25:     // 每一個訊息有二個部分
  26:     // 第一個部分:訊息的Size;
  27:     // 第二個部分:訊息的本身;
  28:     _dataWriter.WriteInt32(message.Length);
  29:     await _dataWriter.StoreAsync();
  30:  
  31:     _dataWriter.WriteString(message);
  32:     await _dataWriter.StoreAsync();
  33:  
  34:     UpdateChatBox(message, false);
  35: }
  36: #endregion

 

處理App to App時,有時在配對上會比較不順利,藍芽很容易被關閉,可能是為了省電,建議在測試時,

(1) 先把節電模式關閉;(2) 不要呼叫PeerFinder.Stop();可以有比較好的測試效果。二個App之間即可以溝通。

 

======

(B) 實作App to Device

      該範例用於練習與已配對過的設備進行溝通,需注意在建立ConnectAsync()時,多使用第二個參數為service name,

根據RFCOMM協定,可以指定 1~30的值,這樣就可以與設備進行溝通了。

 

(B-1). 設定AlternateIdentities["Bluetooth:Paired"],呼叫PeerFinder.FindPeersAll()方法

   1: /// <summary>
   2: /// 非同步呼叫任務,負責重新填寫ListBox的項目。
   3: /// </summary>
   4: private async void RefreshPairedDevicesList()
   5: {
   6:     try
   7:     {
   8:         // 搜尋所有已配對過的設備
   9:         PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
  10:         var peers = await PeerFinder.FindAllPeersAsync();
  11:  
  12:         // 經由清除後端資訊,以有效地清掉ListBox中的項目
  13:         _pairedDevices.Clear();
  14:  
  15:         if (peers.Count == 0)
  16:         {
  17:             MessageBox.Show("No paired devices found");
  18:         }
  19:         else
  20:         {
  21:             // 找到已配對過的設備,加入清單中
  22:             foreach (var peer in peers)
  23:             {
  24:                 _pairedDevices.Add(new PairedDeviceInfo(peer));
  25:             }
  26:         }
  27:     }
  28:     catch (Exception ex)
  29:     {
  30:         if ((uint)ex.HResult == 0x8007048F)
  31:         {
  32:             var result = MessageBox.Show("Bluetooth is turned off. To see the current Bluetooth settings tap 'ok'.", "Bluetooth Off", MessageBoxButton.OKCancel);
  33:             if (result == MessageBoxResult.OK)
  34:             {
  35:                 ConnectionSettingsTask connectionSettingsTask = new ConnectionSettingsTask();
  36:                 connectionSettingsTask.ConnectionSettingsType = ConnectionSettingsType.Bluetooth;
  37:                 connectionSettingsTask.Show();
  38:             }
  39:         }
  40:         else if ((uint)ex.HResult == 0x80070005)
  41:         {
  42:             MessageBox.Show("To run this app, you must have ID_CAP_PROXIMITY enabled in WMAppManifest.xaml");
  43:         }
  44:         else
  45:         {
  46:             MessageBox.Show(ex.Message);
  47:         }
  48:     }
  49: }

     由於指定了PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "",則取得已配對過的設備來進行選擇與連線。

 

(B-2). 指定端設備的HostName與ServiceName,以請求連線

   1: private async void ConnectToDevice(PeerInformation peer)
   2: {
   3:     if (_socket != null)
   4:     {
   5:         // 如果Socket不等於null,則要關閉與釋放所有相關的資源
   6:         _socket.Dispose();
   7:     }
   8:  
   9:     try
  10:     {
  11:         // 建立StreamSocket物件
  12:         // 在ServiceName的部分如果設備有提供用設備的,如果沒有則手動輸入,可輸入1~30
  13:         _socket = new StreamSocket();
  14:         string serviceName = (String.IsNullOrWhiteSpace(peer.ServiceName)) ? tbServiceName.Text : peer.ServiceName;
  15:  
  16:         // 如果這二個傳入的參數有空白或null,則會有Exception
  17:         await _socket.ConnectAsync(peer.HostName, serviceName);
  18:  
  19:         // 如果連線成功,取得連線資訊與顯示遠端設備名稱
  20:         MessageBox.Show(String.Format("Connected to {0}!", _socket.Information.RemoteAddress.DisplayName));
  21:     }
  22:     catch (Exception ex)
  23:     {
  24:         MessageBox.Show(ex.Message);
  25:         _socket.Dispose();
  26:         _socket = null;
  27:     }
  28: }

 

 

以上是介紹有關App to App與App to Device中程式碼比較重要部分,詳細的Source Code可透過下方連結取得:

 

======

[補充]

1. 補捉WP8針對Bluetooth APIs操作時可能拋出的Exception:

    ‧0x8007048F:代表Bluetooth狀態為Off(未開啟);

    ‧0x80070005:代表WMAppManifest.xml中缺少必要的capability;

    ‧0x8000000E:代表目前非宣傳自己的配對,不能使用PeerFinder.Start();

======

在撰寫這篇文章時,我只在意WP 8能支援Bluetooth到何種程度,因為WP 7的Bluetooth應用程度不大,

因為我也不是撰寫硬件整合的開發人員,所以覺得其他OS能支援的標準在WP 8也能達到,就可以讓

我們開發人員完成更多的事,好在沒有讓我們失望。希望該篇有助於操作Bluetooth API,謝謝。

 

References

Bluetooth for Windows Phone 8 (必讀)

Introduction to Bluetooth support on Windows Phone 8 (必讀, 有完整nokia教學)

Windows Phone 8: Bluetooth(必讀)

透過藍牙分享檔案 (手機的使用方法)

Getting started with Bluetooth in Windows Phone 8

What does the Imperial March, Windows Phone 8, Netduino, Bluetooth and Sonar have in common? This!

Windows Phone 8: Networking, Bluetooth, and NFC Proximity for Developers (教學影片)

Bluetooth app to app sample

Bluetooth app to device sample

Proximity for Windows Phone 8

Proximity and tapping (Windows Store apps using C#/VB/C++ and XAML)

 

Dotblogs Tags: