Jimmy Engström

NAVIGATION

Announcing http://holotoolkit.azurewebsites.net

2016-07-10 17:00:00 +0000

Microsoft has really done some awesome stuff with the HoloLens and the tooling for developing for the HoloLens.
I've been developing for the HoloLens for a while now and one of the first thing I do when starting up a new project is making sure my HoloToolkit GIT clone is up to date and then copying the files to the right place.
I wanted to make this process a little bit easier so I created an Azure script that automatically downloads the source and makes a Unity package out of it, every time something is checked in into the HoloToolkit-Unity repo.

You can get the HoloToolkit as a UnityPackage here:
http://holotoolkit.azurewebsites.net/

Enjoy =)

Download all the videos from Build 2015

2015-05-11 17:25:00 +0000

Build 2015 was epic, there was so much content so Microsoft even recorded some content before build (now available at Channel9).
I usually download everything to my laptop and watch it to and from work, this script will download all videos from Build 2015 =)

This is an updated version of my script I previously blogged about here.

cd "d:\build15"

$pattern = "[{0},\:,\']" -f ([Regex]::Escape([String][System.IO.Path]::GetInvalidPathChars()))             
[Environment]::CurrentDirectory=(Get-Location -PSProvider FileSystem).ProviderPath
$a = ([xml](new-object net.webclient).downloadstring("http://s.ch9.ms/Events/Build/2015/RSS/mp4high"))
$a.rss.channel.item | foreach{ 
    $url = New-Object System.Uri($_.enclosure.url)
    $file = [Regex]::Replace($_.Title + " " + $url.Segments[-1] , $pattern, ' ') 
    Write-host  ("{0}  -  {1}" -f "Downloadning" ,$file)
    if (!(test-path $file))
    {
            (New-Object System.Net.WebClient).DownloadFile($url, $file)
    }
}

 

Just copy the code, paste it into a file called “Build2015.ps1”, create a directory (in my case “c:\Build15”) and change the path in the first line of the script.

Right click on the file and choose run with PowerShell.

 

In case you get a problem similar like “Build2015.ps1 cannot be loaded because running scripts is disabled on this system.”

Start PowerShell as an administrator and run “set-executionpolicy unrestricted”, this is probably a bad thing to do for security.

Build2015.ps1 (677.00 bytes)

Universal apps and Bluetooth LE – Battery Service

2014-06-09 00:32:00 +0000

Some devices exposes a battery service that can supply us with the current (no pun intended) battery level.
I started this blog series working with only Windows phone in mind, but then I realized if it is possible to achieve the same results with an universal app of course that’s the route I should take.

Capabilities

To be able to communicate with Bluetooth low energy (or Bluetooth Smart, as it’s also called) you need to add a capability to your app.
This can’t be done from a GUI, you need to edit the package.appmanifest manually and add the following lines of code just above </Package>.
Don’t forget to do that in both your Windows 8 and Windows Phone projects.

  <Capabilities>
    <m2:DeviceCapability Name="bluetooth.genericAttributeProfile">
      <m2:Device Id="any">
        <m2:Function Type="name:battery"/>
      </m2:Device>
    </m2:DeviceCapability>
  </Capabilities>

Now you are are all set to start coding =)

 

Battery Level Service

The battery service must implement read, and notify is optional.
What that means is that you can always assume that you will be able to read the battery level (if your device implements the battery service, but you have to check if it supports Notify  (a good developer should always check Ler med tungan ute ).

image

The value it returns is a byte with a value from 0 to 100 representing a percentage of the current charge (0 being fully discharged).

The property

This is just a standard property with a backing store.

 private int _BatteryLevel;
        public int BatteryLevel
        {
            get
            {
                return _BatteryLevel;
            }
            set
            {
                _BatteryLevel = value;
                OnPropertyChanged();
            }
        }

The interesting part is how we handle OnPropertyChanged to try to avoid “The application called an interface that was marshalled for a different thread”.
I found a very neat piece of code here.


My implementation looks like this:

        public event PropertyChangedEventHandler PropertyChanged;

        private readonly CoreDispatcher _dispatcher;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (_dispatcher == null || _dispatcher.HasThreadAccess)
            {
                var eventHandler = this.PropertyChanged;
                if (eventHandler != null)
                {
                    eventHandler(this,
                        new PropertyChangedEventArgs(propertyName));
                }
            }
            else
            {
                IAsyncAction doNotAwait =
                    _dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                    () => OnPropertyChanged(propertyName));
            }
        }

 

 public BatteryServicePage()
        {
            if (!DesignMode.DesignModeEnabled)
            {
                _dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
            }
          ........
        }


Reading a value

I have written a simple method that reads a value from the service and if it supports notification it will also start a subscription on those notifications.

 public async Task<byte[]> GetValue(Guid gattCharacteristicUuids)
        {
            try
            {
                var gattDeviceService = await GattDeviceService.FromIdAsync(Device.Id);
                if (gattDeviceService != null)
                {
                    var characteristics = gattDeviceService.GetCharacteristics(gattCharacteristicUuids).First();

                    //If the characteristic supports Notify then tell it to notify us.
                    try
                    {
                        if (characteristics.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
                        {
                            characteristics.ValueChanged += characteristics_ValueChanged;
                            await characteristics.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
                        }
                    }
                    catch { }

                    //Read
                    if (characteristics.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Read))
                    {
                        var result = await characteristics.ReadValueAsync(BluetoothCacheMode.Uncached);

                        if (result.Status == GattCommunicationStatus.Success)
                        {
                            byte[] forceData = new byte[result.Value.Length];
                            DataReader.FromBuffer(result.Value).ReadBytes(forceData);
                            return forceData;
                        }
                        else
                        {
                            await new MessageDialog(result.Status.ToString()).ShowAsync();
                        }
                    }
                }
                else 
                {
                    await new MessageDialog("Access to the device has been denied =(").ShowAsync();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            return null;
        }

 

You can simply call the method like this:

BatteryLevel = Convert.ToInt32((await GetValue(GattCharacteristicUuids.BatteryLevel))[0]);

 

You also need a callback method that can handle the notifications.

  void characteristics_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
        {
            byte[] data = new byte[args.CharacteristicValue.Length];
            Windows.Storage.Streams.DataReader.FromBuffer(args.CharacteristicValue).ReadBytes(data);

           //Update properties
            if (sender.Uuid == GattCharacteristicUuids.BatteryLevel)
            {
                BatteryLevel = Convert.ToInt32(data[0]);
            }
        }

 

Now your app can retrieve battery level from a Bluetooth LE device. How awesome is that?

BatteryDemo.zip (289.18 kb)

Controlling an iKettle with Windows phone

2014-06-06 23:15:00 +0000

A week ago I found a kettle that can be controlled over Wi-fi.
It is called iKettle and can be found over here.

When I find a device I look for two things:
Is there an app for Windows (Phone or 8)?
Can I make one?

If the answer is yes to at least one of those questions (well actually none is ok too) I know I need to buy one.

Mark Cox had already done most of the heavy lifting so I only had to implement the protocol found on this blog (Thanks Mark).
I’m a bit sad that it didn’t use http protocol, it could have replied with status 418 Ler

There are still features missing, some because I haven’t gotten around to it yet and some because there are hardware or software limitations, like you won’t be able to set up the iKettle from a Windows phone since you can’t connect directly to it.

</param></embed>
Short app demo


 

Download the app MyKettle


Hope you enjoy the app and if you have any suggestions or comments please let med know.
Still a lot of stuff coming up =D

Windows phone 8.1 and Bluetooth LE – Getting started

2014-05-04 17:03:00 +0000

With Windows phone 8.1 Microsoft also released the ability to communicate with Bluetooth Low Energy devices.
This is really exciting for Windows phone developers, this way we can start developing apps that can talk to devices without draining the battery dry.

Sadly this is not available in the developer preview of Windows phone 8.1, but will be available when Windows Phone 8.1 is released for general availability.

Pairing

The first step is always to pair with the device you want to connect to.
This is easy to do by going to Settings –> Bluetooth on your phone and tapping on the device you wish to pair with, just as you would do with a  “ordinary” Bluetooth device.

 

Capabilities

To be able to communicate with Bluetooth Low energy (or Bluetooth Smart, as it’s also called) you need to add a capability to your app.
This can’t be done from a GUI, you need to edit the package.appmanifest manually and add the following lines of code just above </Package>.

<Capabilities>
  <m2:DeviceCapability Name="bluetooth.genericAttributeProfile">
    <m2:Device Id="any">
      <m2:Function Type="serviceId:1803"/>
    </m2:Device>
  </m2:DeviceCapability>
</Capabilities>

Now you are are set to start coding =)

 

Iterate through devices

To keep this as simple as possible, I’ll just show you how to iterate through devices and pick up a predefined one.

BluetoothLEDevice currentDevice { get; set; }
string deviceName = "Philips AEA1000";
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    foreach (DeviceInformation di in await DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector()))
    {
        BluetoothLEDevice bleDevice = await BluetoothLEDevice.FromIdAsync(di.Id);
        if (bleDevice.Name == deviceName)
        {
            currentDevice = bleDevice;
            break;
        }
    }
}

Find out what your device can do

GATT (Generic Attribute Profile) provides profile discovery and description services for Bluetooth Low Energy protocol, it basically makes it possible to ask your device what it can do. The documentation for this is very thorough and shows how to communicate.

Here is how to get a list of the GATTServices your device supports.

List<string> serviceList = new List<string>();
foreach (var service in currentDevice.GattServices)
{
    switch (service.Uuid.ToString())
    {
        case "00001811-0000-1000-8000-00805f9b34fb":
            serviceList.Add("AlertNotification");
            break;
        case "0000180f-0000-1000-8000-00805f9b34fb":
            serviceList.Add("Battery");
            break;
        case "00001810-0000-1000-8000-00805f9b34fb":
            serviceList.Add("BloodPressure");
            break;
        case "00001805-0000-1000-8000-00805f9b34fb":
            serviceList.Add("CurrentTime");
            break;
        case "00001818-0000-1000-8000-00805f9b34fb":
            serviceList.Add("CyclingPower");
            break;
        case "00001816-0000-1000-8000-00805f9b34fb":
            serviceList.Add("CyclingSpeedAndCadence");
            break;
        case "0000180a-0000-1000-8000-00805f9b34fb":
            serviceList.Add("DeviceInformation");
            break;
        case "00001800-0000-1000-8000-00805f9b34fb":
            serviceList.Add("GenericAccess");
            break;
        case "00001801-0000-1000-8000-00805f9b34fb":
            serviceList.Add("GenericAttribute");
            break;
        case "00001808-0000-1000-8000-00805f9b34fb":
            serviceList.Add("Glucose");
            break;
        case "00001809-0000-1000-8000-00805f9b34fb":
            serviceList.Add("HealthThermometer");
            break;
        case "0000180d-0000-1000-8000-00805f9b34fb":
            serviceList.Add("HeartRate");
            break;
        case "00001812-0000-1000-8000-00805f9b34fb":
            serviceList.Add("HumanInterfaceDevice");
            break;
        case "00001802-0000-1000-8000-00805f9b34fb":
            serviceList.Add("ImmediateAlert");
            break;
        case "00001803-0000-1000-8000-00805f9b34fb":
            serviceList.Add("LinkLoss");
            break;
        case "00001819-0000-1000-8000-00805f9b34fb":
            serviceList.Add("LocationAndNavigation");
            break;
        case "00001807-0000-1000-8000-00805f9b34fb":
            serviceList.Add("NextDstChange");
            break;
        case "0000180e-0000-1000-8000-00805f9b34fb":
            serviceList.Add("PhoneAlertStatus");
            break;
        case "00001806-0000-1000-8000-00805f9b34fb":
            serviceList.Add("ReferenceTimeUpdate");
            break;
        case "00001814-0000-1000-8000-00805f9b34fb":
            serviceList.Add("RunningSpeedAndCadence");
            break;
        case "00001813-0000-1000-8000-00805f9b34fb":
            serviceList.Add("ScanParameters");
            break;
        case "00001804-0000-1000-8000-00805f9b34fb":
            serviceList.Add("TxPower");
            break;
        default:
            break;
    }
}
MessageDialog md = new MessageDialog(String.Join("\r\n", serviceList));
md.ShowAsync();


Time for some fun, Lets make it beep!

In my case I have a Key finder (key fob) and it implements (among other services) the Immediate Alert Service, which makes it possible to make it beep.
The GATT specification shows us how to communicate with the ImmediateAlertService
Download the PDF here.

image


The documentation shows us that if we want to set the alert level we need to do that with “Write without Response”.
The different values for Alert Level can be found here.

Value 0, meaning “No Alert”

Value 1, meaning “Mild Alert”

Value 2, meaning “High Alert”

This snippet will make the key finder (key fob) sound a high alert.

var immediateAlertService = currentDevice.GetGattService(GattServiceUuids.ImmediateAlert);
var characteristics = immediateAlertService.GetCharacteristics(GattCharacteristicUuids.AlertLevel).First();
byte[] data = new byte[1];
data[0] = (byte)2;
await characteristics.WriteValueAsync(data.AsBuffer(), GattWriteOption.WriteWithoutResponse);

 

In my next blog post I will go through more of the things you can do with a key finder (key fob).