|
/* |
|
* 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); |
|
} |
|
} |
Research news
I found this library for esp32 on arduino https://github.com/T-vK/ESP32-BLE-Keyboard/blob/master/BleKeyboard.cpp
And this other https://github.com/T-vK/ESP32-BLE-Mouse/blob/master/BleMouse.cpp
I think that modifying it could adapt to the braille HID takes care of automatically making the description table for the controller you just have to load the list of the hid braille and then one in the code adds the keys or functions that you need to use
the libraries that are called to do all the work are "BLEHIDDevice" and "HIDTypes.h" en https://github.com/espressif/arduino-esp32/blob/master/libraries/BLE/src/
and
Those examples could be replaced by the HID braille example
It is only to order it as a library so that later in the code you can call the library and pass the parameters that are needed such as the number of braille cells, buttons, etc.
something like that
This is what I have been able to understand so far
I understand that with 0x05 I must pass how many braille cells my screen has but I don't know how to do that
according to the hid code nvda source brailleDisplayDrivers hid.py is defined like this
I don't know if I'm on the right track or I'm totally lost, but if someone understands and knows how I can continue this structure, it would be good to help.