Hello, I just started a project with the boron that I plan to use as a macropad / remote of sorts. As such I wanted to start by creating a keyboard out of the boron using the HID service over BLE. I followed the tutorial from Silicon Labs here. I was able to get my iPhone to detect the boron as an HID, but whenever I try to connect, the Bluetooth on my phone reboots and the boron is disconnected. I tried using another example someone posted on a topic here, but it has the same result. I used the examples in the BLE lab (health thermometer) and they worked so I think it is an issue with how I have configured my services. I tried using the Adafruit Bluefruit library for NRF52’s since they support the same SoC that the boron uses, but ended up chasing a rabbit hole of dependencies that are not available through the Particle IDE. I have attached my code below and would really appreciate it if anyone could shed some light or point me in a direction.
The interrupts are how I wanted to send the keys being pressed as my remote currently only has four buttons that won’t be pressed too frequently.
Thank you so much!
Edit: removed logs from ISRs
/*
* Project MFISwitch
* Description:
* Author:
* Date:
*/
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
SerialLogHandler logHandler(LOG_LEVEL_TRACE);
BleUuid genericAccessService(BLE_SIG_UUID_GENERIC_ACCESS_SVC);
BleUuid humanInterfaceDeviceService(BLE_SIG_UUID_HUMAN_INTERFACE_DEVICE_SVC);
BleUuid batteryLevelService(BLE_SIG_UUID_BATTERY_SVC);
BleUuid deviceInfoService(BLE_SIG_UUID_DEVICE_INFORMATION_SVC);
BleCharacteristic batteryLevelCharacteristic("bat", BleCharacteristicProperty::NOTIFY, BleUuid(0x2A19), batteryLevelService);
BleCharacteristic pnpIDCharacteristic("pnp", BleCharacteristicProperty::READ, BleUuid(0x2A50), deviceInfoService);
BleCharacteristic hidInfoCharacteristic("hidInfo", BleCharacteristicProperty::READ, BleUuid(0x2A4A), humanInterfaceDeviceService);
BleCharacteristic protocolModeCharacteristic("protocol", BleCharacteristicProperty::NOTIFY, BleUuid(0x2A4E), humanInterfaceDeviceService);
BleCharacteristic reportMapCharacteristic("reportMap", BleCharacteristicProperty::NOTIFY, BleUuid(0x2A4B), humanInterfaceDeviceService);
BleCharacteristic reportCharacteristic("report", BleCharacteristicProperty::NOTIFY, BleUuid(0x2A4D), humanInterfaceDeviceService);
volatile int lastPressed = -1;
void sendFirst() {
lastPressed = 0;
}
void sendSecond() {
lastPressed = 1;
}
void sendThird() {
lastPressed = 2;
}
void sendFourth() {
lastPressed = 3;
}
void configureBLE() {
BLE.addCharacteristic(batteryLevelCharacteristic);
BLE.addCharacteristic(pnpIDCharacteristic);
BLE.addCharacteristic(hidInfoCharacteristic);
BLE.addCharacteristic(protocolModeCharacteristic);
BLE.addCharacteristic(reportMapCharacteristic);
BLE.addCharacteristic(reportCharacteristic);
BleAdvertisingData advertisementData;
advertisementData.appendLocalName("MFI Switch");
advertisementData.appendAppearance((ble_sig_appearance_t) BLE_APPEARANCE_HID_KEYBOARD);
advertisementData.appendServiceUUID(humanInterfaceDeviceService);
BLE.advertise(&advertisementData);
}
// setup() runs once, when the device is first turned on.
void setup() {
pinMode(D0, INPUT_PULLDOWN);
pinMode(D1, INPUT_PULLDOWN);
pinMode(D2, INPUT_PULLDOWN);
pinMode(D3, INPUT_PULLDOWN);
attachInterrupt(D0, sendFirst, RISING);
attachInterrupt(D1, sendSecond, RISING);
attachInterrupt(D2, sendThird, RISING);
attachInterrupt(D3, sendFourth, RISING);
const uint8_t pnpID[7] = {0x02, 0x06, 0x62, 0x0, 0x1, 0x0, 0x1};
pnpIDCharacteristic.setValue(pnpID, 7);
const uint8_t hidInfo[4] = {0x01, 0x11, 0x0, 0x21};
hidInfoCharacteristic.setValue(hidInfo, 4);
const uint8_t protocolMode[1] = {0x1};
protocolModeCharacteristic.setValue(protocolMode, 1);
const uint8_t reportMap[] = {
0x05, 0x01 , // Usage Page (Generic Desktop)
0x09, 0x06 , // Usage (Keyboard)
0xa1, 0x01 , // Collection (Application)
0x05, 0x07 , // Usage Page (Keyboard)
0x19, 0xe0 , // Usage Minimum (Keyboard LeftControl)
0x29, 0xe7 , // Usage Maximum (Keyboard Right GUI)
0x15, 0x00 , // Logical Minimum (0)
0x25, 0x01 , // Logical Maximum (1)
0x75, 0x01 , // Report Size (1)
0x95, 0x08 , // Report Count (8)
0x81, 0x02 , // Input (Data, Variable, Absolute) Modifier byte
0x95, 0x01 , // Report Count (1)
0x75, 0x08 , // Report Size (8)
0x81, 0x01 , // Input (Constant) Reserved byte
0x95, 0x06 , // Report Count (6)
0x75, 0x08 , // Report Size (8)
0x15, 0x00 , // Logical Minimum (0)
0x25, 0x65 , // Logical Maximum (101)
0x05, 0x07 , // Usage Page (Key Codes)
0x05, 0x01 , // Usage Minimum (Reserved (no event indicated))
0x05, 0x01 , // Usage Maximum (Keyboard Application)
0x05, 0x01 , // Input (Data,Array) Key arrays (6 bytes)
0xC0 // End Collection (Application)
};
reportMapCharacteristic.setValue(reportMap, sizeof(reportMap));
BLE.on();
BLE.onConnected([](const particle::BlePeerDevice&) {Log.info("Connecting");});
configureBLE();
Log.info("Hello World\n");
}
// loop() runs over and over again, as quickly as it can execute.
uint8_t batteryCharge;
void loop() {
// The core of your code will likely live here.
if (BLE.connected()) {
// uint8_t report[] = {0x1, 0x0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F};
// batteryLevelCharacteristic.setValue(report, sizeof(report));
// float rawCharge = System.batteryCharge();
// batteryCharge = rawCharge == -1 ? 100 : rawCharge * 100;
// batteryLevelCharacteristic.setValue(&batteryCharge, 1);
// Log.info("Level: %d, Raw: %.1f", batteryCharge, rawCharge);
}
// Log.info("Last Pressed: %d, First: %d, Second: %d, Third: %d, Fourth: %d",
// lastPressed, digitalRead(D0), digitalRead(D1), digitalRead(D2), digitalRead(D3));
delay(500);
}