在Cocoa中创建并发送蓝牙命令帧

时间:2009-09-06 20:10:40

标签: objective-c cocoa bluetooth l2cap

我正在使用IOBluetooth Cocoa框架与蓝牙设备进行通信。到目前为止,我已经完成了发现设备及其服务的整个过程,与它配对,连接到它,现在我想发送一些实际的命令,但我遇到了一些麻烦。下面是我正在尝试使用的AVRCP配置文件规范的图形。你可以view the pdf here

我相信我需要写一个5字节的值,如图所示:

alt text

以下是我现在编写数据的方法:

- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*)l2capChannel status:(IOReturn)error {
    NSLog(@"Open Complete");
    NSMutableData *playData = [[NSMutableData alloc] initWithCapacity:5];

    unsigned char ctype = 0x0;
    unsigned char subunit = 0x90;
    unsigned char opcode = 0x7C;
    unsigned char opid = 0x44;
    unsigned char opdata = 0x0;

    [playData appendBytes:&ctype length:8];
    [playData appendBytes:&subunit length:8];
    [playData appendBytes:&opcode length:8];
    [playData appendBytes:&opid length:8];
    [playData appendBytes:&opdata length:8];

    usleep(1000);

    [l2capChannel writeAsync:[playData mutableBytes] length:40 refcon:nil];
}

当该函数运行时,设备将使用以下十六进制值0x400010进行响应。

  1. 我甚至不确定我是否正在接近 这是正确的!
  2. 是我的价值观 发送正确的 图中的例子?
  3. 我的任何帮助 努力学习这里会很多 理解!

2 个答案:

答案 0 :(得分:3)

如果您要使用AV / C帧很多,而不是创建一个结构(这对于部分字节打包不会有帮助),您应该创建一个AVCFrame类,可以轻松设置这些框架,完整性检查您提供的值,具有调试说明,并将为您处理所有蹩脚的细节。

您的代码可能如下所示:

AVCFrame *frame = [AVCFrame frameWithCommandType:AVCCommandTypePlay
                                     subunitType:mySubunitType
                                       subunitID:mySubunitID];
// You likely won't actually be writing to the L2CAPChannel. See below.
[l2capChannel writeAsync:[frame mutableBytes] length:[frame length] refcon:nil];

这不是最好的界面。您需要阅读 AV / C数字接口命令集通用规范

就字节打包而言(最终必须发生),你会想要使用类似的东西:

// Returns |subunitType| shifted and masked appropriately for bit_oring
// with subunit ID to create an address octet.
inline UInt8
AVRCAddressSubunitType(UInt8 subunitType) {
   const UInt8 kLeastThreeBytes = 0x07;
   UInt8 shiftedType = (subunitType << 3) & ~kLeastThreeBytes;
   return shiftedType;
}

// Returns |subunitID| masked appropriately for bit_oring with subunit type
// to create an address octet.
inline UInt8
AVRCAddressSubunitID(UInt8 subunitID) {
   const UInt8 kLeastThreeBytes = 0x07;
   UInt8 maskedID = subunitID & kLeastThreeBytes;
   if (subunitID & ~kLeastThreeBytes) {
      NSLog(@"*** %s: subunit ID %#hhx > 0x07 cannot be represented "
            "in the 3 bits allotted. Truncating to %#hhx.",
            __PRETTY_FUNCTION__, subunitID, maskedID);
   }
   return maskedID;
}

- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel *)l2capChannel
                          status:(IOReturn)error {
  /* might be worth looking at the error... */
  NSLog(@"%s: open complete - "
        "error: (system: %#x; subsystem: %#x; code: %#x)",
         __PRETTY_FUNCTION__,
         err_get_system(error), err_get_sub(error), err_get_code(error));

  /* to send, first pack your data into byte-sized variables */
  // some variables...
  // address byte layout is [3:7] = 9 = PANEL; [0:2] = 0 = subunit ID
  UInt8 address = (AVRCAddressSubunitType(0x09) | AVRCAddressSubunitID(0x00));
  // some more variables...

  /* create a mutable data and append the bytes in sequence */
  // some appending...
  [playData appendBytes:&address length:sizeof(address)];
  // more appending...

  /* finally, send all the bytes */
  [l2capChannel writeAsync:[playData mutableBytes]
                    length:[playData length]
                    refcon:NULL];
}

有关IOWhatever的更多详细信息,请查看详尽的IOKit文档。至少在10.5中,docset中的参考文档(与编程指南相对)有点棘手,所以你最好自己查看标题。

到目前为止,您需要查阅的文档比您查看过的文档多。您所包含的AV / C命令帧实际上是 AVCTP帧的有效负载(在命令/响应消息信息字段中承载),这是您实际必须通过L2CAP发送的内容运输。 AVCTP spec在“附录A,AVCTP上界面”中草拟了一个基本的API。

您需要找到或自己编写AVCTP库才能发送AV / C命令帧。您需要让AVCTP库包装L2CAP通道,以便您实际通过它发送命令帧并从中接收命令帧。祝好运!与硬件接口可以带来很多乐趣,您将学到很多东西。

答案 1 :(得分:1)

为了填充一个字节数组,这是一项非常多的工作。另外,你试图用每个-appendBytes:length:messages来将8个字节写入playData。

对于这样的情况,我只是为你的BT命令框声明一个结构。 NSData在这里并不能为你提供太多帮助。