Создать и отправить командный фрейм bluetooth в Какао
Я использую IOBluetooth Cocoa Framework для связи с устройством Bluetooth. До сих пор я провел весь процесс обнаружения устройства и его сервисов, сопряжения с ним, подключения к нему, и теперь я хочу отправить несколько реальных команд, но у меня возникли некоторые проблемы с ним. Ниже приведен график из спецификации профиля AVRCP, который я пытаюсь использовать. Вы можете просмотреть PDF здесь.
Я считаю, что мне нужно написать 5-байтовое значение, как показано на рисунке:
Вот метод, который у меня есть сейчас, который записывает данные:
- (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.
- Я даже не уверен, что подхожу к этому правильно!
- Верны ли значения, которые я отправляю, согласно примеру на картинке?
- Любая помощь в моем стремлении учиться здесь будет высоко ценится!
2 ответа
Если вы собираетесь много работать с кадрами 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 справочные документы (в отличие от руководств по программированию) в наборе документов были довольно странными, так что вам будет полезно взглянуть на сами заголовки.
Вам нужно будет обратиться к большему количеству документации, чем вы уже рассматривали. Командный кадр AV/C, диаграмма которого вы включили, на самом деле является полезной нагрузкой (переносится в поле Информация о команде / ответном сообщении) кадра AVCTP, то есть тем, что вы фактически должны отправить через транспорт L2CAP. Спецификация AVCTP делает набросок элементарного API в "Приложении A, Верхний интерфейс AVCTP".
Вам нужно будет либо найти или написать себе библиотеку AVCTP, чтобы отправлять кадры команд AV/C. Вы захотите, чтобы библиотека AVCTP обернула канал L2CAP, чтобы вы действительно отправляли свои командные кадры через него и получали от него свои командные кадры. Удачи! Взаимодействие с оборудованием может быть очень увлекательным, и вы многому научитесь.
Это огромная работа, чтобы заполнить массив байтов. Кроме того, вы пытаетесь прикрепить восемь байтов к playData с каждым из ваших -appendBytes:length: сообщений.
Для такой ситуации я бы просто объявил структуру для вашего командного фрейма BT. NSData на самом деле не предлагает вам много здесь.