Getting started with iBeacons and Windows 10

I recently picked up an Estimote Proximity Beacons Developer Kit, which contains three BLE (Bluetooth Low Energy) beacons that are compatible with iBeacon and Eddystone protocols.

A developer can use beacons to determine whether the user of an application is within the proximity of a beacon (and how far they are away to a lesser extent). One example of how this could be used is by a museum that has a mobile app that can detect beacons and automatically display information about exhibits. A retail store might use beacons to determine when a customer visits their physical location or even detect their movement within a store. A manufacturing company could use beacons to simplify the tracking of equipment or inventory. In short, beacons enable software to become aware of its physical environment.


There are many examples online showing how to detect beacons from a mobile device (iOS or Android), but I could not find much information about how to use beacons from Windows. It turns out beacons can be used by a Windows 10 application and in this blog post I will create a simple console application that can detect beacons.

First, we will need to install a couple of prerequisites on our development PC:

Once you have Visual Studio 2015 and Windows 10 SDK installed, open Visual Studio and create a new Console Application.

Next, add a reference to C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd. You will need to use the browse button in the reference dialog to locate this file. Also note that the extension is winmd not dll, so you will need to change the file filter to show all files in order to see it in the file dialog.

EDIT: Instead of referencing windows.winmd directly, a simpler and more thorough way to use WinRT from a desktop application is to install the UwpDesktop NuGet Package. Install this package in your newly created project.

Now we’re ready to write some code. Because this is an example, everything will be in Program.cs:

using System;
using System.Linq;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Storage.Streams;

namespace BeaconExample
{
    class Program
    {
        private class BeaconData
        {
            public Guid Uuid { get; set; }
            public ushort Major { get; set; }
            public ushort Minor { get; set; }
            public sbyte TxPower { get; set; }
            public static BeaconData FromBytes(byte[] bytes)
            {
                if (bytes[0] != 0x02) { throw new ArgumentException("First byte in array was exptected to be 0x02", "bytes"); }
                if (bytes[1] != 0x15) { throw new ArgumentException("Second byte in array was expected to be 0x15", "bytes"); }
                if (bytes.Length != 23) { throw new ArgumentException("Byte array length was expected to be 23", "bytes"); }
                return new BeaconData
                {
                    Uuid = new Guid(
                            BitConverter.ToInt32(bytes.Skip(2).Take(4).Reverse().ToArray(), 0),
                            BitConverter.ToInt16(bytes.Skip(6).Take(2).Reverse().ToArray(), 0),
                            BitConverter.ToInt16(bytes.Skip(8).Take(2).Reverse().ToArray(), 0),
                            bytes.Skip(10).Take(8).ToArray()),
                    Major = BitConverter.ToUInt16(bytes.Skip(18).Take(2).Reverse().ToArray(), 0),
                    Minor = BitConverter.ToUInt16(bytes.Skip(20).Take(2).Reverse().ToArray(), 0),
                    TxPower = (sbyte)bytes[22]
                };
            }
            public static BeaconData FromBuffer(IBuffer buffer)
            {
                var bytes = new byte[buffer.Length];
                using (var reader = DataReader.FromBuffer(buffer))
                {
                    reader.ReadBytes(bytes);
                }
                return BeaconData.FromBytes(bytes);
            }
        }

        static void Main(string[] args)
        {
            var watcher = new BluetoothLEAdvertisementWatcher();
            watcher.Received += Watcher_Received;
            watcher.Start();
            Console.WriteLine("Bluetooth LE Advertisement Watcher Started (Press ESC to exit)");
            while (true)
            {
                Thread.Sleep(100);
                if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)
                {
                    break;
                }
            }
            watcher.Stop();
            Console.WriteLine("Bluetooth LE Advertisement Watcher Stopped");
        }

        private static void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
        {
            const ushort AppleCompanyId = 0x004C;
            foreach (var adv in args.Advertisement.ManufacturerData.Where(x => x.CompanyId == AppleCompanyId))
            {
                var beaconData = BeaconData.FromBuffer(adv.Data);
                Console.WriteLine(
                    "[{0}] {1}:{2}:{3} TxPower={4}, Rssi={5}",
                    args.Timestamp,
                    beaconData.Uuid, 
                    beaconData.Major, 
                    beaconData.Minor, 
                    beaconData.TxPower, 
                    args.RawSignalStrengthInDBm);
            }
        }
    }
}

The code above is pretty straightforward. It just creates an instance of BluetoothLEAdvertisementWatcher, starts watching, handles Received events, and displays the information from advertisements that are received.

If everything works, the project should produce output similar to this:

The default UUID for Estimote beacons is B9407F30-F5F8-466E-AFF9-25556B57FE6D and as you can see, I haven’t changed that yet. My three beacons can be distinguished using the Major and Minor identifiers (again the ones set by Estimote, until I change them):

  • 24554:52084
  • 8008:43189
  • 50483:25448

In this post, I showed how to create a simple console application that is able to detect nearby iBeacons. I hope this will be helpful to anyone looking to integrate proximity detection into their Windows 10 applications.

EDIT: You might find that Windows 10 doesn’t seem to be able to detect BLE advertisements as quickly or consistently as Android or iOS. I found an interesting Stack Overflow question that was answered by a user (Emil) who indicated that Windows had a hard-coded scan interval and scan window that would cause it to miss advertisements, only picking up on roughly 1/7th of them based on the scan interval and window numbers he provided. His answer offered a way to use the Windows API function DeviceIoControl to manually initiate the scan using a more appropriate scan interval and window.

13 Comments

  • André Carneiro says:

    Thanks so much! You saved my life!

    But I didn´t understand the ‘AppleCompanyId. What that means? Maybe you could point some material to read for me. Please?

  • Jinal says:

    Hi Brian,

    your post is brilliant and it’s working perfectly fine. After this, I was trying to add Universal Bluetooth Beacon Library to my console app so I can get accurate beacon location. Do you know how am I going to add that?

  • Bosch says:

    Hi,

    I have a fresh installed VS2017 on my computer with W10.
    I have also downloaded Windows 10 SDK as above.
    Started a new project. Now there are two different console alternatives: . Net Core and .Net Framework. I’ve tried both but focused on the Framework at last.
    Also added the UWP Desktop package to my project.

    When coping your code above to my project I get six errors:
    IBuffer, DataReader, BluetoothLEAdvertisementWatcher (twice), Thread, and finnaly BluetoothLEAdvertisementReceivedEventArgs args.
    That can’t be found, I’ve tried many ways to add differnt namespaces/headers and also the overcrossed reference to Windows.winmd above – without any success.
    Does the missing function moved out from the Windows.Devices.Bluetooth.Advertisement and Windows.Storage.Streams headers?
    Is Visual Studio 2017 a problem or do I have missed something basic here?

    Thanks in advance

    • Wen-Ding says:

      change the using block to this, that’s work

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Threading;
      using Windows.Devices.Bluetooth.Advertisement;
      using Windows.Storage.Streams;

    • Wen-Ding says:

      you needs to install the UwpDesktop by NuGet

      • Bosch says:

        Hi,

        Did anyone got the fix for this?

        I Have been occupied with other stuff for a while…

      • Bosch says:

        Hi,
        I Have been occupied with other stuff for a while…

        I’ve tried VS2015 fresh installation,
        and installed the Nuget package UwpDesktop.
        Whitout success

        Anyone have a solution?

    • maxim says:

      I got the same problem with above errors, anyone found solution?

  • Hey, thanks so much for this. I’ve got it to work for cell B2 but how do I duplicate the formula for the whole list without having to enter a new formula for each row?

  • Ahmed says:

    I have this beacons and I need to make app with ionic 2 to only read beacons name or uuid
    Can you help me in that

  • MARTIN says:

    Hi,

    Nice sample, but it doesn’t work for me.
    BluetoothLEAdvertisementWatcher and other references is missing despite nuget package has been setup.
    Any ideas what’s wrong. To old version of Windows 10 maybe ?

    Help would be greatly appreciate.

    Thanks

  • Pavel says:

    Hi,

    I have got “iTag” beacons.
    I’m not able to work with them, do you know where to find the specification?

2 Trackbacks

Leave a Reply

Your email address will not be published. Required fields are marked *