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!


|