├── src ├── low_power │ ├── low_power.h │ └── low_power.c ├── error_handler │ ├── error_handler.h │ └── error_handler.c ├── shared │ ├── shared.h │ └── shared.c ├── config │ ├── pin_mapping.h │ ├── keyboard.h │ └── keymap.h ├── kb_link │ ├── kb_link_config.h │ ├── kb_link.h │ ├── kb_link_c.h │ ├── kb_link.c │ └── kb_link_c.c ├── firmware_config.h ├── main_slave.c ├── keycodes.h └── main_master.c ├── .vscode ├── settings.json └── c_cpp_properties.json ├── .gitignore ├── LICENSE ├── README.md ├── keyboards └── ErgoTravel │ └── default │ ├── keyboard.h │ └── keymap.h ├── flash_placement.xml └── bmk_sdk.emProject /src/low_power/low_power.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOW_POWER_H_ 2 | #define _LOW_POWER_H_ 3 | 4 | #include "app_timer.h" 5 | 6 | void low_power_mode_init(const app_timer_id_t *p_scan_timer_id, void (*scan_timeout_handler)(void *)); 7 | void low_power_mode_start(); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.emProject": "xml", 4 | "*.emSession": "xml", 5 | "app_error.h": "c", 6 | "stdint.h": "c", 7 | "fds.h": "c", 8 | "app_util_platform.h": "c", 9 | "compiler_abstraction.h": "c", 10 | "nrf.h": "c" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/error_handler/error_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef _ERROR_HANDLER_H_ 2 | #define _ERROR_HANDLER_H_ 3 | 4 | #include "app_error.h" 5 | #include "nrf_assert.h" 6 | 7 | void assert_nrf_callback(uint16_t line_num, const uint8_t *p_file_name); 8 | void adv_error_handler(ret_code_t err_code); 9 | void conn_params_error_handler(ret_code_t err_code); 10 | void hid_error_handler(ret_code_t err_code); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/shared/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef _SHARED_H_ 2 | #define _SHARED_H_ 3 | 4 | /* 5 | * nRF52 section. 6 | */ 7 | void conn_params_init(void); 8 | void conn_evt_length_ext_init(void); 9 | void gap_params_init(void); 10 | void idle_state_handle(void); 11 | void log_init(void); 12 | void power_management_init(void); 13 | void scheduler_init(void); 14 | //void shutdown_system(void); 15 | 16 | /* 17 | * Firmware section. 18 | */ 19 | void pins_init(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/config/pin_mapping.h: -------------------------------------------------------------------------------- 1 | #ifndef _PIN_MAPPING_H_ 2 | #define _PIN_MAPPING_H_ 3 | 4 | // Pins mapping for BlueMicro. 5 | #define D3 17 6 | #define D2 7 7 | #define D1 25 // SDA. 8 | #define D0 26 // SCL. 9 | #define D4 27 10 | #define C6 28 11 | #define D7 29 12 | #define E6 30 13 | #define B4 15 14 | #define B5 16 15 | 16 | #define F4 5 17 | #define F5 4 18 | #define F6 3 19 | #define F7 2 20 | #define B1 12 // SCK. 21 | #define B3 14 // MISO. 22 | #define B2 13 // MOSI. 23 | #define B6 11 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/kb_link/kb_link_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _KB_LINK_CONIFG_H_ 2 | #define _KB_LINK_CONIFG_H_ 3 | 4 | // Priority for KB link event in SoftDevice. 5 | #define KB_LINK_BLE_OBSERVER_PRIO 2 6 | 7 | // Base UUID: 0D660000-AF06-44F6-A004-F8A8138518C0. 8 | #define KB_LINK_SERVICE_BASE_UUID {0xC0, 0x18, 0x85, 0x13, 0xA8, 0xF8, 0x04, 0xA0, 0xF6, 0x44, 0x06, 0xAF, 0x00, 0x00, 0x66, 0x0D} 9 | 10 | // Service & characteristics UUIDs. 11 | #define KB_LINK_SERVICE_UUID 0xF36B 12 | #define KB_LINK_ACTIVE_KEY_INDEX_CHAR_UUID 0xC74B 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Custom 35 | /output 36 | bmk_sdk.emSession 37 | bmk_master_Debug.jlink 38 | bmk_master_Release.jlink 39 | bmk_slave_Debug.jlink 40 | bmk_slave_Release.jlink 41 | -------------------------------------------------------------------------------- /src/error_handler/error_handler.c: -------------------------------------------------------------------------------- 1 | #include "error_handler.h" 2 | 3 | #include "nrf_log.h" 4 | 5 | #include "../firmware_config.h" 6 | 7 | void assert_nrf_callback(uint16_t line_num, const uint8_t *p_file_name) { 8 | NRF_LOG_INFO("Assert nrf callback."); 9 | 10 | app_error_handler(DEAD_BEEF, line_num, p_file_name); 11 | } 12 | 13 | void adv_error_handler(ret_code_t err_code) { 14 | NRF_LOG_INFO("ADV error."); 15 | 16 | APP_ERROR_HANDLER(err_code); 17 | } 18 | 19 | void conn_params_error_handler(ret_code_t err_code) { 20 | NRF_LOG_INFO("Conn params error."); 21 | 22 | APP_ERROR_HANDLER(err_code); 23 | } 24 | 25 | void hid_error_handler(ret_code_t err_code) { 26 | NRF_LOG_INFO("HID error."); 27 | 28 | APP_ERROR_HANDLER(err_code); 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "../nRF5_SDK/**" 8 | ], 9 | "defines": [ 10 | "_DEBUG", 11 | "UNICODE", 12 | "_UNICODE", 13 | "uint8_t=unsigned char", 14 | "uint16_t=unsigned short", 15 | "uint32_t=unsigned int", 16 | "uint64_t=unsigned long int" 17 | ], 18 | "compilerPath": "/usr/bin/gcc", 19 | "cStandard": "c11", 20 | "cppStandard": "c++17", 21 | "intelliSenseMode": "clang-x64" 22 | } 23 | ], 24 | "version": 4 25 | } 26 | -------------------------------------------------------------------------------- /src/kb_link/kb_link.h: -------------------------------------------------------------------------------- 1 | #ifndef _KB_LINK_H_ 2 | #define _KB_LINK_H_ 3 | 4 | #include "ble_srv_common.h" 5 | #include "ble.h" 6 | 7 | #include "kb_link_config.h" 8 | 9 | #define KB_LINK_DEF(_name) \ 10 | static kb_link_t _name; \ 11 | NRF_SDH_BLE_OBSERVER(_name ## _obs, \ 12 | KB_LINK_BLE_OBSERVER_PRIO, \ 13 | kb_link_on_ble_evt, \ 14 | &_name) 15 | 16 | typedef struct { 17 | uint8_t *active_key_index; 18 | uint8_t len; 19 | } kb_link_init_t; 20 | 21 | typedef struct { 22 | uint16_t conn_handle; 23 | uint16_t service_handle; 24 | uint8_t uuid_type; 25 | ble_gatts_char_handles_t key_index_char_handles; 26 | } kb_link_t; 27 | 28 | uint32_t kb_link_init(kb_link_t *p_kb_link, kb_link_init_t const *p_kb_link_init); 29 | 30 | void kb_link_on_ble_evt(ble_evt_t const *p_ble_evt, void *p_context); 31 | 32 | uint32_t kb_link_active_key_index_update(kb_link_t *p_kb_link, uint8_t *p_key_index, uint8_t len); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kittipong Yothaithiang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BMK-SDK 2 | 3 | Bluetooth mechanical keyboard firmware for nRF52 SoC using nRF5 SDK 4 | 5 | ## Features 6 | 7 | - [x] Basic functionality. 8 | - [x] Basic keys. 9 | - [x] Shifted keys. 10 | - [x] Multi-layer support. 11 | - [x] Master-to-slave link. 12 | - [x] Devices connectivity. Can connect up to 3 devices and switch between them. 13 | - [x] Low power mode (low power idle state). 14 | - [x] Media keys (Consumer control). 15 | 16 | ## Supported board 17 | 18 | - BlueMicro 19 | 20 | ## Supported keyboard 21 | 22 | - ErgoTravel 23 | 24 | ## Setup 25 | 26 | 1. Download and install [SEGGER Embedded Studio](https://www.segger.com/products/development-tools/embedded-studio). 27 | 2. Download and extract [nRF5 SDK](https://www.nordicsemi.com/Software-and-Tools/Software/nRF5-SDK) into a proper folder along with this project. It should look like this: 28 | ``` 29 | . 30 | +-- nRF5_SDK (This folder contains all contents of nRF5 SDK.) 31 | | +-- components 32 | | +-- config 33 | | +-- documentation 34 | | +-- ... 35 | +-- bmk-sdk (This project folder.) 36 | +-- src 37 | +-- ... 38 | ``` 39 | All referenced source files and headers will be resolved to folder nRF5_SDK above. 40 | 3. Open project file (.emProject) using SEGGER Embedded Studio. 41 | 4. Build and flash your firmware. 42 | 43 | ## Supported Libraries Version 44 | 45 | **SoftDevice:** S132 v7.2.0 46 | 47 | **SDK:** 17.1.0 48 | 49 | ## Tutorials on how to use SEGGER Embedded Studio 50 | 51 | - [The complete cross-platform nRF development tutorial](https://www.novelbits.io/cross-platform-nrf-development-tutorial) 52 | - [Getting started with SEGGER Embedded Studio and the nRF5 SDK](https://www.youtube.com/playlist?list=PLx_tBuQ_KSqGHmzdEL2GWEOeix-S5rgTV) 53 | -------------------------------------------------------------------------------- /src/config/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEYBOARD_H_ 2 | #define _KEYBOARD_H_ 3 | 4 | #include 5 | 6 | #include "pin_mapping.h" 7 | 8 | /* 9 | * Keyboard part, e.g. MASTER and SLAVE. 10 | * #define MASTER 11 | * #define SLAVE 12 | * This is defined in SEGGER Embedded Studio project file. 13 | */ 14 | 15 | // Key source, to specify key stroke came from which part of the keyboard. 16 | #define SOURCE_MASTER 1 17 | #define SOURCE_SLAVE 2 18 | 19 | #define MANUFACTURER_NAME "JPConstantineau.com" 20 | 21 | // UUID, random 16-bit UUIDs. 22 | #define SLAVE_UUID 0xE3C7 23 | 24 | #define MASTER_NAME "ErgoTravel" // ErgoTravel - Master. 25 | #define SLAVE_NAME "ETS" // ErgoTravel - Slave. 26 | 27 | // Matrix. 28 | #define MATRIX_ROW_NUM 4 29 | #define MATRIX_COL_NUM 7 30 | 31 | #define MATRIX_ROW_PINS {C6, D7, E6, B4} 32 | #define MATRIX_COL_PINS {F5, F6, F7, B1, B3, B2, B6} 33 | 34 | // Master keyboard definition. 35 | #ifdef MASTER 36 | // If keyboard has slave side. 37 | #define HAS_SLAVE 38 | #define DEVICE_NAME MASTER_NAME 39 | #define SOURCE SOURCE_MASTER 40 | #define MATRIX_DEFINE \ 41 | { \ 42 | {1, 2, 3, 4, 5, 6, 7}, \ 43 | {15, 16, 17, 18, 19, 20, 21}, \ 44 | {29, 30, 31, 32, 33, 34, 35}, \ 45 | {43, 44, 45, 46, 47, 48, 49} \ 46 | } 47 | #endif 48 | 49 | // Slave keyboard definition. 50 | #ifdef SLAVE 51 | #define DEVICE_NAME SLAVE_NAME 52 | #define SOURCE SOURCE_SLAVE 53 | #define MATRIX_DEFINE \ 54 | { \ 55 | {14, 13, 12, 11, 10, 9, 8}, \ 56 | {28, 27, 26, 25, 24, 23, 22}, \ 57 | {42, 41, 40, 39, 38, 37, 36}, \ 58 | {56, 55, 54, 53, 52, 51, 50} \ 59 | } 60 | #endif 61 | 62 | extern const uint8_t ROWS[MATRIX_ROW_NUM]; 63 | extern const uint8_t COLS[MATRIX_COL_NUM]; 64 | extern const int8_t MATRIX[MATRIX_ROW_NUM][MATRIX_COL_NUM]; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /keyboards/ErgoTravel/default/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEYBOARD_H_ 2 | #define _KEYBOARD_H_ 3 | 4 | #include 5 | 6 | #include "pin_mapping.h" 7 | 8 | /* 9 | * Keyboard part, e.g. MASTER and SLAVE. 10 | * #define MASTER 11 | * #define SLAVE 12 | * This is defined in SEGGER Embedded Studio project file. 13 | */ 14 | 15 | // Key source, to specify key stroke came from which part of the keyboard. 16 | #define SOURCE_MASTER 1 17 | #define SOURCE_SLAVE 2 18 | 19 | #define MANUFACTURER_NAME "JPConstantineau.com" 20 | 21 | // UUID, random 16-bit UUIDs. 22 | #define SLAVE_UUID 0xE3C7 23 | 24 | #define MASTER_NAME "ErgoTravel" // ErgoTravel - Master. 25 | #define SLAVE_NAME "ETS" // ErgoTravel - Slave. 26 | 27 | // Matrix. 28 | #define MATRIX_ROW_NUM 4 29 | #define MATRIX_COL_NUM 7 30 | 31 | #define MATRIX_ROW_PINS {C6, D7, E6, B4} 32 | #define MATRIX_COL_PINS {F5, F6, F7, B1, B3, B2, B6} 33 | 34 | // Master keyboard definition. 35 | #ifdef MASTER 36 | // If keyboard has slave side. 37 | #define HAS_SLAVE 38 | #define DEVICE_NAME MASTER_NAME 39 | #define SOURCE SOURCE_MASTER 40 | #define MATRIX_DEFINE \ 41 | { \ 42 | {1, 2, 3, 4, 5, 6, 7}, \ 43 | {15, 16, 17, 18, 19, 20, 21}, \ 44 | {29, 30, 31, 32, 33, 34, 35}, \ 45 | {43, 44, 45, 46, 47, 48, 49} \ 46 | } 47 | #endif 48 | 49 | // Slave keyboard definition. 50 | #ifdef SLAVE 51 | #define DEVICE_NAME SLAVE_NAME 52 | #define SOURCE SOURCE_SLAVE 53 | #define MATRIX_DEFINE \ 54 | { \ 55 | {14, 13, 12, 11, 10, 9, 8}, \ 56 | {28, 27, 26, 25, 24, 23, 22}, \ 57 | {42, 41, 40, 39, 38, 37, 36}, \ 58 | {56, 55, 54, 53, 52, 51, 50} \ 59 | } 60 | #endif 61 | 62 | extern const uint8_t ROWS[MATRIX_ROW_NUM]; 63 | extern const uint8_t COLS[MATRIX_COL_NUM]; 64 | extern const int8_t MATRIX[MATRIX_ROW_NUM][MATRIX_COL_NUM]; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/kb_link/kb_link_c.h: -------------------------------------------------------------------------------- 1 | #ifndef _KB_LINK_C_H_ 2 | #define _KB_LINK_C_H_ 3 | 4 | #include "kb_link_config.h" 5 | 6 | #include "ble_db_discovery.h" 7 | #include "ble_srv_common.h" 8 | #include "ble.h" 9 | 10 | #define KB_LINK_C_DEF(_name) \ 11 | static kb_link_c_t _name; \ 12 | NRF_SDH_BLE_OBSERVER(_name ## _obs, \ 13 | KB_LINK_BLE_OBSERVER_PRIO, \ 14 | kb_link_c_on_ble_evt, \ 15 | &_name) 16 | 17 | typedef enum { 18 | KB_LINK_C_EVT_DISCOVERY_COMPLETE, 19 | KB_LINK_C_EVT_ACTIVE_KEY_INDEX_UPDATE, 20 | KB_LINK_C_EVT_DISCONNECTED 21 | } kb_link_c_evt_type_t; 22 | 23 | typedef struct { 24 | uint16_t active_key_index_handle; 25 | uint16_t active_key_index_cccd_handle; 26 | } kb_link_c_handles_t; 27 | 28 | typedef struct { 29 | kb_link_c_evt_type_t evt_type; 30 | uint16_t conn_handle; 31 | uint8_t *p_data; 32 | uint8_t len; 33 | kb_link_c_handles_t handles; 34 | } kb_link_c_evt_t; 35 | 36 | typedef struct kb_link_c_s kb_link_c_t; 37 | 38 | typedef void (*kb_link_c_evt_handler_t)(kb_link_c_t *p_kb_link_c, kb_link_c_evt_t const * p_evt); 39 | 40 | typedef struct kb_link_c_s { 41 | uint8_t uuid_type; 42 | uint16_t conn_handle; 43 | kb_link_c_handles_t handles; 44 | kb_link_c_evt_handler_t evt_handler; 45 | } kb_link_c_t; 46 | 47 | typedef struct { 48 | kb_link_c_evt_handler_t evt_handler; 49 | } kb_link_c_init_t; 50 | 51 | uint32_t kb_link_c_init(kb_link_c_t *p_kb_link_c, kb_link_c_init_t *p_kb_link_init); 52 | 53 | void kb_link_c_on_ble_evt(ble_evt_t const *p_ble_evt, void * p_context); 54 | 55 | void kb_link_c_on_db_disc_evt(kb_link_c_t *p_kb_link_c, ble_db_discovery_evt_t *p_evt); 56 | 57 | uint32_t kb_link_c_key_index_notif_enable(kb_link_c_t *p_kb_link_c); 58 | 59 | uint32_t kb_link_c_handles_assign(kb_link_c_t *p_kb_link_c, uint16_t conn_handle, kb_link_c_handles_t const *p_peer_handles); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/low_power/low_power.c: -------------------------------------------------------------------------------- 1 | #include "low_power.h" 2 | 3 | #include "app_error.h" 4 | #include "nrf_gpio.h" 5 | #include "nrf_log.h" 6 | #include "nrfx_gpiote.h" 7 | 8 | #include "../config/keyboard.h" 9 | #include "../firmware_config.h" 10 | 11 | static const app_timer_id_t *m_p_scan_timer_id; 12 | 13 | static void (*m_scan_timeout_handler)(void *); 14 | 15 | static void gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action); 16 | 17 | void low_power_mode_init(const app_timer_id_t *p_scan_timer_id, void (*scan_timeout_handler)(void *)) { 18 | ret_code_t err_code; 19 | 20 | NRF_LOG_INFO("low_power_mode_init."); 21 | 22 | m_p_scan_timer_id = p_scan_timer_id; 23 | m_scan_timeout_handler = scan_timeout_handler; 24 | 25 | // Init GPIOTE module. 26 | if (!nrfx_gpiote_is_init()) { 27 | err_code = nrfx_gpiote_init(); 28 | APP_ERROR_CHECK(err_code); 29 | } 30 | 31 | // Init GPIOTE pins. 32 | nrfx_gpiote_in_config_t config = NRFX_GPIOTE_RAW_CONFIG_IN_SENSE_LOTOHI(false); 33 | 34 | for (int i = 0; i < MATRIX_ROW_NUM; i++) { 35 | err_code = nrfx_gpiote_in_init(ROWS[i], &config, gpiote_evt_handler); 36 | APP_ERROR_CHECK(err_code); 37 | } 38 | } 39 | 40 | static void gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { 41 | ret_code_t err_code; 42 | 43 | NRF_LOG_INFO("GPIOTE evt."); 44 | 45 | for (int i = 0; i < MATRIX_ROW_NUM; i++) { 46 | nrfx_gpiote_in_event_disable(ROWS[i]); 47 | } 48 | 49 | for (int i = 0; i < MATRIX_COL_NUM; i++) { 50 | nrf_gpio_pin_clear(COLS[i]); 51 | } 52 | 53 | // Scan matrix. 54 | m_scan_timeout_handler(NULL); 55 | 56 | // Start scan timer. 57 | err_code = app_timer_start(*m_p_scan_timer_id, SCAN_DELAY_TICKS, NULL); 58 | APP_ERROR_CHECK(err_code); 59 | } 60 | 61 | void low_power_mode_start() { 62 | ret_code_t err_code; 63 | 64 | NRF_LOG_INFO("low_power_mode_start."); 65 | 66 | err_code = app_timer_stop(*m_p_scan_timer_id); 67 | APP_ERROR_CHECK(err_code); 68 | 69 | for (int i = 0; i < MATRIX_ROW_NUM; i++) { 70 | nrfx_gpiote_in_event_enable(ROWS[i], true); 71 | } 72 | 73 | for (int i = 0; i < MATRIX_COL_NUM; i++) { 74 | nrf_gpio_pin_set(COLS[i]); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/config/keymap.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEYMAP_H_ 2 | #define _KEYMAP_H_ 3 | 4 | #include 5 | 6 | #include "../keycodes.h" 7 | #include "keyboard.h" 8 | 9 | const uint32_t KEYMAP[][MATRIX_COL_NUM * MATRIX_ROW_NUM * 2] = { 10 | [_BS] = { 11 | KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_ESC, XXXXXXX, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC, 12 | KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_WNLK, XXXXXXX, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, 13 | KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, XXXXXXX, KC_DEL, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT, 14 | KC_ESC, KC_L4, KC_LGUI, KC_LALT, KC_L1, KC_SPC, XXXXXXX, XXXXXXX, KC_RSFT, KC_L2, KC_RALT, KC_RGUI, KC_DEL, XXXXXXX 15 | }, 16 | [_L1] = { 17 | KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, XXXXXXX, XXXXXXX, KC_CIRC, KC_AMPR, KC_ASTR, KC_MINS, KC_EQL, KC_PIPE, 18 | KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, XXXXXXX, XXXXXXX, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSLS, 19 | _______, _______, _______, KC_LCBR, KC_LBRC, KC_LPRN, _______, _______, KC_RPRN, KC_RBRC, KC_RCBR, KC_UNDS, KC_PLUS, _______, 20 | _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_L3, _______, _______, _______, _______ 21 | }, 22 | [_L2] = { 23 | KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, _______, _______, KC_PGUP, KC_PGDN, KC_HOME, KC_END, KC_INS , KC_DEL, 24 | KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, KC_PSCR, KC_PAUS, 25 | _______, _______, _______, _______, _______, _______, _______, _______, KC_PRWD, KC_NXWD, _______, _______, _______, KC_SLCK, 26 | _______, _______, _______, _______, KC_L3, _______, _______, _______, _______, _______, _______, _______, _______, _______ 27 | }, 28 | [_L3] = { 29 | _______, _______, _______, _______, _______, _______, _______, _______, KC_PAST, KC_P7, KC_P8, KC_P9, KC_PMNS, KC_NLCK, 30 | KC_CAPS, _______, _______, _______, _______, _______, _______, _______, KC_PSLS, KC_P4, KC_P5, KC_P6, KC_PPLS, _______, 31 | _______, _______, _______, _______, _______, _______, _______, _______, KC_P0, KC_P1, KC_P2, KC_P3, KC_PDOT, KC_PENT, 32 | _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ 33 | }, 34 | [_L4] = { 35 | KC_VOLU, KC_BRIU, _______, _______, KC_DVCN, KC_DVC1, _______, _______, _______, _______, _______, _______, _______, _______, 36 | KC_VOLD, KC_BRID, _______, _______, _______, KC_DVC2, _______, _______, _______, _______, _______, _______, _______, _______, 37 | KC_MUTE, _______, _______, _______, _______, KC_DVC3, _______, _______, _______, _______, _______, _______, _______, _______, 38 | _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ 39 | } 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /keyboards/ErgoTravel/default/keymap.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEYMAP_H_ 2 | #define _KEYMAP_H_ 3 | 4 | #include 5 | 6 | #include "../keycodes.h" 7 | #include "keyboard.h" 8 | 9 | const uint32_t KEYMAP[][MATRIX_COL_NUM * MATRIX_ROW_NUM * 2] = { 10 | [_BS] = { 11 | KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_ESC, XXXXXXX, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC, 12 | KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_WNLK, XXXXXXX, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, 13 | KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, XXXXXXX, KC_DEL, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT, 14 | KC_ESC, KC_L4, KC_LGUI, KC_LALT, KC_L1, KC_SPC, XXXXXXX, XXXXXXX, KC_RSFT, KC_L2, KC_RALT, KC_RGUI, KC_DEL, XXXXXXX 15 | }, 16 | [_L1] = { 17 | KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, XXXXXXX, XXXXXXX, KC_CIRC, KC_AMPR, KC_ASTR, KC_MINS, KC_EQL, KC_PIPE, 18 | KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, XXXXXXX, XXXXXXX, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSLS, 19 | _______, _______, _______, KC_LCBR, KC_LBRC, KC_LPRN, _______, _______, KC_RPRN, KC_RBRC, KC_RCBR, KC_UNDS, KC_PLUS, _______, 20 | _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_L3, _______, _______, _______, _______ 21 | }, 22 | [_L2] = { 23 | KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, _______, _______, KC_PGUP, KC_PGDN, KC_HOME, KC_END, KC_INS , KC_DEL, 24 | KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, KC_PSCR, KC_PAUS, 25 | _______, _______, _______, _______, _______, _______, _______, _______, KC_PRWD, KC_NXWD, _______, _______, _______, KC_SLCK, 26 | _______, _______, _______, _______, KC_L3, _______, _______, _______, _______, _______, _______, _______, _______, _______ 27 | }, 28 | [_L3] = { 29 | _______, _______, _______, _______, _______, _______, _______, _______, KC_PAST, KC_P7, KC_P8, KC_P9, KC_PMNS, KC_NLCK, 30 | KC_CAPS, _______, _______, _______, _______, _______, _______, _______, KC_PSLS, KC_P4, KC_P5, KC_P6, KC_PPLS, _______, 31 | _______, _______, _______, _______, _______, _______, _______, _______, KC_P0, KC_P1, KC_P2, KC_P3, KC_PDOT, KC_PENT, 32 | _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ 33 | }, 34 | [_L4] = { 35 | KC_VOLU, KC_BRIU, _______, _______, KC_DVCN, KC_DVC1, _______, _______, _______, _______, _______, _______, _______, _______, 36 | KC_VOLD, KC_BRID, _______, _______, _______, KC_DVC2, _______, _______, _______, _______, _______, _______, _______, _______, 37 | KC_MUTE, _______, _______, _______, _______, KC_DVC3, _______, _______, _______, _______, _______, _______, _______, _______, 38 | _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ 39 | } 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/shared/shared.c: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | 3 | #include "app_scheduler.h" 4 | #include "ble_conn_params.h" 5 | #include "nrf_gpio.h" 6 | #include "nrf_log_ctrl.h" 7 | #include "nrf_log_default_backends.h" 8 | #include "nrf_log.h" 9 | #include "nrf_pwr_mgmt.h" 10 | 11 | #include "../config/keyboard.h" 12 | #include "../error_handler/error_handler.h" 13 | #include "../firmware_config.h" 14 | 15 | /* 16 | * nRF52 section. 17 | */ 18 | void conn_params_init(void) { 19 | ret_code_t err_code; 20 | ble_conn_params_init_t cp_init = {0}; 21 | 22 | cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; 23 | cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY; 24 | cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT; 25 | cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID; 26 | cp_init.disconnect_on_fail = false; 27 | cp_init.evt_handler = NULL; 28 | cp_init.error_handler = conn_params_error_handler; 29 | 30 | err_code = ble_conn_params_init(&cp_init); 31 | APP_ERROR_CHECK(err_code); 32 | } 33 | 34 | void conn_evt_length_ext_init(void) { 35 | ret_code_t err_code; 36 | ble_opt_t ble_opt = {0}; 37 | 38 | ble_opt.common_opt.conn_evt_ext.enable = 1; 39 | 40 | err_code = sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EXT, &ble_opt); 41 | APP_ERROR_CHECK(err_code); 42 | } 43 | 44 | void gap_params_init(void) { 45 | ret_code_t err_code; 46 | ble_gap_conn_params_t gap_conn_params; 47 | ble_gap_conn_sec_mode_t sec_mode; 48 | 49 | BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); 50 | 51 | err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME)); 52 | APP_ERROR_CHECK(err_code); 53 | 54 | err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_GENERIC_HID); 55 | APP_ERROR_CHECK(err_code); 56 | 57 | #ifdef MASTER 58 | gap_conn_params.min_conn_interval = MASTER_MIN_CONN_INTERVAL; 59 | gap_conn_params.max_conn_interval = MASTER_MAX_CONN_INTERVAL; 60 | #endif 61 | #ifdef SLAVE 62 | gap_conn_params.min_conn_interval = SLAVE_MIN_CONN_INTERVAL; 63 | gap_conn_params.max_conn_interval = SLAVE_MAX_CONN_INTERVAL; 64 | #endif 65 | gap_conn_params.slave_latency = SLAVE_LATENCY; 66 | gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; 67 | 68 | err_code = sd_ble_gap_ppcp_set(&gap_conn_params); 69 | APP_ERROR_CHECK(err_code); 70 | } 71 | 72 | void idle_state_handle(void) { 73 | app_sched_execute(); 74 | 75 | if (NRF_LOG_PROCESS() == false) { 76 | nrf_pwr_mgmt_run(); 77 | } 78 | } 79 | 80 | void log_init(void) { 81 | ret_code_t err_code; 82 | 83 | err_code = NRF_LOG_INIT(NULL); 84 | APP_ERROR_CHECK(err_code); 85 | 86 | NRF_LOG_DEFAULT_BACKENDS_INIT(); 87 | } 88 | void power_management_init(void) { 89 | ret_code_t err_code; 90 | 91 | err_code = nrf_pwr_mgmt_init(); 92 | APP_ERROR_CHECK(err_code); 93 | } 94 | 95 | void scheduler_init(void) { 96 | APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); 97 | } 98 | 99 | /*void shutdown_system(void) { 100 | nrf_pwr_mgmt_shutdown( 101 | }*/ 102 | 103 | /* 104 | * Firmware section. 105 | */ 106 | void pins_init(void) { 107 | NRF_LOG_INFO("pins_init."); 108 | 109 | for (int i = 0; i < MATRIX_COL_NUM; i++) { 110 | nrf_gpio_cfg_output(COLS[i]); 111 | nrf_gpio_pin_clear(COLS[i]); 112 | } 113 | 114 | for (int i = 0; i < MATRIX_ROW_NUM; i++) { 115 | nrf_gpio_cfg_input(ROWS[i], NRF_GPIO_PIN_PULLDOWN); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/kb_link/kb_link.c: -------------------------------------------------------------------------------- 1 | #include "kb_link.h" 2 | 3 | #include "nrf_log.h" 4 | 5 | #include "../firmware_config.h" 6 | 7 | static uint32_t active_key_index_characteristics_add(kb_link_t *p_kb_link, const kb_link_init_t *p_kb_link_init); 8 | 9 | uint32_t kb_link_init(kb_link_t *p_kb_link, const kb_link_init_t *p_kb_link_init) { 10 | VERIFY_PARAM_NOT_NULL(p_kb_link); 11 | VERIFY_PARAM_NOT_NULL(p_kb_link_init); 12 | 13 | uint32_t err_code; 14 | ble_uuid_t ble_uuid; 15 | 16 | // Initialize service structure. 17 | p_kb_link->conn_handle = BLE_CONN_HANDLE_INVALID; 18 | 19 | // Add KB link service uuid. 20 | ble_uuid128_t base_uuid = {KB_LINK_SERVICE_BASE_UUID}; 21 | err_code = sd_ble_uuid_vs_add(&base_uuid, &p_kb_link->uuid_type); 22 | VERIFY_SUCCESS(err_code); 23 | 24 | ble_uuid.type = p_kb_link->uuid_type; 25 | ble_uuid.uuid = KB_LINK_SERVICE_UUID; 26 | 27 | // Add KB link service. 28 | err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_kb_link->service_handle); 29 | VERIFY_SUCCESS(err_code); 30 | 31 | // Add key index characteristics. 32 | return active_key_index_characteristics_add(p_kb_link, p_kb_link_init); 33 | } 34 | 35 | static uint32_t active_key_index_characteristics_add(kb_link_t *p_kb_link, const kb_link_init_t *p_kb_link_init) { 36 | ble_add_char_params_t add_char_params = {0}; 37 | 38 | add_char_params.uuid = KB_LINK_ACTIVE_KEY_INDEX_CHAR_UUID; 39 | add_char_params.uuid_type = p_kb_link->uuid_type; 40 | add_char_params.max_len = SLAVE_KEY_NUM; 41 | add_char_params.p_init_value = p_kb_link_init->active_key_index; 42 | add_char_params.init_len = p_kb_link_init->len; 43 | add_char_params.is_var_len = true; 44 | add_char_params.read_access = SEC_OPEN; 45 | add_char_params.write_access = SEC_NO_ACCESS; 46 | add_char_params.cccd_write_access = SEC_OPEN; 47 | add_char_params.char_props.read = 1; 48 | add_char_params.char_props.notify = 1; 49 | 50 | return characteristic_add(p_kb_link->service_handle, &add_char_params, &p_kb_link->key_index_char_handles); 51 | } 52 | 53 | void kb_link_on_ble_evt(ble_evt_t const *p_ble_evt, void *p_context) { 54 | kb_link_t *p_kb_link_service = (kb_link_t *)p_context; 55 | 56 | if (p_ble_evt == NULL || p_kb_link_service == NULL) { 57 | return; 58 | } 59 | 60 | NRF_LOG_INFO("KB link evt; evt: 0x%X.", p_ble_evt->header.evt_id); 61 | 62 | switch (p_ble_evt->header.evt_id) { 63 | case BLE_GAP_EVT_CONNECTED: 64 | NRF_LOG_INFO("Connected."); 65 | 66 | p_kb_link_service->conn_handle = p_ble_evt->evt.gap_evt.conn_handle; 67 | break; 68 | 69 | case BLE_GAP_EVT_DISCONNECTED: 70 | NRF_LOG_INFO("Disconnected."); 71 | 72 | p_kb_link_service->conn_handle = BLE_CONN_HANDLE_INVALID; 73 | break; 74 | 75 | default: 76 | // No implementation needed. 77 | break; 78 | } 79 | } 80 | 81 | uint32_t kb_link_active_key_index_update(kb_link_t *p_kb_link, uint8_t *p_active_key_index, uint8_t len) { 82 | VERIFY_PARAM_NOT_NULL(p_kb_link); 83 | 84 | NRF_LOG_INFO("kb_link_active_key_index_update."); 85 | 86 | uint32_t err_code; 87 | ble_gatts_value_t gatts_value = {0}; 88 | gatts_value.len = len; 89 | gatts_value.p_value = p_active_key_index; 90 | 91 | // Try to notify master if connected. 92 | if (p_kb_link->conn_handle != BLE_CONN_HANDLE_INVALID) { 93 | ble_gatts_hvx_params_t hvx_params = {0}; 94 | 95 | hvx_params.handle = p_kb_link->key_index_char_handles.value_handle; 96 | hvx_params.type = BLE_GATT_HVX_NOTIFICATION; 97 | hvx_params.p_len = &gatts_value.len; 98 | hvx_params.p_data = gatts_value.p_value; 99 | 100 | err_code = sd_ble_gatts_hvx(p_kb_link->conn_handle, &hvx_params); 101 | 102 | if (err_code == NRF_SUCCESS) { 103 | err_code = sd_ble_gatts_value_set(p_kb_link->conn_handle, p_kb_link->key_index_char_handles.value_handle, &gatts_value); 104 | VERIFY_SUCCESS(err_code); 105 | } else { 106 | NRF_LOG_INFO("sd_ble_gatts_hvx; ret: 0x%X.", err_code); 107 | } 108 | } else { 109 | // If not connected just set characteristic value. 110 | err_code = sd_ble_gatts_value_set(p_kb_link->conn_handle, p_kb_link->key_index_char_handles.value_handle, &gatts_value); 111 | VERIFY_SUCCESS(err_code); 112 | } 113 | 114 | return err_code; 115 | } 116 | -------------------------------------------------------------------------------- /src/firmware_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _FIRMWARE_CONFIG_H_ 2 | #define _FIRMWARE_CONFIG_H_ 3 | 4 | #include "app_timer.h" 5 | #include "app_util.h" 6 | 7 | // BLE parameters. 8 | #define APP_BLE_OBSERVER_PRIO 3 // Application's BLE observer priority. You shouldn't need to modify this value. 9 | #define APP_BLE_CONN_CFG_TAG 1 // A tag identifying the SoftDevice BLE configuration. 10 | 11 | // GATT Queue parameters. 12 | #define NRF_BLE_GQ_QUEUE_SIZE 4 13 | 14 | // GAP parameters. 15 | #define SLAVE_LATENCY 0 // Slave latency. 16 | #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(2000, UNIT_10_MS) // Connection supervisory timeout (2000 ms). 17 | 18 | // Let's try to use prime numbers for con interval. 19 | // For master. 20 | #define MASTER_MIN_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) // Minimum connection interval for master part. 21 | #define MASTER_MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_1_25_MS) // Maximum connection interval for master part. 22 | // For slave. 23 | #define SLAVE_MIN_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) // Minimum connection interval for slave part. 24 | #define SLAVE_MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_1_25_MS) // Maximum connection interval for slave part. 25 | 26 | // Advertising parameters. 27 | // For master. 28 | #define MASTER_ADV_FAST_INTERVAL MSEC_TO_UNITS(25, UNIT_0_625_MS) // Fast advertising interval (in units of 0.625 ms. This value corresponds to 25 ms.). 29 | #define MASTER_ADV_FAST_DURATION MSEC_TO_UNITS(60000, UNIT_10_MS) // The advertising duration of fast advertising in units of 10 milliseconds. 30 | #define MASTER_ADV_SLOW_INTERVAL MSEC_TO_UNITS(100, UNIT_0_625_MS) // Slow advertising interval (in units of 0.625 ms. This value corresponds to 100 ms). 31 | #define MASTER_ADV_SLOW_DURATION MSEC_TO_UNITS(60000, UNIT_10_MS) // The advertising duration of slow advertising in units of 10 milliseconds. 32 | 33 | // For slave. 34 | #define SLAVE_ADV_FAST_INTERVAL MSEC_TO_UNITS(25, UNIT_0_625_MS) // Fast advertising interval (in units of 0.625 ms. This value corresponds to 25 ms.). 35 | #define SLAVE_ADV_FAST_DURATION MSEC_TO_UNITS(60000, UNIT_10_MS) // The advertising duration of fast advertising in units of 10 milliseconds. 36 | 37 | // Scanning parameters to scan for slave. 38 | #define SCAN_INTERVAL MSEC_TO_UNITS(50, UNIT_0_625_MS) // 50 ms. 39 | #define SCAN_WINDOW MSEC_TO_UNITS(30, UNIT_0_625_MS) // 30 ms. 40 | #define SCAN_DURATION MSEC_TO_UNITS(60000, UNIT_10_MS) // 30 seconds. 41 | 42 | // Connection parameters. 43 | #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(3000) // Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (3 seconds). 44 | #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(10000) // Time between each call to sd_ble_gap_conn_param_update after the first call (10 seconds). 45 | #define MAX_CONN_PARAMS_UPDATE_COUNT 5 // Number of attempts before giving up the connection parameter negotiation. 46 | 47 | // Peer manager parameters. 48 | #define SEC_PARAM_BOND 1 // Perform bonding. 49 | #define SEC_PARAM_MITM 0 // Man In The Middle protection not required. 50 | #define SEC_PARAM_LESC 0 // LE Secure Connections not enabled. 51 | #define SEC_PARAM_KEYPRESS 0 // Keypress notifications not enabled. 52 | #define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE // No I/O capabilities. 53 | #define SEC_PARAM_OOB 0 // Out Of Band data not available. 54 | #define SEC_PARAM_MIN_KEY_SIZE 7 // Minimum encryption key size. 55 | #define SEC_PARAM_MAX_KEY_SIZE 16 // Maximum encryption key size. 56 | 57 | // HID report parameters. 58 | #define INPUT_REPORT_NUM 2 // Keyboard report and Consumer Control report. 59 | 60 | // Keyboard input report. 61 | #define KB_INPUT_REPORT_INDEX 0 // Index of Keyboard Input Report. 62 | #define KB_INPUT_REPORT_ID 1 // Report Id in HID descriptor. 63 | #define KB_INPUT_REPORT_MAX_LEN 8 // Maximum length of the Keyboard Input Report characteristic. 64 | 65 | // Consumer Control input report. 66 | #define CC_INPUT_REPORT_INDEX 1 67 | #define CC_INPUT_REPORT_ID 2 68 | #define CC_INPUT_REPORT_MAX_LEN sizeof(uint16_t) 69 | 70 | // Output (LEDs) report. 71 | #define OUTPUT_REPORT_INDEX 0 // Index of Output Report. 72 | #define OUTPUT_REPORT_ID 1 // Report Id in HID descriptor. 73 | #define OUTPUT_REPORT_MAX_LEN 1 // Maximum length of Output Report. 74 | #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). 75 | 76 | // HID parameters. 77 | #define BASE_USB_HID_SPEC_VERSION 0x0101 // Version number of base USB HID Specification implemented by this application. 78 | 79 | #define DEAD_BEEF 0xDEADBEEF // Value used as error code on stack dump, can be used to identify stack location on stack unwind. 80 | 81 | // Scheduler parameters. 82 | #define SCHED_MAX_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE // Maximum size of scheduler events. 83 | #ifdef SVCALL_AS_NORMAL_FUNCTION 84 | #define SCHED_QUEUE_SIZE 20 // Maximum number of events in the scheduler queue. More is needed in case of Serialization. 85 | #else 86 | #define SCHED_QUEUE_SIZE 10 // Maximum number of events in the scheduler queue. 87 | #endif 88 | 89 | // Devices connection parameters. 90 | #define CONFIG_FILE_ID 0x41C6 91 | #define DEVICE_CONNECTION_KEY 0x4816 92 | 93 | // Firmware parameters. 94 | #define KEY_NUM (MASTER_KEY_NUM + SLAVE_KEY_NUM) 95 | #define MASTER_KEY_NUM 10 96 | #define SLAVE_KEY_NUM 10 97 | #define HID_REPORT_BUFFER_NUM 5 98 | 99 | #define PIN_SET_DELAY 100 // In us (micro seconds), 100us should be enough. 100 | #define SCAN_DELAY 2 101 | #define SCAN_DELAY_TICKS APP_TIMER_TICKS(SCAN_DELAY) 102 | #define KEY_PRESS_DEBOUNCE 8 103 | #define KEY_RELEASE_DEBOUNCE 32 104 | #define OPERATION_DELAY 1 // In ms, 1ms should be enough. 105 | #define LOW_POWER_MODE_DELAY 3000 // In ms. 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /src/kb_link/kb_link_c.c: -------------------------------------------------------------------------------- 1 | #include "kb_link_c.h" 2 | 3 | #include "nrf_log.h" 4 | 5 | static void on_hvx(kb_link_c_t *p_kb_link_c, ble_evt_t const *p_ble_evt); 6 | static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable); 7 | 8 | uint32_t kb_link_c_init(kb_link_c_t *p_kb_link_c, kb_link_c_init_t *p_kb_link_init) { 9 | VERIFY_PARAM_NOT_NULL(p_kb_link_c); 10 | VERIFY_PARAM_NOT_NULL(p_kb_link_init); 11 | 12 | uint32_t err_code; 13 | ble_uuid_t ble_uuid; 14 | ble_uuid128_t base_uuid = {KB_LINK_SERVICE_BASE_UUID}; 15 | 16 | err_code = sd_ble_uuid_vs_add(&base_uuid, &p_kb_link_c->uuid_type); 17 | VERIFY_SUCCESS(err_code); 18 | 19 | ble_uuid.type = p_kb_link_c->uuid_type; 20 | ble_uuid.uuid = KB_LINK_SERVICE_UUID; 21 | 22 | p_kb_link_c->conn_handle = BLE_CONN_HANDLE_INVALID; 23 | p_kb_link_c->evt_handler = p_kb_link_init->evt_handler; 24 | p_kb_link_c->handles.active_key_index_handle = BLE_CONN_HANDLE_INVALID; 25 | p_kb_link_c->handles.active_key_index_cccd_handle = BLE_CONN_HANDLE_INVALID; 26 | 27 | return ble_db_discovery_evt_register(&ble_uuid); 28 | } 29 | 30 | void kb_link_c_on_ble_evt(ble_evt_t const *p_ble_evt, void * p_context) { 31 | kb_link_c_t *p_kb_link_c = (kb_link_c_t *)p_context; 32 | 33 | if (p_ble_evt == NULL || p_kb_link_c == NULL) { 34 | return; 35 | } 36 | 37 | if (p_kb_link_c->conn_handle != BLE_CONN_HANDLE_INVALID && p_kb_link_c->conn_handle != p_ble_evt->evt.gap_evt.conn_handle) { 38 | return; 39 | } 40 | 41 | switch (p_ble_evt->header.evt_id) { 42 | case BLE_GATTC_EVT_HVX: 43 | NRF_LOG_INFO("Receive notification."); 44 | 45 | on_hvx(p_kb_link_c, p_ble_evt); 46 | break; 47 | 48 | case BLE_GAP_EVT_DISCONNECTED: 49 | NRF_LOG_INFO("Disconnected"); 50 | 51 | if (p_ble_evt->evt.gap_evt.conn_handle == p_kb_link_c->conn_handle) { 52 | p_kb_link_c->conn_handle = BLE_CONN_HANDLE_INVALID; 53 | p_kb_link_c->handles.active_key_index_handle = BLE_CONN_HANDLE_INVALID; 54 | p_kb_link_c->handles.active_key_index_cccd_handle = BLE_CONN_HANDLE_INVALID; 55 | 56 | if (p_kb_link_c->evt_handler != NULL) { 57 | kb_link_c_evt_t kb_link_c_evt; 58 | 59 | kb_link_c_evt.evt_type = KB_LINK_C_EVT_DISCONNECTED; 60 | 61 | p_kb_link_c->evt_handler(p_kb_link_c, &kb_link_c_evt); 62 | } 63 | } 64 | 65 | default: 66 | // No implementation needed. 67 | break; 68 | } 69 | } 70 | 71 | static void on_hvx(kb_link_c_t *p_kb_link_c, ble_evt_t const *p_ble_evt) { 72 | if (p_kb_link_c->handles.active_key_index_handle != BLE_CONN_HANDLE_INVALID && p_kb_link_c->evt_handler != NULL && p_ble_evt->evt.gattc_evt.params.hvx.handle == p_kb_link_c->handles.active_key_index_handle && p_ble_evt->evt.gattc_evt.params.hvx.type == BLE_GATT_HVX_NOTIFICATION) { 73 | kb_link_c_evt_t kb_link_c_evt; 74 | 75 | kb_link_c_evt.evt_type = KB_LINK_C_EVT_ACTIVE_KEY_INDEX_UPDATE; 76 | kb_link_c_evt.len = p_ble_evt->evt.gattc_evt.params.hvx.len; 77 | kb_link_c_evt.p_data = (uint8_t *)p_ble_evt->evt.gattc_evt.params.hvx.data; 78 | 79 | p_kb_link_c->evt_handler(p_kb_link_c, &kb_link_c_evt); 80 | } 81 | } 82 | 83 | void kb_link_c_on_db_disc_evt(kb_link_c_t *p_kb_link_c, ble_db_discovery_evt_t *p_evt) { 84 | NRF_LOG_INFO("kb_link_c_on_db_disc_evt."); 85 | 86 | kb_link_c_evt_t kb_link_c_evt = {0}; 87 | 88 | ble_gatt_db_char_t *p_chars = p_evt->params.discovered_db.charateristics; 89 | 90 | // Check if KB link was discovered. 91 | if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE && p_evt->params.discovered_db.srv_uuid.uuid == KB_LINK_SERVICE_UUID && p_evt->params.discovered_db.srv_uuid.type == p_kb_link_c->uuid_type) { 92 | for (int i = 0; i < p_evt->params.discovered_db.char_count; i++) { 93 | switch (p_chars[i].characteristic.uuid.uuid) { 94 | case KB_LINK_ACTIVE_KEY_INDEX_CHAR_UUID: 95 | kb_link_c_evt.handles.active_key_index_handle = p_chars[i].characteristic.handle_value; 96 | kb_link_c_evt.handles.active_key_index_cccd_handle = p_chars[i].cccd_handle; 97 | break; 98 | 99 | default: 100 | break; 101 | } 102 | } 103 | 104 | if (p_kb_link_c->evt_handler != NULL) { 105 | kb_link_c_evt.conn_handle = p_evt->conn_handle; 106 | kb_link_c_evt.evt_type = KB_LINK_C_EVT_DISCOVERY_COMPLETE; 107 | 108 | p_kb_link_c->evt_handler(p_kb_link_c, &kb_link_c_evt); 109 | } 110 | } 111 | } 112 | 113 | uint32_t kb_link_c_key_index_notif_enable(kb_link_c_t *p_kb_link_c) { 114 | VERIFY_PARAM_NOT_NULL(p_kb_link_c); 115 | 116 | if (p_kb_link_c->conn_handle == BLE_CONN_HANDLE_INVALID || p_kb_link_c->handles.active_key_index_cccd_handle == BLE_CONN_HANDLE_INVALID) { 117 | return NRF_ERROR_INVALID_STATE; 118 | } 119 | 120 | return cccd_configure(p_kb_link_c->conn_handle, p_kb_link_c->handles.active_key_index_cccd_handle, true); 121 | } 122 | 123 | static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable) { 124 | uint8_t buffer[BLE_CCCD_VALUE_LEN]; 125 | 126 | buffer[0] = enable ? BLE_GATT_HVX_NOTIFICATION : 0; 127 | buffer[1] = 0; 128 | 129 | ble_gattc_write_params_t const write_params = { 130 | .write_op = BLE_GATT_OP_WRITE_REQ, 131 | .flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE, 132 | .handle = cccd_handle, 133 | .offset = 0, 134 | .len = sizeof(buffer), 135 | .p_value = buffer 136 | }; 137 | 138 | return sd_ble_gattc_write(conn_handle, &write_params); 139 | } 140 | 141 | uint32_t kb_link_c_handles_assign(kb_link_c_t *p_kb_link_c, uint16_t conn_handle, kb_link_c_handles_t const *p_peer_handles) { 142 | VERIFY_PARAM_NOT_NULL(p_kb_link_c); 143 | 144 | p_kb_link_c->conn_handle = conn_handle; 145 | 146 | if (p_peer_handles != NULL) { 147 | p_kb_link_c->handles.active_key_index_handle = p_peer_handles->active_key_index_handle; 148 | p_kb_link_c->handles.active_key_index_cccd_handle = p_peer_handles->active_key_index_cccd_handle; 149 | } 150 | 151 | return NRF_SUCCESS; 152 | } 153 | -------------------------------------------------------------------------------- /flash_placement.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 10 | 14 | 17 | 20 | 23 | 30 | 37 | 44 | 51 | 58 | 65 | 72 | 79 | 86 | 93 | 100 | 107 | 112 | 118 | 124 | 130 | 136 | 139 | 142 | 145 | 150 | 154 | 158 | 162 | 163 | 166 | 170 | 175 | 180 | 186 | 192 | 198 | 204 | 209 | 212 | 215 | 218 | 221 | 224 | 227 | 231 | 238 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /src/main_slave.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "app_error.h" 4 | #include "app_scheduler.h" 5 | #include "app_timer.h" 6 | #include "ble_advertising.h" 7 | #include "ble_dis.h" 8 | #include "ble_err.h" 9 | #include "ble.h" 10 | #include "nordic_common.h" 11 | #include "nrf_assert.h" 12 | #include "nrf_ble_gatt.h" 13 | #include "nrf_delay.h" 14 | #include "nrf_gpio.h" 15 | #include "nrf_log.h" 16 | #include "nrf_sdh_ble.h" 17 | #include "nrf_sdh_soc.h" 18 | #include "nrf_sdh.h" 19 | #include "nrf.h" 20 | 21 | #include "config/keyboard.h" 22 | #include "error_handler/error_handler.h" 23 | #include "firmware_config.h" 24 | #include "kb_link/kb_link.h" 25 | #include "low_power/low_power.h" 26 | #include "shared/shared.h" 27 | 28 | /* 29 | * Variables declaration. 30 | */ 31 | // nRF52 variables. 32 | APP_TIMER_DEF(m_scan_timer_id); 33 | NRF_BLE_GATT_DEF(m_gatt); 34 | BLE_ADVERTISING_DEF(m_advertising); 35 | KB_LINK_DEF(m_kb_link); 36 | 37 | static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; // Handle of the current connection. 38 | static ble_uuid_t m_adv_uuid = {SLAVE_UUID, BLE_UUID_TYPE_VENDOR_BEGIN}; 39 | 40 | // Firmware variables. 41 | const uint8_t ROWS[MATRIX_ROW_NUM] = MATRIX_ROW_PINS; 42 | const uint8_t COLS[MATRIX_COL_NUM] = MATRIX_COL_PINS; 43 | const int8_t MATRIX[MATRIX_ROW_NUM][MATRIX_COL_NUM] = MATRIX_DEFINE; 44 | 45 | static bool m_key_pressed[MATRIX_ROW_NUM][MATRIX_COL_NUM] = {0}; 46 | static int m_debounce[MATRIX_ROW_NUM][MATRIX_COL_NUM]; 47 | static int m_low_power_mode_counter = LOW_POWER_MODE_DELAY; 48 | 49 | static int8_t m_active_key_index[SLAVE_KEY_NUM] = {0}; 50 | static uint16_t m_active_key_index_count = 0; 51 | 52 | /* 53 | * Functions declaration. 54 | */ 55 | // nRF52 functions. 56 | static void timers_init(void); 57 | static void scan_timeout_handler(void *p_context); 58 | static void ble_stack_init(void); 59 | static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context); 60 | static void gatt_init(void); 61 | static void advertising_init(void); 62 | static void adv_evt_handler(ble_adv_evt_t ble_adv_evt); 63 | static void dis_init(void); 64 | static void kbl_init(void); 65 | static void advertising_start(void); 66 | static void timers_start(void); 67 | 68 | // Firmware functions. 69 | static void firmware_init(void); 70 | static void scan_matrix_task(void *p_data, uint16_t size); 71 | 72 | int main(void) { 73 | // Initialize. 74 | // nRF52. 75 | log_init(); 76 | timers_init(); 77 | power_management_init(); 78 | ble_stack_init(); 79 | conn_evt_length_ext_init(); 80 | scheduler_init(); 81 | gap_params_init(); 82 | gatt_init(); 83 | dis_init(); 84 | kbl_init(); 85 | 86 | // Init advertising after all services. 87 | advertising_init(); 88 | conn_params_init(); 89 | 90 | // Firmware. 91 | firmware_init(); 92 | pins_init(); 93 | low_power_mode_init(&m_scan_timer_id, scan_timeout_handler); 94 | 95 | // Start. 96 | advertising_start(); 97 | timers_start(); 98 | 99 | NRF_LOG_INFO("main; started."); 100 | 101 | // Enter main loop. 102 | for (;;) { 103 | idle_state_handle(); 104 | } 105 | } 106 | 107 | /* 108 | * nRF52 section. 109 | */ 110 | static void timers_init(void) { 111 | ret_code_t err_code; 112 | 113 | err_code = app_timer_init(); 114 | APP_ERROR_CHECK(err_code); 115 | 116 | // Matrix scan timer. 117 | err_code = app_timer_create(&m_scan_timer_id, APP_TIMER_MODE_REPEATED, scan_timeout_handler); 118 | APP_ERROR_CHECK(err_code); 119 | } 120 | 121 | static void scan_timeout_handler(void *p_context) { 122 | UNUSED_PARAMETER(p_context); 123 | ret_code_t err_code; 124 | 125 | err_code = app_sched_event_put(NULL, 0, scan_matrix_task); 126 | APP_ERROR_CHECK(err_code); 127 | } 128 | 129 | static void ble_stack_init(void) { 130 | ret_code_t err_code; 131 | 132 | err_code = nrf_sdh_enable_request(); 133 | APP_ERROR_CHECK(err_code); 134 | 135 | // Configure the BLE stack using the default settings. Fetch the start address of the application RAM. 136 | uint32_t ram_start = 0; 137 | err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); 138 | APP_ERROR_CHECK(err_code); 139 | 140 | // Enable BLE stack. 141 | err_code = nrf_sdh_ble_enable(&ram_start); 142 | APP_ERROR_CHECK(err_code); 143 | 144 | // Register a handler for BLE events. 145 | NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); 146 | } 147 | 148 | static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context) { 149 | ret_code_t err_code; 150 | 151 | NRF_LOG_INFO("BLE evt; evt: 0x%X.", p_ble_evt->header.evt_id); 152 | 153 | switch (p_ble_evt->header.evt_id) { 154 | case BLE_GAP_EVT_CONNECTED: 155 | NRF_LOG_INFO("Connected."); 156 | 157 | if (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_PERIPH) { 158 | NRF_LOG_INFO("As peripheral."); 159 | NRF_LOG_INFO("Conn params; conn interval: %i, conn sup timeout: %i.", p_ble_evt->evt.gap_evt.params.connected.conn_params.min_conn_interval * 1.25, p_ble_evt->evt.gap_evt.params.connected.conn_params.conn_sup_timeout * 10); 160 | 161 | m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; 162 | } 163 | break; 164 | 165 | case BLE_GAP_EVT_CONN_PARAM_UPDATE: 166 | NRF_LOG_INFO("Conn params update; conn interval: %i, conn sup timeout: %i.", p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.min_conn_interval * 1.25, p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.conn_sup_timeout * 10); 167 | break; 168 | 169 | case BLE_GAP_EVT_DISCONNECTED: 170 | NRF_LOG_INFO("Disconnected; reason: 0x%X.", p_ble_evt->evt.gap_evt.params.disconnected.reason); 171 | 172 | if (p_ble_evt->evt.gap_evt.conn_handle == m_conn_handle) { 173 | m_conn_handle = BLE_CONN_HANDLE_INVALID; 174 | } 175 | break; 176 | 177 | case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { 178 | NRF_LOG_DEBUG("PHY update request."); 179 | 180 | ble_gap_phys_t const phys = { 181 | .rx_phys = BLE_GAP_PHY_AUTO, 182 | .tx_phys = BLE_GAP_PHY_AUTO, 183 | }; 184 | 185 | err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); 186 | APP_ERROR_CHECK(err_code); 187 | } 188 | break; 189 | 190 | case BLE_GATTC_EVT_TIMEOUT: 191 | // Disconnect on GATT Client timeout event. 192 | NRF_LOG_DEBUG("GATT client timeout."); 193 | 194 | err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); 195 | APP_ERROR_CHECK(err_code); 196 | break; 197 | 198 | case BLE_GATTS_EVT_TIMEOUT: 199 | // Disconnect on GATT Server timeout event. 200 | NRF_LOG_DEBUG("GATT server simeout."); 201 | 202 | err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); 203 | APP_ERROR_CHECK(err_code); 204 | break; 205 | 206 | default: 207 | // No implementation needed. 208 | break; 209 | } 210 | } 211 | 212 | static void gatt_init(void) { 213 | ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL); 214 | APP_ERROR_CHECK(err_code); 215 | } 216 | 217 | static void dis_init(void) { 218 | ret_code_t err_code; 219 | ble_dis_init_t dis_init_obj = {0}; 220 | 221 | ble_srv_ascii_to_utf8(&dis_init_obj.manufact_name_str, MANUFACTURER_NAME); 222 | dis_init_obj.dis_char_rd_sec = SEC_OPEN; 223 | 224 | err_code = ble_dis_init(&dis_init_obj); 225 | APP_ERROR_CHECK(err_code); 226 | } 227 | 228 | static void kbl_init(void) { 229 | ret_code_t err_code; 230 | kb_link_init_t init = {0}; 231 | 232 | init.len = 0; 233 | init.active_key_index = NULL; 234 | 235 | err_code = kb_link_init(&m_kb_link, &init); 236 | APP_ERROR_CHECK(err_code); 237 | } 238 | 239 | static void advertising_init(void) { 240 | uint32_t err_code; 241 | ble_advertising_init_t init = {0}; 242 | 243 | init.advdata.include_appearance = true; 244 | init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; 245 | init.advdata.uuids_complete.uuid_cnt = 1; 246 | init.advdata.uuids_complete.p_uuids = &m_adv_uuid; 247 | 248 | init.srdata.name_type = BLE_ADVDATA_FULL_NAME; 249 | 250 | init.config.ble_adv_fast_enabled = true; 251 | init.config.ble_adv_fast_interval = SLAVE_ADV_FAST_INTERVAL; 252 | init.config.ble_adv_fast_timeout = SLAVE_ADV_FAST_DURATION; 253 | 254 | init.evt_handler = adv_evt_handler; 255 | init.error_handler = adv_error_handler; 256 | 257 | err_code = ble_advertising_init(&m_advertising, &init); 258 | APP_ERROR_CHECK(err_code); 259 | 260 | ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG); 261 | } 262 | 263 | static void adv_evt_handler(const ble_adv_evt_t ble_adv_evt) { 264 | ret_code_t err_code; 265 | 266 | NRF_LOG_INFO("ADV evt; evt: 0x%X.", ble_adv_evt); 267 | 268 | switch (ble_adv_evt) { 269 | case BLE_ADV_EVT_IDLE: 270 | NRF_LOG_INFO("Stop advertising."); 271 | break; 272 | 273 | case BLE_ADV_EVT_FAST: 274 | NRF_LOG_INFO("Fast advertising."); 275 | break; 276 | 277 | default: 278 | break; 279 | } 280 | } 281 | 282 | static void advertising_start(void) { 283 | ret_code_t err_code; 284 | 285 | err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST); 286 | APP_ERROR_CHECK(err_code); 287 | } 288 | 289 | static void timers_start(void) { 290 | ret_code_t err_code; 291 | 292 | err_code = app_timer_start(m_scan_timer_id, SCAN_DELAY_TICKS, NULL); 293 | APP_ERROR_CHECK(err_code); 294 | } 295 | 296 | /* 297 | * Firmware section. 298 | */ 299 | static void firmware_init(void) { 300 | NRF_LOG_INFO("firmware_init."); 301 | 302 | for (int i = 0; i < MATRIX_ROW_NUM; i++) { 303 | for (int j = 0; j < MATRIX_COL_NUM; j++) { 304 | m_debounce[i][j] = KEY_PRESS_DEBOUNCE; 305 | } 306 | } 307 | } 308 | 309 | static void scan_matrix_task(void *p_data, uint16_t size) { 310 | UNUSED_PARAMETER(p_data); 311 | UNUSED_PARAMETER(size); 312 | 313 | ret_code_t err_code; 314 | bool key_changed = false; 315 | 316 | for (int col = 0; col < MATRIX_COL_NUM; col++) { 317 | nrf_gpio_pin_set(COLS[col]); 318 | nrf_delay_us(PIN_SET_DELAY); 319 | 320 | for (int row = 0; row < MATRIX_ROW_NUM; row++) { 321 | bool pressed = nrf_gpio_pin_read(ROWS[row]) > 0; 322 | 323 | if (m_key_pressed[row][col] == pressed) { 324 | if (pressed) { 325 | m_debounce[row][col] = KEY_RELEASE_DEBOUNCE; 326 | } else { 327 | m_debounce[row][col] = KEY_PRESS_DEBOUNCE; 328 | } 329 | } else { 330 | if (m_debounce[row][col] <= 0) { 331 | if (pressed) { 332 | // On key press. 333 | m_key_pressed[row][col] = true; 334 | m_debounce[row][col] = KEY_RELEASE_DEBOUNCE; 335 | key_changed = true; 336 | 337 | if (m_active_key_index_count < SLAVE_KEY_NUM) { 338 | int i = 0; 339 | 340 | while (i < m_active_key_index_count && m_active_key_index[i] != MATRIX[row][col]) { 341 | i++; 342 | } 343 | 344 | if (i == m_active_key_index_count) { 345 | m_active_key_index[m_active_key_index_count++] = MATRIX[row][col]; 346 | } 347 | } 348 | } else { 349 | // On key release. 350 | m_key_pressed[row][col] = false; 351 | m_debounce[row][col] = KEY_PRESS_DEBOUNCE; 352 | key_changed = true; 353 | int i = 0; 354 | 355 | while (i < m_active_key_index_count && m_active_key_index[i] != MATRIX[row][col]) { 356 | i++; 357 | } 358 | 359 | if (i < m_active_key_index_count) { 360 | while (i < m_active_key_index_count) { 361 | m_active_key_index[i] = m_active_key_index[i + 1]; 362 | i++; 363 | } 364 | 365 | m_active_key_index_count--; 366 | } 367 | } 368 | } else { 369 | m_debounce[row][col] -= SCAN_DELAY; 370 | } 371 | } 372 | } 373 | 374 | nrf_gpio_pin_clear(COLS[col]); 375 | } 376 | 377 | if (key_changed) { 378 | m_low_power_mode_counter = LOW_POWER_MODE_DELAY; 379 | 380 | // Set active key index characteristics. 381 | kb_link_active_key_index_update(&m_kb_link, (uint8_t *)m_active_key_index, m_active_key_index_count); 382 | } else { 383 | m_low_power_mode_counter -= SCAN_DELAY; 384 | } 385 | 386 | if (m_low_power_mode_counter <= 0) { 387 | m_low_power_mode_counter = LOW_POWER_MODE_DELAY; 388 | low_power_mode_start(); 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/keycodes.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEYCODES_H_ 2 | #define _KEYCODES_H_ 3 | 4 | /* 5 | * Keycodes less than 0xFFFF used for HID keycodes 6 | * Keycode that will be sent to a computer 7 | */ 8 | 9 | // Keycodes. 10 | #define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL) 11 | 12 | // Modifier keys. 13 | #define IS_MOD(code) ((code) & 0xFF00) 14 | #define MOD(mod_code, code) ((mod_code) | (code)) 15 | #define MOD_BIT(code) (((code) & 0xFF00) >> 8) 16 | #define MOD_CODE(code) ((code) & 0xFF) 17 | 18 | // Devices connection. 19 | #define IS_DEVICE_CONNECTION(code) (KC_DEVICE_1 <= (code) && (code) <= KC_DEVICE_CONNECT) 20 | #define IS_DEVICE_CONNECT(code) (code == KC_DEVICE_CONNECT) 21 | #define IS_DEVICE_SWITCHING(code) (KC_DEVICE_1 <= (code) && (code) <= KC_DEVICE_3) 22 | #define DEVICE(code) ((code) - KC_DEVICE_1) 23 | 24 | // Layer switching. 25 | #define IS_LAYER(code) (KC_LAYER_BASE <= (code) && (code) <= KC_LAYER_F) 26 | #define LAYER(code) ((code) - KC_LAYER_BASE) 27 | 28 | // Consumer control. 29 | #define IS_CONSUMER(code) (KC_AUDIO_MUTE <= (code) && (code) <= KC_BRIGHTNESS_DOWN) 30 | #define CONSUMER_CODE(code) (CC_KEYCODES[(code - KC_AUDIO_MUTE)]) 31 | 32 | /* 33 | * Short names for ease of definition of keymap 34 | */ 35 | #define KC_LCTL KC_LCTRL 36 | #define KC_RCTL KC_RCTRL 37 | #define KC_LSFT KC_LSHIFT 38 | #define KC_RSFT KC_RSHIFT 39 | #define KC_ESC KC_ESCAPE 40 | #define KC_BSPC KC_BSPACE 41 | #define KC_ENT KC_ENTER 42 | #define KC_DEL KC_DELETE 43 | #define KC_INS KC_INSERT 44 | #define KC_CAPS KC_CAPSLOCK 45 | #define KC_CLCK KC_CAPSLOCK 46 | #define KC_RGHT KC_RIGHT 47 | #define KC_PGDN KC_PGDOWN 48 | #define KC_PSCR KC_PSCREEN 49 | #define KC_SLCK KC_SCROLLLOCK 50 | #define KC_PAUS KC_PAUSE 51 | #define KC_BRK KC_PAUSE 52 | #define KC_NLCK KC_NUMLOCK 53 | #define KC_SPC KC_SPACE 54 | #define KC_MINS KC_MINUS 55 | #define KC_EQL KC_EQUAL 56 | #define KC_GRV KC_GRAVE 57 | #define KC_RBRC KC_RBRACKET 58 | #define KC_LBRC KC_LBRACKET 59 | #define KC_COMM KC_COMMA 60 | #define KC_BSLS KC_BSLASH 61 | #define KC_SLSH KC_SLASH 62 | #define KC_SCLN KC_SCOLON 63 | #define KC_QUOT KC_QUOTE 64 | #define KC_APP KC_APPLICATION 65 | #define KC_NUHS KC_NONUS_HASH 66 | #define KC_NUBS KC_NONUS_BSLASH 67 | #define KC_LCAP KC_LOCKING_CAPS 68 | #define KC_LNUM KC_LOCKING_NUM 69 | #define KC_LSCR KC_LOCKING_SCROLL 70 | #define KC_ERAS KC_ALT_ERASE 71 | #define KC_CLR KC_CLEAR 72 | 73 | // Keypad. 74 | #define KC_P1 KC_KP_1 75 | #define KC_P2 KC_KP_2 76 | #define KC_P3 KC_KP_3 77 | #define KC_P4 KC_KP_4 78 | #define KC_P5 KC_KP_5 79 | #define KC_P6 KC_KP_6 80 | #define KC_P7 KC_KP_7 81 | #define KC_P8 KC_KP_8 82 | #define KC_P9 KC_KP_9 83 | #define KC_P0 KC_KP_0 84 | #define KC_PDOT KC_KP_DOT 85 | #define KC_PCMM KC_KP_COMMA 86 | #define KC_PSLS KC_KP_SLASH 87 | #define KC_PAST KC_KP_ASTERISK 88 | #define KC_PMNS KC_KP_MINUS 89 | #define KC_PPLS KC_KP_PLUS 90 | #define KC_PEQL KC_KP_EQUAL 91 | #define KC_PENT KC_KP_ENTER 92 | 93 | // Unix function key. 94 | #define KC_EXEC KC_EXECUTE 95 | #define KC_SLCT KC_SELECT 96 | #define KC_AGIN KC_AGAIN 97 | #define KC_PSTE KC_PASTE 98 | 99 | // Transparent. 100 | #define KC_TRNS KC_TRANSPARENT 101 | #define _______ KC_TRANSPARENT 102 | 103 | // No operation. 104 | #define XXXXXXX KC_NO 105 | 106 | // Volume. 107 | #define KC_VOLU KC_AUDIO_VOL_UP 108 | #define KC_VOLD KC_AUDIO_VOL_DOWN 109 | #define KC_MUTE KC_AUDIO_MUTE 110 | 111 | // Display. 112 | #define KC_BRIU KC_BRIGHTNESS_UP 113 | #define KC_BRID KC_BRIGHTNESS_DOWN 114 | 115 | // Devices connection. 116 | #define KC_DVC1 KC_DEVICE_1 117 | #define KC_DVC2 KC_DEVICE_2 118 | #define KC_DVC3 KC_DEVICE_3 119 | #define KC_DVCN KC_DEVICE_CONNECT 120 | 121 | // Layer switching. 122 | #define KC_L1 KC_LAYER_1 123 | #define KC_L2 KC_LAYER_2 124 | #define KC_L3 KC_LAYER_3 125 | #define KC_L4 KC_LAYER_4 126 | #define KC_L5 KC_LAYER_5 127 | #define KC_L6 KC_LAYER_6 128 | #define KC_L7 KC_LAYER_7 129 | #define KC_L8 KC_LAYER_8 130 | #define KC_L9 KC_LAYER_9 131 | #define KC_LA KC_LAYER_A 132 | #define KC_LB KC_LAYER_B 133 | #define KC_LC KC_LAYER_C 134 | #define KC_LD KC_LAYER_D 135 | #define KC_LE KC_LAYER_E 136 | #define KC_LF KC_LAYER_F 137 | 138 | /* 139 | * Shifted keys 140 | */ 141 | // Macros. 142 | #define LSHIFT(code) MOD(KC_LSHIFT, code) 143 | #define LSFT(code) LSHIFT(code) 144 | 145 | // Keys. 146 | #define KC_TILDE LSFT(KC_GRAVE) 147 | #define KC_EXCLAIM LSFT(KC_1) 148 | #define KC_AT LSFT(KC_2) 149 | #define KC_HASH LSFT(KC_3) 150 | #define KC_DOLLAR LSFT(KC_4) 151 | #define KC_PERCENT LSFT(KC_5) 152 | #define KC_CIRCUMFLEX LSFT(KC_6) 153 | #define KC_AMPERSAND LSFT(KC_7) 154 | #define KC_ASTERISK LSFT(KC_8) 155 | #define KC_LEFT_PAREN LSFT(KC_9) 156 | #define KC_RIGHT_PAREN LSFT(KC_0) 157 | #define KC_UNDERSCORE LSFT(KC_MINUS) 158 | #define KC_PLUS LSFT(KC_EQUAL) 159 | #define KC_LEFT_CURLY_BRACE LSFT(KC_LBRACKET) 160 | #define KC_RIGHT_CURLY_BRACE LSFT(KC_RBRACKET) 161 | #define KC_PIPE LSFT(KC_BSLASH) 162 | #define KC_COLON LSFT(KC_SCOLON) 163 | #define KC_DOUBLE_QUOTE LSFT(KC_QUOTE) 164 | #define KC_LEFT_ANGLE_BRACKET LSFT(KC_COMMA) 165 | #define KC_RIGHT_ANGLE_BRACKET LSFT(KC_DOT) 166 | #define KC_QUESTION LSFT(KC_SLASH) 167 | 168 | /* 169 | * Shifted keys short version 170 | */ 171 | #define KC_TILD KC_TILDE 172 | #define KC_EXLM KC_EXCLAIM 173 | #define KC_DLR KC_DOLLAR 174 | #define KC_PERC KC_PERCENT 175 | #define KC_CIRC KC_CIRCUMFLEX 176 | #define KC_AMPR KC_AMPERSAND 177 | #define KC_ASTR KC_ASTERISK 178 | #define KC_LPRN KC_LEFT_PAREN 179 | #define KC_RPRN KC_RIGHT_PAREN 180 | #define KC_UNDS KC_UNDERSCORE 181 | #define KC_LCBR KC_LEFT_CURLY_BRACE 182 | #define KC_RCBR KC_RIGHT_CURLY_BRACE 183 | #define KC_COLN KC_COLON 184 | #define KC_DQUO KC_DOUBLE_QUOTE 185 | #define KC_DQT KC_DOUBLE_QUOTE 186 | #define KC_LABK KC_LEFT_ANGLE_BRACKET 187 | #define KC_LT KC_LEFT_ANGLE_BRACKET 188 | #define KC_RABK KC_RIGHT_ANGLE_BRACKET 189 | #define KC_GT KC_RIGHT_ANGLE_BRACKET 190 | #define KC_QUES KC_QUESTION 191 | 192 | /* 193 | * Special keys. 194 | */ 195 | #define KC_PREVWORD MOD(KC_LCTRL, KC_LEFT) 196 | #define KC_NEXTWORD MOD(KC_LCTRL, KC_RIGHT) 197 | #define KC_WINLOCK MOD(KC_LGUI, KC_L) 198 | 199 | /* 200 | * Special keys short version. 201 | */ 202 | #define KC_PRWD KC_PREVWORD 203 | #define KC_NXWD KC_NEXTWORD 204 | #define KC_WNLK KC_WINLOCK 205 | 206 | // USB HID Keyboard/Keypad Usage(0x07). 207 | enum basic_hid_keycodes { 208 | KC_NO = 0x00, 209 | KC_ROLL_OVER, 210 | KC_POST_FAIL, 211 | KC_UNDEFINED, // Used for KC_TRANSPARENT. 212 | KC_A, 213 | KC_B, 214 | KC_C, 215 | KC_D, 216 | KC_E, 217 | KC_F, 218 | KC_G, 219 | KC_H, 220 | KC_I, 221 | KC_J, 222 | KC_K, 223 | KC_L, 224 | KC_M, // 0x10. 225 | KC_N, 226 | KC_O, 227 | KC_P, 228 | KC_Q, 229 | KC_R, 230 | KC_S, 231 | KC_T, 232 | KC_U, 233 | KC_V, 234 | KC_W, 235 | KC_X, 236 | KC_Y, 237 | KC_Z, 238 | KC_1, 239 | KC_2, 240 | KC_3, // 0x20. 241 | KC_4, 242 | KC_5, 243 | KC_6, 244 | KC_7, 245 | KC_8, 246 | KC_9, 247 | KC_0, 248 | KC_ENTER, 249 | KC_ESCAPE, 250 | KC_BSPACE, 251 | KC_TAB, 252 | KC_SPACE, 253 | KC_MINUS, 254 | KC_EQUAL, 255 | KC_LBRACKET, 256 | KC_RBRACKET, // 0x30. 257 | KC_BSLASH, // \ (and |). 258 | KC_NONUS_HASH, // Non-US # and ~ (Typically near the Enter key). 259 | KC_SCOLON, // ; and :. 260 | KC_QUOTE, // ' and ". 261 | KC_GRAVE, // Grave accent and tilde. 262 | KC_COMMA, // , and <. 263 | KC_DOT, // . and >. 264 | KC_SLASH, // / and ?. 265 | KC_CAPSLOCK, 266 | KC_F1, 267 | KC_F2, 268 | KC_F3, 269 | KC_F4, 270 | KC_F5, 271 | KC_F6, 272 | KC_F7, // 0x40. 273 | KC_F8, 274 | KC_F9, 275 | KC_F10, 276 | KC_F11, 277 | KC_F12, 278 | KC_PSCREEN, 279 | KC_SCROLLLOCK, 280 | KC_PAUSE, 281 | KC_INSERT, 282 | KC_HOME, 283 | KC_PGUP, 284 | KC_DELETE, 285 | KC_END, 286 | KC_PGDOWN, 287 | KC_RIGHT, 288 | KC_LEFT, // 0x50. 289 | KC_DOWN, 290 | KC_UP, 291 | KC_NUMLOCK, 292 | KC_KP_SLASH, 293 | KC_KP_ASTERISK, 294 | KC_KP_MINUS, 295 | KC_KP_PLUS, 296 | KC_KP_ENTER, 297 | KC_KP_1, 298 | KC_KP_2, 299 | KC_KP_3, 300 | KC_KP_4, 301 | KC_KP_5, 302 | KC_KP_6, 303 | KC_KP_7, 304 | KC_KP_8, // 0x60. 305 | KC_KP_9, 306 | KC_KP_0, 307 | KC_KP_DOT, 308 | KC_NONUS_BSLASH, // Non-US \ and | (Typically near the Left-Shift key). 309 | KC_APPLICATION, 310 | KC_POWER, 311 | KC_KP_EQUAL, 312 | KC_F13, 313 | KC_F14, 314 | KC_F15, 315 | KC_F16, 316 | KC_F17, 317 | KC_F18, 318 | KC_F19, 319 | KC_F20, 320 | KC_F21, // 0x70. 321 | KC_F22, 322 | KC_F23, 323 | KC_F24, 324 | KC_EXECUTE, 325 | KC_HELP, 326 | KC_MENU, 327 | KC_SELECT, 328 | KC_STOP, 329 | KC_AGAIN, 330 | KC_UNDO, 331 | KC_CUT, 332 | KC_COPY, 333 | KC_PASTE, 334 | KC_FIND, 335 | KC_VOL_MUTE, 336 | KC_VOL_UP, // 0x80. 337 | KC_VOL_DOWN, 338 | KC_LOCKING_CAPS, // Locking Caps Lock. 339 | KC_LOCKING_NUM, // Locking Num Lock. 340 | KC_LOCKING_SCROLL, // Locking Scroll Lock. 341 | KC_KP_COMMA, 342 | KC_KP_EQUAL_AS400, // Equal sign on AS/400. 343 | KC_INT1, 344 | KC_INT2, 345 | KC_INT3, 346 | KC_INT4, 347 | KC_INT5, 348 | KC_INT6, 349 | KC_INT7, 350 | KC_INT8, 351 | KC_INT9, 352 | KC_LANG1, // 0x90. 353 | KC_LANG2, 354 | KC_LANG3, 355 | KC_LANG4, 356 | KC_LANG5, 357 | KC_LANG6, 358 | KC_LANG7, 359 | KC_LANG8, 360 | KC_LANG9, 361 | KC_ALT_ERASE, 362 | KC_SYSREQ, 363 | KC_CANCEL, 364 | KC_CLEAR, 365 | KC_PRIOR, 366 | KC_RETURN, 367 | KC_SEPARATOR, 368 | KC_OUT, // 0xA0. 369 | KC_OPER, 370 | KC_CLEAR_AGAIN, 371 | KC_CRSEL, 372 | KC_EXSEL, // 0xA4. 373 | 374 | // 0xA5 - 0xFF used for special keycodes. 375 | 376 | #if 0 377 | // NOTE: Following codes(0xB0-DD) are not used. Leave them for reference. 378 | KC_KP_00 = 0xB0, 379 | KC_KP_000, 380 | KC_THOUSANDS_SEPARATOR, 381 | KC_DECIMAL_SEPARATOR, 382 | KC_CURRENCY_UNIT, 383 | KC_CURRENCY_SUB_UNIT, 384 | KC_KP_LPAREN, 385 | KC_KP_RPAREN, 386 | KC_KP_LCBRACKET, // {. 387 | KC_KP_RCBRACKET, // }. 388 | KC_KP_TAB, 389 | KC_KP_BSPACE, 390 | KC_KP_A, 391 | KC_KP_B, 392 | KC_KP_C, 393 | KC_KP_D, 394 | KC_KP_E, // 0xC0. 395 | KC_KP_F, 396 | KC_KP_XOR, 397 | KC_KP_HAT, 398 | KC_KP_PERC, 399 | KC_KP_LT, 400 | KC_KP_GT, 401 | KC_KP_AND, 402 | KC_KP_LAZYAND, 403 | KC_KP_OR, 404 | KC_KP_LAZYOR, 405 | KC_KP_COLON, 406 | KC_KP_HASH, 407 | KC_KP_SPACE, 408 | KC_KP_ATMARK, 409 | KC_KP_EXCLAMATION, 410 | KC_KP_MEM_STORE, // 0xD0. 411 | KC_KP_MEM_RECALL, 412 | KC_KP_MEM_CLEAR, 413 | KC_KP_MEM_ADD, 414 | KC_KP_MEM_SUB, 415 | KC_KP_MEM_MUL, 416 | KC_KP_MEM_DIV, 417 | KC_KP_PLUS_MINUS, 418 | KC_KP_CLEAR, 419 | KC_KP_CLEAR_ENTRY, 420 | KC_KP_BINARY, 421 | KC_KP_OCTAL, 422 | KC_KP_DECIMAL, 423 | KC_KP_HEXADECIMAL, // 0xDD. 424 | #endif 425 | }; 426 | 427 | // Special keycodes. 428 | enum special_keycodes { 429 | KC_TRANSPARENT = 0x03, 430 | 431 | // Devices connection. 432 | KC_DEVICE_1 = 0xA5, 433 | KC_DEVICE_2, 434 | KC_DEVICE_3, 435 | KC_DEVICE_CONNECT, 436 | 437 | // Layer switching. 438 | KC_LAYER_BASE, 439 | KC_LAYER_1, 440 | KC_LAYER_2, 441 | KC_LAYER_3, 442 | KC_LAYER_4, 443 | KC_LAYER_5, 444 | KC_LAYER_6, 445 | KC_LAYER_7, 446 | KC_LAYER_8, 447 | KC_LAYER_9, 448 | KC_LAYER_A, 449 | KC_LAYER_B, 450 | KC_LAYER_C, 451 | KC_LAYER_D, 452 | KC_LAYER_E, 453 | KC_LAYER_F, 454 | 455 | // Consumer control. Key definitions must be in order with CC_KEYCODES array. 456 | KC_AUDIO_MUTE, 457 | KC_AUDIO_VOL_UP, 458 | KC_AUDIO_VOL_DOWN, 459 | KC_MEDIA_FAST_FORWARD, 460 | KC_MEDIA_REWIND, 461 | KC_MEDIA_NEXT_TRACK, 462 | KC_MEDIA_PREV_TRACK, 463 | KC_MEDIA_STOP, 464 | KC_MEDIA_EJECT, 465 | KC_MEDIA_PLAY_PAUSE, 466 | KC_MEDIA_SELECT, 467 | KC_MAIL, 468 | KC_CALCULATOR, 469 | KC_MY_COMPUTER, 470 | KC_WWW_SEARCH, 471 | KC_WWW_HOME, 472 | KC_WWW_BACK, 473 | KC_WWW_FORWARD, 474 | KC_WWW_STOP, 475 | KC_WWW_REFRESH, 476 | KC_WWW_FAVORITES, 477 | KC_BRIGHTNESS_UP, 478 | KC_BRIGHTNESS_DOWN 479 | }; 480 | 481 | // Modifier keycodes. 482 | enum modifier_keycodes { 483 | /* 484 | * Modifiers or keys with modifiers. 485 | * 0x{00}00 - 0x{FF}00 used for modifier key. 486 | * Pattern: 0x{AB}CD 487 | * Digit A and B is for midifiers 488 | * Digit C and D is for keycodes 489 | */ 490 | 491 | // Modifiers. 492 | KC_LCTRL = 0x0100, 493 | KC_LSHIFT = 0x0200, 494 | KC_LALT = 0x0400, 495 | KC_LGUI = 0x0800, 496 | KC_RCTRL = 0x1000, 497 | KC_RSHIFT = 0x2000, 498 | KC_RALT = 0x4000, 499 | KC_RGUI = 0x8000 500 | }; 501 | 502 | // Consumer Control keycodes. Array items must be in order with Consumer Control keys definitions. 503 | const uint16_t CC_KEYCODES[] = { 504 | // Audio control. 505 | 0x00E2, // AUDIO_MUTE. 506 | 0x00E9, // AUDIO_VOL_UP. 507 | 0x00EA, // AUDIO_VOL_DOWN. 508 | 509 | // Media control. 510 | 0x00B3, // TRANSPORT_FAST_FORWARD. 511 | 0x00B4, // TRANSPORT_REWIND. 512 | 0x00B5, // TRANSPORT_NEXT_TRACK. 513 | 0x00B6, // TRANSPORT_PREV_TRACK. 514 | 0x00B7, // TRANSPORT_STOP. 515 | 0x00CC, // TRANSPORT_STOP_EJECT. 516 | 0x00CD, // TRANSPORT_PLAY_PAUSE. 517 | 518 | // Application launch.j 519 | 0x0183, // AL_CC_CONFIG. 520 | 0x018A, // AL_EMAIL. 521 | 0x0192, // AL_CALCULATOR. 522 | 0x0194, // AL_LOCAL_BROWSER. 523 | 524 | // Application control. 525 | 0x0221, // AC_SEARCH. 526 | 0x0223, // AC_HOME. 527 | 0x0224, // AC_BACK. 528 | 0x0225, // AC_FORWARD. 529 | 0x0226, // AC_STOP. 530 | 0x0227, // AC_REFRESH. 531 | 0x022A, // AC_BOOKMARKS. 532 | 533 | // Display control. 534 | 0x006F, // BRIGHTNESS_UP. 535 | 0x0070 // BRIGHTNESS_DOWN. 536 | }; 537 | 538 | // Layers. 539 | enum layers { 540 | _BASE_LAYER, 541 | _LAYER_1, 542 | _LAYER_2, 543 | _LAYER_3, 544 | _LAYER_4, 545 | _LAYER_5, 546 | _LAYER_6, 547 | _LAYER_7, 548 | _LAYER_8, 549 | _LAYER_9, 550 | _LAYER_A, 551 | _LAYER_B, 552 | _LAYER_C, 553 | _LAYER_D, 554 | _LAYER_E, 555 | _LAYER_F 556 | }; 557 | 558 | // Layers short name. 559 | #define _BS _BASE_LAYER 560 | #define _L1 _LAYER_1 561 | #define _L2 _LAYER_2 562 | #define _L3 _LAYER_3 563 | #define _L4 _LAYER_4 564 | #define _L5 _LAYER_5 565 | #define _L6 _LAYER_6 566 | #define _L7 _LAYER_7 567 | #define _L8 _LAYER_8 568 | #define _L9 _LAYER_9 569 | #define _LA _LAYER_A 570 | #define _LB _LAYER_B 571 | #define _LC _LAYER_C 572 | #define _LD _LAYER_D 573 | #define _LE _LAYER_E 574 | #define _LF _LAYER_F 575 | 576 | #endif 577 | -------------------------------------------------------------------------------- /bmk_sdk.emProject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 290 | 294 | 298 | 299 | -------------------------------------------------------------------------------- /src/main_master.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "nrf_ble_gq.h" 5 | #include "app_error.h" 6 | #include "app_scheduler.h" 7 | #include "app_timer.h" 8 | #include "ble_advdata.h" 9 | #include "ble_advertising.h" 10 | #include "ble_conn_state.h" 11 | #include "ble_dis.h" 12 | #include "ble_err.h" 13 | #include "ble_hci.h" 14 | #include "ble_hids.h" 15 | #include "ble_srv_common.h" 16 | #include "ble.h" 17 | #include "fds.h" 18 | #include "nordic_common.h" 19 | #include "nrf_ble_gatt.h" 20 | #include "nrf_delay.h" 21 | #include "nrf_gpio.h" 22 | #include "nrf_log_ctrl.h" 23 | #include "nrf_log.h" 24 | #include "nrf_sdh_ble.h" 25 | #include "nrf_sdh_soc.h" 26 | #include "nrf_sdh.h" 27 | #include "nrf.h" 28 | #include "peer_manager_handler.h" 29 | #include "peer_manager.h" 30 | 31 | #include "config/keyboard.h" 32 | #include "config/keymap.h" 33 | #include "error_handler/error_handler.h" 34 | #include "firmware_config.h" 35 | #include "low_power/low_power.h" 36 | #include "shared/shared.h" 37 | 38 | #ifdef HAS_SLAVE 39 | #include "ble_db_discovery.h" 40 | #include "nrf_ble_scan.h" 41 | 42 | #include "kb_link/kb_link_c.h" 43 | #endif 44 | 45 | /* 46 | * Variables declaration. 47 | */ 48 | // nRF52 variables. 49 | APP_TIMER_DEF(m_scan_timer_id); 50 | NRF_BLE_GQ_DEF(m_ble_gatt_queue, NRF_SDH_BLE_CENTRAL_LINK_COUNT, NRF_BLE_GQ_QUEUE_SIZE); 51 | NRF_BLE_GATT_DEF(m_gatt); 52 | BLE_ADVERTISING_DEF(m_advertising); 53 | BLE_HIDS_DEF(m_hids, NRF_SDH_BLE_TOTAL_LINK_COUNT, KB_INPUT_REPORT_MAX_LEN, CC_INPUT_REPORT_MAX_LEN, OUTPUT_REPORT_MAX_LEN); 54 | 55 | #ifdef HAS_SLAVE 56 | NRF_BLE_SCAN_DEF(m_scan); 57 | BLE_DB_DISCOVERY_DEF(m_db_disc); 58 | KB_LINK_C_DEF(m_kb_link_c); 59 | #endif 60 | 61 | static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; // Handle of the current connection. 62 | static pm_peer_id_t m_peer_id = PM_PEER_ID_INVALID; // Device reference handle to the current bonded central. 63 | static ble_uuid_t m_adv_uuid = {BLE_UUID_HUMAN_INTERFACE_DEVICE_SERVICE, BLE_UUID_TYPE_BLE}; 64 | static bool volatile m_fds_initialized = false; 65 | 66 | // HID variables. 67 | static bool m_hids_in_boot_mode = false; // Current protocol mode. 68 | static bool m_caps_lock_on = false; // Variable to indicate if Caps Lock is turned on. 69 | 70 | // Firmware variables. 71 | const uint8_t ROWS[MATRIX_ROW_NUM] = MATRIX_ROW_PINS; 72 | const uint8_t COLS[MATRIX_COL_NUM] = MATRIX_COL_PINS; 73 | const int8_t MATRIX[MATRIX_ROW_NUM][MATRIX_COL_NUM] = MATRIX_DEFINE; 74 | 75 | static bool m_key_pressed[MATRIX_ROW_NUM][MATRIX_COL_NUM] = {false}; 76 | static int m_debounce[MATRIX_ROW_NUM][MATRIX_COL_NUM]; 77 | static int m_low_power_mode_counter = LOW_POWER_MODE_DELAY; 78 | 79 | typedef enum { 80 | KEY_TYPE_NOT_TRANSLATED, 81 | KEY_TYPE_NO_REPORT, 82 | KEY_TYPE_KEY, 83 | KEY_TYPE_MODIFIER, 84 | KEY_TYPE_KEY_WITH_MODIFIER, 85 | KEY_TYPE_CONSUMER 86 | } key_type_t; 87 | 88 | typedef struct { 89 | uint8_t modifiers; 90 | uint8_t key; 91 | } kb_data_t; 92 | 93 | typedef union { 94 | kb_data_t kb; 95 | uint16_t cc; 96 | } key_data_t; 97 | 98 | typedef struct { 99 | int8_t index; 100 | uint8_t source; 101 | bool should_delete; 102 | key_type_t type; 103 | key_data_t data; 104 | } key_t; 105 | 106 | static key_t m_keys[KEY_NUM]; 107 | static int m_key_count = 0; 108 | 109 | static int8_t m_active_key_index[MASTER_KEY_NUM] = {0}; 110 | static uint16_t m_active_key_index_count = 0; 111 | 112 | // Device connection. 113 | typedef struct { 114 | uint8_t current_device; 115 | uint8_t addrs[3]; 116 | pm_peer_id_t peer_ids[3]; 117 | uint16_t padding; // Not used, needed to ensure size of this structure is multiple of 4 bytes. 118 | } device_connection_t; 119 | 120 | static device_connection_t m_device_connection = { 121 | .current_device = 0, 122 | .addrs = {0}, 123 | .peer_ids = { PM_PEER_ID_INVALID, PM_PEER_ID_INVALID, PM_PEER_ID_INVALID } 124 | }; 125 | 126 | static const fds_record_t m_device_connection_record = { 127 | .file_id = CONFIG_FILE_ID, 128 | .key = DEVICE_CONNECTION_KEY, 129 | .data.p_data = &m_device_connection, 130 | .data.length_words = (sizeof(m_device_connection) + 3) / sizeof(uint32_t) // length_words is multiple of 4 bytes. 131 | }; 132 | 133 | static fds_record_desc_t m_device_connection_record_desc = {0}; 134 | static bool m_reset_device_connection_update = false; 135 | 136 | // HID report. 137 | typedef enum { 138 | HID_TYPE_KB_REPORT, 139 | HID_TYPE_CC_REPORT 140 | } hid_report_type_t; 141 | 142 | typedef union { 143 | uint8_t kb[KB_INPUT_REPORT_MAX_LEN]; 144 | uint16_t cc; 145 | } hid_report_data_t; 146 | 147 | typedef struct { 148 | hid_report_type_t type; 149 | hid_report_data_t data; 150 | } hid_report_t; 151 | 152 | typedef struct { 153 | hid_report_t reports[HID_REPORT_BUFFER_NUM]; 154 | int8_t start; 155 | int8_t end; 156 | int8_t count; 157 | } hid_report_buffer_t; 158 | 159 | static hid_report_buffer_t m_hid_buffer = {0}; 160 | 161 | /* 162 | * Functions declaration. 163 | */ 164 | // nRF52 functions. 165 | static void timers_init(void); 166 | static void scan_timeout_handler(void *p_context); 167 | static void ble_stack_init(void); 168 | static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context); 169 | static void gatt_init(void); 170 | static void dis_init(void); 171 | static void hids_init(void); 172 | static void hids_evt_handler(ble_hids_t *p_hids, ble_hids_evt_t *p_evt); 173 | static void on_hid_rep_char_write(ble_hids_evt_t *p_evt); 174 | static void advertising_init(void); 175 | static void adv_evt_handler(ble_adv_evt_t ble_adv_evt); 176 | static void identities_set(pm_peer_id_list_skip_t skip); 177 | static void peer_manager_init(void); 178 | static void pm_evt_handler(pm_evt_t const *p_evt); 179 | static void gap_address_init(void); 180 | static void flash_data_init(void); 181 | static void fds_evt_handler(fds_evt_t const * p_evt); 182 | static void reset_device(void); 183 | static void peers_refresh(void); 184 | static void set_whitelist(void); 185 | static void advertising_start(void); 186 | static void timers_start(void); 187 | static void hids_send_report(hid_report_t *p_report); 188 | #ifdef HAS_SLAVE 189 | static void db_discovery_init(void); 190 | static void db_disc_handler(ble_db_discovery_evt_t *p_evt); 191 | static void kbl_c_init(void); 192 | static void kbl_c_evt_handler(kb_link_c_t *p_kb_link_c, kb_link_c_evt_t const * p_evt); 193 | static void scan_init(void); 194 | static void scan_start(void); 195 | #endif 196 | 197 | // Firmware functions. 198 | static void firmware_init(void); 199 | static void scan_matrix_task(void *p_data, uint16_t size); 200 | static bool update_key_index(int8_t *p_key_index, uint16_t size, uint8_t source); 201 | static void translate_key_index(void); 202 | static void generate_hid_report(void); 203 | #ifdef HAS_SLAVE 204 | static void process_slave_key_index(int8_t *p_key_index, uint16_t size); 205 | static void clear_slave_key_index(void); 206 | #endif 207 | 208 | int main(void) { 209 | // Initialize. 210 | // nRF52. 211 | log_init(); 212 | timers_init(); 213 | power_management_init(); 214 | ble_stack_init(); 215 | conn_evt_length_ext_init(); 216 | scheduler_init(); 217 | gap_params_init(); 218 | gatt_init(); 219 | dis_init(); 220 | hids_init(); 221 | #ifdef HAS_SLAVE 222 | db_discovery_init(); 223 | kbl_c_init(); 224 | scan_init(); 225 | #endif 226 | 227 | // Init advertising after all services. 228 | advertising_init(); 229 | conn_params_init(); 230 | peer_manager_init(); 231 | gap_address_init(); 232 | peers_refresh(); 233 | set_whitelist(); // Set whitelist once when device newly started. 234 | 235 | // Firmware. 236 | pins_init(); 237 | firmware_init(); 238 | low_power_mode_init(&m_scan_timer_id, scan_timeout_handler); 239 | 240 | // Start. 241 | advertising_start(); 242 | #ifdef HAS_SLAVE 243 | scan_start(); 244 | #endif 245 | timers_start(); 246 | 247 | NRF_LOG_INFO("main; started."); 248 | 249 | // Enter main loop. 250 | for (;;) { 251 | idle_state_handle(); 252 | } 253 | } 254 | 255 | /* 256 | * nRF52 section. 257 | */ 258 | static void timers_init(void) { 259 | ret_code_t err_code; 260 | 261 | err_code = app_timer_init(); 262 | APP_ERROR_CHECK(err_code); 263 | 264 | // Matrix scan timer. 265 | err_code = app_timer_create(&m_scan_timer_id, APP_TIMER_MODE_REPEATED, scan_timeout_handler); 266 | APP_ERROR_CHECK(err_code); 267 | } 268 | 269 | static void scan_timeout_handler(void *p_context) { 270 | UNUSED_PARAMETER(p_context); 271 | 272 | ret_code_t err_code; 273 | 274 | err_code = app_sched_event_put(NULL, 0, scan_matrix_task); 275 | APP_ERROR_CHECK(err_code); 276 | } 277 | 278 | static void ble_stack_init(void) { 279 | ret_code_t err_code; 280 | 281 | err_code = nrf_sdh_enable_request(); 282 | APP_ERROR_CHECK(err_code); 283 | 284 | // Configure the BLE stack using the default settings. Fetch the start address of the application RAM. 285 | uint32_t ram_start = 0; 286 | err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); 287 | APP_ERROR_CHECK(err_code); 288 | 289 | // Enable BLE stack. 290 | err_code = nrf_sdh_ble_enable(&ram_start); 291 | APP_ERROR_CHECK(err_code); 292 | 293 | // Register a handler for BLE events. 294 | NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); 295 | } 296 | 297 | static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context) { 298 | ret_code_t err_code; 299 | 300 | switch (p_ble_evt->header.evt_id) { 301 | case BLE_GAP_EVT_CONNECTED: 302 | NRF_LOG_INFO("Connected."); 303 | if (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_PERIPH) { 304 | NRF_LOG_INFO("As peripheral."); 305 | NRF_LOG_INFO("Conn params; conn interval: %i, conn sup timeout: %i.", p_ble_evt->evt.gap_evt.params.connected.conn_params.min_conn_interval * 1.25, p_ble_evt->evt.gap_evt.params.connected.conn_params.conn_sup_timeout * 10); 306 | 307 | m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; 308 | } 309 | #ifdef HAS_SLAVE 310 | else if (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_CENTRAL) { 311 | NRF_LOG_INFO("As central."); 312 | 313 | err_code = kb_link_c_handles_assign(&m_kb_link_c, p_ble_evt->evt.gap_evt.conn_handle, NULL); 314 | APP_ERROR_CHECK(err_code); 315 | 316 | err_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle); 317 | APP_ERROR_CHECK(err_code); 318 | } 319 | #endif 320 | break; 321 | 322 | case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: 323 | NRF_LOG_INFO("Conn param update request."); 324 | 325 | sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, &p_ble_evt->evt.gap_evt.params.conn_param_update_request.conn_params); 326 | break; 327 | 328 | case BLE_GAP_EVT_CONN_PARAM_UPDATE: 329 | NRF_LOG_INFO("Conn params update; conn interval: %i, conn sup timeout: %i.", p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.min_conn_interval * 1.25, p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.conn_sup_timeout * 10); 330 | break; 331 | 332 | case BLE_GAP_EVT_DISCONNECTED: 333 | NRF_LOG_INFO("Disconnected; reason: 0x%X.", p_ble_evt->evt.gap_evt.params.disconnected.reason); 334 | 335 | if (p_ble_evt->evt.gap_evt.conn_handle == m_conn_handle) { 336 | m_conn_handle = BLE_CONN_HANDLE_INVALID; 337 | m_peer_id = PM_PEER_ID_INVALID; 338 | } 339 | break; 340 | 341 | case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { 342 | NRF_LOG_DEBUG("PHY update request."); 343 | 344 | ble_gap_phys_t const phys = { 345 | .rx_phys = BLE_GAP_PHY_AUTO, 346 | .tx_phys = BLE_GAP_PHY_AUTO, 347 | }; 348 | 349 | err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); 350 | APP_ERROR_CHECK(err_code); 351 | } 352 | break; 353 | 354 | case BLE_GATTC_EVT_TIMEOUT: 355 | // Disconnect on GATT Client timeout event. 356 | NRF_LOG_DEBUG("GATT client timeout."); 357 | 358 | err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); 359 | APP_ERROR_CHECK(err_code); 360 | break; 361 | 362 | case BLE_GATTS_EVT_TIMEOUT: 363 | // Disconnect on GATT Server timeout event. 364 | NRF_LOG_DEBUG("GATT server simeout."); 365 | 366 | err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); 367 | APP_ERROR_CHECK(err_code); 368 | break; 369 | 370 | case BLE_GATTS_EVT_HVN_TX_COMPLETE: 371 | if (p_ble_evt->evt.gatts_evt.conn_handle == m_conn_handle && m_hid_buffer.count > 0) { 372 | hids_send_report(NULL); 373 | } 374 | break; 375 | 376 | case BLE_GATTS_EVT_SYS_ATTR_MISSING: 377 | NRF_LOG_DEBUG("GATT sys attr missing."); 378 | 379 | err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0); 380 | APP_ERROR_CHECK(err_code); 381 | break; 382 | 383 | default: 384 | // No implementation needed. 385 | break; 386 | } 387 | } 388 | 389 | static void gatt_init(void) { 390 | ret_code_t err_code; 391 | 392 | err_code = nrf_ble_gatt_init(&m_gatt, NULL); 393 | APP_ERROR_CHECK(err_code); 394 | } 395 | 396 | static void dis_init(void) { 397 | ret_code_t err_code; 398 | ble_dis_init_t dis_init_obj = {0}; 399 | 400 | ble_srv_ascii_to_utf8(&dis_init_obj.manufact_name_str, MANUFACTURER_NAME); 401 | dis_init_obj.dis_char_rd_sec = SEC_JUST_WORKS; 402 | 403 | err_code = ble_dis_init(&dis_init_obj); 404 | APP_ERROR_CHECK(err_code); 405 | } 406 | 407 | static void hids_init(void) { 408 | ret_code_t err_code; 409 | ble_hids_init_t hids_init_obj = {0}; 410 | ble_hids_inp_rep_init_t *p_kb_input_report; 411 | ble_hids_inp_rep_init_t *p_cc_input_report; 412 | ble_hids_outp_rep_init_t *p_output_report; 413 | uint8_t hid_info_flags; 414 | 415 | static ble_hids_inp_rep_init_t input_report_array[INPUT_REPORT_NUM]; 416 | static ble_hids_outp_rep_init_t output_report_array[1]; 417 | static uint8_t report_map_data[] = { 418 | // Keyboard report. 419 | 0x05, 0x01, // Usage Page (Generic Desktop). 420 | 0x09, 0x06, // Usage (Keyboard). 421 | 0xA1, 0x01, // Collection (Application). 422 | 0x85, 0x01, // Report Id (1). 423 | 424 | // Modifiers key, 1 byte to represent 8 keys. 1 bit for 1 key. 425 | 0x05, 0x07, // Usage Page (Keyboard). 426 | 0x19, 0xE0, // Usage Minimum (Left Control). 427 | 0x29, 0xE7, // Usage Maximum (Right Gui). 428 | 0x15, 0x00, // Logical Minimum (0). 429 | 0x25, 0x01, // Logical Maximum (1). 430 | 0x95, 0x08, // Report Count (8). 431 | 0x75, 0x01, // Report Size (1). 432 | 0x81, 0x02, // Input (Data, Variable, Absolute). 433 | 434 | // Reserved byte (8 bits). 435 | 0x95, 0x01, // Report Count (1). 436 | 0x75, 0x08, // Report Size (8). 437 | 0x81, 0x01, // Input (Constant, Array, Absolute). 438 | 439 | // Keyboard report, 6 bytes for 6 keys. 440 | 0x19, 0x00, // Usage Minimum (Reserved (No Event Indicated)). 441 | 0x29, 0xA4, // Usage Maximum (Keyboard ExSel). 442 | 0x15, 0x00, // Logical Minimum (0). 443 | 0x25, 0xA4, // Logical Maximum (164). 444 | 0x95, 0x06, // Report Count (6). 445 | 0x75, 0x08, // Report Size (8). 446 | 0x81, 0x00, // Input (Data, Array, Absolute). 447 | 448 | // LEDs output report. 449 | // Represent by each bit, Kana | Compose | Scroll Lock | Caps Lock | Num Lock. 450 | 0x05, 0x08, // Usage Page (LEDs). 451 | 0x19, 0x01, // Usage Minimum (Num Lock). 452 | 0x29, 0x05, // Usage Maximum (Kana). 453 | 0x95, 0x05, // Report Count (5). 454 | 0x75, 0x01, // Report Size (1). 455 | 0x91, 0x02, // Output (Data, Variable, Absolute). 456 | 457 | // LEDs output report padding. 458 | 0x95, 0x01, // Report Count (1). 459 | 0x75, 0x03, // Report Size (3). 460 | 0x91, 0x01, // Output (Constant, Variable, Absolute). 461 | 462 | 0xC0, // End Collection (Application). 463 | 464 | // Consumer Control report. 465 | 0x05, 0x0C, // Usage Page (Consumer Devices). 466 | 0x09, 0x01, // Usage (Consumer Control). 467 | 0xA1, 0x01, // Collection (Application). 468 | 0x85, 0x02, // Report Id (2). 469 | 470 | // Consumer Control, 1 key at a time. 471 | 0x19, 0x00, // Usage Minimum (Unassigned). 472 | 0x2A, 0xFF, 0x00, // Usage Maximum (255). 473 | 0x15, 0x00, // Logical Minimum (0). 474 | 0x26, 0xFF, 0x00, // Logical Maximum (255). 475 | 0x95, 0x01, // Report Count (1). 476 | 0x75, 0x10, // Report Size (16). 477 | 0x81, 0x00, // Input (Data, Array, Absolute). 478 | 479 | 0xC0 // End Collection (Application). 480 | }; 481 | 482 | memset((void *)input_report_array, 0, sizeof(ble_hids_inp_rep_init_t) * INPUT_REPORT_NUM); 483 | memset((void *)output_report_array, 0, sizeof(ble_hids_outp_rep_init_t)); 484 | 485 | // Initialize HID Service. 486 | // Keyboard input report. 487 | p_kb_input_report = &input_report_array[KB_INPUT_REPORT_INDEX]; 488 | p_kb_input_report->max_len = KB_INPUT_REPORT_MAX_LEN; 489 | p_kb_input_report->rep_ref.report_id = KB_INPUT_REPORT_ID; 490 | p_kb_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT; 491 | 492 | p_kb_input_report->sec.cccd_wr = SEC_JUST_WORKS; 493 | p_kb_input_report->sec.wr = SEC_JUST_WORKS; 494 | p_kb_input_report->sec.rd = SEC_JUST_WORKS; 495 | 496 | // Keyboard input report. 497 | p_cc_input_report = &input_report_array[CC_INPUT_REPORT_INDEX]; 498 | p_cc_input_report->max_len = CC_INPUT_REPORT_MAX_LEN; 499 | p_cc_input_report->rep_ref.report_id = CC_INPUT_REPORT_ID; 500 | p_cc_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT; 501 | 502 | p_cc_input_report->sec.cccd_wr = SEC_JUST_WORKS; 503 | p_cc_input_report->sec.wr = SEC_JUST_WORKS; 504 | p_cc_input_report->sec.rd = SEC_JUST_WORKS; 505 | 506 | // LEDs output report. 507 | p_output_report = &output_report_array[OUTPUT_REPORT_INDEX]; 508 | p_output_report->max_len = OUTPUT_REPORT_MAX_LEN; 509 | p_output_report->rep_ref.report_id = OUTPUT_REPORT_ID; 510 | p_output_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_OUTPUT; 511 | 512 | p_output_report->sec.wr = SEC_JUST_WORKS; 513 | p_output_report->sec.rd = SEC_JUST_WORKS; 514 | 515 | hid_info_flags = HID_INFO_FLAG_REMOTE_WAKE_MSK | HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK; 516 | 517 | hids_init_obj.evt_handler = hids_evt_handler; 518 | hids_init_obj.error_handler = hid_error_handler; 519 | hids_init_obj.is_kb = true; 520 | hids_init_obj.is_mouse = false; 521 | hids_init_obj.inp_rep_count = INPUT_REPORT_NUM; 522 | hids_init_obj.p_inp_rep_array = input_report_array; 523 | hids_init_obj.outp_rep_count = 1; 524 | hids_init_obj.p_outp_rep_array = output_report_array; 525 | hids_init_obj.feature_rep_count = 0; 526 | hids_init_obj.p_feature_rep_array = NULL; 527 | hids_init_obj.rep_map.data_len = sizeof(report_map_data); 528 | hids_init_obj.rep_map.p_data = report_map_data; 529 | hids_init_obj.hid_information.bcd_hid = BASE_USB_HID_SPEC_VERSION; 530 | hids_init_obj.hid_information.b_country_code = 0; 531 | hids_init_obj.hid_information.flags = hid_info_flags; 532 | hids_init_obj.included_services_count = 0; 533 | hids_init_obj.p_included_services_array = NULL; 534 | 535 | hids_init_obj.rep_map.rd_sec = SEC_JUST_WORKS; 536 | hids_init_obj.hid_information.rd_sec = SEC_JUST_WORKS; 537 | 538 | hids_init_obj.boot_kb_inp_rep_sec.cccd_wr = SEC_JUST_WORKS; 539 | hids_init_obj.boot_kb_inp_rep_sec.rd = SEC_JUST_WORKS; 540 | 541 | hids_init_obj.boot_kb_outp_rep_sec.rd = SEC_JUST_WORKS; 542 | hids_init_obj.boot_kb_outp_rep_sec.wr = SEC_JUST_WORKS; 543 | 544 | hids_init_obj.protocol_mode_rd_sec = SEC_JUST_WORKS; 545 | hids_init_obj.protocol_mode_wr_sec = SEC_JUST_WORKS; 546 | hids_init_obj.ctrl_point_wr_sec = SEC_JUST_WORKS; 547 | 548 | err_code = ble_hids_init(&m_hids, &hids_init_obj); 549 | APP_ERROR_CHECK(err_code); 550 | } 551 | 552 | static void hids_evt_handler(ble_hids_t *p_hids, ble_hids_evt_t *p_evt) { 553 | NRF_LOG_INFO("HIDs evt; evt: 0x%X.", p_evt->evt_type); 554 | 555 | switch (p_evt->evt_type) { 556 | case BLE_HIDS_EVT_BOOT_MODE_ENTERED: 557 | NRF_LOG_INFO("Boot mode entered."); 558 | 559 | m_hids_in_boot_mode = true; 560 | break; 561 | 562 | case BLE_HIDS_EVT_REPORT_MODE_ENTERED: 563 | NRF_LOG_INFO("Report mode entered."); 564 | 565 | m_hids_in_boot_mode = false; 566 | break; 567 | 568 | case BLE_HIDS_EVT_REP_CHAR_WRITE: 569 | NRF_LOG_INFO("Rep char write."); 570 | 571 | on_hid_rep_char_write(p_evt); 572 | break; 573 | 574 | case BLE_HIDS_EVT_NOTIF_ENABLED: 575 | NRF_LOG_INFO("Notify enabled."); 576 | break; 577 | 578 | default: 579 | // No implementation needed. 580 | break; 581 | } 582 | } 583 | 584 | static void on_hid_rep_char_write(ble_hids_evt_t *p_evt) { 585 | if (p_evt->params.char_write.char_id.rep_type == BLE_HIDS_REP_TYPE_OUTPUT) { 586 | ret_code_t err_code; 587 | uint8_t report_val; 588 | uint8_t report_index = p_evt->params.char_write.char_id.rep_index; 589 | 590 | if (report_index == OUTPUT_REPORT_INDEX) { 591 | // This code assumes that the output report is one byte long. Hence the following static assert is made. 592 | STATIC_ASSERT(OUTPUT_REPORT_MAX_LEN == 1); 593 | 594 | err_code = ble_hids_outp_rep_get(&m_hids, report_index, OUTPUT_REPORT_MAX_LEN, 0, m_conn_handle, &report_val); 595 | APP_ERROR_CHECK(err_code); 596 | 597 | // Set Caps Lock indicator here. 598 | if (!m_caps_lock_on && ((report_val & OUTPUT_REPORT_BIT_MASK_CAPS_LOCK) != 0)) { 599 | // Caps Lock is turned On. 600 | NRF_LOG_INFO("Caps Lock is turned On!"); 601 | 602 | m_caps_lock_on = true; 603 | } else if (m_caps_lock_on && ((report_val & OUTPUT_REPORT_BIT_MASK_CAPS_LOCK) == 0)) { 604 | // Caps Lock is turned Off. 605 | NRF_LOG_INFO("Caps Lock is turned Off!"); 606 | 607 | m_caps_lock_on = false; 608 | } 609 | } 610 | } 611 | } 612 | 613 | static void advertising_init(void) { 614 | ret_code_t err_code; 615 | ble_advertising_init_t init = {0}; 616 | 617 | init.advdata.include_appearance = true; 618 | init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; 619 | init.advdata.uuids_complete.uuid_cnt = 1; 620 | init.advdata.uuids_complete.p_uuids = &m_adv_uuid; 621 | 622 | init.srdata.name_type = BLE_ADVDATA_FULL_NAME; 623 | 624 | init.config.ble_adv_whitelist_enabled = true; 625 | init.config.ble_adv_fast_enabled = true; 626 | init.config.ble_adv_fast_interval = MASTER_ADV_FAST_INTERVAL; 627 | init.config.ble_adv_fast_timeout = MASTER_ADV_FAST_DURATION; 628 | init.config.ble_adv_slow_enabled = true; 629 | init.config.ble_adv_slow_interval = MASTER_ADV_SLOW_INTERVAL; 630 | init.config.ble_adv_slow_timeout = MASTER_ADV_SLOW_DURATION; 631 | 632 | init.evt_handler = adv_evt_handler; 633 | init.error_handler = adv_error_handler; 634 | 635 | err_code = ble_advertising_init(&m_advertising, &init); 636 | APP_ERROR_CHECK(err_code); 637 | 638 | ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG); 639 | } 640 | 641 | static void adv_evt_handler(const ble_adv_evt_t ble_adv_evt) { 642 | ret_code_t err_code; 643 | 644 | NRF_LOG_INFO("ADV evt; evt: 0x%X.", ble_adv_evt); 645 | 646 | switch (ble_adv_evt) { 647 | case BLE_ADV_EVT_FAST: 648 | NRF_LOG_INFO("Fast advertising."); 649 | break; 650 | 651 | case BLE_ADV_EVT_FAST_WHITELIST: 652 | NRF_LOG_INFO("Fast advertising with whitelist."); 653 | break; 654 | 655 | case BLE_ADV_EVT_SLOW: 656 | NRF_LOG_INFO("Slow advertising."); 657 | break; 658 | 659 | case BLE_ADV_EVT_SLOW_WHITELIST: 660 | NRF_LOG_INFO("Slow advertising with whitelist."); 661 | break; 662 | 663 | case BLE_ADV_EVT_IDLE: 664 | NRF_LOG_INFO("Stop advertising."); 665 | break; 666 | 667 | case BLE_ADV_EVT_WHITELIST_REQUEST:{ 668 | ble_gap_addr_t whitelist_addrs[BLE_GAP_WHITELIST_ADDR_MAX_COUNT]; 669 | ble_gap_irk_t whitelist_irks[BLE_GAP_WHITELIST_ADDR_MAX_COUNT]; 670 | uint32_t addr_cnt = BLE_GAP_WHITELIST_ADDR_MAX_COUNT; 671 | uint32_t irk_cnt = BLE_GAP_WHITELIST_ADDR_MAX_COUNT; 672 | 673 | NRF_LOG_INFO("Whitelist request."); 674 | 675 | err_code = pm_whitelist_get(whitelist_addrs, &addr_cnt, whitelist_irks, &irk_cnt); 676 | APP_ERROR_CHECK(err_code); 677 | 678 | // Set the correct identities list (no excluding peers with no Central Address Resolution). 679 | identities_set(PM_PEER_ID_LIST_SKIP_NO_IRK); 680 | 681 | // Apply the whitelist. 682 | err_code = ble_advertising_whitelist_reply(&m_advertising, whitelist_addrs, addr_cnt, whitelist_irks, irk_cnt); 683 | APP_ERROR_CHECK(err_code); 684 | } break; 685 | 686 | default: 687 | break; 688 | } 689 | } 690 | 691 | static void identities_set(pm_peer_id_list_skip_t skip) { 692 | ret_code_t err_code; 693 | pm_peer_id_t peer_ids[BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT]; 694 | uint32_t peer_id_count = BLE_GAP_DEVICE_IDENTITIES_MAX_COUNT; 695 | 696 | err_code = pm_peer_id_list(peer_ids, &peer_id_count, PM_PEER_ID_INVALID, skip); 697 | APP_ERROR_CHECK(err_code); 698 | 699 | err_code = pm_device_identities_list_set(peer_ids, peer_id_count); 700 | APP_ERROR_CHECK(err_code); 701 | } 702 | 703 | static void peer_manager_init(void) { 704 | ret_code_t err_code; 705 | ble_gap_sec_params_t sec_param = {0}; 706 | 707 | err_code = pm_init(); 708 | APP_ERROR_CHECK(err_code); 709 | 710 | // Security parameters to be used for all security procedures. 711 | sec_param.bond = SEC_PARAM_BOND; 712 | sec_param.mitm = SEC_PARAM_MITM; 713 | sec_param.lesc = SEC_PARAM_LESC; 714 | sec_param.keypress = SEC_PARAM_KEYPRESS; 715 | sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; 716 | sec_param.oob = SEC_PARAM_OOB; 717 | sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; 718 | sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; 719 | sec_param.kdist_own.enc = 1; 720 | sec_param.kdist_own.id = 1; 721 | sec_param.kdist_peer.enc = 1; 722 | sec_param.kdist_peer.id = 1; 723 | 724 | err_code = pm_sec_params_set(&sec_param); 725 | APP_ERROR_CHECK(err_code); 726 | 727 | err_code = pm_register(pm_evt_handler); 728 | APP_ERROR_CHECK(err_code); 729 | } 730 | 731 | static void pm_evt_handler(pm_evt_t const *p_evt) { 732 | pm_handler_on_pm_evt(p_evt); 733 | pm_handler_flash_clean(p_evt); 734 | 735 | ret_code_t err_code; 736 | 737 | switch (p_evt->evt_id) { 738 | case PM_EVT_CONN_SEC_SUCCEEDED: 739 | NRF_LOG_INFO("Connection secured."); 740 | 741 | m_peer_id = p_evt->peer_id; 742 | break; 743 | 744 | case PM_EVT_CONN_SEC_CONFIG_REQ: { 745 | NRF_LOG_INFO("Repairing request."); 746 | 747 | pm_conn_sec_config_t conn_sec_config = { .allow_repairing = true }; 748 | 749 | pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config); 750 | } 751 | break; 752 | 753 | case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: 754 | if (p_evt->params.peer_data_update_succeeded.flash_changed && p_evt->params.peer_data_update_succeeded.data_id == PM_PEER_DATA_ID_BONDING) { 755 | // Set peer id for current device. 756 | m_device_connection.peer_ids[m_device_connection.current_device] = p_evt->peer_id; 757 | 758 | err_code = fds_record_update(&m_device_connection_record_desc, &m_device_connection_record); 759 | APP_ERROR_CHECK(err_code); 760 | } 761 | break; 762 | 763 | case PM_EVT_PEER_DELETE_SUCCEEDED: 764 | NRF_LOG_INFO("Peer deleted."); 765 | break; 766 | 767 | default: 768 | break; 769 | } 770 | } 771 | 772 | static void gap_address_init(void) { 773 | ret_code_t err_code; 774 | 775 | flash_data_init(); 776 | 777 | ble_gap_addr_t gap_addr; 778 | 779 | err_code = sd_ble_gap_addr_get(&gap_addr); 780 | APP_ERROR_CHECK(err_code); 781 | 782 | gap_addr.addr[3] = m_device_connection.addrs[m_device_connection.current_device]; 783 | 784 | err_code = sd_ble_gap_addr_set(&gap_addr); 785 | APP_ERROR_CHECK(err_code); 786 | } 787 | 788 | static void flash_data_init(void) { 789 | ret_code_t err_code; 790 | 791 | err_code = fds_register(fds_evt_handler); 792 | APP_ERROR_CHECK(err_code); 793 | 794 | err_code = fds_init(); 795 | APP_ERROR_CHECK(err_code); 796 | 797 | while (!m_fds_initialized) { 798 | idle_state_handle(); 799 | } 800 | 801 | // Device connection init. 802 | fds_find_token_t token = {0}; 803 | bool generate_new_device_connection = false; 804 | 805 | err_code = fds_record_find(CONFIG_FILE_ID, DEVICE_CONNECTION_KEY, &m_device_connection_record_desc, &token); 806 | 807 | if (err_code == NRF_SUCCESS) { 808 | fds_flash_record_t device_connection_record; 809 | 810 | err_code = fds_record_open(&m_device_connection_record_desc, &device_connection_record); 811 | 812 | if (err_code == NRF_SUCCESS) { 813 | NRF_LOG_INFO("Found device connection record."); 814 | 815 | memcpy(&m_device_connection, device_connection_record.p_data, sizeof(device_connection_t)); 816 | 817 | NRF_LOG_INFO("Addrs; 0x%X, 0x%X, 0x%X.", m_device_connection.addrs[0], m_device_connection.addrs[1], m_device_connection.addrs[2]); 818 | 819 | err_code = fds_record_close(&m_device_connection_record_desc); 820 | APP_ERROR_CHECK(err_code); 821 | } else { 822 | NRF_LOG_INFO("Cannot open record: 0x%X", err_code); 823 | 824 | generate_new_device_connection = true; 825 | } 826 | } else { 827 | NRF_LOG_INFO("Record not found: 0x%X.", err_code); 828 | 829 | generate_new_device_connection = true; 830 | } 831 | 832 | if (generate_new_device_connection) { 833 | NRF_LOG_INFO("Not found device connection record, generating new config."); 834 | 835 | uint8_t bytes_available; 836 | 837 | do { 838 | err_code = sd_rand_application_bytes_available_get(&bytes_available); 839 | APP_ERROR_CHECK(err_code); 840 | 841 | while (bytes_available < 3) { 842 | nrf_delay_ms(OPERATION_DELAY); 843 | 844 | err_code = sd_rand_application_bytes_available_get(&bytes_available); 845 | APP_ERROR_CHECK(err_code); 846 | } 847 | 848 | err_code = sd_rand_application_vector_get(&m_device_connection.addrs[0], 3); 849 | APP_ERROR_CHECK(err_code); 850 | } while (m_device_connection.addrs[0] == m_device_connection.addrs[1] || m_device_connection.addrs[1] == m_device_connection.addrs[2] || m_device_connection.addrs[2] == m_device_connection.addrs[0]); // To ensure unique addresses. 851 | 852 | NRF_LOG_INFO("Addrs; 0x%X, 0x%X, 0x%X.", m_device_connection.addrs[0], m_device_connection.addrs[1], m_device_connection.addrs[2]); 853 | 854 | err_code = fds_record_write(&m_device_connection_record_desc, &m_device_connection_record); 855 | APP_ERROR_CHECK(err_code); 856 | 857 | NRF_LOG_INFO("New device connection config is written, device will restart soon."); 858 | } 859 | } 860 | 861 | static void fds_evt_handler(fds_evt_t const * p_evt) { 862 | ret_code_t err_code; 863 | switch (p_evt->id) { 864 | case FDS_EVT_INIT: 865 | NRF_LOG_INFO("FDS initialized."); 866 | 867 | if (p_evt->result == NRF_SUCCESS) { 868 | m_fds_initialized = true; 869 | } 870 | break; 871 | 872 | case FDS_EVT_WRITE: 873 | case FDS_EVT_UPDATE: 874 | NRF_LOG_INFO("FDS record write."); 875 | 876 | if (p_evt->result == NRF_SUCCESS && p_evt->write.file_id == CONFIG_FILE_ID && p_evt->write.record_key == DEVICE_CONNECTION_KEY) { 877 | err_code = fds_gc(); 878 | APP_ERROR_CHECK(err_code); 879 | } 880 | break; 881 | 882 | case FDS_EVT_GC: 883 | NRF_LOG_INFO("FDS garbage collected."); 884 | 885 | // Reset device if needed. 886 | if (m_reset_device_connection_update) { 887 | reset_device(); 888 | } 889 | break; 890 | 891 | default: 892 | // No implementation needed. 893 | break; 894 | } 895 | } 896 | 897 | static void reset_device(void) { 898 | NRF_LOG_INFO("Restarting."); 899 | NRF_LOG_FINAL_FLUSH(); 900 | 901 | ret_code_t err_code; 902 | 903 | err_code = sd_nvic_SystemReset(); 904 | APP_ERROR_CHECK(err_code); 905 | } 906 | 907 | static void peers_refresh(void) { 908 | ret_code_t err_code; 909 | pm_peer_id_t peer_id; 910 | 911 | // Delete not old peers. 912 | peer_id = pm_next_peer_id_get(PM_PEER_ID_INVALID); 913 | while (peer_id != PM_PEER_ID_INVALID) { 914 | if (peer_id != m_device_connection.peer_ids[0] && peer_id != m_device_connection.peer_ids[1] && peer_id != m_device_connection.peer_ids[2]) { 915 | pm_peer_delete(peer_id); 916 | } 917 | 918 | peer_id = pm_next_peer_id_get(peer_id); 919 | } 920 | } 921 | 922 | static void set_whitelist(void) { 923 | ret_code_t err_code; 924 | pm_peer_id_t peer_id = m_device_connection.peer_ids[m_device_connection.current_device]; 925 | 926 | if (peer_id != PM_PEER_ID_INVALID) { 927 | err_code = pm_whitelist_set(&peer_id, 1); 928 | APP_ERROR_CHECK(err_code); 929 | } else { 930 | err_code = pm_whitelist_set(NULL, 1); 931 | APP_ERROR_CHECK(err_code); 932 | } 933 | } 934 | 935 | static void advertising_start(void) { 936 | ret_code_t err_code; 937 | 938 | err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST); 939 | APP_ERROR_CHECK(err_code); 940 | } 941 | 942 | static void timers_start(void) { 943 | ret_code_t err_code; 944 | 945 | err_code = app_timer_start(m_scan_timer_id, SCAN_DELAY_TICKS, NULL); 946 | APP_ERROR_CHECK(err_code); 947 | } 948 | 949 | static void hids_send_report(hid_report_t *p_report) { 950 | ret_code_t err_code; 951 | 952 | if (m_conn_handle != BLE_CONN_HANDLE_INVALID) { 953 | if (p_report != NULL && m_hid_buffer.count < HID_REPORT_BUFFER_NUM) { 954 | memcpy(&m_hid_buffer.reports[m_hid_buffer.end], p_report, sizeof(hid_report_t)); 955 | m_hid_buffer.count++; 956 | m_hid_buffer.end++; 957 | 958 | if (m_hid_buffer.end >= HID_REPORT_BUFFER_NUM) { 959 | m_hid_buffer.end = 0; 960 | } 961 | } 962 | 963 | while (m_hid_buffer.count > 0) { 964 | err_code = NRF_SUCCESS; 965 | hid_report_type_t report_type = m_hid_buffer.reports[m_hid_buffer.start].type; 966 | 967 | if (m_hids_in_boot_mode) { 968 | if (report_type == HID_TYPE_KB_REPORT) { 969 | err_code = ble_hids_boot_kb_inp_rep_send(&m_hids, KB_INPUT_REPORT_MAX_LEN, (uint8_t *)&m_hid_buffer.reports[m_hid_buffer.start].data.kb, m_conn_handle); 970 | } else if (report_type == HID_TYPE_CC_REPORT) { 971 | err_code = NRF_ERROR_RESOURCES; 972 | } 973 | } else if (report_type == HID_TYPE_KB_REPORT) { 974 | err_code = ble_hids_inp_rep_send(&m_hids, KB_INPUT_REPORT_INDEX, KB_INPUT_REPORT_MAX_LEN, (uint8_t *)&m_hid_buffer.reports[m_hid_buffer.start].data.kb, m_conn_handle); 975 | } else if (report_type == HID_TYPE_CC_REPORT) { 976 | err_code = ble_hids_inp_rep_send(&m_hids, CC_INPUT_REPORT_INDEX, CC_INPUT_REPORT_MAX_LEN, (uint8_t *)&m_hid_buffer.reports[m_hid_buffer.start].data.cc, m_conn_handle); 977 | } 978 | 979 | NRF_LOG_INFO("HIDs report; ret: 0x%X.", err_code); 980 | 981 | if (err_code != NRF_ERROR_RESOURCES) { 982 | m_hid_buffer.count--; 983 | m_hid_buffer.start++; 984 | 985 | if (m_hid_buffer.start >= HID_REPORT_BUFFER_NUM) { 986 | m_hid_buffer.start = 0; 987 | } 988 | } else if (err_code == NRF_ERROR_RESOURCES) { 989 | break; 990 | } 991 | 992 | NRF_LOG_INFO("HIDs report queue: %i", m_hid_buffer.count); 993 | 994 | if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE && err_code != NRF_ERROR_RESOURCES && err_code != NRF_ERROR_BUSY && err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING && err_code != NRF_ERROR_FORBIDDEN) { 995 | APP_ERROR_CHECK(err_code); 996 | } 997 | } 998 | } 999 | } 1000 | 1001 | #ifdef HAS_SLAVE 1002 | void db_discovery_init(void) { 1003 | ret_code_t err_code; 1004 | ble_db_discovery_init_t init; 1005 | 1006 | init.evt_handler = db_disc_handler; 1007 | init.p_gatt_queue = &m_ble_gatt_queue; 1008 | 1009 | err_code = ble_db_discovery_init(&init); 1010 | APP_ERROR_CHECK(err_code); 1011 | } 1012 | 1013 | static void db_disc_handler(ble_db_discovery_evt_t *p_evt) { 1014 | kb_link_c_on_db_disc_evt(&m_kb_link_c, p_evt); 1015 | } 1016 | 1017 | static void kbl_c_init(void) { 1018 | ret_code_t err_code; 1019 | kb_link_c_init_t init; 1020 | 1021 | init.evt_handler = kbl_c_evt_handler; 1022 | 1023 | err_code = kb_link_c_init(&m_kb_link_c, &init); 1024 | APP_ERROR_CHECK(err_code); 1025 | } 1026 | 1027 | static void kbl_c_evt_handler(kb_link_c_t *p_kb_link_c, kb_link_c_evt_t const * p_evt) { 1028 | ret_code_t err_code; 1029 | 1030 | switch (p_evt->evt_type) { 1031 | case KB_LINK_C_EVT_DISCOVERY_COMPLETE: 1032 | NRF_LOG_INFO("KB link discovery complete."); 1033 | 1034 | err_code = kb_link_c_handles_assign(p_kb_link_c, p_evt->conn_handle, &p_evt->handles); 1035 | APP_ERROR_CHECK(err_code); 1036 | 1037 | NRF_LOG_INFO("Enable notification."); 1038 | 1039 | err_code = kb_link_c_key_index_notif_enable(p_kb_link_c); 1040 | APP_ERROR_CHECK(err_code); 1041 | break; 1042 | 1043 | case KB_LINK_C_EVT_ACTIVE_KEY_INDEX_UPDATE: 1044 | NRF_LOG_INFO("Receive notification from KB link; len: %d.", p_evt->len); 1045 | process_slave_key_index((int8_t *)p_evt->p_data, p_evt->len); 1046 | break; 1047 | 1048 | case KB_LINK_C_EVT_DISCONNECTED: 1049 | NRF_LOG_INFO("KB link disconnected."); 1050 | 1051 | // Clear all keys that have been registered by slave. 1052 | clear_slave_key_index(); 1053 | 1054 | // Scan for slave will start automatically. 1055 | break; 1056 | } 1057 | } 1058 | 1059 | static void scan_init(void) { 1060 | ret_code_t err_code; 1061 | nrf_ble_scan_init_t init = {0}; 1062 | ble_gap_scan_params_t scan_params = {0}; 1063 | ble_uuid_t scan_uuid = {SLAVE_UUID, BLE_UUID_TYPE_VENDOR_BEGIN}; 1064 | ble_gap_conn_params_t conn_params = { 1065 | .min_conn_interval = SLAVE_MIN_CONN_INTERVAL, 1066 | .max_conn_interval = SLAVE_MAX_CONN_INTERVAL, 1067 | .slave_latency = SLAVE_LATENCY, 1068 | .conn_sup_timeout = CONN_SUP_TIMEOUT 1069 | }; 1070 | 1071 | scan_params.scan_phys = BLE_GAP_PHY_AUTO; 1072 | scan_params.interval = SCAN_INTERVAL; 1073 | scan_params.window = SCAN_WINDOW; 1074 | scan_params.timeout = SCAN_DURATION; 1075 | 1076 | init.connect_if_match = true; 1077 | init.conn_cfg_tag = APP_BLE_CONN_CFG_TAG; 1078 | init.p_scan_param = &scan_params; 1079 | init.p_conn_param = &conn_params; 1080 | 1081 | err_code = nrf_ble_scan_init(&m_scan, &init, NULL); 1082 | APP_ERROR_CHECK(err_code); 1083 | 1084 | err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_UUID_FILTER, &scan_uuid); 1085 | APP_ERROR_CHECK(err_code); 1086 | 1087 | err_code = nrf_ble_scan_filters_enable(&m_scan, NRF_BLE_SCAN_UUID_FILTER, true); 1088 | APP_ERROR_CHECK(err_code); 1089 | } 1090 | 1091 | static void scan_start(void) { 1092 | NRF_LOG_INFO("scan_start."); 1093 | 1094 | ret_code_t err_code; 1095 | 1096 | err_code = nrf_ble_scan_start(&m_scan); 1097 | APP_ERROR_CHECK(err_code); 1098 | } 1099 | #endif 1100 | 1101 | /* 1102 | * Firmware section. 1103 | */ 1104 | static void firmware_init(void) { 1105 | NRF_LOG_INFO("firmware_init."); 1106 | 1107 | // Init m_keys array. 1108 | memset(&m_keys, 0, sizeof(m_keys)); 1109 | 1110 | for (int i = 0; i < MATRIX_ROW_NUM; i++) { 1111 | for (int j = 0; j < MATRIX_COL_NUM; j++) { 1112 | m_debounce[i][j] = KEY_PRESS_DEBOUNCE; 1113 | } 1114 | } 1115 | } 1116 | 1117 | static void scan_matrix_task(void *p_data, uint16_t size) { 1118 | UNUSED_PARAMETER(p_data); 1119 | UNUSED_PARAMETER(size); 1120 | 1121 | ret_code_t err_code; 1122 | bool has_key_press = false; 1123 | bool has_key_release = false; 1124 | 1125 | for (int col = 0; col < MATRIX_COL_NUM; col++) { 1126 | nrf_gpio_pin_set(COLS[col]); 1127 | nrf_delay_us(PIN_SET_DELAY); 1128 | 1129 | for (int row = 0; row < MATRIX_ROW_NUM; row++) { 1130 | bool pressed = nrf_gpio_pin_read(ROWS[row]) > 0; 1131 | 1132 | if (m_key_pressed[row][col] == pressed) { 1133 | if (pressed) { 1134 | m_debounce[row][col] = KEY_RELEASE_DEBOUNCE; 1135 | } else { 1136 | m_debounce[row][col] = KEY_PRESS_DEBOUNCE; 1137 | } 1138 | } else { 1139 | if (m_debounce[row][col] <= 0) { 1140 | if (pressed) { 1141 | // On key press. 1142 | m_key_pressed[row][col] = true; 1143 | m_debounce[row][col] = KEY_RELEASE_DEBOUNCE; 1144 | has_key_press = true; 1145 | 1146 | if (m_active_key_index_count < MASTER_KEY_NUM) { 1147 | int i = 0; 1148 | 1149 | while (i < m_active_key_index_count && m_active_key_index[i] != MATRIX[row][col]) { 1150 | i++; 1151 | } 1152 | 1153 | if (i == m_active_key_index_count) { 1154 | m_active_key_index[m_active_key_index_count++] = MATRIX[row][col]; 1155 | } 1156 | } 1157 | } else { 1158 | // On key release. 1159 | m_key_pressed[row][col] = false; 1160 | m_debounce[row][col] = KEY_PRESS_DEBOUNCE; 1161 | has_key_release = true; 1162 | int i = 0; 1163 | 1164 | while (i < m_active_key_index_count && m_active_key_index[i] != MATRIX[row][col]) { 1165 | i++; 1166 | } 1167 | 1168 | if (i < m_active_key_index_count) { 1169 | while (i < m_active_key_index_count) { 1170 | m_active_key_index[i] = m_active_key_index[i + 1]; 1171 | i++; 1172 | } 1173 | 1174 | m_active_key_index_count--; 1175 | } 1176 | } 1177 | } else { 1178 | m_debounce[row][col] -= SCAN_DELAY; 1179 | } 1180 | } 1181 | } 1182 | 1183 | nrf_gpio_pin_clear(COLS[col]); 1184 | } 1185 | 1186 | if (has_key_press || has_key_release) { 1187 | update_key_index((int8_t *)&m_active_key_index, m_active_key_index_count, SOURCE); 1188 | translate_key_index(); 1189 | m_low_power_mode_counter = LOW_POWER_MODE_DELAY; 1190 | } else { 1191 | m_low_power_mode_counter -= SCAN_DELAY; 1192 | } 1193 | 1194 | if (m_low_power_mode_counter <= 0) { 1195 | m_low_power_mode_counter = LOW_POWER_MODE_DELAY; 1196 | low_power_mode_start(); 1197 | } 1198 | } 1199 | 1200 | static bool update_key_index(int8_t *p_key_index, uint16_t size, uint8_t source) { 1201 | // Mark all keys from this source as should delete. 1202 | for (int i = 0; i < m_key_count; i++) { 1203 | if (m_keys[i].source == source) { 1204 | m_keys[i].should_delete = true; 1205 | } 1206 | } 1207 | 1208 | // Update all ke presented in p_key_index. 1209 | for (int i = 0; i < size; i++) { 1210 | int j = 0; 1211 | 1212 | while (j < m_key_count && m_keys[j].index != p_key_index[i]) { 1213 | j++; 1214 | } 1215 | 1216 | if (j < m_key_count) { 1217 | m_keys[j].should_delete = false; 1218 | } else if (m_key_count < KEY_NUM) { 1219 | key_t key = {0}; 1220 | key.index = p_key_index[i]; 1221 | key.source = source; 1222 | m_keys[m_key_count++] = key; 1223 | } 1224 | } 1225 | 1226 | // Remove all key that is should_delete. 1227 | int i = 0; 1228 | 1229 | while (i < m_key_count) { 1230 | while (!m_keys[i].should_delete && i < m_key_count) { 1231 | i++; 1232 | } 1233 | 1234 | if (i < m_key_count) { 1235 | for (int j = i; j < m_key_count - 1; j++) { 1236 | m_keys[j] = m_keys[j + 1]; 1237 | } 1238 | 1239 | m_key_count--; 1240 | } 1241 | } 1242 | } 1243 | 1244 | static void translate_key_index(void) { 1245 | ret_code_t err_code; 1246 | uint8_t layer = _BASE_LAYER; 1247 | 1248 | for (int i = 0; i < m_key_count; i++) { 1249 | if (m_keys[i].type != KEY_TYPE_NOT_TRANSLATED) { 1250 | continue; 1251 | } 1252 | 1253 | int8_t index = m_keys[i].index - 1; 1254 | uint32_t code = KEYMAP[layer][index]; 1255 | 1256 | if (IS_LAYER(code)) { 1257 | layer = LAYER(code); 1258 | continue; 1259 | } 1260 | 1261 | if (code == KC_TRANSPARENT) { 1262 | m_keys[i].type = KEY_TYPE_NO_REPORT; 1263 | uint8_t temp_layer = layer; 1264 | 1265 | while (temp_layer >= 0 && KEYMAP[temp_layer][index] == KC_TRANSPARENT) { 1266 | temp_layer--; 1267 | } 1268 | 1269 | if (temp_layer < 0) { 1270 | continue; 1271 | } else { 1272 | code = KEYMAP[temp_layer][index]; 1273 | } 1274 | } 1275 | 1276 | if (IS_MOD(code)) { 1277 | m_keys[i].type = KEY_TYPE_MODIFIER; 1278 | m_keys[i].data.kb.modifiers = MOD_BIT(code); 1279 | 1280 | code = MOD_CODE(code); 1281 | } 1282 | 1283 | if (IS_KEY(code)) { 1284 | if (m_keys[i].type == KEY_TYPE_MODIFIER) { 1285 | m_keys[i].type = KEY_TYPE_KEY_WITH_MODIFIER; 1286 | } else { 1287 | m_keys[i].type = KEY_TYPE_KEY; 1288 | } 1289 | 1290 | m_keys[i].data.kb.key = code; 1291 | continue; 1292 | } 1293 | 1294 | if (IS_CONSUMER(code)) { 1295 | m_keys[i].type = KEY_TYPE_CONSUMER; 1296 | m_keys[i].data.cc = CONSUMER_CODE(code); 1297 | } 1298 | 1299 | if (IS_DEVICE_CONNECTION(code)) { 1300 | NRF_LOG_INFO("Device connection."); 1301 | 1302 | if (IS_DEVICE_SWITCHING(code)) { 1303 | uint8_t device = DEVICE(code); 1304 | 1305 | NRF_LOG_INFO("Switching to device %u.", device); 1306 | 1307 | if (device != m_device_connection.current_device) { 1308 | m_device_connection.current_device = device; 1309 | 1310 | m_reset_device_connection_update = true; 1311 | err_code = fds_record_update(&m_device_connection_record_desc, &m_device_connection_record); 1312 | APP_ERROR_CHECK(err_code); 1313 | } else { 1314 | reset_device(); 1315 | } 1316 | } 1317 | 1318 | if (IS_DEVICE_CONNECT(code)) { 1319 | NRF_LOG_INFO("Reconnect device."); 1320 | 1321 | uint8_t bytes_available; 1322 | uint8_t new_addr; 1323 | 1324 | // Generate new unique address for current device. 1325 | do { 1326 | err_code = sd_rand_application_bytes_available_get(&bytes_available); 1327 | APP_ERROR_CHECK(err_code); 1328 | 1329 | while (bytes_available < 1) { 1330 | nrf_delay_ms(OPERATION_DELAY); 1331 | 1332 | err_code = sd_rand_application_bytes_available_get(&bytes_available); 1333 | APP_ERROR_CHECK(err_code); 1334 | } 1335 | 1336 | err_code = sd_rand_application_vector_get(&new_addr, 1); 1337 | APP_ERROR_CHECK(err_code); 1338 | } while (new_addr == m_device_connection.addrs[0] || new_addr == m_device_connection.addrs[1] || new_addr == m_device_connection.addrs[2]); // To ensure new unique address. 1339 | 1340 | // Save the generated address. 1341 | m_device_connection.addrs[m_device_connection.current_device] = new_addr; 1342 | 1343 | // Reset peer id for current device. 1344 | m_device_connection.peer_ids[m_device_connection.current_device] = PM_PEER_ID_INVALID; 1345 | 1346 | m_reset_device_connection_update = true; 1347 | err_code = fds_record_update(&m_device_connection_record_desc, &m_device_connection_record); 1348 | APP_ERROR_CHECK(err_code); 1349 | } 1350 | } 1351 | } 1352 | 1353 | // Schedule hid report. 1354 | generate_hid_report(); 1355 | } 1356 | 1357 | 1358 | static void generate_hid_report(void) { 1359 | static bool empty_kb_report_sent = true; 1360 | int kb_report_index = 2; 1361 | hid_report_t kb_report = {0}; 1362 | kb_report.type = HID_TYPE_KB_REPORT; 1363 | 1364 | static bool empty_cc_report_sent = true; 1365 | hid_report_t cc_report = {0}; 1366 | cc_report.type = HID_TYPE_CC_REPORT; 1367 | 1368 | for (int i = 0; i < m_key_count; i++) { 1369 | if (m_keys[i].type == KEY_TYPE_NOT_TRANSLATED || m_keys[i].type == KEY_TYPE_NO_REPORT) { 1370 | continue; 1371 | } 1372 | 1373 | if (m_keys[i].type == KEY_TYPE_MODIFIER || m_keys[i].type == KEY_TYPE_KEY_WITH_MODIFIER) { 1374 | kb_report.data.kb[0] |= m_keys[i].data.kb.modifiers; 1375 | } 1376 | 1377 | if ((m_keys[i].type == KEY_TYPE_KEY || m_keys[i].type == KEY_TYPE_KEY_WITH_MODIFIER) && kb_report_index < KB_INPUT_REPORT_MAX_LEN) { 1378 | kb_report.data.kb[kb_report_index++] = m_keys[i].data.kb.key; 1379 | } 1380 | 1381 | if (m_keys[i].type == KEY_TYPE_CONSUMER) { 1382 | cc_report.data.cc = m_keys[i].data.cc; 1383 | } 1384 | } 1385 | 1386 | bool is_empty_kb_report = kb_report.data.kb[0] == 0 && kb_report_index == 2; 1387 | 1388 | if (!empty_kb_report_sent || !is_empty_kb_report) { 1389 | empty_kb_report_sent = is_empty_kb_report; 1390 | 1391 | NRF_LOG_INFO("generate_hid_report; kb len: %d", kb_report_index - 2); 1392 | 1393 | hids_send_report(&kb_report); 1394 | } 1395 | 1396 | bool is_empty_cc_report = cc_report.data.cc == 0; 1397 | 1398 | if (!empty_cc_report_sent || !is_empty_cc_report) { 1399 | empty_cc_report_sent = is_empty_cc_report; 1400 | 1401 | NRF_LOG_INFO("generate_hid_report; cc len: %d", is_empty_cc_report ? 0 : 1); 1402 | 1403 | hids_send_report(&cc_report); 1404 | } 1405 | } 1406 | 1407 | #ifdef HAS_SLAVE 1408 | static void process_slave_key_index(int8_t *p_key_index, uint16_t size) { 1409 | NRF_LOG_INFO("process_slave_key_index; len: %i.", size); 1410 | update_key_index(p_key_index, size, SOURCE_SLAVE); 1411 | translate_key_index(); 1412 | } 1413 | 1414 | static void clear_slave_key_index(void) { 1415 | NRF_LOG_INFO("clear_slave_key_index."); 1416 | int i = 0; 1417 | 1418 | while (i < m_key_count) { 1419 | while (m_keys[i].source != SOURCE_SLAVE && i < m_key_count) { 1420 | i++; 1421 | } 1422 | 1423 | if (i < m_key_count) { 1424 | for (int j = i; j < m_key_count - 1; j++) { 1425 | m_keys[j] = m_keys[j + 1]; 1426 | } 1427 | 1428 | m_key_count--; 1429 | } 1430 | } 1431 | 1432 | // Only remove keys, so no translation needed. 1433 | generate_hid_report(); 1434 | } 1435 | #endif 1436 | --------------------------------------------------------------------------------