https://github.com/01010101/Minitel
Tip revision: 320e007ed9ebfcc50514be9c4fd75452ddee6dc8 authored by 01010101 on 26 July 2018, 07:58:48 UTC
Merge pull request #5 from per1234/example-folder
Merge pull request #5 from per1234/example-folder
Tip revision: 320e007
Minitel.cpp
/**
Minitel library for Arduino (v0.2) / April 2015
http://github.com/01010101/Minitel
By Jerome Saint-Clair aka 01010101
http://saint-clair.net
For the Graffiti Research Lab France
http://graffitiresearchlab.fr
Based on works by the Tetalab (Fabrice, Renaud, PG & Phil)
http://tetalab.org
*/
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "Minitel.h"
byte _currentBgColor = BLACK;
byte _currentTextColor = WHITE;
byte _currentMode = TEXT_MODE;
byte _currentVideo = VIDEO_STANDARD;
byte _currentSize = SIZE_NORMAL;
boolean _currentUnderline = false;
boolean _currentBlink = false;
boolean _currentShowCursor = false;
Minitel::Minitel() : SoftwareSerial(6, 7) {
init();
}
Minitel::Minitel(int rx, int tx) : SoftwareSerial(rx, tx) {
init();
}
void Minitel::init() {
Serial.begin(1200);
begin(1200);
useDefaultColors();
refreshSettings();
}
byte Minitel::getGraphicChar(String s) {
byte carac = 32; // caractère pixel
if (s.length() == 6) {
carac += s[0] == '0' ? 0 : 1;
carac += s[1] == '0' ? 0 : 2;
carac += s[2] == '0' ? 0 : 4;
carac += s[3] == '0' ? 0 : 8;
carac += s[4] == '0' ? 0 : 16;
carac += s[5] == '0' ? 0 : 32;
return carac;
}
return 9;
}
void Minitel::serialprint7(byte b) {
boolean i = false;
for (int j = 0; j < 8; j++) {
if (bitRead(b, j) == 1) {
i = !i; //calcul de la parité
}
}
if (i) {
bitWrite(b, 7, 1); //ecriture de la partié
}
else {
bitWrite(b, 7, 0); //ecriture de la partié
}
write(b); //ecriture du byte sur le software serial
}
void Minitel::graphic(String s, int x, int y) {
moveCursorTo(x, y);
graphic(s);
}
void Minitel::graphic(String s) {
serialprint7(getGraphicChar(s));
}
void Minitel::textByte(byte b) {
serialprint7(b);
}
void Minitel::textByte(byte b, int x, int y) {
moveCursorTo(x, y);
textByte(b);
}
boolean Minitel::textChar(byte c) {
byte charByte = getCharByte(c);
if (isValidChar(charByte)) {
serialprint7(charByte);
return true;
}
return false;
}
boolean Minitel::textChar(byte c, int x, int y) {
moveCursorTo(x, y);
return textChar(c);
}
void Minitel::text(String s, int x, int y) {
text(s, x, y, HORIZONTAL);
}
void Minitel::text(String s) {
text(s, HORIZONTAL);
}
void Minitel::text(String s, int x, int y, int orientation) {
moveCursorTo(x, y);
text(s, orientation);
}
void Minitel::text(String s, int orientation) {
for (unsigned int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
boolean indent = false;
if (isAccent(c)) {
i += 1; // chars with accents take 2 array indexes
c = s.charAt(i);
indent = printAccentChar(c);
}
else {
// TODO Check if c cedil
indent = textChar(c);
}
if (indent && orientation == VERTICAL) {
moveCursor(LEFT);
moveCursor(DOWN);
}
}
}
// Characters
/*
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 0 -> 32
!"#$%&'()*+,-./0123456789:;<=>?@ // 33 -> 64
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] // 65 -> 93
x // 94 up arrow
_ // 95 lower pipe associated to underscore
x // 96 pipe
abcdefghijklmnopqrstuvwxyz // 97 -> 122
// 123 124 125 126 various pipes
*/
// Used to display characters sent from the Arduino
// As a result, not all Minitel supported characters can be sent to/from Arduino
// However, they can be displayed using the specialChar() or graphic functions
byte Minitel::getCharByte(char c) {
String characters = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]x_xabcdefghijklmnopqrstuvwxyz";
return (byte) characters.lastIndexOf(c);
}
//
boolean Minitel::isSerializableKey() {
return (_characterKey >= 33 && _characterKey <= 122 && _characterKey != 94 && _characterKey != 96);
}
void Minitel::specialChar(byte b, int x, int y) {
moveCursorTo(x, y);
specialChar(b);
}
void Minitel::specialChar(byte b) {
if (isValidChar(b)) {
serialprint7(25);
serialprint7(b);
if (b == 75) {
serialprint7(99);
}
}
}
boolean Minitel::isValidChar(byte index) {
if (index >= 32 && index <= 123) {
return true;
}
return false;
}
//
//
// ACCENTS HANDLING
//
//
boolean Minitel::isAccent(char c) {
//String accents = "àáâäèéêëìíîïòóôöùúûü";
if (accents.indexOf(c) >= 0) {
return true;
}
return false;
}
boolean Minitel::printAccentChar(char c) {
//String accents = "àáâäèéêëìíîïòóôöùúûü";
int index = (accents.indexOf(c)) / 2;
int accentTypeIndex = index % 4;
printAccent(accentTypeIndex);
// Check which letter
int letterIndex = floor(index / 4);
char letter = getAccentLetter(letterIndex);
textChar(letter);
return true; // There should be no pb printing accents
}
void Minitel::printAccent(int index) {
switch (index) {
case (0) :
specialChar(SPE_CHAR_GRAVE);
break;
case (1) :
specialChar(SPE_CHAR_ACUTE);
break;
case (2) :
specialChar(SPE_CHAR_CIRCUMFLEX);
break;
default :
specialChar(SPE_CHAR_UMLAUT);
}
}
char Minitel::getAccentLetter(int letterIndex) {
switch (letterIndex) {
case (0) :
return ('a');
break;
case (1) :
return ('e');
break;
case (2) :
return ('i');
break;
case (3) :
return ('o');
break;
default :
return ('u');
}
}
//
//
// REPEAT CHARACTER
//
//
void Minitel::repeat(byte n) {
serialprint7(18);
serialprint7(64 + n);
}
//
//
// COLOR MANAGEMENT
//
//
void Minitel::bgColor(byte c) {
if (c >= 0 && c <= 7) {
serialprint7(27);
serialprint7(c + 80);
_currentBgColor = c;
}
}
void Minitel::textColor(byte c) {
if (c >= 0 && c <= 7) {
serialprint7(27);
serialprint7(c + 64);
_currentTextColor = c;
}
}
void Minitel::useDefaultColors() {
bgColor(BLACK);
textColor(WHITE);
}
//
//
// MOVING AND POSITIONNING THE CURSOR
//
//
void Minitel::moveCursorTo(byte x, byte y) {
serialprint7(31); // Code positionnement de curseur
serialprint7(64 + y); // coordonnées x (x+64) (x de 1 à 40)
serialprint7(64 + x); // coordonnées y (y+64) (y de 1 à 24)
refreshSettings();
}
void Minitel::moveCursor(byte dir) {
if (dir == LEFT || dir == RIGHT || dir == UP || dir == DOWN) {
serialprint7(dir);
}
}
void Minitel::moveCursorTo(byte location) {
if (location == HOME || location == LINE_END || location == TOP_LEFT) {
serialprint7(location);
}
else if (location == CENTER || location == TOP_RIGHT || location == BOTTOM_RIGHT || location == BOTTOM_LEFT) {
if (location == CENTER) {
moveCursorTo(20, 12);
}
else if (location == TOP_RIGHT) {
moveCursorTo(40, 1);
}
else if (location == BOTTOM_RIGHT) {
moveCursorTo(40, 24);
}
else if (location == BOTTOM_LEFT) {
moveCursorTo(1, 24);
}
refreshSettings() ;
}
}
void Minitel::moveCursor(byte dir, int n) {
if (dir == LEFT || dir == RIGHT || dir == UP || dir == DOWN) {
for (int i = 0; i < n; i++) {
serialprint7(dir);
}
}
}
//
//
// RESTORING THE CURRENT SETTINGS AS
// SOME COMMANDS SEEM TO RESET THEM
//
//
void Minitel::refreshSettings() {
// Common parameters
serialprint7(_currentMode);
textColor(_currentTextColor);
bgColor(_currentBgColor); // Only in graphic mode ?
blink(_currentBlink);
cursor(_currentShowCursor);
// Graphic mode specific parameters
if (_currentMode == GRAPHIC_MODE) {
pixelate(_currentUnderline);
}
// Text mode specific parameters
if (_currentMode == TEXT_MODE) {
video(_currentVideo);
charSize(_currentSize);
}
}
//
//
// SHOW / HIDE CURSOR
//
//
void Minitel::cursor() {
cursor(true);
}
void Minitel::noCursor() {
cursor(false);
}
void Minitel::cursor(boolean b) {
if (b) {
serialprint7(CURSOR_SHOW);
}
else {
serialprint7(CURSOR_HIDE);
}
_currentShowCursor = b;
}
//
//
// CLEANING SYSTEM
//
//
void Minitel::clearScreen() {
serialprint7(CLEARSCREEN);
refreshSettings();
}
//
//
// TEXT OR GRAPHIC MODE SELECTION
//
//
void Minitel::mode(byte mode) {
if (mode == GRAPHIC_MODE || mode == TEXT_MODE) {
_currentMode = mode;
refreshSettings();
}
}
void Minitel::graphicMode() {
mode(GRAPHIC_MODE);
}
void Minitel::textMode() {
mode(TEXT_MODE);
}
//
//
//
//
//
void Minitel::blink() {
blink(true);
}
void Minitel::noBlink() {
blink(false);
}
void Minitel::blink(boolean b) {
serialprint7(27);
if (b) {
serialprint7(BLINK_ON);
}
else {
serialprint7(BLINK_OFF);
}
_currentBlink = b;
}
void Minitel::charSize(byte type) {
if (type == SIZE_NORMAL || type == SIZE_DOUBLE_HEIGHT || type == SIZE_DOUBLE_WIDTH || type == SIZE_DOUBLE) {
serialprint7(27);
serialprint7(type);
_currentSize = type;
}
}
void Minitel::incrustation(boolean b) {
serialprint7(27);
if (b) {
serialprint7(INCRUSTATION_ON);
}
else {
serialprint7(INCRUSTATION_OFF);
}
}
void Minitel::incrustation() {
incrustation(INCRUSTATION_ON);
}
void Minitel::noIncrustation() {
incrustation(INCRUSTATION_OFF);
}
void Minitel::pixelate() {
pixelate(true);
}
void Minitel::noPixelate() {
pixelate(false);
}
void Minitel::pixelate(boolean b) {
serialprint7(27);
if (b) {
serialprint7(UNDERLINE_ON);
}
else {
serialprint7(UNDERLINE_OFF);
}
_currentUnderline = b;
}
void Minitel::lineMask(boolean b) {
serialprint7(27);
if (b) {
serialprint7(LINE_MASK_ON);
}
else {
serialprint7(LINE_MASK_OFF);
}
}
void Minitel::lineMask() {
lineMask(LINE_MASK_ON);
}
void Minitel::noLineMask() {
lineMask(LINE_MASK_OFF);
}
void Minitel::video(byte v) {
if (v == VIDEO_INVERT || v == VIDEO_STANDARD || v == VIDEO_TRANSPARENT) {
serialprint7(27);
serialprint7(v);
_currentVideo = v;
}
}
void Minitel::standardVideo() {
video(VIDEO_STANDARD);
}
void Minitel::invertVideo() {
video(VIDEO_INVERT);
}
void Minitel::transparentVideo() {
video(VIDEO_TRANSPARENT);
}
void Minitel::setMaxSpeed() {
/*
serialprint7(27);
serialprint7(SPEED_4800);
*/
}
//
//
// SOUND
//
//
// Less than 200ms isn't taken into account
void Minitel::bip(unsigned long duration) {
unsigned long beginTime = millis();
while (millis() < beginTime + 100ul) { //duration) {
serialprint7(27);
serialprint7(BIP);
delay(100);
}
}
//
//
// KEYSTROKES ANALYSIS AND LOGGING
//
//
/*
Read and decode keyboard input and store values in according variables
_specialCharacterKey if a special character Jeu G2, schema 2.8 p103
_characterKey if a normal character Jeu G0, schema 2.5 p 100
_menuKey is a menu key
*/
void Minitel::readKey() {
_menuKey = -1;
_specialCharacterKey = -1;
_characterKey = -1;
byte b = 255;
b = read();
// Menu keys start with 147 + another number
if (b == 147) {
_accentKey = -1; // Drop previously set accent
delay(50); // Wait a bit
_menuKey = read(); // Read the next byte
}
// Shift or Ctrl key with GP2 character set start with 153
else if (b == 153) {
_accentKey = -1; // Drop previously set accent
delay(50); // Wait a bit
b = read(); // Read the next byte
if (b == 65 || b == 66 || b == 72 || b == 195) { // Accent key
_accentKey = b % 128;
return;
}
else {
_specialCharacterKey = b % 128;
if (b == 75) { // Special case for the ç
delay(50); // Wait a bit
b = read(); // Read the next byte
if (b == 99) {
_specialCharacterKey = 75; // Implicit cedil with implicit c
}
}
}
}
// Non prefixed keys
else if (b != 255) {
_characterKey = b % 128;
// If an accent key was pressed before check if character can have this an accent
if (_characterKey == 97 || _characterKey == 101 || _characterKey == 105 || _characterKey == 111 || _characterKey == 117 ) {
// Remove accents if not supported by this letter
if (_characterKey == 97 && _accentKey == SPE_CHAR_ACUTE ) { // a
_accentKey = -1;
}
else if (_characterKey == 105 && (_accentKey == SPE_CHAR_GRAVE || _accentKey == SPE_CHAR_ACUTE )) { // i
_accentKey = -1;
}
else if (_characterKey == 111 && (_accentKey == SPE_CHAR_GRAVE || _accentKey == SPE_CHAR_ACUTE )) { // o
_accentKey = -1;
}
else if (_characterKey == 117 && _accentKey == SPE_CHAR_ACUTE ) { // u
_accentKey = -1;
}
}
else {
_accentKey = -1;
}
}
}
//
//
// KEYS GETTERS
//
//
boolean Minitel::keyTyped() {
return isMenuKey() || isCharacterKey() || isSpecialCharacterKey() || accentKeyStored() ;
}
boolean Minitel::isMenuKey() {
return _menuKey != -1;
}
int Minitel::getMenuKey() {
return _menuKey;
}
boolean Minitel::isSpecialCharacterKey() {
return _specialCharacterKey != -1;
}
int Minitel::getSpecialCharacterKey() {
return _specialCharacterKey;
}
boolean Minitel::isCharacterKey() {
return _characterKey != -1;
}
char Minitel::getCharacterKey() {
return _characterKey;
}
boolean Minitel::accentKeyStored() {
return _accentKey != -1;
}
int Minitel::getAccentKey() {
return _accentKey;
}
//
//
// DRAWING FUNCTIONS
//
//
void Minitel::rect(char c, int x, int y, int w, int h) {
byte b = getCharByte(c);
rect(b, x, y, w, h);
}
void Minitel::rect(byte c, int x, int y, int w, int h) {
moveCursorTo(x, y);
textByte(c);
repeat(w);
moveCursorTo(x, y + 1);
for (int i = 0; i < h - 2; i++) {
textByte(c);
moveCursor(DOWN);
moveCursor(LEFT);
}
moveCursorTo(x + w, y + 1);
for (int i = 0; i < h - 2; i++) {
textByte(c);
moveCursor(DOWN);
moveCursor(LEFT);
}
moveCursorTo(x, y + h - 1);
textByte(c);
repeat(w);
}
void Minitel::spiral(int x, int y, int siz, int c) {
int curSiz = 1;
// Center
specialChar(c, x, y);
x++;
// Spiral
for (int i = 0; i < siz; i++) {
for (int j = 0; j < curSiz; j++) {
specialChar(c, x, y);
y++;
}
curSiz++;
for (int j = 0; j < curSiz; j++) {
specialChar(c, x, y);
x--;
}
for (int j = 0; j < curSiz; j++) {
specialChar(c, x, y);
y--;
}
curSiz++;
for (int j = 0; j < curSiz; j++) {
specialChar(c, x, y);
x++;
}
}
}