private void ParseEddystoneData(BluetoothLEAdvertisementReceivedEventArgs btAdv) { // Parse Eddystone data foreach (var dataSection in btAdv.Advertisement.DataSections) { //Debug.WriteLine("Beacon data: " + dataSection.DataType + " = " + // BitConverter.ToString(dataSection.Data.ToArray())); //+ " (" + Encoding.UTF8.GetString(dataSection.Data.ToArray()) + ")\n"); // Relvant data of Eddystone is in data section 0x16 // Windows receives: 0x01 = 0x06 // 0x03 = 0xAA 0xFE // 0x16 = 0xAA 0xFE [type] [data] if (dataSection.DataType == 0x16) { var beaconFrame = dataSection.Data.ToArray().CreateEddystoneBeaconFrame(); if (beaconFrame == null) { continue; } var found = false; for (var i = 0; i < BeaconFrames.Count; i++) { if (BeaconFrames[i].GetType() == beaconFrame.GetType()) { var updateFrame = false; if (beaconFrame.GetType() == typeof(UnknownBeaconFrame)) { // Unknown frame - also compare eddystone type var existingEddystoneFrameType = BeaconFrames[i].Payload.GetEddystoneFrameType(); var newEddystoneFrameType = beaconFrame.Payload.GetEddystoneFrameType(); if (existingEddystoneFrameType != null && existingEddystoneFrameType == newEddystoneFrameType) { updateFrame = true; } } else { updateFrame = true; } if (updateFrame) { BeaconFrames[i].Update(beaconFrame); found = true; break; // Don't analyze any other known frames of this beacon } } } if (!found) { BeaconFrames.Add(beaconFrame); } } } }
private void ParseAxaBeaconData(BluetoothLEAdvertisementReceivedEventArgs btAdv) { BeaconFrameBase beaconFrame = null; if (btAdv.Advertisement.DataSections.Any(_ => _.DataType == 9)) { var data = btAdv.Advertisement.DataSections.First(_ => _.DataType == 9).Data.ToArray(); beaconFrame = new AxaCompleteNameFrame(data); } else if (btAdv.Advertisement.DataSections.Any(_ => _.DataType == 22)) { var data = btAdv.Advertisement.DataSections.First(_ => _.DataType == 22).Data.ToArray(); beaconFrame = new AxaBatTempHumFrame(data); } if (beaconFrame != null) { var existingFrame = BeaconFrames.FirstOrDefault(_ => _.GetType() == beaconFrame.GetType()); if (existingFrame != null) { existingFrame.Update(beaconFrame); } else { BeaconFrames.Add(beaconFrame); } } }
/// <summary> /// Received a new advertisement for this beacon. /// If the Bluetooth address of the new advertisement matches this beacon, /// it will parse the contents of the advertisement and extract known frames. /// </summary> /// <param name="btAdv">Bluetooth advertisement to parse, as received from /// the Windows Bluetooth LE API.</param> public void UpdateBeacon(BluetoothLEAdvertisementReceivedEventArgs btAdv) { if (btAdv == null) { return; } if (btAdv.BluetoothAddress != BluetoothAddress) { throw new BeaconException("Bluetooth address of beacon does not match - not updating beacon information"); } Rssi = btAdv.RawSignalStrengthInDBm; Timestamp = btAdv.Timestamp; //Debug.WriteLine($"Beacon advertisment detected (Strength: {Rssi}): Address: {BluetoothAddress}"); // Check if beacon advertisement contains any actual usable data if (btAdv.Advertisement == null) { return; } if (btAdv.Advertisement.ServiceUuids.Any()) { foreach (var serviceUuid in btAdv.Advertisement.ServiceUuids) { // If we have multiple service UUIDs and already recognized a beacon type, // don't overwrite it with another service Uuid. if (BeaconType == BeaconType.Unknown) { BeaconType = serviceUuid.ToBeaconType(); } } } else { //Debug.WriteLine("Bluetooth LE device does not send Service UUIDs"); } // Data sections if (btAdv.Advertisement.DataSections.Any()) { if (BeaconType == BeaconType.Eddystone) { ParseEddystoneData(btAdv); } else if (BeaconType == BeaconType.AXABeacon) { ParseAxaBeaconData(btAdv); } else if (BeaconType == BeaconType.Unknown) { // Unknown beacon type //Debug.WriteLine("\nUnknown beacon"); //foreach (var dataSection in btAdv.Advertisement.DataSections) //{ // Debug.WriteLine("Data section 0x: " + dataSection.DataType.ToString("X") + " = " + // BitConverter.ToString(dataSection.Data.ToArray())); //} } } // Manufacturer data - currently unused if (btAdv.Advertisement.ManufacturerData.Any()) { foreach (var manufacturerData in btAdv.Advertisement.ManufacturerData) { // Print the company ID + the raw data in hex format //var manufacturerDataString = $"0x{manufacturerData.CompanyId.ToString("X")}: {BitConverter.ToString(manufacturerData.Data.ToArray())}"; //Debug.WriteLine("Manufacturer data: " + manufacturerDataString); ManufacturerId = manufacturerData.CompanyId; var manufacturerDataArry = manufacturerData.Data.ToArray(); if (BeaconFrameHelper.IsProximityBeaconPayload(manufacturerData.CompanyId, manufacturerDataArry)) { //BeaconType = BeaconType.iBeacon; //Debug.WriteLine("iBeacon Frame: " + BitConverter.ToString(manufacturerDataArry)); var beaconFrame = new ProximityBeaconFrame(manufacturerDataArry); // Only one relevant data frame for iBeacons if (BeaconFrames.Any()) { BeaconFrames[0].Update(beaconFrame); } else { BeaconFrames.Add(beaconFrame); } } } } }
/// <summary> /// Received a new advertisement for this beacon. /// If the Bluetooth address of the new advertisement matches this beacon, /// it will parse the contents of the advertisement and extract known frames. /// </summary> /// <param name="btAdv">Bluetooth advertisement to parse, as received from /// the Windows Bluetooth LE API.</param> public void UpdateBeacon(BluetoothLEAdvertisementReceivedEventArgs btAdv) { if (btAdv == null) { return; } Rssi = btAdv.RawSignalStrengthInDBm; Timestamp = btAdv.Timestamp; //Debug.WriteLine($"Beacon advertisment detected (Strength: {Rssi}): Address: {BluetoothAddress}"); // Check if beacon advertisement contains any actual usable data if (btAdv.Advertisement == null) { return; } if (btAdv.Advertisement.ServiceUuids.Any()) { foreach (var serviceUuid in btAdv.Advertisement.ServiceUuids) { // If we have multiple service UUIDs and already recognized a beacon type, // don't overwrite it with another service Uuid. if (BeaconType == BeaconTypeEnum.Unknown) { BeaconType = serviceUuid.Equals(_eddystoneGuid) ? BeaconTypeEnum.Eddystone : BeaconTypeEnum.Unknown; } } } else { //Debug.WriteLine("Bluetooth LE device does not send Service UUIDs"); } // Data sections if (btAdv.Advertisement.DataSections.Any()) { if (BeaconType == BeaconTypeEnum.Eddystone) { // This beacon is according to the Eddystone specification - parse data ParseEddystoneData(btAdv); } } // Manufacturer data - currently unused if (btAdv.Advertisement.ManufacturerData.Any()) { foreach (var manufacturerData in btAdv.Advertisement.ManufacturerData) { // Print the company ID + the raw data in hex format //var manufacturerDataString = $"0x{manufacturerData.CompanyId.ToString("X")}: {BitConverter.ToString(manufacturerData.Data.ToArray())}"; //Debug.WriteLine("Manufacturer data: " + manufacturerDataString); var manufacturerDataArry = manufacturerData.Data.ToArray(); if (manufacturerData.CompanyId == 0x4C && manufacturerData.Data.Length >= 23 && manufacturerDataArry[0] == 0x02) { BeaconType = BeaconTypeEnum.iBeacon; //Debug.WriteLine("iBeacon Frame: " + BitConverter.ToString(manufacturerDataArry)); // Only one relevant data frame for iBeacons if (BeaconFrames.Any()) { BeaconFrames[0].Payload = manufacturerDataArry; } else { BeaconFrames.Add(new UnknownBeaconFrame(manufacturerDataArry)); } } else if ((manufacturerData.CompanyId & 0x150) == 0x150) { BluetoothAddress = GetEstimoteAddress(btAdv); if (BeaconFrames.Any()) { BeaconFrames[0].Payload = manufacturerDataArry; ((EstimoteNearableFrame)BeaconFrames[0]).ParsePayload(); } if (manufacturerDataArry[0] == 0x01) { BeaconType = BeaconTypeEnum.EstimoteNearable; EstimoteNearableFrame enf = new EstimoteNearableFrame(manufacturerDataArry); enf.PropertyChanged += Enf_PropertyChanged; if (!BeaconFrames.Any()) { BeaconFrames.Add(enf); } } else { BeaconType = BeaconTypeEnum.EstimoteStone; } } } } if (_bluetoothAddress == null) { BluetoothAddress = BitConverter.GetBytes(btAdv.BluetoothAddress); } //if (!CheckAddress(btAdv)) //{ // throw new BeaconException("Bluetooth address of beacon does not match - not updating beacon information"); //} }