Converter for Consul 262.5 terminal keyboard and VDX 52600 terminal.
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.

484 lines
9.8 KiB

  1. // Resources:
  2. // [Consul 262.4 Converter] https://deskthority.net/viewtopic.php?t=26908
  3. // [Consul 262.5 manual in CS] http://www.sapi.cz/prislusenstvi/c262-5.php#odkazp4
  4. #define KEYBOARD
  5. #ifdef KEYBOARD
  6. #include <Keyboard.h>
  7. #endif
  8. #include <TimerOne.h>
  9. // pinout config
  10. const int pinLedOffline = 10; // out
  11. const int pinLedOnline = 9; // out
  12. const int pinSpeaker = 8; // out
  13. const int pinData = 6; // out, host data
  14. const int pinStatus = 7; // in, host status
  15. const int clockPin = 5; // out, kbd clock
  16. const int dataPin = 3; // in, kbd data
  17. const int outPin = 4; // out, kbd led
  18. // constant config
  19. const int slaveClockDivider = 8;
  20. const int timerDelay = 520 / slaveClockDivider;
  21. // variables
  22. volatile int slaveClockStep = 0;
  23. char m[255];
  24. volatile int data = 0;
  25. int test = 0;
  26. volatile int counter = 0;
  27. int numbits = 10;
  28. volatile int typedKey = -1;
  29. bool hostOnline = false;
  30. // MODS >>>
  31. // [1] send debug scancode information to serial port
  32. bool modConsoleLog = true;
  33. // <<< MODS
  34. // ----------
  35. // KBD Output
  36. // ----------
  37. volatile long lastChange = -100000;
  38. volatile int x = 0;
  39. volatile int dataWord = 0;
  40. volatile int dataState = 0;
  41. volatile int dataDelay = 0;
  42. volatile int packetDelay = 0;
  43. volatile int packetTail = 0;
  44. volatile bool nextKeyReady = false;
  45. volatile byte nextKey = 0;
  46. void typeKey(byte key) {
  47. nextKey = key;
  48. nextKeyReady = true;
  49. //Serial.print("Typing key "); Serial.println((int) key);
  50. }
  51. void sendKey(byte key) {
  52. dataWord = key;
  53. dataState = 8;
  54. dataDelay = 0;
  55. packetDelay = 0;
  56. packetTail = 15;
  57. typedKey = key;
  58. //Serial.print("Sending key "); Serial.println((int) key);
  59. }
  60. void onHostStatusChange() {
  61. long timeNow = millis();
  62. long changeDiff = timeNow - lastChange;
  63. lastChange = timeNow;
  64. if (changeDiff >= 10 && nextKeyReady) {
  65. nextKeyReady = false;
  66. sendKey(nextKey);
  67. Timer1.start(); // synchronize with the host
  68. slaveClockStep = 0;
  69. }
  70. }
  71. void onHostClockCycle(void)
  72. {
  73. int dataBit = HIGH;
  74. if (packetDelay > 0) {
  75. packetDelay--;
  76. } else if (dataDelay > 0) {
  77. dataDelay--;
  78. dataBit = LOW;
  79. } else if (dataState > 0) {
  80. int bitToSend = (dataWord >> (dataState - 1)) & 1;
  81. dataBit = !bitToSend ? LOW : HIGH;
  82. dataState--;
  83. } else if (packetTail > 0) {
  84. packetTail--;
  85. dataBit = LOW;
  86. } else {
  87. }
  88. digitalWrite(pinData, dataBit);
  89. }
  90. // ---------
  91. // KBD Input
  92. // ---------
  93. const int receivingSteps = 16;
  94. volatile int clockStep = 0;
  95. volatile int receivingStep = 0;
  96. volatile int receivingData = 0;
  97. volatile int receivingBit = 0;
  98. void onSlaveClockInterrupt() {
  99. clockStep = (clockStep + 1) % 2;
  100. int clockValue = (clockStep % 2) ? HIGH : LOW;
  101. digitalWrite(clockPin, clockValue);
  102. int dataBit = digitalRead(dataPin);
  103. if (clockValue == LOW) {
  104. if (receivingData == 0 && dataBit == LOW) {
  105. receivingData = 1;
  106. receivingStep = 0;
  107. receivingBit = 0;
  108. test = 0;
  109. } else if (receivingData == 1) {
  110. receivingStep++;
  111. }
  112. if (receivingData == 1 && test == 0) {
  113. test = 1;
  114. receivingBit += dataBit == HIGH ? 1 : 0;
  115. if (receivingStep >= receivingSteps) {
  116. if (counter <= 8) {
  117. data = data >> 1;
  118. if (receivingBit > receivingSteps / 2) {
  119. bitSet(data, 7);
  120. }
  121. }
  122. counter++;
  123. receivingStep = 0;
  124. receivingBit = 0;
  125. if (counter >= numbits) {
  126. receivingData = 0;
  127. }
  128. }
  129. }
  130. }
  131. if (clockValue == HIGH && test == 1) {
  132. test = 0;
  133. }
  134. }
  135. void setupKeyMapping() {
  136. m[0] = 0;
  137. // top row special
  138. m[63] = 0x12; // ?? Setup
  139. m[62] = 'j'; // up
  140. m[61] = 'k'; // down
  141. m[59] = 'h'; // left
  142. m[60] = 'l'; // right
  143. // top numbers row
  144. m[228] = 0x1B; // ESC
  145. m[206] = '1';
  146. m[205] = '2';
  147. m[204] = '3';
  148. m[203] = '4';
  149. m[202] = '5';
  150. m[201] = '6';
  151. m[200] = '7';
  152. m[199] = '8';
  153. m[198] = '9';
  154. m[207] = '0';
  155. m[210] = '-';
  156. m[161] = '^';
  157. m[0] = ' '; // Empty cap
  158. m[247] = 0x08; // Backspace
  159. m[56] = 0x10; // Break
  160. // top letter row
  161. m[246] = '\t';
  162. m[142] = 'q';
  163. m[142+32] = 'Q';
  164. m[136] = 'w';
  165. m[136+32] = 'W';
  166. m[154] = 'e';
  167. m[154+32] = 'E';
  168. m[141] = 'r';
  169. m[141+32] = 'R';
  170. m[139] = 't';
  171. m[139+32] = 'T';
  172. m[134] = 'y';
  173. m[134+32] = 'Y';
  174. m[138] = 'u';
  175. m[138+32] = 'U';
  176. m[150] = 'i';
  177. m[150+32] = 'I';
  178. m[144] = 'o';
  179. m[144+32] = 'O';
  180. m[143] = 'p';
  181. m[143+32] = 'P';
  182. m[191] = '@';
  183. m[164] = '[';
  184. m[245] = 0x0A;
  185. m[128] = 0x7F;
  186. // middle letter row
  187. m[158] = 'a';
  188. m[158+32] = 'A';
  189. m[140] = 's';
  190. m[140+32] = 'S';
  191. m[155] = 'd';
  192. m[155+32] = 'D';
  193. m[153] = 'f';
  194. m[153+32] = 'F';
  195. m[152] = 'g';
  196. m[152+32] = 'G';
  197. m[151] = 'h';
  198. m[151+32] = 'H';
  199. m[149] = 'j';
  200. m[149+32] = 'J';
  201. m[148] = 'k';
  202. m[148+32] = 'K';
  203. m[147] = 'l';
  204. m[147+32] = 'L';
  205. m[196] = ';';
  206. m[196+32] = '+';
  207. m[197] = ':';
  208. m[197+32] = '*';
  209. m[162] = ']';
  210. m[162+32] = '}';
  211. m[242] = 0x0D;
  212. // bottom letter row
  213. m[58] = 0x11; // No Scroll
  214. m[133] = 'z';
  215. m[133+32] = 'Z';
  216. m[135] = 'x';
  217. m[135+32] = 'X';
  218. m[156] = 'c';
  219. m[156+32] = 'C';
  220. m[137] = 'v';
  221. m[137+32] = 'V';
  222. m[157] = 'b';
  223. m[157+32] = 'B';
  224. m[145] = 'n';
  225. m[145+32] = 'N';
  226. m[146] = 'm';
  227. m[146+32] = 'M';
  228. m[211] = ',';
  229. m[211+32] = '<';
  230. m[209] = '.';
  231. m[209+32] = '>';
  232. m[208] = '/';
  233. m[208+32] = '?';
  234. m[163] = '\\';
  235. m[163+32] = '|';
  236. m[78] = 'x'; // Blank cap
  237. m[77] = 'X'; // Blank cap + shift
  238. // spacebar
  239. m[223] = ' '; // Spacebar
  240. /*/ numpad
  241. m[] = '';
  242. m[+32] = '';
  243. m[] = '';
  244. m[+32] = '';
  245. m[] = '';
  246. m[+32] = '';
  247. m[] = '';
  248. m[+32] = '';
  249. m[] = '';
  250. m[+32] = '';
  251. /*
  252. m[] = '';
  253. m[] = '';
  254. m[] = '';
  255. m[] = '';
  256. m[] = '';
  257. m[] = '';
  258. m[] = '';
  259. m[] = '';
  260. m[] = '';
  261. m[] = '';
  262. m[] = '';
  263. m[] = '';
  264. m[] = '';
  265. m[] = '';
  266. m[] = '';
  267. m[] = '';
  268. m[] = '';
  269. m[] = '';
  270. m[] = '';
  271. m[] = '';
  272. m[] = '';
  273. m[] = '';
  274. m[] = '';
  275. m[] = '';
  276. m[] = '';
  277. m[] = '';
  278. m[] = '';
  279. m[] = '';
  280. m[] = '';
  281. m[] = '';
  282. m[] = '';
  283. m[] = '';
  284. m[] = '';
  285. m[] = '';
  286. m[] = '';
  287. m[] = '';
  288. m[] = '';
  289. m[] = '';
  290. m[] = '';
  291. m[] = '';
  292. m[] = '';
  293. m[] = '';
  294. m[] = '';
  295. m[] = '';
  296. m[] = '';
  297. m[] = '';
  298. m[] = '';
  299. m[] = '';
  300. /**/
  301. //
  302. // 208 local ON
  303. // 209 leave local
  304. // 210 half/full duplex
  305. // 211 go home?
  306. // 212 print Ps junk
  307. // 213 mem protect
  308. // 214 disable NAT
  309. // 217 clear line?
  310. // 218 shows cursor
  311. // 219 hides cursor
  312. // 221 mem prot
  313. // 223 reset prompt
  314. // 241 random junk symbols
  315. // 243 random junk symbols
  316. // 244 stop junk
  317. // 248 hides cursor
  318. // 251 Hard copy
  319. // 250 Keyb. Inh.
  320. }
  321. char translateKeyToChar(int key) {
  322. if (sizeof(m) <= key) {
  323. return 0;
  324. }
  325. return m[key];
  326. }
  327. void printChar(char keyChar) {
  328. Serial.print("'"); Serial.print(keyChar); Serial.print("' ("); Serial.print(int(keyChar)); Serial.println(")");
  329. }
  330. void processKbdByte(int data) {
  331. int key = data;
  332. char keyChar = translateKeyToChar(key);
  333. if (modConsoleLog) {
  334. Serial.print("Key: <"); Serial.print(int(key)); Serial.print("> ");
  335. Serial.print("Char: "); printChar(keyChar);
  336. }
  337. #ifdef KEYBOARD
  338. if (!hostOnline) {
  339. Keyboard.press(keyChar);
  340. delay(10);
  341. Keyboard.release(keyChar);
  342. }
  343. #endif
  344. typeKey(keyChar);
  345. }
  346. // ----------------------
  347. // Input and Output Merge
  348. // ----------------------
  349. void onTimerInterrupt()
  350. {
  351. onSlaveClockInterrupt();
  352. if (slaveClockStep == 0) {
  353. onHostClockCycle();
  354. }
  355. slaveClockStep = (slaveClockStep + 1) % slaveClockDivider;
  356. }
  357. // --------------
  358. // User Interface
  359. // --------------
  360. void updateOnlineStatus()
  361. {
  362. long timeNow = millis();
  363. hostOnline = (lastChange > timeNow) || ((timeNow - lastChange) < 10000);
  364. digitalWrite(pinLedOffline, hostOnline ? LOW : HIGH);
  365. digitalWrite(pinLedOnline, hostOnline ? HIGH : LOW);
  366. }
  367. // ----
  368. // Main
  369. // ----
  370. void setup(void)
  371. {
  372. Serial.begin(9600);
  373. setupKeyMapping();
  374. pinMode(pinLedOffline, OUTPUT);
  375. pinMode(pinLedOnline, OUTPUT);
  376. pinMode(pinSpeaker, OUTPUT);
  377. pinMode(pinData, OUTPUT);
  378. pinMode(dataPin, INPUT);
  379. pinMode(outPin, OUTPUT);
  380. pinMode(clockPin, OUTPUT);
  381. pinMode(pinStatus, INPUT_PULLUP);
  382. digitalWrite(pinLedOffline, HIGH);
  383. digitalWrite(pinLedOnline, HIGH);
  384. digitalWrite(pinData, HIGH);
  385. digitalWrite(outPin, LOW);
  386. attachInterrupt(digitalPinToInterrupt(pinStatus), onHostStatusChange, CHANGE);
  387. Timer1.initialize(timerDelay);
  388. Timer1.attachInterrupt(onTimerInterrupt);
  389. delay(500);
  390. tone(pinSpeaker, 650, 200);
  391. digitalWrite(pinLedOffline, LOW);
  392. digitalWrite(pinLedOnline, HIGH);
  393. delay(200);
  394. tone(pinSpeaker, 500, 200);
  395. digitalWrite(pinLedOffline, HIGH);
  396. digitalWrite(pinLedOnline, LOW);
  397. delay(200);
  398. digitalWrite(pinLedOffline, LOW);
  399. digitalWrite(pinLedOnline, LOW);
  400. delay(500);
  401. updateOnlineStatus();
  402. tone(pinSpeaker, 440, 200);
  403. delay(200);
  404. Serial.println("Keyboard ready");
  405. }
  406. int qwe = 0;
  407. void loop(void)
  408. {
  409. // type key from serial
  410. if (!nextKeyReady && Serial.available() > 0) {
  411. long key = Serial.parseInt(SKIP_ALL);
  412. if (key != 0) {
  413. typeKey(key);
  414. }
  415. }
  416. /**/
  417. // type key from keyboard
  418. if (counter >= numbits) {
  419. processKbdByte(data);
  420. data = B0;
  421. counter = 0;
  422. }
  423. /**/
  424. /*/ auto-type test
  425. delay(2000);
  426. int k = 128 + qwe;
  427. typeKey(k);
  428. qwe = (qwe + 1) % 128;
  429. Serial.print("QWE "); Serial.println((int)k);
  430. if (qwe % 5 == 0) delay(2000);
  431. /**/
  432. updateOnlineStatus();
  433. if (nextKeyReady && !hostOnline) { // skip sending key without a host (used for USB)
  434. nextKeyReady = false;
  435. typedKey = nextKey;
  436. }
  437. if (!nextKeyReady && typedKey != -1) {
  438. tone(pinSpeaker, 200 + typedKey, 100);
  439. typedKey = -1;
  440. }
  441. delay(5);
  442. }