我已经为OSX的objc蓝牙框架编写了一个C++包装器。包装器到目前为止可以工作,它可以 a( 获取所有配对设备的列表,b( 获取一个设备,c( 打印其名称,d( 打开通道。这一切都在没有任何报告错误的情况下工作。但是,我的蓝牙设备(RFComm 盒(从不报告连接。它保持离线状态。我在openRFCOMMChannelAsync注册的委托永远不会被调用。
由于它是一个C++包装器,因此标头是标准的C++类:
class IosBT {
public:
//IosBT();
void clearText();
void sendMessage(char* dataToSend, int len);
void log(const char *text);
void discover();
void closeConnection();
private:
void *mRFCOMMChannel;
};
该类的实现以 .mm 文件的形式编写。我分两部分解释。第一个是委托,它保留指向C++类的指针:
@interface AsyncCommDelegate : NSObject <IOBluetoothRFCOMMChannelDelegate> {
@public
IosBT* delegateCPP;
}
@end
@implementation AsyncCommDelegate {
}
-(void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel *)rfcommChannel status:(IOReturn)error
{
if ( error != kIOReturnSuccess ) {
delegateCPP->log("Error - failed to open the RFCOMM channel with error %08lx.n");
return;
}
else{
delegateCPP->log("Connected. Yeah!n");
}
}
-(void)rfcommChannelData:(IOBluetoothRFCOMMChannel *)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength
{
NSString *message = [[NSString alloc] initWithBytes:dataPointer length:dataLength encoding:NSUTF8StringEncoding];
delegateCPP->log([message UTF8String]);
}
@end
.mm 文件的第二个块是建立连接的"发现"方法。 在这里,我怀疑有些不对劲:
void IosBT::discover()
{
IOBluetoothSDPUUID *sppServiceUUID;
NSArray *deviceArray;
IOBluetoothRFCOMMChannel *chan;
log("Attempting to connectn");
sppServiceUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16ServiceClassSerialPort];
deviceArray = [IOBluetoothDevice pairedDevices];
if ( ( deviceArray == nil ) || ( [deviceArray count] == 0 ) ) {
log("Error - no selected device. ***This should never happen.***n");
return;
}
fprintf(stderr,"%lu devicesn",(unsigned long)deviceArray.count);
IOBluetoothDevice *device = [deviceArray objectAtIndex:0];
fprintf(stderr,"dev=%sn",[device.name UTF8String]);
IOBluetoothSDPServiceRecord *sppServiceRecord = [device getServiceRecordForUUID:sppServiceUUID];
if ( sppServiceRecord == nil ) {
log("Error - no spp service in selected device. ***This should never happen since the selector forces the user to select only devices with spp.**
*n");
return;
}
// To connect we need a device to connect and an RFCOMM channel ID to open on the device:
UInt8 rfcommChannelID;
if ( [sppServiceRecord getRFCOMMChannelID:&rfcommChannelID] != kIOReturnSuccess ) {
log("Error - no SPP device.n");
return;
}
AsyncCommDelegate* asyncCommDelegate = [[AsyncCommDelegate alloc] init];
asyncCommDelegate->delegateCPP = this;
if ( [device openRFCOMMChannelAsync:&chan withChannelID:rfcommChannelID delegate:asyncCommDelegate] != kIOReturnSuccess ) {
log("Error - open sequence failed.n");
return;
}
if ( chan == NULL ) {
log("Error - chan == NULL");
return;
}
[chan setDelegate:asyncCommDelegate];
log("Successfully connected");
mRFCOMMChannel = (__bridge void*)chan;
}
我只使用带有fprintf的屏幕上的"log"方法记录从蓝牙设备接收的数据。
主程序很简单:
IosBT ad;
ad.discover();
getchar();
ad.closeConnection();
输出为:
Attempting to connect
1 devices
dev=GN-ATTYS1-FB86
Successfully connected
但是,从不调用"通道数据"方法,但应打印从蓝牙设备接收的数据。蓝牙盒报告它未连接。但是,顶部栏上的右侧蓝牙图标显示Mac已连接到GN-ATTYS1-FB86。然而,事实并非如此。
这里的演示使用窗口在屏幕上打印,适用于我的蓝牙盒:https://github.com/berndporr/coco-bluetooth-rfcomm。对我来说,唯一明显的区别是我使用命令行。
问题是:为什么OSX报告一个有效的连接,但实际上没有建立一个连接?我怎么可能在不报告任何错误的情况下调试它?
为了从命令行使用蓝牙,需要在线程中重复调用NSRunLoop
对象。由于它是一个C++包装器,我将线程创建为C++线程,然后在循环中调用:
while (running) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
running
标志是线程中的常用标志,设置为零以停止线程。
整个编码示例在这里:C++蓝牙包装器,它打开RFCOMM通道,然后将接收到的数据打印到stdout,直到用户按回车键。
这里的答案是如何创建运行事件循环帮助我找到了问题的根源。