JackSojourn
JackSojourn

Reputation: 364

Scanning for beacons using universal beacon library

I am trying to implement a mobile app (on iPhone) that just scans for beacons and displays a notification for each one. I am a noob with beacons/bluetooth.

I implemented it using the universal beacon library (https://github.com/andijakl/universal-beacon) and i've attached my ios bluetooth implementation.

my problem is that i receive about 12 beacon added events even though i only have two (I assume it is picking up all my other bluetooth devices). I also only receive the local name in the advertisement_received event.

My questions are:

  1. how do I distinguish that it is a beacon being added?
  2. how do i get the unique id an url from the beacon? (they are kontakt beacons)

Thanks for any help.

My beacon service:

    public BeaconService()
    {
        // get the platform-specific provider
        var provider = RootWorkItem.Services.Get<IBluetoothPacketProvider>();

        if (null != provider)
        {
            // create a beacon manager, giving it an invoker to marshal collection changes to the UI thread
            _manager = new BeaconManager(provider, Device.BeginInvokeOnMainThread);
            _manager.Start();

            _manager.BeaconAdded += _manager_BeaconAdded;
            provider.AdvertisementPacketReceived += Provider_AdvertisementPacketReceived;
        }
    }

My ios bluetooth implementation:

public class iOSBluetoothPacketProvider : CocoaBluetoothPacketProvider { }

public class CocoaBluetoothPacketProvider : NSObject, IBluetoothPacketProvider
{
    public event EventHandler<BLEAdvertisementPacketArgs> AdvertisementPacketReceived;
    public event EventHandler<BTError> WatcherStopped;

    private readonly CocoaBluetoothCentralDelegate centralDelegate;
    private readonly CBCentralManager central;

    public CocoaBluetoothPacketProvider()
    {
        Debug.WriteLine("BluetoothPacketProvider()");

        centralDelegate = new CocoaBluetoothCentralDelegate();
        central = new CBCentralManager(centralDelegate, null);
    }

    private void ScanCallback_OnAdvertisementPacketReceived(object sender, BLEAdvertisementPacketArgs e)
    {
        AdvertisementPacketReceived?.Invoke(this, e);
    }

    public void Start()
    {
        Debug.WriteLine("BluetoothPacketProvider:Start()");
        centralDelegate.OnAdvertisementPacketReceived += ScanCallback_OnAdvertisementPacketReceived;

        // Wait for the PoweredOn state

        //if(CBCentralManagerState.PoweredOn == central.State) {
        //    central.ScanForPeripherals(peripheralUuids: new CBUUID[] { },
        //                                               options: new PeripheralScanningOptions { AllowDuplicatesKey = false });
        //}
    }

    public void Stop()
    {
        Debug.WriteLine("BluetoothPacketProvider:Stop()");
        centralDelegate.OnAdvertisementPacketReceived -= ScanCallback_OnAdvertisementPacketReceived;

        central.StopScan();
        WatcherStopped?.Invoke(sender: this, e: new BTError(BTError.BluetoothError.Success));
    }
}
internal class CocoaBluetoothCentralDelegate : CBCentralManagerDelegate
{
    public event EventHandler<BLEAdvertisementPacketArgs> OnAdvertisementPacketReceived;

    #region CBCentralManagerDelegate

    public override void ConnectedPeripheral(CBCentralManager central, CBPeripheral peripheral)
    {
        Debug.WriteLine($"ConnectedPeripheral(CBCentralManager central, CBPeripheral {peripheral})");
    }

    public override void DisconnectedPeripheral(CBCentralManager central, CBPeripheral peripheral, NSError error)
    {
        Debug.WriteLine($"DisconnectedPeripheral(CBCentralManager central, CBPeripheral {peripheral}, NSError {error})");
    }

    public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral, NSDictionary advertisementData, NSNumber RSSI)
    {
        Debug.WriteLine($"Cocoa peripheral {peripheral}");
        Debug.WriteLine($"Cocoa advertisementData {advertisementData}");
        Debug.WriteLine($"Cocoa RSSI {RSSI}");

        var bLEAdvertisementPacket = new BLEAdvertisementPacket()
        {
            Advertisement = new BLEAdvertisement()
            {
                LocalName = peripheral.Name,
                ServiceUuids = new List<Guid>(),
                DataSections = new List<BLEAdvertisementDataSection>(),
                ManufacturerData = new List<BLEManufacturerData>()
            },
            AdvertisementType = BLEAdvertisementType.ScanResponse,
            BluetoothAddress = (ulong)peripheral.Identifier.GetHashCode(),
            RawSignalStrengthInDBm = RSSI.Int16Value,
            Timestamp = DateTimeOffset.Now
        };

        //https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataserviceuuidskey
        //if (advertisementData.ContainsKey(CBAdvertisement.DataServiceUUIDsKey))
        //{
        //    bLEAdvertisementPacket.Advertisement.ServiceUuids.Add(
        //        item: new BLEManufacturerData(packetType: BLEPacketType.UUID16List,
        //                                      data: (advertisementData[CBAdvertisement.DataServiceUUIDsKey])));
        //}

        //https://developer.apple.com/documentation/corebluetooth/cbadvertisementdataservicedatakey
        //if (advertisementData.ContainsKey(CBAdvertisement.DataServiceDataKey))
        //{
        //    bLEAdvertisementPacket.Advertisement.DataSections.Add(
        //        item: new BLEManufacturerData(packetType: BLEPacketType.ServiceData,
        //                                      data: advertisementData[CBAdvertisement.DataServiceDataKey]));
        //}

        //https://developer.apple.com/documentation/corebluetooth/cbadvertisementdatamanufacturerdatakey
        if (advertisementData.ContainsKey(CBAdvertisement.DataManufacturerDataKey))
        {
            bLEAdvertisementPacket.Advertisement.ManufacturerData.Add(
                item: new BLEManufacturerData(packetType: BLEPacketType.ManufacturerData,
                                              data: (advertisementData[CBAdvertisement.DataManufacturerDataKey]
                                                     as NSData).ToArray()));
        }

        // Missing CBAdvertisement.DataTxPowerLevelKey

        var bLEAdvertisementPacketArgs = new BLEAdvertisementPacketArgs(data: bLEAdvertisementPacket);
        OnAdvertisementPacketReceived?.Invoke(this, bLEAdvertisementPacketArgs);
    }

    public override void FailedToConnectPeripheral(CBCentralManager central, CBPeripheral peripheral, NSError error)
    {
        Debug.WriteLine($"FailedToConnectPeripheral(CBCentralManager central, CBPeripheral {peripheral}, NSError {error})");
    }

    public override void UpdatedState(CBCentralManager central)
    {
        switch (central.State)
        {
            case CBCentralManagerState.Unknown:
                Debug.WriteLine("CBCentralManagerState.Unknown");
                break;
            case CBCentralManagerState.Resetting:
                Debug.WriteLine("CBCentralManagerState.Resetting");
                break;
            case CBCentralManagerState.Unsupported:
                Debug.WriteLine("CBCentralManagerState.Unsupported");
                break;
            case CBCentralManagerState.Unauthorized:
                Debug.WriteLine("CBCentralManagerState.Unauthorized");
                break;
            case CBCentralManagerState.PoweredOff:
                Debug.WriteLine("CBCentralManagerState.PoweredOff");
                break;
            case CBCentralManagerState.PoweredOn:
                Debug.WriteLine("CBCentralManagerState.PoweredOn");
                central.ScanForPeripherals(peripheralUuids: new CBUUID[] { },
                                                           options: new PeripheralScanningOptions { AllowDuplicatesKey = true });
                break;
            default:
                throw new NotImplementedException();
        }
    }

    public override void WillRestoreState(CBCentralManager central, NSDictionary dict)
    {
        Debug.WriteLine($"WillRestoreState(CBCentralManager central, NSDictionary {dict})");
    }

    #endregion CBCentralManagerDelegate
}

Upvotes: 1

Views: 976

Answers (1)

JackSojourn
JackSojourn

Reputation: 364

So in case anyone is looking for this. The universal beacon library does not have an ios implementation that converts the ios packets to the universal packets. This need to be implemented.

  1. how do I distinguish that it is a beacon being added?

I look for the Eddystone packets and if found I add to the observable list.

  1. how do i get the unique id an url from the beacon? (they are kontakt beacons)

You need to loop through the advertisementData sent with the advertisement and create a BLEAdvertisementDataSection. copy the frame data as NSData.

Upvotes: 1

Related Questions