Open goedzo opened 1 month ago
This is the code I use to find my keyboard, but I never see any keyboard device:
#include <bluefruit.h>
#define CONNECTION_TIMEOUT_MS 5000 // 5 seconds timeout for connection attempts
#define heartbeat_TIMEOUT_MS 5000 //
BLEClientDis disClient; // GATT client for Device Information Service (DIS)
uint32_t lastConnectionAttempt = 0; // Time of the last connection attempt
uint32_t heartbeat = 0; // Time of the last connection attempt
bool isConnecting = false; // Flag to track if we are attempting to connect
uint16_t connHandle = BLE_CONN_HANDLE_INVALID; // Connection handle to track the current connection
// List to store MAC addresses of devices that have been tried (as strings)
const int MAX_TRIED_DEVICES = 50; // Allow more devices to be tried
String triedDevices[MAX_TRIED_DEVICES];
int triedDeviceCount = 0;
String currentDeviceAddr; // Store current device address during connection attempt
// Helper function to convert a MAC address to a string
String addressToString(uint8_t* addr) {
char str[18]; // MAC address as string "XX:XX:XX:XX:XX:XX"
snprintf(str, sizeof(str), "%02X:%02X:%02X:%02X:%02X:%02X",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
return String(str);
}
bool isDeviceInTriedList(String addr) {
for (int i = 0; i < triedDeviceCount; i++) {
if (triedDevices[i] == addr) {
return true; // Device already tried
}
}
return false;
}
void addDeviceToTriedList(String addr) {
if (triedDeviceCount < MAX_TRIED_DEVICES) {
triedDevices[triedDeviceCount] = addr; // Add the address as a string
triedDeviceCount++;
}
}
// Callback function when a connection is established
void connect_callback(uint16_t conn_handle) {
BLEConnection* conn = Bluefruit.Connection(conn_handle);
isConnecting = false;
connHandle = conn_handle; // Store the connection handle
// Discover GATT Services (including Device Information Service)
if (disClient.discover(conn_handle)) {
char buffer[32] = {0}; // Buffer to store characteristic values
// Query the manufacturer name
if (disClient.getManufacturer(buffer, sizeof(buffer))) {
Serial.print("Manufacturer: ");
Serial.println(buffer);
}
// Query the model number
if (disClient.getModel(buffer, sizeof(buffer))) {
Serial.print("Model: ");
Serial.println(buffer);
}
// Query the serial number
if (disClient.getSerial(buffer, sizeof(buffer))) {
Serial.print("Serial Number: ");
Serial.println(buffer);
}
// Query the firmware revision
if (disClient.getFirmwareRev(buffer, sizeof(buffer))) {
Serial.print("Firmware Version: ");
Serial.println(buffer);
}
// Query the hardware revision
if (disClient.getHardwareRev(buffer, sizeof(buffer))) {
Serial.print("Hardware Version: ");
Serial.println(buffer);
}
// Query the software revision (if available)
if (disClient.getSoftwareRev(buffer, sizeof(buffer))) {
Serial.print("Software Version: ");
Serial.println(buffer);
}
} else {
Serial.println("Device Information Service NOT found");
}
// Disconnect after retrieving the information
conn->disconnect();
}
// Callback function when a connection is dropped or disconnected
void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
connHandle = BLE_CONN_HANDLE_INVALID; // Reset the connection handle
isConnecting = false;
// Scanning will automatically resume due to `restartOnDisconnect`
}
void parseAppleManufacturerData(uint8_t* data, int len) {
if (len >= 25) { // iBeacon packets are at least 25 bytes long
uint16_t companyId = (data[1] << 8) | data[0];
if (companyId == 0x004C) { // Apple Inc.
uint8_t beaconType = data[2];
if (beaconType == 0x02 && data[3] == 0x15) { // iBeacon type
Serial.print("iBeacon UUID: ");
for (int i = 4; i < 20; i++) {
Serial.printf("%02X", data[i]);
if (i == 7 || i == 9 || i == 11 || i == 13) Serial.print("-");
}
Serial.println();
int major = (data[20] << 8) | data[21];
int minor = (data[22] << 8) | data[23];
int8_t txPower = data[24];
Serial.printf("Major: %d, Minor: %d, Tx Power: %d dBm\n", major, minor, txPower);
}
}
}
}
void scan_callback(ble_gap_evt_adv_report_t* report) {
// Stop scanning before connecting
Bluefruit.Scanner.stop();
// Convert the device address to a string
currentDeviceAddr = addressToString(report->peer_addr.addr);
// Check if the device is in the tried list before proceeding
if (isDeviceInTriedList(currentDeviceAddr)) {
Bluefruit.Scanner.start(0); // Resume scanning if the device has already been tried
return; // Exit if the device is in the tried list
}
// Add the device to the tried list
addDeviceToTriedList(currentDeviceAddr);
// Print new device found
Serial.printf("New Device: %s\n", currentDeviceAddr.c_str());
// Compact Advertising Report Type Details
Serial.printf("Connectable: %d, Scannable: %d, Directed: %d, Scan Resp: %d, Ext PDU: %d, Data Status: %d\n",
report->type.connectable, report->type.scannable, report->type.directed,
report->type.scan_response, report->type.extended_pdu, report->type.status);
// Skip non-connectable devices
if (report->type.connectable == 0) {
Serial.println("Skipping non-connectable device.");
Bluefruit.Scanner.start(0); // Resume scanning
return;
}
// Check for Appearance (if available)
uint8_t buffer[2] = {0};
if (Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_APPEARANCE, buffer, sizeof(buffer))) {
uint16_t appearance = buffer[1] << 8 | buffer[0]; // Combine the two bytes
Serial.printf("Appearance: 0x%04X\n", appearance);
printAppearance(appearance); // Custom function to print human-readable appearance
} else {
Serial.println("Appearance: Not available");
}
// Check for Manufacturer Specific Data (if available)
uint8_t manuData[32] = {0};
int manuDataLen = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, manuData, sizeof(manuData));
if (manuDataLen > 0) {
Serial.print("Manufacturer Data: ");
for (int i = 0; i < manuDataLen; i++) {
Serial.printf("%02X ", manuData[i]);
}
Serial.println();
// If Apple Manufacturer Data, try to parse it further (e.g., iBeacon data)
if (manuData[0] == 0x4C && manuData[1] == 0x00) {
parseAppleManufacturerData(manuData, manuDataLen); // Custom function to parse Apple data
}
} else {
Serial.println("Manufacturer Data: Not available");
}
// Check for TX Power Level (if available)
uint8_t txPower[1] = {0};
if (Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_TX_POWER_LEVEL, txPower, sizeof(txPower))) {
Serial.printf("TX Power Level: %d dBm\n", (int8_t)txPower[0]);
} else {
Serial.println("TX Power Level: Not available");
}
// Check for common GATT services to identify the device type
if (Bluefruit.Scanner.checkReportForUuid(report, 0x1812)) {
Serial.println("Service: HID (Keyboard/Mouse/Gamepad)");
} else if (Bluefruit.Scanner.checkReportForUuid(report, 0x180F)) {
Serial.println("Service: Battery Service");
} else if (Bluefruit.Scanner.checkReportForUuid(report, 0x180E)) {
Serial.println("Service: Phone Alert Status");
} else {
Serial.println("Service: Unknown or Not Advertised");
}
// Try to connect if no name is advertised
uint8_t nameBuffer[32] = {0};
if (!Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, nameBuffer, sizeof(nameBuffer)) &&
!Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, nameBuffer, sizeof(nameBuffer))) {
// No name advertised, attempting to connect
if (!isConnecting) {
lastConnectionAttempt = millis();
isConnecting = true;
// Try to connect to the device
Bluefruit.Central.connect(report);
}
} else {
Serial.printf("Advertised Name: %s\n", nameBuffer);
}
Bluefruit.Scanner.start(0); // Resume scanning
}
// Helper function to print human-readable appearance
void printAppearance(uint16_t appearance) {
switch (appearance) {
case 961:
Serial.println("Device Type: Keyboard");
break;
case 962:
Serial.println("Device Type: Mouse");
break;
case 963:
Serial.println("Device Type: Gamepad");
break;
case 1344:
Serial.println("Device Type: Phone");
break;
case 832:
Serial.println("Device Type: Watch");
break;
default:
Serial.println("Device Type: Unknown");
break;
}
}
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
// Initialize Bluefruit with max connections (Peripheral = 0, Central = 1)
Bluefruit.begin(0, 1);
// Set name for this BLE Central device
Bluefruit.setName("Bluefruit52 Central");
// Set connection and disconnection callbacks
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
// Automatically restart scanning on disconnection
Bluefruit.Scanner.restartOnDisconnect(true); // Automatically restart scanning after disconnect
// Initialize the Device Information Service client
disClient.begin();
// Start scanning for devices
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.setInterval(160, 80); // Set scan interval and window
Bluefruit.Scanner.useActiveScan(true); // Request scan response data
Bluefruit.Scanner.start(0); // 0 = scan forever
}
void loop() {
if(millis() - heartbeat > heartbeat_TIMEOUT_MS ){
Serial.printf(".");
heartbeat=millis();
}
// Check for timeout on connection attempts
if (isConnecting && (millis() - lastConnectionAttempt > CONNECTION_TIMEOUT_MS)) {
// Forcefully stop the connection attempt by resetting BLE Central
if (connHandle != BLE_CONN_HANDLE_INVALID) {
BLEConnection* conn = Bluefruit.Connection(connHandle);
if (conn) {
conn->disconnect(); // Properly disconnect the active connection
}
}
// Reset connection-related variables
isConnecting = false;
connHandle = BLE_CONN_HANDLE_INVALID;
// Restart scanning
Bluefruit.Scanner.stop();
delay(100); // Short delay to ensure the stop completes
Bluefruit.Scanner.start(0); // Restart scanning
}
}
I am trying to connect a bluetooth HID keyboard to my t-echo, however the example code https://github.com/Xinyuan-LilyGO/T-Echo/tree/main/examples/ble_hid_central does not work.
Even the comment in the code itself says this:
display->print("Scan ble keyboard"); //crashes here
So how can I connect my bluetooth keyboard to my t-echo device?