diff --git a/kolea.ino b/kolea.ino
index 15b471e..87fdcbc 100644
--- a/kolea.ino
+++ b/kolea.ino
@@ -1,484 +1,484 @@
-/**
- * StenoFW is a firmware for Stenoboard keyboards.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- * Copyright 2014 Emanuele Caruso. See LICENSE.txt for details.
- */
-
-/**
- * Matrix modified for the Kolea keyboard.
- */
-
-#define ROWS 4
-#define COLS 11
-
-/* The following matrix is shown here for reference only.
-char keys[ROWS][COLS] = {
- {' ', '2', '3', '4', '5', ' ', '7', '8', '9', '0', ' '},
- {' ', 'q', 'w', 'e', 'r', 't', 'u', 'i', 'o', 'p', '['},
- {' ', 'a', 's', 'd', 'f', 'g', 'j', 'k', 'l', ';', '\''},
- {' ', ' ', ' ', 'c', 'v', ' ', 'n', 'm', ' ', ' ', ' '}
-};*/
-
-// Configuration variables
-int rowPins[ROWS] = {4, 5, 6, 7};
-int colPins[COLS] = {8, 9, 10, 11, 12, 14, 15, 16, 18, 19, 20};
-int ledPin = 3;
-long debounceMillis = 20;
-
-// Keyboard state variables
-boolean isStrokeInProgress = false;
-boolean currentChord[ROWS][COLS];
-boolean currentKeyReadings[ROWS][COLS];
-boolean debouncingKeys[ROWS][COLS];
-unsigned long debouncingMicros[ROWS][COLS];
-
-// Other state variables
-int ledIntensity = 1; // Min 0 - Max 255
-
-// Protocol state
-#define GEMINI 0
-#define TXBOLT 1
-#define NKRO 2
-int protocol = GEMINI;
-
-// This is called when the keyboard is connected
-void setup() {
- Keyboard.begin();
- Serial.begin(9600);
- for (int i = 0; i < COLS; i++)
- pinMode(colPins[i], INPUT_PULLUP);
- for (int i = 0; i < ROWS; i++) {
- pinMode(rowPins[i], OUTPUT);
- digitalWrite(rowPins[i], HIGH);
- }
- pinMode(ledPin, OUTPUT);
- analogWrite(ledPin, ledIntensity);
- clearBooleanMatrixes();
-}
-
-// Read key states and handle all chord events
-void loop() {
- readKeys();
-
- boolean isAnyKeyPressed = true;
-
- // If stroke is not in progress, check debouncing keys
- if (!isStrokeInProgress) {
- checkAlreadyDebouncingKeys();
- if (!isStrokeInProgress) checkNewDebouncingKeys();
- }
-
- // If any key was pressed, record all pressed keys
- if (isStrokeInProgress) {
- isAnyKeyPressed = recordCurrentKeys();
- }
-
- // If all keys have been released, send the chord and reset global state
- if (!isAnyKeyPressed) {
- sendChord();
- clearBooleanMatrixes();
- isStrokeInProgress = false;
- }
-}
-
-// Record all pressed keys into current chord. Return false if no key is currently pressed
-boolean recordCurrentKeys() {
- boolean isAnyKeyPressed = false;
- for (int i = 0; i < ROWS; i++) {
- for (int j = 0; j < COLS; j++) {
- if (currentKeyReadings[i][j] == true) {
- currentChord[i][j] = true;
- isAnyKeyPressed = true;
- }
- }
- }
- return isAnyKeyPressed;
-}
-
-// If a key is pressed, add it to debouncing keys and record the time
-void checkNewDebouncingKeys() {
- for (int i = 0; i < ROWS; i++) {
- for (int j = 0; j < COLS; j++) {
- if (currentKeyReadings[i][j] == true && debouncingKeys[i][j] == false) {
- debouncingKeys[i][j] = true;
- debouncingMicros[i][j] = micros();
- }
- }
- }
-}
-
-// Check already debouncing keys. If a key debounces, start chord recording.
-void checkAlreadyDebouncingKeys() {
- for (int i = 0; i < ROWS; i++) {
- for (int j = 0; j < COLS; j++) {
- if (debouncingKeys[i][j] == true && currentKeyReadings[i][j] == false) {
- debouncingKeys[i][j] = false;
- continue;
- }
- if (debouncingKeys[i][j] == true && micros() - debouncingMicros[i][j] / 1000 > debounceMillis) {
- isStrokeInProgress = true;
- currentChord[i][j] = true;
- return;
- }
- }
- }
-}
-
-// Set all values of all boolean matrixes to false
-void clearBooleanMatrixes() {
- clearBooleanMatrix(currentChord, false);
- clearBooleanMatrix(currentKeyReadings, false);
- clearBooleanMatrix(debouncingKeys, false);
-}
-
-// Set all values of the passed matrix to the given value
-void clearBooleanMatrix(boolean booleanMatrix[][COLS], boolean value) {
- for (int i = 0; i < ROWS; i++) {
- for (int j = 0; j < COLS; j++) {
- booleanMatrix[i][j] = value;
- }
- }
-}
-
-// Read all keys
-void readKeys() {
- for (int i = 0; i < ROWS; i++) {
- digitalWrite(rowPins[i], LOW);
- for (int j = 0; j < COLS; j++)
- currentKeyReadings[i][j] = digitalRead(colPins[j]) == LOW ? true : false;
- digitalWrite(rowPins[i], HIGH);
- }
-}
-
-// Send current chord using NKRO Keyboard emulation
-void sendChordNkro() {
- // QWERTY mapping
- char qwertyMapping[ROWS][COLS] = {
- {' ', '2', '3', '4', '5', ' ', '7', '8', '9', '0', ' '},
- {' ', 'q', 'w', 'e', 'r', 't', 'u', 'i', 'o', 'p', '['},
- {' ', 'a', 's', 'd', 'f', 'g', 'j', 'k', 'l', ';', '\''},
- {' ', ' ', ' ', 'c', 'v', ' ', 'n', 'm', ' ', ' ', ' '}
- };
- int keyCounter = 0;
- char qwertyKeys[ROWS * COLS];
- boolean firstKeyPressed = false;
-
- // Calculate qwerty keys array using qwertyMappings[][]
- for (int i = 0; i < ROWS; i++)
- for (int j = 0; j < COLS; j++)
- if (currentChord[i][j]) {
- qwertyKeys[keyCounter] = qwertyMapping[i][j];
- keyCounter++;
- }
- // Emulate keyboard key presses
- for (int i = 0; i < keyCounter; i++) {
- if (qwertyKeys[i] != ' ') {
- Keyboard.press(qwertyKeys[i]);
- if (!firstKeyPressed) firstKeyPressed = true;
- else Keyboard.release(qwertyKeys[i]);
- }
- }
- Keyboard.releaseAll();
-}
-
-// Send current chord over serial using the Gemini protocol.
-void sendChordGemini() {
- // Initialize chord bytes
- byte chordBytes[] = {B10000000, B0, B0, B0, B0, B0};
-
- // Byte 0
- //#
- if (currentChord[0][1] || currentChord[0][2] || currentChord[0][3] || currentChord[0][4] || currentChord[0][6] || currentChord[0][7] || currentChord[0][8] || currentChord[0][9]) {
- chordBytes[0] = B10000001;
- }
-
- // Byte 1
- //S
- if (currentChord[1][1] || currentChord[2][1]) {
- chordBytes[1] += B01000000;
- }
- //T
- if (currentChord[1][2]) {
- chordBytes[1] += B00010000;
- }
- //K
- if (currentChord[2][2]) {
- chordBytes[1] += B00001000;
- }
- //P
- if (currentChord[1][3]) {
- chordBytes[1] += B00000100;
- }
- //W
- if (currentChord[2][3]) {
- chordBytes[1] += B00000010;
- }
- //H
- if (currentChord[1][4]) {
- chordBytes[1] += B00000001;
- }
-
- // Byte 2
- //R
- if (currentChord[2][4]) {
- chordBytes[2] += B01000000;
- }
- //W
- if (currentChord[3][3]) {
- chordBytes[2] += B00100000;
- }
- //O
- if (currentChord[3][4]) {
- chordBytes[2] += B00010000;
- }
- //*
- if (currentChord[1][5] || currentChord[2][5]) {
- chordBytes[2] += B00001000;
- }
-
- // Byte 3
- //E
- if (currentChord[3][6]) {
- chordBytes[3] += B00001000;
- }
- //U
- if (currentChord[3][7]) {
- chordBytes[3] += B00000100;
- }
- //F
- if (currentChord[1][6]) {
- chordBytes[3] += B00000010;
- }
- //R
- if (currentChord[2][6]) {
- chordBytes[3] += B00000001;
- }
-
- // Byte 4
- //P
- if (currentChord[1][7]) {
- chordBytes[4] += B01000000;
- }
- //B
- if (currentChord[2][7]) {
- chordBytes[4] += B00100000;
- }
- //L
- if (currentChord[1][8]) {
- chordBytes[4] += B00010000;
- }
- //G
- if (currentChord[2][8]) {
- chordBytes[4] += B00001000;
- }
- //T
- if (currentChord[1][9]) {
- chordBytes[4] += B00000100;
- }
- //S
- if (currentChord[2][9]) {
- chordBytes[4] += B00000010;
- }
- //D
- if (currentChord[1][10]) {
- chordBytes[4] += B00000001;
- }
-
- // Byte 5
- //Z
- if (currentChord[2][10]) {
- chordBytes[5] += B00000001;
- }
-
- // Send chord bytes over serial
- for (int i = 0; i < 6; i++) {
- Serial.write(chordBytes[i]);
- }
-}
-
-void sendChordTxBolt() {
- byte chordBytes[] = {B0, B0, B0, B0, B0};
- int index = 0;
-
- // TX Bolt uses a variable length packet. Only those bytes that have active
- // keys are sent. The header bytes indicate which keys are being sent. They
- // must be sent in order. It is a good idea to send a zero after every packet.
- // 00XXXXXX 01XXXXXX 10XXXXXX 110XXXXX
- // HWPKTS UE*OAR GLBPRF #ZDST
-
- // byte 1
- // S-
- if (currentChord[1][1] || currentChord[2][1]) chordBytes[index] |= B00000001;
- // T-
- if (currentChord[1][2]) chordBytes[index] |= B00000010;
- // K-
- if (currentChord[2][2]) chordBytes[index] |= B00000100;
- // P-
- if (currentChord[1][3]) chordBytes[index] |= B00001000;
- // W-
- if (currentChord[2][3]) chordBytes[index] |= B00010000;
- // H-
- if (currentChord[1][4]) chordBytes[index] |= B00100000;
- // Increment the index if the current byte has any keys set.
- if (chordBytes[index]) index++;
-
- // byte 2
- // R-
- if (currentChord[2][4]) chordBytes[index] |= B01000001;
- // A
- if (currentChord[3][3]) chordBytes[index] |= B01000010;
- // O
- if (currentChord[3][4]) chordBytes[index] |= B01000100;
- // *
- if (currentChord[1][5] || currentChord[2][5]) chordBytes[index] |= B01001000;
- // E
- if (currentChord[3][6]) chordBytes[index] |= B01010000;
- // U
- if (currentChord[3][7]) chordBytes[index] |= B01100000;
- // Increment the index if the current byte has any keys set.
- if (chordBytes[index]) index++;
-
- // byte 3
- // -F
- if (currentChord[1][6]) chordBytes[index] |= B10000001;
- // -R
- if (currentChord[2][6]) chordBytes[index] |= B10000010;
- // -P
- if (currentChord[1][7]) chordBytes[index] |= B10000100;
- // -B
- if (currentChord[2][7]) chordBytes[index] |= B10001000;
- // -L
- if (currentChord[1][8]) chordBytes[index] |= B10010000;
- // -G
- if (currentChord[2][8]) chordBytes[index] |= B10100000;
- // Increment the index if the current byte has any keys set.
- if (chordBytes[index]) index++;
-
- // byte 4
- // -T
- if (currentChord[1][9]) chordBytes[index] |= B11000001;
- // -S
- if (currentChord[2][9]) chordBytes[index] |= B11000010;
- // -D
- if (currentChord[1][10]) chordBytes[index] |= B11000100;
- // -Z
- if (currentChord[2][10]) chordBytes[index] |= B11001000;
- // #
- if (currentChord[0][1] || currentChord[0][2] || currentChord[0][3] || currentChord[0][4] || currentChord[0][6] || currentChord[0][7] || currentChord[0][8] || currentChord[0][9]) chordBytes[index] |= B11010000;
- // Increment the index if the current byte has any keys set.
- if (chordBytes[index]) index++;
-
- // Now we have index bytes followed by a zero byte where 0 < index <= 4.
- index++; // Increment index to include the trailing zero byte.
- for (int i = 0; i < index; i++) {
- Serial.write(chordBytes[i]);
- }
-}
-
-// Send the chord using the current protocol. If there are fn keys
-// pressed, delegate to the corresponding function instead.
-// In future versions, there should also be a way to handle fn keys presses before
-// they are released, eg. for mouse emulation functionality or custom key presses.
-void sendChord() {
- // If fn keys have been pressed, delegate to corresponding method and return
- if (currentChord[1][0] && currentChord[2][0]) {
- fn1fn2();
- return;
- } else if (currentChord[1][0]) {
- fn1();
- return;
- } else if (currentChord[2][0]) {
- fn2();
- return;
- }
-
- if (protocol == NKRO) {
- sendChordNkro();
- } else if (protocol == GEMINI) {
- sendChordGemini();
- } else {
- sendChordTxBolt();
- }
-}
-
-// Fn1 functions
-//
-// This function is called when "fn1" key has been pressed, but not "fn2".
-// Tip: maybe it is better to avoid using "fn1" key alone in order to avoid
-// accidental activation?
-//
-// Current functions:
-// PH-PB -> Set NKRO Keyboard emulation mode
-// PH-G -> Set Gemini PR protocol mode
-// PH-B -> Set TX Bolt protocol mode
-void fn1() {
- // "PH" -> Set protocol
- if (currentChord[1][3] && currentChord[1][4]) {
- // "-PB" -> NKRO Keyboard
- if (currentChord[1][7] && currentChord[2][7]) {
- protocol = NKRO;
- }
- // "-G" -> Gemini PR
- else if (currentChord[2][8]) {
- protocol = GEMINI;
- }
- // "-B" -> TX Bolt
- else if (currentChord[2][7]) {
- protocol = TXBOLT;
- }
- }
-}
-
-// Fn2 functions
-//
-// This function is called when "fn2" key has been pressed, but not "fn1".
-// Tip: maybe it is better to avoid using "fn2" key alone in order to avoid
-// accidental activation?
-//
-// Current functions: none.
-void fn2() {
-
-}
-
-// Fn1-Fn2 functions
-//
-// This function is called when both "fn1" and "fn1" keys have been pressed.
-//
-// Current functions:
-// HR-P -> LED intensity up
-// HR-F -> LED intensity down
-void fn1fn2() {
- // "HR" -> Change LED intensity
- if (currentChord[1][4] && currentChord[2][4]) {
- // "-P" -> LED intensity up
- if (currentChord[1][7]) {
- if (ledIntensity == 0) ledIntensity +=1;
- else if(ledIntensity < 50) ledIntensity += 10;
- else ledIntensity += 30;
- if (ledIntensity > 255) ledIntensity = 0;
- analogWrite(ledPin, ledIntensity);
- }
- // "-F" -> LED intensity down
- if (currentChord[1][6]) {
- if(ledIntensity == 0) ledIntensity = 255;
- else if(ledIntensity < 50) ledIntensity -= 10;
- else ledIntensity -= 30;
- if (ledIntensity < 1) ledIntensity = 0;
- analogWrite(ledPin, ledIntensity);
- }
- }
-}
+/**
+ * StenoFW is a firmware for Stenoboard keyboards.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Copyright 2014 Emanuele Caruso. See LICENSE.txt for details.
+ */
+
+/**
+ * Matrix modified for the Kolea keyboard.
+ */
+
+#define ROWS 4
+#define COLS 11
+
+/* The following matrix is shown here for reference only.
+char keys[ROWS][COLS] = {
+ {' ', '2', '3', '4', '5', ' ', '7', '8', '9', '0', ' '},
+ {' ', 'q', 'w', 'e', 'r', 't', 'u', 'i', 'o', 'p', '['},
+ {' ', 'a', 's', 'd', 'f', 'g', 'j', 'k', 'l', ';', '\''},
+ {' ', ' ', ' ', 'c', 'v', ' ', 'n', 'm', ' ', ' ', ' '}
+};*/
+
+// Configuration variables
+int rowPins[ROWS] = {4, 5, 6, 7};
+int colPins[COLS] = {8, 9, 10, 11, 12, 14, 15, 16, 18, 19, 20};
+int ledPin = 3;
+unsigned long debounceMillis = 20;
+
+// Keyboard state variables
+boolean isStrokeInProgress = false;
+boolean currentChord[ROWS][COLS];
+boolean currentKeyReadings[ROWS][COLS];
+boolean debouncingKeys[ROWS][COLS];
+unsigned long debouncingMicros[ROWS][COLS];
+
+// Other state variables
+int ledIntensity = 1; // Min 0 - Max 255
+
+// Protocol state
+#define GEMINI 0
+#define TXBOLT 1
+#define NKRO 2
+int protocol = GEMINI;
+
+// This is called when the keyboard is connected
+void setup() {
+ Keyboard.begin();
+ Serial.begin(9600);
+ for (int i = 0; i < COLS; i++)
+ pinMode(colPins[i], INPUT_PULLUP);
+ for (int i = 0; i < ROWS; i++) {
+ pinMode(rowPins[i], OUTPUT);
+ digitalWrite(rowPins[i], HIGH);
+ }
+ pinMode(ledPin, OUTPUT);
+ analogWrite(ledPin, ledIntensity);
+ clearBooleanMatrixes();
+}
+
+// Read key states and handle all chord events
+void loop() {
+ readKeys();
+
+ boolean isAnyKeyPressed = true;
+
+ // If stroke is not in progress, check debouncing keys
+ if (!isStrokeInProgress) {
+ checkAlreadyDebouncingKeys();
+ if (!isStrokeInProgress) checkNewDebouncingKeys();
+ }
+
+ // If any key was pressed, record all pressed keys
+ if (isStrokeInProgress) {
+ isAnyKeyPressed = recordCurrentKeys();
+ }
+
+ // If all keys have been released, send the chord and reset global state
+ if (!isAnyKeyPressed) {
+ sendChord();
+ clearBooleanMatrixes();
+ isStrokeInProgress = false;
+ }
+}
+
+// Record all pressed keys into current chord. Return false if no key is currently pressed
+boolean recordCurrentKeys() {
+ boolean isAnyKeyPressed = false;
+ for (int i = 0; i < ROWS; i++) {
+ for (int j = 0; j < COLS; j++) {
+ if (currentKeyReadings[i][j] == true) {
+ currentChord[i][j] = true;
+ isAnyKeyPressed = true;
+ }
+ }
+ }
+ return isAnyKeyPressed;
+}
+
+// If a key is pressed, add it to debouncing keys and record the time
+void checkNewDebouncingKeys() {
+ for (int i = 0; i < ROWS; i++) {
+ for (int j = 0; j < COLS; j++) {
+ if (currentKeyReadings[i][j] == true && debouncingKeys[i][j] == false) {
+ debouncingKeys[i][j] = true;
+ debouncingMicros[i][j] = micros();
+ }
+ }
+ }
+}
+
+// Check already debouncing keys. If a key debounces, start chord recording.
+void checkAlreadyDebouncingKeys() {
+ for (int i = 0; i < ROWS; i++) {
+ for (int j = 0; j < COLS; j++) {
+ if (debouncingKeys[i][j] == true && currentKeyReadings[i][j] == false) {
+ debouncingKeys[i][j] = false;
+ continue;
+ }
+ if (debouncingKeys[i][j] == true && micros() - debouncingMicros[i][j] / 1000 > debounceMillis) {
+ isStrokeInProgress = true;
+ currentChord[i][j] = true;
+ return;
+ }
+ }
+ }
+}
+
+// Set all values of all boolean matrixes to false
+void clearBooleanMatrixes() {
+ clearBooleanMatrix(currentChord, false);
+ clearBooleanMatrix(currentKeyReadings, false);
+ clearBooleanMatrix(debouncingKeys, false);
+}
+
+// Set all values of the passed matrix to the given value
+void clearBooleanMatrix(boolean booleanMatrix[][COLS], boolean value) {
+ for (int i = 0; i < ROWS; i++) {
+ for (int j = 0; j < COLS; j++) {
+ booleanMatrix[i][j] = value;
+ }
+ }
+}
+
+// Read all keys
+void readKeys() {
+ for (int i = 0; i < ROWS; i++) {
+ digitalWrite(rowPins[i], LOW);
+ for (int j = 0; j < COLS; j++)
+ currentKeyReadings[i][j] = digitalRead(colPins[j]) == LOW ? true : false;
+ digitalWrite(rowPins[i], HIGH);
+ }
+}
+
+// Send current chord using NKRO Keyboard emulation
+void sendChordNkro() {
+ // QWERTY mapping
+ char qwertyMapping[ROWS][COLS] = {
+ {' ', '2', '3', '4', '5', ' ', '7', '8', '9', '0', ' '},
+ {' ', 'q', 'w', 'e', 'r', 't', 'u', 'i', 'o', 'p', '['},
+ {' ', 'a', 's', 'd', 'f', 'g', 'j', 'k', 'l', ';', '\''},
+ {' ', ' ', ' ', 'c', 'v', ' ', 'n', 'm', ' ', ' ', ' '}
+ };
+ int keyCounter = 0;
+ char qwertyKeys[ROWS * COLS];
+ boolean firstKeyPressed = false;
+
+ // Calculate qwerty keys array using qwertyMappings[][]
+ for (int i = 0; i < ROWS; i++)
+ for (int j = 0; j < COLS; j++)
+ if (currentChord[i][j]) {
+ qwertyKeys[keyCounter] = qwertyMapping[i][j];
+ keyCounter++;
+ }
+ // Emulate keyboard key presses
+ for (int i = 0; i < keyCounter; i++) {
+ if (qwertyKeys[i] != ' ') {
+ Keyboard.press(qwertyKeys[i]);
+ if (!firstKeyPressed) firstKeyPressed = true;
+ else Keyboard.release(qwertyKeys[i]);
+ }
+ }
+ Keyboard.releaseAll();
+}
+
+// Send current chord over serial using the Gemini protocol.
+void sendChordGemini() {
+ // Initialize chord bytes
+ byte chordBytes[] = {B10000000, B0, B0, B0, B0, B0};
+
+ // Byte 0
+ //#
+ if (currentChord[0][1] || currentChord[0][2] || currentChord[0][3] || currentChord[0][4] || currentChord[0][6] || currentChord[0][7] || currentChord[0][8] || currentChord[0][9]) {
+ chordBytes[0] = B10000001;
+ }
+
+ // Byte 1
+ //S
+ if (currentChord[1][1] || currentChord[2][1]) {
+ chordBytes[1] += B01000000;
+ }
+ //T
+ if (currentChord[1][2]) {
+ chordBytes[1] += B00010000;
+ }
+ //K
+ if (currentChord[2][2]) {
+ chordBytes[1] += B00001000;
+ }
+ //P
+ if (currentChord[1][3]) {
+ chordBytes[1] += B00000100;
+ }
+ //W
+ if (currentChord[2][3]) {
+ chordBytes[1] += B00000010;
+ }
+ //H
+ if (currentChord[1][4]) {
+ chordBytes[1] += B00000001;
+ }
+
+ // Byte 2
+ //R
+ if (currentChord[2][4]) {
+ chordBytes[2] += B01000000;
+ }
+ //W
+ if (currentChord[3][3]) {
+ chordBytes[2] += B00100000;
+ }
+ //O
+ if (currentChord[3][4]) {
+ chordBytes[2] += B00010000;
+ }
+ //*
+ if (currentChord[1][5] || currentChord[2][5]) {
+ chordBytes[2] += B00001000;
+ }
+
+ // Byte 3
+ //E
+ if (currentChord[3][6]) {
+ chordBytes[3] += B00001000;
+ }
+ //U
+ if (currentChord[3][7]) {
+ chordBytes[3] += B00000100;
+ }
+ //F
+ if (currentChord[1][6]) {
+ chordBytes[3] += B00000010;
+ }
+ //R
+ if (currentChord[2][6]) {
+ chordBytes[3] += B00000001;
+ }
+
+ // Byte 4
+ //P
+ if (currentChord[1][7]) {
+ chordBytes[4] += B01000000;
+ }
+ //B
+ if (currentChord[2][7]) {
+ chordBytes[4] += B00100000;
+ }
+ //L
+ if (currentChord[1][8]) {
+ chordBytes[4] += B00010000;
+ }
+ //G
+ if (currentChord[2][8]) {
+ chordBytes[4] += B00001000;
+ }
+ //T
+ if (currentChord[1][9]) {
+ chordBytes[4] += B00000100;
+ }
+ //S
+ if (currentChord[2][9]) {
+ chordBytes[4] += B00000010;
+ }
+ //D
+ if (currentChord[1][10]) {
+ chordBytes[4] += B00000001;
+ }
+
+ // Byte 5
+ //Z
+ if (currentChord[2][10]) {
+ chordBytes[5] += B00000001;
+ }
+
+ // Send chord bytes over serial
+ for (int i = 0; i < 6; i++) {
+ Serial.write(chordBytes[i]);
+ }
+}
+
+void sendChordTxBolt() {
+ byte chordBytes[] = {B0, B0, B0, B0, B0};
+ int index = 0;
+
+ // TX Bolt uses a variable length packet. Only those bytes that have active
+ // keys are sent. The header bytes indicate which keys are being sent. They
+ // must be sent in order. It is a good idea to send a zero after every packet.
+ // 00XXXXXX 01XXXXXX 10XXXXXX 110XXXXX
+ // HWPKTS UE*OAR GLBPRF #ZDST
+
+ // byte 1
+ // S-
+ if (currentChord[1][1] || currentChord[2][1]) chordBytes[index] |= B00000001;
+ // T-
+ if (currentChord[1][2]) chordBytes[index] |= B00000010;
+ // K-
+ if (currentChord[2][2]) chordBytes[index] |= B00000100;
+ // P-
+ if (currentChord[1][3]) chordBytes[index] |= B00001000;
+ // W-
+ if (currentChord[2][3]) chordBytes[index] |= B00010000;
+ // H-
+ if (currentChord[1][4]) chordBytes[index] |= B00100000;
+ // Increment the index if the current byte has any keys set.
+ if (chordBytes[index]) index++;
+
+ // byte 2
+ // R-
+ if (currentChord[2][4]) chordBytes[index] |= B01000001;
+ // A
+ if (currentChord[3][3]) chordBytes[index] |= B01000010;
+ // O
+ if (currentChord[3][4]) chordBytes[index] |= B01000100;
+ // *
+ if (currentChord[1][5] || currentChord[2][5]) chordBytes[index] |= B01001000;
+ // E
+ if (currentChord[3][6]) chordBytes[index] |= B01010000;
+ // U
+ if (currentChord[3][7]) chordBytes[index] |= B01100000;
+ // Increment the index if the current byte has any keys set.
+ if (chordBytes[index]) index++;
+
+ // byte 3
+ // -F
+ if (currentChord[1][6]) chordBytes[index] |= B10000001;
+ // -R
+ if (currentChord[2][6]) chordBytes[index] |= B10000010;
+ // -P
+ if (currentChord[1][7]) chordBytes[index] |= B10000100;
+ // -B
+ if (currentChord[2][7]) chordBytes[index] |= B10001000;
+ // -L
+ if (currentChord[1][8]) chordBytes[index] |= B10010000;
+ // -G
+ if (currentChord[2][8]) chordBytes[index] |= B10100000;
+ // Increment the index if the current byte has any keys set.
+ if (chordBytes[index]) index++;
+
+ // byte 4
+ // -T
+ if (currentChord[1][9]) chordBytes[index] |= B11000001;
+ // -S
+ if (currentChord[2][9]) chordBytes[index] |= B11000010;
+ // -D
+ if (currentChord[1][10]) chordBytes[index] |= B11000100;
+ // -Z
+ if (currentChord[2][10]) chordBytes[index] |= B11001000;
+ // #
+ if (currentChord[0][1] || currentChord[0][2] || currentChord[0][3] || currentChord[0][4] || currentChord[0][6] || currentChord[0][7] || currentChord[0][8] || currentChord[0][9]) chordBytes[index] |= B11010000;
+ // Increment the index if the current byte has any keys set.
+ if (chordBytes[index]) index++;
+
+ // Now we have index bytes followed by a zero byte where 0 < index <= 4.
+ index++; // Increment index to include the trailing zero byte.
+ for (int i = 0; i < index; i++) {
+ Serial.write(chordBytes[i]);
+ }
+}
+
+// Send the chord using the current protocol. If there are fn keys
+// pressed, delegate to the corresponding function instead.
+// In future versions, there should also be a way to handle fn keys presses before
+// they are released, eg. for mouse emulation functionality or custom key presses.
+void sendChord() {
+ // If fn keys have been pressed, delegate to corresponding method and return
+ if (currentChord[1][0] && currentChord[2][0]) {
+ fn1fn2();
+ return;
+ } else if (currentChord[1][0]) {
+ fn1();
+ return;
+ } else if (currentChord[2][0]) {
+ fn2();
+ return;
+ }
+
+ if (protocol == NKRO) {
+ sendChordNkro();
+ } else if (protocol == GEMINI) {
+ sendChordGemini();
+ } else {
+ sendChordTxBolt();
+ }
+}
+
+// Fn1 functions
+//
+// This function is called when "fn1" key has been pressed, but not "fn2".
+// Tip: maybe it is better to avoid using "fn1" key alone in order to avoid
+// accidental activation?
+//
+// Current functions:
+// PH-PB -> Set NKRO Keyboard emulation mode
+// PH-G -> Set Gemini PR protocol mode
+// PH-B -> Set TX Bolt protocol mode
+void fn1() {
+ // "PH" -> Set protocol
+ if (currentChord[1][3] && currentChord[1][4]) {
+ // "-PB" -> NKRO Keyboard
+ if (currentChord[1][7] && currentChord[2][7]) {
+ protocol = NKRO;
+ }
+ // "-G" -> Gemini PR
+ else if (currentChord[2][8]) {
+ protocol = GEMINI;
+ }
+ // "-B" -> TX Bolt
+ else if (currentChord[2][7]) {
+ protocol = TXBOLT;
+ }
+ }
+}
+
+// Fn2 functions
+//
+// This function is called when "fn2" key has been pressed, but not "fn1".
+// Tip: maybe it is better to avoid using "fn2" key alone in order to avoid
+// accidental activation?
+//
+// Current functions: none.
+void fn2() {
+
+}
+
+// Fn1-Fn2 functions
+//
+// This function is called when both "fn1" and "fn1" keys have been pressed.
+//
+// Current functions:
+// HR-P -> LED intensity up
+// HR-F -> LED intensity down
+void fn1fn2() {
+ // "HR" -> Change LED intensity
+ if (currentChord[1][4] && currentChord[2][4]) {
+ // "-P" -> LED intensity up
+ if (currentChord[1][7]) {
+ if (ledIntensity == 0) ledIntensity +=1;
+ else if(ledIntensity < 50) ledIntensity += 10;
+ else ledIntensity += 30;
+ if (ledIntensity > 255) ledIntensity = 0;
+ analogWrite(ledPin, ledIntensity);
+ }
+ // "-F" -> LED intensity down
+ if (currentChord[1][6]) {
+ if(ledIntensity == 0) ledIntensity = 255;
+ else if(ledIntensity < 50) ledIntensity -= 10;
+ else ledIntensity -= 30;
+ if (ledIntensity < 1) ledIntensity = 0;
+ analogWrite(ledPin, ledIntensity);
+ }
+ }
+}