Using Bluetooth communication in C#

Bluetooth is a useful communication method especially in the era of IOT. Several devices around the house have a build-in buetooth transceiver and most of them provide really useful capabilitites to automate jobs. For example, imaging your wakeup in the morning, Your alarm clock rings and the coffe machine starts preparing the coffe. Unfortunately these automated procedures are not yet available out-of-the-box. For that reason it is really interesting to be able to create a main-device (like a PC) which will have an application that connects to the devices around the house and perform these operations. In this example we are going to see how we could implement a base bluetooth application in our PC using C#.

Let’s start

As a first step we need to create a new Net.Framework application (either WPF or Console) and add some useful nuget packages that will ease up our development. (We dont have to do everything from scratch..).
The nuget packages that we need to install are the following :

(Please note that with these packages some depedencies will be also installed)

After that we will need to create the following two classes.

    public class Bluetooth
    {      
        private BluetoothListener _listener;
        private CancellationTokenSource _cancelSource;

        public List<BluetoothClientExt> clients;
        public Action<string, string> onReceive;
        public Action<int> onClientJoin;
     

        public Bluetooth()
        {
            clients = new List<BluetoothClientExt>();
        }

        public bool Start()
        {
            BluetoothRadio myRadio = BluetoothRadio.PrimaryRadio;
            if (myRadio == null)
                return false;
            _cancelSource = new CancellationTokenSource();
            RadioMode mode = myRadio.Mode;
            _listener = new BluetoothListener(myRadio.LocalAddress, BluetoothService.SerialPort);
            _listener.Start();
            Task.Run(() => Listener(_cancelSource));
            return true;
        }

        public void Stop()
        {
            _cancelSource.Cancel();
            _listener.Stop();
            for (int i = 0; i < clients.Count; i++)
                clients[i].Stop();
        }

        public int GetConnectedClients()
        {
            int result = 0;

            for (int i = clients.Count() - 1; i >= 0; i--)
            {
                if (clients[i].isConnected == false)
                    clients.RemoveAt(i);              
                else
                    result++;               
            }
            return result;
        }

        private void OnDisconnect()
        {
            for (int i = clients.Count() - 1; i >= 0; i--)
            {
                if (clients[i].isConnected == false)
                {
                    clients.RemoveAt(i);
                    onClientJoin?.Invoke(clients.Count());
                }
            }
        }

        private void Listener(CancellationTokenSource token)
        {
            try
            {
                while (Common.Base.Instance.IsApplicationRunning == true)
                {

                    if (token.IsCancellationRequested)
                        break;

                    var client = new BluetoothClientExt(onReceive);
                    client.handler = _listener.AcceptBluetoothClient();
                    if (client.handler != null)
                    {
                        client.onDisconnect = OnDisconnect;
                        client.Start();
                        clients.Add(client);
                        onClientJoin?.Invoke(clients.Count());
                    }
                    for (int i = clients.Count() - 1; i >= 0; i--)
                    {
                        if (clients[i].isConnected == false)
                        {
                            clients.RemoveAt(i);
                            onClientJoin?.Invoke(clients.Count());
                        }
                    }
                }
            }
            catch (Exception)
            {  }

            for (int i = clients.Count() - 1; i >= 0; i--)
                clients[i].Stop();

            clients.Clear();
        }

        public void RefreshTimer(string address)
        {
            for (int i = 0; i < clients.Count(); i++)
            {
                if (clients[i].handler.RemoteEndPoint.Address.ToString() == address)
                    clients[i].RefreshTimer();
            }
        }

        public void Send(string address, string message)
        {
            for (int i = 0; i < clients.Count(); i++)
            {
                if (clients[i].handler.RemoteEndPoint.Address.ToString() == address)
                    clients[i].Send(message);
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_cancelSource != null)
                {
                    _listener.Stop();
                    _listener = null;
                    _cancelSource.Dispose();
                    _cancelSource = null;
                }
            }
        }
    }
    public class BluetoothClientExt : IDisposable
    {
        public BluetoothClient handler = null;
        public bool isConnected = false;
        private Action<string, string> onReceive;
        private CancellationTokenSource cancelSource;
        public NetworkStream stream;
        private string incomingdata = "";
        private bool ping = false;
        private Stopwatch sw = new Stopwatch();
        public Action onDisconnect;

        public BluetoothClientExt(BluetoothClient _handler)
        {
            isConnected = true;
            handler = _handler;
            cancelSource = new CancellationTokenSource();
            Task.Run(() => Listener(cancelSource));
        }

        public BluetoothClientExt(BluetoothClient _handler, Action<string, string> action)
        {
            isConnected = true;
            handler = _handler;
            this.onReceive = action;
            cancelSource = new CancellationTokenSource();
            Task.Run(() => Listener(cancelSource));
        }

        public BluetoothClientExt(Action<string, string> action)
        {
            isConnected = false;
            this.onReceive = action;
            cancelSource = new CancellationTokenSource();
        }

        public void Start()
        {
            isConnected = true;
            stream = handler.GetStream();
            stream.ReadTimeout = 2;
            stream.WriteTimeout = 20;
            Task.Run(() => Listener(cancelSource));
        }

        public void SetOnReceive(Action<string, string> action)
        {
            onReceive = action;
        }

        public void Send(string arg)
        {
            if (isConnected == false)
                return;
            incomingdata = arg;
        }

        private void PingPong(CancellationTokenSource token)
        {
            while (true)
            {
                for (int i = 0; i < 10; i++)
                {
                    if (token.IsCancellationRequested)
                        break;
                    Thread.Sleep(100);
                }
                ping = true;
            }
        }

        public void RefreshTimer()
        {
            sw.Restart();
        }

        private void Listener(CancellationTokenSource token)
        {
            string buffer = "";
            byte[] arg = new byte[1024 * 2];

            sw.Start();

            try
            {
                while (Common.Base.Instance.IsApplicationRunning == true)
                {
                    Thread.Sleep(25);
                    if (token.IsCancellationRequested)
                        break;

                    try
                    {
                        if (handler.Connected == false)
                        {
                            isConnected = false;
                            if (handler != null)
                                handler.Client.Disconnect(true);                               
                            onDisconnect?.Invoke();
                            return;
                        }

                        if (stream.DataAvailable == true)
                        {
                            var content = stream.Read(arg, 0, (1024 * 4) - 2);

                            if (content != 0)
                            {
                                buffer += ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(arg, 0, content).Replace("\n", "").Replace("\r", "")));
                                onReceive?.Invoke(handler.RemoteEndPoint.Address.ToString(), buffer);
                                buffer = "";
                            }
                        }
                        else if (incomingdata.Length != 0)
                        {
                            byte[] ar = Encoding.UTF8.GetBytes(Convert.ToBase64String(Encoding.UTF8.GetBytes(incomingdata)));

                            stream.Write(ar, 0, ar.Length);
                            stream.Write(Encoding.UTF8.GetBytes("\r\n"), 0, 2);
                            incomingdata = "";
                        }
                    }
                    catch (IOException)
                    {
                        isConnected = false;
                        if (handler != null)
                            handler.Client.Disconnect(true);
                        onDisconnect?.Invoke();
                        return;
                    }
                }
            }
            catch (Exception)
            {
                isConnected = false;
                if (handler != null)
                    handler.Client.Disconnect(true);
            }
            isConnected = false;
            onDisconnect?.Invoke();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Stop()
        {
            cancelSource.Cancel();
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (cancelSource != null)
                {
                    cancelSource.Dispose();
                    cancelSource = null;
                }
            }
        }
    }

After creating these two classes we are ready to start using our bluetooth 🙂 Let’s see a simple example.

Bluetooth bluetooth = new Bluetooth();
...
...
bluetooth.onReceive = BluetoothOnReceive;
bluetooth.onClientJoin = BluetoothOnClientJoins;
bluetooth.Start();
private void BluetoothOnReceive(string device,string arg)
{
   if (arg.Contains("-request-connect"))
   {
      bluetooth.Send(device, "-response-connect-positive");
   }
 }
private void BluetoothOnClientJoins(int clientsCount)
{
   Console.WriteLine("A new client connected!");
}

9 comments

  • Hello, very nice post, but I’m having problems with the line “Common.Base.Instance.IsApplicationRunning ”

    I don’t know what dependency is missing and visual studiojust gives me System.Data as option which is not the correct one.

    As I understand that check is there to end the task when the app is closed.

    • Hi, thanks for your comment.

      Regarding the ‘Common.Base.Instance.IsApplicationRunning’ it is a boolean that (as you said) indicates the activity of the application. If it is running it is true and during ‘application closure’ it turns to false.

      I would suggest to use a bool variable which at the startup of the application is true and during the termination turns to false. Depending of which (WPF/WinForm/Console) you are using.

  • Where does “Common.Base.Instance” come from?

    >while (Common.Base.Instance.IsApplicationRunning == true)

    • Hi, thanks for your comment.

      Regarding the ‘Common.Base.Instance.IsApplicationRunning’ it is a boolean that indicates the activity of the application. If it is running it is true and during ‘application closure’ it turns to false.

      I would suggest to use a bool variable which at the startup of the application is true and during the termination turns to false. Depending of which (WPF/WinForm/Console) you are using.

  • Common.Base.Instance.IsApplicationRunning

    Which namespace does this common come from

  • 1、i don’t know how to use after writing

    2、how to listen bluetooth in windows 10

    3、How to pair with other Bluetooth

      • Hi there, I was wondering if there is an example already? Maybe I looked over it but I would love to use this in a personal project.

Categories

Tags