├── .gitignore ├── BLE-Keyboard ├── keyboard_driver.c ├── keyboard_driver.h ├── keyboard_map.h └── main.c ├── LICENSE ├── NORDIC_LICENSE └── Readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | -------------------------------------------------------------------------------- /BLE-Keyboard/keyboard_driver.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved. 2 | * 3 | * The information contained herein is property of Nordic Semiconductor ASA. 4 | * Terms and conditions of usage are described in detail in NORDIC 5 | * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 6 | * 7 | * Licensees are granted free, non-transferable use of the information. NO 8 | * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 9 | * the file. 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #include "nrf.h" 17 | #include "nrf_gpio.h" 18 | #include "keyboard_map.h" 19 | #include "keyboard_driver.h" 20 | 21 | #define MODIFIER_HID_START 0xE0 22 | #define MODIFIER_HID_END 0xE7 23 | 24 | static uint8_t tmp_key_array[2][MAX_NUM_OF_PRESSED_KEYS]; //!< Array holding the keys that have already been transmitted and currently pressed. 25 | 26 | static uint8_t *transmitted_keys = tmp_key_array[0]; //!These pointers deside which part of the tmp_key_array 27 | static uint8_t *currently_pressed_keys = tmp_key_array[1]; //!saves the current keys and which saves the transmitted one 28 | static uint8_t transmitted_keys_num; //!< Number of keys in transmitted_keys 29 | static uint8_t currently_pressed_keys_num; //!< Number of keys in currently_pressed_keys 30 | 31 | static uint8_t key_packet[KEY_PACKET_SIZE]; //!< Stores last created key packet. One byte is used for modifier keys, one for OEMs. Key values are USB HID keycodes. 32 | //!See the HID Usage Tables for more detail. 33 | static uint32_t input_scan_vector; //!A trick to quickly detecte if any keys is pressed 34 | static const uint8_t *matrix_lookup; //!< Pointer to the key lookup matrix in use 35 | 36 | static bool have_keys_changed(void); 37 | static bool keymatrix_read(uint8_t *pressed_keys, uint8_t *number_of_pressed_keys); 38 | static void keypacket_addkey(uint8_t key); 39 | static void keypacket_create(uint8_t *key_packet, uint8_t key_packet_size); 40 | static void remap_fn_keys(uint8_t *keys, uint8_t number_of_keys); 41 | 42 | void sleep_mode_prepare(void) 43 | { 44 | for (uint_fast8_t i = KEYBOARD_NUM_OF_COLUMNS; i--;){ 45 | nrf_gpio_pin_clear((uint32_t)column_pin_array[i]); 46 | } 47 | nrf_gpio_pin_set((uint32_t)column_pin_array[wakeup_button_column_index]); 48 | nrf_gpio_cfg_sense_input(row_pin_array[wakeup_button_row_index], NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH); 49 | } 50 | 51 | bool keyboard_init(void) 52 | { 53 | input_scan_vector = 0; 54 | if (row_pin_array == 0 || column_pin_array == 0){ 55 | return false; //return if pins have not been define 56 | }else{ 57 | for (uint_fast8_t i = KEYBOARD_NUM_OF_COLUMNS; i--;){ 58 | nrf_gpio_cfg_output((uint32_t)column_pin_array[i]); 59 | NRF_GPIO->PIN_CNF[(uint32_t)column_pin_array[i]] |= 0x400; //Set pin to be "Disconnected 0 and standard 1" 60 | nrf_gpio_pin_clear((uint32_t)column_pin_array[i]); //Set pin to low 61 | } 62 | for (uint_fast8_t i = KEYBOARD_NUM_OF_ROWS; i--;){ 63 | nrf_gpio_cfg_input((uint32_t)row_pin_array[i],NRF_GPIO_PIN_PULLDOWN); 64 | input_scan_vector |= (1U << row_pin_array[i]); //Prepare the magic number 65 | } 66 | if (((NRF_GPIO->IN)&input_scan_vector) != 0){ //Detect if any input pin is high 67 | return false; //If inputs are not all low while output are, there must be something wrong 68 | }else{ 69 | transmitted_keys_num = 0; //Clear the arrays 70 | currently_pressed_keys_num = 0; 71 | for (uint_fast8_t i = MAX_NUM_OF_PRESSED_KEYS; i--;){ 72 | transmitted_keys[i] = 0; 73 | currently_pressed_keys[i] = 0; 74 | } 75 | } 76 | matrix_lookup = default_matrix_lookup; 77 | } 78 | return true; 79 | } 80 | 81 | bool keyboard_new_packet(const uint8_t **p_key_packet, uint8_t *p_key_packet_size) 82 | { 83 | uint8_t *p_tmp; 84 | bool is_new_packet; 85 | 86 | p_tmp = currently_pressed_keys; //Save the currently pressed keys to transmitted keys by swapping two pointers 87 | currently_pressed_keys = transmitted_keys; 88 | transmitted_keys = p_tmp; 89 | transmitted_keys_num = currently_pressed_keys_num; //Save the number of currently pressed keys 90 | 91 | if (keymatrix_read(currently_pressed_keys, ¤tly_pressed_keys_num)){ //Scan the key matrix, save the result in currently_pressed_keys 92 | if (have_keys_changed()){ //Some keys have been pressed, check if there is key changes 93 | keypacket_create(&key_packet[0], KEY_PACKET_SIZE); 94 | *p_key_packet = &key_packet[0]; 95 | *p_key_packet_size = KEY_PACKET_SIZE; 96 | is_new_packet = true; 97 | }else{ 98 | is_new_packet = false; //No need to create a new packet if there is no changes 99 | } 100 | } 101 | else{ 102 | is_new_packet = false; //No key is pressed or ghosting detected. Don't create a packet. 103 | } 104 | return is_new_packet; 105 | } 106 | 107 | static bool keymatrix_read(uint8_t *pressed_keys, uint8_t *number_of_pressed_keys) 108 | { 109 | uint_fast8_t row_state[KEYBOARD_NUM_OF_COLUMNS]; 110 | uint_fast8_t blocking_mask = 0; 111 | *number_of_pressed_keys = 0; 112 | 113 | for (uint_fast8_t column = KEYBOARD_NUM_OF_COLUMNS; column--;){ //Loop through each column 114 | nrf_gpio_pin_set((uint32_t) column_pin_array[column]); 115 | if (((NRF_GPIO->IN)&input_scan_vector) != 0){ //If any input is high 116 | uint_fast8_t pin_state; 117 | row_state[column] = 0; 118 | uint_fast8_t detected_keypresses_on_column = 0; 119 | for (uint_fast8_t row = KEYBOARD_NUM_OF_ROWS; row--;){ //Loop through each row 120 | pin_state = nrf_gpio_pin_read((uint32_t) row_pin_array[row]); 121 | row_state[column] |= (pin_state << row); //Record pin state 122 | if (pin_state){ //If pin is high 123 | if (*number_of_pressed_keys < MAX_NUM_OF_PRESSED_KEYS){ 124 | *pressed_keys = matrix_lookup[row * KEYBOARD_NUM_OF_COLUMNS + column]; 125 | if(*pressed_keys != 0){ //Record the pressed key if it's not 0 126 | pressed_keys++; 127 | (*number_of_pressed_keys)++; 128 | } 129 | } 130 | detected_keypresses_on_column++; 131 | } 132 | } 133 | if (detected_keypresses_on_column > 1){ 134 | if (blocking_mask & row_state[column]){ //If there is ghosting 135 | return false; 136 | } 137 | } 138 | blocking_mask |= row_state[column]; 139 | } 140 | nrf_gpio_pin_clear((uint32_t) column_pin_array[column]); 141 | } 142 | return true; 143 | } 144 | 145 | static void keypacket_create(uint8_t *key_packet, uint8_t key_packet_size) 146 | { 147 | bool fn_key_is_set = false; 148 | 149 | for (uint_fast8_t i = KEY_PACKET_KEY_INDEX; i < key_packet_size; i++){ 150 | key_packet[i] = KEY_PACKET_NO_KEY; // Clear key_packet contents 151 | } 152 | key_packet[KEY_PACKET_MODIFIER_KEY_INDEX] = 0; 153 | key_packet[KEY_PACKET_RESERVED_INDEX] = 0; 154 | 155 | for (uint_fast8_t i = 0; i < currently_pressed_keys_num; i++){ //Loop through the currently pressed keys array 156 | if (currently_pressed_keys[i] == 0xFF) { //If Fn is pressed, 157 | fn_key_is_set = true; 158 | }else if (currently_pressed_keys[i] >= MODIFIER_HID_START && currently_pressed_keys[i] <= MODIFIER_HID_END) { 159 | //Detect and set modifier key, see HID Usage Tables for more detail 160 | key_packet[KEY_PACKET_MODIFIER_KEY_INDEX] |= (uint8_t)(1U << (currently_pressed_keys[i] - MODIFIER_HID_START)); 161 | } 162 | else if (currently_pressed_keys[i] != 0){ 163 | keypacket_addkey(currently_pressed_keys[i]); //Add keys to the packsge 164 | } 165 | } 166 | if (fn_key_is_set){ 167 | remap_fn_keys(&key_packet[0], KEY_PACKET_MAX_KEYS); 168 | } 169 | } 170 | 171 | static void keypacket_addkey(uint8_t key){ 172 | for (uint_fast8_t i = KEY_PACKET_KEY_INDEX; i < KEY_PACKET_SIZE; i++){ 173 | if (key_packet[i] == key){ //If the key is already in the packet 174 | return; 175 | } 176 | } 177 | for (uint_fast8_t i = KEY_PACKET_KEY_INDEX; i < KEY_PACKET_SIZE; i++){ 178 | if (key_packet[i] == KEY_PACKET_NO_KEY){ 179 | key_packet[i] = key; 180 | return; 181 | } 182 | } 183 | } 184 | 185 | static bool have_keys_changed(void){ 186 | if (currently_pressed_keys_num != transmitted_keys_num){ 187 | return true; 188 | }else{ 189 | for (uint_fast8_t i = currently_pressed_keys_num; i--;){ 190 | if (currently_pressed_keys[i] != transmitted_keys[i]){ 191 | return true; 192 | } 193 | } 194 | } 195 | return false; 196 | } 197 | 198 | static void remap_fn_keys(uint8_t *keys, uint8_t number_of_keys){ 199 | for (uint_fast8_t i = KEY_PACKET_KEY_INDEX; i < number_of_keys; i++){ 200 | switch (keys[i]){ 201 | case 0x3A: 202 | keys[i] = 0x7F; break; //F1 -> Mute 203 | case 0x3B: 204 | keys[i] = 0x81; break; //F2 -> VolumnDown 205 | case 0x3C: 206 | keys[i] = 0x80; break; //F3 -> VolumnUp 207 | case 0x3D: 208 | keys[i] = 0x48; break; //F4 -> Pause 209 | case 0x52: 210 | keys[i] = 0x4B; break; //Up -> PageUp 211 | case 0x51: 212 | keys[i] = 0x4E; break; //Down -> PageDown 213 | case 0x50: 214 | keys[i] = 0x4A; break; //Left -> Home 215 | case 0x4F: 216 | keys[i] = 0x4D; break; //Right -> End 217 | case 0x2C: 218 | keys[i] = 0xFA; break; //Space -> Sleep mode 219 | default: break; 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /BLE-Keyboard/keyboard_driver.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved. 2 | * 3 | * The information contained herein is property of Nordic Semiconductor ASA. 4 | * Terms and conditions of usage are described in detail in NORDIC 5 | * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 6 | * 7 | * Licensees are granted free, non-transferable use of the information. NO 8 | * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 9 | * the file. 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #define MAX_NUM_OF_PRESSED_KEYS 6 //!< Maximum number of pressed keys kept in buffers 17 | #define KEY_PACKET_MODIFIER_KEY_INDEX (0) //!< Index in the key packet where modifier keys such as ALT and Control are stored 18 | #define KEY_PACKET_RESERVED_INDEX (1) //!< Index in the key packet where OEMs can store information 19 | #define KEY_PACKET_KEY_INDEX (2) //!< Start index in the key packet where pressed keys are stored 20 | #define KEY_PACKET_MAX_KEYS (MAX_NUM_OF_PRESSED_KEYS) //!< Maximum number of keys that can be stored into the key packet 21 | #define KEY_PACKET_SIZE (KEY_PACKET_KEY_INDEX+KEY_PACKET_MAX_KEYS) //!< Total size of the key packet in bytes 22 | #define KEY_PACKET_NO_KEY (0) //!< Value to be stored to key index to indicate no key is pressed 23 | 24 | void sleep_mode_prepare(void); 25 | 26 | bool keyboard_init(void); 27 | 28 | /** 29 | * @brief Function for creating a new key packet if new data is available and key ghosting is not detected. 30 | * 31 | * @param p_key_packet Array that will hold the created key packet. Previously created packet will be discarded. 32 | * @param p_key_packet_size Key packet size in bytes. 33 | * @return 34 | * @retval true If new packet was created. 35 | * @retval false If packet was not created. 36 | */ 37 | bool keyboard_new_packet(const uint8_t **p_key_packet, uint8_t *p_key_packet_size); 38 | 39 | /** 40 | *@} 41 | **/ 42 | 43 | /*lint --flb "Leave library region" */ 44 | -------------------------------------------------------------------------------- /BLE-Keyboard/keyboard_map.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define KEYBOARD_NUM_OF_COLUMNS 16 // !< Number of columns in the keyboard matrix 4 | #define KEYBOARD_NUM_OF_ROWS 8 // !< Number of rows in the keyboard matrix 5 | 6 | static const uint8_t row_pin_array[KEYBOARD_NUM_OF_ROWS] = {0,3,21,22,23,25,28,30}; 7 | static const uint8_t column_pin_array[KEYBOARD_NUM_OF_COLUMNS] = {1,2,4,5,6,7,8,9,10,11,12,13,14,15,24,29}; 8 | 9 | static const uint8_t wakeup_button_row_index = 2; //select row_pin_array[2] = 21 10 | static const uint8_t wakeup_button_column_index = 1; //select column_pin_array[1] = 2 11 | //wakeup button is default_matrix_lookup[2][1] = 0x29 and it's ESC key 12 | 13 | /** Table containing the mapping between the key matrix and the HID Usage codes for each key. */ 14 | static const uint8_t default_matrix_lookup[KEYBOARD_NUM_OF_COLUMNS * KEYBOARD_NUM_OF_ROWS] = 15 | { 16 | 0x3E, 0x35, 0x3B, 0x22, 0xFF, 0x23, 0x00, 0x00,0x00, 0x00, 0x31, 0x42, 0x2D, 0x00, 0x00, 0x00, 17 | 0x41, 0x39, 0x3A, 0x05, 0x00, 0x11, 0x00, 0x00,0x00, 0xE6, 0x00, 0x45, 0x2E, 0x4C, 0x00, 0x00, 18 | 0x40, 0x29, 0x3D, 0x0A, 0x00, 0x0B, 0x00, 0xE3,0x49, 0x00, 0x2A, 0x44, 0x34, 0x46, 0x00, 0x00, 19 | 0x1A, 0x14, 0x08, 0x15, 0x00, 0x18, 0x0C, 0x00,0x00, 0x00, 0x52, 0x12, 0x13, 0x50, 0xE5, 0x00, 20 | 0x1F, 0x1E, 0x20, 0x21, 0x00, 0x24, 0x25, 0x00,0x00, 0xE2, 0x51, 0x26, 0x27, 0x4F, 0x00, 0x00, 21 | 0x16, 0x04, 0x07, 0x09, 0x00, 0x0D, 0x0E, 0x00,0x00, 0x00, 0x28, 0x0F, 0x33, 0x00, 0x00, 0xE4, 22 | 0x1B, 0x1D, 0x06, 0x19, 0x00, 0x10, 0x36, 0x00,0x00, 0x00, 0x2C, 0x37, 0x38, 0x00, 0xE1, 0x00, 23 | 0x3F, 0x2B, 0x3C, 0x17, 0x00, 0x1C, 0x30, 0x00,0x00, 0x00, 0x00, 0x43, 0x2F, 0x00, 0x00, 0xE0 24 | }; 25 | -------------------------------------------------------------------------------- /BLE-Keyboard/main.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved. 2 | * 3 | * The information contained herein is property of Nordic Semiconductor ASA. 4 | * Terms and conditions of usage are described in detail in NORDIC 5 | * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 6 | * 7 | * Licensees are granted free, non-transferable use of the information. NO 8 | * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 9 | * the file. 10 | * 11 | */ 12 | 13 | /** @file 14 | * This file contains is the source code for a sample application using the HID and Device 15 | * Information Services for implementing a simple keyboard functionality. 16 | * 17 | * Also it would accept pairing requests from any peer device. 18 | */ 19 | 20 | #include 21 | #include 22 | #include "nordic_common.h" 23 | #include "nrf.h" 24 | #include "nrf_assert.h" 25 | #include "app_error.h" 26 | #include "nrf_gpio.h" 27 | #include "nrf51_bitfields.h" 28 | #include "ble.h" 29 | #include "ble_hci.h" 30 | #include "ble_srv_common.h" 31 | #include "ble_advertising.h" 32 | #include "ble_advdata.h" 33 | #include "ble_hids.h" 34 | #include "ble_bas.h" 35 | #include "ble_dis.h" 36 | #include "ble_conn_params.h" 37 | #include "bsp.h" 38 | #include "sensorsim.h" 39 | #include "bsp_btn_ble.h" 40 | #include "app_scheduler.h" 41 | #include "softdevice_handler_appsh.h" 42 | #include "app_timer_appsh.h" 43 | #include "device_manager.h" 44 | #include "app_button.h" 45 | #include "pstorage.h" 46 | #include "app_trace.h" 47 | #include "keyboard_driver.h" 48 | 49 | 50 | #define IS_SRVC_CHANGED_CHARACT_PRESENT 1 /**< Include or not the service_changed characteristic. if not enabled, the server's database cannot be changed for the lifetime of the device*/ 51 | 52 | #define DEVICE_NAME "Nordic_Keyboard" /**< Name of device. Will be included in the advertising data. */ 53 | #define MANUFACTURER_NAME "NordicSemiconductor" /**< Manufacturer. Will be passed to Device Information Service. */ 54 | 55 | #define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */ 56 | #define APP_TIMER_MAX_TIMERS (4+BSP_APP_TIMERS_NUMBER) /**< Maximum number of simultaneously created timers. */ 57 | #define APP_TIMER_OP_QUEUE_SIZE 4 /**< Size of timer operation queues. */ 58 | 59 | #define KEYBOARD_SCAN_INTERVAL APP_TIMER_TICKS(25, APP_TIMER_PRESCALER) /**< Keyboard scan interval (ticks). */ 60 | 61 | #define PNP_ID_VENDOR_ID_SOURCE 0x02 /**< Vendor ID Source. */ 62 | #define PNP_ID_VENDOR_ID 0x1915 /**< Vendor ID. */ 63 | #define PNP_ID_PRODUCT_ID 0xEEEE /**< Product ID. */ 64 | #define PNP_ID_PRODUCT_VERSION 0x0001 /**< Product Version. */ 65 | 66 | #define APP_ADV_FAST_INTERVAL 0x0028 /**< Fast advertising interval (in units of 0.625 ms. This value corresponds to 25 ms.). */ 67 | #define APP_ADV_SLOW_INTERVAL 0x0C80 /**< Slow advertising interval (in units of 0.625 ms. This value corrsponds to 2 seconds). */ 68 | #define APP_ADV_FAST_TIMEOUT 30 /**< The duration of the fast advertising period (in seconds). */ 69 | #define APP_ADV_SLOW_TIMEOUT 180 /**< The duration of the slow advertising period (in seconds). */ 70 | 71 | /*lint -emacro(524, MIN_CONN_INTERVAL) // Loss of precision */ 72 | #define MIN_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) /**< Minimum connection interval (7.5 ms) */ 73 | #define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /**< Maximum connection interval (30 ms). */ 74 | #define SLAVE_LATENCY 6 /**< Slave latency. */ 75 | #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(430, UNIT_10_MS) /**< Connection supervisory timeout (430 ms). */ 76 | 77 | #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000, APP_TIMER_PRESCALER) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */ 78 | #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000, APP_TIMER_PRESCALER) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */ 79 | #define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */ 80 | 81 | #define SEC_PARAM_BOND 1 /**< Perform bonding. */ 82 | #define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */ 83 | #define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */ 84 | #define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */ 85 | #define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */ 86 | #define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */ 87 | 88 | #define OUTPUT_REPORT_INDEX 0 /**< Index of Output Report. */ 89 | #define OUTPUT_REPORT_MAX_LEN 1 /**< Maximum length of Output Report. */ 90 | #define INPUT_REPORT_KEYS_INDEX 0 /**< Index of Input Report. */ 91 | #define OUTPUT_REPORT_BIT_MASK_CAPS_LOCK 0x02 /**< CAPS LOCK bit in Output Report (based on 'LED Page (0x08)' of the Universal Serial Bus HID Usage Tables). */ 92 | #define INPUT_REP_REF_ID 0 /**< Id of reference to Keyboard Input Report. */ 93 | #define OUTPUT_REP_REF_ID 0 /**< Id of reference to Keyboard Output Report. */ 94 | 95 | #define APP_FEATURE_NOT_SUPPORTED BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 /**< Reply when unsupported features are requested. */ 96 | 97 | #define MAX_BUFFER_ENTRIES 5 /**< Number of elements that can be enqueued */ 98 | 99 | #define BASE_USB_HID_SPEC_VERSION 0x0101 /**< Version number of base USB HID Specification implemented by this application. */ 100 | 101 | #define INPUT_REPORT_KEYS_MAX_LEN 8 /**< Maximum length of the Input Report characteristic. */ 102 | 103 | #define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */ 104 | 105 | #define SCHED_MAX_EVENT_DATA_SIZE MAX(APP_TIMER_SCHED_EVT_SIZE,\ 106 | BLE_STACK_HANDLER_SCHED_EVT_SIZE) /**< Maximum size of scheduler events. */ 107 | #define SCHED_QUEUE_SIZE 10 /**< Maximum number of events in the scheduler queue. */ 108 | 109 | #define MODIFIER_KEY_POS 0 /**< Position of the modifier byte in the Input Report. */ 110 | #define SCAN_CODE_POS 2 /**< This macro indicates the start position of the key scan code in a HID Report. As per the document titled 'Device Class Definition for Human Interface Devices (HID) V1.11, each report shall have one modifier byte followed by a reserved constant byte and then the key scan code. */ 111 | #define SHIFT_KEY_CODE 0x02 /**< Key code indicating the press of the Shift Key. */ 112 | 113 | #define MAX_KEYS_IN_ONE_REPORT (INPUT_REPORT_KEYS_MAX_LEN - SCAN_CODE_POS) /**< Maximum number of key presses that can be sent in one Input Report. */ 114 | 115 | /**Buffer queue access macros 116 | * 117 | * @{ */ 118 | /** Initialization of buffer list */ 119 | #define BUFFER_LIST_INIT() \ 120 | do \ 121 | { \ 122 | buffer_list.rp = 0; \ 123 | buffer_list.wp = 0; \ 124 | buffer_list.count = 0; \ 125 | } while (0) 126 | 127 | /** Provide status of data list is full or not */ 128 | #define BUFFER_LIST_FULL()\ 129 | ((MAX_BUFFER_ENTRIES == buffer_list.count - 1) ? true : false) 130 | 131 | /** Provides status of buffer list is empty or not */ 132 | #define BUFFER_LIST_EMPTY()\ 133 | ((0 == buffer_list.count) ? true : false) 134 | 135 | #define BUFFER_ELEMENT_INIT(i)\ 136 | do \ 137 | { \ 138 | buffer_list.buffer[(i)].p_data = NULL; \ 139 | } while (0) 140 | 141 | /** @} */ 142 | 143 | typedef enum 144 | { 145 | BLE_NO_ADV, /**< No advertising running. */ 146 | BLE_DIRECTED_ADV, /**< Direct advertising to the latest central. */ 147 | BLE_FAST_ADV_WHITELIST, /**< Advertising with whitelist. */ 148 | BLE_FAST_ADV, /**< Fast advertising running. */ 149 | BLE_SLOW_ADV, /**< Slow advertising running. */ 150 | BLE_SLEEP, /**< Go to system-off. */ 151 | } ble_advertising_mode_t; 152 | 153 | /** Abstracts buffer element */ 154 | typedef struct hid_key_buffer 155 | { 156 | uint8_t data_offset; /**< Max Data that can be buffered for all entries */ 157 | uint8_t data_len; /**< Total length of data */ 158 | uint8_t * p_data; /**< Scanned key pattern */ 159 | ble_hids_t * p_instance; /**< Identifies peer and service instance */ 160 | }buffer_entry_t; 161 | 162 | STATIC_ASSERT(sizeof(buffer_entry_t) % 4 == 0); 163 | 164 | /** Circular buffer list */ 165 | typedef struct 166 | { 167 | buffer_entry_t buffer[MAX_BUFFER_ENTRIES]; /**< Maximum number of entries that can enqueued in the list */ 168 | uint8_t rp; /**< Index to the read location */ 169 | uint8_t wp; /**< Index to write location */ 170 | uint8_t count; /**< Number of elements in the list */ 171 | }buffer_list_t; 172 | 173 | STATIC_ASSERT(sizeof(buffer_list_t) % 4 == 0); 174 | 175 | static ble_hids_t m_hids; /**< Structure used to identify the HID service. */ 176 | static bool m_in_boot_mode = false; /**< Current protocol mode. */ 177 | static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ 178 | 179 | static app_timer_id_t m_keyboard_timer_id; /**< Keyboard scan timer. */ 180 | 181 | static dm_application_instance_t m_app_handle; /**< Application identifier allocated by device manager. */ 182 | static dm_handle_t m_bonded_peer_handle; /**< Device reference handle to the current bonded central. */ 183 | static bool m_caps_on = false; /**< Variable to indicate if Caps Lock is turned on. */ 184 | 185 | bool sleep = false; 186 | 187 | static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_HUMAN_INTERFACE_DEVICE_SERVICE, BLE_UUID_TYPE_BLE}}; 188 | 189 | static void sleep_mode_enter(void); 190 | static void clear_bond(void); 191 | 192 | static void on_hids_evt(ble_hids_t * p_hids, ble_hids_evt_t * p_evt); 193 | 194 | /**@brief Callback function for asserts in the SoftDevice. 195 | * 196 | * @details This function will be called in case of an assert in the SoftDevice. 197 | * 198 | * @warning This handler is an example only and does not fit a final product. You need to analyze 199 | * how your product is supposed to react in case of Assert. 200 | * @warning On assert from the SoftDevice, the system can only recover on reset. 201 | * 202 | * @param[in] line_num Line number of the failing ASSERT call. 203 | * @param[in] file_name File name of the failing ASSERT call. 204 | */ 205 | void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) 206 | { 207 | app_error_handler(DEAD_BEEF, line_num, p_file_name); 208 | } 209 | 210 | 211 | /**@brief Function for handling Service errors. 212 | * 213 | * @details A pointer to this function will be passed to each service which may need to inform the 214 | * application about an error. 215 | * 216 | * @param[in] nrf_error Error code containing information about what went wrong. 217 | */ 218 | static void service_error_handler(uint32_t nrf_error) 219 | { 220 | APP_ERROR_HANDLER(nrf_error); 221 | } 222 | 223 | 224 | /**@brief Function for handling advertising errors. 225 | * 226 | * @param[in] nrf_error Error code containing information about what went wrong. 227 | */ 228 | static void ble_advertising_error_handler(uint32_t nrf_error) 229 | { 230 | APP_ERROR_HANDLER(nrf_error); 231 | } 232 | 233 | /**@brief Function for the GAP initialization. 234 | * 235 | * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the 236 | * device including the device name, appearance, and the preferred connection parameters. 237 | */ 238 | static void gap_params_init(void) 239 | { 240 | uint32_t err_code; 241 | ble_gap_conn_params_t gap_conn_params; 242 | ble_gap_conn_sec_mode_t sec_mode; 243 | 244 | BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); 245 | 246 | err_code = sd_ble_gap_device_name_set(&sec_mode, 247 | (const uint8_t *)DEVICE_NAME, 248 | strlen(DEVICE_NAME)); 249 | APP_ERROR_CHECK(err_code); 250 | 251 | err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HID_KEYBOARD); 252 | APP_ERROR_CHECK(err_code); 253 | 254 | memset(&gap_conn_params, 0, sizeof(gap_conn_params)); 255 | 256 | gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; 257 | gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; 258 | gap_conn_params.slave_latency = SLAVE_LATENCY; 259 | gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; 260 | 261 | err_code = sd_ble_gap_ppcp_set(&gap_conn_params); 262 | APP_ERROR_CHECK(err_code); 263 | } 264 | 265 | 266 | /**@brief Function for initializing Device Information Service. 267 | */ 268 | static void dis_init(void) 269 | { 270 | uint32_t err_code; 271 | ble_dis_init_t dis_init_obj; 272 | ble_dis_pnp_id_t pnp_id; 273 | 274 | pnp_id.vendor_id_source = PNP_ID_VENDOR_ID_SOURCE; 275 | pnp_id.vendor_id = PNP_ID_VENDOR_ID; 276 | pnp_id.product_id = PNP_ID_PRODUCT_ID; 277 | pnp_id.product_version = PNP_ID_PRODUCT_VERSION; 278 | 279 | memset(&dis_init_obj, 0, sizeof(dis_init_obj)); 280 | 281 | ble_srv_ascii_to_utf8(&dis_init_obj.manufact_name_str, MANUFACTURER_NAME); 282 | dis_init_obj.p_pnp_id = &pnp_id; 283 | 284 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&dis_init_obj.dis_attr_md.read_perm); 285 | BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&dis_init_obj.dis_attr_md.write_perm); 286 | 287 | err_code = ble_dis_init(&dis_init_obj); 288 | APP_ERROR_CHECK(err_code); 289 | } 290 | 291 | 292 | /**@brief Function for initializing HID Service. 293 | */ 294 | static void hids_init(void) 295 | { 296 | uint32_t err_code; 297 | ble_hids_init_t hids_init_obj; 298 | ble_hids_inp_rep_init_t input_report_array[1]; 299 | ble_hids_inp_rep_init_t * p_input_report; 300 | ble_hids_outp_rep_init_t output_report_array[1]; 301 | ble_hids_outp_rep_init_t * p_output_report; 302 | uint8_t hid_info_flags; 303 | 304 | memset((void *)input_report_array, 0, sizeof(ble_hids_inp_rep_init_t)); 305 | memset((void *)output_report_array, 0, sizeof(ble_hids_outp_rep_init_t)); 306 | 307 | static uint8_t report_map_data[] = 308 | { 309 | 0x05, 0x01, // Usage Page (Generic Desktop) 310 | 0x09, 0x06, // Usage (Keyboard) 311 | 0xA1, 0x01, // Collection (Application) 312 | 0x05, 0x07, // Usage Page (Key Codes) 313 | 0x19, 0xe0, // Usage Minimum (224) 314 | 0x29, 0xe7, // Usage Maximum (231) 315 | 0x15, 0x00, // Logical Minimum (0) 316 | 0x25, 0x01, // Logical Maximum (1) 317 | 0x75, 0x01, // Report Size (1) 318 | 0x95, 0x08, // Report Count (8) 319 | 0x81, 0x02, // Input (Data, Variable, Absolute) 320 | 321 | 0x95, 0x01, // Report Count (1) 322 | 0x75, 0x08, // Report Size (8) 323 | 0x81, 0x01, // Input (Constant) reserved byte(1) 324 | 325 | 0x95, 0x05, // Report Count (5) 326 | 0x75, 0x01, // Report Size (1) 327 | 0x05, 0x08, // Usage Page (Page# for LEDs) 328 | 0x19, 0x01, // Usage Minimum (1) 329 | 0x29, 0x05, // Usage Maximum (5) 330 | 0x91, 0x02, // Output (Data, Variable, Absolute), Led report 331 | 0x95, 0x01, // Report Count (1) 332 | 0x75, 0x03, // Report Size (3) 333 | 0x91, 0x01, // Output (Data, Variable, Absolute), Led report padding 334 | 335 | 0x95, 0x06, // Report Count (6) 336 | 0x75, 0x08, // Report Size (8) 337 | 0x15, 0x00, // Logical Minimum (0) 338 | 0x25, 0x65, // Logical Maximum (101) 339 | 0x05, 0x07, // Usage Page (Key codes) 340 | 0x19, 0x00, // Usage Minimum (0) 341 | 0x29, 0x65, // Usage Maximum (101) 342 | 0x81, 0x00, // Input (Data, Array) Key array(6 bytes) 343 | 344 | 0x09, 0x05, // Usage (Vendor Defined) 345 | 0x15, 0x00, // Logical Minimum (0) 346 | 0x26, 0xFF, 0x00, // Logical Maximum (255) 347 | 0x75, 0x08, // Report Count (2) 348 | 0x95, 0x02, // Report Size (8 bit) 349 | 0xB1, 0x02, // Feature (Data, Variable, Absolute) 350 | 351 | 0xC0 // End Collection (Application) 352 | }; 353 | 354 | // Initialize HID Service 355 | p_input_report = &input_report_array[INPUT_REPORT_KEYS_INDEX]; 356 | p_input_report->max_len = INPUT_REPORT_KEYS_MAX_LEN; 357 | p_input_report->rep_ref.report_id = INPUT_REP_REF_ID; 358 | p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT; 359 | 360 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.cccd_write_perm); 361 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.read_perm); 362 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_input_report->security_mode.write_perm); 363 | 364 | p_output_report = &output_report_array[OUTPUT_REPORT_INDEX]; 365 | p_output_report->max_len = OUTPUT_REPORT_MAX_LEN; 366 | p_output_report->rep_ref.report_id = OUTPUT_REP_REF_ID; 367 | p_output_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_OUTPUT; 368 | 369 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_output_report->security_mode.read_perm); 370 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&p_output_report->security_mode.write_perm); 371 | 372 | hid_info_flags = HID_INFO_FLAG_REMOTE_WAKE_MSK | HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK; 373 | 374 | memset(&hids_init_obj, 0, sizeof(hids_init_obj)); 375 | 376 | hids_init_obj.evt_handler = on_hids_evt; 377 | hids_init_obj.error_handler = service_error_handler; 378 | hids_init_obj.is_kb = true; 379 | hids_init_obj.is_mouse = false; 380 | hids_init_obj.inp_rep_count = 1; 381 | hids_init_obj.p_inp_rep_array = input_report_array; 382 | hids_init_obj.outp_rep_count = 1; 383 | hids_init_obj.p_outp_rep_array = output_report_array; 384 | hids_init_obj.feature_rep_count = 0; 385 | hids_init_obj.p_feature_rep_array = NULL; 386 | hids_init_obj.rep_map.data_len = sizeof(report_map_data); 387 | hids_init_obj.rep_map.p_data = report_map_data; 388 | hids_init_obj.hid_information.bcd_hid = BASE_USB_HID_SPEC_VERSION; 389 | hids_init_obj.hid_information.b_country_code = 0; 390 | hids_init_obj.hid_information.flags = hid_info_flags; 391 | hids_init_obj.included_services_count = 0; 392 | hids_init_obj.p_included_services_array = NULL; 393 | 394 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.rep_map.security_mode.read_perm); 395 | BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.rep_map.security_mode.write_perm); 396 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.hid_information.security_mode.read_perm); 397 | BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.hid_information.security_mode.write_perm); 398 | 399 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM( 400 | &hids_init_obj.security_mode_boot_kb_inp_rep.cccd_write_perm); 401 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_kb_inp_rep.read_perm); 402 | BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.security_mode_boot_kb_inp_rep.write_perm); 403 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_kb_outp_rep.read_perm); 404 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_boot_kb_outp_rep.write_perm); 405 | 406 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_protocol.read_perm); 407 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_protocol.write_perm); 408 | BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hids_init_obj.security_mode_ctrl_point.read_perm); 409 | BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&hids_init_obj.security_mode_ctrl_point.write_perm); 410 | 411 | err_code = ble_hids_init(&m_hids, &hids_init_obj); 412 | APP_ERROR_CHECK(err_code); 413 | } 414 | 415 | 416 | /**@brief Function for initializing services that will be used by the application. 417 | */ 418 | static void services_init(void) 419 | { 420 | dis_init(); 421 | hids_init(); 422 | } 423 | 424 | 425 | /**@brief Function for handling a Connection Parameters error. 426 | * 427 | * @param[in] nrf_error Error code containing information about what went wrong. 428 | */ 429 | static void conn_params_error_handler(uint32_t nrf_error) 430 | { 431 | APP_ERROR_HANDLER(nrf_error); 432 | } 433 | 434 | 435 | /**@brief Function for initializing the Connection Parameters module. 436 | */ 437 | static void conn_params_init(void) 438 | { 439 | uint32_t err_code; 440 | ble_conn_params_init_t cp_init; 441 | 442 | memset(&cp_init, 0, sizeof(cp_init)); 443 | 444 | cp_init.p_conn_params = NULL; 445 | cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; 446 | cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY; 447 | cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT; 448 | cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID; 449 | cp_init.disconnect_on_fail = false; 450 | cp_init.evt_handler = NULL; 451 | cp_init.error_handler = conn_params_error_handler; 452 | 453 | err_code = ble_conn_params_init(&cp_init); 454 | APP_ERROR_CHECK(err_code); 455 | } 456 | 457 | 458 | /**@brief Function for starting timers. 459 | */ 460 | static void timers_start(void) 461 | { 462 | uint32_t err_code; 463 | err_code = app_timer_start(m_keyboard_timer_id, KEYBOARD_SCAN_INTERVAL, NULL); 464 | APP_ERROR_CHECK(err_code); 465 | } 466 | 467 | /**@brief Function for sending sample key presses to the peer. 468 | * 469 | * @param[in] key_pattern_len Pattern length. 470 | * @param[in] p_key_pattern Pattern to be sent. 471 | */ 472 | static void keys_send(uint8_t key_pattern_len, uint8_t * p_key_pattern) 473 | { 474 | uint32_t err_code; 475 | 476 | if (!m_in_boot_mode) 477 | { 478 | err_code = ble_hids_inp_rep_send(&m_hids, 479 | INPUT_REPORT_KEYS_INDEX, 480 | INPUT_REPORT_KEYS_MAX_LEN, 481 | p_key_pattern); 482 | } 483 | else 484 | { 485 | err_code = ble_hids_boot_kb_inp_rep_send(&m_hids, 486 | INPUT_REPORT_KEYS_MAX_LEN, 487 | p_key_pattern); 488 | } 489 | 490 | if ((err_code != NRF_SUCCESS) && 491 | (err_code != NRF_ERROR_INVALID_STATE) && 492 | (err_code != BLE_ERROR_NO_TX_BUFFERS) && 493 | (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) 494 | ) 495 | { 496 | APP_ERROR_HANDLER(err_code); 497 | } 498 | } 499 | /**@brief Function for handling the keyboard scan timer timeout. 500 | * 501 | * @details This function will be called each time the keyboard scan timer expires. 502 | * 503 | */ 504 | static void keyboard_scan_timeout_handler(void * p_context) 505 | { 506 | const uint8_t *key_packet; 507 | uint8_t key_packet_size; 508 | if (keyboard_new_packet(&key_packet, &key_packet_size)) 509 | { 510 | if(key_packet[2] == 0xFA){ 511 | sleep_mode_enter(); 512 | }else{ 513 | if (m_conn_handle != BLE_CONN_HANDLE_INVALID) 514 | { 515 | keys_send(key_packet_size,(uint8_t *)&key_packet[0]); 516 | } 517 | } 518 | } 519 | } 520 | 521 | /**@brief Function for handling the HID Report Characteristic Write event. 522 | * 523 | * @param[in] p_evt HID service event. 524 | */ 525 | static void on_hid_rep_char_write(ble_hids_evt_t *p_evt) 526 | { 527 | if (p_evt->params.char_write.char_id.rep_type == BLE_HIDS_REP_TYPE_OUTPUT) 528 | { 529 | uint32_t err_code; 530 | uint8_t report_val; 531 | uint8_t report_index = p_evt->params.char_write.char_id.rep_index; 532 | 533 | if (report_index == OUTPUT_REPORT_INDEX) 534 | { 535 | // This code assumes that the outptu report is one byte long. Hence the following 536 | // static assert is made. 537 | STATIC_ASSERT(OUTPUT_REPORT_MAX_LEN == 1); 538 | 539 | err_code = ble_hids_outp_rep_get(&m_hids, 540 | report_index, 541 | OUTPUT_REPORT_MAX_LEN, 542 | 0, 543 | &report_val); 544 | APP_ERROR_CHECK(err_code); 545 | 546 | if (!m_caps_on && ((report_val & OUTPUT_REPORT_BIT_MASK_CAPS_LOCK) != 0)) 547 | { 548 | // Caps Lock is turned On. 549 | APP_ERROR_CHECK(err_code); 550 | m_caps_on = true; 551 | nrf_gpio_pin_set(16); 552 | } 553 | else if (m_caps_on && ((report_val & OUTPUT_REPORT_BIT_MASK_CAPS_LOCK) == 0)) 554 | { 555 | // Caps Lock is turned Off . 556 | APP_ERROR_CHECK(err_code); 557 | m_caps_on = false; 558 | nrf_gpio_pin_clear(16); 559 | } 560 | else 561 | { 562 | // The report received is not supported by this application. Do nothing. 563 | } 564 | } 565 | } 566 | } 567 | 568 | /**@brief Function for putting the chip into sleep mode. 569 | * 570 | * @note This function will not return. 571 | */ 572 | static void sleep_mode_enter(void) 573 | { 574 | app_timer_stop_all(); 575 | sleep_mode_prepare(); 576 | sleep = true; 577 | } 578 | /**@brief Function for handling HID events. 579 | * 580 | * @details This function will be called for all HID events which are passed to the application. 581 | * 582 | * @param[in] p_hids HID service structure. 583 | * @param[in] p_evt Event received from the HID service. 584 | */ 585 | static void on_hids_evt(ble_hids_t * p_hids, ble_hids_evt_t *p_evt) 586 | { 587 | switch (p_evt->evt_type) 588 | { 589 | case BLE_HIDS_EVT_BOOT_MODE_ENTERED: 590 | m_in_boot_mode = true; 591 | break; 592 | 593 | case BLE_HIDS_EVT_REPORT_MODE_ENTERED: 594 | m_in_boot_mode = false; 595 | break; 596 | 597 | case BLE_HIDS_EVT_REP_CHAR_WRITE: 598 | on_hid_rep_char_write(p_evt); 599 | break; 600 | 601 | case BLE_HIDS_EVT_NOTIF_ENABLED: 602 | { 603 | dm_service_context_t service_context; 604 | service_context.service_type = DM_PROTOCOL_CNTXT_GATT_SRVR_ID; 605 | service_context.context_data.len = 0; 606 | service_context.context_data.p_data = NULL; 607 | 608 | if (m_in_boot_mode) 609 | { 610 | // Protocol mode is Boot Protocol mode. 611 | if ( 612 | p_evt->params.notification.char_id.uuid 613 | == 614 | BLE_UUID_BOOT_KEYBOARD_INPUT_REPORT_CHAR 615 | ) 616 | { 617 | // The notification of boot keyboard input report has been enabled. 618 | // Save the system attribute (CCCD) information into the flash. 619 | uint32_t err_code; 620 | 621 | err_code = dm_service_context_set(&m_bonded_peer_handle, &service_context); 622 | if (err_code != NRF_ERROR_INVALID_STATE) 623 | { 624 | APP_ERROR_CHECK(err_code); 625 | } 626 | else 627 | { 628 | // The system attributes could not be written to the flash because 629 | // the connected central is not a new central. The system attributes 630 | // will only be written to flash only when disconnected from this central. 631 | // Do nothing now. 632 | } 633 | } 634 | else 635 | { 636 | // Do nothing. 637 | } 638 | } 639 | else if (p_evt->params.notification.char_id.rep_type == BLE_HIDS_REP_TYPE_INPUT) 640 | { 641 | // The protocol mode is Report Protocol mode. And the CCCD for the input report 642 | // is changed. It is now time to store all the CCCD information (system 643 | // attributes) into the flash. 644 | uint32_t err_code; 645 | 646 | err_code = dm_service_context_set(&m_bonded_peer_handle, &service_context); 647 | if (err_code != NRF_ERROR_INVALID_STATE) 648 | { 649 | APP_ERROR_CHECK(err_code); 650 | } 651 | else 652 | { 653 | // The system attributes could not be written to the flash because 654 | // the connected central is not a new central. The system attributes 655 | // will only be written to flash only when disconnected from this central. 656 | // Do nothing now. 657 | } 658 | } 659 | else 660 | { 661 | // The notification of the report that was enabled by the central is not interesting 662 | // to this application. So do nothing. 663 | } 664 | break; 665 | } 666 | 667 | default: 668 | // No implementation needed. 669 | break; 670 | } 671 | } 672 | 673 | 674 | /**@brief Function for handling advertising events. 675 | * 676 | * @details This function will be called for advertising events which are passed to the application. 677 | * 678 | * @param[in] ble_adv_evt Advertising event. 679 | */ 680 | static void on_adv_evt(ble_adv_evt_t ble_adv_evt) 681 | { 682 | uint32_t err_code; 683 | 684 | switch (ble_adv_evt) 685 | { 686 | case BLE_ADV_EVT_DIRECTED: 687 | //!err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_DIRECTED); 688 | //!APP_ERROR_CHECK(err_code); 689 | break; 690 | case BLE_ADV_EVT_FAST: 691 | //!err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING); 692 | //!APP_ERROR_CHECK(err_code); 693 | break; 694 | case BLE_ADV_EVT_SLOW: 695 | //!err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_SLOW); 696 | //!APP_ERROR_CHECK(err_code); 697 | break; 698 | case BLE_ADV_EVT_FAST_WHITELIST: 699 | //!err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_WHITELIST); 700 | //!APP_ERROR_CHECK(err_code); 701 | break; 702 | case BLE_ADV_EVT_SLOW_WHITELIST: 703 | //!err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING_WHITELIST); 704 | //!APP_ERROR_CHECK(err_code); 705 | break; 706 | case BLE_ADV_EVT_IDLE: 707 | sleep_mode_enter(); 708 | break; 709 | 710 | case BLE_ADV_EVT_WHITELIST_REQUEST: 711 | { 712 | ble_gap_whitelist_t whitelist; 713 | ble_gap_addr_t * p_whitelist_addr[BLE_GAP_WHITELIST_ADDR_MAX_COUNT]; 714 | ble_gap_irk_t * p_whitelist_irk[BLE_GAP_WHITELIST_IRK_MAX_COUNT]; 715 | 716 | whitelist.addr_count = BLE_GAP_WHITELIST_ADDR_MAX_COUNT; 717 | whitelist.irk_count = BLE_GAP_WHITELIST_IRK_MAX_COUNT; 718 | whitelist.pp_addrs = p_whitelist_addr; 719 | whitelist.pp_irks = p_whitelist_irk; 720 | 721 | err_code = dm_whitelist_create(&m_app_handle, &whitelist); 722 | APP_ERROR_CHECK(err_code); 723 | 724 | err_code = ble_advertising_whitelist_reply(&whitelist); 725 | APP_ERROR_CHECK(err_code); 726 | break; 727 | } 728 | case BLE_ADV_EVT_PEER_ADDR_REQUEST: 729 | { 730 | ble_gap_addr_t peer_address; 731 | 732 | // Only Give peer address if we have a handle to the bonded peer. 733 | if(m_bonded_peer_handle.appl_id != DM_INVALID_ID) 734 | { 735 | 736 | err_code = dm_peer_addr_get(&m_bonded_peer_handle, &peer_address); 737 | APP_ERROR_CHECK(err_code); 738 | 739 | err_code = ble_advertising_peer_addr_reply(&peer_address); 740 | APP_ERROR_CHECK(err_code); 741 | 742 | } 743 | break; 744 | } 745 | default: 746 | break; 747 | } 748 | } 749 | 750 | 751 | /**@brief Function for handling the Application's BLE Stack events. 752 | * 753 | * @param[in] p_ble_evt Bluetooth stack event. 754 | */ 755 | static void on_ble_evt(ble_evt_t * p_ble_evt) 756 | { 757 | uint32_t err_code; 758 | ble_gatts_rw_authorize_reply_params_t auth_reply; 759 | 760 | switch (p_ble_evt->header.evt_id) 761 | { 762 | case BLE_GAP_EVT_CONNECTED: 763 | m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; 764 | break; 765 | case BLE_GAP_EVT_DISCONNECTED: 766 | m_conn_handle = BLE_CONN_HANDLE_INVALID; 767 | m_caps_on = false; 768 | break; 769 | case BLE_EVT_USER_MEM_REQUEST: 770 | err_code = sd_ble_user_mem_reply(m_conn_handle, NULL); 771 | APP_ERROR_CHECK(err_code); 772 | break; 773 | case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: 774 | if(p_ble_evt->evt.gatts_evt.params.authorize_request.type 775 | != BLE_GATTS_AUTHORIZE_TYPE_INVALID) 776 | { 777 | if ((p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.op 778 | == BLE_GATTS_OP_PREP_WRITE_REQ) 779 | || (p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.op 780 | == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) 781 | || (p_ble_evt->evt.gatts_evt.params.authorize_request.request.write.op 782 | == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) 783 | { 784 | if (p_ble_evt->evt.gatts_evt.params.authorize_request.type 785 | == BLE_GATTS_AUTHORIZE_TYPE_WRITE) 786 | { 787 | auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE; 788 | } 789 | else 790 | { 791 | auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ; 792 | } 793 | auth_reply.params.write.gatt_status = APP_FEATURE_NOT_SUPPORTED; 794 | err_code = sd_ble_gatts_rw_authorize_reply(m_conn_handle,&auth_reply); 795 | APP_ERROR_CHECK(err_code); 796 | } 797 | } 798 | break; 799 | 800 | case BLE_GATTC_EVT_TIMEOUT: 801 | case BLE_GATTS_EVT_TIMEOUT: 802 | // Disconnect on GATT Server and Client timeout events. 803 | err_code = sd_ble_gap_disconnect(m_conn_handle, 804 | BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); 805 | APP_ERROR_CHECK(err_code); 806 | break; 807 | 808 | default: 809 | // No implementation needed. 810 | break; 811 | } 812 | } 813 | 814 | 815 | /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler. 816 | * 817 | * @details This function is called from the scheduler in the main loop after a BLE stack 818 | * event has been received. 819 | * 820 | * @param[in] p_ble_evt Bluetooth stack event. 821 | */ 822 | static void ble_evt_dispatch(ble_evt_t * p_ble_evt) 823 | { 824 | dm_ble_evt_handler(p_ble_evt); 825 | bsp_btn_ble_on_ble_evt(p_ble_evt); 826 | on_ble_evt(p_ble_evt); 827 | ble_advertising_on_ble_evt(p_ble_evt); 828 | ble_conn_params_on_ble_evt(p_ble_evt); 829 | ble_hids_on_ble_evt(&m_hids, p_ble_evt); 830 | } 831 | 832 | 833 | /**@brief Function for dispatching a system event to interested modules. 834 | * 835 | * @details This function is called from the System event interrupt handler after a system 836 | * event has been received. 837 | * 838 | * @param[in] sys_evt System stack event. 839 | */ 840 | static void sys_evt_dispatch(uint32_t sys_evt) 841 | { 842 | pstorage_sys_event_handler(sys_evt); 843 | ble_advertising_on_sys_evt(sys_evt); 844 | } 845 | 846 | 847 | /**@brief Function for initializing the BLE stack. 848 | * 849 | * @details Initializes the SoftDevice and the BLE event interrupt. 850 | */ 851 | static void ble_stack_init(void) 852 | { 853 | uint32_t err_code; 854 | 855 | // Initialize the SoftDevice handler module. 856 | SOFTDEVICE_HANDLER_APPSH_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, true); 857 | 858 | // Enable BLE stack 859 | ble_enable_params_t ble_enable_params; 860 | memset(&ble_enable_params, 0, sizeof(ble_enable_params)); 861 | #ifdef S130 862 | ble_enable_params.gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT; 863 | #endif 864 | ble_enable_params.gatts_enable_params.service_changed = IS_SRVC_CHANGED_CHARACT_PRESENT; 865 | err_code = sd_ble_enable(&ble_enable_params); 866 | APP_ERROR_CHECK(err_code); 867 | 868 | // Register with the SoftDevice handler module for BLE events. 869 | err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch); 870 | APP_ERROR_CHECK(err_code); 871 | 872 | // Register with the SoftDevice handler module for BLE events. 873 | err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch); 874 | APP_ERROR_CHECK(err_code); 875 | } 876 | 877 | /**@brief Function for the Timer initialization. 878 | * 879 | * @details Initializes the timer module. 880 | */ 881 | static void timers_init(void) 882 | { 883 | uint32_t err_code; 884 | 885 | // Initialize timer module, making it use the scheduler. 886 | APP_TIMER_APPSH_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true); 887 | // Create keyboard timer. 888 | err_code = app_timer_create(&m_keyboard_timer_id, 889 | APP_TIMER_MODE_REPEATED, 890 | keyboard_scan_timeout_handler); 891 | APP_ERROR_CHECK(err_code); 892 | } 893 | 894 | 895 | /**@brief Function for the Event Scheduler initialization. 896 | */ 897 | static void scheduler_init(void) 898 | { 899 | APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); 900 | } 901 | 902 | /**@brief Function for initializing the Advertising functionality. 903 | */ 904 | static void advertising_init(void) 905 | { 906 | uint32_t err_code; 907 | uint8_t adv_flags; 908 | ble_advdata_t advdata; 909 | 910 | // Build and set advertising data 911 | memset(&advdata, 0, sizeof(advdata)); 912 | 913 | adv_flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE; 914 | advdata.name_type = BLE_ADVDATA_FULL_NAME; 915 | advdata.include_appearance = true; 916 | advdata.flags = adv_flags; 917 | advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]); 918 | advdata.uuids_complete.p_uuids = m_adv_uuids; 919 | 920 | ble_adv_modes_config_t options = 921 | { 922 | BLE_ADV_WHITELIST_DISABLED, 923 | BLE_ADV_DIRECTED_ENABLED, 924 | BLE_ADV_DIRECTED_SLOW_DISABLED, 0,0, 925 | BLE_ADV_FAST_ENABLED, APP_ADV_FAST_INTERVAL, APP_ADV_FAST_TIMEOUT, 926 | BLE_ADV_SLOW_ENABLED, APP_ADV_SLOW_INTERVAL, APP_ADV_SLOW_TIMEOUT 927 | }; 928 | 929 | err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, ble_advertising_error_handler); 930 | APP_ERROR_CHECK(err_code); 931 | } 932 | 933 | 934 | /**@brief Function for handling the Device Manager events. 935 | * 936 | * @param[in] p_evt Data associated to the device manager event. 937 | */ 938 | static uint32_t device_manager_evt_handler(dm_handle_t const * p_handle, 939 | dm_event_t const * p_event, 940 | ret_code_t event_result) 941 | { 942 | APP_ERROR_CHECK(event_result); 943 | 944 | switch(p_event->event_id) 945 | { 946 | case DM_EVT_DEVICE_CONTEXT_LOADED: // Fall through. 947 | case DM_EVT_SECURITY_SETUP_COMPLETE: 948 | m_bonded_peer_handle = (*p_handle); 949 | break; 950 | } 951 | 952 | return NRF_SUCCESS; 953 | } 954 | 955 | 956 | /**@brief Function for the Device Manager initialization. 957 | * 958 | * @param[in] erase_bonds Indicates whether bonding information should be cleared from 959 | * persistent storage during initialization of the Device Manager. 960 | */ 961 | static void device_manager_init(bool erase_bonds) 962 | { 963 | uint32_t err_code; 964 | dm_init_param_t init_param = {.clear_persistent_data = erase_bonds}; 965 | dm_application_param_t register_param; 966 | 967 | // Initialize peer device handle. 968 | err_code = dm_handle_initialize(&m_bonded_peer_handle); 969 | APP_ERROR_CHECK(err_code); 970 | 971 | // Initialize persistent storage module. 972 | err_code = pstorage_init(); 973 | APP_ERROR_CHECK(err_code); 974 | 975 | err_code = dm_init(&init_param); 976 | APP_ERROR_CHECK(err_code); 977 | 978 | memset(®ister_param.sec_param, 0, sizeof(ble_gap_sec_params_t)); 979 | 980 | register_param.sec_param.bond = SEC_PARAM_BOND; 981 | register_param.sec_param.mitm = SEC_PARAM_MITM; 982 | register_param.sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; 983 | register_param.sec_param.oob = SEC_PARAM_OOB; 984 | register_param.sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; 985 | register_param.sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; 986 | register_param.evt_handler = device_manager_evt_handler; 987 | register_param.service_type = DM_PROTOCOL_CNTXT_GATT_SRVR_ID; 988 | 989 | err_code = dm_register(&m_app_handle, ®ister_param); 990 | APP_ERROR_CHECK(err_code); 991 | } 992 | /**@brief Function for initializing buttons and leds. 993 | * 994 | * @param[out] p_erase_bonds Will be true if the clear bonding button was pressed to wake the application up. 995 | */ 996 | static void keyboard_LED_init(void) 997 | { 998 | nrf_gpio_cfg_output(16); //Capslock_LED+ 999 | nrf_gpio_cfg_output(17); //Capslock_LED- 1000 | nrf_gpio_pin_clear(16); 1001 | nrf_gpio_pin_clear(17); 1002 | } 1003 | 1004 | /**@brief Function for the Power manager. 1005 | */ 1006 | static void power_manage(void) 1007 | { 1008 | uint32_t err_code = sd_app_evt_wait(); 1009 | APP_ERROR_CHECK(err_code); 1010 | } 1011 | 1012 | 1013 | /**@brief Function for application main entry. 1014 | */ 1015 | int main(void) 1016 | { 1017 | uint32_t err_code; 1018 | bool erase_bonds = false; 1019 | // Initialize. 1020 | timers_init(); 1021 | keyboard_LED_init(); 1022 | keyboard_init(); 1023 | ble_stack_init(); 1024 | scheduler_init(); 1025 | device_manager_init(erase_bonds); 1026 | gap_params_init(); 1027 | advertising_init(); 1028 | services_init(); 1029 | conn_params_init(); 1030 | 1031 | // Start execution. 1032 | timers_start(); 1033 | err_code = ble_advertising_start(BLE_ADV_MODE_FAST); 1034 | APP_ERROR_CHECK(err_code); 1035 | 1036 | // Enter main loop. 1037 | for (;;) 1038 | { 1039 | if(sleep == true){ 1040 | sd_power_system_off(); 1041 | } 1042 | app_sched_execute(); 1043 | power_manage(); 1044 | } 1045 | } 1046 | 1047 | /** 1048 | * @} 1049 | */ 1050 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /NORDIC_LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/I0x0I/DIY-A-BLE-Keyboard/cd24b5b8987f000e1b5241912863eeba82ade7ce/NORDIC_LICENSE -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # DIY A BLE Keyboard 2 | 3 | ## Introduction 4 | This is a simple open source BLE keyboard firmware for the nRF51 bluetooth chip. 5 | 6 | ## How to use 7 | * Follow the [tutorials](https://devzone.nordicsemi.com/tutorials/) to set up development environment 8 | and test the chip. 9 | * Download the [nRF51 SDK](https://developer.nordicsemi.com/) and 10 | the [S110 SoftDevice](http://www.nordicsemi.com/eng/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF51822) 11 | . In this case the nRF51 SDK v9.0 and a 51822 module with S110 v8 are used. 12 | * Find the *ble_app_hids_keyboard* floder in the SDK, put the *.c* files in this project 13 | in the root directory and the *.h* files in the *config* floder 14 | * If you want to use the nRF51 chip as both a keyboard controller and a BLE controller, 15 | you need to connect the keyboard matrix to it. After doing that, set variables of your 16 | keyboard in *keyboard_map.h* and compile the project. After flashing, you should be able 17 | to find a *Nrodic Keyboard* device and that is your BLE keyboard. The lookup matrix uses 18 | HID key code defined in 19 | [USB HID Usage Codes](https://deskthority.net/wiki/Scancode) 20 | or [USB HID to PS/2 Scan Code Translation Table](http://www.hiemalis.org/~keiji/PC/scancode-translate.pdf) 21 | * If you only want to use the nRF51 chip as a BLE controller, beside setting the variables, 22 | you would need to write some code for the nRF51 to communicate with the keyboard controller, 23 | examples can be find in the SDK and more detail information is in 24 | [here](http://infocenter.nordicsemi.com/index.jsp) 25 | 26 | ## How does it work 27 | These codes originated from the hid_keyboard example and the cherry example, a timer is 28 | setted to scan the keyboard every 25ms. If there are key presses, it will create a report 29 | packet in HID keyboard format and send it to the host, when all keys are released, a packet 30 | filled with 0 will be sent. If a key is hold, no packet will be sent until it's release, 31 | the host can recongnize that the key is holding as it does not recive a released packet 32 | (full 0). Doing so is also power saving as there's no need to send packet continously when 33 | keys are holded, know more about the HID keyboard packet format in 34 | [Device Class Definition for Human Interface Devices (HID)](http://www.usb.org/developers/hidpage/HID1_11.pdf) 35 | and [HID Usage Table](http://www.usb.org/developers/hidpage/Hut1_12v2.pdf). Codes on 36 | the battery service has been removed and the whole frame work has been simplified. 37 | 38 | ## Future work 39 | * Add support to the sleep mode 40 | * Add support to touchpad 41 | --------------------------------------------------------------------------------