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 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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. // Protocol state
  43. #define GEMINI 0
  44. #define TXBOLT 1
  45. int protocol = GEMINI;
  46. // This is called when the keyboard is connected
  47. void setup() {
  48. Serial.begin(9600);
  49. for (int i = 0; i < COLS; i++)
  50. pinMode(colPins[i], INPUT_PULLUP);
  51. for (int i = 0; i < ROWS; i++) {
  52. pinMode(rowPins[i], OUTPUT);
  53. digitalWrite(rowPins[i], HIGH);
  54. }
  55. pinMode(ledPin, OUTPUT);
  56. analogWrite(ledPin, ledIntensity);
  57. clearBooleanMatrixes();
  58. }
  59. // Read key states and handle all chord events
  60. void loop() {
  61. readKeys();
  62. boolean isAnyKeyPressed = true;
  63. // If stroke is not in progress, check debouncing keys
  64. if (!isStrokeInProgress) {
  65. checkAlreadyDebouncingKeys();
  66. if (!isStrokeInProgress) checkNewDebouncingKeys();
  67. }
  68. // If any key was pressed, record all pressed keys
  69. if (isStrokeInProgress) {
  70. isAnyKeyPressed = recordCurrentKeys();
  71. }
  72. // If all keys have been released, send the chord and reset global state
  73. if (!isAnyKeyPressed) {
  74. sendChord();
  75. clearBooleanMatrixes();
  76. isStrokeInProgress = false;
  77. }
  78. }
  79. // Record all pressed keys into current chord. Return false if no key is currently pressed
  80. boolean recordCurrentKeys() {
  81. boolean isAnyKeyPressed = false;
  82. for (int i = 0; i < ROWS; i++) {
  83. for (int j = 0; j < COLS; j++) {
  84. if (currentKeyReadings[i][j] == true) {
  85. currentChord[i][j] = true;
  86. isAnyKeyPressed = true;
  87. }
  88. }
  89. }
  90. return isAnyKeyPressed;
  91. }
  92. // If a key is pressed, add it to debouncing keys and record the time
  93. void checkNewDebouncingKeys() {
  94. for (int i = 0; i < ROWS; i++) {
  95. for (int j = 0; j < COLS; j++) {
  96. if (currentKeyReadings[i][j] == true && debouncingKeys[i][j] == false) {
  97. debouncingKeys[i][j] = true;
  98. debouncingMicros[i][j] = micros();
  99. }
  100. }
  101. }
  102. }
  103. // Check already debouncing keys. If a key debounces, start chord recording.
  104. void checkAlreadyDebouncingKeys() {
  105. for (int i = 0; i < ROWS; i++) {
  106. for (int j = 0; j < COLS; j++) {
  107. if (debouncingKeys[i][j] == true && currentKeyReadings[i][j] == false) {
  108. debouncingKeys[i][j] = false;
  109. continue;
  110. }
  111. if (debouncingKeys[i][j] == true && micros() - debouncingMicros[i][j] / 1000 > debounceMillis) {
  112. isStrokeInProgress = true;
  113. currentChord[i][j] = true;
  114. return;
  115. }
  116. }
  117. }
  118. }
  119. // Set all values of all boolean matrixes to false
  120. void clearBooleanMatrixes() {
  121. clearBooleanMatrix(currentChord, false);
  122. clearBooleanMatrix(currentKeyReadings, false);
  123. clearBooleanMatrix(debouncingKeys, false);
  124. }
  125. // Set all values of the passed matrix to the given value
  126. void clearBooleanMatrix(boolean booleanMatrix[][COLS], boolean value) {
  127. for (int i = 0; i < ROWS; i++) {
  128. for (int j = 0; j < COLS; j++) {
  129. booleanMatrix[i][j] = value;
  130. }
  131. }
  132. }
  133. // Read all keys
  134. void readKeys() {
  135. for (int i = 0; i < ROWS; i++) {
  136. digitalWrite(rowPins[i], LOW);
  137. for (int j = 0; j < COLS; j++)
  138. currentKeyReadings[i][j] = digitalRead(colPins[j]) == LOW ? true : false;
  139. digitalWrite(rowPins[i], HIGH);
  140. }
  141. }
  142. // Send current chord over serial using the Gemini protocol.
  143. void sendChordGemini() {
  144. // Initialize chord bytes
  145. byte chordBytes[] = {B10000000, B0, B0, B0, B0, B0};
  146. // Byte 0
  147. if (currentChord[2][4]) {
  148. chordBytes[0] = B10000001;
  149. }
  150. // Byte 1
  151. if (currentChord[0][0] || currentChord[1][0]) {
  152. chordBytes[1] += B01000000;
  153. }
  154. if (currentChord[0][1]) {
  155. chordBytes[1] += B00010000;
  156. }
  157. if (currentChord[1][1]) {
  158. chordBytes[1] += B00001000;
  159. }
  160. if (currentChord[0][2]) {
  161. chordBytes[1] += B00000100;
  162. }
  163. if (currentChord[1][2]) {
  164. chordBytes[1] += B00000010;
  165. }
  166. if (currentChord[0][3]) {
  167. chordBytes[1] += B00000001;
  168. }
  169. // Byte 2
  170. if (currentChord[1][3]) {
  171. chordBytes[2] += B01000000;
  172. }
  173. if (currentChord[2][0]) {
  174. chordBytes[2] += B00100000;
  175. }
  176. if (currentChord[2][1]) {
  177. chordBytes[2] += B00010000;
  178. }
  179. if (currentChord[0][4] || currentChord[1][4]) {
  180. chordBytes[2] += B00001000;
  181. }
  182. // Byte 3
  183. if (currentChord[2][2]) {
  184. chordBytes[3] += B00001000;
  185. }
  186. if (currentChord[2][3]) {
  187. chordBytes[3] += B00000100;
  188. }
  189. if (currentChord[3][0]) {
  190. chordBytes[3] += B00000010;
  191. }
  192. if (currentChord[4][0]) {
  193. chordBytes[3] += B00000001;
  194. }
  195. // Byte 4
  196. if (currentChord[3][1]) {
  197. chordBytes[4] += B01000000;
  198. }
  199. if (currentChord[4][1]) {
  200. chordBytes[4] += B00100000;
  201. }
  202. if (currentChord[3][2]) {
  203. chordBytes[4] += B00010000;
  204. }
  205. if (currentChord[4][2]) {
  206. chordBytes[4] += B00001000;
  207. }
  208. if (currentChord[3][3]) {
  209. chordBytes[4] += B00000100;
  210. }
  211. if (currentChord[4][3]) {
  212. chordBytes[4] += B00000010;
  213. }
  214. if (currentChord[3][4]) {
  215. chordBytes[4] += B00000001;
  216. }
  217. // Byte 5
  218. if (currentChord[4][4]) {
  219. chordBytes[5] += B00000001;
  220. }
  221. // Send chord bytes over serial
  222. for (int i = 0; i < 6; i++) {
  223. Serial.write(chordBytes[i]);
  224. }
  225. }
  226. void sendChordTxBolt() {
  227. byte chordBytes[] = {B0, B0, B0, B0, B0};
  228. int index = 0;
  229. // TX Bolt uses a variable length packet. Only those bytes that have active
  230. // keys are sent. The header bytes indicate which keys are being sent. They
  231. // must be sent in order. It is a good idea to send a zero after every packet.
  232. // 00XXXXXX 01XXXXXX 10XXXXXX 110XXXXX
  233. // HWPKTS UE*OAR GLBPRF #ZDST
  234. // byte 1
  235. // S-
  236. if (currentChord[0][0] || currentChord[1][0]) chordBytes[index] |= B00000001;
  237. // T-
  238. if (currentChord[0][1]) chordBytes[index] |= B00000010;
  239. // K-
  240. if (currentChord[1][1]) chordBytes[index] |= B00000100;
  241. // P-
  242. if (currentChord[0][2]) chordBytes[index] |= B00001000;
  243. // W-
  244. if (currentChord[1][2]) chordBytes[index] |= B00010000;
  245. // H-
  246. if (currentChord[0][3]) chordBytes[index] |= B00100000;
  247. // Increment the index if the current byte has any keys set.
  248. if (chordBytes[index]) index++;
  249. // byte 2
  250. // R-
  251. if (currentChord[1][3]) chordBytes[index] |= B01000001;
  252. // A
  253. if (currentChord[2][0]) chordBytes[index] |= B01000010;
  254. // O
  255. if (currentChord[2][1]) chordBytes[index] |= B01000100;
  256. // *
  257. if (currentChord[0][4] || currentChord[1][4]) chordBytes[index] |= B01001000;
  258. // E
  259. if (currentChord[2][2]) chordBytes[index] |= B01010000;
  260. // U
  261. if (currentChord[2][3]) chordBytes[index] |= B01100000;
  262. // Increment the index if the current byte has any keys set.
  263. if (chordBytes[index]) index++;
  264. // byte 3
  265. // -F
  266. if (currentChord[3][0]) chordBytes[index] |= B10000001;
  267. // -R
  268. if (currentChord[4][0]) chordBytes[index] |= B10000010;
  269. // -P
  270. if (currentChord[3][1]) chordBytes[index] |= B10000100;
  271. // -B
  272. if (currentChord[4][1]) chordBytes[index] |= B10001000;
  273. // -L
  274. if (currentChord[3][2]) chordBytes[index] |= B10010000;
  275. // -G
  276. if (currentChord[4][2]) chordBytes[index] |= B10100000;
  277. // Increment the index if the current byte has any keys set.
  278. if (chordBytes[index]) index++;
  279. // byte 4
  280. // -T
  281. if (currentChord[3][3]) chordBytes[index] |= B11000001;
  282. // -S
  283. if (currentChord[4][3]) chordBytes[index] |= B11000010;
  284. // -D
  285. if (currentChord[3][4]) chordBytes[index] |= B11000100;
  286. // -Z
  287. if (currentChord[4][4]) chordBytes[index] |= B11001000;
  288. // #
  289. if (currentChord[2][4]) chordBytes[index] |= B11010000;
  290. // Increment the index if the current byte has any keys set.
  291. if (chordBytes[index]) index++;
  292. // Now we have index bytes followed by a zero byte where 0 < index <= 4.
  293. index++; // Increment index to include the trailing zero byte.
  294. for (int i = 0; i < index; i++) {
  295. Serial.write(chordBytes[i]);
  296. }
  297. }
  298. // Send the chord using the current protocol. If there are fn keys
  299. // pressed, delegate to the corresponding function instead.
  300. // In future versions, there should also be a way to handle fn keys presses before
  301. // they are released, eg. for mouse emulation functionality or custom key presses.
  302. void sendChord() {
  303. // If fn keys have been pressed, delegate to corresponding method and return
  304. if (currentChord[0][5] && currentChord[1][5]) {
  305. fn1fn2();
  306. return;
  307. } else if (currentChord[0][5]) {
  308. fn1();
  309. return;
  310. } else if (currentChord[1][5]) {
  311. fn2();
  312. return;
  313. }
  314. if (protocol == GEMINI) {
  315. sendChordGemini();
  316. } else {
  317. sendChordTxBolt();
  318. }
  319. }
  320. // This function is called when only "fn1" key has been pressed.
  321. void fn1() {
  322. protocol = GEMINI;
  323. }
  324. // This function is called when only "fn2" key has been pressed.
  325. void fn2() {
  326. protocol = TXBOLT;
  327. }
  328. // This function is called when both "fn1" and "fn1" key has been pressed.
  329. void fn1fn2() {
  330. // "HR" -> Change LED intensity
  331. if (currentChord[0][3] && currentChord[1][3]) {
  332. // "P" -> LED intensity up
  333. if (currentChord[3][1]) {
  334. if (ledIntensity == 0) ledIntensity +=1;
  335. else if(ledIntensity < 50) ledIntensity += 10;
  336. else ledIntensity += 30;
  337. if (ledIntensity > 255) ledIntensity = 0;
  338. analogWrite(ledPin, ledIntensity);
  339. }
  340. // "F" -> LED intensity down
  341. if (currentChord[3][0]) {
  342. if(ledIntensity == 0) ledIntensity = 255;
  343. else if(ledIntensity < 50) ledIntensity -= 10;
  344. else ledIntensity -= 30;
  345. if (ledIntensity < 1) ledIntensity = 0;
  346. analogWrite(ledPin, ledIntensity);
  347. }
  348. }
  349. }