Unverified Commit bde21e5a authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

Add BLE support to BluetoothHIDMaster (#2208)

Support Bluetooth BLE keyboard and mice using the same HID master
infrastructure as the BT Classic.
parent c104c671
...@@ -87,7 +87,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo ...@@ -87,7 +87,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
# Features # Features
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others) * Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial * Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
* Bluetooth Classic HID master mode (connect to BT keyboard or mouse) * Bluetooth Classic and BLE HID master mode (connect to BT keyboard or mouse)
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation * Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
* WiFi (Pico W, ESP32-based ESPHost, Atmel WINC1500) * WiFi (Pico W, ESP32-based ESPHost, Atmel WINC1500)
* Ethernet (Wired W5500, W5100, ENC28J60) * Ethernet (Wired W5500, W5100, ENC28J60)
......
#include <BluetoothHCI.h>
BluetoothHCI hci;
void BTBasicSetup() {
l2cap_init();
gatt_client_init();
sm_init();
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_SNIFF_MODE | LM_LINK_POLICY_ENABLE_ROLE_SWITCH);
hci_set_master_slave_policy(HCI_ROLE_MASTER);
hci_set_inquiry_mode(INQUIRY_MODE_RSSI_AND_EIR);
hci.setBLEName("Pico BLE Scanner");
hci.install();
hci.begin();
}
void setup() {
delay(5000);
BTBasicSetup();
}
void loop() {
Serial.printf("BEGIN BLE SCAN @%lu ...", millis());
auto l = hci.scanBLE(BluetoothHCI::any_cod);
Serial.printf("END BLE SCAN @%lu\n\n", millis());
Serial.printf("%-8s | %-17s | %-4s | %s\n", "Class", "Address", "RSSI", "Name");
Serial.printf("%-8s | %-17s | %-4s | %s\n", "--------", "-----------------", "----", "----------------");
for (auto e : l) {
Serial.printf("%08lx | %17s | %4d | %s\n", e.deviceClass(), e.addressString(), e.rssi(), e.name());
}
Serial.printf("\n\n\n");
}
...@@ -20,9 +20,13 @@ scan KEYWORD2 ...@@ -20,9 +20,13 @@ scan KEYWORD2
scanAsyncDone KEYWORD2 scanAsyncDone KEYWORD2
scanAsyncResult KEYWORD2 scanAsyncResult KEYWORD2
setName KEYWORD2
scanBLE KEYWORD2
# BTDeviceInfo # BTDeviceInfo
deviceClass KEYWORD2 deviceClass KEYWORD2
address KEYWORD2 address KEYWORD2
addressType KEYWORD2
addressString KEYWORD2 addressString KEYWORD2
rssi KEYWORD2 rssi KEYWORD2
name KEYWORD2 name KEYWORD2
......
...@@ -27,14 +27,26 @@ public: ...@@ -27,14 +27,26 @@ public:
BTDeviceInfo(uint32_t dc, const uint8_t addr[6], int rssi, const char *name) { BTDeviceInfo(uint32_t dc, const uint8_t addr[6], int rssi, const char *name) {
_deviceClass = dc; _deviceClass = dc;
memcpy(_address, addr, sizeof(_address)); memcpy(_address, addr, sizeof(_address));
_addressType = -1;
sprintf(_addressString, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); sprintf(_addressString, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
_rssi = rssi; _rssi = rssi;
_name = strdup(name); _name = strdup(name);
} }
BTDeviceInfo(uint32_t dc, const uint8_t addr[6], int addressType, int rssi, const char *name, size_t nameLen) {
_deviceClass = dc;
memcpy(_address, addr, sizeof(_address));
sprintf(_addressString, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
_addressType = addressType;
_rssi = rssi;
_name = (char *)malloc(nameLen + 1);
memcpy(_name, name, nameLen);
_name[nameLen] = 0;
}
// Copy constructor to ensure we deep-copy the string // Copy constructor to ensure we deep-copy the string
BTDeviceInfo(const BTDeviceInfo &b) { BTDeviceInfo(const BTDeviceInfo &b) {
_deviceClass = b._deviceClass; _deviceClass = b._deviceClass;
memcpy(_address, b._address, sizeof(_address)); memcpy(_address, b._address, sizeof(_address));
_addressType = b._addressType;
memcpy(_addressString, b._addressString, sizeof(_addressString)); memcpy(_addressString, b._addressString, sizeof(_addressString));
_rssi = b._rssi; _rssi = b._rssi;
_name = strdup(b._name); _name = strdup(b._name);
...@@ -57,9 +69,13 @@ public: ...@@ -57,9 +69,13 @@ public:
const char *name() { const char *name() {
return _name; return _name;
} }
int addressType() {
return _addressType;
}
private: private:
uint32_t _deviceClass; uint32_t _deviceClass;
uint8_t _address[6]; uint8_t _address[6];
int _addressType;
char _addressString[18]; char _addressString[18];
int8_t _rssi; int8_t _rssi;
char *_name; char *_name;
......
This diff is collapsed.
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
class BluetoothHCI { class BluetoothHCI {
public: public:
void install(); void install();
void setBLEName(const char *bleMasterName);
void begin(); void begin();
void uninstall(); void uninstall();
bool running(); bool running();
...@@ -42,6 +43,18 @@ public: ...@@ -42,6 +43,18 @@ public:
bool scanAsyncDone(); bool scanAsyncDone();
std::list<BTDeviceInfo> scanAsyncResult(); std::list<BTDeviceInfo> scanAsyncResult();
std::list<BTDeviceInfo> scanBLE(uint32_t uuid, int scanTimeSec = 5);
friend class BluetoothHIDMaster;
protected:
hci_con_handle_t getHCIConn() {
return _hciConn;
}
void setPairOnMeta(bool v) {
_smPair = v;
}
private: private:
void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
btstack_packet_callback_registration_t hci_event_callback_registration; btstack_packet_callback_registration_t hci_event_callback_registration;
...@@ -50,4 +63,10 @@ private: ...@@ -50,4 +63,10 @@ private:
std::list<BTDeviceInfo> _btdList; std::list<BTDeviceInfo> _btdList;
volatile bool _scanning = false; volatile bool _scanning = false;
bool _running = false; bool _running = false;
// BLE specific
uint8_t *_att = nullptr;
void parse_advertisement_data(uint8_t *packet);
volatile hci_con_handle_t _hciConn = HCI_CON_HANDLE_INVALID;
bool _smPair = false;
}; };
// KeyboardPiano example - Released to the public domain in 2024 by Earle F. Philhower, III
//
// Demonstrates using the BluetoothHIDMaster class to use a Bluetooth keyboard as a
// piano keyboard on the PicoW
//
// Hook up a phono plug to GP0 and GP1 (and GND of course...the 1st 3 pins on the PCB)
// Connect wired earbuds up and connect over BT from your keyboard and play some music.
#include <BluetoothHIDMaster.h>
#include <PWMAudio.h>
// We need the inverse map, borrow from the Keyboard library
#include <HID_Keyboard.h>
extern const uint8_t KeyboardLayout_en_US[128];
BluetoothHIDMaster hid;
PWMAudio pwm;
HIDKeyStream keystream;
int16_t sine[1000]; // One complete precalculated sine wave for oscillator use
void precalculateSine() {
for (int i = 0; i < 1000; i++) {
sine[i] = (int16_t)(2000.0 * sin(i * 2 * 3.14159 / 1000.0)); // Only make amplitude ~1/16 max so we can sum up w/o clipping
}
}
// Simple variable frequency resampling oscillator state
typedef struct {
uint32_t key; // Identifier of which key started this tone
uint32_t pos; // Current sine offset
uint32_t step; // Delta in fixed point 16p16 format
} Oscillator;
Oscillator osc[6]; // Look, ma! 6-note polyphony!
// Quiet down, now!
void silenceOscillators() {
noInterrupts();
for (int i = 0; i < 6; i++) {
osc[i].pos = 0;
osc[i].step = 0;
}
interrupts();
}
// PWM callback, generates sum of online oscillators
void fill() {
int num_samples = pwm.availableForWrite() / 2;
int16_t buff[32 * 2];
while (num_samples > 63) {
// Run in 32 LR sample chunks for speed, less loop overhead
for (int o = 0; o < 32; o++) {
int32_t sum = 0;
for (int i = 0; i < 6; i++) {
if (osc[i].step) {
sum += sine[osc[i].pos >> 16];
osc[i].pos += osc[i].step;
while (osc[i].pos >= 1000 << 16) {
osc[i].pos -= 1000 << 16;
}
}
}
if (sum > 32767) {
sum = 32767;
} else if (sum < -32767) {
sum = -32767;
}
buff[o * 2] = (int16_t) sum;
buff[o * 2 + 1] = (int16_t) sum;
}
pwm.write((const uint8_t *)buff, sizeof(buff));
num_samples -= 64;
}
}
// Mouse callbacks. Could keep track of global mouse position, update a cursor, etc.
void mm(void *cbdata, int dx, int dy, int dw) {
(void) cbdata;
Serial.printf("Mouse: X:%d Y:%d Wheel:%d\n", dx, dy, dw);
}
// Buttons are sent separately from movement
void mb(void *cbdata, int butt, bool down) {
(void) cbdata;
Serial.printf("Mouse: Button %d %s\n", butt, down ? "DOWN" : "UP");
}
// Convert a hertz floating point into a step fixed point 16p16
inline uint32_t stepForHz(float hz) {
const float stepHz = 1000.0 / 44100.0;
const float step = hz * stepHz;
return (uint32_t)(step * 65536.0);
}
uint32_t keyStepMap[128]; // The frequency of any raw HID key
void setupKeyStepMap() {
for (int i = 0; i < 128; i++) {
keyStepMap[i] = 0;
}
// Implements the "standard" PC keyboard to piano setup
// https://ux.stackexchange.com/questions/46669/mapping-piano-keys-to-computer-keyboard
keyStepMap[KeyboardLayout_en_US['a']] = stepForHz(261.6256);
keyStepMap[KeyboardLayout_en_US['w']] = stepForHz(277.1826);
keyStepMap[KeyboardLayout_en_US['s']] = stepForHz(293.6648);
keyStepMap[KeyboardLayout_en_US['e']] = stepForHz(311.1270);
keyStepMap[KeyboardLayout_en_US['d']] = stepForHz(329.6276);
keyStepMap[KeyboardLayout_en_US['f']] = stepForHz(349.2282);
keyStepMap[KeyboardLayout_en_US['t']] = stepForHz(369.9944);
keyStepMap[KeyboardLayout_en_US['g']] = stepForHz(391.9954);
keyStepMap[KeyboardLayout_en_US['y']] = stepForHz(415.3047);
keyStepMap[KeyboardLayout_en_US['h']] = stepForHz(440.0000);
keyStepMap[KeyboardLayout_en_US['u']] = stepForHz(466.1638);
keyStepMap[KeyboardLayout_en_US['j']] = stepForHz(493.8833);
keyStepMap[KeyboardLayout_en_US['k']] = stepForHz(523.2511);
keyStepMap[KeyboardLayout_en_US['o']] = stepForHz(554.3653);
keyStepMap[KeyboardLayout_en_US['l']] = stepForHz(587.3295);
keyStepMap[KeyboardLayout_en_US['p']] = stepForHz(622.2540);
keyStepMap[KeyboardLayout_en_US[';']] = stepForHz(659.2551);
keyStepMap[KeyboardLayout_en_US['\'']] = stepForHz(698.4565);
}
// We get make/break for every key which lets us hold notes while a key is depressed
void kb(void *cbdata, int key) {
bool state = (bool)cbdata;
if (state && key < 128) {
// Starting a new note
for (int i = 0; i < 6; i++) {
if (osc[i].step == 0) {
// This one is free
osc[i].key = key;
osc[i].pos = 0;
osc[i].step = keyStepMap[key];
break;
}
}
} else {
for (int i = 0; i < 6; i++) {
if (osc[i].key == (uint32_t)key) {
osc[i].step = 0;
break;
}
}
}
// The HIDKeyStream object converts a key and state into ASCII. HID key IDs do not map 1:1 to ASCII!
// Write the key and make/break state, then read 1 ASCII char back out.
keystream.write((uint8_t)key);
keystream.write((uint8_t)state);
Serial.printf("Keyboard: %02x %s = '%c'\n", key, state ? "DOWN" : "UP", state ? keystream.read() : '-');
}
// Consumer keys are the special media keys on most modern keyboards (mute/etc.)
void ckb(void *cbdata, int key) {
bool state = (bool)cbdata;
Serial.printf("Consumer: %02x %s\n", key, state ? "DOWN" : "UP");
}
void setup() {
Serial.begin();
delay(3000);
Serial.printf("Starting HID master, put your device in pairing mode now.\n");
// Init the sound generator
precalculateSine();
silenceOscillators();
setupKeyStepMap();
// Setup the HID key to ASCII conversion
keystream.begin();
// Init the PWM audio output
pwm.setStereo(true);
pwm.setBuffers(16, 64);
pwm.onTransmit(fill);
pwm.begin(44100);
// Mouse buttons and movement reported separately
hid.onMouseMove(mm);
hid.onMouseButton(mb);
// We can use the cbData as a flag to see if we're making or breaking a key
hid.onKeyDown(kb, (void *)true);
hid.onKeyUp(kb, (void *)false);
// Consumer keys are the special function ones like "mute" or "home"
hid.onConsumerKeyDown(ckb, (void *)true);
hid.onConsumerKeyUp(ckb, (void *)false);
hid.begin(true);
hid.connectBLE(); //Keyboard();
// or hid.connectMouse();
}
void loop() {
if (BOOTSEL) {
while (BOOTSEL) {
delay(1);
}
hid.disconnect();
hid.clearPairing();
Serial.printf("Restarting HID master, put your device in pairing mode now.\n");
hid.connectBLE(); //Keyboard();
}
}
...@@ -2,8 +2,8 @@ name=BluetoothHIDMaster ...@@ -2,8 +2,8 @@ name=BluetoothHIDMaster
version=1.0 version=1.0
author=Earle F. Philhower, III <earlephilhower@yahoo.com> author=Earle F. Philhower, III <earlephilhower@yahoo.com>
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com> maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
sentence=Classic Bluetooth HID (Keyboard/Mouse/Joystick) master mode sentence=Bluetooth Classic and BLE HID (Keyboard/Mouse/Joystick) master mode
paragraph=Classic Bluetooth HID (Keyboard/Mouse/Joystick) master mode paragraph=Bluetooth Classic and BLE HID (Keyboard/Mouse/Joystick) master mode
category=Communication category=Communication
url=http://github.com/earlephilhower/arduino-pico url=http://github.com/earlephilhower/arduino-pico
architectures=rp2040 architectures=rp2040
......
...@@ -73,22 +73,41 @@ ...@@ -73,22 +73,41 @@
static_cast<btstack_packet_handler_t>(CCALLBACKNAME<void(uint8_t, uint16_t, uint8_t*, uint16_t), __COUNTER__ - 1>::callback)) static_cast<btstack_packet_handler_t>(CCALLBACKNAME<void(uint8_t, uint16_t, uint8_t*, uint16_t), __COUNTER__ - 1>::callback))
void BluetoothHIDMaster::begin() { void BluetoothHIDMaster::begin(bool ble, const char *bleName) {
_ble = ble;
if (!ble) {
// Initialize HID Host // Initialize HID Host
hid_host_init(_hid_descriptor_storage, sizeof(_hid_descriptor_storage)); hid_host_init(_hid_descriptor_storage, sizeof(_hid_descriptor_storage));
hid_host_register_packet_handler(PACKETHANDLERCB(BluetoothHIDMaster, hid_packet_handler)); hid_host_register_packet_handler(PACKETHANDLERCB(BluetoothHIDMaster, hid_packet_handler));
} else {
if (bleName) {
_hci.setBLEName(bleName);
}
_hci.setPairOnMeta(true);
}
// Initialize L2CAP // Initialize L2CAP
l2cap_init(); l2cap_init();
// Initialize LE Security Manager. Needed for cross-transport key derivation // Initialize LE Security Manager. Needed for cross-transport key derivation
if (ble) {
// register for events from Security Manager
_sm_event_callback_registration.callback = PACKETHANDLERCB(BluetoothHIDMaster, sm_packet_handler);
sm_add_event_handler(&_sm_event_callback_registration);
}
sm_init(); sm_init();
if (ble) {
gatt_client_init();
hids_client_init(_hid_descriptor_storage, sizeof(_hid_descriptor_storage));
} else {
// Allow sniff mode requests by HID device and support role switch // Allow sniff mode requests by HID device and support role switch
gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_SNIFF_MODE | LM_LINK_POLICY_ENABLE_ROLE_SWITCH); gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_SNIFF_MODE | LM_LINK_POLICY_ENABLE_ROLE_SWITCH);
// try to become master on incoming connections // try to become master on incoming connections
hci_set_master_slave_policy(HCI_ROLE_MASTER); hci_set_master_slave_policy(HCI_ROLE_MASTER);
}
// enabled EIR // enabled EIR
hci_set_inquiry_mode(INQUIRY_MODE_RSSI_AND_EIR); hci_set_inquiry_mode(INQUIRY_MODE_RSSI_AND_EIR);
...@@ -158,7 +177,7 @@ bool BluetoothHIDMaster::connected() { ...@@ -158,7 +177,7 @@ bool BluetoothHIDMaster::connected() {
} }
bool BluetoothHIDMaster::connect(const uint8_t *addr) { bool BluetoothHIDMaster::connect(const uint8_t *addr) {
if (!_running) { if (!_running || _ble) {
return false; return false;
} }
while (!_hci.running()) { while (!_hci.running()) {
...@@ -170,7 +189,7 @@ bool BluetoothHIDMaster::connect(const uint8_t *addr) { ...@@ -170,7 +189,7 @@ bool BluetoothHIDMaster::connect(const uint8_t *addr) {
} }
bool BluetoothHIDMaster::connectCOD(uint32_t cod) { bool BluetoothHIDMaster::connectCOD(uint32_t cod) {
if (!_running) { if (!_running || _ble) {
return false; return false;
} }
while (!_hci.running()) { while (!_hci.running()) {
...@@ -192,6 +211,39 @@ bool BluetoothHIDMaster::connectCOD(uint32_t cod) { ...@@ -192,6 +211,39 @@ bool BluetoothHIDMaster::connectCOD(uint32_t cod) {
return false; return false;
} }
bool BluetoothHIDMaster::connectBLE(const uint8_t *addr, int addrType) {
if (!_running || !_ble) {
return false;
}
while (!_hci.running()) {
delay(10);
}
uint8_t a[6];
memcpy(a, addr, sizeof(a));
return ERROR_CODE_SUCCESS == gap_connect(a, (bd_addr_type_t)addrType);
}
bool BluetoothHIDMaster::connectBLE() {
if (!_running || !_ble) {
return false;
}
while (!_hci.running()) {
delay(10);
}
clearPairing();
auto l = _hci.scanBLE(0x1812 /* HID */);
for (auto e : l) {
DEBUGV("Scan connecting %s at %s ... ", e.name(), e.addressString());
if (connectBLE(e.address(), e.addressType())) {
DEBUGV("Connection established\n");
return true;
}
DEBUGV("Failed\n");
}
return false;
}
bool BluetoothHIDMaster::connectKeyboard() { bool BluetoothHIDMaster::connectKeyboard() {
return connectCOD(0x2540); return connectCOD(0x2540);
} }
...@@ -202,12 +254,14 @@ bool BluetoothHIDMaster::connectMouse() { ...@@ -202,12 +254,14 @@ bool BluetoothHIDMaster::connectMouse() {
bool BluetoothHIDMaster::disconnect() { bool BluetoothHIDMaster::disconnect() {
BluetoothLock b; BluetoothLock b;
if (connected()) {
hid_host_disconnect(_hid_host_cid);
}
if (!_running || !connected()) { if (!_running || !connected()) {
return false; return false;
} }
if (!_ble && connected()) {
hid_host_disconnect(_hid_host_cid);
} else if (_ble && connected()) {
gap_disconnect(_hci.getHCIConn());
}
_hid_host_descriptor_available = false; _hid_host_descriptor_available = false;
return true; return true;
} }
...@@ -215,8 +269,12 @@ bool BluetoothHIDMaster::disconnect() { ...@@ -215,8 +269,12 @@ bool BluetoothHIDMaster::disconnect() {
void BluetoothHIDMaster::clearPairing() { void BluetoothHIDMaster::clearPairing() {
BluetoothLock b; BluetoothLock b;
if (connected()) { if (connected()) {
if (_ble) {
gap_disconnect(_hci.getHCIConn());
} else {
hid_host_disconnect(_hid_host_cid); hid_host_disconnect(_hid_host_cid);
} }
}
gap_delete_all_link_keys(); gap_delete_all_link_keys();
_hid_host_descriptor_available = false; _hid_host_descriptor_available = false;
} }
...@@ -453,6 +511,102 @@ void BluetoothHIDMaster::hid_packet_handler(uint8_t packet_type, uint16_t channe ...@@ -453,6 +511,102 @@ void BluetoothHIDMaster::hid_packet_handler(uint8_t packet_type, uint16_t channe
void BluetoothHIDMaster::sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(channel);
UNUSED(size);
if (packet_type != HCI_EVENT_PACKET) {
return;
}
switch (hci_event_packet_get_type(packet)) {
case SM_EVENT_JUST_WORKS_REQUEST:
DEBUGV("Just works requested\n");
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
break;
case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
DEBUGV("Confirming numeric comparison: %" PRIu32 "\n", sm_event_numeric_comparison_request_get_passkey(packet));
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
break;
case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
DEBUGV("Display Passkey: %" PRIu32 "\n", sm_event_passkey_display_number_get_passkey(packet));
break;
case SM_EVENT_PAIRING_COMPLETE:
switch (sm_event_pairing_complete_get_status(packet)) {
case ERROR_CODE_SUCCESS:
DEBUGV("Pairing complete, success\n");
// continue - query primary services
DEBUGV("Search for HID service.\n");
//app_state = W4_HID_CLIENT_CONNECTED;
hids_client_connect(_hci.getHCIConn(), PACKETHANDLERCB(BluetoothHIDMaster, handle_gatt_client_event), HID_PROTOCOL_MODE_REPORT, &_hid_host_cid);
break;
case ERROR_CODE_CONNECTION_TIMEOUT:
DEBUGV("Pairing failed, timeout\n");
break;
case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
DEBUGV("Pairing failed, disconnected\n");
break;
case ERROR_CODE_AUTHENTICATION_FAILURE:
DEBUGV("Pairing failed, reason = %u\n", sm_event_pairing_complete_get_reason(packet));
break;
default:
break;
}
break;
default:
break;
}
}
void BluetoothHIDMaster::handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(packet_type);
UNUSED(channel);
UNUSED(size);
uint8_t status;
int idx;
if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META) {
return;
}
switch (hci_event_gattservice_meta_get_subevent_code(packet)) {
case GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED:
status = gattservice_subevent_hid_service_connected_get_status(packet);
switch (status) {
case ERROR_CODE_SUCCESS:
DEBUGV("HID service client connected, found %d services\n", gattservice_subevent_hid_service_connected_get_num_instances(packet));
_hidConnected = true;
_hid_host_descriptor_available = true;
break;
default:
DEBUGV("HID service client connection failed, status 0x%02x.\n", status);
gap_disconnect(_hci.getHCIConn());
//handle_outgoing_connection_error();
break;
}
break;
case GATTSERVICE_SUBEVENT_HID_REPORT:
idx = gattservice_subevent_hid_report_get_service_index(packet);
btstack_hid_parser_t parser;
btstack_hid_parser_init(&parser, hids_client_descriptor_storage_get_descriptor_data(_hid_host_cid, idx), hids_client_descriptor_storage_get_descriptor_len(_hid_host_cid, idx), HID_REPORT_TYPE_INPUT, gattservice_subevent_hid_report_get_report(packet), gattservice_subevent_hid_report_get_report_len(packet));
hid_host_handle_interrupt_report(&parser);
//hid_handle_input_report(
// gattservice_subevent_hid_report_get_service_index(packet),
// gattservice_subevent_hid_report_get_report(packet),
// gattservice_subevent_hid_report_get_report_len(packet));
break;
default:
break;
}
}
// Simplified US Keyboard with Shift modifier // Simplified US Keyboard with Shift modifier
#define CHAR_ILLEGAL 0xff #define CHAR_ILLEGAL 0xff
......
/* /*
Bluetooth HID Master class, can connect to keyboards, mice, and joypads Bluetooth HID Master class, can connect to keyboards, mice, and joypads
Works with Bluetooth Classic and BLE devices
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com> Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
...@@ -66,7 +67,10 @@ private: ...@@ -66,7 +67,10 @@ private:
class BluetoothHIDMaster { class BluetoothHIDMaster {
public: public:
void begin(); void begin(const char *bleName) {
begin(true, bleName);
}
void begin(bool ble = false, const char *bleName = nullptr);
bool connected(); bool connected();
void end(); void end();
bool hciRunning(); bool hciRunning();
...@@ -82,6 +86,10 @@ public: ...@@ -82,6 +86,10 @@ public:
bool connect(const uint8_t *addr); bool connect(const uint8_t *addr);
bool connectKeyboard(); bool connectKeyboard();
bool connectMouse(); bool connectMouse();
bool connectBLE(const uint8_t *addr, int addrType);
bool connectBLE();
bool disconnect(); bool disconnect();
void clearPairing(); void clearPairing();
...@@ -93,6 +101,7 @@ public: ...@@ -93,6 +101,7 @@ public:
void onConsumerKeyUp(void (*)(void *, int), void *cbData = nullptr); void onConsumerKeyUp(void (*)(void *, int), void *cbData = nullptr);
private: private:
bool _ble = false;
bool connectCOD(uint32_t cod); bool connectCOD(uint32_t cod);
BluetoothHCI _hci; BluetoothHCI _hci;
void hid_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); void hid_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
...@@ -121,4 +130,8 @@ private: ...@@ -121,4 +130,8 @@ private:
void *_consumerKeyDownData; void *_consumerKeyDownData;
void (*_consumerKeyUpCB)(void *, int) = nullptr; void (*_consumerKeyUpCB)(void *, int) = nullptr;
void *_consumerKeyUpData; void *_consumerKeyUpData;
btstack_packet_callback_registration_t _sm_event_callback_registration;
void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment