Using Bluetooth communication in C#

U

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!");
}

Add comment

Categories

Subscribe to our Newsletter

Tags