Quantcast
Channel: @awsomedevsigner » .NET
Viewing all articles
Browse latest Browse all 3

DigiPOT – A Home-Made Gadgeteer Module – Part Two

0
0

 

In the last part you read about the CAT5114 (digital potentiometer) and how to create your own HM (Hardware Module) and SM (Software Module) for .NET Gadgeteer. This part (which is also the last part of this series) will extend the project using the awesome BLE (Bluetooth Low Energy) development kit from Bluegiga DKBLE to dim a led via Bluetooth.

Bluegiga DKBLE Development Kit

The Bluegiga DKBLE Development Kit comes with:

  • The development board – Integrated on the board: UART-to-USB converter, 3 programmable buttons, Display (SPI), 3-axis accelerometer, Altimeter, Potentiometer, IO headers, CR3032 battery-holder, TPS62730 DC/DC Converster, Debug interface + on-board CC debugger, External SPI flash (for OTA updates)
  • 4 BLE carrier boards (BLE121LR, BLE113-A,BLE113-A-M256K,BLE112) – all Bluetooth Smart Modules with different ranges, memory sizes, etc.
  • USB cable
  • Documentation

That’s for the hardware side. On the software-side you get:

  • A full Bluetoot Smart stack with L2CAP, ATT, GATT, and Security Manager
  • BGAPI binary protocol for applications with separate host (MCU)
  • BGScript scripting language for standalone applications
  • Profile Toolkit for Bluetooth low energy profile development

The software-development kit itself contains:

  • BGAPI and BGScript documentation
  • BGLib host library, which implements BGAPI protocol (ANSI C)
  • BGScript compiler
  • BGScript and BGAPI example applications
  • iOS and Android example application (no WP samples)
  • Application notes

It is a very comprehensive and out-of-the-box kit that allows you to develop standard solutions (like heart-rate monitors, keyboard or for example altimeters and battery-monitoring solutions). You can however implement your own GATT based solutions using BGScript. BGScript is a VB-Script like language, easy to use and easy to learn.

Here is an image of the DKBLE development board (The image was taken from Bluegiga):

front_rgb

Attached on the top you can see the BLE121LR (Long Range carrier (up to 480 meters)) carrier. I am not going deeper into the specifications or the components on the board. If you are interested, you can read this “DKBLE out-of-the-box quick start guide” on the Bluegiga support site.

I bought mine at Mouser Electronics. It’s $199 and about €170.

Again – I am not affiliated with Mouser or Bluegiga.

Making the BLE121LR-A-M256K Communicate with the FEZ Spider Board

One of the simplest communication protocols out there is still the UART protocol, a serial protocol that is implemented by nearly any microcontroller on the market today. The Fez Spider is driven by a microcontroller called LPC2478 (direct link to the datasheet), it has four UARTS with fractional baud rate generation:

  • One with modem I/O control
  • One with IrDA support
  • All with FIFO (FirstIn-FirstOut)

Here is a piece of the LPC2478 block diagram showing the above mentioned UARTS:

UART_LPC2478

As you can see, only UART1 offers CTS (Clear to Send) and RTS (Request to send) features as well as DTR (Data Terminal Ready) and DSR (Data Set Ready). This is what is called RTS/CTS Hardware Flow Control. All of the UARTS have the RX (Receiver) and TX (Transmitter) capabilities. This is what I needed to make the DKBLE board communicate with the FEZ Spider. The Gadgeteer Socket Type “U” offers these connections out of the box:

sockettype_u

To wire the DKBLE dev-board with the Fez Spider (Socket U) two wires are needed:

SerialWiring

This type of wiring is sometimes called also crossover or null-modem wiring. You can find similar approaches also with Ethernet cables that are cross-wired and connect 2 PC’s for example directly without a switching-hub for example. It’s important that TX can talk to RX and vice-versa to receive and transmit data. That’s all the magic behind it. The article “The Difference Between a Null Modem and Straight Through Serial Cable” explains everything in a simple and understandable way.

To have a simple and straight-forward way of wiring both devices together, I am using the “Breakout TB10 Module” HW-Module from GHI. That way I don’t have to solder anything to make it work.

DKBLE

The right hand image is a bit blurry, but it’s still kind of readable. On the left-hand side you can see the Breakout TB 10 module with pins 4 (TX) and pin 5 (RX) connected. Pin 4 is connected to RX on the DKBLE development board and pin 5 to TX on DKBLE.

That’s it from the hardware-side. Let’s move on to the code.

Developing Firmware Using the DKBLE-Board and BGScript™

To download the “Bluegiga Bluetooth Smart Software (SDK)” just visit the product site and click on one of the available modules. The pages have the same structure. To make it easier, I have created a screenshot to show you which files should be downloaded to start BLE development (click on the screenshot to see the larger image file):

developmentkit

For a deep-dive download all of the files within the “USER GUIDES” and “APPLICATION NOTES” section. for the SDK within the section “SOFTWARE RELEASES” download the latest SDK (at the time of this writing v.1.3.2). Download the documents the SDK and unzip the SDK folder into a destination of your choice. I have used the root of my C:\ drive.

Two of the files within your DKBLE folder are the two files you will use the most during development:

  • within [YOUR_INSTALLATION_FOLDER]\BleUpdate\BleUpdate.exe <== This is the tool you will use to deploy and compile your BGScript firmware
  • within [YOUR_INSTALLATION_FOLDER]\ble-1.3.2-122\bin\blegui.exe <== This is the tool that you will use to test your firmware (please read within the documentation about this tool, because it is just too mighty to write a few lines about it here)

Files Needed for a Custom BLE Firmware

To successfully compile and deploy a BGScript firmware, you will need at least the following files:

  • Your .bgscript file (the file where you implement the actual firmware using BGScript)
  • Your .bgproject file . You can compare this file to a project-configuration file. Within this file you define the different types of files that are needed to successfully compile your firmware.
  • Your hardware.xml file. This is where you define the hardware-based properties. Like the UART channel to be used, the sleep-mode of the BLE-Module and more.
  • Your services.xml file. This is the file where you define the GAP (Generic Access Profile) and your GATT (Generic Attribute Profile) services. This is for example the place where you would define your Heart-Rate–Monitor-Profile.

Once you have all this files together in one place (you have to create them manually). You can call BleUpdate.exe to compile your firmware. If you are familiar with NotePad++ you can download BGScript language specific syntax-highlighting and completition files that can be used with NotePad++ (you can download them on the same product pages as the SDK, mentioned in this blog-post).

The Hardware.xml File

<?xml version="1.0" encoding="UTF-8" ?>
<hardware>
    <usart channel="1" alternate="1" baud="115200" endpoint="none" flow="false" />
    <script enable="true" />
    <sleep  enable="false" />
</hardware>

What you can see here, is that I am disabling flow-control to allow the BLE-Module not to get stuck, even if there is no host connected to the TX pin. Otherwise the internal send-buffer will be filled and the firmware will stop working. The script-tag attribute enable guarantees that the BGScript application and the VM are enabled on the BLE-Module. The sleep-tag enable-attribute set to “false” prevents the firmware from entering any of the sleep-modes.

The Services.xml File

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!-- 1800: org.bluetooth.service.generic_access -->
    <service uuid="1800" id="s_generic_access">
        <description>Generic Access</description>

        <!-- 2A00: org.bluetooth.characteristic.gap.device_name -->
        <characteristic uuid="2A00" id="c_device_name">
            <description>Device Name</description>
            <properties read="true" const="true" />
            <value>Awesome Device Commander</value>
        </characteristic>

        <!-- 2A01: org.bluetooth.characteristic.gap.appearance -->
        <characteristic uuid="2A01" id="c_appearance">
            <description>Appearance</description>
            <properties read="true" const="true" />
            <!-- Generic device, Generic category -->
            <value type="hex">0000</value>
         </characteristic>
    </service>
  
    <!-- custom service -->
    <service uuid="624e957f-cb42-4cd6-bacc-84aeb898f69b" advertise="true">
        <description>Device Commander Service</description>
        
        <!-- the command to execute --> 
        <characteristic uuid="4d6309c4-6ca9-44bf-a343-91880301100d" id="c_command">
            <description>Device Command</description>
           <properties write="true"  />
            <value length="20" variable_length="true"  />
        </characteristic>
        <characteristic uuid="f8909bb7-73a0-48c9-afe6-23bd1ef61bc7" id="alive_message">
            <description>Alive Message</description>
           <properties read="true" notify="true" />
            <value length="32" variable_length="true"  />
        </characteristic>
    </service>
</configuration>

The upper part of the file until the comment “<!– custom service –>” contains the generic access profile (GAP), that is needed for device-discovery and device identification. Which is part of the official GATT (Generic Attribute Profile) Specifications here (org.bluetooth.characteristic.gap.device_name) and here(org.bluetooth.characteristic.gap.appearance) . This upper part is mandatory and should be included in any GATT-Service definition.

The lower part starting from the comment “<!– custom service –>” defines the, “Device Commander Service“. This service is a custom service. A custom service defines a service that has no pre-defined uuid like the battery-status service or a heart-rate service for example. You can see a list of standard services here (Official Bluetooth Developer Portal): Services.

For each custom service a 128-Bit uuid has to be used. 36 character uiid’s are reserved for standard-services. The Bluetooth SIG (Special Interest Group) recommends to use this tool to generate 128 Bit uuid’s (maintained by the ITU (International Communication Union)): Universally Unique Identifiers.

The “Device Commander Service” has the custom uuid “4d6309c4-6ca9-44bf-a343-91880301100d” assigned. It has two characteristics, one that can be written and one that can be read and sends notifications, when new values are available:

<!-- the command to execute --> 
        <characteristic uuid="4d6309c4-6ca9-44bf-a343-91880301100d" id="c_command">
            <description>Device Command</description>
           <properties write="true"  />
            <value length="20" variable_length="true"  />
        </characteristic>

The above XML represents the command characteristics that is writable. You can write up to 20 bytes with a variable length. This means for example 1 byte, 2 bytes or 10 bytes – but a maximum of 20 bytes.

Characteristics 2 can be compared to a notification service. This service simply sends sequentially an integer-value to the host (the Windows Phone for example) that is paired with the Bluetooth device:

</characteristic>
        <characteristic uuid="f8909bb7-73a0-48c9-afe6-23bd1ef61bc7" id="alive_message">
            <description>Alive Message</description>
           <properties read="true" notify="true" />
            <value length="32" variable_length="true"  />
        </characteristic>

It’s named “Alive Message” and has the read and notify attribute set to true. It can contain a value (any value) that has a max. length of 32 bytes and is variable in length. The host can then subscribe for this notification using an event (you will see how this works when you read through the Windows Phone source).

The deviceCommander.bgproj File

The .bgproj file can be compared to a .csproj file (Visual Studio C# project file). It contains the references to all files that are needed for a successful compilation of the Bluetooth software:

<?xml version="1.0" encoding="UTF-8" ?>
<project>
    <gatt in="services.xml" />
    <device type="ble121lr-m256k" />
    <hardware in="hardware.xml" />    
    <script in="deviceCommander.bgs" />
    <image out="out-deviceCommander.hex" />    
</project>

The services.xml file was already discussed. Usually this file is called gatt.xml, because the GATT services are defined within this file. You can name it anything you want, as long as it is present within the same directory. This rule is valid for all other files as well. The device type specifies the type of the BLE-Chip that is used. In this case it is the BLE121LR (long range) chip from Bluegiga with 256k memory. The hardware specific setup can be found within the hardware-tag, which is defined in the file hardware.xml we also discussed earlier. The BGScript file is defined within the file deviceCommander.bgs, which contains the BGScript-Code. The image-tag defines the name of the actual firmware-file that goes with the standard-extension .hex. This is actually uploaded to the BLE-Chip.

The deviceCommander.bgs File

Despite of all the profiles and definitions we discussed earlier, a file that executes the logic and defines the actual firmware is needed. This is where the file deviceCommander.bgs comes into play:

#holding the current tick
dim tick
dim string(3)

#catch system boot
event system_boot(major, minor, patch, build, ll_version, protocol_version, hw)
    #set gap discovery mode
    call gap_set_mode(gap_general_discoverable, gap_undirected_connectable)
    #Enable bonding - important for Windows 8.1 and Windows Phone
    call sm_encrypt_start(0, 1)
    
    #timer
    call hardware_set_soft_timer(32768, 0, 0)
    
    tick = 1
end

event hardware_soft_timer(handle)
    if tick < 99 then
    tick = tick +1 
    end if
    
    if tick >= 99
        tick = 1
    end if
    
    string(0:1) = (tick / 100) + 48
    string(1:1) = (tick / 10) + (tick / -100 * 10) + 48
    string(2:1) = tick + (tick / 10 * -10) + 48
    
    
    call attributes_write(alive_message,0,3,string(0:3))
end

#Listen to incoming commands and send them to uart
event attributes_value(connection, reason, handle, offset, value_len, value)
    if handle = c_command then
        
        #call hardware_spi_transfer(0, value_len, value(0:value_len))   
        #Now actually send data again
        call system_endpoint_tx(5,value_len , value(0:value_len))
            
        
    end if
end

event connection_disconnected(handle, result)
    # if disconnected, return to advertisement mode (disabled for lab environment)
    call gap_set_mode(gap_general_discoverable, gap_undirected_connectable)
    #Enable bonding - important for Windows 8.1 and Windows Phone
    call sm_set_bondable_mode(1)
    call sm_encrypt_start(0, 1)
    
    #set the timer again
    call hardware_set_soft_timer(32768, 0, 0)
    tick = 1
end

event connection_status(connection, flags, address, address_type, conn_interval, timeout, latency,bonding)
                    call sm_encrypt_start(connection, 1)
end

Not much code for a firmware. Only 61 lines of code. The entry point is the system_boot event. This event is fired when the Bluetooth module has finished booting. First the GAP discovery mode is set and the device is generally discoverable. In addition to the genral_discoverable constant there is a second constant gap_unidirected_discoverable. Both of this constants together define the advertising packet for a peripheral. A device that can be connected to a central (this is Apple jargon. The peripheral is for example an iBeacon and he central an iPhone). But we are not dealing with IPhones or Android here, we are dealing with Windows Phone (not very much examples out there for Windows Phone).

The next line initiates bonding with the master device (I call it master now) which is for example a Windows Phone 8.1 or a Windows 8.1 tablet/pc/whatever running Windows 8.1 supporting BLE (Bluetooth Low Energy). Without this line the bonding will fail.

To have something like a timer sending out messages every second (approximately every second). I use a function or sub (I think that is more appropriate) that was published by Bluegiga. The value 32768 represents the 32.768 kHz frequency that is internally used by the module for clock-generation. If you start counting at 32768 to 65535. Exactly when the 65535 barrier is broken, one second has passed. Please don’t ask me how this works internally. I have not had the interest (until now, I am getting curious about this topic) to work with crystals and timing. Instead please read the comments on this Sparkfun Product Site to get more information’s about this topic.

The attribute_values event is fired, when a bonded master is sending an incoming-value for a specified characteristics. You can distinguish the characteristics by their id. In my case it is the c_command id. If you take a look at the services.xml file you can see the id within the first characteristics within the first characteristics of the “Device Commander Service“. Via the system_endpoint_tx method (the C-Function representation returns void, therefore I call it sub). The first parameter is the endpoint, the second parameter is the length of the data and the last parameter is the array containing the data. That’s the point where I am writing the data to the serial port. The Fez Spider (or any other UART capable device) can receive the data then, if connected to RX and TX.

If the connection was disconnected, the same functions/subs like in the system_boot events are called to re-establish the desired functionality. When the event connection_status is fired, I am initiating the encryption on the connection using the sm_encrypt_start function/sub. This is required for Windows Phone as well.

The Windows Phone Part

The Windows Phone part does not require a lot of code either. There is a manual step that needs to be done prior to writing any code. We need to modify the Package.appmanifest file to add the Bluetooth capability. This cannot be done using Visual Studio currently (maybe in 2015?). Right click the app-manifest file and select view code. Then insert this snippet:

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

This allows the app to access any device that comes up with a genericaAccess profile.

To move the wiper from the low-terminal to the high-terminal, I am using two buttons and one button to initiate the connection:

2015-04-28 16_40_50-Project My Screen App

 

This image was taken using the Project My Screen app from Microsoft. Just click it to get a better image. If your Windows Phone 8.1 won’t ask you to project your screen, just follow this tutorial on YouTube: Windows Phone Tutorial: Fix for Project My Screen Not Working . That fixed it for me.

Ok. Back to the source now. Here is the source-code of the Main.xaml.cs file:

 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=391641

namespace BleDimmer
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private GattCharacteristic _characteristics;

        public MainPage()
        {
            this.InitializeComponent();

            this.NavigationCacheMode = NavigationCacheMode.Required;

            //Prepare the bluetooth connection
           
        }

        private async Task PrepareBluetoohConnection()
        {
            var bleDevices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(
                                 GattDeviceService.GetDeviceSelectorFromUuid(new Guid("624e957f-cb42-4cd6-bacc-84aeb898f69b")));




            if (bleDevices.Count > 0)
            {
                var device = bleDevices[0];



                var gattService = await GattDeviceService.FromIdAsync(device.Id);



                var characteristict = gattService.GetCharacteristics(new Guid("4d6309c4-6ca9-44bf-a343-91880301100d"));



                var alive = gattService.GetCharacteristics(new Guid("f8909bb7-73a0-48c9-afe6-23bd1ef61bc7"));



                if (alive.Count > 0)
                {
                    alive[0].ValueChanged += alive_ValueChanged;

                    var status = await alive[0].WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
                }


                try
                {
                    _characteristics = characteristict[0];
                    GetConnectionStatus();
                }

                catch (Exception ex)
                {

                }

            }
        }

        private void GetConnectionStatus()
        {
            if (_characteristics.Service.Device.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected)
            {
                lblStatus.Text = "CONNECTED";
            }
            if (_characteristics.Service.Device.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Disconnected)
            {
                lblStatus.Text = "DISCONNECTED";
            }
        }



        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.
        /// This parameter is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // TODO: Prepare page for display here.

            // TODO: If your application contains multiple pages, ensure that you are
            // handling the hardware Back button by registering for the
            // Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
            // If you are using the NavigationHelper provided by some templates,
            // this event is handled for you.

            
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            await PrepareBluetoohConnection();

        }

        void alive_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
        {
            var bytes = args.CharacteristicValue.ToArray();
            var text = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
            Debug.WriteLine(text);

        }

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            if (_characteristics != null && _characteristics.Service.Device.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected)
            {
                var writer = new DataWriter();
                writer.WriteString("l" + "\r\n");


                var status = await _characteristics.WriteValueAsync(writer.DetachBuffer());

                //Debug.WriteLine(Enum.GetName(typeof(GattCommunicationStatus), status));
                GetConnectionStatus();
                return;
            }

            GetConnectionStatus();
        }

        private async void Button_Click_2(object sender, RoutedEventArgs e)
        {
            if (_characteristics != null && _characteristics.Service.Device.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected)
            {
                var writer = new DataWriter();
                writer.WriteString("h" + "\r\n");


                var status = await _characteristics.WriteValueAsync(writer.DetachBuffer());

                //Debug.WriteLine(Enum.GetName(typeof(GattCommunicationStatus), status));
                GetConnectionStatus();
                return;
            }

            GetConnectionStatus();
        }
    }
}

The code is not too hard to understand. The important things are starting from line 43. This is where the phone is looking for a paired device (you have to pair the device before you can actually use it – no pairing no fun) with a specific GUID. If you compare that GUID with the GUID in services.xml, you will see that the code is looking for the device “Device Commander Service” , the GUID is the same GUID. If the device was found (the enumeration contains at least one element) the code requests the available GATT-Services. The first characteristic the code is looking for (unfortunately the variable name is a bad one “characteristic”, saying nothing) is the one that can be written and will accept our commands. The second characteristic is the “alive” characteristic and will send every second a different integer via the “ValueChanged” event.

The GetConnectionStatus() method is executed every-time something happens to the BLE connection and assigns the current status of the connection to the TextBlock named “lblStatus”.

Real magic happens within the 2 ButtonClick-Events. One is responsible for sending the ‘l’ for (move to low-terminal) and the other for sending ‘h’ (move to high-terminal) commands to any device that is UART capable and can receive the commands.

That’s it! I hope you enjoyed this series, like I enjoyed writing it. Here is the source and a short video of the self made module in action.

The Video

The Source

devicecommander DKBLE Source

BleDimmer Windows Phone Solution

NETMF Solution (Software Module)


Viewing all articles
Browse latest Browse all 3

Latest Images

Trending Articles





Latest Images