|  |  | @@ -0,0 +1,254 @@ | 
    
    |  |  | /* | 
    
    |  |  | * Sample program for ESP32 acting as a Bluetooth keyboard | 
    
    |  |  | * | 
    
    |  |  | * Copyright (c) 2019 Manuel Bl | 
    
    |  |  | * | 
    
    |  |  | * Licensed under MIT License | 
    
    |  |  | * https://opensource.org/licenses/MIT | 
    
    |  |  | */ | 
    
    |  |  | 
 | 
    
    |  |  | // | 
    
    |  |  | // This program lets an ESP32 act as a keyboard connected via Bluetooth. | 
    
    |  |  | // When a button attached to the ESP32 is pressed, it will generate the key strokes for a message. | 
    
    |  |  | // | 
    
    |  |  | // For the setup, a momentary button should be connected to pin 2 and to ground. | 
    
    |  |  | // Pin 2 will be configured as an input with pull-up. | 
    
    |  |  | // | 
    
    |  |  | // In order to receive the message, add the ESP32 as a Bluetooth keyboard of your computer | 
    
    |  |  | // or mobile phone: | 
    
    |  |  | // | 
    
    |  |  | // 1. Go to your computers/phones settings | 
    
    |  |  | // 2. Ensure Bluetooth is turned on | 
    
    |  |  | // 3. Scan for Bluetooth devices | 
    
    |  |  | // 4. Connect to the device called "ESP32 Keyboard" | 
    
    |  |  | // 5. Open an empty document in a text editor | 
    
    |  |  | // 6. Press the button attached to the ESP32 | 
    
    |  |  | 
 | 
    
    |  |  | #define US_KEYBOARD 1 | 
    
    |  |  | 
 | 
    
    |  |  | #include <Arduino.h> | 
    
    |  |  | #include "BLEDevice.h" | 
    
    |  |  | #include "BLEHIDDevice.h" | 
    
    |  |  | #include "HIDTypes.h" | 
    
    |  |  | #include "HIDKeyboardTypes.h" | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | // Change the below values if desired | 
    
    |  |  | #define BUTTON_PIN 2 | 
    
    |  |  | #define MESSAGE "Hello from ESP32\n" | 
    
    |  |  | #define DEVICE_NAME "ESP32 Keyboard" | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | // Forward declarations | 
    
    |  |  | void bluetoothTask(void*); | 
    
    |  |  | void typeText(const char* text); | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | bool isBleConnected = false; | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | void setup() { | 
    
    |  |  | Serial.begin(115200); | 
    
    |  |  | 
 | 
    
    |  |  | // configure pin for button | 
    
    |  |  | pinMode(BUTTON_PIN, INPUT_PULLUP); | 
    
    |  |  | 
 | 
    
    |  |  | // start Bluetooth task | 
    
    |  |  | xTaskCreate(bluetoothTask, "bluetooth", 20000, NULL, 5, NULL); | 
    
    |  |  | } | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | void loop() { | 
    
    |  |  | if (isBleConnected && digitalRead(BUTTON_PIN) == LOW) { | 
    
    |  |  | // button has been pressed: type message | 
    
    |  |  | Serial.println(MESSAGE); | 
    
    |  |  | typeText(MESSAGE); | 
    
    |  |  | } | 
    
    |  |  | 
 | 
    
    |  |  | delay(100); | 
    
    |  |  | } | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | // Message (report) sent when a key is pressed or released | 
    
    |  |  | struct InputReport { | 
    
    |  |  | uint8_t modifiers;	     // bitmask: CTRL = 1, SHIFT = 2, ALT = 4 | 
    
    |  |  | uint8_t reserved;        // must be 0 | 
    
    |  |  | uint8_t pressedKeys[6];  // up to six concurrenlty pressed keys | 
    
    |  |  | }; | 
    
    |  |  | 
 | 
    
    |  |  | // Message (report) received when an LED's state changed | 
    
    |  |  | struct OutputReport { | 
    
    |  |  | uint8_t leds;            // bitmask: num lock = 1, caps lock = 2, scroll lock = 4, compose = 8, kana = 16 | 
    
    |  |  | }; | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | // The report map describes the HID device (a keyboard in this case) and | 
    
    |  |  | // the messages (reports in HID terms) sent and received. | 
    
    |  |  | static const uint8_t REPORT_MAP[] = { | 
    
    |  |  | USAGE_PAGE(1),      0x01,       // Generic Desktop Controls | 
    
    |  |  | USAGE(1),           0x06,       // Keyboard | 
    
    |  |  | COLLECTION(1),      0x01,       // Application | 
    
    |  |  | REPORT_ID(1),       0x01,       //   Report ID (1) | 
    
    |  |  | USAGE_PAGE(1),      0x07,       //   Keyboard/Keypad | 
    
    |  |  | USAGE_MINIMUM(1),   0xE0,       //   Keyboard Left Control | 
    
    |  |  | USAGE_MAXIMUM(1),   0xE7,       //   Keyboard Right Control | 
    
    |  |  | LOGICAL_MINIMUM(1), 0x00,       //   Each bit is either 0 or 1 | 
    
    |  |  | LOGICAL_MAXIMUM(1), 0x01, | 
    
    |  |  | REPORT_COUNT(1),    0x08,       //   8 bits for the modifier keys | 
    
    |  |  | REPORT_SIZE(1),     0x01, | 
    
    |  |  | HIDINPUT(1),        0x02,       //   Data, Var, Abs | 
    
    |  |  | REPORT_COUNT(1),    0x01,       //   1 byte (unused) | 
    
    |  |  | REPORT_SIZE(1),     0x08, | 
    
    |  |  | HIDINPUT(1),        0x01,       //   Const, Array, Abs | 
    
    |  |  | REPORT_COUNT(1),    0x06,       //   6 bytes (for up to 6 concurrently pressed keys) | 
    
    |  |  | REPORT_SIZE(1),     0x08, | 
    
    |  |  | LOGICAL_MINIMUM(1), 0x00, | 
    
    |  |  | LOGICAL_MAXIMUM(1), 0x65,       //   101 keys | 
    
    |  |  | USAGE_MINIMUM(1),   0x00, | 
    
    |  |  | USAGE_MAXIMUM(1),   0x65, | 
    
    |  |  | HIDINPUT(1),        0x00,       //   Data, Array, Abs | 
    
    |  |  | REPORT_COUNT(1),    0x05,       //   5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) | 
    
    |  |  | REPORT_SIZE(1),     0x01, | 
    
    |  |  | USAGE_PAGE(1),      0x08,       //   LEDs | 
    
    |  |  | USAGE_MINIMUM(1),   0x01,       //   Num Lock | 
    
    |  |  | USAGE_MAXIMUM(1),   0x05,       //   Kana | 
    
    |  |  | LOGICAL_MINIMUM(1), 0x00, | 
    
    |  |  | LOGICAL_MAXIMUM(1), 0x01, | 
    
    |  |  | HIDOUTPUT(1),       0x02,       //   Data, Var, Abs | 
    
    |  |  | REPORT_COUNT(1),    0x01,       //   3 bits (Padding) | 
    
    |  |  | REPORT_SIZE(1),     0x03, | 
    
    |  |  | HIDOUTPUT(1),       0x01,       //   Const, Array, Abs | 
    
    |  |  | END_COLLECTION(0)               // End application collection | 
    
    |  |  | }; | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | BLEHIDDevice* hid; | 
    
    |  |  | BLECharacteristic* input; | 
    
    |  |  | BLECharacteristic* output; | 
    
    |  |  | 
 | 
    
    |  |  | const InputReport NO_KEY_PRESSED = { }; | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | /* | 
    
    |  |  | * Callbacks related to BLE connection | 
    
    |  |  | */ | 
    
    |  |  | class BleKeyboardCallbacks : public BLEServerCallbacks { | 
    
    |  |  | 
 | 
    
    |  |  | void onConnect(BLEServer* server) { | 
    
    |  |  | isBleConnected = true; | 
    
    |  |  | 
 | 
    
    |  |  | // Allow notifications for characteristics | 
    
    |  |  | BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); | 
    
    |  |  | cccDesc->setNotifications(true); | 
    
    |  |  | 
 | 
    
    |  |  | Serial.println("Client has connected"); | 
    
    |  |  | } | 
    
    |  |  | 
 | 
    
    |  |  | void onDisconnect(BLEServer* server) { | 
    
    |  |  | isBleConnected = false; | 
    
    |  |  | 
 | 
    
    |  |  | // Disallow notifications for characteristics | 
    
    |  |  | BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); | 
    
    |  |  | cccDesc->setNotifications(false); | 
    
    |  |  | 
 | 
    
    |  |  | Serial.println("Client has disconnected"); | 
    
    |  |  | } | 
    
    |  |  | }; | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | /* | 
    
    |  |  | * Called when the client (computer, smart phone) wants to turn on or off | 
    
    |  |  | * the LEDs in the keyboard. | 
    
    |  |  | * | 
    
    |  |  | * bit 0 - NUM LOCK | 
    
    |  |  | * bit 1 - CAPS LOCK | 
    
    |  |  | * bit 2 - SCROLL LOCK | 
    
    |  |  | */ | 
    
    |  |  | class OutputCallbacks : public BLECharacteristicCallbacks { | 
    
    |  |  | void onWrite(BLECharacteristic* characteristic) { | 
    
    |  |  | OutputReport* report = (OutputReport*) characteristic->getData(); | 
    
    |  |  | Serial.print("LED state: "); | 
    
    |  |  | Serial.print((int) report->leds); | 
    
    |  |  | Serial.println(); | 
    
    |  |  | } | 
    
    |  |  | }; | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | void bluetoothTask(void*) { | 
    
    |  |  | 
 | 
    
    |  |  | // initialize the device | 
    
    |  |  | BLEDevice::init(DEVICE_NAME); | 
    
    |  |  | BLEServer* server = BLEDevice::createServer(); | 
    
    |  |  | server->setCallbacks(new BleKeyboardCallbacks()); | 
    
    |  |  | 
 | 
    
    |  |  | // create an HID device | 
    
    |  |  | hid = new BLEHIDDevice(server); | 
    
    |  |  | input = hid->inputReport(1); // report ID | 
    
    |  |  | output = hid->outputReport(1); // report ID | 
    
    |  |  | output->setCallbacks(new OutputCallbacks()); | 
    
    |  |  | 
 | 
    
    |  |  | // set manufacturer name | 
    
    |  |  | hid->manufacturer()->setValue("Maker Community"); | 
    
    |  |  | // set USB vendor and product ID | 
    
    |  |  | hid->pnp(0x02, 0xe502, 0xa111, 0x0210); | 
    
    |  |  | // information about HID device: device is not localized, device can be connected | 
    
    |  |  | hid->hidInfo(0x00, 0x02); | 
    
    |  |  | 
 | 
    
    |  |  | // Security: device requires bonding | 
    
    |  |  | BLESecurity* security = new BLESecurity(); | 
    
    |  |  | security->setAuthenticationMode(ESP_LE_AUTH_BOND); | 
    
    |  |  | 
 | 
    
    |  |  | // set report map | 
    
    |  |  | hid->reportMap((uint8_t*)REPORT_MAP, sizeof(REPORT_MAP)); | 
    
    |  |  | hid->startServices(); | 
    
    |  |  | 
 | 
    
    |  |  | // set battery level to 100% | 
    
    |  |  | hid->setBatteryLevel(100); | 
    
    |  |  | 
 | 
    
    |  |  | // advertise the services | 
    
    |  |  | BLEAdvertising* advertising = server->getAdvertising(); | 
    
    |  |  | advertising->setAppearance(HID_KEYBOARD); | 
    
    |  |  | advertising->addServiceUUID(hid->hidService()->getUUID()); | 
    
    |  |  | advertising->addServiceUUID(hid->deviceInfo()->getUUID()); | 
    
    |  |  | advertising->addServiceUUID(hid->batteryService()->getUUID()); | 
    
    |  |  | advertising->start(); | 
    
    |  |  | 
 | 
    
    |  |  | Serial.println("BLE ready"); | 
    
    |  |  | delay(portMAX_DELAY); | 
    
    |  |  | }; | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | void typeText(const char* text) { | 
    
    |  |  | int len = strlen(text); | 
    
    |  |  | for (int i = 0; i < len; i++) { | 
    
    |  |  | 
 | 
    
    |  |  | // translate character to key combination | 
    
    |  |  | uint8_t val = (uint8_t)text[i]; | 
    
    |  |  | if (val > KEYMAP_SIZE) | 
    
    |  |  | continue; // character not available on keyboard - skip | 
    
    |  |  | KEYMAP map = keymap[val]; | 
    
    |  |  | 
 | 
    
    |  |  | // create input report | 
    
    |  |  | InputReport report = { | 
    
    |  |  | .modifiers = map.modifier, | 
    
    |  |  | .reserved = 0, | 
    
    |  |  | .pressedKeys = { | 
    
    |  |  | map.usage, | 
    
    |  |  | 0, 0, 0, 0, 0 | 
    
    |  |  | } | 
    
    |  |  | }; | 
    
    |  |  | 
 | 
    
    |  |  | // send the input report | 
    
    |  |  | input->setValue((uint8_t*)&report, sizeof(report)); | 
    
    |  |  | input->notify(); | 
    
    |  |  | 
 | 
    
    |  |  | delay(5); | 
    
    |  |  | 
 | 
    
    |  |  | // release all keys between two characters; otherwise two identical | 
    
    |  |  | // consecutive characters are treated as just one key press | 
    
    |  |  | input->setValue((uint8_t*)&NO_KEY_PRESSED, sizeof(NO_KEY_PRESSED)); | 
    
    |  |  | input->notify(); | 
    
    |  |  | 
 | 
    
    |  |  | delay(5); | 
    
    |  |  | } | 
    
    |  |  | } |