RFCOMM channel to Lego NXT

Discussion in 'Programmer Misc' started by outpaddling, Dec 9, 2007.

  1. outpaddling

    outpaddling Guest

    Dear Mac programming heroes:

    I'm developing a cross-platform C API for communicating with the Lego
    NXT via USB and Bluetooth. The code is mostly functional on FreeBSD,
    Linux, and OS X, with the exception of Bluetooth on OS X, which is
    vastly different from the other two platforms' socket-based
    interfaces.

    I wrote a simple program to figure out the IOBluetooth framework, and
    using just the online developer docs was able to complete almost
    everything I need. I *thought* I was one line of code away from done
    when I discovered that OS X has no support for synchronous reads on an
    RFCOMM connection. (Argh.) I.e., there is no read analog to
    IOBluetoothRFCOMMChannelWrite(). OK, fine. Asynchronous I/O will
    probably become part of my API down the road anyway.

    From what I was able to dig up on the docs and on the WEB, it seems I
    need to set up a CFRunLoop in my program in order to make my callback
    function work, but I haven't found any hints as to what type of input
    source is required or how to set it up.

    Everything works up to the point of sending a command to the NXT, and
    I wrote a callback based on examples in the IOBluetooth framework
    docs. The IOBluetoothRFCOMMChannelWrite() call indicates success, but
    I can't yet read the response, presumably because my CFRunLoop is not
    set up properly.

    My code and the output are below. If someone can suggest a simple way
    to set up the CFRunLoop, I'd really appreciate it. I'm hoping to
    avoid buying yet another computer book I'll probably never look at
    again.

    Thanks.

    Code:
    #include <stdio.h>
    #include <CoreFoundation/CFArray.h>
    #include <CoreFoundation/CFNumber.h>
    #include <IOBluetooth/IOBluetoothUserLib.h>

    #define NXT_RFCOMM_CHANNEL_ID 1

    void listener(IOBluetoothRFCOMMChannelRef rfcommChannel, void
    *data, UInt16 l
    ength, void *refCon);

    int main(int argc,char *argv[])

    {
    BluetoothDeviceAddress btAddr =
    {{0x00,0x16,0x53,0x01,0x11,0xB9}};
    BluetoothDeviceName btName;
    IOBluetoothRFCOMMChannelRef rfcommChannel;
    IOBluetoothDeviceRef btDevice;
    CFStringRef name;
    char raw_name[100],
    cmd[100];
    int new_data;
    /* Create a BluetoothDeviceAddress for use in setting up a
    connection */
    btDevice = IOBluetoothDeviceCreateWithAddress(&btAddr);
    printf("btDevice = %p\n",btDevice);

    /* Quick test of address */
    if
    ( IOBluetoothDeviceRemoteNameRequest(btDevice,NULL,NULL,btName) !=
    kIOReturnSuccess )
    {
    fprintf(stderr,"IOBluetoothDeviceRemoteNameRequest() failed.
    \n");
    return 1;
    }
    else
    {
    name = IOBluetoothDeviceGetName(btDevice);
    printf("CFString name = %p\n",name);
    if ( CFStringGetCString(name,raw_name,
    100,kCFStringEncodingISOLatin1) )
    printf("Device name = %s\n",raw_name);
    else
    fprintf(stderr,"CFStringGetCString() failed.\n");
    }

    /* Open an RFCOMM channel to the NXT. The underlying baseband
    connection is automatically opened if needed. The process
    isn't finished until a listener callback function is
    registered,
    since synchronous reads are not supported. */
    if
    ( IOBluetoothDeviceOpenRFCOMMChannel(btDevice,NXT_RFCOMM_CHANNEL_ID,
    &rfcommChannel) != kIOReturnSuccess )
    {
    fprintf(stderr,"IOBluetoothDeviceOpenRFCOMMChannel() failed.
    \n");
    return 1;
    }
    else
    {
    printf("Successfully opened RFCOMM channel!\n");
    }

    /* Check Max Transmission unit, just for kicks. */
    printf("MTU = %d
    \n",IOBluetoothRFCOMMChannelGetMTU(rfcommChannel));

    /* Send a get-battery-level command and check response */
    if ( IOBluetoothRFCOMMChannelRegisterIncomingDataListener(
    rfcommChannel,listener,&new_data) == 0 )
    {
    /* Get battery level */
    cmd[0] = 0x00;
    cmd[1] = 0x0B;
    new_data = 0; /* Make use of the refCon arg by setting a
    flag. */
    if ( IOBluetoothRFCOMMChannelWrite(rfcommChannel,cmd,2,0) ==
    0 )
    {
    printf("Successfully sent command!\n");

    /* Now read back response. This will require using the
    callback function, since there is no synchronous read
    function. */

    // Use CFMessagePort?
    //
    CFRunLoopAddSource(CFRunLoopGetCurrent(),RFCOMMchannel,kCFRunLoopC
    ommonModes);
    //CFRunLoopRun();
    }
    else
    {
    fprintf(stderr,"IOBluetoothRFCOMMChannelWrite() failed.
    \n");
    }
    }
    else
    {

    fprintf(stderr,"IOBluetoothRFCOMMChannelRegisterIncomingDataListener()
    f
    ailed.\n");
    }

    if ( IOBluetoothRFCOMMChannelCloseChannel(rfcommChannel) != 0 )
    fprintf(stderr,"IOBluetoothRFCOMMChannelCloseChannel() failed.
    \n");
    IOBluetoothDeviceCloseConnection(btDevice);
    return 0;
    }


    void listener(IOBluetoothRFCOMMChannelRef rfcommChannel, void
    *data,
    UInt16 length, void *refCon)

    {
    int c,
    *new_data = refCon;

    puts("Got data!");
    for (c=0; c<length; ++c)
    printf("%02X ",((char *)data)[c]);
    putchar('\n');
    *new_data = 1;
    //CFRunLoopStop(CFRunLoopGetCurrent());
    }

    Output:

    btDevice = 0x305740
    CFString name = 0x308790
    Device name = NXT
    Successfully opened RFCOMM channel!
    MTU = 126
    Successfully sent command!
     
    outpaddling, Dec 9, 2007
    #1
    1. Advertisements

  2. outpaddling

    outpaddling Guest

    On Dec 9, 2:14 pm, Michael Ash <> wrote:
    > outpaddling <> wrote:
    > > My code and the output are below. If someone can suggest a simple way
    > > to set up the CFRunLoop, I'd really appreciate it. I'm hoping to
    > > avoid buying yet another computer book I'll probably never look at
    > > again.

    >
    > I don't really know the answer to your specific question, but there are
    > examples in /Developer/Examples/Bluetooth which appear to do what you're
    > trying to do. If you haven't taken a look at them yet, maybe they can show
    > you the missing piece.
    >
    > --
    > Michael Ash
    > Rogue Amoeba Software


    Thanks for the suggestion. I looked at the examples, but
    unfortunately, they don't seem to demonstrate what I need to do.

    I tried just calling CFRunLoopRun() with the default env, but the
    handler doesn't fire. Some other docs I found suggested that I need
    to add a source using CFRunLoopAddSource(), but I haven't been able to
    figure out how as yet.

    Thanks anyway,

    Jason
     
    outpaddling, Dec 18, 2007
    #2
    1. Advertisements

  3. outpaddling

    outpaddling Guest

    On Dec 18, 10:03 am, Michael Ash <> wrote:
    > outpaddling <> wrote:
    > > I tried just calling CFRunLoopRun() with the default env, but the
    > > handler doesn't fire. Some other docs I found suggested that I need
    > > to add a source using CFRunLoopAddSource(), but I haven't been able to
    > > figure out how as yet.

    >
    > The way this stuff *usually* works is, you create the object, set up a
    > callback, then there's some function to create a CFRunLoopSourceRef from
    > the thing you're working with. For example, a CFMessagePort has a function
    > called CFMessagePortCreateRunLoopSource which returns a CFRunLoopSourceRef
    > and you can then add that to the CFRunLoop.
    >
    > I don't know if that's really how it works with Bluetooth, and I don't
    > know what the function would be if so, but I guess that's the sort of
    > thing to look for.
    >
    > --
    > Michael Ash
    > Rogue Amoeba Software


    I actually already got this far, and found in the Xcode docs:

    CFMessagePortCreateRunLoopSource
    Creates a CFRunLoopSource object for a CFMessagePort object.

    CFRunLoopSourceRef CFMessagePortCreateRunLoopSource (
    CFAllocatorRef allocator,
    CFMessagePortRef ms,
    CFIndex order
    );

    Problem is, I don't know where to get the CFMessagePortRef from. I
    browsed the framework docs, and found some possibilities, each with
    additional parameters that I didn't know where to get. At this point
    I just had to chuckle. The time required to explore every possible
    branch of this trail grows geometrically with each additional layer,
    and I'm not even certain that CFMEssagePort is the right path to
    follow. It's clear that I would need to know a lot about Carbon/Cocoa
    programming, including several core frameworks, in order to solve this
    one on my own, and I just don't have the time, so I have to hope I can
    find an analogous code example. It would have been so simple if Apple
    would have just let us do synchronous reads. Oh well...

    Thanks for the ideas,

    Jason
     
    outpaddling, Dec 20, 2007
    #3
    1. Advertisements

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Newsbot
    Replies:
    0
    Views:
    621
    Newsbot
    Feb 16, 2007
  2. Newsbot

    LEGO Star Wars [(universal) Patch]

    Newsbot, Apr 4, 2007, in forum: Mac Gaming
    Replies:
    0
    Views:
    1,200
    Newsbot
    Apr 4, 2007
  3. Newsbot

    LEGO Star Wars II [(universal) Feature]

    Newsbot, Apr 18, 2007, in forum: Mac Gaming
    Replies:
    0
    Views:
    680
    Newsbot
    Apr 18, 2007
  4. Newsbot

    LEGO Star Wars II [(universal) Demo]

    Newsbot, May 2, 2007, in forum: Mac Gaming
    Replies:
    0
    Views:
    899
    Newsbot
    May 2, 2007
  5. Newsbot
    Replies:
    0
    Views:
    541
    Newsbot
    Nov 26, 2008
  6. Newsbot
    Replies:
    0
    Views:
    721
    Newsbot
    Dec 20, 2008
  7. Newsbot

    LEGO Batman [(Intel) Demo]

    Newsbot, Mar 26, 2009, in forum: Mac Gaming
    Replies:
    0
    Views:
    2,081
    Newsbot
    Mar 26, 2009
  8. Newsbot
    Replies:
    0
    Views:
    608
    Newsbot
    Apr 1, 2009
Loading...