Bluetooth BLE HID Mouse 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:2749e6ff50ce
- Parent:
- 0:3435302551b3
- Child:
- 2:0218f7d82bfa
File content as of revision 1:2749e6ff50ce:
/** * Handheld BLE finger input device based on Seeed Studio Tiny BLE (TinyBLE). * By Shervin Emami (http://shervinemami.info/), 25th Aug 2018. * If the analog voltage on a "finger" pin is below a threshold voltage, * then it sends a mouse command using Bluetooth BLE, 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 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 "MouseService.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; MouseService *mouseServicePtr; static const char DEVICE_NAME[] = "ShervMouse"; static const char SHORT_DEVICE_NAME[] = "ShMouse"; // 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; } // 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 (!mouseServicePtr->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 Mouse Input device, using TinyBLE + BLE HID mouse 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 mouse service\n\r"); MouseService mouseService(ble); mouseServicePtr = &mouseService; HID_DEBUG("adding device info and battery service\n\r"); initializeHOGP(ble); HID_DEBUG("setting up gap\n\r"); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MOUSE); 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 ... LOG("Ready to run forever ...\n\r"); 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 (mouseServicePtr->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"); //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"); // 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"); //counter2++; //send_keypress(counter2, 3); //LOG("%d\n\r", counter2); LOG("send scroll down\n\r"); mouseServicePtr->setSpeed(0, 0, 1); wait(0.05); // Add a slight delay ble.waitForEvent(); // Add a slight delay mouseServicePtr->setSpeed(0, 0, 0); } if (fing4 > FINGER4_PRESS && !press_keyup[4]) { //send_keypress(FING4_KEYCODE, 4); HID_DEBUG("sent keypress %d for 4.\n\r"); //counter2--; //send_keypress(counter2, 4); //LOG("%d\n\r", counter2); } } } }