https://github.com/01010101/Minitel
Raw File
Tip revision: 320e007ed9ebfcc50514be9c4fd75452ddee6dc8 authored by 01010101 on 26 July 2018, 07:58:48 UTC
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++;
    }
  }
}

back to top