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

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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. // Fn1 functions
  321. //
  322. // This function is called when "fn1" key has been pressed, but not "fn2".
  323. // Tip: maybe it is better to avoid using "fn1" key alone in order to avoid
  324. // accidental activation?
  325. //
  326. // Current functions:
  327. // PH-G -> Set Gemini PR protocol mode
  328. // PH-B -> Set TX Bolt protocol mode
  329. void fn1() {
  330. // "PH" -> Set protocol
  331. if (currentChord[0][2] && currentChord[0][3]) {
  332. // "-G" -> Gemini PR
  333. if (currentChord[4][2]) {
  334. protocol = GEMINI;
  335. }
  336. // "-B" -> TX Bolt
  337. if (currentChord[4][1]) {
  338. protocol = TXBOLT;
  339. }
  340. }
  341. }
  342. // Fn2 functions
  343. //
  344. // This function is called when "fn2" key has been pressed, but not "fn1".
  345. // Tip: maybe it is better to avoid using "fn2" key alone in order to avoid
  346. // accidental activation?
  347. //
  348. // Current functions: none.
  349. void fn2() {
  350. }
  351. // Fn1-Fn2 functions
  352. //
  353. // This function is called when both "fn1" and "fn1" keys have been pressed.
  354. //
  355. // Current functions:
  356. // HR-P -> LED intensity up
  357. // HR-F -> LED intensity down
  358. void fn1fn2() {
  359. // "HR" -> Change LED intensity
  360. if (currentChord[0][3] && currentChord[1][3]) {
  361. // "-P" -> LED intensity up
  362. if (currentChord[3][1]) {
  363. if (ledIntensity == 0) ledIntensity +=1;
  364. else if(ledIntensity < 50) ledIntensity += 10;
  365. else ledIntensity += 30;
  366. if (ledIntensity > 255) ledIntensity = 0;
  367. analogWrite(ledPin, ledIntensity);
  368. }
  369. // "-F" -> LED intensity down
  370. if (currentChord[3][0]) {
  371. if(ledIntensity == 0) ledIntensity = 255;
  372. else if(ledIntensity < 50) ledIntensity -= 10;
  373. else ledIntensity -= 30;
  374. if (ledIntensity < 1) ledIntensity = 0;
  375. analogWrite(ledPin, ledIntensity);
  376. }
  377. }
  378. }