public void Beacon_ParseDataWithoutFlags() { var data = new byte[] { 0x00, 0x00, 0x28, 0xEA, 0x6B, 0xB8, 0x5F, 0xD0, 0x1B, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xA4, 0x95, 0x00, 0x00, 0xC5, 0xB1, 0x4B, 0x44, 0xB5, 0x12, 0x13, 0x70, 0xF0, 0x2D, 0x74, 0xDE, 0x00, 0x02, 0x6D, 0x65, 0xC5, 0xBC }; var queue = new Queue <byte>(data); var beacon = Beacon.Parse(LeAdvertisingEvent.Parse(queue)); Assert.NotNull(beacon); Assert.AreEqual(0x4C00, beacon.CompanyId); Assert.AreEqual(-59, beacon.CalibratedTxPower); }
/// <summary> /// Parse low energy advertising event to a beacon instance. /// </summary> /// <param name="e">The event.</param> /// <returns>The beacon or null in case of failure.</returns> internal static Beacon Parse(LeAdvertisingEvent e) { if (e.EventType == LeAdvertisingEventType.ScanRsp) { return(null); } var payload = new Queue <byte>(e.Payload); while (payload.Count > 0) { var adLength = payload.Dequeue(); if (payload.Count < adLength) { break; } var adType = payload.Dequeue(); if (adType == 0xFF && adLength >= 26) { var companyId = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); var b0advInd = payload.Dequeue(); var b1advInd = payload.Dequeue(); var uuid = payload.Dequeue(16); var major = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); var minor = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); var txPower = (sbyte)payload.Dequeue(); return(new Beacon { Uuid = uuid.ToLittleEndianFormattedUuidString(), Address = e.Address, Major = major, Minor = minor, Rssi = e.Rssi, CalibratedTxPower = txPower, CompanyId = companyId }); } else { if (adLength > 0) { payload.Dequeue(adLength - 1); } } } return(null); }
public void Beacon_ParseDataWithFlags() { var data = new byte[] { 0x00, 0x00, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, 0x1E, 0x02, 0x01, 0x1A, 0x1A, 0xFF, 0xAA, 0xBB, 0x02, 0x15, 0x4F, 0xE5, 0xD5, 0xF6, 0xAB, 0xCE, 0xDD, 0xFE, 0x15, 0x87, 0x12, 0x3D, 0x1A, 0x4B, 0x56, 0x7F, 0x04, 0xD2, 0x16, 0x2E, 0xD0, 0xCC }; var queue = new Queue <byte>(data); var beacon = Beacon.Parse(LeAdvertisingEvent.Parse(queue)); Assert.AreEqual(new byte[] { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC }, beacon.Address); Assert.AreEqual("4fe5d5f6-abce-ddfe-1587-123d1a4b567f", beacon.Uuid); Assert.AreEqual(1234, beacon.Major); Assert.AreEqual(5678, beacon.Minor); Assert.AreEqual(-48, beacon.CalibratedTxPower); Assert.AreEqual(0xAABB, beacon.CompanyId); Assert.AreEqual(-52, beacon.Rssi); Assert.IsFalse(beacon.IsAppleIBeacon); }
/// <summary> /// Parse low energy advertising event to a beacon instance. /// </summary> /// <param name="e">The event.</param> /// <returns>The beacon or null in case of failure.</returns> internal static Beacon Parse(LeAdvertisingEvent e) { if (e.EventType == LeAdvertisingEventType.ScanRsp) { return(null); } if (e.Payload.Length < 9) { return(null); } var payload = new Queue <byte>(e.Payload); var ad1Length = payload.Dequeue(); var ad1Type = payload.Dequeue(); var flags = payload.Dequeue(); var ad2Length = payload.Dequeue(); var ad2Type = payload.Dequeue(); var companyId = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); var b0advInd = payload.Dequeue(); var b1advInd = payload.Dequeue(); if (ad1Length != 2 || ad1Type != 0x01 || ad2Length < 26 || ad2Type != 0xFF) { return(null); } var uuid = payload.Dequeue(16); var major = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); var minor = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); var txPower = (sbyte)payload.Dequeue(); return(new Beacon { Uuid = uuid.ToLittleEndianFormattedUuidString(), Address = e.Address, Major = major, Minor = minor, Rssi = e.Rssi, CalibratedTxPower = txPower, CompanyId = companyId }); }
public void Beacon_Parse() { var address = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC }; var queue = new Queue <byte>(); queue.Enqueue(0x00); queue.Enqueue(0x00); queue.Enqueue(address.Reverse()); queue.Enqueue((byte)data.Length); queue.Enqueue(data); unchecked { queue.Enqueue((byte)-52); } var beacon = Beacon.Parse(LeAdvertisingEvent.Parse(queue)); Assert.AreEqual(address, beacon.Address); Assert.AreEqual("4fe5d5f6-abce-ddfe-1587-123d1a4b567f", beacon.Uuid); Assert.AreEqual(1234, beacon.Major); Assert.AreEqual(5678, beacon.Minor); Assert.AreEqual(-48, beacon.CalibratedTxPower); Assert.AreEqual(0xAABB, beacon.CompanyId); Assert.AreEqual(-52, beacon.Rssi); Assert.IsFalse(beacon.IsAppleIBeacon); }
/// <summary> /// Parses an incoming <see cref="LeAdvertisingEvent"/> for Eddystone frames. /// </summary> /// <param name="e">The incoming event frame.</param> /// <returns>An <see cref="Eddystone"/> instance or null in case the event could not be parsed as an Eddystone event.</returns> /// <remarks>Implemented according to https://github.com/google/eddystone/blob/master/protocol-specification.md </remarks> internal static Eddystone Parse(LeAdvertisingEvent e) { if (e.EventType == LeAdvertisingEventType.ScanRsp) { return(null); } var payload = new Queue <byte>(e.Payload); // Skip flags var flagsLength = payload.Dequeue(); payload.Dequeue(flagsLength); // Extract and verify Eddystone UUID // The Complete List of 16-bit Service UUIDs as defined in The Bluetooth Core Specification Supplement (CSS) v5, Part A, § 1.1. // The Complete List of 16-bit Service UUIDs must contain the Eddystone Service UUID of 0xFEAA. This is included to allow background scanning on iOS devices. var eddystoneUuidLength = payload.Dequeue(); if (eddystoneUuidLength != 3) { return(null); } payload.Dequeue(); var eddystoneUuid = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); if (eddystoneUuid != 0xAAFE) { return(null); } // Extract service data // The Service Data data type, Ibid., § 1.11. // The Service Data - 16 bit UUID data type must be the Eddystone Service UUID of 0xFEAA var serviceDataLength = payload.Dequeue(); var serviceDataTypeValue = payload.Dequeue(); eddystoneUuid = (ushort)((payload.Dequeue() << 8) + payload.Dequeue()); if (eddystoneUuid != 0xAAFE) { return(null); } var eddystoneFrameType = (EddystoneFrameType)payload.Dequeue(); Eddystone result = null; switch (eddystoneFrameType) { case EddystoneFrameType.Uid: result = EddystoneUid.ParseFrame(payload); break; case EddystoneFrameType.Url: result = EddystoneUrl.ParseFrame(payload); break; case EddystoneFrameType.Tlm: result = EddystoneTlm.ParseFrame(payload); break; case EddystoneFrameType.Eid: result = EddystoneEid.ParseFrame(payload); break; } if (result != null) { result.Address = e.Address; result.Rssi = e.Rssi; } return(result); }