Giao thức HID của thiết bị theo dõi đầu

Giao thức thiết bị có giao diện dành cho người dùng (HID) của thiết bị theo dõi chuyển động đầu, dành cho các thiết bị chạy Android 13 trở lên, cho phép kết nối thiết bị theo dõi chuyển động đầu với thiết bị Android thông qua USB hoặc Bluetooth và hiển thị khung Android cũng như các ứng dụng thông qua khung cảm biến. Giao thức này dùng để kiểm soát hiệu ứng ảo hoá âm thanh (âm thanh 3D). Trang này sử dụng các thuật ngữ device (thiết bị) và host (máy chủ) theo nghĩa Bluetooth, trong đó device có nghĩa là thiết bị theo dõi chuyển động của đầu và máy chủ có nghĩa là máy chủ lưu trữ Android.

Nhà sản xuất thiết bị phải định cấu hình thiết bị Android để hỗ trợ giao thức HID của thiết bị theo dõi đầu. Để biết thêm thông tin chi tiết về cấu hình, hãy xem README về cảm biến động.

Trang này giả định rằng bạn đã quen thuộc với các tài nguyên sau:

Cấu trúc cấp cao nhất

Khung Android xác định thiết bị theo dõi chuyển động của đầu là thiết bị HID.

Để biết ví dụ đầy đủ về bộ mô tả HID hợp lệ, hãy xem Phụ lục 1: Ví dụ về Bộ mô tả HID.

Ở cấp cao nhất, thiết bị theo dõi đầu là một bộ sưu tập ứng dụng có trang Sensors (0x20) và cách sử dụng Other: Custom (0xE1). Bên trong bộ sưu tập này có một số trường dữ liệu (đầu vào) và thuộc tính (tính năng).

Tài sản và trường dữ liệu

Phần này mô tả các thuộc tính và trường dữ liệu trong một tập hợp ứng dụng của thiết bị theo dõi đầu.

Thuộc tính: Nội dung mô tả cảm biến (0x0308)

Thuộc tính Nội dung mô tả cảm biến (0x0308) là thuộc tính chuỗi ASCII (8 bit) chỉ đọc và phải chứa các giá trị sau:

Trình theo dõi chuyển động đầu phiên bản 1.0:

#AndroidHeadTracker#1.0

Trình theo dõi chuyển động đầu phiên bản 2.0 (có trong Android 15 trở lên), bao gồm cả tính năng hỗ trợ âm thanh LE:

#AndroidHeadTracker#2.0#x

x là một số nguyên (1, 2, 3) cho biết truyền tải hỗ trợ:

  • 1: ACL
  • 2: ISO
  • 3: ACL + ISO

Không có ký tự kết thúc rỗng, nghĩa là tổng kích thước của thuộc tính này là 23 ký tự 8 bit đối với phiên bản 1.0.

Thuộc tính này đóng vai trò là bộ phân biệt để tránh xung đột với các cảm biến tuỳ chỉnh khác.

Thuộc tính: Mã nhận dạng duy nhất ổn định (0x0302)

Thuộc tính Mã nhận dạng duy nhất ổn định (0x0302) là một mảng chỉ có thể đọc gồm 16 phần tử, mỗi phần tử 8 bit (tổng 128 bit). Không có ký tự kết thúc rỗng nào được dự kiến. Thuộc tính này là không bắt buộc.

Thuộc tính này cho phép các thiết bị theo dõi chuyển động đầu được tích hợp trong thiết bị âm thanh tham chiếu đến thiết bị âm thanh mà chúng được gắn vào. Các giao thức sau được hỗ trợ.

Trình theo dõi đầu độc lập

Nếu thuộc tính Mã nhận dạng duy nhất ổn định (0x0302) không tồn tại hoặc được đặt thành tất cả số 0, thì tức là thiết bị theo dõi đầu không được gắn vĩnh viễn với thiết bị âm thanh và có thể được sử dụng riêng biệt, chẳng hạn như bằng cách cho phép người dùng liên kết thiết bị theo dõi đầu theo cách thủ công với một thiết bị âm thanh riêng biệt.

Tham chiếu sử dụng địa chỉ MAC Bluetooth

Bộ tám 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Giá trị 0 0 0 0 0 0 0 0 B T5 MAC Bluetooth

Trong lược đồ này, 8 octet đầu tiên phải là 0, octet 8 và 9 phải chứa giá trị ASCII tương ứng BT, 6 octet sau được hiểu là địa chỉ MAC Bluetooth, giả sử thiết bị theo dõi đầu áp dụng cho bất kỳ thiết bị âm thanh nào có địa chỉ MAC này. Địa chỉ này phải là địa chỉ nhận dạng, ngay cả khi thiết bị sử dụng một địa chỉ MAC ngẫu nhiên để thiết lập kết nối. Các thiết bị chế độ kép kết nối qua Bluetooth cổ điển (định dạng HID v1.0) và Bluetooth LE (định dạng HID v2.0) phải hiển thị hai chỉ số mô tả HID có cùng địa chỉ nhận dạng. Các thiết bị chế độ kép có thiết bị trái và phải tách biệt phải hiển thị Bluetooth LE HID bằng thiết bị chế độ kép chính thay vì thiết bị phụ chỉ có LE.

Tham chiếu bằng mã nhận dạng duy nhất (UUID)

Bất cứ khi nào bit quan trọng nhất (MSB) của octet 8 được đặt (≥0x80), trường này sẽ được hiểu là mã nhận dạng duy nhất (UUID), như đã chỉ định trong RFC-4122. Thiết bị âm thanh tương ứng cung cấp cùng một mã nhận dạng duy nhất (UUID) đã được đăng ký trên khung Android thông qua một cơ chế không xác định dành riêng cho loại phương thức truyền tải được sử dụng.

Thuộc tính: Trạng thái báo cáo (0x0316)

Thuộc tính Trạng thái báo cáo (0x0316) là một thuộc tính đọc/ghi có ngữ nghĩa chuẩn như được xác định trong thông số kỹ thuật HID. Máy chủ lưu trữ sử dụng thuộc tính này để cho thiết bị biết cần báo cáo sự kiện nào. Chỉ sử dụng các giá trị Không có sự kiện (0x0840) và Tất cả sự kiện (0x0841).

Giá trị ban đầu cho trường này phải là Không có sự kiện nào và chỉ máy chủ lưu trữ mới được sửa đổi giá trị này trên thiết bị.

Thuộc tính: Trạng thái nguồn (0x0319)

Thuộc tính Trạng thái nguồn (0x0319) là thuộc tính đọc/ghi có ngữ nghĩa chuẩn như được xác định trong thông số kỹ thuật HID. Máy chủ lưu trữ sử dụng thuộc tính này để cho thiết bị biết trạng thái nguồn của thiết bị. Chỉ sử dụng các giá trị Pin đầy (0x0851) và Tắt nguồn (0x0855).

Giá trị ban đầu cho trường này do thiết bị xác định và không bao giờ được sửa đổi bởi thiết bị mà chỉ do máy chủ lưu trữ.

Thuộc tính: Khoảng thời gian báo cáo (0x030E)

Thuộc tính Report Interval (0x030E) là một thuộc tính đọc/ghi có ngữ nghĩa chuẩn như được xác định trong thông số kỹ thuật HID. Máy chủ lưu trữ sử dụng thuộc tính này để cho thiết bị biết tần suất báo cáo các lần đọc dữ liệu. Đơn vị là giây. Phạm vi hợp lệ cho giá trị này do thiết bị xác định và được mô tả bằng cơ chế Tối thiểu/Tối đa thực tế. Tốc độ báo cáo tối thiểu phải được hỗ trợ là 50 Hz và tốc độ báo cáo tối đa được đề xuất là 100 Hz. Do đó, khoảng thời gian báo cáo tối thiểu phải nhỏ hơn hoặc bằng 20 mili giây và nên lớn hơn hoặc bằng 10 mili giây.

Thuộc tính: Chuyển tải LE do nhà cung cấp đặt trước (0xF410)

Thuộc tính Truyền tải năng lượng thấp do nhà cung cấp dành riêng (0xF410) là một thuộc tính đọc/ghi có ngữ nghĩa chuẩn như được xác định trong thông số kỹ thuật HID. Máy chủ lưu trữ sử dụng thuộc tính này để cho biết phương thức truyền tải đã chọn (ACL hoặc ISO). Chỉ sử dụng các giá trị ACL (0xF800) và ISO (0xF801) và cả hai giá trị này đều phải có trong tập hợp logic.

Tài sản này được định cấu hình trước khi bật/tắt trạng thái báo cáo.

Trường dữ liệu: Giá trị tuỳ chỉnh 1 (0x0544)

Trường Giá trị tuỳ chỉnh 1 (0x0544) là trường nhập dữ liệu dùng để báo cáo thông tin thực tế theo dõi chuyển động của người dùng. Đây là một mảng gồm 3 phần tử, được diễn giải theo các quy tắc HID thông thường cho các giá trị thực tế như được chỉ định trong mục 6.2.2.7 của quy cách HID. Phạm vi hợp lệ cho mỗi phần tử là [-π, π] rad. Đơn vị luôn là radian.

Các phần tử được diễn giải là: [rx, ry, rz], sao cho [rx, ry, rz] là một vectơ xoay, đại diện cho sự biến đổi từ khung tham chiếu sang khung đầu. Độ lớn phải nằm trong phạm vi [0..π].

Khung tham chiếu là tuỳ ý, nhưng thường được cố định và phải là khung thuận. Có thể chấp nhận một lượng nhỏ độ lệch. Các trục của đầu là:

  • X từ tai trái sang phải
  • Y từ sau đầu đến mũi (từ sau ra trước)
  • Z từ cổ đến đỉnh đầu

Trường dữ liệu: Giá trị tuỳ chỉnh 2 (0x0545)

Trường Giá trị tuỳ chỉnh 2 (0x0545) là trường nhập dùng để báo cáo thông tin theo dõi chuyển động đầu thực tế. Đây là một mảng dấu phẩy cố định gồm 3 phần tử, được diễn giải theo các quy tắc HID thông thường cho các giá trị vật lý. Đơn vị luôn là radian/giây.

Các phần tử được diễn giải là: [vx, vy, vz], sao cho [vx, vy, vz] là một vectơ xoay, đại diện cho vận tốc góc của khung phần đầu (so với chính nó).

Trường dữ liệu: Giá trị tuỳ chỉnh 3 (0x0546)

Trường Giá trị tuỳ chỉnh 3 (0x0546) là trường đầu vào dùng để theo dõi các điểm không liên tục trong khung tham chiếu. Đây là một số nguyên vô hướng có kích thước 8 bit. Thiết bị phải tăng giá trị này (với tính năng bao quanh) mỗi khi khung tham chiếu thay đổi, ví dụ: nếu thuật toán bộ lọc hướng dùng để xác định hướng đã đặt lại trạng thái. Giá trị này được diễn giải theo các quy tắc HID thông thường đối với các giá trị vật lý. Tuy nhiên, giá trị vật lý và đơn vị không quan trọng. Thông tin duy nhất liên quan đến máy chủ lưu trữ là một giá trị đã thay đổi. Để tránh các vấn đề về số học liên quan đến việc mất độ chính xác trong khi chuyển đổi từ đơn vị logic sang đơn vị thực tế, bạn nên đặt giá trị cho giá trị tối thiểu thực tế, giá trị tối đa thực tế và hệ số đơn vị thành 0 cho trường này.

Cấu trúc báo cáo

Việc nhóm các tài sản thành báo cáo (bằng cách chỉ định mã báo cáo) rất linh hoạt. Để đạt được hiệu quả, bạn nên tách các thuộc tính chỉ đọc khỏi các thuộc tính đọc/ghi.

Đối với các trường dữ liệu, các trường Giá trị tuỳ chỉnh 1, 2 và 3 phải nằm trong cùng một báo cáo và chỉ nằm trong một báo cáo cho một thiết bị cụ thể (bộ sưu tập ứng dụng).

Gửi báo cáo đầu vào

Thiết bị phải gửi báo cáo đầu vào theo định kỳ và không đồng bộ (thông qua thông báo HID INPUT) khi đáp ứng tất cả các điều kiện sau:

  • Thuộc tính Trạng thái nguồn được đặt thành Mức năng lượng tối đa.
  • Thuộc tính Trạng thái báo cáo được đặt thành Tất cả sự kiện.
  • Thuộc tính Khoảng thời gian báo cáo khác 0.

Thuộc tính Khoảng thời gian báo cáo xác định tần suất gửi báo cáo. Khi không đáp ứng bất kỳ điều kiện nào ở trên, thiết bị không được gửi báo cáo.

Khả năng tương thích chuyển tiếp và ngược

Giao thức HID của công cụ theo dõi chuyển động đầu sử dụng lược đồ tạo phiên bản cho phép cập nhật, đồng thời cho phép khả năng tương tác giữa một máy chủ lưu trữ và một thiết bị sử dụng các phiên bản giao thức khác nhau. Các phiên bản của giao thức được xác định bằng hai số, lớn và nhỏ, có ngữ nghĩa riêng biệt như mô tả trong các phần sau.

Bạn có thể xác định các phiên bản mà thiết bị hỗ trợ bằng cách kiểm tra thuộc tính Mô tả cảm biến (0x0308) của thiết bị.

Khả năng tương thích với phiên bản nhỏ

Các thay đổi đối với phiên bản nhỏ tương thích ngược với các phiên bản nhỏ trước đó dựa trên cùng một phiên bản lớn. Trong các bản cập nhật cho phiên bản nhỏ, máy chủ lưu trữ sẽ bỏ qua các trường và thuộc tính dữ liệu khác. Ví dụ: một thiết bị sử dụng giao thức phiên bản 1.6 tương thích với máy chủ hỗ trợ giao thức phiên bản 1.x, bao gồm cả phiên bản 1.5.

Khả năng tương thích với phiên bản lớn

Các thay đổi không tương thích ngược được cho phép đối với các thay đổi đối với phiên bản lớn. Để hỗ trợ nhiều phiên bản chính nhằm tương tác với máy chủ cũ và mới, thiết bị có thể chỉ định nhiều bộ sưu tập ứng dụng trong chỉ số mô tả báo cáo. Ví dụ:

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.5"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 12 (read-only).
        HID_REPORT_ID(12),

        // Magic value: "#AndroidHeadTracker#2.4"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,
};

Trong trường hợp này, máy chủ lưu trữ có thể liệt kê tất cả các tập hợp ứng dụng khác nhau do thiết bị quảng cáo, kiểm tra thuộc tính Mô tả cảm biến để xác định các phiên bản giao thức mà mỗi máy chủ triển khai, sau đó chọn phiên bản giao thức mới nhất mà máy chủ hỗ trợ. Khi được chọn, máy chủ lưu trữ sẽ hoạt động với một giao thức duy nhất đã được chọn trong suốt thời gian kết nối của thiết bị.

Phụ lục: Ví dụ về bộ mô tả HID

Ví dụ sau đây minh hoạ một chỉ số mô tả HID hợp lệ thông thường. Mã này sử dụng các macro C thường dùng, được cung cấp trong phần Sử dụng cảm biến HID (mục 4.1).

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.0"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};

Phụ lục 2: Ví dụ về bộ mô tả HID v2.0

Ví dụ sau đây minh hoạ một chỉ số mô tả HID v2.0 cho một thiết bị chỉ hỗ trợ phương thức truyền tải ACL Bluetooth LE.

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#2.0#1"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(25),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // 1-bit transport selection
        HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ACL,
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ISO,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};