/**********************************************************
   JI3BNB RTTY ENCODER for ARDUINO UNO <Simple Version>
   2016.06.25 v3.1
   5BIT-BAUDOT-CODE, 45.45baud
   LCD 16x2 (SC1602BBWB-XA-GB-G) or 20x4 (UC-204)
/**********************************************************/

#include <LiquidCrystal.h>
#include <TimerOne.h>
#include <FlexiTimer2.h>
#include <PS2Keyboard.h>

LiquidCrystal lcd(4,5,6,7,8,9,10);
const boolean lcdType = 0; // ***** 16x2: 0 / 20x4: 1 *****

#define MARK  346   // 346 = (500000 / 1445 Hz)
#define SPACE 392   // 392 = (500000 / 1275 Hz)

/*
#define MARK  218   // 218 = (500000 / 2295 Hz)
#define SPACE 235   // 235 = (500000 / 2125 Hz)
*/

PS2Keyboard keyboard;
const int DataPin = 2;  //PS/2 DATA
const int IRQpin  = 3;  //PS/2 CLOCK

boolean  ddl    = 1;  //DIDDLE ENABLE
boolean  space;
boolean  crLf;
boolean  snd;
int  fig_2;
int  ti;
byte   x;
char   ch;
uint8_t  baudot;

void setup()
{
  if(lcdType == 0)
  {
    lcd.begin(16, 2);
  }
  else
  {
    lcd.begin(20, 4);
  }
  lcd.cursor();
  lcd.setCursor(0, 0);
  keyboard.begin(DataPin, IRQpin);
  pinMode(12, OUTPUT);  //--AFSK AUDIO OUTPUT
  pinMode(13, OUTPUT);  //--TTL LEVEL OUTPUT
  digitalWrite(13, 1);
  
  Timer1.initialize();
  Timer1.attachInterrupt(timer1_interrupt, MARK);
  
  FlexiTimer2::set(1, timer2_interrupt);
  FlexiTimer2::start();
}

//AFSK TONE GENERATOR
void timer1_interrupt(void)
{
  static boolean toggle;
  
  toggle = toggle ^ 1;
  digitalWrite(12, toggle);
}

//5BIT BAUDOT GENERATOR
void timer2_interrupt(void)
{
  static boolean bit1;
  static boolean bit2;
  static boolean bit3;
  static boolean bit4;
  static boolean bit5;
  
  if(snd == 1)
  {
    switch(ti)
    {
      case 0:
        digitalWrite(13, 0);
        Timer1.setPeriod(SPACE);
        
        bit1 = baudot & B00001;
        bit2 = baudot & B00010;
        bit3 = baudot & B00100;
        bit4 = baudot & B01000;
        bit5 = baudot & B10000;
        break;
      case 22:
        digitalWrite(13, bit1);
        if(bit1){Timer1.setPeriod(MARK);}
            else{Timer1.setPeriod(SPACE);}
        break;
      case 44:
        digitalWrite(13, bit2);
        if(bit2){Timer1.setPeriod(MARK);}
            else{Timer1.setPeriod(SPACE);}
        break;
      case 66:
        digitalWrite(13, bit3);
        if(bit3){Timer1.setPeriod(MARK);}
            else{Timer1.setPeriod(SPACE);}
        break;
      case 88:
        digitalWrite(13, bit4);
        if(bit4){Timer1.setPeriod(MARK);}
            else{Timer1.setPeriod(SPACE);}
        break;
      case 110:
        digitalWrite(13, bit5);
        if(bit5){Timer1.setPeriod(MARK);}
            else{Timer1.setPeriod(SPACE);}
        break;
      case 132:
        digitalWrite(13, 1);
        Timer1.setPeriod(MARK);
        break;
      case 165:
        snd = 0;
        break;
    }
    ti++;
  }
}

//CONTROLS 16x2 or 20x4 LCD DISPLAY
void lcdOut()
{
  static byte y;
  int i;
  
  if(lcdType == 0) //16x2
  {

    static char c[17];
    
    lcd.print(ch);
    c[x] = ch;
    x++;
    if(x == 16 && y == 0)
    {
      x = 0; y = 1;
    }
    else if(x == 16 && y == 1)
    {
      lcd.clear();
      lcd.noCursor();
      for(i = 0; i < 16; i++)
      {
        lcd.print(c[i]);
      }
      lcd.cursor();
      x = 0; y = 1;
    }
    lcd.setCursor(x, y);
  }
  else //20x4
  {
    static char c[61];
    
    lcd.print(ch);
         if(y == 1){c[     x] = ch;}
    else if(y == 2){c[20 + x] = ch;}
    else if(y == 3){c[40 + x] = ch;}
    x++;
         if(x == 20 && y == 0){x = 0, y = 1;}
    else if(x == 20 && y == 1){x = 0, y = 2;}
    else if(x == 20 && y == 2){x = 0, y = 3;}
    else if(x == 20 && y == 3)
    {
      lcd.clear();
      lcd.setCursor(0, 0); for(i = 0; i < 20; i++){lcd.print(c[     i]);}
      lcd.setCursor(0, 1); for(i = 0; i < 20; i++){lcd.print(c[20 + i]);}
      lcd.setCursor(0, 2); for(i = 0; i < 20; i++){lcd.print(c[40 + i]);}
      x = 0, y = 3;
      for(i = 0; i < 20; i++){c[     i] = c[20 + i];}
      for(i = 0; i < 20; i++){c[20 + i] = c[40 + i];}
    }
    lcd.setCursor(x, y);
  }
}

void lcdCrLf()
{
  byte rest;
  
  if(lcdType == 0)
  {
    rest = (16 - x);
  }
  else
  {
    rest = (20 - x);
  }
  ch = ' ';
  for(int i = 0; i < rest; i++) //FILL THE LINE WITH SPACES, INSTEAD OF CR&LF
  {
    lcdOut();
  }
}

//LOOK UP TABLE
void chTable()
{
  fig_2 = -1;
  switch(ch)
  {
    case 'A': baudot = B00011; fig_2 = 0; break;
    case 'B': baudot = B11001; fig_2 = 0; break;
    case 'C': baudot = B01110; fig_2 = 0; break;
    case 'D': baudot = B01001; fig_2 = 0; break;
    case 'E': baudot = B00001; fig_2 = 0; break;
    case 'F': baudot = B01101; fig_2 = 0; break;
    case 'G': baudot = B11010; fig_2 = 0; break;
    case 'H': baudot = B10100; fig_2 = 0; break;
    case 'I': baudot = B00110; fig_2 = 0; break;
    case 'J': baudot = B01011; fig_2 = 0; break;
    case 'K': baudot = B01111; fig_2 = 0; break;
    case 'L': baudot = B10010; fig_2 = 0; break;
    case 'M': baudot = B11100; fig_2 = 0; break;
    case 'N': baudot = B01100; fig_2 = 0; break;
    case 'O': baudot = B11000; fig_2 = 0; break;
    case 'P': baudot = B10110; fig_2 = 0; break;
    case 'Q': baudot = B10111; fig_2 = 0; break;
    case 'R': baudot = B01010; fig_2 = 0; break;
    case 'S': baudot = B00101; fig_2 = 0; break;
    case 'T': baudot = B10000; fig_2 = 0; break;
    case 'U': baudot = B00111; fig_2 = 0; break;
    case 'V': baudot = B11110; fig_2 = 0; break;
    case 'W': baudot = B10011; fig_2 = 0; break;
    case 'X': baudot = B11101; fig_2 = 0; break;
    case 'Y': baudot = B10101; fig_2 = 0; break;
    case 'Z': baudot = B10001; fig_2 = 0; break;
    case '0': baudot = B10110; fig_2 = 1; break;
    case '1': baudot = B10111; fig_2 = 1; break;
    case '2': baudot = B10011; fig_2 = 1; break;
    case '3': baudot = B00001; fig_2 = 1; break;
    case '4': baudot = B01010; fig_2 = 1; break;
    case '5': baudot = B10000; fig_2 = 1; break;
    case '6': baudot = B10101; fig_2 = 1; break;
    case '7': baudot = B00111; fig_2 = 1; break;
    case '8': baudot = B00110; fig_2 = 1; break;
    case '9': baudot = B11000; fig_2 = 1; break;
    case '-': baudot = B00011; fig_2 = 1; break;
    case '?': baudot = B11001; fig_2 = 1; break;
    case ':': baudot = B01110; fig_2 = 1; break;
    case '(': baudot = B01111; fig_2 = 1; break;
    case ')': baudot = B10010; fig_2 = 1; break;
    case '.': baudot = B11100; fig_2 = 1; break;
    case ',': baudot = B01100; fig_2 = 1; break;
    case '/': baudot = B11101; fig_2 = 1; break;
    case '\r':
      baudot = B01000; //CR
      crLf = 1;
      break;
    case ' ':
      baudot = B00100; //SPACE
      space = 1;
      break;
    default:
      ch = ' ';
      baudot = B00100; //SPACE
      space = 1;
      break;
  }
}

void chConvt()
{
  if(ch >= 97 && ch <= 122) //CONVERT LOWER CASE TO UPPER CASE
  {
    ch = ch - 32;
  }
  else if(ch == PS2_TAB       ){ch = '\0';} //IGNORE THESE KEYS
  else if(ch == PS2_BACKSPACE ){ch = '\0';}
  else if(ch == PS2_ESC       ){ch = '\0';}
  else if(ch == PS2_DELETE    ){ch = '\0';}
  else if(ch == PS2_PAGEUP    ){ch = '\0';}
  else if(ch == PS2_PAGEDOWN  ){ch = '\0';}
  else if(ch == PS2_UPARROW   ){ch = '\0';}
  else if(ch == PS2_LEFTARROW ){ch = '\0';}
  else if(ch == PS2_DOWNARROW ){ch = '\0';}
  else if(ch == PS2_RIGHTARROW){ch = '\0';}
  else if(ch == '!'     ){ch = '\0';}
  else if(ch == '"'     ){ch = '\0';}
  else if(ch == '#'     ){ch = '\0';}
  else if(ch == '$'     ){ch = '\0';}
  else if(ch == '%'     ){ch = '\0';}
  else if(ch == '&'     ){ch = '\0';}
  else if(ch == '\''    ){ch = '\0';}
  else if(ch == '='     ){ch = '\0';}
  else if(ch == '^'     ){ch = '\0';}
  else if(ch == '~'     ){ch = '\0';}
  else if(ch == '|'     ){ch = '\0';}
  else if(ch == '@'     ){ch = '\0';}
  else if(ch == '`'     ){ch = '\0';}
  else if(ch == '['     ){ch = '\0';}
  else if(ch == '{'     ){ch = '\0';}
  else if(ch == ';'     ){ch = '\0';}
  else if(ch == '+'     ){ch = '\0';}
  else if(ch == '*'     ){ch = '\0';}
  else if(ch == ']'     ){ch = '\0';}
  else if(ch == '}'     ){ch = '\0';}
  else if(ch == '<'     ){ch = '\0';}
  else if(ch == '>'     ){ch = '\0';}
  else if(ch == '\\'    ){ch = '\0';}
  else if(ch == '_'     ){ch = '\0';}
}

void loop()
{
  static boolean  shift;
  static boolean  fig_1;
  static uint8_t  baudot_;
  
  if(snd == 0)
  {
    if(shift == 1) //2ND BYTE AFTER SENDING SHIFT CODE
    {
      baudot = baudot_; //RESTORE 
      lcdOut();
      shift = 0;
      ti = 0; snd = 1; //SEND(2)
    }
    else if(crLf == 1) //2ND BYTE AFTER SENDING "CR"
    {
      baudot = B00010; //LF
      lcdCrLf();
      crLf = 0;
      ti = 0; snd = 1; //SEND(2)
    }
    else //SENDING 2ND BYTE DONE
    {
      if (keyboard.available()) //KEYBOARD IS TYPED
      {
        ch = keyboard.read();
        chConvt();
        if(ch != '\0')
        {
          chTable();
          if(fig_1 == 0 && fig_2 == 1) //SHIFT "UP"
          {
            baudot_ = baudot; //EVACUATE
            baudot = B11011; //SEND FIGURE CODE FIRST
            shift = 1;
          }
          else if(fig_1 == 1 && fig_2 == 0) //SHIFT "DOWN"
          {
            baudot_ = baudot; //EVACUATE
            baudot = B11111; //SEND LETTER CODE FIRST
            shift = 1;
          }
          else if(space == 1 && fig_2 == 1) //FIGURE AFTER SPACE (TX_UOS)
          {
            baudot_ = baudot; //EVACUATE
            baudot = B11011; //SEND FIGURE CODE FIRST
            shift = 1;
          }
          if(shift != 1 && crLf != 1)
          {
            lcdOut();
          }
          if(fig_2 == 0 || fig_2 == 1)
          {
            space = 0;
            fig_1 = fig_2; //REGISTER LAST STATE (EXCEPT SPACE, CR&LF)
          }
          ti = 0; snd = 1; //SEND(1)
        }
      }
      else //NOTHING TO PROCESS, DIDDLE
      {
        if(ddl == 1)
        {
          baudot = B11111; //LTR(DIDDLE)
          fig_1 = 0;
          ti = 0; snd = 1;
        }
      }
    }
  }
  delay(5);
}