Search code examples
iosxamarinbluetooth-lowenergycore-bluetooth

How to add services to CBPeripheralManager correctly in Xamarin


I am attempting to add services to a CBPeripheralManager for IOS using Visual Studio for Mac. However, when I scan for my peripheral using another device, I do not see my added service. The advertising name is correct, but I only see other services (presumably default for iphone).

As a side note, I wrote the program in swift and get the same issue.

My xcode version is 9 and my ios target version is 11.

My code is as follows

CBPeripheralManager _peripheral;
public BLEPeripheral()
    {
        _peripheral = new CBPeripheralManager();//this, DispatchQueue.CurrentQueue);
        _peripheral.CharacteristicSubscribed += (object sender, CBPeripheralManagerSubscriptionEventArgs e) =>
        {
            Console.WriteLine("Device Subscribed: {0}", e.Central.UUID);
            _connectedDevices.Add(e.Central);
            ServiceSubscribed(this, e);
        };

        _peripheral.WriteRequestsReceived += (object sender, CBATTRequestsEventArgs e) => {
            Console.WriteLine("Write Request Received");
        };


        _peripheral.ServiceAdded += (object sender, CBPeripheralManagerServiceEventArgs e) =>
        {
            Console.WriteLine("Service {0} was added", e.Error);
            Console.WriteLine(e.Service.Characteristics.Length.ToString());
            Console.WriteLine(e.Service.UUID.ToString());

            IsAdvertising = true;
            StartAdvertisingOptions advOptions = new StartAdvertisingOptions();
            advOptions.LocalName = "myDevice";

            _peripheral.StartAdvertising(advOptions);
            Console.WriteLine("starting the advertisement");
        };

        _peripheral.AdvertisingStarted += (object sender, Foundation.NSErrorEventArgs e) =>
        {
            Console.WriteLine("Advertising did start");
        };

        _peripheral.StateUpdated += (object sender, EventArgs e) =>
        {
            Console.WriteLine("UpdatedState: {0}", _peripheral.State);
        };

    }

My Code for setting up the service is:

public void setupService()
    {
        txChar = new CBMutableCharacteristic(txCharUUID, CBCharacteristicProperties.Notify, null, CBAttributePermissions.Readable);
        var MyServiceUUID = CBUUID.FromString("A66C4FFC-52C6-4C04-B97C-34E5E91DG5BG");

        MyService = new CBMutableService(MyServiceUUID, true);
        MyService.Characteristics = new CBMutableCharacteristic[] { txChar };
        _peripheral.AddService(MyService);

    }

The state of the peripheralmanager is PoweredOn. I check this before pressing the button which adds the service and starts advertisement.


Solution

  • Probably the main reason why your solution is not working is that CBPeripheralManager methods must be used only when it's in State==PoweredOn. The Apple documentation states this in the Overview.

    I can see another minor potential problem in your example. In StartAdvertisingOption you didn't set the ServiceUUID, so you are not advertising your service. It should be StartAdvertisingOptions advOptions = new StartAdvertisingOptions { LocalName = "myDevice", ServicesUUID = new CBUUID[] { _uuidService } };

    Here follows a working solution where I moved all the CBPeripheralManager logic in the UpdateState event:

    public class BleGattServerManager
    {
        private CBPeripheralManager _PeripheralManager;
        private CBUUID _uuidService = CBUUID.FromString("625D74A2-3E61-4D1E-8949-8BE42DFDC6DA");
    
        public BleGattServerManager()
        {
            //set up peripheral
            _PeripheralManager = new CBPeripheralManager();
            _PeripheralManager.StateUpdated += _PeripheralManager_StateUpdated;
            _PeripheralManager.AdvertisingStarted += _PeripheralManager_AdvertisingStarted;
            _PeripheralManager.ServiceAdded += _PeripheralManager_ServiceAdded;
        }
    
        private void _PeripheralManager_ServiceAdded(object sender, CBPeripheralManagerServiceEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Service added");
        }
    
        private void _PeripheralManager_AdvertisingStarted(object sender, NSErrorEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Advertising started");
        }
    
        private void _PeripheralManager_StateUpdated(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("State=" + _PeripheralManager.State);
    
            if (_PeripheralManager.State == CBPeripheralManagerState.PoweredOn)
            {
                //set up the GATT service
                CBUUID uuidCharact = CBUUID.FromString("5BDAFB34-DA3F-40AA-8F9C-8AD409CB0063");
                NSData readme = NSData.FromString("readme!");
                CBMutableCharacteristic _CharacRead = new CBMutableCharacteristic(uuidCharact, CBCharacteristicProperties.Read, readme, CBAttributePermissions.Readable);
                CBMutableService service = new CBMutableService(_uuidService, true);
                service.Characteristics = new CBMutableCharacteristic[] { _CharacRead };
    
                _PeripheralManager.AddService(service);
    
                StartAdvertisingOptions advData = new StartAdvertisingOptions { LocalName = "my GATT!", ServicesUUID = new CBUUID[] { _uuidService } };
                _PeripheralManager.StartAdvertising(advData);
            }
        }
    }