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
Diff: main.cpp
- Revision:
- 1:2749e6ff50ce
- Parent:
- 0:3435302551b3
- Child:
- 2:0218f7d82bfa
--- a/main.cpp Sat Feb 27 05:08:02 2016 +0000 +++ b/main.cpp Sun Aug 26 10:51:20 2018 +0000 @@ -1,3 +1,46 @@ +/** + * 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 * @@ -17,82 +60,119 @@ #include "mbed.h" #include "ble/BLE.h" -#include "KeyboardService.h" +//#include "KeyboardService.h" +#include "MouseService.h" + #include "examples_common.h" - -/** - * 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. - */ - -DigitalOut waiting_led(LED1); -DigitalOut connected_led(LED2); - -InterruptIn button1(D2); + + + +#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; +//KeyboardService *kbdServicePtr; +MouseService *mouseServicePtr; -static const char DEVICE_NAME[] = "uKbd"; -static const char SHORT_DEVICE_NAME[] = "kbd1"; - +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("disconnected\r\n"); - connected_led = 0; + 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("connected\r\n"); - waiting_led = false; + HID_DEBUG("conn\n\r"); + waiting_led = LED_OFF; + connected_led = LED_ON; } - -static void waiting() { - if (!kbdServicePtr->isConnected()) + + + + + +// 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; + else { + //connected_led = !connected_led; + //waiting_led = 0; + } + + // Signal that we should check the finger sensors soon. + check_fingers = true; } -void send_string(const char * c) { - if (!kbdServicePtr) - return; - - if (!kbdServicePtr->isConnected()) { - HID_DEBUG("we haven't connected yet..."); - } else { - int len = strlen(c); - kbdServicePtr->printf(c); - HID_DEBUG("sending %d chars\r\n", len); - } -} - -void send_stuff() { - send_string("hello world!\n"); -} int main() { - Ticker heartbeat; - - button1.rise(send_stuff); +#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\r\n"); + 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; - heartbeat.attach(waiting, 1); - - HID_DEBUG("initialising ble\r\n"); + HID_DEBUG("initialising ble\n\r"); ble.init(); ble.gap().onDisconnection(onDisconnect); @@ -100,24 +180,156 @@ initializeSecurity(ble); - HID_DEBUG("adding hid service\r\n"); - KeyboardService kbdService(ble); - kbdServicePtr = &kbdService; + HID_DEBUG("adding hid mouse service\n\r"); + MouseService mouseService(ble); + mouseServicePtr = &mouseService; - HID_DEBUG("adding device info and battery service\r\n"); + HID_DEBUG("adding device info and battery service\n\r"); initializeHOGP(ble); - HID_DEBUG("setting up gap\r\n"); - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD); + 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\r\n"); + 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); + + } + } } -} +} \ No newline at end of file