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 Yuuichi Akagawa

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /**
00002  * Handheld BLE finger input device based on Seeed Studio Tiny BLE (TinyBLE).
00003  * By Shervin Emami (http://shervinemami.info/), Apr 2018.
00004  * If the analog voltage on a "finger" pin is below a threshold voltage,
00005  * then it sends a rare keypress using Bluetooth, so that any computer or
00006  * smartphone can be configured to perform a desired function due to this input event.
00007  *
00008  * 
00009  * This program implements a complete HID-over-Gatt Profile:
00010  *  - HID is provided by KeyboardService
00011  *  - Battery Service
00012  *  - Device Information Service
00013  *
00014  * Complete strings can be sent over BLE using printf. Please note, however, than a 12char string
00015  * will take about 500ms to transmit, principally because of the limited notification rate in BLE.
00016  * KeyboardService uses a circular buffer to store the strings to send, and calls to putc will fail
00017  * once this buffer is full. This will result in partial strings being sent to the client.
00018  
00019  * Tested with mbed libraries for April 2nd, 2018. If you use a newer version of mbed or other
00020  * libs and you have problems, try rolling back to April 2nd, 2018.
00021  */ 
00022 
00023 // Configure the settings of my board.
00024 #include "tiny_ble.h"   // Seeed Studio Tiny BLE
00025 
00026 
00027 // Define LOG_MESSAGES if you want various debug messages to be sent to the PC via a UART cable.
00028 #define LOG_MESSAGES
00029 
00030 // Various USB Keyboard keycodes that we can send.
00031 #define KEYCODE_F4  131
00032 #define KEYCODE_F5  132
00033 #define KEYCODE_F6  133
00034 #define KEYCODE_F7  134
00035 #define KEYCODE_F8  135
00036 #define KEYCODE_F9  136
00037 #define KEYCODE_F10 137
00038 #define KEYCODE_F11 138
00039 #define KEYCODE_F12 139
00040 #define KEYCODE_HOME     145
00041 #define KEYCODE_PAGEUP   146
00042 #define KEYCODE_PAGEDOWN 147
00043 #define KEYCODE_RIGHT    148
00044 #define KEYCODE_LEFT     149
00045 #define KEYCODE_DOWN     150
00046 #define KEYCODE_UP       151
00047 #define KEYCODE_ESCAPE   157
00048 #define KEYCODE_RIGHTCLK 188
00049 #define KEYCODE_ACCENT    96
00050 #define KEYCODE_NUMLOCK  193
00051 
00052 
00053 
00054 
00055 const int FING1_KEYCODE = KEYCODE_F8;         // Set the keypress that will be sent over bluetooth.
00056 const int FING2DOWN_KEYCODE = KEYCODE_F4;         //
00057 const int FING2UP_KEYCODE = KEYCODE_NUMLOCK;
00058 const int FING3_KEYCODE = KEYCODE_UP;         //
00059 const int FING4_KEYCODE = KEYCODE_DOWN;         //
00060 
00061 const float FINGER1_PRESS = 0.42f;   // Set how much the person needs to press their finger in to trigger a keypress.
00062 const float FINGER2_PRESS = 0.41f;   // Note that each finger is slightly different strength, due to the mechanical design.
00063 const float FINGER3_PRESS = 0.38f;   //
00064 const float FINGER4_PRESS = 0.37f;   //
00065 
00066 const float FINGER1_RELEASE = 0.35f;   // Set how much the person needs to release their finger to finish a keypress.
00067 const float FINGER2_RELEASE = 0.35f;   // Note that each finger is slightly different strength, due to the mechanical design.
00068 const float FINGER3_RELEASE = 0.34f;   //
00069 const float FINGER4_RELEASE = 0.34f;   //
00070 
00071 const float BATTERY_NEEDS_CHARGE = 0.198f;  // 0.20 means >= 3.4V, 0.19 means >= 2.4V.
00072 
00073  
00074 /* mbed Microcontroller Library
00075  * Copyright (c) 2015 ARM Limited
00076  *
00077  * Licensed under the Apache License, Version 2.0 (the "License");
00078  * you may not use this file except in compliance with the License.
00079  * You may obtain a copy of the License at
00080  *
00081  *     http://www.apache.org/licenses/LICENSE-2.0
00082  *
00083  * Unless required by applicable law or agreed to in writing, software
00084  * distributed under the License is distributed on an "AS IS" BASIS,
00085  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00086  * See the License for the specific language governing permissions and
00087  * limitations under the License.
00088  */
00089  
00090 #include "mbed.h"
00091  
00092 #include "ble/BLE.h"
00093 #include "KeyboardService.h"
00094  
00095 #include "examples_common.h"
00096 
00097 
00098 
00099 #undef HID_DEBUG
00100 #undef LOG
00101 #ifdef LOG_MESSAGES
00102     #define LOG(...)    { pc.printf(__VA_ARGS__); }
00103     Serial pc(UART_TX, UART_RX);
00104 #else
00105     #define LOG(...)
00106 #endif
00107 #define HID_DEBUG   LOG
00108 
00109 
00110 AnalogIn    battery(BATTERY_PIN);   // For measuring the battery level
00111 AnalogIn    finger1(FINGER1_PIN);   // For measuring a finger level
00112 AnalogIn    finger2(FINGER2_PIN);   //
00113 AnalogIn    finger3(FINGER3_PIN);   //
00114 AnalogIn    finger4(FINGER4_PIN);   //
00115 DigitalOut waiting_led(LED_RED);      // For showing BLE is trying to connect
00116 DigitalOut connected_led(LED_GREEN);  // For showing BLE is connected
00117 DigitalOut status_led(LED_BLUE);      // For showing BLE is busy
00118 DigitalOut enable_out3v3(OUT3V3_PIN); // For using OUT_3V3 pin of TinyBLE
00119 
00120 #define LED_ON   0      // LEDs on Tiny BLE need 0V to light up.
00121 #define LED_OFF  1      //
00122 
00123 
00124 bool haveAskedForRecharge = false;  // Only ask for a recharge once per poweron. Requires user to turn device off once per day!
00125 
00126 
00127 InterruptIn button1(BUTTON_PIN);    // For sending a BLE command
00128  
00129 BLE ble;
00130 KeyboardService *kbdServicePtr;
00131  
00132 static const char DEVICE_NAME[] = "ShervFingerControl";
00133 static const char SHORT_DEVICE_NAME[] = "ShFingerCtl";
00134 
00135 
00136 // Status flags that are updated in ISR callback functions.
00137 volatile bool check_fingers = false;
00138 volatile bool press_keyup[5] = {false};
00139 
00140 
00141 
00142 static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params)
00143 {
00144     HID_DEBUG("discon\n\r");
00145     waiting_led = LED_ON;
00146     connected_led = LED_OFF;
00147  
00148     ble.gap().startAdvertising(); // restart advertising
00149 }
00150  
00151 static void onConnect(const Gap::ConnectionCallbackParams_t *params)
00152 {
00153     HID_DEBUG("conn\n\r");
00154     waiting_led = LED_OFF;
00155     connected_led = LED_ON;
00156 }
00157 
00158 
00159 
00160 void send_keypress(int keycode, int finger) {
00161     if (!kbdServicePtr)
00162         return;
00163  
00164     if (!kbdServicePtr->isConnected()) {
00165         HID_DEBUG("no conn yet\n\r");
00166     } else {
00167         //LOG("send_keypress(%d, %d)\n\r", keycode, finger);
00168         kbdServicePtr->keyDownCode(keycode, 0);
00169         press_keyup[finger] = true;
00170         
00171     }
00172 }
00173 
00174 void send_string(const char * c) {
00175     if (!kbdServicePtr)
00176         return;
00177  
00178     if (!kbdServicePtr->isConnected()) {
00179         HID_DEBUG("no conn yet\n\r");
00180     } else {
00181         int len = strlen(c);
00182         kbdServicePtr->printf(c);
00183         HID_DEBUG("sending %d chars\n\r", len);
00184     }
00185 }
00186 
00187 
00188 // Call the button1_ISR function whenever the pushbutton is pressed.
00189 // Make sure there isn't anything slow like printf in this ISR!
00190 void button1_ISR() {
00191     send_string("x\n");
00192 }
00193 
00194 
00195 // ISR callback function that is automatically called every 0.1 seconds or so.
00196 // Make sure there isn't anything slow like printf in this ISR!
00197 static void heartbeat_ISR() {
00198     if (!kbdServicePtr->isConnected())
00199         waiting_led = !waiting_led;
00200     else {
00201         //connected_led = !connected_led;
00202         //waiting_led = 0;
00203     }
00204 
00205     // Signal that we should check the finger sensors soon.
00206     check_fingers = true;
00207 }
00208  
00209  
00210 int main()
00211 {
00212 #ifdef LOG_MESSAGES
00213     pc.baud(115200);  // Use fast UART for log messages instead of default 9600 bps.
00214 #endif
00215     
00216     waiting_led = LED_OFF;    // Set LEDs to blue, until ready.
00217     connected_led = LED_OFF;
00218     status_led = LED_ON;
00219     
00220     wait(1);    
00221     LOG("---- Shervin's Finger Input device, using TinyBLE + BLE HID keyboard service ----\n\r");
00222 
00223     // Call the button1_ISR function whenever the pushbutton is pressed.
00224     button1.rise(button1_ISR);
00225  
00226     HID_DEBUG("initialising ticker\n\r");
00227 
00228     // Call the heartbeat_ISR function every 0.1 seconds 
00229     Ticker heartbeat;
00230     heartbeat.attach(heartbeat_ISR, 0.1f);
00231 
00232     HID_DEBUG("enabling finger sensors\n\r");
00233     enable_out3v3 = 1;
00234  
00235     HID_DEBUG("initialising ble\n\r");
00236     ble.init();
00237  
00238     ble.gap().onDisconnection(onDisconnect);
00239     ble.gap().onConnection(onConnect);
00240  
00241     initializeSecurity(ble);
00242  
00243     HID_DEBUG("adding hid service\n\r");
00244     KeyboardService kbdService(ble);
00245     kbdServicePtr = &kbdService;
00246  
00247     HID_DEBUG("adding device info and battery service\n\r");
00248     initializeHOGP(ble);
00249  
00250     HID_DEBUG("setting up gap\n\r");
00251     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
00252     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME,
00253                                            (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
00254     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
00255                                            (uint8_t *)SHORT_DEVICE_NAME, sizeof(SHORT_DEVICE_NAME));
00256  
00257     HID_DEBUG("advertising\n\r");
00258     ble.gap().startAdvertising();
00259 
00260     // Turn LEDs to green connected.
00261     connected_led = LED_ON;
00262     waiting_led = LED_OFF;
00263     status_led = LED_OFF;
00264 
00265     // Run forever ...
00266     int counter1 = 0; 
00267     int counter2 = 0; 
00268     while (true) {
00269         ble.waitForEvent();
00270         
00271         // Signal that we should check the finger sensors soon.
00272         if (check_fingers) {
00273             check_fingers = false;
00274 
00275             // Measure all the finger sensors and battery level.
00276             float fing1 = finger1.read();
00277             float fing2 = finger2.read();
00278             float fing3 = finger3.read();
00279             float fing4 = finger4.read();
00280             float batt = battery.read();
00281             
00282             // If a finger was pressed and now has been released, send the keyUp event soon after.
00283             if (press_keyup[1] && fing1 < FINGER1_RELEASE) {
00284                 press_keyup[1] = false;    // Clear the flag
00285                 kbdServicePtr->keyUpCode();
00286                 HID_DEBUG("sent key up for 1.\n\r");
00287             }
00288             if (press_keyup[2] && fing2 < FINGER2_RELEASE) {
00289                 press_keyup[2] = false;    // Clear the flag
00290                 send_keypress(FING2UP_KEYCODE, 2);
00291                 HID_DEBUG("sent final key down for 2.\n\r");
00292                 ble.waitForEvent(); // Add a slight delay
00293                 //wait(0.1);          // Add a slight delay
00294                 //ble.waitForEvent(); // Add a slight delay
00295                 kbdServicePtr->keyUpCode();
00296                 HID_DEBUG("sent final key up for 2.\n\r");
00297                 press_keyup[2] = false;    // Clear the flag
00298             }
00299             if (press_keyup[3] && fing3 < FINGER3_RELEASE) {
00300                 press_keyup[3] = false;    // Clear the flag
00301                 kbdServicePtr->keyUpCode();
00302                 HID_DEBUG("sent key up for 3.\n\r");
00303             }
00304             if (press_keyup[4] && fing4 < FINGER4_RELEASE) {
00305                 press_keyup[4] = false;    // Clear the flag
00306                 kbdServicePtr->keyUpCode();
00307                 HID_DEBUG("sent key up for 4.\n\r");
00308             }
00309             
00310 
00311             // Very occasionally, show the connected LED
00312             counter1++;
00313             if (counter1 == 1) {
00314                 if (kbdServicePtr->isConnected()) {
00315                     connected_led = LED_ON;
00316                     waiting_led = LED_OFF;
00317                 }
00318             }
00319             if (counter1 == 2) {
00320                 //counter2 = 0;
00321                 connected_led = LED_OFF;
00322                 waiting_led = LED_OFF;
00323             }
00324             
00325 
00326             // Occasionally show some debug info
00327             if (counter1 > 15) {
00328                 //counter2++;
00329                 LOG("%.2f%. F1=%.2f, F2=%.2f, F3=%.2f, F4=%.2f\n\r", batt, fing1, fing2, fing3, fing4);
00330 
00331                 // Check for low battery
00332                 if (batt < BATTERY_NEEDS_CHARGE) {
00333                     // Toggle blue LED
00334                     waiting_led = LED_OFF;
00335                     connected_led = LED_OFF;
00336                     status_led = !status_led;
00337                     if (!haveAskedForRecharge) {
00338                         send_string("RECHARGE BATTERY!");
00339                         haveAskedForRecharge = true;
00340                     }
00341                     LOG("RECHARGE BATTERY!\n\r");
00342                 }
00343                 //else {
00344                 //}
00345                 counter1 = 0;
00346             }
00347             
00348             // Check if a finger was pressed
00349             if (fing1 > FINGER1_PRESS && !press_keyup[1]) {
00350                 send_keypress(FING1_KEYCODE, 1);
00351                 HID_DEBUG("sent keypress %d for 1.\n\r", FING1_KEYCODE);
00352                 //counter2+=20;
00353                 //LOG("%d\n\r", counter2);
00354             }
00355             if (fing2 > FINGER2_PRESS && !press_keyup[2]) {
00356                 send_keypress(FING2DOWN_KEYCODE, 2);
00357                 HID_DEBUG("sent keypress %d for 2.\n\r", FING2DOWN_KEYCODE);
00358                 // Finger 2 is treated differently. We want to be able to hold down finger 2
00359                 // Without causing repeated keystrokes, so we will send an up press straight after the down press, 
00360                 // and a different key down and up when the fingure is released, so the client software can
00361                 // figure out the duration that it was held for.
00362                 ble.waitForEvent(); // Add a slight delay
00363                 //wait(0.1);          // Add a slight delay
00364                 //ble.waitForEvent(); // Add a slight delay
00365                 kbdServicePtr->keyUpCode();
00366                 HID_DEBUG("sent initial key up for 2.\n\r");
00367             }
00368             if (fing3 > FINGER3_PRESS && !press_keyup[3]) {
00369                 send_keypress(FING3_KEYCODE, 3);               
00370                 HID_DEBUG("sent keypress %d for 3.\n\r", FING3_KEYCODE);
00371                 //counter2++;
00372                 //send_keypress(counter2, 3);
00373                 //LOG("%d\n\r", counter2);
00374             }
00375             if (fing4 > FINGER4_PRESS && !press_keyup[4]) {
00376                 send_keypress(FING4_KEYCODE, 4);
00377                 HID_DEBUG("sent keypress %d for 4.\n\r", FING4_KEYCODE);
00378                 //counter2--;
00379                 //send_keypress(counter2, 4);
00380                 //LOG("%d\n\r", counter2);
00381                 
00382             }
00383         }
00384     }
00385 }