A bluetooth phone call in C# & VB.NET

Making a phone call via bluetooth in C#
Making a phone call via bluetooth in C#

Dialing a phone number using bluetooth in .NET

Did you ever want to be able to actually make a phone call with C# or VB.NET using bluetooth? Well, then I got some good news for you! In today’s blog post, we will talk about using the bluetooth technology to start dialing a phone / mobile number from within your own .NET app.

In general: Calling somebody from your .NET app can be easily done by installing a package called „32feet.NET“. Use the „BluetoothClient“ class to connect to a specific bluetooth device and finally send those commands of interest. This will instruct your phone to actually call the number and you’re done.

Sounds too hard? Well, no problem, just dive deeper into this blog post, where I will explain everything you need – for C# and VB.NET. If you are rather interested in listing / searching for bluetooth devices, feel free to take a look that blog post as well! Keep in mind, that I (almost) always provide an example application for you to download and test. Just scroll down a bit, or use the table of contents from above.

Why would you want to call somebody through .NET?

In today’s (digital) age – especially considering user experience – everything is about efficiency and intuivity. I mean, you don’t want to take 10 steps to finish a task, when it could’ve been done in like 3, right? Considering my own software, this was one of the main arguments of many customers, why they’ve chosen my software over others.

Imagine creating a CRM (customer relationship management) software, where your users would actually need to pull out there phone to call someone – meh! It would be a lot better, if they could just click a small button inside your app, to call their client, right!? I mean, for most persons this would basically be like a „standard functionality“, but I’ve seen a lot apps, where this wasn’t the case at all, so..

Installing the package – nobody wants to reinvent the wheel

Imagine, you would have to build everything yourself, before using it: Your car, your phone itself, even your whole house. That wouldn’t be pretty much amusing, right? It’s just like that for us developers. I mean, I love exploring custom things and digging codes, this is why I am a software developer in the first place, but I don’t want to build everything from scratch.

Thank god (or the devs I guess?), that there are some nice dudes and dudettes, who actually make functionality easily available for fellow developers. This will be the case for our scenario as well, as we will be using a known NuGet library. In the end, everyone has to decide himself/herself, if using existing this provide more value than creating it yourself.

Opening the NuGet Package Manager

So, please go ahead and open the NuGet package manager inside your Visual Studio application. Don’t get confused, as I’m using my german Visual Studio installation here. Just use the first tab on the left side saying like „search“.

Next, search for „32feet.NET“ and install this package inside of your existing application / project. For sure, you need to take care, that this package matches your .NET version / setup. This package should work for a usual WPF as well as a Windows Forms application.

Installing the 32feet.net bluetooth library in Visual Studio
Installing the 32feet.net bluetooth library in Visual Studio

Creating a simple user interface for our bluetooth phone call

After you’ve successfully completed the previous step, we can now focus on the UI part of our little example application. We won’t create like a complex UI, as this would be overkill and not really expedient. Actually, we will just put a simple button on our Form / Window – depending on the project type being Winforms or WPF.

Staying with the „simple“ part, I won’t create something like a ViewModel (which would be typical for WPF apps). If you don’t have a WPF project anyways – just ignore, what I’ve just said.

Please put a simple button on your Form / Window and go into the typical click event handler by double clicking the button in like Windows Forms (or using something similar, like the designer dropdown..). If you’re using WPF, just go into your XAML code and specify the „Click“ attribute for the button. This will show a little prompt, asking you, if it should create a handler for you.

We will need some sort of input for the phone number to dial as well. Please make sure, that you’ll also add some sort of textbox and a label to make the purpose of the textbox clear.

Currently your app should therefore look like something like this. For sure, you could also like create a complete example with single dialing buttons, being single digits.

Windows Forms

If you are using windows forms, take a look at the following example.

Winforms UI example

Windows Forms App UI - Dialing a phone number with C#
Windows Forms App UI – Dialing a phone number with C#

WPF

When you are however using Windows Presentation Foundation (WPF), just peek at this example. Again, keep in mind, that I’m not using MVVM, etc. for this simple example, just plain old view and code mixing.

WPF UI example

WPF App UI - Making a phone call via bluetooth in C#
WPF App UI – Making a phone call via bluetooth in C#

XAML code

<Window x:Class="WpfExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen">
    <Grid>
        <StackPanel Width="200" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Label Content="Phone number to dial" Margin="0 0 0 3" />
            <TextBox Name="tbPhoneNumber" Margin="0 0 0 6" />
            <Button Content="dial" Click="Button_Click" />
        </StackPanel>
    </Grid>
</Window>

The call itself – using the BluetoothClient class

After we have created a small user interface in the previous step, we will now fill up some code. As already explained, you should already have some sort of button click handler available. If not, just create one as usual..

The button handler

Depending on your used technology (Winforms / WPF), take care for naming the following void / sub correctly. The WPF button click handler from above was called „Button_Click“. If you are using Winforms, there should be no problem at this point.

Next, mark your handler function async, as we will be using an async variant. I mean, we don’t want to act like as we are 20 years back in time. That means, having too much synchronous code – which should be async –, to avoid lagging our app and therefore creating a bad user experience.

The BluetoothClient – building our communicator

After preparing our eventhandler, we can now create a BluetoothClient instance of the installed library. This will enable interacting with the bluetooth device. Please make sure, that the device is already paired with the computer. Now, go ahead and create the BluetoothClient (which resides in the „InTheHand.Net.Sockets“-namespace) instance.

Then we will use a specified device, to actually call the specified phone number. As mentioned above, please also refer to my blog post considering listing / searching bluetooth devices. To do so, we first need to connect to the specified device. After that, we can now obtain a stream, which enables us writing data (via Bluetooth) to the target device.

// somewhere on top / above the form
using InTheHand.Net;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;

private async void btnDialNumber_Click(object sender, EventArgs e)
{
    BluetoothAddress address;
    // if you know the device (BluetoothDeviceInfo) -> device.DeviceAddress
    // address = device.DeviceAddress;
    // if you know the address -> BluetoothAddress.Parse / TryParse
    // address = BluetoothAddress.Parse("...");
    using var bluetoothClient = new BluetoothClient();
    await Task.Run(() => bluetoothClient.Connect(address, BluetoothService.Handsfree));
    // send data to the device..
}
' somewhere on top / above the form
Imports InTheHand.Net
Imports InTheHand.Net.Bluetooth
Imports InTheHand.Net.Sockets

Private Async Sub btnDialNumber_Click(ByVal sender As Object, ByVal e As EventArgs)
    Dim address As BluetoothAddress
    ' if you know the device (BluetoothDeviceInfo) -> device.DeviceAddress
    ' address = device.DeviceAddress;
    ' if you know the address -> BluetoothAddress.Parse / TryParse
    ' address = BluetoothAddress.Parse("...");
    Using bluetoothClient = New BluetoothClient()
        Await Task.Run(Function() bluetoothClient.Connect(address, BluetoothService.Handsfree))
    End Usin
End Sub

Take care, that I’m using the newer C# syntax for the „using“ statement, which I personally prefer over the old one. I’m using the Task.Run method to offload the creation of the connection to the threadpool, making it async / non-laggy.

Calling & sending data over Bluetooth

Right now, we already finished the most important steps, so the next ones are basic operations like „do this, do that“. For sure, Bluetooth and the corresponding devices have their own „language“ / protocol for that. I’m no bluetooth command „pro“, to be honest. ..but well, I could get it working, specifying the – soon – following commands. If you made any findings yourself, feel free to leave a comment in the section below the blog post.

Next, we will get the data stream, where we can send our own text based commands. For this, we will (like most of the time, when using streams) use the Write / Flush method variants. At the end, we could read the response data and handle the different responses.

// ... previous code
private async void btnDialNumber_Click(object sender, EventArgs e)
{
    // ... previous code
    var phoneNumber = "<the phone number you want to call like 0157.....>";
    using var stream = bluetoothClient.GetStream();
    var cmds = new List<string>()
    {
        "AT+CMER\r",
        "AT+CIND=?\r",
        "AT+BRSF=\r",
        $"ATD{phoneNumber};\r"
    };
    foreach (var cmd in cmds)
    {
        Debug.WriteLine($"sending: {cmd}");
        var cmdData = Encoding.ASCII.GetBytes(cmd);
        await stream.WriteAsync(cmdData);
        await stream.FlushAsync();

        // could be coming from a pool, but for demo..
        var buffer = new byte[1024];
        var readResponseByteCount = await stream
            .ReadAsync(buffer, 0, buffer.Length);
        var responseData = buffer
            .Take(readResponseByteCount)
            .ToArray();
        var responseText = Encoding
            .ASCII
            .GetString(responseData)
            .Trim();
        // do something with the response
    }
}
Private Async Sub btnDialNumber_Click(ByVal sender As Object, ByVal e As EventArgs)
    Dim phoneNumber = "<the phone number you want to call like 0157.....>"

    Using stream = bluetoothClient.GetStream()
        Dim cmds = New List(Of String)() From {
            "AT+CMER" & vbCr,
            "AT+CIND=?" & vbCr,
            "AT+BRSF=" & vbCr,
            $"ATD{phoneNumber};\r"
        }

        For Each cmd In cmds
            Debug.WriteLine($"sending: {cmd}")
            Dim cmdData = Encoding.ASCII.GetBytes(cmd)
            Await stream.WriteAsync(cmdData)
            Await stream.FlushAsync()
            Dim buffer = New Byte(1023) {}
            Dim readResponseByteCount = Await stream.ReadAsync(buffer, 0, buffer.Length)
            Dim responseData = buffer.Take(readResponseByteCount).ToArray()
            Dim responseText = Encoding.ASCII.GetString(responseData).Trim()
        Next
    End Using
End Sub

Full code & refactoring – bluetooth phone call

If you want to make your code more reusable (or at least begin with it), I would encourage you to create a fitting class. This would increase performance (a little) as well, because we could use „ConfigureAwait(false) “ more easily. I will keep my dev comments inside the code, maybe it helps you, when you’re thinking about the code.

using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BluetoothPhoneDialExampleCS.Models;

public class BluetoothPhone
{

    public async Task CallAsync(BluetoothAddress bluetoothAddress, string phoneNumber)
    {
        //var bluetoothAddress = BluetoothAddress.Parse("E0897EE5FBEE");
        //var endPoint = new BluetoothEndPoint(bluetoothAddress, BluetoothService.Handsfree);
        using var client = new BluetoothClient();
        //await Task.Factory
        //   .FromAsync(client.BeginConnect, client.EndConnect, device.DeviceAddress, BluetoothService.Handsfree, null)
        //   .ConfigureAwait(false);
        await Task.Run(() => client.Connect(bluetoothAddress, BluetoothService.Handsfree))
            .ConfigureAwait(false);
        using var stream = client.GetStream();
        var cmds = new List<string>()
        {
            "AT+CMER\r",
            "AT+CIND=?\r",
            "AT+BRSF=\r",
            $"ATD{phoneNumber};\r"
        };
        // AT+CMER
        // enables or disables unsolicited result codes related to MT events
        // AT+CIND
        // used to get the current indicator values
        // AT+BRSF=
        // hands-free (HF) devices  report supported features and request the phone's audio gateway (AG)
        foreach (var cmd in cmds)
        {
            Debug.WriteLine($"sending: {cmd}");
            var cmdData = Encoding.ASCII.GetBytes(cmd);
            await stream
                .WriteAsync(cmdData)
                .ConfigureAwait(false);
            await stream
                .FlushAsync()
                .ConfigureAwait(false);

            // could be coming from a pool, but for demo..
            var buffer = new byte[1024];
            // SocketException 10055!
            var readResponseByteCount = await stream
                .ReadAsync(buffer, 0, buffer.Length)
                .ConfigureAwait(false);
            var responseData = buffer
                .Take(readResponseByteCount)
                .ToArray();
            var responseText = Encoding
                .ASCII
                .GetString(responseData)
                .Trim();
            Debug.WriteLine($"response: `{responseText}`");
        }
    }

}
Imports InTheHand.Net.Bluetooth
Imports InTheHand.Net.Sockets
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks

Namespace BluetoothPhoneDialExampleCS.Models
    Public Class BluetoothPhone
        Public Async Function CallAsync(bluetoothAddress As BluetoothAddress, phoneNumber As String) As Task
            Using client = New BluetoothClient()

                Await Task.Run(Function() client.Connect(bluetoothAddress, BluetoothService.Handsfree)).ConfigureAwait(False)

                Using stream = client.GetStream()
                    Dim cmds = New List(Of String)() From {
                        "AT+CMER" & vbCr,
                        "AT+CIND=?" & vbCr,
                        "AT+BRSF=" & vbCr,
                        $"ATD{phoneNumber};\r"
                    }

                    For Each cmd In cmds
                        Debug.WriteLine($"sending: {cmd}")
                        Dim cmdData = Encoding.ASCII.GetBytes(cmd)
                        Await stream.WriteAsync(cmdData).ConfigureAwait(False)
                        Await stream.FlushAsync().ConfigureAwait(False)
                        Dim buffer = New Byte(1023) {}
                        Dim readResponseByteCount = Await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(False)
                        Dim responseData = buffer.Take(readResponseByteCount).ToArray()
                        Dim responseText = Encoding.ASCII.GetString(responseData).Trim()
                        Debug.WriteLine($"response: `{responseText}`")
                    Next
                End Using
            End Using
        End Function
    End Class
End Namespace

Downloads

Are you pretty much tired off, of copying and pasting code manually? Well, I got you covered! Please feel free, to download the matching example application for your needs and test without further delay.

The projects (VB / C#) will contain WPF, as well as the Windows Forms apps.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert