Sunday, February 21, 2010

MonoTouch Bluetooth device inquiry sample code

Abstract: I demonstrate a MonoTouch bluetooth device inquiry sample code. iPhone is an attractive device as a remote controller of LEGO mindstorms bricks or a bluetooth heart rate monitor, however, iPhone SDK itself does not provide any API to access bluetooth interface. This example shows how to access  iPhone bluetooth stack BTstack[1] which is an open-source bluetooth stack written in Obj-C from MonoTouch. This sample code [2] put on github under the New BSD license would be a good starting point for designing your original bluetooth remote control application.

 Last two weeks I have been working on iPhone LEGO mindstorms bluetooth control application. LEGO mindstorms is a very popular robotics toys which control brick has a build-in bluetooth modem. Up to now so many iPhone controlled LEGO robot demonstrations have been uploaded to YouTube, however they uses wi-fi for wireless communication. I have not seen a demonstration that controls LEGO bricks directly from iPhone. Actually, such application is not easy to create as a individuals because we are required to conclude iPhone accessory development program[3] to get technical information and libraries.

BTstack - iPhone bluetooth stack

 BTstack is the open-source iphone bluetooth stack project written in Obj-C. As described in its  getting started document, it requires a jailbreak iPhone. I used my iPhone 3G with iPhone OS 3.1.3. BTstack is easy to install using cydia. (Because I have spend almost a whole day to prepare a jailbreak iPhone and to install bstack, I think I have to write instructions here, but I should not?)

How to use BTstack from MonoTouch

 Next we proceed to use Platform invocation Services (PInvoke) to call an unmanaged c library function (BTstack) from a managed code (monotouch). Following code is a part of  a BTstack include file "include/btstack/btstack.h":

   1:  // packet handler
   2:  typedef void (*btstack_packet_handler_t) (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
   3:   
   4:  // init BTstack library
   5:  int bt_open();
   6:   
   7:  // register packet handler -- channel only valid for l2cap and rfcomm packets
   8:  // @returns old packet handler
   9:  btstack_packet_handler_t bt_register_packet_handler(btstack_packet_handler_t handler);

 Following c# code binds a C# functions to a a corresponding BTstack function using a dllimport attribute. 
  First, BTstack functions like bt_open() is simply associated to c# bt_open() method (line# 5-6). DllImport attribute has two parameters "BTstack" and "bt_open". Former argument specifies a library name in which this c function is included. BTstack client library named "libBTstack.dylib" is installed in a directory "/usr/local/lib/". Dllimport attribute requires a library name excluded prefix "lib" and a extension ".dylib". Latter named parameter is a c library function name.
   1:  using System.Runtime.InteropServices;
   2:  namespace com.ReinforceLab.iPhone.Controls.AlphaRexRemoteController {
   3:   public static class BTstackWrapper {
   4:     public delegate void BTstackPacketCallback(byte packet_type, UInt16 channel, IntPtr packet_ptr, UInt16 size);
   5:     [DllImport("BTstack", EntryPoint = "bt_open")]
   6:     public static extern int bt_open();

 Second, a method in managed code is passed to the c library as a unmanaged bluetooth packet handling callback function. To do this, first we declare a delegate (above code line #4) whose arguments is the same as the c callback function. Then we pass a delegate instance to a c function as a callback (line #5).  

   1:  [DllImport("BTstack", EntryPoint = "bt_register_packet_handler", CallingConvention=CallingConvention.Cdecl)]
   2:  public static extern void bt_register_packet_handler(BTstackPacketCallback handler); //[MarshalAs(UnmanagedType.FunctionPtr)] 
   3:   
   4:  void init() {
   5:   BTstackWrapper.bt_register_packet_handler(new BTstackWrapper.BTstackPacketCallback(BTPacketHandler.packet_handler));
   6:  }

 Point is, the callback method must be a static method and has a "MonoPInvokeCallback" attribute because monotouch requires it.

   1:  namespace com.ReinforceLab.iPhone.Controls.AlphaRexRemoteController {
   2:   internal static class BTPacketHandler {
   3:    [MonoTouch.MonoPInvokeCallback(typeof(BTstackWrapper.BTstackPacketCallback))]
   4:    static void packet_handler(byte packet_type,  UInt16 channel, IntPtr packet_ptr, UInt16 size) {
   5:      Byte[] packet = new Byte[size];
   6:      Marshal.Copy(packet_ptr, packet, (int)0, (int)size);

Discussion

 iPhone bluetooth application in MonoTouch has been demonstrated. This sample code would be a good starting point for designing iPhone bluetooth applications. Next objective is to write a bluetooth serial port profile (SPP) wrapper class to exposes it as a "serial port" so that we can port windows .net bluetooth control program to monotouch without patch.

Acknowledgments

 I would thank to BTstack open-source project. I also thank to Geoff Norton (http://twitter.com/kangamono) who let me know how to implement a callback function.

References

  1. BTstack - A Portable User-Space Bluetooth Stack, http://code.google.com/p/BTstack/
  2. Sample code on github, http://github.com/reinforce-lab/com.ReinforceLab.MonoTouch.Controls/tree/master/AlphaRexRemoteController/
  3. The Made for iPod and Works with iPhone Licensing Program, http://developer.apple.com/iphone/program/sdk/accessories.html
  4. Bluetooth.com specification documents, http://www.bluetooth.com/Bluetooth/Technology/Building/Specifications/

2 コメント:

  1. Hi. Nice you use BTstack with MonoTouch, whatever that is :)

    Please use "BTstack" with a lower case 's'. Are you on the BTstack mailing list? I just updated BTstackManager, an Objective-C class to provide easier access, check it out. It handles activation and device discovery, more to come.

    Matthias Ringwald
    ReplyDelete
  2. Thanks. I have corrected my mistakes, replacing "btstack" by "BTstack".
    I will check your BTstackManager. I had just read example/rfcomm.c and I was just wondering whether I have to handle bluetooth packet directly to use serial port profile or there is another good way to do it.

    Akihiro Uehara
    ReplyDelete