Bluetooth BLE implements HID questions

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
User avatar
Walkline
Posts: 13
Joined: Wed Feb 19, 2020 4:44 pm

Bluetooth BLE implements HID questions

Post by Walkline » Thu Feb 20, 2020 3:07 am

Hello, Everyone:

I am trying to learn the BLE of ESP32 and intend to implement the function of HID controller. I have added related Services and Characteristics, and can use gatts_write() and gatts_notify() to write and notify the value of Characteristics.

I encountered some problems when adding Descriptors to Characteristics, here is some fake code:

Code: Select all

import ubluetooth as bt
import struct
import time
from ble_advertising import advertising_payload

from micropython import const
_IRQ_CENTRAL_CONNECT    = const(1 << 0)
_IRQ_CENTRAL_DISCONNECT = const(1 << 1)

pack = struct.pack


class BatteryService(object):
	UUID = bt.UUID(0x180F)
	
	__CLIENT_CHARACTERISTIC_CONFIGURATION_DESC = ((bt.UUID(0x2902), bt.FLAG_READ),)

	__BATTERY_LEVEL_CHAR = (bt.UUID(0x2A19), bt.FLAG_READ | bt.FLAG_NOTIFY, __CLIENT_CHARACTERISTIC_CONFIGURATION_DESC)
	
	SERVICE = (UUID, (__BATTERY_LEVEL_CHAR,),)


class BLE_HID(object):
	def __init__(self, ble, name="hid"):
		self._ble = ble
		self._conn_handle = None
		
		self._write = self._ble.gatts_write
		self._read = self._ble.gatts_read
		self._notify = self._ble.gatts_notify

		self._services = (BatteryService.SERVICE)
		self._ble.active(True)
		self._ble.irq(handler=self._irq)

		(
			# (
			# 	self._device_name_handle,
			# 	self._appearance_handle,
			# ),
			(
				self._battery_level_handle,
				self._battery_descriptor_handle
			),
		) = self._ble.gatts_register_services(self._services)
		
		self._payload = advertising_payload(name=name, services=[BatteryService.UUID], appearance=961)
		self._advertise()

	def _irq(self, event, data):
		if event == _IRQ_CENTRAL_CONNECT:
			conn_handle, _, _, = data
			self._conn_handle = conn_handle

			time.sleep(0.5)

			# self._setup_generic_access()
			self._set_battery_level()
		elif event == _IRQ_CENTRAL_DISCONNECT:
			conn_handle, _, _, = data
			self._conn_handle = None
			self._advertise()

	# def _setup_generic_access(self):
	# 	self._write(self._device_name_handle, GenericAccess.Values.DEVICE_NAME)
	# 	self._write(self._appearance_handle, GenericAccess.Values.APPEARANCE)

	def _set_battery_level(self):
		# self._write(self._battery_descriptor_handle, pack("h", 0x0100))

	def _advertise(self, interval_us=500000):
		self._ble.gap_advertise(interval_us, adv_data=self._payload)


def main():
	ble = bt.BLE()
	hid = BLE_HID(ble)

if __name__ == "__main__":
	main()

In the _set_battery_level() function, I tried to use gatts_write() to write some values through descriptor_handle, according to the official documentation, the values format required by this Descriptor is "16bit", in nRF Connect app, it looks like:

Code: Select all

01-01
I tried 0x0100, 0b1100000000000000, 0b1100, 0b1100000, etc., but they were not written correctly, even It will also affect other Characteristic values that have been written.

To be sure, I don’t know what “16bit” is. Secondly, I don’t know if gatts_write() can write values to Descriptors?

This is my first question.

A part of the code is commented out in the fake code. I intend to replace the Generic Access Service for changing the Device Name (default is ESP32) but it also fails. I don’t know if the Generic Access Service is immutable or there is something wrong with my method?

My ESP32 board firmware is v1.12-169 with idf 3.x

User avatar
jimmo
Posts: 2015
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Bluetooth BLE implements HID questions

Post by jimmo » Thu Feb 20, 2020 3:13 am

Walkline wrote:
Thu Feb 20, 2020 3:07 am
I tried 0x0100, 0b1100000000000000, 0b1100, 0b1100000, etc., but they were not written correctly, even It will also affect other Characteristic values that have been written.
The gatts_write function takes bytes -- i.e. bytearray() or bytes() or b'\x00...'. You'll need to use struct.pack to generate bytes from your input type.

Have a look at the examples/bluetooth/ble_temperature.py demo, which works with a signed int16 value:

Code: Select all

        self._ble.gatts_write(self._handle, struct.pack('<h', int(temp_deg_c * 100)))
Walkline wrote:
Thu Feb 20, 2020 3:07 am
A part of the code is commented out in the fake code. I intend to replace the Generic Access Service for changing the Device Name (default is ESP32) but it also fails. I don’t know if the Generic Access Service is immutable or there is something wrong with my method?
Yeah... good question. At the moment you always get the default GAP service. I was actually looking at this earlier this week for an unrelated reason, i think we might have to add a ble.set_device_name() method or something.

I actually have a working HID demo, have been plannign to turn it into another example in examples/bluetooth. I will try and get this done later this week.

User avatar
Walkline
Posts: 13
Joined: Wed Feb 19, 2020 4:44 pm

Re: Bluetooth BLE implements HID questions

Post by Walkline » Thu Feb 20, 2020 3:25 am

jimmo wrote:
Thu Feb 20, 2020 3:13 am
Yeah... good question. At the moment you always get the default GAP service. I was actually looking at this earlier this week for an unrelated reason, i think we might have to add a ble.set_device_name() method or something.

I actually have a working HID demo, have been plannign to turn it into another example in examples/bluetooth. I will try and get this done later this week.
Great!!! I got two good news in one reply :lol:

And I will try your suggestion now, thanks a lot.

User avatar
Walkline
Posts: 13
Joined: Wed Feb 19, 2020 4:44 pm

Re: Bluetooth BLE implements HID questions

Post by Walkline » Thu Feb 20, 2020 10:10 am

Finally, my device can be bound to the host as a HID device and stay connected.
esp32_ble_hid.jpg
esp32_ble_hid.jpg (190.14 KiB) Viewed 1013 times
Last item "wk_kb" status "已连接" means connected.

User avatar
Walkline
Posts: 13
Joined: Wed Feb 19, 2020 4:44 pm

Re: Bluetooth BLE implements HID questions

Post by Walkline » Thu Feb 20, 2020 3:56 pm

At present, it is possible to bond and connect on the mobile, and use the BOOT button on the board to randomly input English letters.

However, a "Driver Error" message appears on the Windows 10 and the board cannot be bonded.

Another problem is that when the board is reconnected, although _IRQ_CENTRAL_CONNECT will be triggered, there is a high probability that the mobile will stuck in the "connecting" state. Unless reset the board first, and then unbond on the phone, then I can bond and connect it normally.

User avatar
Walkline
Posts: 13
Joined: Wed Feb 19, 2020 4:44 pm

Re: Bluetooth BLE implements HID questions

Post by Walkline » Mon Mar 02, 2020 5:46 am

jimmo wrote:
Thu Feb 20, 2020 3:13 am
I actually have a working HID demo, have been plannign to turn it into another example in examples/bluetooth. I will try and get this done later this week.
Hello jimmo, is there any news could be shared to us?

Now I still have a problem not solved, the device will be stuck when it reconnect to central automatically. I analyze the possible cause that when the device is connected to the mobile, the mobile will generate a mac address, and both sides will save it. After turning off Bluetooth and turning it on again on the mobile, this mac address was changed, but the saved mac of the device is not updated. At this time, reconnection will inevitably get stuck.

I try to save the previously connected mac address on the device, until the next time the mobile is connected, and then judge whether the mac addresses are consistent. If not, use gap_disconnect () to disconnect and then use gap_connect () to actively connect in the __IRQ_CENTRAL_DISCONNECT event. But their master-slave relationship is changed, and it is also stucked anyway.

Here is my code https://gitee.com/walkline/esp32-ble/bl ... er/main.py

Post Reply