├── .gitignore ├── components ├── mouse │ ├── schematic.pdf │ ├── Button.h │ ├── Button.cpp │ ├── Joystic.h │ ├── Joystic.cpp │ └── mouse.ino ├── keyboard-left │ ├── schematic.pdf │ └── keyboard-left.ino └── keyboard-right │ ├── schematic.pdf │ └── keyboard-right.ino ├── Makefile ├── lib └── keyboardHandler │ ├── KeyboardEmitter.h │ ├── KeyboardScanner.h │ ├── KeyboardEmitter.cpp │ ├── KeyboardHandler.h │ └── KeyboardScanner.cpp └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /components/mouse/schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PauloIVM/custom-keyboard/HEAD/components/mouse/schematic.pdf -------------------------------------------------------------------------------- /components/keyboard-left/schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PauloIVM/custom-keyboard/HEAD/components/keyboard-left/schematic.pdf -------------------------------------------------------------------------------- /components/keyboard-right/schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PauloIVM/custom-keyboard/HEAD/components/keyboard-right/schematic.pdf -------------------------------------------------------------------------------- /components/mouse/Button.h: -------------------------------------------------------------------------------- 1 | class Button { 2 | public: 3 | Button(int pin); 4 | bool clicked(void); 5 | private: 6 | int pin; 7 | bool pressed; 8 | }; 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## INFO: Make sure that the path to the Arduino libraries is correct on your machine, 2 | ## replace if necessary. 3 | 4 | FROM := lib 5 | TO := /home/$(USER)/Arduino/libraries 6 | 7 | build: 8 | cp -ru $(FROM)/* $(TO) 9 | 10 | .PHONY: build 11 | -------------------------------------------------------------------------------- /components/mouse/Button.cpp: -------------------------------------------------------------------------------- 1 | #include "Button.h" 2 | #include "Arduino.h" 3 | 4 | Button::Button(int pin) { 5 | this->pin = pin; 6 | this->pressed = false; 7 | } 8 | 9 | bool Button::clicked(void) { 10 | if (digitalRead(pin) == LOW && pressed == false) { 11 | pressed = true; 12 | return true; 13 | } 14 | if (digitalRead(pin) == HIGH && pressed == true) { 15 | pressed = false; 16 | } 17 | return false; 18 | } 19 | -------------------------------------------------------------------------------- /components/mouse/Joystic.h: -------------------------------------------------------------------------------- 1 | class Joystic { 2 | public: 3 | Joystic(int xPin, int yPin, int xAnalogCenter, int yAnalogCenter); 4 | int readX(void); 5 | int readY(void); 6 | void toggleRange(void); 7 | void setRange(int range); 8 | private: 9 | int read(int pin, int analogCenter); 10 | int threshold; 11 | int range; 12 | int xPin; 13 | int yPin; 14 | int xAnalogCenter; 15 | int yAnalogCenter; 16 | }; 17 | -------------------------------------------------------------------------------- /lib/keyboardHandler/KeyboardEmitter.h: -------------------------------------------------------------------------------- 1 | #include "Keyboard.h" 2 | 3 | #ifndef KEYBOARD_EMITTER_H 4 | #define KEYBOARD_EMITTER_H 5 | 6 | class KeyboardEmitter { 7 | public: 8 | KeyboardEmitter(); 9 | KeyboardEmitter(uint8_t*** layers, int layersLength, uint8_t keyLayerUp, uint8_t keyLayerDown); 10 | void press(int row, int column); 11 | void release(int row, int column); 12 | private: 13 | uint8_t*** layers; 14 | int layersLength; 15 | int layerIndex; 16 | uint8_t keyLayerUp; 17 | uint8_t keyLayerDown; 18 | void decreaseLayerIndex(); 19 | void increaseLayerIndex(); 20 | }; 21 | #endif 22 | -------------------------------------------------------------------------------- /components/keyboard-left/keyboard-left.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const int rowsLength = 6; 4 | const int colsLength = 7; 5 | const int layersLength = 2; 6 | int rowPins[rowsLength] = {19, 18, 10, 16, 15, 14}; 7 | int colPins[colsLength] = {5, 9, 3, 7, 4, 8, 6}; 8 | 9 | uint8_t layers[layersLength][rowsLength][colsLength] = { 10 | // INFO: Layer 0 11 | { 12 | {KEY_ESC, 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5}, 13 | {KEY_APOSTROPHE, (uint8_t)'1', (uint8_t)'2', (uint8_t)'3', (uint8_t)'4', (uint8_t)'5', (uint8_t)'6'}, 14 | {KEY_TAB, (uint8_t)'q', (uint8_t)'w', (uint8_t)'e', (uint8_t)'r', (uint8_t)'t', KEY_LS_BRACKET}, 15 | {KEY_CAPS_LOCK, (uint8_t)'a', (uint8_t)'s', (uint8_t)'d', (uint8_t)'f', (uint8_t)'g', KEY_RS_BRACKET}, 16 | {KEY_WINDOWS, KEY_BACKSLASH, (uint8_t)'z', (uint8_t)'x', (uint8_t)'c', (uint8_t)'v', (uint8_t)'b'}, 17 | {KEY_UNKNOWN, KEY_UNKNOWN, 0, KEY_LEFT_ALT, KEY_LEFT_SHIFT, 0, KEY_SPACE}, 18 | }, 19 | }; 20 | 21 | KeyboardHandlerConfig configs = { 22 | KEY_LAYER_UP, 23 | KEY_LAYER_DOWN, 24 | rowPins, 25 | colPins, 26 | }; 27 | 28 | KeyboardHandler keyboardHandler(layers, configs); 29 | 30 | void setup(void) { 31 | keyboardHandler.begin(); 32 | } 33 | 34 | void loop(void) { 35 | keyboardHandler.exec(); 36 | } 37 | -------------------------------------------------------------------------------- /components/mouse/Joystic.cpp: -------------------------------------------------------------------------------- 1 | #include "Joystic.h" 2 | #include "Arduino.h" 3 | #include "pins_arduino.h" 4 | 5 | Joystic::Joystic(int xPin, int yPin, int xAnalogCenter, int yAnalogCenter) { 6 | this->xPin = xPin; 7 | this->yPin = yPin; 8 | this->xAnalogCenter = xAnalogCenter; 9 | this->yAnalogCenter = yAnalogCenter; 10 | this->threshold = 10; 11 | this->range = 40; 12 | } 13 | 14 | void Joystic::toggleRange(void) { 15 | if (range == 40) { range = 10; } 16 | else { range = 40; } 17 | } 18 | 19 | void Joystic::setRange(int range) { 20 | this->range = range; 21 | } 22 | 23 | int Joystic::readX(void) { 24 | int result = this->read(xPin, xAnalogCenter); 25 | return result; 26 | } 27 | 28 | int Joystic::readY(void) { 29 | int result = this->read(yPin, yAnalogCenter); 30 | return result; 31 | } 32 | 33 | int Joystic::read(int pin, int analogCenter) { 34 | int distance = 0; 35 | int reading = analogRead(pin); 36 | int borderDown = analogCenter - threshold; 37 | int borderUp = analogCenter + threshold; 38 | if (reading <= borderDown) { 39 | reading = map(reading, borderDown, 0, 0, -range); 40 | return -reading; 41 | } 42 | if (reading >= borderUp) { 43 | reading = map(reading, borderUp, 1023, 0, range); 44 | return -reading; 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /lib/keyboardHandler/KeyboardScanner.h: -------------------------------------------------------------------------------- 1 | #include "KeyboardEmitter.h" 2 | 3 | #ifndef KEYBOARD_LISTENNER_H 4 | #define KEYBOARD_LISTENNER_H 5 | 6 | #define KEY_UNKNOWN 0x00 7 | #define KEY_CEDIL 0x3B 8 | #define KEY_TIL 0x27 9 | #define KEY_SEMICOLON 0x2F 10 | #define KEY_ACUTE_ACCENT 0x5B 11 | #define KEY_RS_BRACKET 0x5C 12 | #define KEY_RC_BRACKET 0x7C 13 | #define KEY_LS_BRACKET 0x5D 14 | #define KEY_GRAVE_ACCENT 0x7B 15 | #define KEY_LC_BRACKET 0x7D 16 | #define KEY_APOSTROPHE 0x60 17 | #define KEY_BREAKPOINT 0xCA 18 | #define KEY_BACKSLASH 0xEC 19 | #define KEY_FOWARD_SLASH 0xDC 20 | #define KEY_SPACE 0xB4 21 | #define KEY_LAYER_UP 0x01 22 | #define KEY_LAYER_DOWN 0x02 23 | #define KEY_WINDOWS 0x83 24 | 25 | enum KeyEventType { 26 | pressed, 27 | released 28 | }; 29 | 30 | class KeyboardScanner { 31 | public: 32 | KeyboardScanner(int* rowPins, int rowsLength, int* colPins, int colsLength, const KeyboardEmitter& emitter); 33 | KeyboardScanner(); 34 | void scan(); 35 | private: 36 | void fillCols(int value); 37 | bool hasKeyToRelease(int rowIndex); 38 | bool hasKeyToPress(int rowIndex); 39 | int* rowPins; 40 | int rowsLength; 41 | int* colPins; 42 | int colsLength; 43 | int** keysStateMatrix; 44 | KeyboardEmitter emitter; 45 | }; 46 | #endif 47 | -------------------------------------------------------------------------------- /components/mouse/mouse.ino: -------------------------------------------------------------------------------- 1 | #include "Mouse.h" 2 | #include "Keyboard.h" 3 | #include "Button.h" 4 | #include "Joystic.h" 5 | 6 | #define BUTTON_LEFT_PIN 2 7 | #define BUTTON_MID_PIN 3 8 | #define BUTTON_RIGHT_PIN 5 9 | 10 | Joystic joysticR = Joystic(A1, A0, 542, 515); 11 | Joystic joysticL = Joystic(A2, A3, 517, 503); 12 | Button ButtonL = Button(BUTTON_LEFT_PIN); 13 | Button ButtonM = Button(BUTTON_MID_PIN); 14 | Button ButtonR = Button(BUTTON_RIGHT_PIN); 15 | 16 | void setup(void) { 17 | Mouse.begin(); 18 | Keyboard.begin(); 19 | pinMode(BUTTON_LEFT_PIN, INPUT_PULLUP); 20 | pinMode(BUTTON_MID_PIN, INPUT_PULLUP); 21 | pinMode(BUTTON_RIGHT_PIN, INPUT_PULLUP); 22 | // INFO: Unfortunately my joystick has some problem with resistance floating between 500-700 ohms. That's why 23 | // I'm using Range as 1 to avoid bugs. Fix the hardware and change here later. 24 | joysticL.setRange(1); 25 | } 26 | 27 | // TODO: Criar distinção do press e release.. preciso disso por exemplo para selecionar texto 28 | // através do mouse. 29 | void loop(void) { 30 | int x = joysticR.readX(); 31 | int y = joysticR.readY(); 32 | int yWheel = joysticL.readY(); 33 | int xWheel = joysticL.readX(); 34 | Mouse.move(x, y, -yWheel); 35 | delay(30); 36 | if (xWheel != 0) { 37 | Keyboard.press(KEY_LEFT_SHIFT); 38 | Mouse.move(x, y, -xWheel); 39 | Keyboard.releaseAll(); 40 | } 41 | if (ButtonL.clicked()) { Mouse.click(); } 42 | if (ButtonM.clicked()) { Mouse.click(MOUSE_RIGHT); } 43 | if (ButtonR.clicked()) { joysticR.toggleRange(); } 44 | } -------------------------------------------------------------------------------- /lib/keyboardHandler/KeyboardEmitter.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyboardEmitter.h" 2 | 3 | KeyboardEmitter::KeyboardEmitter(uint8_t*** layers, int layersLength, uint8_t keyLayerUp, uint8_t keyLayerDown) { 4 | this->layers = layers; 5 | this->layersLength = layersLength; 6 | this->keyLayerUp = keyLayerUp; 7 | this->keyLayerDown = keyLayerDown; 8 | this->layerIndex = 0; 9 | } 10 | 11 | KeyboardEmitter::KeyboardEmitter() { 12 | this->layersLength = 0; 13 | this->keyLayerUp = 0; 14 | this->keyLayerDown = 0; 15 | this->layerIndex = 0; 16 | } 17 | 18 | // TODO: Tem um desafio aqui... eu posso acabar pressionando uma tecla em uma layer... trocar de layer, 19 | // e aí eu iria dar o release em outra tecla, o q manteria pressionada pra sempre a tecla da primeira 20 | // layer. Tratar isso depois. 21 | void KeyboardEmitter::press(int row, int column) { 22 | if(this->layersLength == 0) { return; } 23 | uint8_t key = layers[this->layerIndex][row][column]; 24 | if (key == this->keyLayerUp) { return; } 25 | if (key == this->keyLayerDown) { return; } 26 | if (key != 0) { Keyboard.press(key); } 27 | } 28 | 29 | void KeyboardEmitter::release(int row, int column) { 30 | if(this->layersLength == 0) { return; } 31 | uint8_t key = layers[this->layerIndex][row][column]; 32 | if (key == this->keyLayerUp) { this->increaseLayerIndex(); } 33 | else if (key == this->keyLayerDown) { this->decreaseLayerIndex(); } 34 | else if (key != 0) { Keyboard.release(key); } 35 | } 36 | 37 | void KeyboardEmitter::decreaseLayerIndex() { 38 | if (this->layerIndex > 0) { 39 | this->layerIndex--; 40 | return; 41 | } 42 | this->layerIndex = this->layersLength - 1; 43 | } 44 | 45 | void KeyboardEmitter::increaseLayerIndex() { 46 | if (this->layerIndex < this->layersLength - 1) { 47 | this->layerIndex++; 48 | return; 49 | } 50 | this->layerIndex = 0; 51 | } 52 | -------------------------------------------------------------------------------- /components/keyboard-right/keyboard-right.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const int rowsLength = 6; 4 | const int colsLength = 7; 5 | const int layersLength = 2; 6 | int rowPins[rowsLength] = {9, 4, 8, 6, 7, 5}; 7 | int colPins[colsLength] = {15, 18, 10, 20, 14, 19, 16}; 8 | 9 | uint8_t layers[layersLength][rowsLength][colsLength] = { 10 | // INFO: Layer 0 11 | { 12 | {KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12}, 13 | {(uint8_t)'7', (uint8_t)'8', (uint8_t)'9', (uint8_t)'0', (uint8_t)'-', (uint8_t)'=', KEY_BACKSPACE}, 14 | {(uint8_t)'y', (uint8_t)'u', (uint8_t)'i', (uint8_t)'o', (uint8_t)'p', KEY_ACUTE_ACCENT, 0}, 15 | {(uint8_t)'h', (uint8_t)'j', (uint8_t)'k', (uint8_t)'l', KEY_CEDIL, KEY_TIL, KEY_KP_ENTER}, 16 | {KEY_FOWARD_SLASH, (uint8_t)'n', (uint8_t)'m', (uint8_t)',', (uint8_t)'.', KEY_SEMICOLON, KEY_RIGHT_SHIFT}, 17 | {0, KEY_RIGHT_CTRL, KEY_LAYER_UP, KEY_RIGHT_ALT, KEY_RIGHT_ALT, KEY_UNKNOWN, KEY_UNKNOWN}, 18 | }, 19 | // INFO: Layer 1 20 | { 21 | {KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12}, 22 | {(uint8_t)'7', (uint8_t)'8', (uint8_t)'9', (uint8_t)'0', (uint8_t)'-', (uint8_t)'=', KEY_BACKSPACE}, 23 | {KEY_PAGE_UP, KEY_HOME, KEY_UP_ARROW, KEY_END, KEY_DELETE, KEY_ACUTE_ACCENT, 0}, 24 | {KEY_PAGE_DOWN, KEY_LEFT_ARROW, KEY_DOWN_ARROW, KEY_RIGHT_ARROW, KEY_CEDIL, KEY_TIL, KEY_KP_ENTER}, 25 | {KEY_FOWARD_SLASH, (uint8_t)'n', (uint8_t)'m', (uint8_t)',', (uint8_t)'.', KEY_SEMICOLON, KEY_RIGHT_SHIFT}, 26 | {0, KEY_RIGHT_CTRL, KEY_LAYER_UP, KEY_RIGHT_ALT, KEY_RIGHT_ALT, KEY_UNKNOWN, KEY_UNKNOWN}, 27 | }, 28 | }; 29 | 30 | KeyboardHandlerConfig configs = { 31 | KEY_LAYER_UP, 32 | KEY_LAYER_DOWN, 33 | rowPins, 34 | colPins, 35 | }; 36 | 37 | KeyboardHandler keyboardHandler(layers, configs); 38 | 39 | void setup(void) { 40 | keyboardHandler.begin(); 41 | } 42 | 43 | void loop(void) { 44 | keyboardHandler.exec(); 45 | } 46 | -------------------------------------------------------------------------------- /lib/keyboardHandler/KeyboardHandler.h: -------------------------------------------------------------------------------- 1 | #include "KeyboardScanner.h" 2 | 3 | #ifndef KEYBOARD_HANDLER_H 4 | #define KEYBOARD_HANDLER_H 5 | 6 | struct KeyboardHandlerConfig { 7 | uint8_t keyLayerUp; 8 | uint8_t keyLayerDown; 9 | int* rowPins; 10 | int* colPins; 11 | }; 12 | 13 | template 14 | class KeyboardHandler { 15 | public: 16 | KeyboardHandler(const uint8_t (&layers)[L_LENGTH][R_LENGTH][C_LENGTH], const KeyboardHandlerConfig& configs) { 17 | uint8_t*** dynamicLayers = this->toDynamicLayers(layers); 18 | KeyboardEmitter emitter = KeyboardEmitter( 19 | dynamicLayers, 20 | L_LENGTH, 21 | configs.keyLayerUp, 22 | configs.keyLayerDown 23 | ); 24 | this->configs = configs; 25 | this->scanner = KeyboardScanner( 26 | configs.rowPins, 27 | R_LENGTH, 28 | configs.colPins, 29 | C_LENGTH, 30 | emitter 31 | ); 32 | } 33 | 34 | void exec(void) { this->scanner.scan(); } 35 | 36 | void begin(void) { 37 | Keyboard.begin(); 38 | setPinModes(this->configs.colPins, OUTPUT, C_LENGTH); 39 | setPinModes(this->configs.rowPins, INPUT_PULLUP, R_LENGTH); 40 | } 41 | 42 | private: 43 | KeyboardScanner scanner; 44 | KeyboardHandlerConfig configs; 45 | 46 | uint8_t*** toDynamicLayers(const uint8_t (&layers)[L_LENGTH][R_LENGTH][C_LENGTH]) { 47 | uint8_t*** dynamicMatrix = (uint8_t***)malloc(L_LENGTH * sizeof(uint8_t**)); 48 | for (size_t i = 0; i < L_LENGTH; ++i) { 49 | dynamicMatrix[i] = (uint8_t**)malloc(R_LENGTH * sizeof(uint8_t*)); 50 | for (size_t row = 0; row < R_LENGTH; ++row) { 51 | dynamicMatrix[i][row] = (uint8_t*)malloc(C_LENGTH * sizeof(uint8_t)); 52 | for (size_t col = 0; col < C_LENGTH; ++col) { 53 | dynamicMatrix[i][row][col] = layers[i][row][col]; 54 | } 55 | } 56 | } 57 | return dynamicMatrix; 58 | } 59 | 60 | void setPinModes(int pins[], int mode, int length) { 61 | for (int i = 0; i < length; i++) { pinMode(pins[i], mode); } 62 | } 63 | }; 64 | #endif 65 | -------------------------------------------------------------------------------- /lib/keyboardHandler/KeyboardScanner.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyboardScanner.h" 2 | 3 | KeyboardScanner::KeyboardScanner(int* rowPins, int rowsLength, int* colPins, int colsLength, const KeyboardEmitter& emitter) { 4 | this->rowPins = rowPins; 5 | this->rowsLength = rowsLength; 6 | this->colPins = colPins; 7 | this->colsLength = colsLength; 8 | this->emitter = emitter; 9 | this->keysStateMatrix = (int**)malloc(rowsLength * sizeof(int*)); 10 | for (int i = 0; i < rowsLength; ++i) { 11 | this->keysStateMatrix[i] = (int*)malloc(colsLength * sizeof(int)); 12 | } 13 | for (int i = 0; i < rowsLength; ++i) { 14 | for (int j = 0; j < colsLength; ++j) { 15 | this->keysStateMatrix[i][j] = HIGH; 16 | } 17 | } 18 | } 19 | 20 | KeyboardScanner::KeyboardScanner() { 21 | this->rowsLength = 0; 22 | this->colsLength = 0; 23 | } 24 | 25 | void KeyboardScanner::scan() { 26 | if (this->rowsLength == 0 || this->colsLength == 0) { return; } 27 | for (int rowIndex = 0; rowIndex < rowsLength; rowIndex++) { 28 | if (hasKeyToPress(rowIndex) == false && hasKeyToRelease(rowIndex) == false) continue; 29 | fillCols(HIGH); 30 | for (int colIndex = 0; colIndex < colsLength; colIndex++) { 31 | if (colIndex != 0) digitalWrite(colPins[colIndex - 1], HIGH); 32 | digitalWrite(colPins[colIndex], LOW); 33 | if (digitalRead(rowPins[rowIndex]) == LOW && keysStateMatrix[rowIndex][colIndex] == HIGH) { 34 | this->emitter.press(rowIndex, colIndex); 35 | keysStateMatrix[rowIndex][colIndex] = LOW; 36 | } 37 | if (digitalRead(rowPins[rowIndex]) == HIGH && keysStateMatrix[rowIndex][colIndex] == LOW) { 38 | this->emitter.release(rowIndex, colIndex); 39 | keysStateMatrix[rowIndex][colIndex] = HIGH; 40 | } 41 | } 42 | fillCols(LOW); 43 | } 44 | // INFO: This delay avoid double-clicks 45 | delay(20); 46 | } 47 | 48 | void KeyboardScanner::fillCols(int value) { 49 | for (int i = 0; i < this->colsLength; i++) { 50 | digitalWrite(this->colPins[i], value); 51 | } 52 | } 53 | 54 | bool KeyboardScanner::hasKeyToRelease(int rIndex) { 55 | for (int cIndex = 0; cIndex < this->colsLength; cIndex++) { 56 | if (keysStateMatrix[rIndex][cIndex] == LOW) return true; 57 | } 58 | return false; 59 | } 60 | 61 | bool KeyboardScanner::hasKeyToPress(int rIndex) { 62 | return digitalRead(rowPins[rIndex]) == LOW; 63 | } 64 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Custom Keyboard ⌨️ 2 | 3 | ### Sumário: 4 | - [1 - Introdução](#1---introdução) 5 | - [2 - Motivação e Construção do Projeto](#2---motivação-e-construção-do-projeto) 6 | - [3 - Hardware](#3---hardware) 7 | - [4 - Software](#4---software) 8 | 9 | ## 1 - Introdução 10 | 11 | Este é um projeto de software e hardware, onde transformei um Redragon Mitra K551 em um teclado split totalmente customizável, com layers e incluindo um componente especial: um mouse de _joystics_. 12 | 13 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/e9e20eb4-6c2c-4bad-bd4b-35b1f91c0921) 14 | 15 | ## 2 - Motivação e Construção do Projeto 16 | 17 | Há alguns meses, meu interesse em aprimorar minhas habilidades de digitação e utilização de atalhos cresceu significativamente. No entanto, a dependência do mouse era (e ainda é, em certa medida) uma barreira considerável. Inicialmente, desenvolvi apenas o mouse analógico, visando manter minhas mãos sempre próximas do teclado; e incentivando a utilização de atalhos em detrimento do mouse convencional. 18 | 19 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/aee006d4-4c1e-4a40-a4d0-c1c7c63bddaf) 20 | 21 | Entretanto, esse dispositivo não proporcionou o efeito desejado. Sua dimensão dificultava na digitação. Por alguns meses deixei o projeto de lado; contudo, ao final, percebi que esse mouse se integraria de maneira mais adequada a um teclado split. 22 | 23 | Assim, comecei a pesquisar mais sobre essa categoria de teclados. O que me surpreendeu foram os preços (na casa dos 1000 reais). Diante disso, pensei: "O que me impede de jogar fora a eletrônica desse Redragon e reconstruir um teclado split a partir da carcaça dele? De custos eu teria os arduinos, diodos e fios, algo na casa dos 200 reais, e o resto dos materiais eu tiraria do próprio teclado". 24 | 25 | Dessa forma, dei continuidade ao projeto. Como evidenciado nas imagens a seguir, procedi com a remoção da placa do meu teclado, dividi a carcaça em dois e, por fim, realizei a soldagem manual dos switches em um Arduino. 26 | 27 | ![5](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/0fd848ed-57b7-4765-a498-79e50013f8d8) 28 | 29 | ![9](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/9fd293bc-f831-409d-8edb-e9a7e9b35a38) 30 | 31 | ![12](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/8dfde9fe-4ff4-49ca-bdb3-31ee560ed2f3) 32 | 33 | ![20](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/c3cea75e-0690-45f5-bbdb-cb2691cca659) 34 | 35 | ## 3 - Hardware 36 | 37 | A seguir, apresento a relação de materiais utilizados na montagem do hardware: 38 | 39 | Ferramentas para confecção e prototipagem: 40 | - Estação de solda; 41 | - Micro-retífica; 42 | - Cola quente; 43 | - Cola super bonder; 44 | 45 | Peças adquiridas: 46 | - 100 diodos 1n4148; 47 | - Rolo de fio wire wrap 30awg; 48 | - 3 arduinos Pro Micro - Atmega32u4; 49 | 50 | Peças reutilizadas: 51 | - Base metálica do Redragon Mitra K551; 52 | - Keycaps do Redragon Mitra K551; 53 | - Switches blues do Redragon Mitra K551; 54 | - Joystics (removidos de uma manete de PS2 antiga); 55 | - Estrutura de plástico do Redragon Mitra K551; 56 | 57 | Caso esteja montando um projeto similar, recomendo fortemente a utilização do Atmega32u4 por conta de sua funcionalidade nativa de ser reconhecido como um dispositivo USB HID. Caso escolha um microcontrolador sem esta funcionalidade, será preciso escrever seus próprios descritores e isso pode dificultar bastante o projeto. 58 | 59 | ### 3.1 - Mouse 60 | 61 | 62 | Como mencionado anteriormente, esse componente atua como um mouse. O analógico esquerdo é destinado a realizar scrolls tanto na direção horizontal quanto vertical, enquanto o analógico direito controla o movimento do cursor. Dos três botões, um corresponde ao clique esquerdo do mouse, outro ao clique direito, e o terceiro tem a função de ajustar a sensibilidade, assemelhando-se ao ajuste de DPI em mouses convencionais. 63 | 64 | Para uma compreensão visual detalhada, cada componente de hardware possui um PDF contendo o circuito esquemático. O circuito esquemático específico do mouse pode ser encontrado [aqui](https://github.com/PauloIVM/my-custom-keyboard/blob/master/components/mouse/schematic.pdf) ou na imagem a seguir. 65 | 66 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/32c7e92b-7c6c-4cf5-81fb-abe280bd29d5) 67 | 68 | A construção do componente foi à base de muitas gambiarras. Fiz um pequeno suporte de madeira para comportar os botões e os joystics. Soldei os fios no arduino e colei tudo com cola quente e cola super bonder + bicarbonato. Além disso, com o plástico de outras partes do teclado que seriam descartadas, criei um "case" para comportar todo o componente e esconder os fios. 69 | 70 | Um ponto importante é que me ajudou muito ter uma microretífica. 71 | 72 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/ba36725c-196f-4b5b-ae77-f2a70d3f7711) 73 | 74 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/8ea4626b-1666-4587-9f3c-a52034dbadba) 75 | 76 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/0f9629cb-4dca-4c3a-8606-24b883370142) 77 | 78 | ### 3.2 - Teclado 79 | 80 | Para a construção tanto do lado esquerdo quanto do lado direito do teclado, montei um circuito matricial de diodos para cada um. A escolha de utilizar uma matriz visa otimizar o uso de pinos, uma vez que, se cada tecla fosse tratada como um input independente, seria necessário mais pinos do que o Arduino dispõe. 81 | 82 | A inclusão dos diodos é necessária para prevenir um problema comum em teclados matriciais, conhecido como _ghosting_. Esse fenômeno ocorre quando múltiplas teclas são pressionadas de forma triangular, resultando na interpretação de uma tecla não pressionada como se estivesse sendo pressionada. 83 | 84 | Para elucidar o funcionamento do circuito matricial de diodos, considere a simplificação abaixo: 85 | 86 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/32c253bc-aadf-4dfe-8333-4b0b2693ed7d) 87 | 88 | 89 | Observe que, na linha R1, o botão mais à direita está pressionado (fechado). As linhas são configuradas como inputs PULL_UP, enquanto as colunas são definidas como outputs (todas com nível lógico LOW neste momento). Quando um botão é pressionado, a linha correspondente apresenta nível lógico LOW; caso nenhum botão esteja pressionado, a linha permanece com nível lógico HIGH. 90 | 91 | Dessa maneira, em uma primeira etapa, realizamos uma varredura em busca de alguma linha com estado lógico LOW. Ao encontrar, interrompemos essa varredura e iniciamos uma inspeção nessa linha para identificar quais colunas estão sendo pressionadas. 92 | 93 | Supondo que encontramos uma linha com nível lógico LOW, seguimos com uma varredura nas colunas para verificar quais alteram o estado da linha. Inicialmente, configuramos todas as colunas para HIGH, como ilustrado na imagem a seguir. Isso garante que, independentemente do botão estar aberto ou fechado, o valor da linha permanece em HIGH. 94 | 95 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/dbd4582f-3a57-474d-a48c-b180a10238ba) 96 | 97 | Agora iteramos coluna por coluna, definindo cada uma delas como LOW sequencialmente. As colunas que, ao serem definidas como LOW, provocam a mudança do estado da linha para LOW são identificadas como aquelas que estão sendo pressionadas. 98 | 99 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/a9aa27f3-0733-4571-9610-cbce00f3d56e) 100 | 101 | A construção de ambos os lados do teclado seguiu seus respectivos esquemáticos (seguem logo abaixo). A prototipação foi feita soldando-se os fios manualmente, conforme uma imagem já anexada em [2 - Motivação e Construção do Projeto](#2---motivação-e-construção-do-projeto). 102 | 103 | #### 3.2.1 - Teclado Esquerdo 104 | 105 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/c116f7cd-754f-46b1-9c5a-7c60562bea03) 106 | 107 | #### 3.2.2 - Teclado Direito 108 | 109 | ![image](https://github.com/PauloIVM/my-custom-keyboard/assets/59659732/4b28bf3e-3866-454b-8ad3-f744b0947da3) 110 | 111 | ## 4 - Software 112 | 113 | Um primeiro cuidado importante a se ter é com relação à gravação dos arduinos. Devido à funcionalidade nativa de ser reconhecido como um USB HID, gravar um código defeituoso pode resultar no não reconhecimento do dispositivo pela IDE do Arduino. Se isso ocorrer, não entre em desespero, há uma solução; o Arduino, ao ser plugado, aguarda aproximadamente 8 segundos antes de executar o código fonte; assim, você consegue gravar um novo código nesse curto intervalo. 114 | 115 | Neste tipo de projeto é bem comum se utilizar o https://qmk.fm/ para gerar o código fonte dos arduinos. É um caminho possível, mas eu preferi escrever todo o meu código, especialmente porque no meu projeto existem algumas peculiaridades que acredito que o qmk não contemple, como os joystics por exemplo. 116 | 117 | Dentro da pasta _components_, encontramos três subpastas; cada uma com um arquivo _.ino_ referente a um dos três componentes de hardware. 118 | 119 | O _.ino_ em `/components/mouse` pode ser compilado em um Arduino Pro Micro sem problemas, sendo necessário apenas ajustar os pinos conforme o seu projeto. 120 | 121 | Os arquivos _.ino_ em `/components/keyboard-left` e `/components/keyboard-right` precisam de uma lib para funcionar, denominada _KeyboardHandler_ e disponível em `/lib/keyboardHandler`. Como esse trecho do código é comum aos dois lados do teclado, optei por organizá-lo no formato de uma biblioteca que precisa ser importada. 122 | 123 | O _KeyboardHandler_ pode ser importado de diversas formas. Você pode copiar o código e colar em cada uma das pastas `/components/keyboard-left` e `/components/keyboard-right`; ou ainda gerar um _.zip_ e adicionar a lib pela IDE do arduino; ou, caso esteja usando uma distribuição linux, pode executar um `make build` na raiz do projeto e a biblioteca será automaticamente adicionada à IDE do Arduino. 124 | 125 | Adicionado o _KeyboardHandler_, você precisará passar os pinos corretos do seu hardware e passar as camadas com os layouts de teclas que quiser. Perceba que normalmente os teclados não possuem todas as linhas com a mesma quantidade de colunas; mas isso não é um problema, serão apenas teclas na sua matriz que nunca serão clicadas. 126 | 127 | Você poderá passar quantas camadas desejar, bastando ajustar a dimensão em `layersLength`. O número de linhas e colunas também pode ser personalizado. Por fim, para trocar de camada, basta escolher um botão na matriz e passar a key `KEY_LAYER_UP` ou `KEY_LAYER_DOWN`, assim esses botões serão responsáveis por trocar de layer (semelhante a um botão "fn"). 128 | 129 | Na branch master você encontrará as layers que eu tenho utilizado no momento, isto poderá ajudar a tirar quaisquer dúvidas. 130 | --------------------------------------------------------------------------------