该描述符支持单拍,连拍,调焦,前后摄像头切换,拍照录像切换。其中,调焦,前后摄像头切换,拍照录像切换功能需要特定的手机才能支持。
0x05,0x0C, // Usage Page (Consumer)
0x09,0x01, // Usage (Consumer Control)
0xA1,0x01, // Collection
0x85,0x01, // Report ID (1)
0x09,0xCD, // Usage (None)
0x09,0xA0, // Usage (VCR Plus)
0x09,0x82, // Usage (Mode Step)
0x09,0xB6, // Usage (Scan Previous Track)
0x09,0xEA, // Usage (Volume Decrement)
0x09,0xE9, // Usage (Volume Decrement)
0x0A,0x2D,0x02, // Usage (AC Zoom In)
0x0A,0x2E,0x02, // Usage (AC Zoom Out)
0x15,0x00, // Logical Minimum (0)
0x25,0x01, // Logical Maximum (1)
0x75,0x01, // Report Size (1)
0x95,0x08, // Report Count (8)
0x81,0x02, // Input
0xC0, // End Collection
功能 | KeyCode | 说明 |
---|---|---|
单拍/连拍 | 0x10/0x20 | 就是音量加/减 按键 |
拍照/录像切换 | 0x02 | |
前/后摄像头切换 | 0x04 | |
调焦缩小 | 0x80 | |
调焦放大 | 0x40 | |
按键释放 | 0x00 |
在btstack/example 下添加 hid_selfie_stick.c文件,源码如下:
#define BTSTACK_FILE__ "hid_selfie_stick.c"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "btstack.h"
#ifdef HAVE_BTSTACK_STDIN
#include "btstack_stdin.h"
#endif
// 蓝牙自拍杆示例
// to enable demo text on POSIX systems
// #undef HAVE_BTSTACK_STDIN
// timing of keypresses
#define TYPING_KEYDOWN_MS 60
#define TYPING_DELAY_MS 20
// When not set to 0xffff, sniff and sniff subrating are enabled
static uint16_t host_max_latency = 1600;
static uint16_t host_min_timeout = 3200;
#define REPORT_ID 0x01
// close to USB HID Specification 1.1, Appendix B.1
const uint8_t hid_descriptor_selfie_stick[] = {
0x05,0x0C, // Usage Page (Consumer)
0x09,0x01, // Usage (Consumer Control)
0xA1,0x01, // Collection
0x85,0x01, // Report ID (1)
0x09,0xCD, // Usage (None)
0x09,0xA0, // Usage (VCR Plus)
0x09,0x82, // Usage (Mode Step)
0x09,0xB6, // Usage (Scan Previous Track)
0x09,0xEA, // Usage (Volume Decrement)
0x09,0xE9, // Usage (Volume Decrement)
0x0A,0x2D,0x02, // Usage (AC Zoom In)
0x0A,0x2E,0x02, // Usage (AC Zoom Out)
0x15,0x00, // Logical Minimum (0)
0x25,0x01, // Logical Maximum (1)
0x75,0x01, // Report Size (1)
0x95,0x08, // Report Count (8)
0x81,0x02, // Input
0xC0, // End Collection
};
//
#define CHAR_ILLEGAL 0xff
#define CHAR_RETURN '\n'
#define CHAR_ESCAPE 27
#define CHAR_TAB '\t'
#define CHAR_BACKSPACE 0x7f
// STATE
static uint8_t hid_service_buffer[300];
static uint8_t device_id_sdp_service_buffer[100];
static const char hid_device_name[] = "BTstack HID selfie_stick";
static btstack_packet_callback_registration_t hci_event_callback_registration;
static uint16_t hid_cid;
static uint8_t hid_boot_device = 0;
// HID Report sending
static uint8_t send_buffer_storage[16];
static btstack_ring_buffer_t send_buffer;
static btstack_timer_source_t send_timer;
static uint8_t send_modifier;
static uint8_t send_keycode;
static bool send_active;
#ifdef HAVE_BTSTACK_STDIN
static bd_addr_t device_addr;
static const char * device_addr_string = "00:1F:20:86:DF:52";
#endif
static enum {
APP_BOOTING,
APP_NOT_CONNECTED,
APP_CONNECTING,
APP_CONNECTED
} app_state = APP_BOOTING;
static void send_report(uint8_t keycode){
uint8_t report[] = { 0xa1, REPORT_ID, keycode};
hid_device_send_interrupt_message(hid_cid, &report[0], sizeof(report));
}
static void trigger_key_up(btstack_timer_source_t * ts){
UNUSED(ts);
send_report(0);//释放按键
// hid_device_request_can_send_now_event(hid_cid);
}
// Demo Application
#ifdef HAVE_BTSTACK_STDIN
// On systems with STDIN, we can directly type on the console
static void stdin_process(char character){
switch (app_state){
case APP_BOOTING:
case APP_CONNECTING:
// ignore
break;
case APP_CONNECTED:// 连接完成后才能操作
switch (character)
{
case 'D'://单拍——减小音量键
send_keycode = 0x10;
break;
case 'U'://连拍——增加音量键
send_keycode = 0x20;
break;
case 't'://拍照/录像切换
send_keycode = 0x02;
break;
case 'T':// 前/后摄像头切换
send_keycode = 0x04;
break;
case 'S'://调焦缩小
send_keycode = 0x80;
break;
case 'A'://调焦放大
send_keycode = 0x40;
break;
default:
break;
}
if(send_keycode){
send_report(send_keycode);
send_keycode = 0x00;
btstack_run_loop_set_timer_handler(&send_timer, trigger_key_up);
btstack_run_loop_set_timer(&send_timer, TYPING_KEYDOWN_MS);
btstack_run_loop_add_timer(&send_timer);
}
break;
case APP_NOT_CONNECTED:
printf("Connecting to %s...\n", bd_addr_to_str(device_addr));
hid_device_connect(device_addr, &hid_cid);
break;
default:
btstack_assert(false);
break;
}
}
#else
// On embedded systems, send constant demo text
#define TYPING_DEMO_PERIOD_MS 100
static const char * demo_text = "\n\nHello World!\n\nThis is the BTstack HID selfie_stick Demo running on an Embedded Device.\n\n";
static int demo_pos;
static btstack_timer_source_t demo_text_timer;
#endif
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
UNUSED(channel);
UNUSED(packet_size);
uint8_t status;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)){
case BTSTACK_EVENT_STATE:
if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return;
app_state = APP_NOT_CONNECTED;
break;
case HCI_EVENT_USER_CONFIRMATION_REQUEST:
// ssp: inform about user confirmation request
log_info("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", hci_event_user_confirmation_request_get_numeric_value(packet));
log_info("SSP User Confirmation Auto accept\n");
break;
case HCI_EVENT_HID_META:
switch (hci_event_hid_meta_get_subevent_code(packet)){
case HID_SUBEVENT_CONNECTION_OPENED:
status = hid_subevent_connection_opened_get_status(packet);
if (status != ERROR_CODE_SUCCESS) {
// outgoing connection failed
printf("Connection failed, status 0x%x\n", status);
app_state = APP_NOT_CONNECTED;
hid_cid = 0;
return;
}
app_state = APP_CONNECTED;
hid_cid = hid_subevent_connection_opened_get_hid_cid(packet);
#ifdef HAVE_BTSTACK_STDIN
printf("HID Connected, please start typing...\n");
#else
printf("HID Connected, sending demo text...\n");
#endif
break;
case HID_SUBEVENT_CONNECTION_CLOSED:
btstack_run_loop_remove_timer(&send_timer);
printf("HID Disconnected\n");
app_state = APP_NOT_CONNECTED;
hid_cid = 0;
break;
case HID_SUBEVENT_CAN_SEND_NOW:
// if (!send_keycode){
// send_report(0);//释放按键
// }
break;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
}
/* @section Main Application Setup
*
* @text Listing MainConfiguration shows main application code.
* To run a HID Device service you need to initialize the SDP, and to create and register HID Device record with it.
* At the end the Bluetooth stack is started.
*/
/* LISTING_START(MainConfiguration): Setup HID Device */
int btstack_main(int argc, const char * argv[]);
int btstack_main(int argc, const char * argv[]){
(void)argc;
(void)argv;
// allow to get found by inquiry
gap_discoverable_control(1);
// use Limited Discoverable Mode; Peripheral; selfie_stick as CoD
gap_set_class_of_device(0x2540);
// set local name to be identified - zeroes will be replaced by actual BD ADDR
gap_set_local_name("HID selfie_stick 00:00:00:00:00:00");
// allow for role switch in general and sniff mode
gap_set_default_link_policy_settings( LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE );
// allow for role switch on outgoing connections - this allow HID Host to become master when we re-connect to it
gap_set_allow_role_switch(true);
// L2CAP
l2cap_init();
#ifdef ENABLE_BLE
// Initialize LE Security Manager. Needed for cross-transport key derivation
sm_init();
#endif
// SDP Server
sdp_init();
memset(hid_service_buffer, 0, sizeof(hid_service_buffer));
uint8_t hid_virtual_cable = 0;
uint8_t hid_remote_wake = 1;
uint8_t hid_reconnect_initiate = 1;
uint8_t hid_normally_connectable = 1;
hid_sdp_record_t hid_params = {
// hid sevice subclass 2540 selfie_stick, hid counntry code 33 US
0x2540, 33,
hid_virtual_cable, hid_remote_wake,
hid_reconnect_initiate, hid_normally_connectable,
hid_boot_device,
host_max_latency, host_min_timeout,
3200,
hid_descriptor_selfie_stick,
sizeof(hid_descriptor_selfie_stick),
hid_device_name
};
hid_create_sdp_record(hid_service_buffer, 0x10001, &hid_params);
printf("HID service record size: %u\n", de_get_len( hid_service_buffer));
sdp_register_service(hid_service_buffer);
// See https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers if you don't have a USB Vendor ID and need a Bluetooth Vendor ID
// device info: BlueKitchen GmbH, product 1, version 1
device_id_create_sdp_record(device_id_sdp_service_buffer, 0x10003, DEVICE_ID_VENDOR_ID_SOURCE_BLUETOOTH, BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH, 1, 1);
printf("Device ID SDP service record size: %u\n", de_get_len((uint8_t*)device_id_sdp_service_buffer));
sdp_register_service(device_id_sdp_service_buffer);
// HID Device
hid_device_init(hid_boot_device, sizeof(hid_descriptor_selfie_stick), hid_descriptor_selfie_stick);
// register for HCI events
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
// register for HID events
hid_device_register_packet_handler(&packet_handler);
#ifdef HAVE_BTSTACK_STDIN
sscanf_bd_addr(device_addr_string, device_addr);
btstack_stdin_setup(stdin_process);
#endif
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}
/* LISTING_END */
/* EXAMPLE_END */
由于例程时采用串口输入字母来模拟自拍杆的一些动作的,所以要在这个头文件中需要定义HAVE_BTSTACK_STDIN
#define HAVE_BTSTACK_STDIN
在串口助手中发送以下字母,可以实现对应的功能
D:单拍——减小音量键
U:连拍——增加音量键
t:拍照/录像切换
T:前/后摄像头切换
S:调焦缩小
A:调焦放大
遗留问题:本人用自己的手机测试发现,调焦缩小与放大没有功能,D与U都是单拍。希望懂的朋友指点一下