簡體   English   中英

如何通過藍牙使用Xamarin.Android/iOS/Mono和SPP?

[英]How to work with Xamarin.Android/iOS/Mono and SPP over Bluetooth?

我需要在Xamarin.Android/iOS/Mono上通過藍牙使用SPP。 這是我為Xamarin.Android嘗試的代碼,但是如果我在Linux / Mac上轉到iOS和Mono,則行為是相同的:

    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<ResponseBase> _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<string> 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<ResponseBase>();
                Task.Factory.StartNew(() => ReceiveData());
                return true;
            }

            return false;
        }

        public void ReceiveData()
        {
            while (_socket != null && _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 -> APP] {c.ToString().Dump()}");
#endif
                            _abort = true;
                            return;
                        case ControlChars.ACK:
#if DEBUG
                            System.Diagnostics.Debug.WriteLine($"[PINPAD -> 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 -> 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 -> PINPAD] {data.Dump()}");
#endif

            if (data[0] == ControlChars.CAN)
            {
                Thread.Sleep(100);
                return null;
            }

            while (!_abort)
            {
                if (_dispatcher.Count > 0)
                {
                    var response = _dispatcher.Dequeue();
                    if (response != null)
                    {
                        if (response is PPNotifyResponse)
                        {
                            if (Notify != null && Notify.GetInvocationList().Length > 0)
                                Notify(response.Message);

                            continue;
                        }

                        return response;
                    }
                }
            }
            throw new InvalidOperationException("invalidData");
        }

        public ResponseBase SendData(CommandBase data)
        {
            var cmd = data.ToBCCommandString();
            return SendData(cmd);
        }
    }
}

我想使用SerialPort類和COMxxx端口實現與Windows相同的代碼行為,其中該端口不過是帶有目標設備的藍牙串行COM。

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<ResponseBase> _dispatcher;
        private bool _abort = false;

        public event Action<string> 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<ResponseBase>();
            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 -> APP] {c.ToString().Dump()}");
#endif
                        _abort = true;
                        return;
                    case ControlChars.ACK:
#if DEBUG
                        Console.WriteLine($"[PINPAD -> 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 -> 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 -> PINPAD] {data.Dump()}");
#endif

            if (data[0] == ControlChars.CAN)
            {
                Thread.Sleep(100);
                return null;
            }

            while (!_abort)
            {
                if (_dispatcher.Count > 0)
                {
                    var response = _dispatcher.Dequeue();
                    if (response != null)
                    {
                        if (response is PPNotifyResponse)
                        {
                            if (Notify != null && Notify.GetInvocationList().Length > 0)
                                Notify(response.Message);

                            continue;
                        }

                        return response;
                    }
                }
            }
            throw new InvalidOperationException("invalidData");
        }

        public ResponseBase SendData(CommandBase data)
        {
            var cmd = data.ToBCCommandString();
            return SendData(cmd);
        }
    }
}

調用_reader.ReadToEnd()時,此代碼將掛起。 在Windows以外的所有其他平台上。 好像我沒有以某種方式得到答復。

請注意,Android / iOS / Mono版本必須遵守Windows類ctor上所述的串行連接配置,並且消息和串行通信的編碼必須為Windows-1252。

指出錯誤或如何使其與Windows相同的工作方式的任何幫助將不勝感激,因為沒有Seri​​alPort類,在這些設備上我有點迷失了,當談論xamarin / mobile時,藍牙通訊似乎有些晦澀設備。

謝謝!

此致Gutemberg

找到了問題。 在Windows中,對serialPort.Read()的調用可以是異步的,而在其他線程上,在Android / iOS / Mono中則不能。

如果我剛在_writer.Write()之后開始閱讀,換句話說,在同一線程上,我可以使其正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM