Skip to content

Instantly share code, notes, and snippets.

@morganabc
Created January 15, 2025 00:04
Show Gist options
  • Select an option

  • Save morganabc/da35d7a3b045d59741e328a7867a14ea to your computer and use it in GitHub Desktop.

Select an option

Save morganabc/da35d7a3b045d59741e328a7867a14ea to your computer and use it in GitHub Desktop.
#include "BLEDevice.h"
#include "BLEHIDDevice.h"
#include "HIDTypes.h"
#define DEVICE_NAME "ESP32 Mouse"
#define BLE_MANUFACTURER "TinyUSB"
BLEHIDDevice* hid;
BLECharacteristic* input;
BLECharacteristic* output;
bool isBleConnected = false;
#define MOUSE_LEFT 1
#define MOUSE_RIGHT 2
#define MOUSE_MIDDLE 4
struct InputReport {
uint8_t buttons;
int8_t x;
int8_t y;
int8_t w;
int8_t hw;
};
void setup() {
Serial.begin(115200);
Serial2.begin(115200, SERIAL_8N1, 22, 23);
xTaskCreate(bluetoothTask, "bluetooth", 20000, NULL, 5, NULL);
}
void loop() {
while (Serial2.available()) {
String uartData = Serial2.readStringUntil('\n');
if (uartData.startsWith("REPORT: ")) {
int r, m, l, w, x, y;
sscanf(uartData.c_str(), "REPORT: BTN %d %d %d WHEEL %d X %d Y %d", &l, &m, &r, &w, &x, &y);
uint8_t button = 0;
if (r) button |= MOUSE_RIGHT;
else button &= ~MOUSE_RIGHT;
if (m) button |= MOUSE_MIDDLE;
else button &= ~MOUSE_MIDDLE;
if (l) button |= MOUSE_LEFT;
else button &= ~MOUSE_LEFT;
InputReport report = {
.buttons = button,
.x = x,
.y = y,
.w = w,
.hw = 0
};
if (isBleConnected) {
input->setValue((uint8_t*)&report, sizeof(report));
input->notify();
}
}
// Serial.println(uartData);
}
}
static const uint8_t _hidReportDescriptor[] = {
USAGE_PAGE(1), 0x01, // USAGE_PAGE (Generic Desktop)
USAGE(1), 0x02, // USAGE (Mouse)
COLLECTION(1), 0x01, // COLLECTION (Application)
USAGE(1), 0x01, // USAGE (Pointer)
COLLECTION(1), 0x00, // COLLECTION (Physical)
// ------------------------------------------------- Buttons (Left, Right, Middle, Back, Forward)
USAGE_PAGE(1), 0x09, // USAGE_PAGE (Button)
USAGE_MINIMUM(1), 0x01, // USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM(1), 0x05, // USAGE_MAXIMUM (Button 5)
LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM(1), 0x01, // LOGICAL_MAXIMUM (1)
REPORT_SIZE(1), 0x01, // REPORT_SIZE (1)
REPORT_COUNT(1), 0x05, // REPORT_COUNT (5)
HIDINPUT(1), 0x02, // INPUT (Data, Variable, Absolute) ;5 button bits
// ------------------------------------------------- Padding
REPORT_SIZE(1), 0x03, // REPORT_SIZE (3)
REPORT_COUNT(1), 0x01, // REPORT_COUNT (1)
HIDINPUT(1), 0x03, // INPUT (Constant, Variable, Absolute) ;3 bit padding
// ------------------------------------------------- X/Y position, Wheel
USAGE_PAGE(1), 0x01, // USAGE_PAGE (Generic Desktop)
USAGE(1), 0x30, // USAGE (X)
USAGE(1), 0x31, // USAGE (Y)
USAGE(1), 0x38, // USAGE (Wheel)
LOGICAL_MINIMUM(1), 0x81, // LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM(1), 0x7f, // LOGICAL_MAXIMUM (127)
REPORT_SIZE(1), 0x08, // REPORT_SIZE (8)
REPORT_COUNT(1), 0x03, // REPORT_COUNT (3)
HIDINPUT(1), 0x06, // INPUT (Data, Variable, Relative) ;3 bytes (X,Y,Wheel)
// ------------------------------------------------- Horizontal wheel
USAGE_PAGE(1), 0x0c, // USAGE PAGE (Consumer Devices)
USAGE(2), 0x38, 0x02, // USAGE (AC Pan)
LOGICAL_MINIMUM(1), 0x81, // LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM(1), 0x7f, // LOGICAL_MAXIMUM (127)
REPORT_SIZE(1), 0x08, // REPORT_SIZE (8)
REPORT_COUNT(1), 0x01, // REPORT_COUNT (1)
HIDINPUT(1), 0x06, // INPUT (Data, Var, Rel)
END_COLLECTION(0), // END_COLLECTION
END_COLLECTION(0) // END_COLLECTION
};
class BleHIDCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* server) {
isBleConnected = true;
BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
cccDesc->setNotifications(true);
Serial.println("CONNECTED");
}
void onDisconnect(BLEServer* server) {
isBleConnected = false;
BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
cccDesc->setNotifications(false);
Serial.println("DISCONNECTED");
}
};
void bluetoothTask(void*) {
BLEDevice::init(DEVICE_NAME);
BLEServer* server = BLEDevice::createServer();
server->setCallbacks(new BleHIDCallbacks());
hid = new BLEHIDDevice(server);
input = hid->inputReport(0);
hid->manufacturer()->setValue(BLE_MANUFACTURER);
hid->pnp(0x02, 0xe502, 0xa111, 0x0210);
hid->hidInfo(0x00, 0x02);
BLESecurity* security = new BLESecurity();
security->setAuthenticationMode(ESP_LE_AUTH_BOND);
hid->reportMap((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor));
hid->startServices();
hid->setBatteryLevel(100);
BLEAdvertising* advertising = server->getAdvertising();
advertising->setAppearance(HID_MOUSE);
advertising->addServiceUUID(hid->hidService()->getUUID());
advertising->addServiceUUID(hid->deviceInfo()->getUUID());
advertising->addServiceUUID(hid->batteryService()->getUUID());
advertising->start();
Serial.println("BLE READY");
delay(portMAX_DELAY);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment