Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

LightBlue Beans - Magic Beans and no beans[talk]?

4.98/5 (19 votes)
9 Nov 2014CPOL16 min read 45.7K   216  
Low Energy Bluetooth meets Arduino compatible microcontroller in a small form factor, is this just a fairy tale?

Warning: This article and associated code could be classed as experimental/research. As things unfold, you will see why.....

IoT Competition Stage 2 - Preface

When you begin to get more heavily involved with devices or are working with devices which are new to market, there isn't always someone around to hold your hand. There can also be no Step 1 Step 2 Step 3...type articles and tutorials or if there are, they are still miles off what you are trying to work with, you often have to get the gloves off and get your hands dirty. Steep learning curves may be involved, hacking away at sample code for similar areas just to see if you get that little bit of data, even a single byte of traffic which may be enough to say "we're getting somewhere".  Maybe you are working with something new that you have never developed for or there is a magnitude of things that all have to be mashed together to try and get results, whatever it may be the best thing you can do is just Get Stuck In!

Read on to meet one of those very tutorials/articles.......

Introduction

Microcontrollers are everywhere, whether it be Raspberry Pi, Arduino, Beaglebone to name a few of the common ones, they have certainly captured the imagination of hobbyists, creators, inventors or anything else you want to call them. You just have to search the web to see just how many sites are dedicated to these boards and the guys and gals that build projects on the back of them.

You just have to look at my article list and you can see there are a few related to this very subject.

Stumbling around the web one day, I came across the LightBlue Beans and these small devices looked interesting, so why not, become a backer and get something else to build upon.

Bring on the Beans!

I put down a pre-production backer payment for a "makers kit", which comprised 4 beans, replacement batteries, resistors, LED, Buzzer, twin-AA battery enclosure, some headers, sticky pads and a zip tie. This came in at US$80 including shipping to the U.K. Looking at their website now (a year later) and the beans are retailing at US$30 each. Below was the received makers kit:

Image 1

The Beans themselves came in a snug padded box, each no bigger than a matchbox, yes that unit of size that has so many variations, but everyone knows roughly! Taking a Bean out of the box and there is no getting away from the fact that these things are compact! Each Bean is 45 x20 x 8 mm. As you can see below, next to an AA battery, they do look small.

Image 2

In the picture above you can see that the ICs are on one side and the battery cell on the other. And there are a number of pads for connecting components to. All the pads are connected through the board.

Input-Output and Comms

The key selling point for these boards is the wireless communications. They are designed to be programmed and communicate with the outside world via Bluetooth Low Energy (BT LE). Now that is all well and good, but you really need to be connecting up to other things, whether it be sensors or switches. As you can see in the photo above, a number of the pads on the PCB are labelled A0, A1, 0,1,2,3,4,5. You would be right in assuming that these are analog and digital pins. However, they are multi-purpose and also allow for pulse width modulation (PWM) as well as communication through protocols Inter-Integrated Circuits (I2C), Serial Peripheral Interface (SPI) and In-Circuit Serial Programming (ICSP). The image below shows the multi-purpose nature of the pins and how they are assigned;

Image 3

In addition to these pads, a number of others labelled such as BAT,GND, R (Reset) and VCC there is an RGB LED integrated onto the PCB. This LED is located immediately to the left of the pad labelled BAT in the image above (bottom middle). 

Note: When you first power up a bean, the LED will momentarily blink as the board estabilishes itself.

But wait! That is not all, there is onboard Temperature and Accelerometers and it is also possible to read back the voltage status of the battery. The IC that provides the temperature and accelerometers is a Bosch BMA250 and more information on the chip scpecification is available in the data sheet [0].

Getting Started

The beans are shipped powered on, so technically, you should be able to communicate with them before you even take them out of the shipping envelope.

This is where I ran into my first problems. My Google Nexus phone nor my Google Nexus 10 tablet could detect the Beans. Maybe the batteries were flat, unlikely but possible. In fact, it was down to the specifications of the Bluetooth, they were not compatible with these devices. They weren't that old, but sadly, old enough not to be up to scratch! My computer had an old Belkin Bluetooth dongle, and again, that failed to detect the beans, although that was expected as it was ancient!

In the end, I ordered up a new Asus USB-BT400 compatible dongle;

Image 4

Programming and Firmware Updating

Over Bluetooth is not an option (yet!) via Windows

It was pretty clear from the start that the developers of the Beans were only really focusing on one platform initially, iOS. I have limited Apple products in my house, a sprinkling of iPods and an AppleTV and none of these devices are suitable for programming the Beans. The developers were claiming to be looking at a November 2014 release for suitable BeanLoader software to run on Windows boxes.

With the wireless programming out of the equation for now, I had to look at alternative methods to program these. This is in the end means looking at ISP programming options.

Image 5

I purchased a Deek Robot USBtinyISP programmer (above), these are available off E-Bay and various other electronics websites.

Fortunately, there is a guide already pulled together [1], so downloading the latest software from Arduino website [2] and extract the zip to a folder on my PC, then grabbing the necessary support files and programmer drivers [3] as per the guide instructions, we should be good to go!

Arduino Test Sketch Download

If we cannot get a simple sketch to download to the Arduino, then we are in for a bumpy ride, so for the very first attempt, let us keep it simple. For this initial test of the setup, I am simply going to program the Bean to rotate the onboard LED through Red- Green-Blue-Off and repeat stepping every 1 second.

The code to perform this is as follow;

C++
//Demo Program 
// rotate the onboard LED through Red - Green - Blue - Off
// 1 second interval between steps
void setup(){
  Serial.begin(57600); 
}
void loop(){
 Bean.setLed(128,0,0);
 delay(1000);
 Bean.setLed(0,128,0);
 delay(1000);
 Bean.setLed(0,0,128);
 delay(1000);
 Bean.setLed(0,0,0);
 delay(1000);
}

The Serial.Begin(57600) configures the serial communications on the Bean. The serial bus not only allows the Arduino to talk to the outside world, but also the internal messaging to the Bluetooth module.

The Bean class is used when telling the Bean hardware to specifically do something. There are a number of functions available, which range from Bluetooth specific, LED control, battery status, temperature sensor and accelerometer sensors.

The setLed command takes 3 parameters, representing the Red, Green and Blue values. These can be between 0 (off) and 255 (full on). I simply set to half way as this would give me an idea how bright the LED on the board was. As it turns out 128 is just fine under normal daylight conditions.

The delay command is a standard Arduino function that takes a number of milliseconds to pause execution for. There is a similiar Bean.sleep() method, but this physically puts the Arduino chip to sleep, the rest of the Bean stays awake.

The code then was verified and downloaded to the bean successfully and the onboard LED began to colour cycle.

The TinyISP programmer was connected via the 6 pin header using jump wires to the Bean as follows;

 AVR  | Bean
------|------
RST  -> R
SCK  -> 5
MISO -> 4
MOSI -> 3
GND  -> GND

WARNING: In this instance, I left the battery connected to the bean so did not use the supplied power from the programmer. It had its power feed jumper disconnected and also had no jump wire from VCC to the bean. If you end up connecting a socket etc for easy uploading, becareful of the power status or you will likely damage the bean. 

Image 6

Below is a recorded animated gif of the download;

Image 7

Note: The image above is missing the Serial.Begin(57600) instruction that is shown in the code further up. The was an ommission on my part, but the sketch still downloaded and operated correctly as no communication with the Bluetooth module were required. Safe to say, I should have included the statement, so something to remember for the future.

That turned out to be quite a painless first test. Hopefully the next test will be as painless!

Firmware Updating

While researching and reading the Beantalk forums, I noticed a post that talked about updating the firmware of the AT-Mega on the bean. As it turns out, this can be achieved using the same connection method as for downloading the Arduino sketch program. The difference however is we use an AVR programming application. In this instance I used AVR-Dude.

I followed the tutorial available at LadyAda [4] and using the windows installer to install the software to a folder called AVR under the Arduino Tools folder.

Having downloaded the new firmware from the Beantalk forums, I opened up a command prompt and used the following command to prove I had good communications with the device;

C:\Users\Dave\Desktop\LightBlueBean\arduino-1.0.6-windows\arduino-1.0.6\tools\avr\bin>avrdude -c usbtiny -p m328p

You can see in the command above, we have to specify the type of programmer to connect with and the type of processer we are connecting to.

In return the console responded with;

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e950f

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Now we can install the firmware using;

C:\Users\Dave\Desktop\LightBlueBean\arduino-1.0.6-windows\arduino-1.0.6\tools\avr\bin>avrdude -p m328p -c usbtiny -v -v -v -v  -U flash:w:C:\Users\Dave\Desktop\LightBlueBean\Bean_Arduino_FW_1_1_0\ATM328P_PTD_PTM_RELEASE_1_1_0.hex:i -U lfuse:w:0xff:m -U hfuse:w:0xdc:m -U efuse:w:0x07:m

There is a lot going on and I do not begin to understand it all, fortunately the firmware file came with a readme file that specify what to set for the various fuse parameters etc.

Below is an animated gif capturing the firmware programming and verification stages. It took approximately 2 minutes to peform a firmware update:

Image 8

Pairing and Communicating with Windows

Now that the first Bean is alive and accepting Arduino sketches the next test is to try and get data back from the Bean over Bluetooth.

As the device has in built temperature, one thing I want to use these for is monitoring temperature either in my server rack or outside etc. so that is what we will initially try and do.

After you have installed your Bluetooth adapter you need to pair the Bean with Windows. This can be achieved by using the "Add a Bluetooth Device" from the Bluetooth icon context menu;

Image 9

This will bring up the Windows Bluetooth screen and it will start to search for the device. The default name is "Bean", once this appears, select the Bean and click pair. The default pin code is "00000". Once you have paired the Bean, it will show connected.

Image 10

The whole Bluetooth experience on Windows comes across as a bit 'clunky', the Beans fail to pair from time to time and needed to unplug/plugin the Bluetooth adapter to sort things out. 

I also noticed that if a Bean was paired and showed as connected, if you disconnect the bean by for example removing the battery, it would not automatically reconnect unless you 'removed device' and went back through the re-pairing process. 

Windows Demo Application

Reading the various messages on the Bean website, it made a statement that you could enable the bean to have a virtual serial port using the BeanLoader software. Unfortuately, this is not available yet on Windows. This left me with no option but to try and communicate with the device using the Bluetooth Serial Service and then read and decode the messages. The basic documentation for the Serial Message Protocol is available [5], but was still pretty heavy going trying to work it out.

Understanding the Serial Message Protocol

Before we get into the actual Windows Demo App, let us take a moment to understand the serial protocol. At first I used the demo Arduino Sketch that simply wrote out the temperature. A single byte representing the Degrees Centrigrade at the device sensor.

The messages received back at the application were;

C0-03-00-00-00-1C-63-2C<br />
E0-03-00-00-00-1B-84-5C

It turns out that the 1B and 1C are simply the hex values for 27 and 28 Degrees C, initially I didn't have a clue what I was looking at in the byte sequence, so did some other tests to try and understand the messages.

I sent a simple loop running in the Bean, and wrote out the values 1 to 9. The messages being returned were;

A0-03-00-00-00-01-FF-EF
C0-03-00-00-00-02-9C-DF
E0-03-00-00-00-03-BD-CF
80-03-00-00-00-04-5A-BF
A0-03-00-00-00-05-7B-AF
C0-03-00-00-00-06-18-9F
E0-03-00-00-00-07-39-8F
80-03-00-00-00-08-D6-7E
A0-03-00-00-00-09-F7-6E

Next I wrote out a string; 

Sending: Serial.write("Hello");
Received:
C0-07-00-00-00-48-65-6C-6C-6F-0F-62
               H  e  l  l  o

You can clearly see the Hex values of the ASCII characters, starting to make sense now!

Next, I wrote out a string that was slightly longer, to see what happens when I exceed the 20byte packet length;

Sending: Serial.write("Hello World. This is a Bean!");
Received:
81-1E-00-00-00-48-65-6C-6C-6F-20-57-6F-72-6C-64-2E-20-54-68
               H  e  l  l  o     W  o  r  l  d  .     T  h 
00-69-73-20-69-73-20-61-20-42-65-61-6E-21-25-F4
   i  s     i  s     a     B  e  a  n  !

Excellent, now I can see how the header information and checksums are handled over a multi-packet message.

Ok, lets push this a bit further. The maximum message size is 70 bytes, which means I would need 4 packets to see the full specification happen, i.e. the message counters roll down and where the headers etc. all sat;

Sending Loop of: Serial.write("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); 
Received:
C3-40-00-00-00-30-31-32-33-34-35-36-37-38-39-41-42-43-44-45
42-46-47-48-49-4A-4B-4C-4D-4E-4F-50-51-52-53-54-55-56-57-58
41-59-5A-61-62-63-64-65-66-67-68-69-6A-6B-6C-6D-6E-6F-70-71
40-72-73-74-75-76-77-78-79-7A-E9-6B
E3-40-00-00-00-30-31-32-33-34-35-36-37-38-39-41-42-43-44-45
62-46-47-48-49-4A-4B-4C-4D-4E-4F-50-51-52-53-54-55-56-57-58
61-59-5A-61-62-63-64-65-66-67-68-69-6A-6B-6C-6D-6E-6F-70-71
60-72-73-74-75-76-77-78-79-7A-E9-6B
83-40-00-00-00-30-31-32-33-34-35-36-37-38-39-41-42-43-44-45
02-46-47-48-49-4A-4B-4C-4D-4E-4F-50-51-52-53-54-55-56-57-58
01-59-5A-61-62-63-64-65-66-67-68-69-6A-6B-6C-6D-6E-6F-70-71
00-72-73-74-75-76-77-78-79-7A-E9-6B
A3-40-00-00-00-30-31-32-33-34-35-36-37-38-39-41-42-43-44-45
22-46-47-48-49-4A-4B-4C-4D-4E-4F-50-51-52-53-54-55-56-57-58
21-59-5A-61-62-63-64-65-66-67-68-69-6A-6B-6C-6D-6E-6F-70-71
20-72-73-74-75-76-77-78-79-7A-E9-6B

Great, for the last test I pushed the length beyond the 70 bytes, and all that happended was the outstanding characters simply started a new message packet sequence.

Using the information grabbed from these tests allowed me to fully understand the headers, start messages, message counters  and where the checksums were found, allowing me to pull out the sent data.

Image 11

The rules for the extracting of the actual data were;

IF: 1) Byte 1, Bit 8 (MSB) = 1, this is a start message, first 5 bytes are headers
 &  2) Byte 1, Bits 1,2,3,4,5 = 0  then last 2 Bytes are checksums
    3) Remaining bytes are data
  OR
IF: 1) Byte 1, Bit 8 (MSB) = 1, this is a start message, first 5 bytes are headers
 &  2) Byte 1, Bits 1,2,3,4,5 > 0, then remaining bytes are data
  OR
IF: 1) Byte 1, Bit 8 (MSB) = 0, this is a secondary packet, first byte is header
    2) Byte 1, Bits 1,2,3,4,5 = 0 then last 2 Bytes are checksums
    3) Remaining bytes are data
  OR
IF: 1) Byte 1, Bit 8 (MSB) = 0, this is a secondary packet, first byte is header
 &  2) Byte 1, Bits 1,2,3,4,5 > 0, then remaining bytes are data

The Demo App

This is going to be a simple app......very simple app! The purpose will be nothing more to be able to read back data over the Bluetooth GATT Serial Transport.

The application GUI is shown below, once running you click the 'Find Devices' button, this will uses the Serial Service GUID to locate the Beans. Clicking a Bean will then start displaying the data in a label.

Below is the demo app listening to messages from the Bean. The Bean is running the simple getTemperature() code and writing the byte across the serial link. You will see the byte initially read and then drop to 0 and then wrap around as I hit it with an inverted 'Air Duster' to get the liquid propellant to chill down the sensor below 0'C.

Image 12

The sensor has a specification that is 8-Bit and a range of -40 to + 87.5'C, the fact that the getTemperature() method returns Centrigrade and not a value in the range i.e. 0 = -40 and 255 = 87.5 results in the wrap around. Hopefully the developers will amend this implementation and make it easier to work with.

The GUI

In the click event for the Find Beans button, we used the GUID for the Serial Service (provided from the documentation page) to populate a list of all the beans located.

C#
private async void buttonAction_Click(object sender, EventArgs e)
{ 
  buttonAction.Enabled = false; 
 
  var devices = await DeviceInformation.FindAllAsync(
                GattDeviceService.GetDeviceSelectorFromUuid(
                                            BeanMessageService.Instance.BeanSerialServiceID), 
                new string[] { "System.Devices.ContainerId" }); 
 
  listboxDevices.Items.Clear(); 
 
  if (devices.Count > 0) 
  { 
    foreach (var device in devices) 
    { 
      listboxDevices.Items.Add(device); 
    } 
  } 
  else 
  {
    labelStatus.Text = "No Beans Founds!";
    System.Diagnostics.Debug.WriteLine("Could not find any Beans. Please make sure your device is paired and powered on!"); 
  } 
  buttonAction.Enabled = true; 
}

When you click the Bean device in the listbox, a new instance of the Bean Message Service I created is initialised and the GUI starts listening for a Characteristic Value Change event to then update the display;

C#
private async void listboxDevices_SelectedIndexChanged(object sender, EventArgs e)
{
  buttonAction.Enabled = false;
  var device = listboxDevices.SelectedItem as DeviceInformation;
  labelDeviceName.Text = device.Name;
  labelStatus.Text = "Initializing Devices...";

  BeanMessageService.Instance.DeviceConnectionUpdated += OnDeviceConnectionUpdated;
  await BeanMessageService.Instance.InitializeServiceAsync(device);

  try
  {
    var deviceObject = await PnpObject.CreateFromIdAsync(PnpObjectType.DeviceContainer, device.Properties["System.Devices.ContainerId"].ToString(), new string[] { "System.Devices.Connected" });

    bool isConnected;
    if (Boolean.TryParse(deviceObject.Properties["System.Devices.Connected"].ToString(), out isConnected))
    {
      OnDeviceConnectionUpdated(isConnected);
    }
  }
  catch (Exception ex)
  {
    System.Diagnostics.Debug.WriteLine("Retreiving device properties failed with message: " + ex.Message);
  }
}

And the value change;

C#
private void Instance_ValueChangeCompleted(BeanMessage data)
{
  //Do Something with the Message Value
  System.Diagnostics.Debug.WriteLine("Client: Value Changed Event");
  System.Diagnostics.Debug.WriteLine("Client RCVD:" + BitConverter.ToString(data.data));

  //Check if we need to invoke a delegate to handle cross threading issues.
  if (this.InvokeRequired)
  {
    this.BeginInvoke(new MethodInvoker(delegate() { Instance_ValueChangeCompleted(data); }));
    return;
  }

  labelStatus.Text = "Bean Sent Data!";
  labelData.Text = BitConverter.ToString(data.data);
}

You will see that we also check to see if we need to invoke the call through delegate to update the GUI components as the messages are called from another thread.

The Bean Message Service

This was a service that dealt with all the Bluetooth GATT messages.

First, to simply the process of passing the received data up to the GUI (or whatever implements the bean message service), I first created a BeanMessage class that would contain the array of bytes.

C#
public class BeanMessage
{
  public byte[] data { get; private set; }

  public BeanMessage(byte[] inData)
  {
    data = inData;
  }
}

In the Bean Message Service, we start by declaring all the GUIDs, device services, characteristic handlers and the events.

C#
//Serial Service: A495-FF10-C5B1-4B44-B512-1370F02D74DE
//Bean Transport Characteristic A495-FF11-C5B1-4B44-B512-1370F02D74DE
//Strings below adjusted to the correct format, otherwise Guid() throws a hissy fit.
private const String SerialService = "A495FF10-C5B1-4B44-B512-1370F02D74DE";
private const String BeanTransportCharacteristic = "A495FF11-C5B1-4B44-B512-1370F02D74DE";

//Convert to GUIds
public Guid BeanSerialServiceID = new Guid(SerialService);
private Guid CHARACTERISTIC_UUID = new Guid(BeanTransportCharacteristic);

//The specification at url above state only 1 Characteristic defined.
private const int CHARACTERISTIC_INDEX = 0;

//Setup the Characteristic Configuration to Notify
private const GattClientCharacteristicConfigurationDescriptorValue CHARACTERISTIC_NOTIFICATION_TYPE = GattClientCharacteristicConfigurationDescriptorValue.Notify;

private static BeanMessageService instance = new BeanMessageService();
private GattDeviceService service;
private GattCharacteristic characteristic;
private PnpObjectWatcher watcher;
private String deviceContainerId;

//Events
public event ValueChangedCompletedHandler ValueChangeCompleted;
public event DeviceConnectionUpdatedHandler DeviceConnectionUpdated;

When the Bean Message Service is initialised, we have to obtain the necessary service for the device and then configure it for 'Notify'. The event handler to deal with the notification is attached and also tell it the GUID of the Characteristic we are interested in.

C#
public async Task InitializeServiceAsync(DeviceInformation device)
{
  try
  {
    deviceContainerId = "{" + device.Properties["System.Devices.ContainerId"] + "}";

    service = await GattDeviceService.FromIdAsync(device.Id);
    if (service != null)
    {
      IsServiceInitialized = true;
      await ConfigureServiceForNotificationsAsync();
    }
    else
    {
      System.Diagnostics.Debug.WriteLine("Access to the device is denied, because the application was not granted access, " +
      "or the device is currently in use by another application.");
    }
  }
  catch (Exception e)
  {
    System.Diagnostics.Debug.WriteLine("ERROR: Accessing your device failed." + Environment.NewLine + e.Message);
  }
}

private async Task ConfigureServiceForNotificationsAsync()
{
  try
  {
  // Obtain the characteristic for which notifications are to be received 
  characteristic = service.GetCharacteristics(CHARACTERISTIC_UUID)[CHARACTERISTIC_INDEX];

  // While encryption is not required by all devices, if encryption is supported by the device, 
  // it can be enabled by setting the ProtectionLevel property of the Characteristic object. 
  // All subsequent operations on the characteristic will work over an encrypted link. 
  characteristic.ProtectionLevel = GattProtectionLevel.EncryptionRequired;

  // Register the event handler for receiving notifications 
  characteristic.ValueChanged += Characteristic_ValueChanged;

  // In order to avoid unnecessary communication with the device, determine if the device is already  
  // correctly configured to send notifications. 
  // By default ReadClientCharacteristicConfigurationDescriptorAsync will attempt to get the current 
  // value from the system cache and communication with the device is not typically required. 
  var currentDescriptorValue = await characteristic.ReadClientCharacteristicConfigurationDescriptorAsync();

  if ((currentDescriptorValue.Status != GattCommunicationStatus.Success) ||
     (currentDescriptorValue.ClientCharacteristicConfigurationDescriptor != CHARACTERISTIC_NOTIFICATION_TYPE))
  {
    // Set the Client Characteristic Configuration Descriptor to enable the device to send notifications    // when the Characteristic value changes 
    GattCommunicationStatus status =
                        await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
                        CHARACTERISTIC_NOTIFICATION_TYPE);

    if (status == GattCommunicationStatus.Unreachable)
    {
      // Register a PnpObjectWatcher to detect when a connection to the device is established, 
      // such that the application can retry device configuration. 
      StartDeviceConnectionWatcher();
    }
    }
  }
  catch (Exception e)
  {
    System.Diagnostics.Debug.WriteLine("ERROR: Accessing your device failed." + Environment.NewLine + e.Message);
  }
}

Once the characteristic value is received and the event is raised, we process the individual packets/messages to determine if they are complete or broken down into the smaller packets. This is done using the rules described at the start of this section.

C#
private async void Characteristic_ValueChanged(
            GattCharacteristic sender,
            GattValueChangedEventArgs args)
{
  //Pull the data off the event args into a databuffer
  var buffer = new byte[args.CharacteristicValue.Length];
  DataReader.FromBuffer(args.CharacteristicValue).ReadBytes(buffer);

  //buffer to hold the stripped out data.
  var data = new byte[0];

            //Rules:
            //IF: 1) Byte 1, Bit 8 (MSB) = 1, this is a start message, first 5 bytes are headers
            // &  2) Byte 1, Bits 1,2,3,4,5 = 0  then last 2 Bytes are checksums
            //    3) Remaining bytes are data
            // OR
            //IF: 1) Byte 1, Bit 8 (MSB) = 1, this is a start message, first 5 bytes are headers
            // &  2) Byte 1, Bits 1,2,3,4,5 > 0, then remaining bytes are data
            // OR
            //IF: 1) Byte 1, Bit 8 (MSB) = 0, this is a secondary packet, first byte is header
            // &  2) Byte 1, Bits 1,2,3,4,5 = 0 then last 2 Bytes are checksums
            //    3) REmaining bytes are data
            // OR
            //IF: 1) Byte 1, Bit 8 (MSB) = 0, this is a secondary packet, first byte is header
            // &  2) Byte 1, Bits 1,2,3,4,5 > 0, then remaining bytes are data

  if (IsBitSet(buffer[0], 7))
  {
    //Primary message
    if (IsBitSet(buffer[0],0) || IsBitSet(buffer[0],1)|| IsBitSet(buffer[0],2)|| IsBitSet(buffer[0],3)|| IsBitSet(buffer[0],4))
    {
      //Not Last one
      //Ignore the first 5 bytes (header) and the rest is data
      data = new Byte[buffer.Length - 5];
      int index = 0;
      for (int position = 5; position < buffer.Length;position++)
      {
        data[index] = buffer[position];
        index++;
      }
    }
    else
    {
      //Last One
      //Ignore the first 5 bytes(header) and the last 2 bytes (checksum)
      data = new Byte[buffer.Length - 7];
      int index = 0;
      for (int position = 5; position < buffer.Length-2; position++)
      {
        data[index] = buffer[position];
        index++;
      }
    }
  }
  else
  {
    //Secondary Message
    if (IsBitSet(buffer[0], 0) || IsBitSet(buffer[0], 1) || IsBitSet(buffer[0], 2) || IsBitSet(buffer[0], 3) || IsBitSet(buffer[0], 4))
    {
      //Not Last one
      //Ignore the first 1 byte (header) and the rest is data
      data = new Byte[buffer.Length - 1];
      int index = 0;
      for (int position = 1; position < buffer.Length; position++)
      {
        data[index] = buffer[position];
        index++;
      }
    }
    else
    {
      //Last One
      //Ignore the first 1 bytes(header) and the last 2 bytes (checksum)
      data = new Byte[buffer.Length - 3];
      int index = 0;
      for (int position = 1; position < buffer.Length-2; position++)
      {
        data[index] = buffer[position];
        index++;
      }
    }
  }

  //Write out some diagnostics to compare in and out data
  System.Diagnostics.Debug.WriteLine("Buffer: " + BitConverter.ToString(buffer));
  System.Diagnostics.Debug.WriteLine("Data  : " + BitConverter.ToString(data));

  //Generate a new data message
  BeanMessage dataMessage = new BeanMessage(data);
  //Raise CompleteEvent containing the data
  this.ValueChangeCompleted(dataMessage);
}

The IsBitSet was a little helper method that did a bit shift and compare and returned true if a bit was set or not.

C#
bool IsBitSet(byte b, int pos)
{
  return (b & (1 << pos)) != 0;
}

This was the first time I had done any of this and to say it was mind blowing would be an understatement. The code was heavily butchered from the Bluetooth Generic Attribute Profile - Heart Rate Service example. Sufficient to say without that, I wouldn't have got anywhere.

All Read and No Write

Yes, this is simply reading the data one way, from Bean to PC. I didn't even want to think about writing data the other way. The whole setup was not robust enough. It didn't warrant any more research at this time. Until the Bean developers have finished the SDK for Windows and ironed out some othe the underlying Windows Bluetooth niggles, it just wasn't worth my effort to try and make this a reliable solution.

Windows Runtime and Windows 8.1 / Windows Phone 8.1

Bluetooth LE is only supported on a limited platform under Windows at this time. Therefor to enable use of the Windows Runtime under Windows Forms etc, you need to modify the Visual Studio Project as described in my other article: http://www.codeproject.com/Articles/526154/UltraDynamo-Part-It-all-starts-here

Modify the project and add the following property group and set the appropriate version;

HTML
<PropertyGroup>
  <TargetPlatformVersion>8.1</TargetPlatformVersion>
</PropertyGroup>

And don't forget to add the necessary references both to the project and to the relevant code files.

Conclusions

A very capable little device, but until the full offical support for Windows is available, it will be a bit of pain to work with, hopefully the developers get this out soon, as it would be great to properly work with these without having to use the AVR methods to program. Time will tell.

Once the full SDK is available, I will maybe have to come back and revisit this article.

As it stands, there was so much flakiness, that it doesn't warrant playing around with it until the official Windows SDK is released. 

There is also an unofficial SDK for Android on the go, but as my main development environment is Windows, I will happily wait to get that up and going first.

Maybe if you're an iOS type of person, then you could dive in and take the Beans for a spin and report back your findings!

References

[0]- http://ae-bst.resource.bosch.com/media/products/dokumente/bma250/bst-bma250-ds002-05.pdf

[1] - http://beantalk.punchthrough.com/t/bean-loader-options-for-windows-users/48/10

[2] - http://arduino.cc/en/Main/Software

[3] - https://learn.adafruit.com/usbtinyisp/drivers

[4] - http://www.ladyada.net/learn/avr/avrdude.html

[5] - https://github.com/PunchThrough/bean-documentation/blob/master/serial_message_protocol.md

History

  • 9th November 2014, Add IoT Stage 2 preface and fix a couple of formatting issues.
  • 6th November 2014, Add additonal packet diagram and link to sensor data sheet, plus typo fixes
  • 4th November 2014, First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)