ble nano hid over gatt

Dependencies:   BLE_API mbed-dev nRF51822

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* BLE Keyboard Implementation for mbed
00002  * Copyright (c) 2016 cho45 <cho45@lowreal.net>
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include <cmath>
00018 #include "mbed.h"
00019 
00020 #include "HIDController_BLE.h"
00021 #include "KeyboardMatrixController.h"
00022 #include "BatteryLevel.h"
00023 #include "WatchDog.h"
00024 
00025 #include "keymap.h"
00026 #include "config.h"
00027 
00028 RawSerial serial(USBTX, USBRX);
00029 
00030 static I2C i2c(I2C_SDA0, I2C_SCL0);
00031 static KeyboardMatrixController keyboardMatrixController(i2c);
00032 static Keymap keymap;
00033 
00034 // Interrupt from MCP23017
00035 // (pulled-up and two MCP23017 is configured with open drain INT)
00036 static InterruptIn keyboardInterruptIn(P0_5);
00037 
00038 #define PIN_STATUS_LED P0_4
00039 static DigitalOut statusLed(PIN_STATUS_LED, 0);
00040 
00041 // timeout for status led and wakeup from sleep
00042 static Timeout timeout;
00043 
00044 // ROWS=8
00045 // COLS=16
00046 // 列ごとに1バイトにパックしてキーの状態を保持する
00047 static uint8_t keysA[COLS];
00048 static uint8_t keysB[COLS];
00049 static bool state = 0;
00050 #define is_pressed(keys, row, col) (!!(keys[col] & (1<<row)))
00051 
00052 // delay for interrupt
00053 static volatile int8_t pollCount = 50;
00054 
00055 static void keyboardInterrupt() {
00056     // just for wakeup
00057     pollCount = 25;
00058 }
00059 
00060 static void powerOff() {
00061     DEBUG_PRINTF("power off\r\n");
00062     NRF_POWER->SYSTEMOFF = 1;
00063 }
00064 
00065 static bool updateStatudLedEnabled = false;
00066 static void updateStatusLed() {
00067     switch (HIDController::status()) {
00068         case TIMEOUT:
00069         case DISCONNECTED:
00070         case CONNECTED:
00071             statusLed = 0;
00072             updateStatudLedEnabled = false;
00073             return;
00074         case ADVERTISING:
00075         case CONNECTING:
00076             statusLed = !statusLed;
00077             updateStatudLedEnabled = true;
00078             timeout.attach(updateStatusLed, statusLed ? 0.1 : 0.5);
00079             break;
00080     }
00081 }
00082 
00083 static volatile bool keyIntervalInterrupt = false;
00084 static void wakeupKeyIntervalSleep() {
00085     keyIntervalInterrupt = true;
00086 }
00087 
00088 static volatile bool shouldUpdateBatteryLevel = true;
00089 static void wakeupIgnoreLongSleep() {
00090     // just wakeup for reloading WDT
00091     shouldUpdateBatteryLevel = true;
00092 }
00093 
00094 void setupIO () {
00095     // mbed's Serial of TARGET_RBLAB_BLENANO sucks
00096     // DO NOT CONNECT RTS/CTS WITHOUT PRIOR CONSENT!
00097     NRF_UART0->PSELRTS = 0xFFFFFFFFUL;
00098     NRF_UART0->PSELCTS = 0xFFFFFFFFUL;
00099 
00100     // Set LED Pin as HIGH Current mode
00101     NRF_GPIO->PIN_CNF[PIN_STATUS_LED] =
00102         (NRF_GPIO->PIN_CNF[PIN_STATUS_LED] & ~GPIO_PIN_CNF_DRIVE_Msk) |
00103         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos);
00104 
00105     // Unsed pins.
00106     // Set as PullUp for power consumption
00107     // Use GPIO registers directly for saving ram
00108     // Pad pinouts
00109     /* 
00110     DigitalIn unused_p0_7(P0_7, PullUp);
00111     DigitalIn unused_p0_6(P0_6, PullUp);
00112     DigitalIn unused_p0_15(P0_15, PullUp);
00113     DigitalIn unused_p0_29(P0_29, PullUp);
00114     DigitalIn unused_p0_28(P0_28, PullUp);
00115     */
00116     NRF_GPIO->PIN_CNF[P0_7] =
00117         (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
00118         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
00119         (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
00120         (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
00121         (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
00122     NRF_GPIO->PIN_CNF[P0_6] =
00123         (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
00124         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
00125         (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
00126         (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
00127         (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
00128     NRF_GPIO->PIN_CNF[P0_15] =
00129         (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
00130         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
00131         (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
00132         (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
00133         (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
00134     NRF_GPIO->PIN_CNF[P0_29] =
00135         (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
00136         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
00137         (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
00138         (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
00139         (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
00140     NRF_GPIO->PIN_CNF[P0_28] =
00141         (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
00142         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
00143         (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
00144         (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
00145         (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
00146     /*
00147     DigitalIn unused_p0_19(P0_19, PullUp); // This is on board LED which connected to VDD
00148     DigitalIn unused_p0_11(P0_11, PullUp); // RXD
00149     */
00150     NRF_GPIO->PIN_CNF[P0_19] = // This is on board LED which connected to VDD
00151         (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
00152         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
00153         (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
00154         (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
00155         (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
00156     NRF_GPIO->PIN_CNF[P0_11] = // RXD
00157         (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
00158         (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
00159         (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
00160         (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
00161         (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
00162 
00163 }
00164 
00165 int main(void) {
00166     {
00167         const uint32_t reason = NRF_POWER->RESETREAS;
00168         NRF_POWER->RESETREAS = 0xffffffff; // clear reason
00169         // reset cause should be shown everytime
00170         serial.printf("init [%x] sp:%x\r\n", reason, GET_SP());
00171     }
00172 
00173     float batteryVoltage = BatteryLevel::readBatteryVoltage();
00174     if (batteryVoltage < BatteryLevel::BATTERY_LOW) {
00175         powerOff();
00176     }
00177 
00178     // Enable Pin-reset on DEBUG mode
00179     // This makes possiable booting without normal mode easily.
00180     NRF_POWER->RESET = 1;
00181     // Disable Internal DC/DC step down converter surely
00182     NRF_POWER->DCDCEN = 0;
00183 
00184     setupIO();
00185 
00186     WatchDog::init();
00187 
00188     // only 100kHz/250khz/400khz
00189     i2c.frequency(250000);
00190 
00191     keyboardInterruptIn.mode(PullUp);
00192     keyboardInterruptIn.fall(keyboardInterrupt);
00193 
00194     keyboardMatrixController.init();
00195     pollCount = 10;
00196 
00197     HIDController::init();
00198 
00199     // STOP UART RX for power consumption
00200     NRF_UART0->TASKS_STOPRX = 1;
00201 
00202     // Disable TWI by default.
00203     NRF_TWI0->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
00204 
00205     {
00206         // init battery level
00207         const uint8_t batteryPercentage = BatteryLevel::readBatteryPercentage(batteryVoltage);
00208         HIDController::updateBatteryLevel(batteryPercentage, batteryVoltage * 1000);
00209     }
00210 
00211     while (1) {
00212         if (pollCount > 0) {
00213             DEBUG_PRINTF("scan keys\r\n");
00214 
00215             while (pollCount-- > 0) {
00216                 uint8_t (&keysCurr)[COLS] = state ? keysA : keysB;
00217                 uint8_t (&keysPrev)[COLS] = state ? keysB : keysA;
00218 
00219                 NRF_TWI0->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos;
00220                 keyboardMatrixController.scanKeyboard(keysCurr);
00221                 NRF_TWI0->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
00222 
00223                 bool queue = false;
00224 
00225                 for (int col = 0; col < COLS; col++) {
00226                     const uint8_t changed = keysPrev[col] ^ keysCurr[col];
00227                     if (changed) queue = true;
00228                     for (int row = 0; row < ROWS; row++) {
00229                         if (changed & (1<<row)) {
00230                             bool pressed = keysCurr[col] & (1<<row);
00231                             // DEBUG_KEYEVENT("changed: col=%d, row=%d / pressed=%d\r\n", col, row, pressed);
00232                             keymap.execute(row, col, pressed);
00233                         }
00234                     }
00235                 }
00236                 state = !state;
00237 
00238                 if (HIDController::status() == DISCONNECTED ||
00239                     HIDController::status() == TIMEOUT) {
00240 
00241                     if (
00242                         is_pressed(keysCurr, 0, 0) && // left top 1
00243                         is_pressed(keysCurr, 1, 0) && // left top 2
00244                         is_pressed(keysCurr, 0, 15)   // right top
00245                     ) {
00246                         DEBUG_PRINTF("re-init connection\r\n");
00247                         HIDController::initializeConnection(true);
00248                     } else {
00249                         if (HIDController::status() == TIMEOUT) {
00250                             DEBUG_PRINTF("wakeup\r\n");
00251                             HIDController::initializeConnection(false);
00252                         }
00253                     }
00254                 }
00255 
00256                 if (queue) HIDController::queueCurrentReportData();
00257 
00258                 // use timer to use wait 5ms
00259                 timeout.detach();
00260                 keyIntervalInterrupt = false;
00261                 timeout.attach_us(wakeupKeyIntervalSleep, 5000);
00262                 while (!keyIntervalInterrupt) HIDController::waitForEvent();
00263             }
00264         } else {
00265             if (!updateStatudLedEnabled) updateStatusLed();
00266 
00267             if (shouldUpdateBatteryLevel) {
00268                 shouldUpdateBatteryLevel = false;
00269                 float batteryVoltage = BatteryLevel::readBatteryVoltage();
00270                 const uint8_t batteryPercentage = BatteryLevel::readBatteryPercentage(batteryVoltage);
00271                 const bool isLowBattery = batteryVoltage < BatteryLevel::BATTERY_LOW;
00272 
00273                 HIDController::updateBatteryLevel(batteryPercentage, batteryVoltage * 1000);
00274 
00275                 if (isLowBattery) {
00276                     DEBUG_PRINTF("LOWBAT %f %d%%", batteryVoltage, batteryPercentage);
00277                     powerOff();
00278                 }
00279             }
00280 
00281             if (HIDController::status() == DISCONNECTED) {
00282                 HIDController::initializeConnection(false);
00283             }
00284 
00285             DEBUG_PRINTF("WFE [%d:%s]\r\n",
00286                 HIDController::status(),
00287                 HIDController::statusString()
00288             );
00289 
00290             if (!updateStatudLedEnabled) {
00291                 timeout.detach();
00292                 timeout.attach(wakeupIgnoreLongSleep, 60);
00293             }
00294             HIDController::waitForEvent();
00295         }
00296     }
00297 }