Bluetooth protocol Stack The main function of the Bluetooth is a Bluetooth protocol stack. It defines and provides different types of layers and functionalities. Bluetooth can run different application over different protocol stacks, but, each one of these protocol stacks uses the same Bluetooth link and physical layers. The diagram shows a complete Bluetooth protocol stack. It shows the relationship between the protocols that use the services of other protocols when there is a payload to be transferred in the air. Anyhow, the protocols have many other relationship between the other protocols – for example, some protocols (L2CAP, TCS Binary) use the LMP to control the link manager. This article describes how to use Bluetooth Framework with Classic Bluetooth RFCOMM Protocol. If you are looking for Bluetooth Low Energy support refer to the "Bluetooth Framework and Bluetooth Low Energy GATT" article.

Table Of Contents

Bluetooth Framework and RFCOMM

The Bluetooth RFCOMM (Radio frequency communication) protocol is a simple set of transport protocols, made on top of the L2CAP protocol, providing emulated RS-232 serial ports.

You usually know RFCOMM as Serial Port Profile (SPP). But that is not true at all. Serial Port Profile is just one of many profiles that are based on Bluetooth RFCOMM protocol. Object Push Profile (OPP), File Transfer Profile (FTP) and many others Bluetooth profiles work above RFCOMM (use RFCOMM as transport protocol).

Bluetooth Framework natively supports Bluetooth Classic RFCOMM protocol in client and server modes. That means that Bluetooth Framework can act as RFCOMM client (connects to other Bluetooth enabled devices) or as RFCOMM server (accepts connection from other Bluetooth enabled devices). That also means that you do not need to create and/or use any virtual COM ports to be able to communicate with Bluetooth enabled devices.

Bluetooth Profiles (Services)

In order to use Bluetooth technology, a device must be compatible with the subset of Bluetooth profiles (often called services). A Bluetooth profile (service) is a specification regarding an aspect of Bluetooth-based wireless communication between devices.

The way a device uses Bluetooth technology depends on its profile capabilities. The profiles provide standards which manufacturers follow to allow devices to use Bluetooth in the intended manner. Each Bluetooth profile built on-top of Bluetooth protocol. Bluetooth Framework can work only with profiles that built on-top of RFCOMM protocol. There are few exceptions but those exceptions are not a part of this article

Bluetooth profile (service) is defined by something known as UUID (Universally Unique ID). UUID is an abbreviation you will see a lot in the Bluetooth world. This service's UUID tells to toher device which Bluetooth features device supports. RFCOMM connection usually established with using service's UUID.

Bluetooth enabled device may query other Bluetooth enabled device device about supported service by sending request to its SDP (Service Discovering Protocol) service. In Bluetooth Framework you can do it by calling EnumRemoteServices function. Please, refer to the BluetoothManager demo application to see a services enumeration code sample.

procedure TfmMain.btEnumServicesClick(Sender: TObject);
var
  Item: TListItem;
  Radio: TwclBluetoothRadio;
  Address: Int64;
  Res: Integer;
  Services: TwclBluetoothServices;
  i: Integer;
begin
  if lvDevices.Selected = nil then
    MessageDlg('Select device', mtWarning, [mbOK], 0)
  else begin
    ClearServices;
    Item := lvDevices.Selected;
    Radio := TwclBluetoothRadio(Item.Data);
    Address := StrToInt64('$' + Item.SubItems[0]);
    Res := Radio.EnumRemoteServices(Address, nil, Services);
    if Res <> WCL_E_SUCCESS then
      ShowError(Res)
    else begin
      if Services <> nil then begin
        try
          for i := 0 to Length(Services) - 1 do begin
            Item := lvServices.Items.Add;
            Item.Caption := Radio.ApiName;
            Item.SubItems.Add(IntToHex(Address, 12));
            Item.SubItems.Add(IntToHex(Services[i].Handle, 8));
            Item.SubItems.Add(GUIDToString(Services[i].Uuid));
            Item.SubItems.Add(IntToStr(Services[i].Channel));
            Item.SubItems.Add(Services[i].Name);
            Item.SubItems.Add(Services[i].Comment);
          end;
        finally
          Services := nil;
        end;
      end;
    end;
  end;
end;
void __fastcall TfmMain::btEnumServicesClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);
  else
  {
    ClearServices();
    TListItem* Item = lvDevices->Selected;
    TwclBluetoothRadio* Radio = (TwclBluetoothRadio*)Item->Data;
    __int64 Address = StrToInt64("$" + Item->SubItems->Strings[0]);
    TwclBluetoothServices Services;
    int Res = Radio->EnumRemoteServices(Address, NULL, Services);
    if (Res != WCL_E_SUCCESS)
      ShowError(Res);
    else
    {
      for (int i = 0; i < Services.Length; i++)
      {
        Item = lvServices->Items->Add();
        Item->Caption = Radio->ApiName;
        Item->SubItems->Add(IntToHex(Address, 12));
        Item->SubItems->Add(IntToHex((int)Services[i].Handle, 8));
        Item->SubItems->Add(Sysutils::GUIDToString(Services[i].Uuid));
        Item->SubItems->Add(IntToStr(Services[i].Channel));
        Item->SubItems->Add(Services[i].Name);
        Item->SubItems->Add(Services[i].Comment);
      }
    }
  }
}
private void btEnumServices_Click(object sender, EventArgs e)
{
    if (lvDevices.SelectedItems.Count == 0)
        MessageBox.Show("Select device", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    else 
    {
        ClearServices();
        ListViewItem Item = lvDevices.SelectedItems[0];
        wclBluetoothRadio Radio = (wclBluetoothRadio)Item.Tag;
        Int64 Address = Convert.ToInt64(Item.SubItems[1].Text, 16);
        wclBluetoothService[] Services;
        Guid g = Guid.Empty;
        Int32 Res = Radio.EnumRemoteServices(Address, g, out Services);
        if (Res != wclErrors.WCL_E_SUCCESS)
            ShowError(Res);
        else
        {
            if (Services != null)
            {
                try
                {
                    foreach(wclBluetoothService Service in Services)
                    {
                        Item = lvServices.Items.Add(Radio.ApiName);
                        Item.SubItems.Add(Address.ToString("X12"));
                        Item.SubItems.Add(Service.Handle.ToString("X8"));
                        Item.SubItems.Add(Service.Uuid.ToString());
                        Item.SubItems.Add(Service.Channel.ToString());
                        Item.SubItems.Add(Service.Name);
                        Item.SubItems.Add(Service.Comment);
                    }
                }
                finally
                {
                    Services = null;
                }
            }
        }
    }
}
Private Sub btEnumServices_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btEnumServices.Click
    If lvDevices.SelectedItems.Count = 0 Then
        MessageBox.Show("Select device", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
    Else
        ClearServices()
        Dim Item As ListViewItem = lvDevices.SelectedItems(0)
        Dim Radio As wclBluetoothRadio = CType(Item.Tag, wclBluetoothRadio)
        Dim Address As Int64 = Convert.ToInt64(Item.SubItems(1).Text, 16)
        Dim Services() As wclBluetoothService = Nothing
        Dim g As Guid = Guid.Empty
        Dim Res As Int32 = Radio.EnumRemoteServices(Address, g, Services)
        If Res <> wclErrors.WCL_E_SUCCESS Then
            ShowError(Res)
        Else
            If Services IsNot Nothing Then
                Try
                    For Each Service As wclBluetoothService In Services
                        Item = lvServices.Items.Add(Radio.ApiName)
                        Item.SubItems.Add(Address.ToString("X12"))
                        Item.SubItems.Add(Service.Handle.ToString("X8"))
                        Item.SubItems.Add(Service.Uuid.ToString())
                        Item.SubItems.Add(Service.Channel.ToString())
                        Item.SubItems.Add(Service.Name)
                        Item.SubItems.Add(Service.Comment)
                    Next Service
                Finally
                    Services = Nothing
                End Try
            End If
        End If
    End If
End Sub
void CBluetoothManagerDlg::OnBnClickedButtonEnumServices()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		ClearServices();
		int Item = lvDevices.GetNextSelectedItem(Pos);
		CwclBluetoothRadio* Radio = (CwclBluetoothRadio*)lvDevices.GetItemData(Item);
		__int64 Address = StrToInt64(lvDevices.GetItemText(Item, 1));
		wclBluetoothServices Services;
		int Res = Radio->EnumRemoteServices(Address, NULL, Services);
		if (Res != WCL_E_SUCCESS)
			ShowError(Res);
		else
		{
			if (Services.size() > 0)
			{
				try
				{
					int ndx = 0;
					for (wclBluetoothServices::iterator i = Services.begin(); i != Services.end(); i++)
					{
						LPOLESTR Guid;
						StringFromCLSID((*i).Uuid, &Guid);
						lvServices.InsertItem(ndx, Radio->GetApiName().c_str());
						lvServices.SetItemText(ndx, 1, IntToHex(Address));
						lvServices.SetItemText(ndx, 2, IntToHex((int)(*i).Handle));
						lvServices.SetItemText(ndx, 3, Guid);
						lvServices.SetItemText(ndx, 4, IntToStr((*i).Channel));
						lvServices.SetItemText(ndx, 5, (*i).Name.c_str());
						lvServices.SetItemText(ndx, 6, (*i).Comment.c_str());
						CoTaskMemFree(Guid);
						ndx ++;
					}
				}
				finally(
					Services.clear();
				)
			}
		{
	}
}


Bluetooth Framework uses wclBluetoothService structure to describe Bluetooth service information (it is also called Bluetooth Service Record or SDP Record) received from a remote Bluetooth enabled device.

TwclBluetoothService = record
  Handle: Cardinal;
  Uuid: TGuid;
  Channel: Byte;
  Name: string;
  Comment: string;
end;
public struct wclBluetoothService
{
    public UInt32 Handle;
    public Guid Uuid;
    public Byte Channel;
    public String Name;
    public String Comment;
};
typedef struct
{
	unsigned long Handle;
	GUID Uuid;
	unsigned char Channel;
	tstring Name;
	tstring Comment;
} wclBluetoothService;


  • Handle - a Bluetooth service's handle. This is an internal field and usualy means nothing for an application;
  • Uuid - this is the service's UUID, what we are looking for;
  • Channel - an RFCOMM channel number (see below);
  • Name - service's name;
  • Comment - service's comment, may contains some additional information about service implementation details.

RFCOMM Channel Number

As described above, a Bluetooth Service UUID just describes some feature (service) that is supported by a Bluetooth enabled device. It is something like TCP/IP service: HTTP, FTP, TelNet or others. You can connect to any Internet service by using its IP address and service.

However, Bluetooth device may have few services with identical UUID. For example, a device may have two Serial Port Profile (SPP) services: one for configuration and other for communication. It is similar if an Internet server has two HTTP servers running: one is public and other is for administration.

And now we have a question: how a Bluetooth Client device may select which service it wants to connect to? Again, refer to the Internet: there is something known as TCP/IP port. If an Interner server has two HTTP servers then one may run on standard HTTP port 80 and the other may run on port 8080. So when connecting to such server browser uses default port 80 when it specifies HTTP service in connection request. But we also may provide TCP/IP port (8080) and a Web browser connects to specified port.

The same appears when we connect to a Bluetooth RFCOMM service. But instead of Port in Bluetooth world it is called RFCOMM channel number (the Channel field in the wclBluetoothService structure). Each RFCOMM service has own unique RFCOMM channel number. This channel number can be hardcoded or can be dynamically assigned. But it is always unique.

When Bluetooth RFCOMM client connects to a remote Bluetooth RFCOMM server it always connects to an RFCOMM channel. If application does not provide a channel number in connection request, OS queries remote Bluetooth device about its services and tries to resolve the RFCOMM channel number by service's UUID. If there are two or more services with the same UUID OS uses first found randomly.

However, in case of few services with the same UUID an application may query device about supported service, find required one (by its name or comment) and provide RFCOMM channel number in connection request to connect to exactly required service. Or, if RFCOMM channel number is hardcoded on a device's side an application may simple provide well-known RFCOMM channel number without quering device services.

When you run a Bluetooth RFCOMM server you also provides service's UUID and (if required) RFCOMM channel number. If (by default) an application does not provide a channel number (this is recomended way) an OS assigns it automaticaly. However an application may provide fixed RFCOMM channel number. But in this case server may not start if OS already uses this channel number. The RFCOMM channel number can be any from 1 to 31 (including both).

Bluetooth Framework as RFCOMM Client

Bluetooth Framework includes wclRfCommClient class that allows to connect to a remote Bluetooth enabled device's RFCOMM based service. Each object of the wclRfCommClient class can connect to one remote Bluetooth enabled device's service. To connect to more than one Bluetooth device at the same time an application must use more than one object of the wclRfCommClient class. The code below shows how to start connection.

procedure TfmMain.btConnectClick(Sender: TObject);
var
  Res: Integer;
  Radio: TwclBluetoothRadio;
  Services: TwclBluetoothServices;
  Connect: Boolean;
  i: Integer;
  Service: TGUID;
begin
  if lvDevices.Selected = nil then
    MessageDlg('Select device', mtWarning, [mbOK], 0)
  else begin
    Radio := GetRadio;
    if Radio <> nil then begin
      wclRfCommClient.Address := StrToInt64('$' + lvDevices.Selected.Caption);
      wclRfCommClient.Authentication := cbAuthentication.Checked;
      wclRfCommClient.Encryption := cbEncryption.Checked;
      wclRfCommClient.Timeout := StrToInt(edTimeout.Text);
      wclRfCommClient.Channel := 0;
      wclRfCommClient.Service := SerialPortServiceClass_UUID;
      Connect := False;
      if cbServiceName.Checked then begin
        Service := wclRfCommClient.Service;
        Res := Radio.EnumRemoteServices(wclRfCommClient.Address,
          @Service, // Se are looking for specified services only!
          Services);
        if Res <> WCL_E_SUCCESS then
          MessageDlg('Service enumerating error: 0x' + IntToHex(Res, 8), mtError, [mbOK], 0)
        else begin
          if Length(Services) = 0 then
            ShowMessage('Services not found')
          else begin
            for i := 0 to Length(Services) - 1 do begin
              if Services[i].Name = edServiceName.Text then begin
                wclRfCommClient.Channel := Services[i].Channel;
                Connect := True;
                Break;
              end;
            end;
            if not Connect then
              ShowMessage('Service not found');
          end;
        end;
      end else
        Connect := True;
      if Connect then begin
        Res := wclRfCommClient.Connect(Radio);
        if Res <> WCL_E_SUCCESS then
          MessageDlg('Error: 0x' + IntToHex(Res, 8), mtError, [mbOK], 0)
      end;
    end;
  end;
end;
void __fastcall TfmMain::btConnectClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);
  else
  {
    TwclBluetoothRadio* Radio = GetRadio();
    if (Radio != NULL)
    {
      wclRfCommClient->Address = StrToInt64("$" + lvDevices->Selected->Caption);
      wclRfCommClient->Authentication = cbAuthentication->Checked;
      wclRfCommClient->Encryption = cbEncryption->Checked;
      wclRfCommClient->Timeout = StrToInt(edTimeout->Text);
      wclRfCommClient->Channel = 0;
      wclRfCommClient->Service = SerialPortServiceClass_UUID;
      bool Connect = false;
      if (cbServiceName->Checked)
      {
        TwclBluetoothServices Services;
        TGUID Service = wclRfCommClient->Service;
        int Res = Radio->EnumRemoteServices(wclRfCommClient->Address,
          &Service, // Se are looking for specified services only!
          Services);
        if (Res != WCL_E_SUCCESS)
          MessageDlg("Service enumerating error: 0x" + IntToHex(Res, 8), mtError, TMsgDlgButtons() << mbOK, 0);
        else
        {
          if (Services.Length == 0)
            ShowMessage("Services not found");
          else
          {
            for (int i = 0; i < Services.Length; i++)
            {
              if (Services[i].Name == edServiceName->Text)
              {
                wclRfCommClient->Channel = Services[i].Channel;
                Connect = true;
                break;
              }
            }
            if (!Connect)
              ShowMessage("Service not found");
          }
        }
      }
      else
        Connect = true;
      if (Connect)
      {
        int Res = wclRfCommClient->Connect(Radio);
        if (Res != WCL_E_SUCCESS)
          MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError, TMsgDlgButtons() << mbOK, 0);
      }
    }
  }
}
private void btConnect_Click(object sender, EventArgs e)
{
    if (lvDevices.SelectedItems.Count == 0)
        MessageBox.Show("Select device", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    else
    {
        wclBluetoothRadio Radio = GetRadio();
        if (Radio != null)
        {
            Client.Address = Convert.ToInt64(lvDevices.SelectedItems[0].Text, 16);
            Client.Authentication = cbAuthentication.Checked;
            Client.Encryption = cbEncryption.Checked;
            Client.Timeout = (UInt32)Convert.ToInt32(edTimeout.Text);
            Client.Channel = 0;
            Client.Service = wclUUIDs.SerialPortServiceClass_UUID;
            Boolean Connect = false;
            if (cbServiceName.Checked)
            {
                Guid Service = Client.Service;
                wclBluetoothService[] Services;
                Int32 Res = Radio.EnumRemoteServices(Client.Address,
                    Service, // Se are looking for specified services only!
                    out Services);
                if (Res != wclErrors.WCL_E_SUCCESS)
                    MessageBox.Show("Service enumerating error: 0x" + Res.ToString("X8"));
                else
                {
                    if (Services == null || Services.Length == 0)
                        MessageBox.Show("Services not found");
                    else
                    {
                        foreach (wclBluetoothService s in Services)
                        {
                            if (s.Name == edServiceName.Text)
                            {
                                Client.Channel = s.Channel;
                                Connect = true;
                                break;
                            }
                        }
                        if (!Connect)
                            MessageBox.Show("Service not found");
                    }
                }
            }
            else
                Connect = true;
            if (Connect)
            {
                Int32 Res = Client.Connect(Radio);
                if (Res != wclErrors.WCL_E_SUCCESS)
                    MessageBox.Show("Error: 0x" + Res.ToString("X8"), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}
Private Sub btConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btConnect.Click
    If lvDevices.SelectedItems.Count = 0 Then
        MessageBox.Show("Select device", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
    Else
        Dim Radio As wclBluetoothRadio = GetRadio()
        If Radio IsNot Nothing Then
            Client.Address = Convert.ToInt64(lvDevices.SelectedItems(0).Text, 16)
            Client.Authentication = cbAuthentication.Checked
            Client.Encryption = cbEncryption.Checked
            Client.Timeout = Convert.ToInt32(edTimeout.Text)
            Client.Channel = 0
            Client.Service = wclUUIDs.SerialPortServiceClass_UUID
            Dim Connect As Boolean = False
            If cbServiceName.Checked Then
                Dim Service As Guid = Client.Service
                Dim Services As wclBluetoothService()
                Dim Res As Int32 = Radio.EnumRemoteServices(Client.Address, Service, Services)
                If Res <> wclErrors.WCL_E_SUCCESS Then
                    MessageBox.Show("Service enumerating error: 0x" + Res.ToString("X8"))
                Else
                    If Services Is Nothing Or Services.Length = 0 Then
                        MessageBox.Show("Services not found")
                    Else
                        Dim s As wclBluetoothService
                        For Each s In Services
                            If s.Name = edServiceName.Text Then
                                Client.Channel = s.Channel
                                Connect = True
                                Exit For
                            End If
                        Next s
                        If Not Connect Then
                            MessageBox.Show("Service not found")
                        End If
                    End If
                End If
            Else
                Connect = True
            End If
            If Connect Then
                Dim Res As Int32 = Client.Connect(Radio)
                If Res <> wclErrors.WCL_E_SUCCESS Then
                    MessageBox.Show("Error: 0x" + Res.ToString("X8"), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                End If
            End If
        End If
    End If
End Sub
void CRfCommClientDlg::OnBnClickedButtonConnect()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		int Item = lvDevices.GetNextSelectedItem(Pos);

		CwclBluetoothRadio*  Radio = GetRadio();
		if (Radio != NULL)
		{
			wclRfCommClient.SetAddress(StrToInt64(lvDevices.GetItemText(Item, 0)));
			wclRfCommClient.SetAuthentication(cbAuthentication.GetCheck() != 0);
			wclRfCommClient.SetEncryption(cbEncryption.GetCheck() != 0);
			CString s;
			edTimeout.GetWindowText(s);
			wclRfCommClient.SetTimeout(StrToInt(s));

			wclRfCommClient.SetChannel(0);
			wclRfCommClient.SetService(SerialPortServiceClass_UUID);
			
			bool Connect = false;
			if (cbServiceName.GetCheck())
			{
				GUID Service = wclRfCommClient.GetService();
				wclBluetoothServices Services;
				int Res = Radio->EnumRemoteServices(wclRfCommClient.GetAddress(), &Service, Services);
				if (Res != WCL_E_SUCCESS)
					AfxMessageBox(_T("Service enumerating error: 0x") + IntToHex(Res));
				else
				{
					if (Services.size() == 0)
						AfxMessageBox(_T("Services not found"));
					else
					{
						for (wclBluetoothServices::iterator s = Services.begin(); s != Services.end(); s++)
						{
							CString str;
							edServiceName.GetWindowText(str);
							tstring strs(str);
							if ((*s).Name == strs)
							{
								wclRfCommClient.SetChannel((*s).Channel);
								Connect = true;
								break;
							}
						}
						
						if (!Connect)
							AfxMessageBox(_T("Service not found"));
					}
				}
			}
			else
				Connect = true;
			
			if (Connect)
			{
				int Res = wclRfCommClient.Connect(Radio);
				if (Res != WCL_E_SUCCESS)
					AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
			}
		}
	}
}


Once an application sets all required parameters (target device's address, service's UUID, RFCOMM channel number, Authentication and or Encryption mode) an application calls Connect() method that starts connection procedure. The only required parameter is a terget device's MAC address. By default RfCommClient connects to Serial Port Profile (SPP) with Authentication enabled and Encryption disabled. If the RFCOMM channel number (Channel property) is not set or set to zero (the default value) the OS tries to resolve it using service's UUID as described above in RFCOMM Channel Number part.

The Connect method just starts connection procedure. The returning value just indicates (if success) that the connection procedure has been started. Or that the connection procedure has not been started (in case of error). When connection procedure is completed (connection is established or is not established) the OnConnect event fires with the connection result.

procedure TfmMain.wclRfCommClientConnect(Sender: TObject;
  const Error: Integer);
begin
  if Error = WCL_E_SUCCESS then begin
    lbEvents.Items.Add('Connected');
    GetBuffers;
  end else
    lbEvents.Items.Add('Connect error: 0x' + IntToHex(Error, 8));
end;
void __fastcall TfmMain::wclRfCommClientConnect(TObject *Sender, const int Error)
{
  if (Error == WCL_E_SUCCESS)
  {
    lbEvents->Items->Add("Connected");
    GetBuffers();
  }
  else
    lbEvents->Items->Add("Connect error: 0x" + IntToHex(Error, 8));
}
void Client_OnConnect(object Sender, int Error)
{
    if (Error == wclErrors.WCL_E_SUCCESS)
    {
        lbEvents.Items.Add("Connected");
        GetBuffers();
    }
    else
        lbEvents.Items.Add("Connect error: 0x" + Error.ToString("X8"));
}
Private Sub Client_OnConnect(ByVal Sender As Object, ByVal [Error] As Integer) Handles Client.OnConnect
    If [Error] = wclErrors.WCL_E_SUCCESS Then
        lbEvents.Items.Add("Connected")
        GetBuffers()
    Else
        lbEvents.Items.Add("Connect error: 0x" + [Error].ToString("X8"))
    End If
End Sub
void CRfCommClientDlg::wclRfCommClientConnect(void* Sender, int Error)
{
	if (Error == WCL_E_SUCCESS)
	{
		lbEvents.AddString(_T("Connected"));
		GetBuffers();
	}
	else
		lbEvents.AddString(_T("Connect error: 0x") + IntToHex(Error));
}


As you can see in the code above the real connection result is passed as Error parameter into the OnConnect event handler. If the Error parameter is WCL_E_SUCCESS the connection to a remote Bluetooth enabled device has been established with success and an application can start communication. If the Error parameter has other value it indicates one of the connection error code (you can find list of all Wireless Communication Library error codes by thisu link: https://www.btframework.com/errors.htm.

Once connection is established an application can start communication with a connected Bluetooth enabled device. To send data to the connected device an application calls the Write method of the wclRfCommClient class.

procedure TfmMain.btSendClick(Sender: TObject);
var
  Ansi: AnsiString;
  Res: Integer;
  Sent: Cardinal;
begin
  Ansi := AnsiString(edText.Text);
  Res := wclRfCommClient.Write(PByte(Ansi), Length(Ansi), Sent);
  if Res <> WCL_E_SUCCESS then
    MessageDlg('Error: 0x' + IntToHex(Res, 8), mtError, [mbOK], 0)
  else
    MessageDlg('Sent: ' + IntToStr(Sent) + ' from ' + IntToStr(Length(Ansi)),
      mtInformation, [mbOK], 0);
end;
void __fastcall TfmMain::btSendClick(TObject *Sender)
{
  AnsiString Ansi = AnsiString(edText->Text);
  unsigned int Sent = 0;
  int Res = wclRfCommClient->Write(Ansi.c_str(), Ansi.Length(), Sent);
  if (Res != WCL_E_SUCCESS)
    MessageDlg("Error: 0x" + IntToHex(Res, 8), mtError,
      TMsgDlgButtons() << mbOK, 0);
  else
    MessageDlg("Sent: " + IntToStr((int)Sent) + " from " + IntToStr(Ansi.Length()),
      mtInformation, TMsgDlgButtons() << mbOK, 0);
}
private void btSend_Click(object sender, EventArgs e)
{
    Byte[] Ansi = Encoding.ASCII.GetBytes(edText.Text);
    UInt32 Sent = 0;
    Int32 Res = Client.Write(Ansi, out Sent);
    if (Res != wclErrors.WCL_E_SUCCESS)
        MessageBox.Show("Error: 0x" + Res.ToString("X8"));
    else
        MessageBox.Show("Sent: " + Sent.ToString() + " from " + Ansi.Length.ToString());
}
Private Sub btSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btSend.Click
    Dim Ansi As Byte() = System.Text.Encoding.ASCII.GetBytes(edText.Text)
    Dim Sent As UInt32 = 0
    Dim Res As Int32 = Client.Write(Ansi, Sent)
    If Res <> wclErrors.WCL_E_SUCCESS Then
        MessageBox.Show("Error: 0x" + Res.ToString("X8"))
    Else
        MessageBox.Show("Sent: " + Sent.ToString() + " from " + Ansi.Length.ToString())
    End If
End Sub
void CRfCommClientDlg::OnBnClickedButtonSend()
{
	CString s;
	edText.GetWindowText(s);
	CStringA Ansi(s);
	unsigned long Sent;
	int Res = wclRfCommClient.Write(Ansi.GetBuffer(), Ansi.GetLength(), Sent);
	if (Res != WCL_E_SUCCESS)
		AfxMessageBox(_T("Error: 0x") + IntToHex(Res));
	else
		AfxMessageBox(_T("Sent: ") + IntToStr(Sent) + _T(" from ") + IntToStr(Ansi.GetLength()));
}


When data received from the connected device the OnData event fires.

procedure TfmMain.wclRfCommClientData(Sender: TObject; const Data: Pointer;
  const Size: Cardinal);
var
  Str: AnsiString;
begin
  if Size > 0 then begin
    SetLength(Str, Size);
    CopyMemory(Pointer(Str), Data, Size);
    lbEvents.Items.Add('Received: ' + string(Str));
  end;
end;
void __fastcall TfmMain::wclRfCommClientData(TObject *Sender,
      const Pointer Data, const DWORD Size)
{
  if (Size > 0)
  {
    AnsiString Str = AnsiString((AnsiChar*)Data, Size);
    lbEvents->Items->Add("Received: " + String(Str));
  }
}
void Client_OnData(object Sender, byte[] Data)
{
    if (Data != null && Data.Length > 0)
    {
        String Str = Encoding.ASCII.GetString(Data);
        lbEvents.Items.Add("Received " + Data.Length.ToString() + " bytes: " + Str);
    }
}
Private Sub Client_OnData(ByVal Sender As Object, ByVal Data() As Byte) Handles Client.OnData
    If Data IsNot Nothing And Data.Length > 0 Then
        Dim Str As String = System.Text.Encoding.ASCII.GetString(Data)
        lbEvents.Items.Add("Received: " + Str)
    End If
End Sub
void CRfCommClientDlg::wclRfCommClientData(void* Sender, void* Data, unsigned long Size)
{
	if (Size > 0)
	{
		CStringA Str((PCHAR)Data, (int)Size);
		CString s(Str);
		lbEvents.AddString(_T("Received: ") + s);
	}
}


To find more information about using Bluetooth Framework as Bluetooth RFCOMM client refer to RfCommClient demo application from Bluetooth Framework package.

Demo Applications

That was the basic information you need to know about RFCOMM connection. For more details we recommend to take a look at RfCommClient and RfCommServer demo applications from Bluetooth Framework package. Please note that the demo applications use Serial Port Profile (SPP) service and waits text on both sides. That means that the applications converts received data into text to be able to display it to the user.