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

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