Voice HoGP Remote

Table of Contents

Introduction

Voice HoGP Remote is a demonstration which exercises many areas of the BLE stack in a single application. The application demonstrates an implementation of the (Human Interface Device) HID over GATT profile (HoGP) where Keyboard, Consumer Control and Voice reports are used.

This document discusses the procedure use the Voice HoGP Remote for evaluation, as well as how various systems within in demo function.

Hardware Prerequisites

The default Voice HoGP Remote configuration requires the modified configuration of an CC2650RC kit, where the CC2650 is replaced by a CC2640R2F. The hardware is shown below:

cc2640r2rc

Additionally, to program/debug the Voice HoGP Remote Project, a Debugger DevPack should be used. Attach this as shown below, where the JTAG connector and Groove Connector are exposed.

remote-debugger-powered

For custom hardware, see the Running the SDK on Custom Boards section of the BLE-Stack User’s Guide for Bluetooth 4.2.

To test Voice over HID-Over-GATT, an evaluation script can be used. The voice.py script is available on GitHub

To test the HID Profile’s keyboard and consumer control reports, a BLE capable smart phone may be utilized.

Software Prerequisites

For information on what versions of Code Composer Studio and IAR Embedded Workbench to use, see the Release Notes located in the docs/blestack folder. For information on how to import this project into your IDE workspace and build/run, please refer to The CC2640R2F Platform section in the BLE-Stack User’s Guide for Bluetooth 4.2.

For Voice over HID over GATT testing, refer to the voice.py script.

Functional Description

Hardware Overview

Hardware Available on Remote

The following table contains information on the hardware resources and peripherals on the Remote.

remote-internals

Component Manufacturer Product
Wireless MCU Texas Instruments CC2650
Digital Microphone Knowles SPH0641LM4H-1
Motion Sensor Invensense MPU-9250
Memory Macronix MX25R8035F
Load Switch Texas Instruments TPS22910AYZV
Shift Register Texas Instruments SN74LV164A
IR LED Everlight IR333-A
Bi-Color LED Kingbright APHB1608SGEC

Powering Remote

The remote can be powered in two ways, via AAA batteries, or by utilizing the Debugger DevPack.

To insert the batteries, ensure they are facing the correct direction, the positive ends of the batteries towards the bottom of the remote.

remote-batteries

To power from the Debugger DevPack, simply connect a USB cable to the DevPack.

Software Overview

This section describes software components and the corresponding source file.

Application

Project Files Description
kcb.c Key press handling.
util.c This file contains utility functions commonly used by BLE applications for CC26xx with TI-RTOS.
voice_hogp_remote.c Top level application. Initialization of hardware, connection settings, key handling and voice streaming control.

The main application task can be found voice_hogp_remote.c. Besides setting up advertisement and scan response data and connection related parameters in voiceHogpRemote_init, this task processes key press events and handles the API for voice streaming.

Profiles And Services

Project Files Description
battservice.c Battery Service.
devinfoservice.c Device Information Service.
hiddev.c HID Profile. Support HID reads, writes, queuing reports, register services and device states.
hidservice.c HID Service. Service for keyboard, consumer control, and voice reports.
scanparamsservice.c Scan Parameter Service.

HID Profile

All HID over GATT Profile (HoGP) related functionality is abstracted from the application task and is handled in a separate task implemented in hiddev.c. This task handles:

The following services are defined in the HID profile specification as either mandatory or optional. The voice_hogp_remote project includes all these services:

Service Requirement Supported
HID Service Mandatory Yes
Battery Service Mandatory Yes
Device Information Service Mandatory Yes
Scan Parameter Service Optional Yes

The Battery Service, Device Information Service and Scan Parameter Service are all adopted services and the specification for each service can be found at developer.bluetooth.org.

HID Terminology

Name Description
HID Host The target machine that the user interacts with (e.g. Laptop, tablet, smartphone, etc…)
Report Host A Report Host is a HID Host that is required to support a HID Parser and be able to handle HID reports with arbitrary formats. (e.g. Windows, Linux, Android, iOS central device)
Boot Host A Boot Host is a HID Host that is not required to support a HID Parser as all Reports for the Boot Protocol Mode have predefined length and format.
HID Device The device that is used by the user to interact with the Host (e.g. Keyboard, mouse, remote control, game controller, etc…)
HID Report A data message sent between the host and device. Input Reports go from HID Device to HID Host, such as a key press. Output reports go from HID Host to HID Device, such as a PC changing the caps lock LED on a keyboard.
HID Report Descriptor A data structure that the device sends to the host which describes the HID device’s capabilities, including the types, sizes, and directions of the reports that are supported. In HID over GATT this is also referred to as the Report Map.

Report Map Characteristic

The Report Map characteristic contains the HID Report Descriptor which is used to define formatting information for Input Report, Output Report, and Feature Report data transferred between a HID Device and Report Host.

Each HID Service can only include one instance of the Report Map characteristic. The length of the Report Map characteristic value is limited to 512 bytes.

Report Map as shown is included in the HID Service in hidservice.c:

static CONST uint8 hidReportMap[] =
{
  0x05, 0x01,        // USAGE_PAGE (Generic Desktop)
  0x09, 0x06,        // USAGE (Keyboard)
  0xA1, 0x01,        // COLLECTION (Application)
  0x85, HID_RPT_ID_KEY_IN, // REPORT_ID (1)
                     //
  0x05, 0x07,        //   USAGE_PAGE (Key Codes)
  0x19, 0xE0,        //   USAGE_MIN (224)
  0x29, 0xE7,        //   USAGE_MAX (231)
  0x15, 0x00,        //   LOGICAL_MINIMUM (0)
  0x25, 0x01,        //   LOGICAL_MAXIMUM (1)
                     //
                     //   Modifier byte
  0x95, 0x08,        //     REPORT_COUNT (8)
  0x75, 0x01,        //     REPORT_SIZE (1)
  0x81, 0x02,        //     INPUT (Data, Variable, Absolute)
                     //
                     //   Reserved byte
  0x95, 0x01,        //     REPORT_COUNT (1)
  0x75, 0x08,        //     REPORT_SIZE (8)
  0x81, 0x01,        //     INPUT (Constant)
                     //
                     //   LED report
  0x95, 0x05,        //     REPORT_COUNT (5)
  0x75, 0x01,        //     REPORT_SIZE (1)
  0x05, 0x08,        //     USAGE_PAGE (LEDs)
  0x19, 0x01,        //     USAGE_MIN (1)
  0x29, 0x05,        //     USAGE_MAX (5)
  0x91, 0x02,        //     OUTPUT (Data, Variable, Absolute)
                     //
                     //   LED report padding
  0x95, 0x01,        //     REPORT_COUNT (1)
  0x75, 0x03,        //     REPORT_SIZE (3)
  0x91, 0x01,        //     OUTPUT (Constant)
                     //
                     //   Key arrays (6 bytes)
  0x95, 0x06,        //     REPORT_COUNT (6)
  0x75, 0x08,        //     REPORT_SIZE (8)
  0x15, 0x00,        //     LOGICAL_MINIMUM (0)
  0x25, 0x65,        //     LOGICAL_MAXIMUM (101)
  0x05, 0x07,        //     USAGE_PAGE (Key Codes)
  0x19, 0x00,        //     USAGE_MIN (0)
  0x29, 0x65,        //     USAGE_MAX (101)
  0x81, 0x00,        //     INPUT (Data, Array)
                     //
  0xC0,              // END_COLLECTION
                     //
  0x05, 0x0c,        // USAGE_PAGE (Consumer Devices)
  0x09, 0x01,        // USAGE (Consumer Control)
  0xa1, 0x01,        // COLLECTION (Application)
  0x85, HID_RPT_ID_CC_IN, // REPORT_ID (2)
                     //
  0x09, 0x30,        //   USAGE (Power)
  0x09, 0xCD,        //   USAGE (Play/Pause)
  0x09, 0xB7,        //   USAGE (Stop)
  0x09, 0xB5,        //   USAGE (Skip track)
  0x09, 0xB6,        //   USAGE (Previous track)
  0x09, 0xB3,        //   USAGE (Fast forward)
  0x09, 0xB4,        //   USAGE (Rewind)
  0x09, 0xB2,        //   USAGE (Record)
  0x09, 0xE9,        //   USAGE (Volume up)
  0x09, 0xEA,        //   USAGE (Volume down)
  0x09, 0xE2,        //   USAGE (Mute)
  0x15, 0x01,        //   LOGICAL_MINIMUM (1)
  0x25, 0x0B,        //   LOGICAL_MAXIMUM (11)
  0x95, 0x01,        //   REPORT_COUNT (1)
  0x75, 0x08,        //   REPORT_SIZE (8)
  0x81, 0x00,        //   INPUT (Data,Ary,Abs)
                     //
  0xC0,               // END_COLLECTION

  //Voice collection
  0x05, 0x0C,        // Usage Page (Consumer Devices)
  0x09, 0x01,        // Usage (Consumer Control)
  0xA1, 0x01,        // Collection (Application)
  0x85, HID_RPT_ID_VOICE_START_IN, //   Report ID (10)
  0x15, 0x00,        //   Logical Minimum (0)
  0x26, 0xFF, 0x00,  //   Logical Maximum (255)
  0x75, 0x08,        //   Report Size (8)
  0x95, 0x05,        //   Report Count (5)
  0x09, 0x01,        //   Usage (Consumer Control)
  0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
  0x85, HID_RPT_ID_VOICE_DATA_IN, //   Report ID (11)
  0x15, 0x00,        //   Logical Minimum (0)
  0x26, 0xFF, 0x00,  //   Logical Maximum (255)
  0x75, 0x08,        //   Report Size (8)
  0x95, 0x14,        //   Report Count (20)
  0x09, 0x01,        //   Usage (Consumer Control)
  0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)

  0xC0               // END_COLLECTION
};

HID Reports

The Report Reference characteristic descriptors contain the Report ID and Report Type for each Report Characteristic defined in the Report Map. This information is used on the Report Host to route USB HID class driver data into and out of GATT characteristic values.

Report IDs in hidservice.h:

// HID Report IDs for the service
#define HID_RPT_ID_LED_OUT               0  // LED output report ID
#define HID_RPT_ID_KEY_IN                1  // Keyboard input report ID
#define HID_RPT_ID_CC_IN                 2  // Consumer Control input report ID
#define HID_RPT_ID_VOICE_START_IN        10  // Voice Start input report ID
#define HID_RPT_ID_VOICE_DATA_IN         11  // Voice Data input report ID

Report Types in hiddev.h:

// HID Report type
#define HID_REPORT_TYPE_INPUT       1
#define HID_REPORT_TYPE_OUTPUT      2
#define HID_REPORT_TYPE_FEATURE     3

The Report ID is not transmitted as a part of the HID Report over-the-air but is prepended by the Report Host, using the information in Report Reference characteristic descriptors, to each received HID report before passing them to a USB HID Class driver.

Key Board Input Reports

The Report Map includes the full 101-key part of the Keyboard Usage Page. This gives a one-to-one mapping between the transmitted report and the Usage ID in the usage table. The Usage IDs can be found in hiddev.c.

Consumer Control Input Reports

Only a subset of the Consumer Control Usage Page is included in this Report Map. The HID Report transmitted is in this case not the Usage ID but the index of the report as include in the Report Map.

The Consumer Control reports that can be sent can be found in voice_hogp_remote.h as:

// HID Consumer Control keycodes
// (based on the HID Report Map characteristic value)
#define HID_CC_RPT_POWER                    1
#define HID_CC_RPT_PLAY_PAUSE               2
#define HID_CC_RPT_STOP                     3
#define HID_CC_RPT_SCAN_NEXT_TRK            4
#define HID_CC_RPT_SCAN_PREV_TRK            5
#define HID_CC_RPT_FAST_FWD                 6
#define HID_CC_RPT_REWIND                   7
#define HID_CC_RPT_RECORD                   8
#define HID_CC_RPT_VOLUME_UP                9
#define HID_CC_RPT_VOLUME_DOWN              10
#define HID_CC_RPT_MUTE                     11

LED Output Reports

The LED Output reports are set in the Report Map to be a 5 bit bit-vector where each bit represents a report value. When an output is received on the CC2650RC it will be passed to the application callback in voice_hogp_remote.c:

static uint8_t voiceHogpRemote_handleReportCB(uint8_t id, uint8_t type,
                                           uint16_t uuid, uint8_t oper,
                                           uint16_t *pLen, uint8_t *pData)
{
  // Intentionally left blank
  return SUCCESS;
}

No specific handling of received Output Reports is implanted into the voice_hogp_remote project so the correct handling must be added to use the information in the LED Output Report.

Voice Output Reports

The audio data is transmitted using 2 HID Output reports:

Name Report ID Description
HID_RPT_ID_VOICE_START_IN 0x0A (10) This HID report is used to transmit a start command before the streaming starts and stop command as the last packet of a stream.
HID_RPT_ID_VOICE_DATA_IN 0x0B (11) This HID report is used to transmit all voice data.

The main advantage in transporting data over the HID is that modern Operating Systems typically natively support the HID-Over-GATT (HoGP) profile, simplifying the application development by not having to develop custom GATT profiles.

Applications using HoGP only need to collect voice frames using HID reports generated by the Operating System.

The HID reports used to transport voice frames follow the a similar paradigm as with TI’s custom GATT Voice Profile. The HID_RPT_ID_VOICE_START_IN report is used to indicate the start and stop of the voice data stream, whereas the voice data itself is sent via the HID_RPT_ID_VOICE_DATA_IN report.

The basic flow of a voice transmission is:

  1. Voice HoGP Remote sends a start command (0x04) on HID_RPT_ID_VOICE_START_IN.
  2. Voice HoGP Remote starts streaming voice data on HID_RPT_ID_VOICE_DATA_IN. See Voice over HID over GATT Profile section for more details.
  3. Voice HoGP Remote sends a stop command (0x00) on HID_RPT_ID_VOICE_START_IN.

Key Scanning

This section describes the key scanner.

There are 32 buttons on the Remote Control that are scanned with a 3x11 matrix.

Normally, a total of 14 IOs would be required to perform key scanning in this configuration. To save IOs, shift registers are used to extend the number of IOs. A single shift register can be used to get 8 IOs from 3. When cascading shift registers, the same 3 IOs can emulate additional IOs. The two shift registers used in this example, can support up to 16 IOs, but only 11 are used for the rows. Including 3 IOs for the columns, 6 IOs are used perform key scanning. The spare IOs are connected to the DevPack connector.

Note. that these 32 buttons could have been scanned with 12 IOs in a 6x6 matrix.

Algorithm

As shown in the there are 3 IOs for columns and 11 IOs for rows. When the remote is in standby the IOs are configured such that a button press generates an interrupt. The interrupt triggers on the column pins.

  1. Interrupt detected
  2. Start de-bounce timer
  3. Key scan is repeated periodically as long as a press is detected.

De-bounce : Refers to unintentional changes in voltage, or that the voltage “bounce”. This typically happens as the button is pressed. As the button shorts the connection between row and column the voltage can “bounce”. We don’t want to scan the keys as this happens. Thus, the keys are only scanned after some time has passed. This time is greater than the time it takes for the voltage to settle.

Implementation

The key scan algorithm discussed above is implemented in the module key_scan.c/.h. This module has two public functions:

  1. KeyInit(void)
  2. KeyConfig(KeyEvtCBack_t key_cback, uint16_t initialKeyRepeatInterval, uint16_t debounceTime, uint16_t pollRate)

KeyInit

This function must be called before KeyConfig() is called. It initializes and configures all IOs. It also constructs the timer resources the key scanner requires.

KeyConfigure

This function is called to register callbacks and configure timer values. The parameters are as follows:

  1. debounceTime, de-bounce period
  2. pollRate, scanning period.
  3. initialKeyRepeatInterval. It determines how frequent the callback key_cback is called. If a key is kept pressed for a longer duration, then the callback is called every initialKeyRepeatInterval/pollRate ms.

Key Mapping

The following shows a mapping of which key pressed on the Remote Control corresponds to which HID report or application action.

Key Voice HoGP Remote
NUM_0 HID_KEYBOARD_0
NUM_1 HID_KEYBOARD_1
NUM_2 HID_KEYBOARD_2
NUM_3 HID_KEYBOARD_3
NUM_4 HID_KEYBOARD_4
NUM_5 HID_KEYBOARD_5
NUM_6 HID_KEYBOARD_6
NUM_7 HID_KEYBOARD_7
NUM_8 HID_KEYBOARD_8
NUM_9 HID_KEYBOARD_9
UP HID_KEYBOARD_UP_ARROW
DOWN HID_KEYBOARD_DOWN_ARROW
LEFT HID_KEYBOARD_LEFT_ARROW
RIGHT HID_KEYBOARD_RIGHT_ARROW
Power None
PLAY/PAUSE HID_CC_RPT_PLAY_PAUSE
MUTE HID_CC_RPT_MUTE
V+ HID_CC_RPT_VOLUME_UP
V- HID_CC_RPT_VOLUME_DOWN
REC None
FAST FORWARD HID_CC_RPT_SCAN_NEXT_TRK
FAST REWIND HID_CC_RPT_SCAN_PREV_TRK
MIC Start streaming Voice
MENU None
PAIR None
INFINITE None
STB None
BACK None
OK None
HOME None
(Red LED) Indicates streaming Voice when flashing
AV None
TV None

Usage

This section describes how to exercise various parts of the Voice HoGP Remote.

Bonding

The Voice HoGP Remote can be bonded with HID Hosts such as Android and iOS mobile devices as well as other standard HoGP-supported Operating Systems such as Windows® 8.1 or later and Linux distributions operating with the BlueZ Linux Bluetooth protocol stack.

Windows

Keyboard, Consumer Control and Voice over HID-Over-GATT reports can be evaluated using a Windows workstation.

To evaluate the voice reports, you will need the voice.py script that is available on the GitHub repo.

Ensure the Remote is advertising by pressing any key on the Remote. And verify that the smart phone can detect the remote.

Open Settings -> Devices -> Bluetooth. When the remote is advertising, you will see it listed as shown here:

bluetooth-settings

Click on the Pair button to initiate bonding.

Under normal circumstances, Windows should prompt the user to enter in a random-generated passcode. However, there is a known issue with Windows, where it doesn’t prompt the user for a passcode if the peripheral has the Bluetooth LE Secure Connections feature enabled. Windows will look like it is unresponsive at shown here, but you need to press 000000 on the remote control to pair successfully.

bluetooth-settings-pairing

After successful pairing, you should see this when the remote has connected. The remote control must be connected in order to evaluate the voice.py script on GitHub.

bluetooth-settings-connected

Android

For Android devices, keyboard and consumer control reports are easily evaluated. In this section an Android Phone is used to connect to the device. Once connected, the remote will emulate a keyboard with various functions as HID Reports. See Key Mapping.

Ensure the Remote is advertising by pressing any key on the Remote. And verify that the smart phone can detect the remote.

To access the Bluetooth Menu of the smart phone:

bluetooth-menu

Connect to the device, during pairing the phone will display a passcode to enter in the Remote.

passcode-entry

Enter this code onto the Remote via the keypad; after a few moments, the Remote can be used as a input device.

hid-input

Voice over HID over GATT Profile

For detail on how the voice sevice is implememented, see Voice over HID over GATT Profile

  1. Ensure that the remote control is connected to a Windows workstation
  2. Start the voice.py script.
  3. Press and hold the MIC key.
  4. Talk into the microphone
  5. Release the MIC key.
  6. Find the generated .wav file generated by the voice.py script.

Keyboard and Consumer Control via HID over GATT Profile

Windows and Android natively support the keyboard and consumer control services.

The keys listed in the Key Mapping section describe on what behavior is to be expected upon a key press on the remote.