Custom HCI command

Document created by Estephania Martinez Employee on Oct 15, 2018Last modified by Estephania Martinez Employee on Oct 25, 2018
Version 5Show Document
  • View in full screen mode

HCI Application is a Host Controller Interface application which provides a serial communication to interface with the KW40/KW41/KW35/KW36/QN9080 BLE radio part.


It enables the user to have a way to control the radio through serial commands.


The format of the HCI Command Packet it’s composed by the following parts.


Figure 1. HCI Command Packet


Each command is assigned a 2 byte Opcode which it’s divided into two fields, called the OpCode Group Field (OGF) and OpCode Command Field (OCF).


The OGF uses the upper 6 bits of the Opcode, while the OCF corresponds to the remaining 10 bits.


The OGF of 0x3F is reserved for vendor-specific debug commands. The organization of the opcodes allows additional information to be inferred without fully decoding the entire Opcode. 


For further information regarding this, please check the BLUETOOTH SPECIFICATION Version 5.0 | Vol 2, Part E, 5.4 EXCHANGE OF HCI-SPECIFIC INFORMATION. 


This document will guide you through the implementation of custom HCI commands in the KW36, but it can be applied as well for the rest of the NXP Bluetooth LE MCU’s that support HCI.


The following changes were made and tested in the FREEDOM KW36 and will generate a continuous with both channel and power configurable. 


You will need to perform the following changes to the HCI black box demo.


Modify the hci_transport.h public constants and macros section by adding:


#define gHciCustomCommandOpcodeUpper (0xFC50)
#define gHciCustomCommandOpcodeLower (0xFC00)
#define gHciInCustomVendorCommandsRange(x) (((x) <= gHciCustomCommandOpcodeUpper) && \
((x) >= gHciCustomCommandOpcodeLower))


In this case, the opcodes 0xFC50 to 0xFC00 are not used by our stack. These opcodes meet the requirements for vendor specific commands (OCF = 3F).


Then you will need to declare the handler for the custom command.


void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler);



In the  hcit_serial_interface.c modify the following :


Add in the private memory declarations section


static hciTransportInterface_t mCustomTransportInterface = NULL;

Change the Hcit_SendMessage as it is shown:


static inline void Hcit_SendMessage(void)
    uint16_t opcode = 0;
    /* verify if this is an event packet */
    if(mHcitData.pktHeader.packetTypeMarker == gHciEventPacket_c)
       /* verify if this is a command complete event */
       if(mHcitData.pPacket->raw[0] == gHciCommandCompleteEvent_c)
          /* extract the first opcode to verify if it is a custom command */
          opcode = mHcitData.pPacket->raw[3] + (mHcitData.pPacket->raw[4] << 8);
    /* verify if command packet */
    else if(mHcitData.pktHeader.packetTypeMarker == gHciCommandPacket_c)
       /* extract opcode */
       opcode = mHcitData.pPacket->raw[0] + (mHcitData.pPacket->raw[1] << 8);

          mCustomTransportInterface( mHcitData.pktHeader.packetTypeMarker,
       /* Send the message to HCI */
       mTransportInterface( mHcitData.pktHeader.packetTypeMarker,
   MEM_BufferFree( mHcitData.pPacket );
   mHcitData.pPacket = NULL;
    mPacketDetectStep = mDetectMarker_c;


Implement the registration of the handler

void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler)
   mCustomTransportInterface = mCustomInterfaceHandler;


 Once those changes are done, we will need to modify the  hci_black_box.c with the following changes. 


Add the files to support HCI Custom commands.


#include "hci_transport.h"
#include "fsl_xcvr.h"


Define the custom commands, in this case, we will create some to turn ON/OFF the continuous wave as well as to set up the channel and power. 


//@CC custom command
#define CUSTOM_HCI_CW_ON (0xFC50)
#define CUSTOM_HCI_CW_OFF (0xFC4F)
#define CUSTOM_HCI_CW_SET_CHN_0 (0xFC00) /*Channel 0 Freq 2402 MHz*/
#define CUSTOM_HCI_CW_SET_CHN_19 (0xFC01) /*Channel 19 Freq 2440 MHz*/
#define CUSTOM_HCI_CW_SET_CHN_39 (0xFC02) /*Channel 39 Freq 2480 MHz*/
#define CUSTOM_HCI_CW_SET_PA_PWR_1 (0xFC10) /*PA_POWER 1 */
#define CUSTOM_HCI_CW_SET_PA_PWR_32 (0xFC11) /*PA_POWER 32 */
#define CUSTOM_HCI_CW_SET_PA_PWR_62 (0xFC12) /*PA_POWER 62 */
#define CUSTOM_HCI_EVENT_FAIL (0x01)

Also, adding some app auxiliar variables 

static uint16_t channelCC = 2402;
static uint8_t powerCC = 0x3E;
static uint8_t state_CC = 0; /*0 OFF 1 ON */


Create the custom event packet 


uint8_t  eventPacket[6] = {gHciCommandCompleteEvent_c, CUSTOM_HCI_CW_EVENT_SIZE, 1, 0, 0, 0 };


In the main_task() after the BleApp_Init() register the callback for the custom commands.


/* Initialize peripheral drivers specific to the application */
//Register the callback for the custom commands.


Once that it’s added, add the handler for the command


bleResult_t BleApp_CustomCommandsHandle(hciPacketType_t packetType, void* pPacket, uint16_t packetSize)
   uint16_t opcode = 0;

   uint8_t error=0;
      case gHciCommandPacket_c:
      opcode = ((uint8_t*)pPacket)[0] + (((uint8_t*)pPacket)[1] << 8);

            /*@CC: Set Channel   */
            case CUSTOM_HCI_CW_SET_CHN_0:
                 /*@CC: Set Channel 0 Freq 2402 MHz */
                 eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
            case CUSTOM_HCI_CW_SET_CHN_19:
                 /*@CC: Channel 19 Freq 2440 MHz*/
                 eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
            case CUSTOM_HCI_CW_SET_CHN_39:
                 /*@CC: Channel 39 Freq 2480 MHz */
                 eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;

              /*@CC: Set PA_POWER  */

            case CUSTOM_HCI_CW_SET_PA_PWR_1:
                 /*@CC: Set PA_POWER 1 */
                 eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
            case CUSTOM_HCI_CW_SET_PA_PWR_32:
                 /*@CC: Set PA_POWER 32 */
                 eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
            case CUSTOM_HCI_CW_SET_PA_PWR_62:
                 /*@CC:  Set PA_POWER 62 */
                 eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;

              /*@CC: Generate a Continuous Unmodulated Signal ON / OFF  */

             case CUSTOM_HCI_CW_ON:
                  /*@CC: Generate a Continuous Unmodulated Signal when pressing SW3 */
                  XCVR_DftTxCW(channelCC, 6);
                  state_CC = 1;
                  eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;

             case CUSTOM_HCI_CW_OFF:
                  /*@CC: Turn OFF the transmitter */
                  /* Initialize the PHY as BLE */
                  XCVR_Init(BLE_MODE, DR_1MBPS);
                  state_CC = 0;
                  eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;

              eventPacket[5] = CUSTOM_HCI_EVENT_FAIL;


      if(state_CC && (opcode==CUSTOM_HCI_CW_ON))
            eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
            eventPacket[5] = CUSTOM_HCI_EVENT_FAIL;

      eventPacket[3] = (uint8_t)opcode;
      eventPacket[4] = (uint8_t)(opcode >> 8);

      Hcit_SendPacket(gHciEventPacket_c, eventPacket, sizeof(eventPacket));




To test it out in your side you will need to send the following raw commands through test tool:


  • 01 4F FC 00 – This is to stop the CW and configure the radio in BLE mode again. This way you can continue sending HCI commands.
  • 01 50 FC 00 – This is to send a CW signal in channel 0  with the defined channel and output power (default: frequency 2.402GHz and PA_POWER register with value of 0x3E). 
  • 01 FC 00 00 – Set the Channel 0 Freq 2402 MHz
  • 01 FC 01 00 – Set the Channel 1 Freq 2440 MHz
  • 01 FC 02 00 – Set the Channel 2 Freq 2480 MHz
  • 01 FC 10 00 – Set the PA_POWER 1
  • 01 FC 11 00 Set the PA_POWER 32
  • 01 FC 12 00 Set the PA_POWER 62


If you want to add some parameter to it, please consider that the fourth byte of the packet will correspond to the number of parameters to enter and you will need to indicate it there.