Skip to content

Last review: Sept 15,2025

I decided to turn my Raspberry Pi into a Bluetooth keyboard and mouse. In this article, I’ll share my understanding of how to emulate these devices. There are still some aspects I don’t fully understand, but I plan to update this article as I learn more.

HID

HID stands for Human Interface Device. Any device that allows a human to send input to a computer is considered an HID. For example, a mouse, keyboard, and joystick are all HIDs.

The Bluetooth specification categorizes devices into classes based on common properties, and HID devices form one such class.

Bluez

Since I want to emulate a Bluetooth HID device, I will use BlueZ, the Bluetooth stack for Linux.

If you're familiar with Linux, you’ve probably encountered the Bluetooth service:

bluetooth service

Once the service is running, you can interact with it using the bluetoothctl command.

For example, you can discover available devices by running the scan on command. The following screen shows my device.

bluetoothctl

Now, let's try connecting to this device using the command connect [MAC]:

Failed to connect

As you can see, the connection failed. This is expected because you need to pair with the device first using the pair command before you can connect to it.

For now, just take note of the error message. You might notice the unusual string org.bluez.Error.Failed.

This suggests that BlueZ uses D-Bus. But wait… what is D-Bus?

DBUS

On Linux, processes can communicate in various ways, such as through sockets, shared memory, named pipes, or… D-Bus!

You can think of D-Bus as a virtual message bus where multiple processes connect to send and receive messages from each other.

DBUS

Each process is identified by a 'well-known bus name.' From the previous error, we can see that org.bluez is the well-known bus name for the Bluetooth service.

This can be confirmed using the busctl command:

busctl

Objects and Interfaces

org.bluez refers to the Bluetooth service on D-Bus. The service exposes multiple objects, each named with a Linux-like path. For example, the well-known bus org.bluez has an object called /org/bluez.

busctl

Each object has multiple interfaces.

Here are some examples of interfaces provided by the object '/org/bluez':

Finally, each interface provides functions that we can use to communicate with BlueZ.

Specifications documents

Being able to communicate with bluez is not enough. There is no magical functions to emulate a HID device. We need to go a little bit further and read the bluetooth specifications.

Bluetooth

Being able to communicate with BlueZ is not enough. There are no magical functions to emulate an HID device.

We need to go a step further and refer to the Bluetooth specifications.

spec bluetoothcorespec bluetoothhid

SPD

An important aspect that will help us tell BlueZ we want to emulate an HID device is the SDP.

The Bluetooth specification defines it as follows:

SDP database which consists of a list of service records that describe the characteristics of services associated with the server. Each service record contains information about a single service.

All of the information about a service that is maintained by an SDP Server is contained within a single service record. The service record shall only be a list of service attributes.

spec bluetoothhid

An attribute ID is a 16-bit unsigned integer

A service class definition specifies each of the attribute IDs for a service class and assigns a meaning to the attribute value associated with each attribute ID. Each attribute ID is defined to be unique only within each service class

A service record contains attributes that are specific to a service class as well as universal attributes that are common to all services

SDP attributes are split into two categories: Universal attributes and Specific attributes.

What you need to remember is that an SDP record describes a device through a list of attributes. This is defined using an XML file:

XML
<?xml version="1.0" encoding="UTF-8" ?>

<record>
	<attribute id="ID1">
        VALUE 1
	</attribute>

	<attribute id="ID2">
        VALUE 2
	</attribute>
</record>

The question then becomes: which attributes should we use, and for each attribute, what kind of values can we assign?

attributes ID

Universal attributes are defined in the Bluetooth specification under 'Assigned Numbers,' in section 5.1.5, 'Bluetooth Core Specification: Universal Attributes.

spec bluetoothhid

ServiceClassIDList (0x0001)

spec bluetoothhid

This attribute allows us to specify the class of the HID. The value is a sequence of UUIDs

XML
<attribute id="0x0001"> <!-- This is the attribute for specifying a class ID List -->
		<sequence>
			<uuid value="0x1124" /> <!-- This describe a Human Interface Device -->
		</sequence>
</attribute>

ProtocolDescriptorList (0x0004)

spec bluetoothhid

This attribute allows us to specify the protocol used. In this case, we will use the Logical Link Control and Adaptation Protocol (L2CAP):

XML
<attribute id="0x0004"> <!-- Protocol Descriptor List -->
	<sequence>
		<sequence>
			<uuid value="0x0100" /> <!-- use the L2CAP protocol -->
		</sequence>
	</sequence>
</attribute>

HIDDescriptorList (0x0206)

The Bluetooth Human Interface Device Profile (HIDP) 1.1 specification provides the details of this attribute.

spec bluetoothhid

XML
        <attribute id="0x0206">
                <sequence>
                        <sequence>
                                <uint8 value="0x22" />
                                <text encoding="hex" value="05010906a101850175019508050719e029e715002501810295017508810395057501050819012905910295017503910395067508150026ff000507190029ff8100c005010902A10185020901A1000509190129031500250175019503810275059501810105010930093109381581257F750895038106C0C0"/>
                        </sequence>
                </sequence>
        </attribute>

The long chain 05010906... is an HID descriptor, but it requires further clarification. To understand it better, we need to take a closer look at the USB specification, which inspired the Bluetooth HID descriptor.

HID descriptor

An HID descriptor defines how data is transferred between the device and the computer.

For example, it tells the computer that the connected device is a mouse with two buttons and two axes. Since the mouse transfers data through a frame of bits, the HID descriptor also specifies which parts of the frame correspond to the buttons and which parts correspond to the axes.

An HID descriptor is essentially a list of items. Here, we'll focus specifically on short items:

spec bluetoothhid

As the figure shows, each item has a type (Main, Global, and Local) and a tag, which depends on the type.

You can think of the type as a category, and the tag as a subcategory.

For example, the following figure shows the five tags for the Main type:

spec bluetoothhid

The following table summarizes all of these values. nn represents the size of the record, excluding the first prefix byte.

Main (xxxx 00 nn)Global(xxxx 01 nn)Local (xxxx 10 nn)
Input
(1000 00 nn : 0x80 -> 0x83)
Usage Page
(0000 01 nn : 0x04 -> 0x06)
Usage
(0000 10 nn : 0x08 -> 0x0a)
Output
(1001 00 nn : 0x90 -> 0x92)
Logical Minimum
(0001 01 nn : 0x14 -> 0x16)
Usage Minimum
(0001 10 nn : 0x18 -> 0x1a)
Feature
(1011 00 nn : 0xb0 -> 0xb2)
Logical Maximum
(0010 01 nn : 0x24 -> 0x26)
Usage Maximum
(0010 10 nn : 0x28 -> 0x2a)
Collection
(1010 00 nn : 0xa0 -> 0xa2)
Physical Minimum
(0011 01 nn : 0x34 -> 0x36)
End of Collection(
1100 00 nn : 0xc0 -> 0xc2)
Physical Maximum
(0100 01 nn : 0x44 -> 0x46)
Report Size
(0111 01 nn : 0x74 -> 0x76)
Report ID
(1000 01 nn : 0x84 -> 0x86)
Report Count
(1001 01 nn : 0x94 -> 0x96)
Report Push
(1010 01 nn : 0xa4 -> 0xa6)
Report Pop
(1011 01 nn : 0xb4 -> 0xb6)

With all that in mind, I created the following Python script (it might contain bugs) to parse my HID descriptor

python
hid_descriptor=bytes.fromhex("05010906a101850175019508050719e029e715002501810295017508810395057501050819012905910295017503910395067508150026ff000507190029ff8100c005010902A10185020901A1000509190129031500250175019503810275059501810105010930093109381581257F750895038106C0C0")

i=0
err = False
while(i<len(hid_descriptor)):
    bSize=hid_descriptor[i]&3
    bType=(hid_descriptor[i]>>2)&3
    bTag=(hid_descriptor[i]>>4)&15

    if( (bSize == 0b10) and (bType == 0b11) and bTag == 0b1111):
        print("Long item detected")
        print("Not implemented yet. Check the USB Device Class Definition for Human Interface Devices (HID) Specification")
        exit(0)
    else:
        print(f"Short item of size {bSize}.", end=" ")
        if(bType==0):
            print("Main type.", end=" ")
            if(bTag == 0b1000):
              print("Input tag.", end=" ")
            elif(bTag == 0b1001):
              print("Output tag.", end=" ")
            elif(bTag == 0b1011):
              print("Feature tag.", end=" ")
            elif(bTag == 0b1010):
              print("Collection tag.", end=" ")
            elif(bTag == 0b1100):
              print("End Collection tag.", end=" ")
        elif(bType==1):
            print("Global type.", end=" ")
            if(bTag == 0b0000):
              print("Usage Page tag.", end=" ")
            elif(bTag == 0b0001):
              print("Logical Minimum tag.", end=" ")
            elif(bTag == 0b0010):
              print("Logical Maximum tag.", end=" ")
            elif(bTag == 0b0011):
              print("Physical Minimum tag.", end=" ")
            elif(bTag == 0b0100):
              print("Physical Maximum tag.", end=" ")
            elif(bTag == 0b0101):
              print("Unit exponent tag.", end=" ")
            elif(bTag == 0b0110):
              print("Unit tag.", end=" ")
            elif(bTag == 0b0111):
              print("Report Size tag.", end=" ")
            elif(bTag == 0b1000):
              print("Report ID tag.", end=" ")
            elif(bTag == 0b1001):
              print("Report Count tag.", end=" ")
            elif(bTag == 0b1010):
              print("Report Push tag.", end=" ")
            elif(bTag == 0b1011):
              print("Report Pop tag.", end=" ")
            else:
              print(f"Unexpected TAG {bTag}", end= " ")
              err=True
        elif(bType==2):
            print("Local type.", end=" ")
            if(bTag == 0b0000):
              print("Usage tag.", end=" ")
            elif(bTag == 0b0001):
              print("Usage Minimum tag.", end=" ")
            elif(bTag == 0b0010):
              print("Usage Maximum tag.", end=" ")
            elif(bTag == 0b0011):
              print("Designator index tag.", end=" ")
            else:
              print(f"Unexpected TAG {bTag}", end= " ")
              err=True
        else:
            print("unexpected type", end= " ")
            err=True


        if(bSize > 0):
          print("Value : ", end=" ")
          for j in range(bSize):
              print(hex(hid_descriptor[i+j+1])[2:],end=" ")
        print("")


        if(err):
            exit(0)
        i=i+1
        i=i+bSize

This results in the following output, which includes some comments, spaces, and new lines:

Short item of size 1. Global type. Usage Page tag. Value :  1 ------> Generic Desktop Page 
Short item of size 1. Local type. Usage tag. Value :  6 -> keyboard
Short item of size 1. Main type. Collection tag. Value :  1 
	Short item of size 1. Global type. Report ID tag. Value :  1 
	Short item of size 1. Global type. Report Size tag. Value :  1 
	Short item of size 1. Global type. Report Count tag. Value :  8 

	Short item of size 1. Global type. Usage Page tag. Value :  7 --> Keyboard / Keypad page from e0 to e7 (usage minimum and maximum) see https://www.usb.org/sites/default/files/hut1_6.pdf
	Short item of size 1. Local type. Usage Minimum tag. Value :  e0 -> LeftControl, LeftShift,LeftAlt,LeftGUI, RightControl, RightShit, RightAlt,RightGUI
	Short item of size 1. Local type. Usage Maximum tag. Value :  e7 
	Short item of size 1. Global type. Logical Minimum tag. Value :  0 -> signal press ou release
	Short item of size 1. Global type. Logical Maximum tag. Value :  1 
	Short item of size 1. Main type. Input tag. Value :  2 
	Short item of size 1. Global type. Report Count tag. Value :  1 
	Short item of size 1. Global type. Report Size tag. Value :  8 
	Short item of size 1. Main type. Input tag. Value :  3 
	Short item of size 1. Global type. Report Count tag. Value :  5 
	Short item of size 1. Global type. Report Size tag. Value :  1 

	Short item of size 1. Global type. Usage Page tag. Value :  8 --> LED page from 1 to 5
	Short item of size 1. Local type. Usage Minimum tag. Value :  1 
	Short item of size 1. Local type. Usage Maximum tag. Value :  5 
	Short item of size 1. Main type. Output tag. Value :  2 
	Short item of size 1. Global type. Report Count tag. Value :  1 
	Short item of size 1. Global type. Report Size tag. Value :  3 
	Short item of size 1. Main type. Output tag. Value :  3 
	Short item of size 1. Global type. Report Count tag. Value :  6 
	Short item of size 1. Global type. Report Size tag. Value :  8 
	Short item of size 1. Global type. Logical Minimum tag. Value :  0 
	Short item of size 2. Global type. Logical Maximum tag. Value :  ff 0 

	Short item of size 1. Global type. Usage Page tag. Value :  7 -> Keyboard / Keypad page from 0 to ff (usage minimum and maximum)
	Short item of size 1. Local type. Usage Minimum tag. Value :  0 
	Short item of size 1. Local type. Usage Maximum tag. Value :  ff 
	Short item of size 1. Main type. Input tag. Value :  0 
Short item of size 0. Main type. End Collection tag. 

Short item of size 1. Global type. Usage Page tag. Value :  1 
Short item of size 1. Local type. Usage tag. Value :  2 -----------> Mouse 
Short item of size 1. Main type. Collection tag. Value :  1 
	Short item of size 1. Global type. Report ID tag. Value :  2 
	Short item of size 1. Local type. Usage tag. Value :  1 
	Short item of size 1. Main type. Collection tag. Value :  0 
		
        Short item of size 1. Global type. Usage Page tag. Value :  9 -> Button Page from 1 to 3 (souris a 3 boutons) 
		Short item of size 1. Local type. Usage Minimum tag. Value :  1 
		Short item of size 1. Local type. Usage Maximum tag. Value :  3 
		Short item of size 1. Global type. Logical Minimum tag. Value :  0 
		Short item of size 1. Global type. Logical Maximum tag. Value :  1 
		Short item of size 1. Global type. Report Size tag. Value :  1 
		Short item of size 1. Global type. Report Count tag. Value :  3 
		Short item of size 1. Main type. Input tag. Value :  2 
		Short item of size 1. Global type. Report Size tag. Value :  5 
		Short item of size 1. Global type. Report Count tag. Value :  1 
		Short item of size 1. Main type. Input tag. Value :  1

		Short item of size 1. Global type. Usage Page tag. Value :  1 -> Generic Desktop page
		Short item of size 1. Local type. Usage tag. Value :  30 -> X
		Short item of size 1. Local type. Usage tag. Value :  31 -> Y
		Short item of size 1. Local type. Usage tag. Value :  38 -> Wheel
		Short item of size 1. Global type. Logical Minimum tag. Value :  81 
		Short item of size 1. Global type. Logical Maximum tag. Value :  7f 
		Short item of size 1. Global type. Report Size tag. Value :  8 
		Short item of size 1. Global type. Report Count tag. Value :  3 
		Short item of size 1. Main type. Input tag. Value :  6 
	Short item of size 0. Main type. End Collection tag. 
Short item of size 0. Main type. End Collection tag.

As mentioned in the comments, this HID descriptor can be split into two parts:

  • One part describes a keyboard
  • The other describes a mouse

Let's focus on the mouse part and explain some of the items:

Explication of Short item of size 1. Global type. Usage Page tag. Value : 1

It begins with a Global item tagged as a Usage Page (Short item of size 1. Global type. Usage Page tag. Value : 1). The value of the Usage Page is 1, which indicates 'Generic Desktop':: spec bluetoothhid

Explication of Short item of size 1. Local type. Usage tag. Value : 2

Next, a Local item tagged as Usage is used (Short item of size 1. Local type. Usage tag. Value : 2). Since the value is 2, it indicates that the device is a mouse:

spec bluetoothhid

The previous figure shows that the Usage Type for the mouse is CA, which stands for Collection Application according to the USB HID Usage Tables specification:

spec bluetoothhid

Explication of Short item of size 1. Main type. Collection tag. Value : 1

The 'USB Device Class Definition for Human Interface Device (HID)' specification provides the values that a Collection can take:

spec bluetoothhid

This explains that we have a Short item of size 1, Main type, Collection tag, Value: 1.

As shown in the latest figure, an application collection is 'a group of Main items.'

As far as I understand, this means a collection must contain one or more Main items, but it doesn't exclude the possibility of including Global or Local items.

This is further described in the 'Device Class Definition for Human Interface Devices (HID)':

Collection item tag: A meaningful grouping of Input, Output, and Feature items—for example, mouse, keyboard, joystick, and pointer.

A group of Main items that might be familiar to applications

The 'USB Device Class Definition for Human Interface Device (HID)' specification provides an example:"

spec bluetoothhid

Explication of Short item of size 1. Global type. Report ID tag. Value : 2

Since an HID descriptor can describe devices with multiple features (such as a mouse and a keyboard), the report ID is used to distinguish between these features.

When the device sends information to the computer, it includes the value of this report ID, allowing the computer to identify which feature corresponds to the sent data.


Internal

Let's now focus on the internal items:

        Short item of size 1. Global type. Usage Page tag. Value :  9 -> Button Page from 1 to 3 (souris a 3 boutons) 
		Short item of size 1. Local type. Usage Minimum tag. Value :  1 
		Short item of size 1. Local type. Usage Maximum tag. Value :  3 
		Short item of size 1. Global type. Logical Minimum tag. Value :  0 
		Short item of size 1. Global type. Logical Maximum tag. Value :  1 
		Short item of size 1. Global type. Report Size tag. Value :  1 
		Short item of size 1. Global type. Report Count tag. Value :  3 
		Short item of size 1. Main type. Input tag. Value :  2 
		Short item of size 1. Global type. Report Size tag. Value :  5 
		Short item of size 1. Global type. Report Count tag. Value :  1 
		Short item of size 1. Main type. Input tag. Value :  1

		Short item of size 1. Global type. Usage Page tag. Value :  1 -> Generic Desktop page
		Short item of size 1. Local type. Usage tag. Value :  30 -> X
		Short item of size 1. Local type. Usage tag. Value :  31 -> Y
		Short item of size 1. Local type. Usage tag. Value :  38 -> Wheel
		Short item of size 1. Global type. Logical Minimum tag. Value :  81 
		Short item of size 1. Global type. Logical Maximum tag. Value :  7f 
		Short item of size 1. Global type. Report Size tag. Value :  8 
		Short item of size 1. Global type. Report Count tag. Value :  3 
		Short item of size 1. Main type. Input tag. Value :  6

Here again, there are two parts that describe:

  • The buttons of the mouse in the first part
  • The axes in the second part

Note that each part begins with a Global item, tagged as a Usage Page:

  • Short item of size 1. Global type. Usage Page tag. Value : 9
  • Short item of size 1. Global type. Usage Page tag. Value : 1

Note that each part begins with a Global item, tagged as a Usage Page:

Let's focus on the first part:

        Short item of size 1. Global type. Usage Page tag. Value :  9 -> Button Page from 1 to 3 (souris a 3 boutons) 
		Short item of size 1. Local type. Usage Minimum tag. Value :  1 
		Short item of size 1. Local type. Usage Maximum tag. Value :  3 
		Short item of size 1. Global type. Logical Minimum tag. Value :  0 
		Short item of size 1. Global type. Logical Maximum tag. Value :  1 

		Short item of size 1. Global type. Report Size tag. Value :  1 
		Short item of size 1. Global type. Report Count tag. Value :  3 
		Short item of size 1. Main type. Input tag. Value :  2 ---> First Input

		Short item of size 1. Global type. Report Size tag. Value :  5 
		Short item of size 1. Global type. Report Count tag. Value :  1 
		Short item of size 1. Main type. Input tag. Value :  1 ---> Second input

Since the Usage Page value is 9, this corresponds to a 'Button Page.' It will describe the buttons of the mouse.

Finally, there are three pairs that describe the buttons:

  • The items tagged 'Usage Minimum' and 'Usage Maximum' define the range of the buttons. Since the values are 1 and 3, it means there are three buttons (button 1, button 2, and button 3).
  • The items tagged 'Logical Minimum' and 'Logical Maximum' define the values each button can take. Here, the values are 0 and 1, which can be interpreted as 'button released' and 'button clicked,' respectively.
  • The items tagged 'Report Size' and 'Report Count' represent the size of an element to send and the number of elements, respectively.

References