StenoFW is a firmware for StenoBoard keyboards.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

StenoFW.ino 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /**
  2. * StenoFW is a firmware for Stenoboard keyboards.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * Copyright 2014 Emanuele Caruso. See LICENSE.txt for details.
  18. */
  19. #define ROWS 5
  20. #define COLS 6
  21. /* The following matrix is shown here for reference only.
  22. char keys[ROWS][COLS] = {
  23. {'S', 'T', 'P', 'H', '*', Fn1},
  24. {'S', 'K', 'W', 'R', '*', Fn2},
  25. {'a', 'o', 'e', 'u', '#'},
  26. {'f', 'p', 'l', 't', 'd'},
  27. {'r', 'b', 'g', 's', 'z'}
  28. };*/
  29. // Configuration variables
  30. int rowPins[ROWS] = {13, 12, 11, 10, 9};
  31. int colPins[COLS] = {8, 7, 6, 5, 4, 2};
  32. int ledPin = 3;
  33. long debounceMillis = 20;
  34. // Keyboard state variables
  35. boolean isStrokeInProgress = false;
  36. boolean currentChord[ROWS][COLS];
  37. boolean currentKeyReadings[ROWS][COLS];
  38. boolean debouncingKeys[ROWS][COLS];
  39. unsigned long debouncingMicros[ROWS][COLS];
  40. // Other state variables
  41. int ledIntensity = 1; // Min 0 - Max 255
  42. // This is called when the keyboard is connected
  43. void setup() {
  44. Serial.begin(9600);
  45. for (int i = 0; i < COLS; i++)
  46. pinMode(colPins[i], INPUT_PULLUP);
  47. for (int i = 0; i < ROWS; i++) {
  48. pinMode(rowPins[i], OUTPUT);
  49. digitalWrite(rowPins[i], HIGH);
  50. }
  51. pinMode(ledPin, OUTPUT);
  52. analogWrite(ledPin, ledIntensity);
  53. clearBooleanMatrixes();
  54. }
  55. // Read key states and handle all chord events
  56. void loop() {
  57. readKeys();
  58. boolean isAnyKeyPressed = true;
  59. // If stroke is not in progress, check debouncing keys
  60. if (!isStrokeInProgress) {
  61. checkAlreadyDebouncingKeys();
  62. if (!isStrokeInProgress) checkNewDebouncingKeys();
  63. }
  64. // If any key was pressed, record all pressed keys
  65. if (isStrokeInProgress) {
  66. isAnyKeyPressed = recordCurrentKeys();
  67. }
  68. // If all keys have been released, send the chord and reset global state
  69. if (!isAnyKeyPressed) {
  70. sendChord();
  71. clearBooleanMatrixes();
  72. isStrokeInProgress = false;
  73. }
  74. }
  75. // Record all pressed keys into current chord. Return false if no key is currently pressed
  76. boolean recordCurrentKeys() {
  77. boolean isAnyKeyPressed = false;
  78. for (int i = 0; i < ROWS; i++) {
  79. for (int j = 0; j < COLS; j++) {
  80. if (currentKeyReadings[i][j] == true) {
  81. currentChord[i][j] = true;
  82. isAnyKeyPressed = true;
  83. }
  84. }
  85. }
  86. return isAnyKeyPressed;
  87. }
  88. // If a key is pressed, add it to debouncing keys and record the time
  89. void checkNewDebouncingKeys() {
  90. for (int i = 0; i < ROWS; i++) {
  91. for (int j = 0; j < COLS; j++) {
  92. if (currentKeyReadings[i][j] == true && debouncingKeys[i][j] == false) {
  93. debouncingKeys[i][j] = true;
  94. debouncingMicros[i][j] = micros();
  95. }
  96. }
  97. }
  98. }
  99. // Check already debouncing keys. If a key debounces, start chord recording.
  100. void checkAlreadyDebouncingKeys() {
  101. for (int i = 0; i < ROWS; i++) {
  102. for (int j = 0; j < COLS; j++) {
  103. if (debouncingKeys[i][j] == true && currentKeyReadings[i][j] == false) {
  104. debouncingKeys[i][j] = false;
  105. continue;
  106. }
  107. if (debouncingKeys[i][j] == true && micros() - debouncingMicros[i][j] / 1000 > debounceMillis) {
  108. isStrokeInProgress = true;
  109. currentChord[i][j] = true;
  110. return;
  111. }
  112. }
  113. }
  114. }
  115. // Set all values of all boolean matrixes to false
  116. void clearBooleanMatrixes() {
  117. clearBooleanMatrix(currentChord, false);
  118. clearBooleanMatrix(currentKeyReadings, false);
  119. clearBooleanMatrix(debouncingKeys, false);
  120. }
  121. // Set all values of the passed matrix to the given value
  122. void clearBooleanMatrix(boolean booleanMatrix[][COLS], boolean value) {
  123. for (int i = 0; i < ROWS; i++) {
  124. for (int j = 0; j < COLS; j++) {
  125. booleanMatrix[i][j] = value;
  126. }
  127. }
  128. }
  129. // Read all keys
  130. void readKeys() {
  131. for (int i = 0; i < ROWS; i++) {
  132. digitalWrite(rowPins[i], LOW);
  133. for (int j = 0; j < COLS; j++)
  134. currentKeyReadings[i][j] = digitalRead(colPins[j]) == LOW ? true : false;
  135. digitalWrite(rowPins[i], HIGH);
  136. }
  137. }
  138. // Send current chord over serial using the Gemini protocol. If there are fn keys
  139. // pressed, delegate to the corresponding function instead.
  140. // In future versions, there should also be a way to handle fn keys presses before
  141. // they are released, eg. for mouse emulation functionality or custom key presses.
  142. void sendChord() {
  143. // Initialize chord bytes
  144. byte chordBytes[] = {B10000000, B0, B0, B0, B0, B0};
  145. // If fn keys have been pressed, delegate to corresponding method and return
  146. if (currentChord[0][5] && currentChord[1][5]) {
  147. fn1fn2();
  148. return;
  149. } else if (currentChord[0][5]) {
  150. fn1();
  151. return;
  152. } else if (currentChord[1][5]) {
  153. fn2();
  154. return;
  155. }
  156. // Byte 0
  157. if (currentChord[2][4]) {
  158. chordBytes[0] = B10000001;
  159. }
  160. // Byte 1
  161. if (currentChord[0][0] || currentChord[1][0]) {
  162. chordBytes[1] += B01000000;
  163. }
  164. if (currentChord[0][1]) {
  165. chordBytes[1] += B00010000;
  166. }
  167. if (currentChord[1][1]) {
  168. chordBytes[1] += B00001000;
  169. }
  170. if (currentChord[0][2]) {
  171. chordBytes[1] += B00000100;
  172. }
  173. if (currentChord[1][2]) {
  174. chordBytes[1] += B00000010;
  175. }
  176. if (currentChord[0][3]) {
  177. chordBytes[1] += B00000001;
  178. }
  179. // Byte 2
  180. if (currentChord[1][3]) {
  181. chordBytes[2] += B01000000;
  182. }
  183. if (currentChord[2][0]) {
  184. chordBytes[2] += B00100000;
  185. }
  186. if (currentChord[2][1]) {
  187. chordBytes[2] += B00010000;
  188. }
  189. if (currentChord[0][4] || currentChord[1][4]) {
  190. chordBytes[2] += B00001000;
  191. }
  192. // Byte 3
  193. if (currentChord[2][2]) {
  194. chordBytes[3] += B00001000;
  195. }
  196. if (currentChord[2][3]) {
  197. chordBytes[3] += B00000100;
  198. }
  199. if (currentChord[3][0]) {
  200. chordBytes[3] += B00000010;
  201. }
  202. if (currentChord[4][0]) {
  203. chordBytes[3] += B00000001;
  204. }
  205. // Byte 4
  206. if (currentChord[3][1]) {
  207. chordBytes[4] += B01000000;
  208. }
  209. if (currentChord[4][1]) {
  210. chordBytes[4] += B00100000;
  211. }
  212. if (currentChord[3][2]) {
  213. chordBytes[4] += B00010000;
  214. }
  215. if (currentChord[4][2]) {
  216. chordBytes[4] += B00001000;
  217. }
  218. if (currentChord[3][3]) {
  219. chordBytes[4] += B00000100;
  220. }
  221. if (currentChord[4][3]) {
  222. chordBytes[4] += B00000010;
  223. }
  224. if (currentChord[3][4]) {
  225. chordBytes[4] += B00000001;
  226. }
  227. // Byte 5
  228. if (currentChord[4][4]) {
  229. chordBytes[5] += B00000001;
  230. }
  231. // Send chord bytes over serial
  232. for (int i = 0; i < 6; i++) {
  233. Serial.write(chordBytes[i]);
  234. }
  235. }
  236. // This function is called when only "fn1" key has been pressed.
  237. void fn1() {
  238. }
  239. // This function is called when only "fn2" key has been pressed.
  240. void fn2() {
  241. }
  242. // This function is called when both "fn1" and "fn1" key has been pressed.
  243. void fn1fn2() {
  244. // "HR" -> Change LED intensity
  245. if (currentChord[0][3] && currentChord[1][3]) {
  246. // "P" -> LED intensity up
  247. if (currentChord[3][1]) {
  248. if (ledIntensity == 0) ledIntensity +=1;
  249. else if(ledIntensity < 50) ledIntensity += 10;
  250. else ledIntensity += 30;
  251. if (ledIntensity > 255) ledIntensity = 0;
  252. analogWrite(ledPin, ledIntensity);
  253. }
  254. // "F" -> LED intensity down
  255. if (currentChord[3][0]) {
  256. if(ledIntensity == 0) ledIntensity = 255;
  257. else if(ledIntensity < 50) ledIntensity -= 10;
  258. else ledIntensity -= 30;
  259. if (ledIntensity < 1) ledIntensity = 0;
  260. analogWrite(ledPin, ledIntensity);
  261. }
  262. }
  263. }