Bluetooth BLE HID Keyboard for the AlterErgo device, based on Seeed Studio Tiny BLE.
Dependencies: BLE_API BLE_HID mbed nRF51822
Fork of BLENano_HID by
main.cpp
- Committer:
- shervinemami
- Date:
- 2018-08-26
- Revision:
- 1:c6659c8882c9
- Parent:
- 0:3435302551b3
File content as of revision 1:c6659c8882c9:
/** * Handheld BLE finger input device based on Seeed Studio Tiny BLE (TinyBLE). * By Shervin Emami (http://shervinemami.info/), Apr 2018. * If the analog voltage on a "finger" pin is below a threshold voltage, * then it sends a rare keypress using Bluetooth, so that any computer or * smartphone can be configured to perform a desired function due to this input event. * * * This program implements a complete HID-over-Gatt Profile: * - HID is provided by KeyboardService * - Battery Service * - Device Information Service * * Complete strings can be sent over BLE using printf. Please note, however, than a 12char string * will take about 500ms to transmit, principally because of the limited notification rate in BLE. * KeyboardService uses a circular buffer to store the strings to send, and calls to putc will fail * once this buffer is full. This will result in partial strings being sent to the client. * Tested with mbed libraries for April 2nd, 2018. If you use a newer version of mbed or other * libs and you have problems, try rolling back to April 2nd, 2018. */ // Configure the settings of my board. #include "tiny_ble.h" // Seeed Studio Tiny BLE // Define LOG_MESSAGES if you want various debug messages to be sent to the PC via a UART cable. #define LOG_MESSAGES // Various USB Keyboard keycodes that we can send. #define KEYCODE_F4 131 #define KEYCODE_F5 132 #define KEYCODE_F6 133 #define KEYCODE_F7 134 #define KEYCODE_F8 135 #define KEYCODE_F9 136 #define KEYCODE_F10 137 #define KEYCODE_F11 138 #define KEYCODE_F12 139 #define KEYCODE_HOME 145 #define KEYCODE_PAGEUP 146 #define KEYCODE_PAGEDOWN 147 #define KEYCODE_RIGHT 148 #define KEYCODE_LEFT 149 #define KEYCODE_DOWN 150 #define KEYCODE_UP 151 #define KEYCODE_ESCAPE 157 #define KEYCODE_RIGHTCLK 188 #define KEYCODE_ACCENT 96 #define KEYCODE_NUMLOCK 193 const int FING1_KEYCODE = KEYCODE_F8; // Set the keypress that will be sent over bluetooth. const int FING2DOWN_KEYCODE = KEYCODE_F4; // const int FING2UP_KEYCODE = KEYCODE_NUMLOCK; const int FING3_KEYCODE = KEYCODE_UP; // const int FING4_KEYCODE = KEYCODE_DOWN; // const float FINGER1_PRESS = 0.42f; // Set how much the person needs to press their finger in to trigger a keypress. const float FINGER2_PRESS = 0.41f; // Note that each finger is slightly different strength, due to the mechanical design. const float FINGER3_PRESS = 0.38f; // const float FINGER4_PRESS = 0.37f; // const float FINGER1_RELEASE = 0.35f; // Set how much the person needs to release their finger to finish a keypress. const float FINGER2_RELEASE = 0.35f; // Note that each finger is slightly different strength, due to the mechanical design. const float FINGER3_RELEASE = 0.34f; // const float FINGER4_RELEASE = 0.34f; // const float BATTERY_NEEDS_CHARGE = 0.198f; // 0.20 means >= 3.4V, 0.19 means >= 2.4V. /* mbed Microcontroller Library * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mbed.h" #include "ble/BLE.h" #include "KeyboardService.h" #include "examples_common.h" #undef HID_DEBUG #undef LOG #ifdef LOG_MESSAGES #define LOG(...) { pc.printf(__VA_ARGS__); } Serial pc(UART_TX, UART_RX); #else #define LOG(...) #endif #define HID_DEBUG LOG AnalogIn battery(BATTERY_PIN); // For measuring the battery level AnalogIn finger1(FINGER1_PIN); // For measuring a finger level AnalogIn finger2(FINGER2_PIN); // AnalogIn finger3(FINGER3_PIN); // AnalogIn finger4(FINGER4_PIN); // DigitalOut waiting_led(LED_RED); // For showing BLE is trying to connect DigitalOut connected_led(LED_GREEN); // For showing BLE is connected DigitalOut status_led(LED_BLUE); // For showing BLE is busy DigitalOut enable_out3v3(OUT3V3_PIN); // For using OUT_3V3 pin of TinyBLE #define LED_ON 0 // LEDs on Tiny BLE need 0V to light up. #define LED_OFF 1 // bool haveAskedForRecharge = false; // Only ask for a recharge once per poweron. Requires user to turn device off once per day! InterruptIn button1(BUTTON_PIN); // For sending a BLE command BLE ble; KeyboardService *kbdServicePtr; static const char DEVICE_NAME[] = "ShervFingerControl"; static const char SHORT_DEVICE_NAME[] = "ShFingerCtl"; // Status flags that are updated in ISR callback functions. volatile bool check_fingers = false; volatile bool press_keyup[5] = {false}; static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) { HID_DEBUG("discon\n\r"); waiting_led = LED_ON; connected_led = LED_OFF; ble.gap().startAdvertising(); // restart advertising } static void onConnect(const Gap::ConnectionCallbackParams_t *params) { HID_DEBUG("conn\n\r"); waiting_led = LED_OFF; connected_led = LED_ON; } void send_keypress(int keycode, int finger) { if (!kbdServicePtr) return; if (!kbdServicePtr->isConnected()) { HID_DEBUG("no conn yet\n\r"); } else { //LOG("send_keypress(%d, %d)\n\r", keycode, finger); kbdServicePtr->keyDownCode(keycode, 0); press_keyup[finger] = true; } } void send_string(const char * c) { if (!kbdServicePtr) return; if (!kbdServicePtr->isConnected()) { HID_DEBUG("no conn yet\n\r"); } else { int len = strlen(c); kbdServicePtr->printf(c); HID_DEBUG("sending %d chars\n\r", len); } } // Call the button1_ISR function whenever the pushbutton is pressed. // Make sure there isn't anything slow like printf in this ISR! void button1_ISR() { send_string("x\n"); } // ISR callback function that is automatically called every 0.1 seconds or so. // Make sure there isn't anything slow like printf in this ISR! static void heartbeat_ISR() { if (!kbdServicePtr->isConnected()) waiting_led = !waiting_led; else { //connected_led = !connected_led; //waiting_led = 0; } // Signal that we should check the finger sensors soon. check_fingers = true; } int main() { #ifdef LOG_MESSAGES pc.baud(115200); // Use fast UART for log messages instead of default 9600 bps. #endif waiting_led = LED_OFF; // Set LEDs to blue, until ready. connected_led = LED_OFF; status_led = LED_ON; wait(1); LOG("---- Shervin's Finger Input device, using TinyBLE + BLE HID keyboard service ----\n\r"); // Call the button1_ISR function whenever the pushbutton is pressed. button1.rise(button1_ISR); HID_DEBUG("initialising ticker\n\r"); // Call the heartbeat_ISR function every 0.1 seconds Ticker heartbeat; heartbeat.attach(heartbeat_ISR, 0.1f); HID_DEBUG("enabling finger sensors\n\r"); enable_out3v3 = 1; HID_DEBUG("initialising ble\n\r"); ble.init(); ble.gap().onDisconnection(onDisconnect); ble.gap().onConnection(onConnect); initializeSecurity(ble); HID_DEBUG("adding hid service\n\r"); KeyboardService kbdService(ble); kbdServicePtr = &kbdService; HID_DEBUG("adding device info and battery service\n\r"); initializeHOGP(ble); HID_DEBUG("setting up gap\n\r"); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (uint8_t *)SHORT_DEVICE_NAME, sizeof(SHORT_DEVICE_NAME)); HID_DEBUG("advertising\n\r"); ble.gap().startAdvertising(); // Turn LEDs to green connected. connected_led = LED_ON; waiting_led = LED_OFF; status_led = LED_OFF; // Run forever ... int counter1 = 0; int counter2 = 0; while (true) { ble.waitForEvent(); // Signal that we should check the finger sensors soon. if (check_fingers) { check_fingers = false; // Measure all the finger sensors and battery level. float fing1 = finger1.read(); float fing2 = finger2.read(); float fing3 = finger3.read(); float fing4 = finger4.read(); float batt = battery.read(); // If a finger was pressed and now has been released, send the keyUp event soon after. if (press_keyup[1] && fing1 < FINGER1_RELEASE) { press_keyup[1] = false; // Clear the flag kbdServicePtr->keyUpCode(); HID_DEBUG("sent key up for 1.\n\r"); } if (press_keyup[2] && fing2 < FINGER2_RELEASE) { press_keyup[2] = false; // Clear the flag send_keypress(FING2UP_KEYCODE, 2); HID_DEBUG("sent final key down for 2.\n\r"); ble.waitForEvent(); // Add a slight delay //wait(0.1); // Add a slight delay //ble.waitForEvent(); // Add a slight delay kbdServicePtr->keyUpCode(); HID_DEBUG("sent final key up for 2.\n\r"); press_keyup[2] = false; // Clear the flag } if (press_keyup[3] && fing3 < FINGER3_RELEASE) { press_keyup[3] = false; // Clear the flag kbdServicePtr->keyUpCode(); HID_DEBUG("sent key up for 3.\n\r"); } if (press_keyup[4] && fing4 < FINGER4_RELEASE) { press_keyup[4] = false; // Clear the flag kbdServicePtr->keyUpCode(); HID_DEBUG("sent key up for 4.\n\r"); } // Very occasionally, show the connected LED counter1++; if (counter1 == 1) { if (kbdServicePtr->isConnected()) { connected_led = LED_ON; waiting_led = LED_OFF; } } if (counter1 == 2) { //counter2 = 0; connected_led = LED_OFF; waiting_led = LED_OFF; } // Occasionally show some debug info if (counter1 > 15) { //counter2++; LOG("%.2f%. F1=%.2f, F2=%.2f, F3=%.2f, F4=%.2f\n\r", batt, fing1, fing2, fing3, fing4); // Check for low battery if (batt < BATTERY_NEEDS_CHARGE) { // Toggle blue LED waiting_led = LED_OFF; connected_led = LED_OFF; status_led = !status_led; if (!haveAskedForRecharge) { send_string("RECHARGE BATTERY!"); haveAskedForRecharge = true; } LOG("RECHARGE BATTERY!\n\r"); } //else { //} counter1 = 0; } // Check if a finger was pressed if (fing1 > FINGER1_PRESS && !press_keyup[1]) { send_keypress(FING1_KEYCODE, 1); HID_DEBUG("sent keypress %d for 1.\n\r", FING1_KEYCODE); //counter2+=20; //LOG("%d\n\r", counter2); } if (fing2 > FINGER2_PRESS && !press_keyup[2]) { send_keypress(FING2DOWN_KEYCODE, 2); HID_DEBUG("sent keypress %d for 2.\n\r", FING2DOWN_KEYCODE); // Finger 2 is treated differently. We want to be able to hold down finger 2 // Without causing repeated keystrokes, so we will send an up press straight after the down press, // and a different key down and up when the fingure is released, so the client software can // figure out the duration that it was held for. ble.waitForEvent(); // Add a slight delay //wait(0.1); // Add a slight delay //ble.waitForEvent(); // Add a slight delay kbdServicePtr->keyUpCode(); HID_DEBUG("sent initial key up for 2.\n\r"); } if (fing3 > FINGER3_PRESS && !press_keyup[3]) { send_keypress(FING3_KEYCODE, 3); HID_DEBUG("sent keypress %d for 3.\n\r", FING3_KEYCODE); //counter2++; //send_keypress(counter2, 3); //LOG("%d\n\r", counter2); } if (fing4 > FINGER4_PRESS && !press_keyup[4]) { send_keypress(FING4_KEYCODE, 4); HID_DEBUG("sent keypress %d for 4.\n\r", FING4_KEYCODE); //counter2--; //send_keypress(counter2, 4); //LOG("%d\n\r", counter2); } } } }