蓝牙不断写入特性

时间:2018-03-30 01:39:33

标签: android bluetooth xamarin.android

我有2个Android应用程序。其中一个通过蓝牙宣传UUID,并提供写入特性。另一个扫描设备,宣传此UUID,连接到它,然后写入其2个特征。

基本上,我需要宣传UUID的设备/应用程序(我现在称之为“服务器应用程序”),以便始终知道其他设备/应用程序何时出现,以及我正在编写的UUID至。我必须为UUID设置2个写入特性,因为它太长而不适合1,所以我在第一个特征中写入UUID的前半部分,在第二个特征中写下半部分。

所以应用程序的流程是这样的:

  1. “服务器应用”广告UUID
  2. 其他应用会扫描广告UUID
  3. 设备属于蓝牙范围
  4. 正在扫描的应用找到广告UUID,然后连接到该设备。
  5. 然后,应用程序将自己的UUID写入2个特征。
  6. “服务器应用程序”接收UUID并将其显示在屏幕上。
  7. 该应用程序在蓝牙范围内连续将其UUID写入2个特征,即使在超出范围后再将其带回范围内。
  8. 我工作的是前6个步骤。我无法让第7步工作。

    我遇到的问题是,在将UUID写入2个特征之​​后,它将不再写入它。因此,“服务器应用程序”将不知道其他设备/应用程序是否存在。我必须终止应用程序(执行扫描)并重新启动它,以便它可以连接到其他设备/应用程序并再次写入特征。

    当他们彼此靠近时,我需要不断地写出特征(两者之间有一些延迟很好)。即使设备分开的距离大于蓝牙范围,然后再将它们带回来。我在Xamarin中编写了这些应用程序,但我认为它一般适用于Android。这是我发布UUID的应用程序的代码:

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        private static Java.Util.UUID AdvertiseUUID = Java.Util.UUID.FromString("59ed0f55-e984-4fc2-9403-b1e64269ec5e");
        private static Java.Util.UUID ServiceUUID = Java.Util.UUID.FromString("89c9a1f2-5c38-492d-b8e9-67830268682e");
        private static Java.Util.UUID WriteCharacteristic1UUID = Java.Util.UUID.FromString("92515af8-8d70-40a7-b5b1-5a5bd624e5a0");
        private static Java.Util.UUID WriteCharacteristic2UUID = Java.Util.UUID.FromString("71d640cb-bb78-45bd-ae26-614fead76efe");
    
        BluetoothLeAdvertiser advertiser;
        BluetoothGattServer gattServer;
        BluetoothGattService service;
        MyAdvertiseCallback callback = new MyAdvertiseCallback();
    
        private static string writeText1;
        private static string writeText2;
    
        private static List<PassengerDevice> passengerDevices = new List<PassengerDevice>();
    
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;
    
            base.OnCreate(bundle);
    
            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
    
            AndroidBluetoothServer();
        }
    
        private async Task AndroidBluetoothServer()
        {
            try
            {
                CreateGattServer();
    
                advertiser = BluetoothAdapter.DefaultAdapter.BluetoothLeAdvertiser;
    
                var advertiseBuilder = new AdvertiseSettings.Builder();
                var parameters = advertiseBuilder.SetConnectable(true)
                                                 .SetAdvertiseMode(AdvertiseMode.LowLatency)
                                                 .SetTxPowerLevel(AdvertiseTx.PowerHigh)
                                                 .Build();
    
                AdvertiseData data = (new AdvertiseData.Builder()).AddServiceUuid(new ParcelUuid(AdvertiseUUID)).Build();
                advertiser.StartAdvertising(parameters, data, callback);
            }
            catch(Exception e)
            {
    
            }
        }
    
        private void CreateGattServer()
        {
            service = new BluetoothGattService(ServiceUUID, GattServiceType.Primary);
    
            BluetoothGattCharacteristic writeCharacteristic1 = new BluetoothGattCharacteristic(WriteCharacteristic1UUID, GattProperty.WriteNoResponse, GattPermission.Write);
            BluetoothGattCharacteristic writeCharacteristic2 = new BluetoothGattCharacteristic(WriteCharacteristic2UUID, GattProperty.WriteNoResponse, GattPermission.Write);
    
            service.AddCharacteristic(writeCharacteristic1);
            service.AddCharacteristic(writeCharacteristic2);
    
            var bluetoothManager = GetSystemService(BluetoothService) as BluetoothManager;
            gattServer = bluetoothManager.OpenGattServer(BaseContext, new MyGattServerCallback());
            gattServer.AddService(service);
        }
    
        public class MyGattServerCallback : BluetoothGattServerCallback
        {
            public override void OnCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, bool preparedWrite, bool responseNeeded, int offset, byte[] value)
            {
                base.OnCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
    
                var str = System.Text.Encoding.Default.GetString(value);
    
                var passengerDevice = passengerDevices.FirstOrDefault(d => d.Address == device.Address);
    
                if(passengerDevice != null)
                {
                    if (characteristic.Uuid == WriteCharacteristic1UUID)
                    {
                        passengerDevice.Text1 = str;
                    }
                    else if (characteristic.Uuid == WriteCharacteristic2UUID)
                    {
                        passengerDevice.Text2 = str;
                        passengerDevice.RecievedTimestamp = DateTime.Now;
                    }
                }
                else
                {
                    var newDevice = new PassengerDevice();
                    newDevice.Address = device.Address;
    
                    if (characteristic.Uuid == WriteCharacteristic1UUID)
                    {
                        newDevice.Text1 = str;
                    }
                    else if (characteristic.Uuid == WriteCharacteristic2UUID)
                    {
                        newDevice.Text2 = str;
                    }
    
                    passengerDevices.Add(newDevice);
                }
    
                App2.Class1.SetText(passengerDevices);
            }
        }
    
        public class MyAdvertiseCallback : AdvertiseCallback
        {
            public override void OnStartFailure([GeneratedEnum] AdvertiseFailure errorCode)
            {
                base.OnStartFailure(errorCode);
            }
    
            public override void OnStartSuccess(AdvertiseSettings settingsInEffect)
            {
                base.OnStartSuccess(settingsInEffect);
            }
        }
    
        protected override void OnDestroy()
        {
            try
            {
                if(advertiser != null)
                {
                    advertiser.StopAdvertising(callback);
                    advertiser.Dispose();
                    advertiser = null;
                }
    
                if (service != null)
                {
                    service.Dispose();
                    service = null;
                }
    
                if (gattServer != null)
                {
                    gattServer.Close();
                    gattServer.Dispose();
                    gattServer = null;
                }
            }
            catch(Exception e)
            {
    
            }
            finally
            {
                base.OnDestroy();
            }
        }
    }
    

    这是我的应用程序代码,它扫描UUID并写入特征:

    public class AndroidBluetooth
    {
        private static Java.Util.UUID AdvertiseUUID = Java.Util.UUID.FromString("59ed0f55-e984-4fc2-9403-b1e64269ec5e");
        private static Java.Util.UUID ServiceUUID = Java.Util.UUID.FromString("89c9a1f2-5c38-492d-b8e9-67830268682e");
        private static Java.Util.UUID WriteCharacteristic1UUID = Java.Util.UUID.FromString("92515af8-8d70-40a7-b5b1-5a5bd624e5a0");
        private static Java.Util.UUID WriteCharacteristic2UUID = Java.Util.UUID.FromString("71d640cb-bb78-45bd-ae26-614fead76efe");
    
        private ScanCallback _scanCallback;
        private ScanSettings _scanSettings;
        private ScanFilter _scanFilter;
        private MyBluetoothGattCallback _gattCallback;
        private static ParcelUuid _parcelUuid;
    
        public static bool IsStarted { get; private set; }
    
        public void StartAndroidBluetoothClient()
        {
            IsStarted = true;
    
            _parcelUuid = new ParcelUuid(AdvertiseUUID);
    
            _gattCallback = new MyBluetoothGattCallback();
    
            _scanCallback = new MyScanCallback() { GattCallback = _gattCallback };
    
            _scanFilter = new ScanFilter.Builder().SetServiceUuid(new ParcelUuid(AdvertiseUUID))
                                                  .Build();
    
            _scanSettings = new ScanSettings.Builder().SetScanMode(Android.Bluetooth.LE.ScanMode.LowLatency)
                                                      .Build();
    
            BluetoothAdapter.DefaultAdapter.BluetoothLeScanner.StartScan(new List<ScanFilter>() { _scanFilter }, _scanSettings, _scanCallback);
        }
    
        public class MyScanCallback : ScanCallback
        {
            public MyBluetoothGattCallback GattCallback { get; set; }
    
            public override void OnScanResult([GeneratedEnum] ScanCallbackType callbackType, ScanResult result)
            {
                base.OnScanResult(callbackType, result);
    
                result.Device.ConnectGatt(Android.App.Application.Context, true, GattCallback);
            }
    
            public override void OnScanFailed([GeneratedEnum] ScanFailure errorCode)
            {
                base.OnScanFailed(errorCode);
            }
        }
    
        public class MyBluetoothGattCallback : BluetoothGattCallback
        {
            public BluetoothGatt Gatt { get; set; }
    
            public override void OnConnectionStateChange(BluetoothGatt gatt, [GeneratedEnum] GattStatus status, [GeneratedEnum] ProfileState newState)
            {
                base.OnConnectionStateChange(gatt, status, newState);
    
                // It is Success the first time, but then never again until I kill the app and restart it
                if (status == GattStatus.Success)
                {
                    Gatt = gatt;
                    gatt.DiscoverServices();
                }
            }
    
            public override void OnServicesDiscovered(BluetoothGatt gatt, [GeneratedEnum] GattStatus status)
            {
                base.OnServicesDiscovered(gatt, status);
    
                if (status == GattStatus.Success)
                {
                    var service = gatt.GetService(ServiceUUID);
                    if (service != null)
                    {
                        var wc1 = service.GetCharacteristic(WriteCharacteristic1UUID);
    
                        if (wc1 != null)
                        {
                            wc1.SetValue("71d640cb-bb78-45bd");
                            gatt.WriteCharacteristic(wc1);
                        }
                    }
                }
            }
    
            public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, [GeneratedEnum] GattStatus status)
            {
                base.OnCharacteristicWrite(gatt, characteristic, status);
    
                if (status != GattStatus.Success)
                {
                    // try again
                    if (characteristic.Uuid.Equals(WriteCharacteristic1UUID))
                    {
                        var service = gatt.GetService(ServiceUUID);
                        var wc1 = service.GetCharacteristic(WriteCharacteristic1UUID);
                        if (wc1 != null)
                        {
                            wc1.SetValue("71d640cb-bb78-45bd");
                            gatt.WriteCharacteristic(wc1);
                        }
                    }
                    else if (characteristic.Uuid.Equals(WriteCharacteristic2UUID))
                    {
                        var service = gatt.GetService(ServiceUUID);
                        var wc2 = service.GetCharacteristic(WriteCharacteristic2UUID);
                        if (wc2 != null)
                        {
                            wc2.SetValue("-ae26-614fead76efe");
                            gatt.WriteCharacteristic(wc2);
                        }
                    }
    
                    return;
                }
    
                if (characteristic.Uuid.Equals(WriteCharacteristic1UUID))
                {
                    // now send the second text
                    var service = gatt.GetService(ServiceUUID);
                    var wc2 = service.GetCharacteristic(WriteCharacteristic2UUID);
                    if (wc2 != null)
                    {
                        wc2.SetValue("-ae26-614fead76efe");
                        gatt.WriteCharacteristic(wc2);
                    }
                }
            }
        }
    
        public void Close()
        {
            if(_gattCallback.Gatt != null)
            {
                // Some devices (or Android versions) will disconnect automatically when the app closes, but some won't and then you'll have to power off the phone to make sure the hardware is disconnected, and you won't be able to connect again until you do.
                _gattCallback.Gatt.Close();
                _gattCallback.Gatt.Disconnect();
                _gattCallback.Gatt = null;
            }
    
            IsStarted = false;
        }
    }
    

    我一个接一个地写入2个特征,然后它再也不会写入它们直到我杀死并重新启动应用程序...当设备在范围内时,如何让它连续写入特征?

1 个答案:

答案 0 :(得分:1)

你有一些大问题,但我仔细阅读了所有内容,并且会给你一些关于去哪里的建议。对于一个完整的应用程序,大多数这些东西将是恕我直言,在单独的课程,只负责1个责任,但我会尽我所能:

  • “我需要(...)”服务器应用程序“(...)始终知道其他设备/应用程序何时出现,”

为此你可以/应该使用来自BluetoothGattServerCallback.onConnectionStateChange()的回调。通过这种方式,您可以知道设备何时连接和断开连接,而无需使用这种错综复杂的“始终写入”逻辑。

  • 根据我在您希望的客户端应用程序上的理解:

    while(true){    而(未找到){        扫描()    }    连接()    while(已连接){      写特征1      写特征2    } }

但是从你的代码来看,你似乎错过了while部分。你肯定错过了OnCharacteristicWrite内部不断重复的部分。像这样:

        // if I just wrote the first
        if (characteristic.Uuid.Equals(WriteCharacteristic1UUID))
        {
            // write the sencond
            var service = gatt.GetService(ServiceUUID);
            var wc2 = service.GetCharacteristic(WriteCharacteristic2UUID);
            if (wc2 != null)
            {
                wc2.SetValue("-ae26-614fead76efe");
                gatt.WriteCharacteristic(wc2);
            }
        }
        // if we just wrote the second
        else if (characteristic.Uuid.Equals(WriteCharacteristic2UUID))
        {
            //write the 1st again
            ... insert here code that writes the 1st characteristic
        }

它似乎缺少一些断开处理代码。这应该在OnConnectionStateChange

之内

你必须做这样的事情:

 if (status != GattStatus.Success || newState != CONNECTED)
 {
     // we're not connected, close the gatt
     try{  gatt.disconnect() } catch(exception) { } 
     gatt.close()
 }
 else
 {
      Gatt = gatt;
      gatt.DiscoverServices();
 }

Android对此gatt有一些限制,因此断开并关闭它非常重要,否则您将遇到一些系统限制。

我从未使用过此自动连接选项true,但在我看来,您从未停止过扫描。这意味着您将继续在OnScanResult接收回调,因此您不断要求一次又一次地连接。

你当然应该制作一些已经找到设备的标志,并且不需要再次连接。像:

onScanResul(...
  if(!connectedOrConnectin) {
       connectedOrConnectin = true
       -> connect
       // make sure to clear the flag after disconnection.
  }

此外,您可能不希望将“自动连接”标志用于True(https://issuetracker.google.com/issues/37071781)。如果尝试通过其他方法进行连接,您似乎可以获得更快的连接。

我希望它有所帮助。