private async void OnTogglePublisherButtonClickedAsync(object sender, RoutedEventArgs e) { if (_bluetoothLEAdvertisementPublisher.Status != BluetoothLEAdvertisementPublisherStatus.Started) { Beacon beacon = new Beacon(); beacon.ManufacturerId = ManufacturerId; beacon.Code = BeaconCode; beacon.Id1 = BeaconId1; try { beacon.Id2 = UInt16.Parse(BeaconId2); beacon.Id3 = UInt16.Parse(BeaconId3); } catch (Exception) { } beacon.MeasuredPower = -58; System.Diagnostics.Debug.WriteLine("Will try to advertise as beacon " + beacon.ToString()); BluetoothLEAdvertisementDataSection dataSection = BeaconFactory.BeaconToSecondDataSection(beacon); _bluetoothLEAdvertisementPublisher.Advertisement.DataSections.Add(dataSection); try { _bluetoothLEAdvertisementPublisher.Start(); } catch (Exception ex) { MessageDialog messageDialog = new MessageDialog("Failed to start Bluetooth LE Advertisement Publisher: " + ex.Message); await messageDialog.ShowAsync(); } } else { _bluetoothLEAdvertisementPublisher.Stop(); } }
/// <summary> /// Constructs a Beacon instance and sets the properties based on the given data. /// /// The expected specification of the data is as follows: /// /// Byte(s) Name /// -------------------------- /// 0-1 Manufacturer ID (16-bit unsigned integer, big endian) /// 2-3 Beacon code (two 8-bit unsigned integers, but can be considered as one 16-bit unsigned integer in little endian) /// 4-19 ID1 (UUID) /// 20-21 ID2 (16-bit unsigned integer, big endian) /// 22-23 ID3 (16-bit unsigned integer, big endian) /// 24 Measured Power (signed 8-bit integer) /// 25 Reserved for use by the manufacturer to implement special features (optional) /// /// For more details on the beacon specifications see https://github.com/AltBeacon/spec /// /// The minimum length of the given byte array is 25. If it is longer than 26 bits, /// everything after the 26th bit is ignored. /// </summary> /// <param name="data">The data to populate the Beacon instance properties with.</param> /// <returns>A newly created Beacon instance or null in case of a failure.</returns> public static Beacon BeaconFromByteArray([ReadOnlyArray] byte[] data) { if (data == null || data.Length < SecondBeaconDataSectionMinimumLengthInBytes) { // The given data is null or too short return null; } Beacon beacon = new Beacon(); beacon.Code = BitConverter.ToUInt16(data, 2); // Bytes 2-3 beacon.Id1 = FormatUuid(BitConverter.ToString(data, 4, 16)); // Bytes 4-19 beacon.MeasuredPower = Convert.ToSByte(BitConverter.ToString(data, 24, 1), 16); // Byte 24 if (data.Length >= SecondBeaconDataSectionMinimumLengthInBytes + 1) { beacon.MfgReserved = data[25]; // Byte 25 } // Data is expected to be big endian. Thus, if we are running on a little endian, // we need to switch the bytes if (BitConverter.IsLittleEndian) { data = ChangeInt16ArrayEndianess(data); } beacon.ManufacturerId = BitConverter.ToUInt16(data, 0); // Bytes 0-1 beacon.Id2 = BitConverter.ToUInt16(data, 20); // Bytes 20-21 beacon.Id3 = BitConverter.ToUInt16(data, 22); // Bytes 22-23 return beacon; }
/// <summary> /// Compares the given beacon to this. /// </summary> /// <param name="beacon">The beacon to compare to.</param> /// <returns>True, if the beacons match.</returns> public bool Matches(Beacon beacon) { return beacon.Id1.Equals(Id1) && beacon.Id2 == Id2 && beacon.Id3 == Id3; }
/// <summary> /// Creates the second part of the beacon advertizing packet. /// Uses the beacon IDs 1, 2, 3 and measured power to create the data section. /// </summary> /// <param name="beacon">A beacon instance.</param> /// <param name="includeMfgReservedByte">Defines whether we should add the additional, manufacturer reserved byte or not.</param> /// <returns>A newly created data section.</returns> public static BluetoothLEAdvertisementDataSection BeaconToSecondDataSection( Beacon beacon, bool includeMfgReservedByte = false) { string[] temp = beacon.Id1.Split(HexStringSeparator); string beaconId1 = string.Join("", temp); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(ManufacturerIdToString(beacon.ManufacturerId)); stringBuilder.Append(BeaconCodeToString(beacon.Code)); stringBuilder.Append(beaconId1.ToUpper()); byte[] beginning = HexStringToByteArray(stringBuilder.ToString()); byte[] data = includeMfgReservedByte ? new byte[SecondBeaconDataSectionMinimumLengthInBytes + 1] : new byte[SecondBeaconDataSectionMinimumLengthInBytes]; beginning.CopyTo(data, 0); ChangeInt16ArrayEndianess(BitConverter.GetBytes(beacon.Id2)).CopyTo(data, 20); ChangeInt16ArrayEndianess(BitConverter.GetBytes(beacon.Id3)).CopyTo(data, 22); data[24] = (byte)Convert.ToSByte(beacon.MeasuredPower); if (includeMfgReservedByte) { data[25] = beacon.MfgReserved; } BluetoothLEAdvertisementDataSection dataSection = new BluetoothLEAdvertisementDataSection(); dataSection.DataType = SecondBeaconDataSectionDataType; dataSection.Data = data.AsBuffer(); return dataSection; }
/// <summary> /// Updates the beacon data, if the given beacon matches this one. /// </summary> /// <param name="beacon">The beacon with new data.</param> /// <returns>True, if the given beacon matches this one (and the data was updated). False otherwise.</returns> public bool Update(Beacon beacon) { bool matches = Matches(beacon); if (matches) { Timestamp = beacon.Timestamp; RawSignalStrengthInDBm = beacon.RawSignalStrengthInDBm; if (_lastSeenTimer != null) { _lastSeenTimer.Dispose(); _lastSeenTimer = new Timer(LastSeenTimerCallbackAsync, null, LastSeenTimerTimeoutInMilliseconds, LastSeenTimerTimeoutInMilliseconds); } } return matches; }