Minitel.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  1. /**
  2. * Minitel library for Arduino (v0.1) / May 2013
  3. * http://github.com/01010101/Minitel
  4. *
  5. * By Jerome Saint-Clair aka 01010101
  6. * http://saint-clair.net
  7. *
  8. * For the Graffiti Research Lab France
  9. * http://graffitiresearchlab.fr
  10. *
  11. * Based on works by the Tetalab (Fabrice, Renaud, PG & Phil)
  12. * http://tetalab.org
  13. */
  14. #include "Arduino.h"
  15. #include "SoftwareSerial.h"
  16. #include "Minitel.h"
  17. byte _currentBgColor = BLACK;
  18. byte _currentTextColor = WHITE;
  19. byte _currentMode = TEXT_MODE;
  20. byte _currentVideo = VIDEO_STANDARD;
  21. byte _currentSize = SIZE_NORMAL;
  22. boolean _currentUnderline = false;
  23. boolean _currentBlink = false;
  24. boolean _currentShowCursor = false;
  25. Minitel::Minitel() : SoftwareSerial(6,7) {
  26. init();
  27. }
  28. Minitel::Minitel(int rx, int tx) : SoftwareSerial(rx,tx) {
  29. init();
  30. }
  31. void Minitel::init() {
  32. Serial.begin(1200);
  33. begin(1200);
  34. useDefaultColors();
  35. refreshSettings();
  36. }
  37. byte Minitel::getGraphicChar(String s) {
  38. byte carac= 32; // caractère pixel
  39. if (s.length() == 6) {
  40. carac += s[0] == '0' ? 0 : 1;
  41. carac += s[1] == '0' ? 0 : 2;
  42. carac += s[2] == '0' ? 0 : 4;
  43. carac += s[3] == '0' ? 0 : 8;
  44. carac += s[4] == '0' ? 0 : 16;
  45. carac += s[5] == '0' ? 0 : 32;
  46. return carac;
  47. }
  48. return 9;
  49. }
  50. void Minitel::serialprint7(byte b) {
  51. boolean i = false;
  52. for(int j = 0; j<8;j++) {
  53. if (bitRead(b,j)==1) {
  54. i =!i; //calcul de la parité
  55. }
  56. }
  57. if (i) {
  58. bitWrite(b,7,1); //ecriture de la partié
  59. }
  60. else {
  61. bitWrite(b,7,0); //ecriture de la partié
  62. }
  63. write(b); //ecriture du byte sur le software serial
  64. }
  65. void Minitel::graphic(String s, int x, int y){
  66. moveCursorTo(x, y);
  67. graphic(s);
  68. }
  69. void Minitel::graphic(String s) {
  70. serialprint7(getGraphicChar(s));
  71. }
  72. void Minitel::textByte(byte c) {
  73. serialprint7(c);
  74. }
  75. void Minitel::textByte(byte b, int x, int y) {
  76. moveCursorTo(x, y);
  77. textByte(b);
  78. }
  79. boolean Minitel::textChar(byte c) {
  80. byte charByte = getCharByte(c);
  81. if (isValidChar(charByte)) {
  82. serialprint7(charByte);
  83. return true;
  84. }
  85. return false;
  86. }
  87. boolean Minitel::textChar(byte c, int x, int y) {
  88. moveCursorTo(x, y);
  89. return textChar(c);
  90. }
  91. void Minitel::text(String s, int x, int y) {
  92. text(s, x, y, HORIZONTAL);
  93. }
  94. void Minitel::text(String s) {
  95. text(s, HORIZONTAL);
  96. }
  97. void Minitel::text(String s, int x, int y, int orientation) {
  98. moveCursorTo(x, y);
  99. text(s, orientation);
  100. }
  101. void Minitel::text(String s, int orientation) {
  102. for (int i=0; i<s.length(); i++) {
  103. char c = s.charAt(i);
  104. boolean indent = false;
  105. if (isAccent(c)) {
  106. i+=1; // chars with accents take 2 array indexes
  107. c = s.charAt(i);
  108. indent = printAccentChar(c);
  109. }
  110. else {
  111. indent = textChar(c);
  112. }
  113. if (indent && orientation == VERTICAL) {
  114. moveCursor(LEFT);
  115. moveCursor(DOWN);
  116. }
  117. }
  118. }
  119. // Characters
  120. /*
  121. String chars0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // 0 -> 33
  122. String chars1= "!\"#$%&'()*+,-./0123456789:;<=>?@"; // 33 -> 64
  123. String chars2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 65 -> 90
  124. String chars3 = "abcdefghijklmnopqrstuvwxyz"; // 97 -> 122
  125. */
  126. byte Minitel::getCharByte(char c) {
  127. String characters = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]x_xabcdefghijklmnopqrstuvwxyz";
  128. return (byte) characters.lastIndexOf(c);
  129. }
  130. void Minitel::specialChar(byte c, int x, int y) {
  131. moveCursorTo(x, y);
  132. specialChar(c);
  133. }
  134. void Minitel::specialChar(byte c) {
  135. if (c == SPE_CHAR_BOOK || c == SPE_CHAR_PARAGRAPH
  136. || c == SPE_CHAR_ARROW_LEFT || c == SPE_CHAR_ARROW_UP
  137. || c == SPE_CHAR_ARROW_RIGHT || c == SPE_CHAR_ARROW_DOWN
  138. || c == SPE_CHAR_CIRCLE || c == SPE_CHAR_MINUS_PLUS
  139. || c == SPE_CHAR_1_4 || c == SPE_CHAR_1_2
  140. || c == SPE_CHAR_3_4 || c == SPE_CHAR_UPPER_OE
  141. || c == SPE_CHAR_LOWER_OE || c == SPE_CHAR_BETA) {
  142. serialprint7(25);
  143. serialprint7(c);
  144. }
  145. }
  146. boolean Minitel::isValidChar(byte index) {
  147. if (index >= 32 && index < 123) {
  148. return true;
  149. }
  150. return false;
  151. }
  152. boolean Minitel::isAccent(char c) {
  153. String accents = "áàâäéèëêíìîïóòôöúùûü";
  154. if (accents.indexOf(c) >=0) {
  155. return true;
  156. }
  157. return false;
  158. }
  159. boolean Minitel::printAccentChar(char c) {
  160. if (isAccent(c)) {
  161. String accents = "áàâäéèëêíìîïóòôöúùûü";
  162. int index = (accents.indexOf(c)-1)/2;
  163. int accentTypeIndex = index%4;
  164. printAccent(accentTypeIndex);
  165. int letterIndex = floor(index/4);
  166. char letter = getAccentLetter(letterIndex);
  167. textChar(letter);
  168. return true;
  169. }
  170. return false;
  171. }
  172. void Minitel::printAccent(int index) {
  173. serialprint7(25);
  174. switch(index) {
  175. case(0) :
  176. serialprint7(GRAVE);
  177. break;
  178. case(1) :
  179. serialprint7(ACCUTE);
  180. break;
  181. case(2) :
  182. serialprint7(CIRCUMFLEX);
  183. break;
  184. default :
  185. serialprint7(UMLAUT);
  186. }
  187. }
  188. char Minitel::getAccentLetter(int letterIndex) {
  189. switch(letterIndex) {
  190. case(0) :
  191. return('a');
  192. break;
  193. case(1) :
  194. return('e');
  195. break;
  196. case(2) :
  197. return('i');
  198. break;
  199. case(3) :
  200. return('o');
  201. break;
  202. default :
  203. return('u');
  204. }
  205. }
  206. void Minitel::repeat(byte n) {
  207. serialprint7(18);
  208. serialprint7(64+n);
  209. }
  210. void Minitel::bgColor(byte c) {
  211. if (c >= 0 && c <=7) {
  212. serialprint7(27);
  213. serialprint7(c+80);
  214. _currentBgColor = c;
  215. }
  216. }
  217. void Minitel::textColor(byte c) {
  218. if (c >= 0 && c <=7) {
  219. serialprint7(27);
  220. serialprint7(c+64);
  221. _currentTextColor = c;
  222. }
  223. }
  224. void Minitel::useDefaultColors() {
  225. bgColor(BLACK);
  226. textColor(WHITE);
  227. }
  228. void Minitel::moveCursorTo(byte x, byte y) {
  229. serialprint7(31); // Code positionnement de curseur
  230. serialprint7(64+y); // coordonnées x (x+64) (x de 1 à 40)
  231. serialprint7(64+x); // coordonnées y (y+64) (y de 1 à 24)
  232. refreshSettings();
  233. }
  234. void Minitel::moveCursor(byte dir) {
  235. if (dir == LEFT || dir == RIGHT || dir == UP || dir == DOWN) {
  236. serialprint7(dir);
  237. }
  238. }
  239. void Minitel::moveCursorTo(byte location) {
  240. if (location == HOME || location == LINE_END || location == TOP_LEFT) {
  241. serialprint7(location);
  242. }
  243. else if (location == CENTER || location == TOP_RIGHT || location == BOTTOM_RIGHT || location == BOTTOM_LEFT) {
  244. if (location == CENTER) {
  245. moveCursorTo(20, 12);
  246. }
  247. else if (location == TOP_RIGHT) {
  248. moveCursorTo(40, 1);
  249. }
  250. else if (location == BOTTOM_RIGHT) {
  251. moveCursorTo(40, 24);
  252. }
  253. else if (location == BOTTOM_LEFT) {
  254. moveCursorTo(1, 24);
  255. }
  256. refreshSettings() ;
  257. }
  258. }
  259. void Minitel::moveCursor(byte dir, int n) {
  260. if (dir == LEFT || dir == RIGHT || dir == UP || dir == DOWN) {
  261. for (int i=0; i<n; i++) {
  262. serialprint7(dir);
  263. }
  264. }
  265. }
  266. void Minitel::refreshSettings() {
  267. // Common parameters
  268. serialprint7(_currentMode);
  269. textColor(_currentTextColor);
  270. bgColor(_currentBgColor); // Only in graphic mode ?
  271. blink(_currentBlink);
  272. cursor(_currentShowCursor);
  273. // Graphic mode specific parameters
  274. if (_currentMode == GRAPHIC_MODE) {
  275. pixelate(_currentUnderline);
  276. }
  277. // Text mode specific parameters
  278. if (_currentMode == TEXT_MODE) {
  279. video(_currentVideo);
  280. charSize(_currentSize);
  281. }
  282. }
  283. void Minitel::cursor() {
  284. cursor(true);
  285. }
  286. void Minitel::noCursor() {
  287. cursor(false);
  288. }
  289. void Minitel::cursor(boolean b) {
  290. if(b) {
  291. serialprint7(CURSOR_SHOW);
  292. }
  293. else {
  294. serialprint7(CURSOR_HIDE);
  295. }
  296. _currentShowCursor = b;
  297. }
  298. void Minitel::clearScreen() {
  299. serialprint7(CLEARSCREEN);
  300. refreshSettings();
  301. }
  302. void Minitel::mode(byte mode) {
  303. if (mode == GRAPHIC_MODE || mode == TEXT_MODE) {
  304. _currentMode = mode;
  305. refreshSettings();
  306. }
  307. }
  308. void Minitel::graphicMode() {
  309. mode(GRAPHIC_MODE);
  310. }
  311. void Minitel::textMode() {
  312. mode(TEXT_MODE);
  313. }
  314. void Minitel::blink() {
  315. blink(true);
  316. }
  317. void Minitel::noBlink() {
  318. blink(false);
  319. }
  320. void Minitel::blink(boolean b) {
  321. serialprint7(27);
  322. if (b) {
  323. serialprint7(BLINK_ON);
  324. }
  325. else {
  326. serialprint7(BLINK_OFF);
  327. }
  328. _currentBlink = b;
  329. }
  330. void Minitel::charSize(byte type) {
  331. if (type == SIZE_NORMAL || type == SIZE_DOUBLE_HEIGHT || type == SIZE_DOUBLE_WIDTH || type == SIZE_DOUBLE) {
  332. serialprint7(27);
  333. serialprint7(type);
  334. _currentSize = type;
  335. }
  336. }
  337. void Minitel::incrustation(boolean b) {
  338. serialprint7(27);
  339. if (b) {
  340. serialprint7(INCRUSTATION_ON);
  341. }
  342. else {
  343. serialprint7(INCRUSTATION_OFF);
  344. }
  345. }
  346. void Minitel::incrustation() {
  347. incrustation(INCRUSTATION_ON);
  348. }
  349. void Minitel::noIncrustation() {
  350. incrustation(INCRUSTATION_OFF);
  351. }
  352. void Minitel::pixelate() {
  353. pixelate(true);
  354. }
  355. void Minitel::noPixelate() {
  356. pixelate(false);
  357. }
  358. void Minitel::pixelate(boolean b) {
  359. serialprint7(27);
  360. if (b) {
  361. serialprint7(UNDERLINE_ON);
  362. }
  363. else {
  364. serialprint7(UNDERLINE_OFF);
  365. }
  366. _currentUnderline = b;
  367. }
  368. void Minitel::lineMask(boolean b) {
  369. serialprint7(27);
  370. if (b) {
  371. serialprint7(LINE_MASK_ON);
  372. }
  373. else {
  374. serialprint7(LINE_MASK_OFF);
  375. }
  376. }
  377. void Minitel::lineMask() {
  378. lineMask(LINE_MASK_ON);
  379. }
  380. void Minitel::noLineMask() {
  381. lineMask(LINE_MASK_OFF);
  382. }
  383. void Minitel::video(byte v) {
  384. if (v == VIDEO_INVERT || v == VIDEO_STANDARD || v == VIDEO_TRANSPARENT) {
  385. serialprint7(27);
  386. serialprint7(v);
  387. _currentVideo = v;
  388. }
  389. }
  390. void Minitel::standardVideo() {
  391. video(VIDEO_STANDARD);
  392. }
  393. void Minitel::invertVideo() {
  394. video(VIDEO_INVERT);
  395. }
  396. void Minitel::transparentVideo() {
  397. video(VIDEO_TRANSPARENT);
  398. }
  399. void Minitel::setMaxSpeed() {
  400. /*
  401. serialprint7(27);
  402. serialprint7(SPEED_4800);
  403. */
  404. }
  405. // Bip
  406. // Less than 200ms isn't taken into account
  407. void Minitel::bip(long duration) {
  408. long beginTime = millis();
  409. while(millis() < beginTime+100) {//duration) {
  410. serialprint7(27);
  411. serialprint7(BIP);
  412. delay(100);
  413. }
  414. }
  415. byte Minitel::getKeyCode() {
  416. byte b = 255;
  417. b = read();
  418. if (b != 255) {
  419. Serial.println(b);
  420. }
  421. return b;
  422. }
  423. char Minitel::getKey() {
  424. byte b = 255;
  425. b = read();
  426. char c = '^';
  427. // Menu keys
  428. if (b == 147) {
  429. _menuKeyPressed = true;
  430. delay(50);
  431. }
  432. else if (_menuKeyPressed) {
  433. if (b == 198) { // Sommaire
  434. c = '1';
  435. }
  436. else if (b == 197) { // Annul
  437. c = '2';
  438. }
  439. else if (b == 66) { // Retour
  440. c = '3';
  441. }
  442. else if (b == 195) { // Répétition
  443. c = '4';
  444. }
  445. else if (b == 68) { // Guide
  446. c = '5';
  447. }
  448. else if (b == 71) { // Correction
  449. c = '6';
  450. }
  451. else if (b == 72) { // Suite
  452. c = '7';
  453. }
  454. else if (b == 65) { // Envoi
  455. c = '8';
  456. }
  457. _menuKeyPressed = false;
  458. }
  459. else {
  460. if (b == 160) { // Space
  461. c = ' ';
  462. }
  463. else if (b == 177) { // 1
  464. c = '1';
  465. }
  466. else if (b == 178) { // 2
  467. c = '2';
  468. }
  469. else if (b == 51) { // 3
  470. c = '3';
  471. }
  472. else if (b == 180) { // 4
  473. c = '4';
  474. }
  475. else if (b == 53) { // 5
  476. c = '5';
  477. }
  478. else if (b == 54) { // 6
  479. c = '6';
  480. }
  481. else if (b == 183) { // 7
  482. c = '7';
  483. }
  484. else if (b == 184) { // 8
  485. c = '8';
  486. }
  487. else if (b == 57) { // 9
  488. c = '9';
  489. }
  490. else if (b == 48) { // 0
  491. c = '0';
  492. }
  493. else if (b == 170) { // *
  494. c = '*';
  495. }
  496. else if (b == 163) { // #
  497. c = '#';
  498. }
  499. else if (b == 172) { // ,
  500. c = ',';
  501. }
  502. else if (b == 46) { // .
  503. c = '.';
  504. }
  505. else if (b == 39) { // '
  506. c = '\'';
  507. }
  508. else if (b == 187) { // ;
  509. c = ';';
  510. }
  511. else if (b == 45) { // -
  512. c = '-';
  513. }
  514. else if (b == 58) { // :
  515. c = ':';
  516. }
  517. else if (b == 63) { // ?
  518. c = '?';
  519. }
  520. else if (b == 65) { // A
  521. c = 'A';
  522. }
  523. else if (b == 66) { // B
  524. c = 'B';
  525. }
  526. else if (b == 195) { // C
  527. c = 'C';
  528. }
  529. else if (b == 68) { // D
  530. c = 'D';
  531. }
  532. else if (b == 197) { // E
  533. c = 'E';
  534. }
  535. else if (b == 198) { // F
  536. c = 'F';
  537. }
  538. else if (b == 71) { // G
  539. c = 'G';
  540. }
  541. else if (b == 72) { // H
  542. c = 'H';
  543. }
  544. else if (b == 201) { // I
  545. c = 'I';
  546. }
  547. else if (b == 202) { // J
  548. c = 'J';
  549. }
  550. else if (b == 75) { // K
  551. c = 'K';
  552. }
  553. else if (b == 204) { // L
  554. c = 'L';
  555. }
  556. else if (b == 77) { // M
  557. c = 'M';
  558. }
  559. else if (b == 78) { // N
  560. c = 'N';
  561. }
  562. else if (b == 207) { // O
  563. c = 'O';
  564. }
  565. else if (b == 80) { // P
  566. c = 'P';
  567. }
  568. else if (b == 209) { // Q
  569. c = 'Q';
  570. }
  571. else if (b == 210) { // R
  572. c = 'R';
  573. }
  574. else if (b == 83) { // S
  575. c = 'S';
  576. }
  577. else if (b == 212) { // T
  578. c = 'T';
  579. }
  580. else if (b == 85) { //U
  581. c = 'U';
  582. }
  583. else if (b == 86) { // V
  584. c = 'V';
  585. }
  586. else if (b == 215) { // W
  587. c = 'W';
  588. }
  589. else if (b == 216) { // X
  590. c = 'X';
  591. }
  592. else if (b == 89) { // Y
  593. c = 'Y';
  594. }
  595. else if (b == 90) { // Z
  596. c = 'Z';
  597. }
  598. else if (b == 33) { // !
  599. c = '!';
  600. }
  601. else if (b == 34) { // !
  602. c = '"';
  603. }
  604. else if (b == 163) { // #
  605. c = '#';
  606. }
  607. else if (b == 36) { // $
  608. c = '$';
  609. }
  610. else if (b == 165) { // %
  611. c = '%';
  612. }
  613. else if (b == 166) { // &
  614. c = '&';
  615. }
  616. else if (b == 39) { // '
  617. c = '\'';
  618. }
  619. else if (b == 40) { // (
  620. c = '(';
  621. }
  622. else if (b == 169) { // )
  623. c = ')';
  624. }
  625. else if (b == 219) { // [
  626. c = '[';
  627. }
  628. else if (b == 222) { // ↑
  629. c = '↑';
  630. }
  631. else if (b == 221) { // ]
  632. c = '[';
  633. }
  634. else if (b == 60) { // <
  635. c = '<';
  636. }
  637. else if (b == 190) { // >
  638. c = '>';
  639. }
  640. else if (b == 192) { // @
  641. c = '@';
  642. }
  643. else if (b == 43) { // +
  644. c = '+';
  645. }
  646. else if (b == 189) { // =
  647. c = '=';
  648. }
  649. else if (b == 170) { // *
  650. c = '*';
  651. }
  652. else if (b == 175) { // /
  653. c = '/';
  654. }
  655. else if (b == 123) { // /
  656. c = '|';
  657. }
  658. }
  659. return c;
  660. }
  661. boolean Minitel::isMenuKey() {
  662. return _menuKeyPressed;
  663. }
  664. void Minitel::rect(char c, int x, int y, int w, int h) {
  665. byte b = getCharByte(c);
  666. rect(b, x, y, w, h);
  667. }
  668. void Minitel::rect(byte c, int x, int y, int w, int h) {
  669. moveCursorTo(x, y);
  670. textByte(c);
  671. repeat(w);
  672. moveCursorTo(x, y+1);
  673. for (int i=0; i<h-2; i++) {
  674. textByte(c);
  675. moveCursor(DOWN);
  676. moveCursor(LEFT);
  677. }
  678. moveCursorTo(x+w, y+1);
  679. for (int i=0; i<h-2; i++) {
  680. textByte(c);
  681. moveCursor(DOWN);
  682. moveCursor(LEFT);
  683. }
  684. moveCursorTo(x, y+h-1);
  685. textByte(c);
  686. repeat(w);
  687. }
  688. void Minitel::spiral(int x, int y, int siz, int c) {
  689. int curSiz = 1;
  690. // Center
  691. specialChar(x, y, c);
  692. x++;
  693. // Spiral
  694. for (int i=0; i< siz; i++) {
  695. for (int j=0; j<curSiz; j++) {
  696. specialChar(x, y, c);
  697. y++;
  698. }
  699. curSiz++;
  700. for (int j=0; j<curSiz; j++) {
  701. specialChar(x, y, c);
  702. x--;
  703. }
  704. for (int j=0; j<curSiz; j++) {
  705. specialChar(x, y, c);
  706. y--;
  707. }
  708. curSiz++;
  709. for (int j=0; j<curSiz; j++) {
  710. specialChar(x, y, c);
  711. x++;
  712. }
  713. }
  714. }