title: How to work with Xamarin.Android/iOS/Mono and SPP over Bluetooth?
body: <p>I need use SPP over Bluetooth on Xamarin.Android/iOS/Mono. This is the code I'm trying for Xamarin.Android but the behavior is the same if I go to iOS and Mono on Linux/Mac:</p>
  
  <pre><code>    using System;
  using System.Collections.Generic;
  using System.Text;
  using Android.Bluetooth;
  using Java.Util;
  using System.IO;
  using System.Threading;
  using PI.SDK.Devices.BC.Responses;
  using System.Threading.Tasks;
  
  namespace PI.SDK.Devices.BC
  {
      public class BluetoothDeviceConnectionChannel : IBCDeviceConnectionChannel
      {
          private Queue&lt;ResponseBase&gt; _dispatcher;
          private bool _abort = false;
  
          private BluetoothAdapter _adapter;
          private BluetoothSocket _socket;
          private BluetoothDevice _device;
          private static UUID _uuid = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB");
          private StreamReader _reader;
          private StreamWriter _writer;
          private string _deviceAddress;
  
          public event Action&lt;string&gt; Notify;
          public bool IsOpen { get { return _socket.IsConnected; } }
  
          public BluetoothDeviceConnectionChannel(string deviceAddress)
          {
              _adapter = BluetoothAdapter.DefaultAdapter;
              if (_adapter == null)
                  throw new PIDeviceManagerException("Bluetooth is not supported on this Android device");
  
              _deviceAddress = deviceAddress;
          }
  
          public BluetoothDeviceConnectionChannel(BluetoothDevice device) : this(device.Address) { }
  
          public void Close()
          {
              _socket.Close();
          }
  
          public bool Open()
          {
              if (!_adapter.IsEnabled)
              {
                  throw new PIDeviceManagerException("Bluetooth is not enabled");
              }
  
              _adapter.CancelDiscovery();
  
              _device = _adapter.GetRemoteDevice(_deviceAddress);
              _socket = _device.CreateRfcommSocketToServiceRecord(_uuid);
              _socket.Connect();
  
              if (_socket.IsConnected)
              {
                  _reader = new StreamReader(_socket.InputStream, Encoding.GetEncoding("Windows-1252"));
                  _writer = new StreamWriter(_socket.OutputStream, Encoding.GetEncoding("Windows-1252"));
                  _dispatcher = new Queue&lt;ResponseBase&gt;();
                  Task.Factory.StartNew(() =&gt; ReceiveData());
                  return true;
              }
  
              return false;
          }
  
          public void ReceiveData()
          {
              while (_socket != null &amp;&amp; _socket.IsConnected)
              {
                  var data = _reader.ReadToEnd();
  
                  if (string.IsNullOrWhiteSpace(data))
                      continue;
  
                  var dataBuffer = data.ToCharArray();
                  var synBuilder = new StringBuilder();
  
                  foreach (var c in dataBuffer)
                  {
                      switch (c)
                      {
                          case ControlChars.NACK:
                          case ControlChars.EOT:
  #if DEBUG
                              System.Diagnostics.Debug.WriteLine($"[PINPAD -&gt; APP] {c.ToString().Dump()}");
  #endif
                              _abort = true;
                              return;
                          case ControlChars.ACK:
  #if DEBUG
                              System.Diagnostics.Debug.WriteLine($"[PINPAD -&gt; APP] {c.ToString().Dump()}");
  #endif
                              continue;
                          case ControlChars.SYN:
                              synBuilder.Append(c);
                              break;
                          case ControlChars.ETB:
                              synBuilder.Append(c);
                              var cmdResponse = synBuilder.ToString();
  #if DEBUG
                              System.Diagnostics.Debug.WriteLine($"[PINPAD -&gt; APP] {cmdResponse.Dump()}");
  #endif
                              var response = CommandResponseParser.Parse(cmdResponse);
                              if (response != null)
                              {
                                  _dispatcher.Enqueue(response);
                              }
                              return;
                          default:
                              synBuilder.Append(c);
                              break;
                      }
                  }
              }
          }
  
          public ResponseBase SendData(string data)
          {
              _abort = false;
              try
              {
                  _writer.Write(data);
              }
              catch
              {
                  throw new PIException("Unable to send data to device");
              }
  #if DEBUG
              System.Diagnostics.Debug.WriteLine($"[APP -&gt; PINPAD] {data.Dump()}");
  #endif
  
              if (data[0] == ControlChars.CAN)
              {
                  Thread.Sleep(100);
                  return null;
              }
  
              while (!_abort)
              {
                  if (_dispatcher.Count &gt; 0)
                  {
                      var response = _dispatcher.Dequeue();
                      if (response != null)
                      {
                          if (response is PPNotifyResponse)
                          {
                              if (Notify != null &amp;&amp; Notify.GetInvocationList().Length &gt; 0)
                                  Notify(response.Message);
  
                              continue;
                          }
  
                          return response;
                      }
                  }
              }
              throw new InvalidOperationException("invalidData");
          }
  
          public ResponseBase SendData(CommandBase data)
          {
              var cmd = data.ToBCCommandString();
              return SendData(cmd);
          }
      }
  }
  </code></pre>
  
  <p>I want to achieve he same behavior of the code bellow for Windows using SerialPort class and a COMxxx port where this port, is nothing more than a Serial-Over-Bluetooth COM with the target device.</p>
  
  <pre><code>using PI.SDK.Devices.BC.Responses;
  using System;
  using System.Collections.Generic;
  using System.IO.Ports;
  using System.Text;
  using System.Threading;
  
  namespace PI.SDK.Devices.BC
  {
      public class SerialDeviceConnectionChannel : IBCDeviceConnectionChannel
      {
          private SerialPort _port;
          private Queue&lt;ResponseBase&gt; _dispatcher;
          private bool _abort = false;
  
          public event Action&lt;string&gt; Notify;
          public bool IsOpen { get { return _port.IsOpen; } }
  
          public SerialDeviceConnectionChannel(string port)
          {
              _port = new SerialPort(port, 19200, Parity.None, 8, StopBits.One);
              _port.ReadTimeout = 3 * 1000;
              _port.WriteTimeout = 3 * 1000;
              _port.Encoding = Encoding.GetEncoding(1252);
              _port.DataReceived += DataReceived;
          }
          public void Close()
          {
              _port.Close();
          }
  
          public bool Open()
          {
              while (true)
              {
                  try
                  {
                      _port.Open();
                      _port.DiscardInBuffer();
                      _port.DiscardInBuffer();
  
                      break;
                  }
                  catch { Console.WriteLine($"Trying to connect to  {_port}"); }
              }
              _dispatcher = new Queue&lt;ResponseBase&gt;();
              return _port.IsOpen;
          }
  
          private void DataReceived(object sender, SerialDataReceivedEventArgs e)
          {
              var data = _port.ReadExisting();
  
              var dataBuffer = data.ToCharArray();
              var synBuilder = new StringBuilder();
              foreach (var c in dataBuffer)
              {
                  switch (c)
                  {
                      case ControlChars.NACK:
                      case ControlChars.EOT:
  #if DEBUG
                          Console.WriteLine($"[PINPAD -&gt; APP] {c.ToString().Dump()}");
  #endif
                          _abort = true;
                          return;
                      case ControlChars.ACK:
  #if DEBUG
                          Console.WriteLine($"[PINPAD -&gt; APP] {c.ToString().Dump()}");
  #endif
                          continue;
                      case ControlChars.SYN:
                          synBuilder.Append(c);
                          break;
                      case ControlChars.ETB:
                          synBuilder.Append(c);
                          var cmdResponse = synBuilder.ToString();
  #if DEBUG
                          Console.WriteLine($"[PINPAD -&gt; APP] {cmdResponse.Dump()}");
  #endif
                          var response = CommandResponseParser.Parse(cmdResponse);
                          if (response != null)
                          {
                              _dispatcher.Enqueue(response);
                          }
                          return;
                      default:
                          synBuilder.Append(c);
                          break;
                  }
              }    
          }
  
          public ResponseBase SendData(string data)
          {
              _abort = false;
              try
              {
                  _port.Write(data);
              }
              catch
              {
                  throw new PIException("Unable to send data to device");
              }
  #if DEBUG
              Console.WriteLine($"[APP -&gt; PINPAD] {data.Dump()}");
  #endif
  
              if (data[0] == ControlChars.CAN)
              {
                  Thread.Sleep(100);
                  return null;
              }
  
              while (!_abort)
              {
                  if (_dispatcher.Count &gt; 0)
                  {
                      var response = _dispatcher.Dequeue();
                      if (response != null)
                      {
                          if (response is PPNotifyResponse)
                          {
                              if (Notify != null &amp;&amp; Notify.GetInvocationList().Length &gt; 0)
                                  Notify(response.Message);
  
                              continue;
                          }
  
                          return response;
                      }
                  }
              }
              throw new InvalidOperationException("invalidData");
          }
  
          public ResponseBase SendData(CommandBase data)
          {
              var cmd = data.ToBCCommandString();
              return SendData(cmd);
          }
      }
  }
  </code></pre>
  
  <p>This code hangs when calling _reader.ReadToEnd(); on all other platforms except on Windows. Looks like I'm not getting the response back somehow.</p>
  
  <p>Note that the Android/iOS/Mono version, must respect the serial connection configuration as stated on the ctor of the Windows classe, and the Encoding for the messages and the Serial communication, must be Windows-1252.</p>
  
  <p>Any help pointing the mistakes or how to get it work the same way as on Windows would be appreciated, since there is no SerialPort class, I'm kinda lost on those devices and looks like bluetooth comm is something obscure when talking about xamarin/mobile devices.</p>
  
  <p>Thanks!</p>
  
  <p>Best regards,
  Gutemberg</p>