Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
383 views
in Technique[技术] by (71.8m points)

python - Detecting the buttons on a Bluetooth Remote (HID over GATT)

I have a Bluetooth LE Remote which I would like to pair with my Raspberry Pi Zero.

My Remote]

I was able to successfully pair the remote with an Android phone and it then worked like a Keyboard and I was able to type numbers on the keypad into a blank note and even change the volume on the phone.

It seems raspbian does not support HID over Gatt, which means that I need to implement/mimic a minimal HID over GATT behaviour using python. (Please correct me if there is a more straight-forward way to do this.)

Raspbian

I can discover the remote in the Linux Terminal using lescan:

sudo hcitool lescan
...
AA:BB:CC:DD:EE:FF FancyRemoteXY

Now in GATTTool I can pair with the remote. However after a few seconds while the remote keeps flashing the white LED the remote disconnects from my Pi. This dis not happen with the Android phone. So there must be some sort of Handshake between the remote and the host so that the connection is retained.

# Press 1 & 3 to unpair the remote from existing devices
gatttool -I -b AA:BB:CC:DD:EE:FF
connect
# Press the OK button on the remote

Python GATT Library

I can now read the characteristics and I have also already dealt with BLE and reading/writing characteristics in the past to control a light bulb.

Currently it is unclear how the key-press-events of the remote can be detected in a "GATT-ian" fashion. I did try to enable notifications for all the characteristics I found in the remote but the callback was never called.

I did look at the Bluetooth HID over GATT Specification but did not find clues on how to achieve to be notified about key-press-events.

To sum up. My questions are as follows:

  1. How can I successfully connect the remote without connection loss
  2. How can I detect pressed buttons on the remote using GATT

Appendix: Bluetooth Explorer GATT properties

Using the iOS app I was able to list the following GATT services and characteristics.

Using this app I was also able to connect to the remote but also I was disconnected just like with the Raspberry Pi.

image 1 image 2

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I am able to access the bluetooth LE remote now on my Raspberry Pi Zero thanks to @ukBaz who pointed out how to access the HID over GATT (short HoG in Linux:

Pair the remote using bluetoothctl

This has to happen only once.

First I press and hold 1 and 3 simultaneously to clear any current pairing on the remote. This is specific for my remote model, but it is good to know that resetting a device prior to connecting it might be necessary.

sudo bluetoothctl
power on
agent on
# you might need to do `scan on` / `scan off` until you see the remote
pair AA:BB:CC:DD:EE:FF
# click ok on the remote or otherwise confirm the pairing
trust AA:BB:CC:DD:EE:FF
connect AA:BB:CC:DD:EE:FF # Pairing might perform a connect, but it does not hurt
info # info shows you if you're connected and or paired with the remote

Now reboot the RaspberryPi

Auto-Connect

In bluetoothctl the prompt shows you if you're connected to the device. After 60 seconds the connection was disconnected. I thought that this was a problem and that the remote would not send commands to the PI now, but it's important to note that once the pairing is established the remote seems to auto-connects to my Raspberry Pi.

HoG in Linux and /dev/input

I first thought that I will need to hook into the GATT protocol stack and detect those GATT messages and events in order to detect the button events of my remote. This is possible but since BlueZ on the Raspberry Pi supports HID over GATT which namely are Bluetooth Low Energy mice, joysticks, keyboards, game controllers or remote controls. This means that the Kernel on the Pi creates an input device which resides in /dev/input/. So once I was paired and connected my BLE Remote two new entries did show up in /dev/input: /dev/input/event1 and /dev/input/event0.

I made a cat /dev/input/event1 and once I pressed a key on the remote I saw some byte-garbage flying through the terminal. So I could verify that the remote was sending events to the Pi.

Reading events using evdev

Update: The evdev has a module that let's you quickly output events for the device of your choice:

python3 -m evdev.evtest

@ukBaz pointed out that I can use the pip3 package evdev to access my device. And by running the following python code I was able to detect the buttons on my remote ??

Install evdev and run the python3 console:

pip3 install evdev
python3

Run the python script

import evdev
device = evdev.InputDevice('/dev/input/event1')
print(device)
# device /dev/input/event1, name "Swisscom RC", phys "AA:BB:CC:DD:EE:FF"
for event in device.read_loop():
  if event.type == evdev.ecodes.EV_KEY:
    print(evdev.categorize(event))

This would output the following:

key event at 1550575506.080840, 2 (KEY_1), down
key event at 1550575506.230643, 2 (KEY_1), up
key event at 1550575506.410622, 3 (KEY_2), down
key event at 1550575506.560618, 3 (KEY_2), up
key event at 1550575506.730621, 4 (KEY_3), down
key event at 1550575506.880608, 4 (KEY_3), up
key event at 1550575507.080630, 5 (KEY_4), down
key event at 1550575507.230630, 5 (KEY_4), up
key event at 1550575507.430884, 6 (KEY_5), down
key event at 1550575507.580611, 6 (KEY_5), up
key event at 1550575507.770633, 7 (KEY_6), down
key event at 1550575507.930623, 7 (KEY_6), up
key event at 1550575514.040805, 104 (KEY_PAGEUP), down
key event at 1550575514.295156, 104 (KEY_PAGEUP), hold
key event at 1550575514.345174, 104 (KEY_PAGEUP), hold
key event at 1550575514.395158, 104 (KEY_PAGEUP), hold
key event at 1550575514.445154, 104 (KEY_PAGEUP), hold
key event at 1550575514.495155, 104 (KEY_PAGEUP), hold
key event at 1550575514.545187, 104 (KEY_PAGEUP), hold
key event at 1550575514.595153, 104 (KEY_PAGEUP), hold
key event at 1550575514.645155, 104 (KEY_PAGEUP), hold
key event at 1550575514.695154, 104 (KEY_PAGEUP), hold
key event at 1550575514.745153, 104 (KEY_PAGEUP), hold
key event at 1550575514.795154, 104 (KEY_PAGEUP), hold
key event at 1550575514.840643, 104 (KEY_PAGEUP), up
key event at 1550575517.290737, 116 (KEY_POWER), down
key event at 1550575517.440740, 116 (KEY_POWER), up
key event at 1550575520.110901, 158 (KEY_BACK), down
key event at 1550575520.230905, 158 (KEY_BACK), up
key event at 1550575658.372344, 113 (['KEY_MIN_INTERESTING', 'KEY_MUTE']), down
key event at 1550575658.375718, 113 (['KEY_MIN_INTERESTING', 'KEY_MUTE']), up
key event at 1550575520.530643, 164 (KEY_PLAYPAUSE), down
key event at 1550575520.680666, 164 (KEY_PLAYPAUSE), up
key event at 1550575520.880818, 167 (KEY_RECORD), down
key event at 1550575521.020807, 167 (KEY_RECORD), up

The event object has a value 0,1,2 which is down up and hold and a code like 104 which can be resolved to the corresponding code KEY_PAGEUP which on my remote is the change program key.

Please note that the Mute key has some sort of double assignment which you may need to handle differently.

Gotcha #1 - Update Bluez

The bluez package does support HID over GATT and some fixes were made along the way. At some point you would have to add the --experimental flag to the end of ExecStart in the bluez service /etc/systemd/system/dbus-org.bluez.service. However, HID over GATT was at some point added to the Default.

I did update bluez to the latest version which is bluez-5.50. I did this from source which is not too hard and described here

bluetoothctl -v
5.43

sudo apt-get install libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev -y

wget www.kernel.org/pub/linux/bluetooth/bluez-5.50.tar.xz
tar xvf bluez-5.50.tar.xz 
cd cd bluez-5.50/

./configure --prefix=/usr --mandir=/usr/share/man --sysconfdir=/etc --localstatedir=/var --enable-experimental 
make -j4
sudo make install
sudo reboot
bluetoothctl -v
# bluetoothctl: 5.50

Gotcha #2 - Run rpi-update

Just to be sure I did update the raspberry pi to the latest version using rpi-update. As with all these gotchas, I don't know if they're required, but if in doubt it's ususally a good practise to update your system.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...