No Description
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.

keymapper.ino 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. Usage: Convert standard QWERTY keyboard to any keyboard layout you want
  3. Default layout: QWERTY
  4. Reserved key combination to switch layout:
  5. Ctrl-Shift 0 => QWERTY (DEFAULT)
  6. Ctrl-Shift 1 => tarmak1
  7. Ctrl-Shift 2 => tarmak2
  8. Ctrl-Shift 3 => tarmak3
  9. Ctrl-Shift 4 => tarmak4
  10. Ctrl-Shift 5 => Colemak
  11. Ctrl-Shift 6 => Dvorak
  12. Ctrl-Shift 7 => Workman
  13. */
  14. #ifdef __MK66FX1M0__
  15. /* Teensy 3.6 */
  16. #define TEENSY_USB_HOST 1
  17. #endif
  18. #include <avr/pgmspace.h>
  19. #ifdef TEENSY_USB_HOST
  20. #include <USBHost_t36.h>
  21. #else
  22. #include <Usb.h>
  23. #include <hidboot.h>
  24. #endif
  25. #include <Keyboard.h>
  26. #include "keymapper_game.h"
  27. //#define DEBUG
  28. #define modeLED LED_BUILTIN
  29. // function definitions
  30. bool HandleReservedKeystrokes(uint8_t *buf);
  31. inline void SendKeysToHost (uint8_t *buf);
  32. void play_word_game(void);
  33. inline void LatchKey (uint8_t keyToLatch);
  34. // variable definitions
  35. typedef enum
  36. {
  37. qwerty = 0,
  38. tarmak1,
  39. tarmak2,
  40. tarmak3,
  41. tarmak4,
  42. colemak,
  43. dvorak,
  44. workman
  45. } KeyboardLayout;
  46. // Keymap based on the scancodes from 4 to 57, refer to the HID usage table on the meaning of each element
  47. PROGMEM const uint8_t qwertyKeymap[] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57};
  48. PROGMEM const uint8_t tarmak1Keymap[] = {4, 5, 6, 7, 13, 9, 10, 11, 12, 17, 8, 15, 16, 14, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 42};
  49. PROGMEM const uint8_t tarmak2Keymap[] = {4, 5, 6, 7, 9, 23, 13, 11, 12, 17, 8, 15, 16, 14, 18, 19, 20, 21, 22, 10, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 42};
  50. PROGMEM const uint8_t tarmak3Keymap[] = {4, 5, 6, 7, 9, 23, 51, 11, 12, 17, 8, 15, 16, 14, 28, 19, 20, 21, 22, 10, 24, 25, 26, 27, 13, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 18, 52, 53, 54, 55, 56, 42};
  51. PROGMEM const uint8_t tarmak4Keymap[] = {4, 5, 6, 7, 9, 23, 51, 11, 24, 17, 8, 12, 16, 14, 28, 19, 20, 21, 22, 10, 15, 25, 26, 27, 13, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 18, 52, 53, 54, 55, 56, 42};
  52. PROGMEM const uint8_t colemakKeymap[] = {4, 5, 6, 22, 9, 23, 7, 11, 24, 17, 8, 12, 16, 14, 28, 51, 20, 19, 21, 10, 15, 25, 26, 27, 13, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 18, 52, 53, 54, 55, 56, 42};
  53. PROGMEM const uint8_t dvorakKeymap[] = {4, 27, 13, 8, 55, 24, 12, 7, 6, 11, 23, 17, 16, 5, 21, 15, 52, 19, 18, 28, 10, 14, 54, 20, 9, 51, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 47, 48, 56, 46, 49, 50, 22, 45, 53, 26, 25, 29, 57};
  54. PROGMEM const uint8_t workmanKeymap[] = {4, 25, 16, 11, 21, 23, 10, 28, 24, 17, 8, 18, 15, 14, 19, 51, 20, 26, 22, 5, 9, 6, 7, 27, 13, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 12, 52, 53, 54, 55, 56, 42};
  55. const uint8_t *Keymap[] =
  56. {
  57. qwertyKeymap,
  58. tarmak1Keymap,
  59. tarmak2Keymap,
  60. tarmak3Keymap,
  61. tarmak4Keymap,
  62. colemakKeymap,
  63. dvorakKeymap,
  64. workmanKeymap
  65. };
  66. // global variables
  67. //uint32_t ledBlinkTime = millis();
  68. //uint16_t ledBlinkDelay = 500;
  69. KeyboardLayout CurrentLayout = qwerty;
  70. uint8_t KeyBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0};
  71. uint8_t specialKeyLatch = 0;
  72. bool specialKeyLatchReleased = false;
  73. void print_hex(const uint8_t *buf, size_t len)
  74. {
  75. #ifdef DEBUG
  76. for (size_t i = 0; i < 8; i++) {
  77. if (i) Serial.print(' ');
  78. Serial.print(buf[i], HEX);
  79. }
  80. Serial.println();
  81. #endif
  82. }
  83. // *******************************************************************************************
  84. // Parse
  85. // *******************************************************************************************
  86. void remapper(uint8_t *buf, uint8_t len)
  87. {
  88. uint8_t i;
  89. // On error - return
  90. if (buf[2] == 1)
  91. return;
  92. print_hex(buf, len);
  93. KeyBuffer[0] = buf[0];
  94. if (!HandleReservedKeystrokes(buf))
  95. {
  96. specialKeyLatchReleased = true;
  97. // remap all keys according to the existing keymap
  98. for (i = 2; i < 8; i++)
  99. {
  100. // handle special case of Shift-CAPSLOCK to be ignored by the remapper
  101. if (buf[i] == KEY_CAPS_LOCK && buf[0] & 0x22)
  102. {
  103. KeyBuffer[i] = KEY_CAPS_LOCK;
  104. LatchKey(KEY_CAPS_LOCK);
  105. }
  106. else
  107. {
  108. // print the key based on the current layout
  109. if (buf[i] >= 4 && buf[i] <= 57) // transpose of 4 becoz our array starts from 0 but A is 4
  110. // limit check to 57, which is the last mappable key (CAPSLOCK)
  111. {
  112. // if it was a special key of shift-CAPS, then only allow mapping if the key has been released at least once
  113. if (buf[i] != specialKeyLatch)
  114. KeyBuffer[i] = pgm_read_byte(Keymap[CurrentLayout] + buf[i] - 4);
  115. else // key is not released yet. do not allow mapping
  116. {
  117. // Serial.println("key is not released");
  118. KeyBuffer[i] = 0;
  119. specialKeyLatchReleased = false;
  120. }
  121. }
  122. else
  123. KeyBuffer[i] = buf[i];
  124. }
  125. }
  126. // reset latch if key is released
  127. if (specialKeyLatchReleased)
  128. {
  129. // Serial.println("latch is released");
  130. specialKeyLatch = 0;
  131. }
  132. // send out key press
  133. SendKeysToHost (KeyBuffer);
  134. print_hex(KeyBuffer, 8);
  135. }
  136. }
  137. bool HandleReservedKeystrokes(uint8_t *buf) // return true if it is a reserved keystroke
  138. {
  139. uint8_t mod = buf[0]; // read the modifier byte
  140. uint8_t numKeysPressed = 0;
  141. uint8_t keyPosition = 0;
  142. // check that there is only 1 single key that is pressed
  143. for (uint8_t i = 2; i < 8; i++) if (buf[i] > 0) {
  144. numKeysPressed++;
  145. keyPosition = i;
  146. }
  147. if (numKeysPressed != 1) return false; // only allow single keypress for reserved keystrokes (besides modifiers)
  148. // check if we are changing layouts
  149. if ((mod & 0x22) && (mod & 0x11)) { // Shift-Alt keystrokes
  150. switch (buf[keyPosition]) {
  151. case 0x27: // 0
  152. CurrentLayout = qwerty;
  153. digitalWrite(modeLED, LOW);
  154. LatchKey(buf[keyPosition]);
  155. return true;
  156. case 0x1e: // 1
  157. CurrentLayout = tarmak1;
  158. digitalWrite(modeLED, HIGH);
  159. LatchKey(buf[keyPosition]);
  160. return true;
  161. case 0x1f: // 2
  162. CurrentLayout = tarmak2;
  163. digitalWrite(modeLED, HIGH);
  164. LatchKey(buf[keyPosition]);
  165. return true;
  166. case 0x20: // 3
  167. CurrentLayout = tarmak3;
  168. digitalWrite(modeLED, HIGH);
  169. LatchKey(buf[keyPosition]);
  170. return true;
  171. case 0x21: // 4
  172. CurrentLayout = tarmak4;
  173. digitalWrite(modeLED, HIGH);
  174. LatchKey(buf[keyPosition]);
  175. return true;
  176. case 0x22: // 5
  177. CurrentLayout = colemak;
  178. digitalWrite(modeLED, HIGH);
  179. LatchKey(buf[keyPosition]);
  180. return true;
  181. case 0x23: // 6
  182. CurrentLayout = dvorak;
  183. digitalWrite(modeLED, HIGH);
  184. LatchKey(buf[keyPosition]);
  185. return true;
  186. case 0x24: // 7
  187. CurrentLayout = workman;
  188. digitalWrite(modeLED, HIGH);
  189. LatchKey(buf[keyPosition]);
  190. return true;
  191. case 0x2c: // space bar
  192. play_word_game();
  193. LatchKey(buf[keyPosition]);
  194. return true;
  195. }
  196. }
  197. return false;
  198. }
  199. inline void SendKeysToHost (uint8_t *buf)
  200. {
  201. #ifdef TEENSYDUINO
  202. Keyboard.set_modifier(buf[0]);
  203. Keyboard.set_key1(buf[2]);
  204. Keyboard.set_key2(buf[3]);
  205. Keyboard.set_key3(buf[4]);
  206. Keyboard.set_key4(buf[5]);
  207. Keyboard.set_key5(buf[6]);
  208. Keyboard.set_key6(buf[7]);
  209. Keyboard.send_now();
  210. #else /* TEENSYDUINO */
  211. HID().SendReport(2, buf, 8);
  212. #endif /* TEENSYDUINO */
  213. }
  214. inline void LatchKey (uint8_t keyToLatch)
  215. {
  216. specialKeyLatch = keyToLatch;
  217. specialKeyLatchReleased = false;
  218. // Serial.print(keyToLatch);
  219. // Serial.println(" is latched");
  220. }
  221. // *******************************************************************************************
  222. // WORD GAME!!!
  223. // *******************************************************************************************
  224. void play_word_game(void)
  225. {
  226. char buffer[GAME_MAXWORDLENGTH];
  227. char priorityAlphabets[10];
  228. char *instrPtr;
  229. uint16_t randNum;
  230. switch (CurrentLayout) {
  231. case tarmak1:
  232. strcpy (priorityAlphabets, "nek");
  233. break;
  234. case tarmak2:
  235. strcpy (priorityAlphabets, "ftg");
  236. break;
  237. case tarmak3:
  238. strcpy (priorityAlphabets, "jyo");
  239. break;
  240. case tarmak4:
  241. strcpy (priorityAlphabets, "lui");
  242. break;
  243. case colemak:
  244. strcpy (priorityAlphabets, "rspd");
  245. break;
  246. default:
  247. strcpy (priorityAlphabets, "");
  248. }
  249. Keyboard.print( "Word game! Letters being prioritised: " );
  250. Keyboard.println( priorityAlphabets );
  251. for (int i = 0; i < 15; i++) {
  252. if (priorityAlphabets[0] != 0) {
  253. instrPtr = NULL;
  254. while (instrPtr == NULL) {
  255. randNum = random(GAME_NUMWORDS);
  256. strcpy_P(buffer, (char*)pgm_read_word(&(game_word_list[randNum])));
  257. instrPtr = strpbrk (buffer, priorityAlphabets);
  258. }
  259. }
  260. else {
  261. randNum = random(GAME_NUMWORDS);
  262. strcpy_P(buffer, (char*)pgm_read_word(&(game_word_list[randNum])));
  263. }
  264. Keyboard.print( buffer );
  265. Keyboard.print( " " );
  266. }
  267. Keyboard.println( "" );
  268. }
  269. #ifdef TEENSY_USB_HOST
  270. USBHost Usb;
  271. KeyboardController keyboard_in(Usb);
  272. /*
  273. Key remap possiblities
  274. Swap LeftCtrl and CapsLock. Put CapsLock in the corner where it belongs!
  275. Switchable QWERTY, Dvorak, and colemak keyboard layouts
  276. Hardware key macros
  277. Remappings and macros that work even in BIOS and recovery mode.
  278. Remappings and macros that work with KVMs and game consoles.
  279. */
  280. void reportReader(uint8_t report[8])
  281. {
  282. remapper(report, 8);
  283. }
  284. #else /* TEENSY_USB_HOST */
  285. class KbdRptParser : public KeyboardReportParser
  286. {
  287. protected:
  288. virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
  289. };
  290. void KbdRptParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)
  291. {
  292. remapper(buf, len);
  293. // Run parent class method so keyboard LEDs are updated.
  294. KeyboardReportParser::Parse(hid, is_rpt_id, len, buf);
  295. };
  296. USB Usb;
  297. //USBHub Hub(&Usb);
  298. HIDBoot<USB_HID_PROTOCOL_KEYBOARD> ExtKeyboard(&Usb);
  299. KbdRptParser Prs;
  300. #endif /* TEENSY_USB_HOST */
  301. void setup()
  302. {
  303. randomSeed(analogRead(0));
  304. // initialize the digital pin as an output.
  305. pinMode(modeLED, OUTPUT);
  306. Keyboard.begin();
  307. #ifdef DEBUG
  308. Serial.begin( 115200 );
  309. while (!Serial) delay(1);
  310. Serial.println("Start");
  311. #endif
  312. #ifdef TEENSY_USB_HOST
  313. Usb.begin();
  314. keyboard_in.attachReportReader(reportReader);
  315. #else
  316. if (Usb.Init() == -1)
  317. #ifdef DEBUG
  318. Serial.println("USB host did not start.");
  319. #else
  320. delay( 1 );
  321. #endif
  322. delay( 200 );
  323. ExtKeyboard.SetReportParser(0, (HIDReportParser*)&Prs);
  324. #endif
  325. }
  326. void loop()
  327. {
  328. Usb.Task();
  329. }