////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//   JI3BNB RTTY ENCODER for ARDUINO UNO  - 20CH MEMORY VERSION -
//   2018.03.20 v7.0
//
//   DESIGNED FOR AMATEUR RADIO COMMUNICATION
//   - REALTIME KEYBOARD INPUT
//   - 5BIT BAUDOT CODE, 45.45baud
//   - EDIT MEMORY VIA PS/2 KEYBOARD
//   - LCD 16x2 (SC1602BBWB-XA-GB-G) or 20x4 (UC-204)
//
//   SCHEMATIC http://k183.bake-neko.net/ji3bnb/page13.html
//   ** THIS PROGRAM IS IN THE PUBLIC DOMAIN **-
//
////////////////////////////////////////////////////////////////////////////////
          ////////////////////////////////////////


////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//         -- BASIC SET UP AND PIN ASSIGNMENTS --
//
////////////////////////////////////////////////////////////////////////////////
          ////////////////////////////////////////
     #include <TimerOne.h>

//   ** REQUIRE "TimerOne" LIBRARY **
//   http://playground.arduino.cc/code/timer1

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

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

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

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

////////////////////////////////////////////////////////////////////////////////
     #include <FlexiTimer2.h>

//   ** REQUIRE "FlexiTimer2" LIBRARY **
//   http://playground.arduino.cc/Main/FlexiTimer2

////////////////////////////////////////////////////////////////////////////////
     #include <PS2Keyboard.h>

//   ** REQUIRE "PS2Keyboard" LIBRARY **
//   http://playground.arduino.cc/Main/PS2Keyboard

     PS2Keyboard keyboard;
     const int  DataPin = 2; //D2 = "DATA"  PIN
     const int  IRQpin  = 3; //D3 = "CLOCK" PIN (!!DO NOT CAHNGE THIS)

     const byte jp106 = 0;
//   SELECT 0: ENGLISH  KEYBOARD
//   SELECT 1: JAPANESE KEYBOARD

////////////////////////////////////////////////////////////////////////////////
     #include <LiquidCrystal.h>

//   LCD DISPLAY (HITACHI HD44780 INTERFACE)

     const boolean lcdType = 1;
//   SELECT 0: 16x2
//   SELECT 1: 20x4

     LiquidCrystal lcd(4,5,6,7,8,9,10);
//   ARDUINO D10 -> LCD D7
//   ARDUINO D9  -> LCD D6
//   ARDUINO D8  -> LCD D5
//   ARDUINO D7  -> LCD D4
//   ARDUINO D6  -> LCD EN
//   ARDUINO D5  -> LCD R/W
//   ARDUINO D4  -> LCD RS

////////////////////////////////////////////////////////////////////////////////
//   20CH MEMORY '1'-'0','F1'-'F10' KEYS
     
     #include <EEPROM.h>
     
//   ADDRESS TABLE
     const int addrTb[] = {2,35,68,101,134,167,200,233,266,299,-1,332,365,398,431,464,545,626,659,692};
     
//   NUMBER KEY '1'-'0'
     char  n01[33]; //00  * MAX 32 CHARACTERS (INCLUDING CR & SPACE)
     char  n02[33]; //01  *
     char  n03[33]; //02  *
     char  n04[33]; //03  *
     char  n05[33]; //04  *
     char  n06[33]; //05  *
     char  n07[33]; //06  *
     char  n08[33]; //07  *
     char  n09[33]; //08  *
     char  n00[33]; //09  *
     
//   FUNCTION KEY 'F1'-'F10'
     char  f01[17]; //10    * MAX 16 CHARACTERS -- HIS/HER CALLSIGN -- (!! NOT SAVED TO EEPROM !!)
     char  f02[33]; //11  *
     char  f03[33]; //12  *
     char  f04[33]; //13  *
     char  f05[33]; //14  *
     char  f06[81]; //15    * MAX 80 CHARACTERS (INCLUDING CR & SPACE)
     char  f07[81]; //16    * MAX 80 CHARACTERS (INCLUDING CR & SPACE)
     char  f08[33]; //17  *
     char  f09[33]; //18  *
     char  f10[33]; //19  *
     
//   ALERTS
     char  a01[] = "EMPTY!";    //20
     char  a02[] = "OVERWRITE?";  //21
     char  a03[] = "SAVED!";    //22
     
//   ARRAY
     char* ms[] = {n00,n01,n02,n03,n04,n05,n06,n07,n08,n09,f01,f02,f03,f04,f05,f06,f07,f08,f09,f10,a01,a02,a03};

////////////////////////////////////////////////////////////////////////////////
//   D12 : ENCODER OUTPUT (AFSK AUDIO)

////////////////////////////////////////////////////////////////////////////////
//   D13 : ENCODER OUTPUT (0/5V TTL LEVEL)

////////////////////////////////////////////////////////////////////////////////
//   A0(=D14) : ESC INDICATOR

//   THIS INDICATOR MEANS "NOW IN 20CH MEMORY MODE"
//   WHEN FLASHING, IT MEANS "EDIT MODE ACTIVE"

////////////////////////////////////////////////////////////////////////////////
//   A1(=D15) : TX INDICATOR

//   ALSO PTT CONTROL VOLTAGE

////////////////////////////////////////////////////////////////////////////////
//   A4(=D18) : TX SW

//   CONNECT TO GND WHEN TX

////////////////////////////////////////////////////////////////////////////////
//   "BAUDOT" CODE HAS SOME TYPES OF VARIATION

     const boolean  usFig  = 1;
//   SELECT 0: "TONO THETA-5000E" COMPATIBLE (ALSO "MixW" COMPATIBLE)
//   SELECT 1: "Fldigi" COMPATIBLE (=USTTY, POPULARLY USED)

////////////////////////////////////////////////////////////////////////////////
//   DIDDLE ENABLE AND INTERVAL PERIOD

     const boolean  ddl    =  1;   //ENABLE
     const int      ddItv  =  260; //INTERVAL (ms)

////////////////////////////////////////////////////////////////////////////////
//   SPECIAL KEYS

//   [Esc]     : GO INTO OR GO OUT OF "20CH MEMORY MODE"
//   [DOWN  ARROW] : SAVE CHANGES
//   [RIGHT ARROW] : SKIP PLAYBACK


////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//        -- GLOBAL VARIABLES, FLAGS, COUNTERS etc --
//
////////////////////////////////////////////////////////////////////////////////
          ////////////////////////////////////////
boolean  space;
boolean  shift;
boolean  crLf;
boolean  fig_1;
int  fig_2;
int  x; //CURSOR POSITION
byte   y; //CURSOR POSITION
char   c[81]; //DISPLAY BUFFER
char   keyBuf[5]; //KEY BUFFER
char   ch;

volatile boolean  empCh;
volatile boolean  snd;
volatile boolean  f1Stord;
volatile int    ti;
volatile int    ddCnt;
volatile int    flshCnt;
volatile byte   tRx;
volatile byte   tRL;
volatile byte   eSq;

uint8_t  baudot;
uint8_t  baudot_;


////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//         -- setup() --
//
////////////////////////////////////////////////////////////////////////////////
          ////////////////////////////////////////
void setup()
{
  Serial.begin(9600);
  
       if(lcdType == 0){lcd.begin(16, 2);}
  else if(lcdType == 1){lcd.begin(20, 4);}
  lcd.cursor();
  lcd.setCursor(0, 0);
  keyboard.begin(DataPin, IRQpin);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);
  digitalWrite(13, 1);
  digitalWrite(18, 1); //INTERNAL PULL UP
  
  Timer1.initialize();
  
  FlexiTimer2::set(1, timer2_interrupt);
  FlexiTimer2::start();
  
  //RESTORE MEMORIES WHEN START UP
  if(EEPROM.read(0) != 'N' || EEPROM.read(1) != 'N') //CLEAN UP
  {
    EEPROM.write(0, 'N');
    EEPROM.write(1, 'N');
    for(int i = 2; i < 1024; i++)
    {
      EEPROM.write(i, '\0');
    }
  }
  for(int msNo = 0; msNo < 20; msNo++)
  {
    int startAddr = addrTb[msNo];
    if(msNo == 10)
    {
      //SKIP
    }
    else if(msNo == 15 || msNo == 16)
    {
      Serial.println(startAddr);
      for(int m = 0; m < 80; m++)
      {
        ms[msNo][m] = EEPROM.read(startAddr + m);
      }
      Serial.println("RESTORED");
    }
    else
    {
      Serial.println(startAddr);
      for(int m = 0; m < 32; m++)
      {
        ms[msNo][m] = EEPROM.read(startAddr + m);
      }
      Serial.println("RESTORED");
    }
  }
}


////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//           -- timer_interrupt --
//
////////////////////////////////////////////////////////////////////////////////
          ////////////////////////////////////////
//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(tRx == 0 && digitalRead(18) == 0)
  {
    tRx = 1;
    tRL = 1;
  }
  else if(tRx == 1 || tRx == 2)
  {  
    if(ddCnt < 2000)
    {
      ddCnt++;
    }
    if(snd == 1)
    {
      switch(ti)
      {
        case 0:
        ddCnt = 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++;
    }
  }
  if(tRx == 0 && eSq >= 2)
  {
    if(flshCnt == 250)
    {
      digitalWrite(14, 1);
    }
    else if(flshCnt == 500)
    {
      digitalWrite(14, 0);
      flshCnt = 0;
    }
    flshCnt++;
  }
  else if(f1Stord == true)
  {
    switch(flshCnt)
    {
      case 150:
        digitalWrite(14, 0);
        break;
      case 300:
        digitalWrite(14, 1);
        break;
      case 450:
        digitalWrite(14, 0);
        f1Stord = false;
        break;
    }
    flshCnt++;
  }
  else if(empCh == true)
  {
    if(flshCnt == 230)
    {
      digitalWrite(14, 0);
      empCh = false;
    }
    flshCnt++;
  }
}

////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//        -- OTHER FUNCTIONS etc --
//
////////////////////////////////////////////////////////////////////////////////
          ////////////////////////////////////////
//DISPLAY CHARACTER
void lcdChOut()
{
  int i;
  
  if(lcdType == 0) //16x2
  {
    lcd.print(ch);
    c[16 * y + x] = ch;
    x++;
    if(x == 16 && y == 0)
    {
      x = 0; y = 1;
    }
    else if(x == 16 && y == 1)
    {
      for(i = 0; i < 16; i++)
      {
        c[i] = c[16 + i];
      }
      for(i = 0; i < 16; i++)
      {
        c[16 + i] = '\0';
      }
      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 if(lcdType == 1) //20x4
  {
    lcd.print(ch);
    c[20 * y + x] = ch;
    x++;
    if(x == 20 && y <= 2)
    {
      x = 0; y++;
    }
    else if(x == 20 && y == 3)
    {
      for(i = 0; i < 60; i++)
      {
        c[i] = c[20 + i];
      }
      for(i = 0; i < 20; i++)
      {
        c[60 + i] = '\0';
      }
      lcd.clear();
      lcd.noCursor();
      for(y = 0; y < 3; y++)
      {
        lcd.setCursor(0, y);
        for(i = 0; i < 20; i++)
        {
          if(c[20 * y + i] == '\r' || c[20 * y + i] == '\0')
          {
            lcd.print(' ');
          }
          else
          {
            lcd.print(c[20 * y + i]);
          }
        }
      }
      lcd.cursor();
      x = 0; y = 3;
    }
    lcd.setCursor(x, y);
  }
}

//HANDLES BACKSPACE
void lcdBs()
{
  int i;
  
  if(lcdType == 0) //16x2
  {
    x--;
    if(x == -1 && y == 1)
    {
      x = 15; y = 0;
      while(c[x] == '\0')
      {
        x--;
      }
    }
    lcd.noCursor();
    lcd.setCursor(x, y);
    lcd.print(" ");
    lcd.setCursor(x, y);
    lcd.cursor();
    c[16 * y + x] = '\0';
  }
  else if(lcdType == 1) //20x4
  {
    x--;
    if(x == -1 && y >= 1)
    {
      x = 19; y--;
      while(c[20 * y + x] == '\0')
      {
        x--;
      }
    }
    lcd.noCursor();
    lcd.setCursor(x, y);
    lcd.print(" ");
    lcd.setCursor(x, y);
    lcd.cursor();
    c[20 * y + x] = '\0';
  }
}

//HANDLES CRLF
void lcdCrLf()
{
  int i;
  
  if(lcdType == 0) //16x2
  {
    byte skip = (16 - x);
    
    lcd.noCursor();
    for(i = 0; i < skip; i++)
    {
      if(i == 0)
      {
        c[16 * y + x] = '\r';
      }
      else
      {
        c[16 * y + x] = '\0';
      }
      x++;
    }
    if(y == 0)
    {
      x = 0; y = 1;
    }
    else
    {
      for(i = 0; i < 16; i++)
      {
        c[i] = c[16 + i];
      }
      for(i = 0; i < 16; i++)
      {
        c[16 + i] = '\0';
      }
      lcd.clear();
      for(i = 0; i < 16; i++)
      {
        if(c[i] == '\r' || c[i] == '\0')
        {
          lcd.print(' ');
        }
        else
        {
          lcd.print(c[i]);
        }
      }
      x = 0; y = 1;
    }
    lcd.setCursor(x, y);
    lcd.cursor();
  }
  else if(lcdType == 1) //20x4
  {
    byte skip = (20 - x);
    
    lcd.noCursor();
    for(i = 0; i < skip; i++)
    {
      if(i == 0)
      {
        c[20 * y + x] = '\r';
      }
      else
      {
        c[20 * y + x] = '\0';
      }
      x++;
    }
    if(y <= 2)
    {
      x = 0; y++;
    }
    else if(y == 3)
    {
      for(i = 0; i < 60; i++)
      {
        c[i] = c[20 + i];
      }
      for(i = 0; i < 20; i++)
      {
        c[60 + i] = '\0';
      }
      lcd.clear();
      for(y = 0; y < 3; y++)
      {
        lcd.setCursor(0, y);
        for(i = 0; i < 20; i++)
        {
          if(c[20 * y + i] == '\r' || c[20 * y + i] == '\0')
          {
            lcd.print(' ');
          }
          else
          {
            lcd.print(c[20 * y + i]);
          }
        }
      }
      x = 0; y = 3;
    }
    lcd.setCursor(x, y);
    lcd.cursor();
  }
}

//LOOK UP TABLE
void chTableT()
{
  if(usFig == 1) //"Fldigi" COMPATIBLE
  {
    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;
      
      //"Fldigi" COMPATIBLE
      case '$' : baudot = B01001; fig_2 = 1; break;
      case '\'': baudot = B01011; fig_2 = 1; break;
      case '!' : baudot = B01101; fig_2 = 1; break;
      case '"' : baudot = B10001; fig_2 = 1; break;
      case '#' : baudot = B10100; fig_2 = 1; break;
      case '&' : baudot = B11010; fig_2 = 1; break;
      case ';' : baudot = B11110; fig_2 = 1; break;
      
      case '\r':
        baudot = B01000; //CR
        crLf = 1;
        break;
      case ' ':
        baudot = B00100; //SPACE
        space = 1;
        break;
      
      case PS2_ESC:
        break;
      case PS2_BACKSPACE:
        break;
      case PS2_DOWNARROW:
        break;
      case PS2_RIGHTARROW:
        break;
      case 14:
      case 15:
      case 16:
      case 17:
      case 18:
      case 19:
      case 20:
      case 21:
      case 22:
      case 23:
        break;
      
      default:
        ch = ' ';
        baudot = B00100; //SPACE
        space = 1;
        break;
    }
  }
  else if(usFig == 0) //"TONO THETA-5000E" COMPATIBLE
  {
    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;
      
      //"TONO THETA-5000E" COMPATIBLE
      case '\'': baudot = B00101; fig_2 = 1; break;
      case '$' : baudot = B01001; fig_2 = 1; break;
      case '!' : baudot = B01101; fig_2 = 1; break;
      case '+' : baudot = B10001; fig_2 = 1; break;
      case '#' : baudot = B10100; fig_2 = 1; break;
      case '&' : baudot = B11010; fig_2 = 1; break;
      case '=' : baudot = B11110; fig_2 = 1; break;
      
      case '\r':
        baudot = B01000; //CR
        crLf = 1;
        break;
      case ' ':
        baudot = B00100; //SPACE
        space = 1;
        break;
      
      case PS2_ESC:
        break;
      case PS2_BACKSPACE:
        break;
      case PS2_DOWNARROW:
        break;
      case PS2_RIGHTARROW:
        break;
      case 14:
      case 15:
      case 16:
      case 17:
      case 18:
      case 19:
      case 20:
      case 21:
      case 22:
      case 23:
        break;
      
      default:
        ch = ' ';
        baudot = B00100; //SPACE
        space = 1;
        break;
    }
  }
}

void chConvt()
{
  if(jp106 == 1) //JP106 KEYBOARD
  {
    switch(ch){ 
      case '@' : ch = '"' ; break;
      case '^' : ch = '&' ; break;
      case '&' : ch = '\''; break;
      case '*' : ch = '(' ; break;
      case '(' : ch = ')' ; break;
      case ')' : ch = '~' ; break;
      case '_' : ch = '=' ; break;
      case '=' : ch = '^' ; break;
      case '+' : ch = '\0'; break;
      case '[' : ch = '@' ; break;
      case '{' : ch = '`' ; break;
      case ']' : ch = '[' ; break;
      case '}' : ch = '{' ; break;
      case ':' : ch = '+' ; break;
      case '\'': ch = ':' ; break;
      case '"' : ch = '*' ; break;
      case '\\': ch = ']' ; break;
      case '|' : ch = '}' ; break;
    }
  }
  if(usFig == 1) //"Fldigi" COMPATIBLE
  {
    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_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 == 24            ){ch = '\0';}
    else if(ch == 25            ){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(usFig == 0) //"TONO THETA-5000E" COMPATIBLE
  {
    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_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 == 24            ){ch = '\0';}
    else if(ch == 25            ){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 chFlush()
{
  while(keyboard.available())
  {
    ch = keyboard.read();
  }
  for(int i = 0; i < 4; i++)
  {
    keyBuf[i] = '\0';
  }
}

void chSend1()
{
  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)
  {
    lcdChOut();
  }
  if(fig_2 == 0 || fig_2 == 1)
  {
    space = 0;
    fig_1 = fig_2; //REGISTER LAST STATE (EXCempCh SPACE, CR&LF)
  }
  ti = 0; snd = 1; //SEND(1)
}

void chSend2()
{
  if(shift == 1) //2ND BYTE AFTER SENDING SHIFT CODE
  {
    baudot = baudot_; //RESTORE
    lcdChOut();
    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)
  }
}


////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//             -- MAIN LOOP --
//
////////////////////////////////////////////////////////////////////////////////
          ////////////////////////////////////////
void loop()
{
  static boolean  esc;
  static int  msNo = -1;
  static int  msNo_;
  static byte m;
  static byte n;
  static byte o;
  static char mnBuf[8];
  static char editBuf[81];
  static char ch2;
  
  if(tRx == 0) //RX
  {
    if(eSq > 0) //EDIT MODE
    {
      switch(eSq)
      {
        case 1: //PLAYBACK
        
          ch = ms[msNo][m]; //READ MESSAGE
          if(ch == '\0') //END OF MESSAGE
          {
            if(m == 0) //EMPTY
            {
              msNo_ = msNo;
              msNo = 20;
            }
            else
            {
              if(msNo == 20)
              {
                msNo = msNo_;
              }
              if(x != 0)
              {
                lcdCrLf();
              }
              chFlush();
              for(m = 0; m < 80; m++)
              {
                editBuf[m] = '\0';
              }
              m = 0;
              flshCnt = 0;
              digitalWrite(14, 0);
              eSq = 2;
            }
          }
          else
          {
            if(ch == '\r')
            {
              lcdCrLf();
            }
            else
            {
              lcdChOut();
            }
            delay(120); //--
            m++;
          }
          if(keyboard.available())
          {
            ch2 = keyboard.read();
            if(ch2 == PS2_RIGHTARROW) //SKIP
            {
              if(msNo == 20)
              {
                msNo = msNo_;
              }
              if(x != 0)
              {
                lcdCrLf();
              }
              chFlush();
              for(m = 0; m < 80; m++)
              {
                editBuf[m] = '\0';
              }
              m = 0;
              flshCnt = 0;
              digitalWrite(14, 0);
              eSq = 2;
            }
          }
          break;
          
        case 2: //INPUT
          
          while(keyboard.available())
          {
            ch = keyboard.read();
            chConvt();
            if(ch != '\0')
            {
              switch(ch)
              {
                case PS2_ESC: //QUIT
                  chFlush();
                  msNo = -1;
                  esc = 0; digitalWrite(14, 0);
                  eSq = 0;
                  break;
                
                case PS2_DOWNARROW: //OVERWRITE?
                  if(x != 0)
                  {
                    lcdCrLf();
                  }
                  msNo_ = msNo;
                  msNo = 21;
                  m = 0;
                  eSq = 3;
                  break;
                
                case '\r':
                  if(msNo == 10)
                  {
                    if(m < 16  )
                    {
                      editBuf[m] = ch;
                      lcdCrLf();
                      m++;
                    }
                  }
                  else if(msNo == 15 || msNo == 16)
                  {
                    if(m < 80)
                    {
                      editBuf[m] = ch;
                      lcdCrLf();
                      m++;
                    }
                  }
                  else
                  {
                    if(m < 32)
                    {
                      editBuf[m] = ch;
                      lcdCrLf();
                      m++;
                    }
                  }
                  break;
                
                case PS2_BACKSPACE:
                  if(m > 0)
                  {
                    if(x > 0 || y > 0)
                    {
                      m--;
                      editBuf[m] = '\0';
                      lcdBs();
                    }
                  }
                  break;
                
                case PS2_RIGHTARROW:
                  break;
                case 14:
                case 15:
                case 16:
                case 17:
                case 18:
                case 19:
                case 20:
                case 21:
                case 22:
                case 23:
                  break;
                
                default:
                  if(msNo == 10)
                  {
                    if(m < 16  )
                    {
                      editBuf[m] = ch;
                      lcdChOut();
                      m++;
                    }
                  }
                  else if(msNo == 15 || msNo == 16)
                  {
                    if(m < 80)
                    {
                      editBuf[m] = ch;
                      lcdChOut();
                      m++;
                    }
                  }
                  else
                  {
                    if(m < 32)
                    {
                      editBuf[m] = ch;
                      lcdChOut();
                      m++;
                    }
                  }
                  break;
              }
            }
          }
          break;
          
        case 3: //ALERT
        
          ch = ms[msNo][m]; //READ MESSAGE
          if(ch == '\0') //END OF MESSAGE
          {
            msNo = msNo_;
            if(x != 0)
            {
              lcdCrLf();
            }
            chFlush();
            eSq = 4;
          }
          else
          {
            lcdChOut();
            delay(75); //--
            m++;
          }
          break;
          
        case 4: //CONFIRM
        
          while(keyboard.available())
          {
            ch = keyboard.read();
            if(ch != '\0')
            {
              if(ch == PS2_ESC) //QUIT
              {
                chFlush();
                esc = 0; digitalWrite(14, 0);
                eSq = 0;
              }
              else if(ch == PS2_DOWNARROW) //TYPE AGAIN - SAVE
              {
                int startAddr = addrTb[msNo];
                if(msNo == 10)
                {
                  for(m = 0; m < 16; m++)
                  {
                    ms[msNo][m] = editBuf[m];
                  }
                }
                else if(msNo == 15 || msNo == 16)
                {
                  for(m = 0; m < 80; m++)
                  {
                    ms[msNo][m] = editBuf[m];
                  }
                  
                  Serial.println(startAddr);
                  for(m = 0; m < 80; m++)
                  {
                    EEPROM.write(startAddr + m, editBuf[m]);
                  }
                  EEPROM.write(startAddr + 80, '\0');
                  Serial.println("SAVED!");
                }
                else
                {
                  for(m = 0; m < 32; m++)
                  {
                    ms[msNo][m] = editBuf[m];
                  }
                  
                  Serial.println(startAddr);
                  for(m = 0; m < 32; m++)
                  {
                    EEPROM.write(startAddr + m, editBuf[m]);
                  }
                  EEPROM.write(startAddr + 32, '\0');
                  Serial.println("SAVED!");
                }
                msNo = 22;
                m = 0;
                eSq = 5;
              }
            }
          }
          break;
          
        case 5: //DONE
        
          ch = ms[msNo][m]; //READ MESSAGE
          if(ch == '\0') //END OF MESSAGE
          {
            if(x != 0)
            {
              lcdCrLf();
            }
            chFlush();
            esc = 0; digitalWrite(14, 0); //RETURN
            eSq = 0;
          }
          else
          {
            lcdChOut();
            delay(120); //--
            m++;
          }
          break;
      }
    }
    else if(keyboard.available())
    {
      ch = keyboard.read();
      chConvt();
      if(ch != '\0')
      {
        if(ch == PS2_ESC) //HANDLES [Esc] KEY
        {
          esc = esc ^ 1;
          digitalWrite(14, esc);
          eSq = 0;
          
        }
        else if(esc == 1)
        {
          if(ch >= 48 && ch <= 57) //'0'-'9'
          {
            msNo = ch - 48; //WHEN esc=1 AND A NUMBER KEY IS TYPED, START MESSAGE PLAYBACK
            //--Ini
            m = 0;
            o = 0;
            for(int i = 0; i < 80; i++)
            {
              editBuf[i] = '\0';
            }
            //--
            if(x != 0)
            {
              lcdCrLf();
            }
            eSq = 1;
          }
          else if(ch >= 14 && ch <= 23) //'F1'-'F10'
          {
            msNo = ch - 4; //WHEN esc=1 AND A FUNCTION KEY IS TYPED, START MESSAGE PLAYBACK
            //--Ini
            m = 0;
            o = 0;
            for(int i = 0; i < 80; i++)
            {
              editBuf[i] = '\0';
            }
            //--
            if(x != 0)
            {
              lcdCrLf();
            }
            eSq = 1;
          }
        }
        else if(esc == 0)
        {
          if(ch >= 14 && ch <= 23) //'F1'-'F10'
          {
            esc = 1;
            digitalWrite(14, 1);
            
            msNo = ch - 4; //WHEN esc=0 AND A FUNCTION KEY IS TYPED, START MESSAGE PLAYBACK
            //--Ini
            m = 0;
            o = 0;
            for(int i = 0; i < 80; i++)
            {
              editBuf[i] = '\0';
            }
            //--
            if(x != 0)
            {
              lcdCrLf();
            }
            eSq = 1;
          }
        }
      }
    }
  }
  else if(tRx == 1) //RX to TX
  {
    if(snd == 0)
    {  
      if(tRL == 1) //1st loop
      {
        chFlush();
        msNo = -1;
        esc  =  0; digitalWrite(14, 0);
        eSq  =  0;
        digitalWrite(15, 1); //PTT ON
        Timer1.attachInterrupt(timer1_interrupt, MARK); //SOUND ON
        delay(180);
        //--
        baudot = B01000; //-- CR --
        ti = 0; snd = 1;
        //--
        tRL = 2;
      }
      else if(tRL == 2) //2nd loop
      {
        lcdCrLf();
        //--
        baudot = B00010; //-- LF --
        ti = 0; snd = 1;
        //--
        tRL = 3;
      }
      else if(tRL == 3) //3rd loop
      {
        fig_1 = 0;
        //--
        baudot = B11111; //-- LTR (INITIALIZE "HIS/HER" DECODER) --
        ti = 0; snd = 1;
        //--
        tRx = 2;
      }
    }
  }
  else if(tRx == 2) //TX
  {
    int  i;
    
    //snd 0,1
    if(keyboard.available())
    {
      if(keyBuf[3] == '\0')
      {
        for(i = 0; i < 4; i++) //SEEK POSITION
        {
          if(keyBuf[i] == '\0')
          {
            break;
          }
        }
        char ch3 = keyboard.read();
        if(ch3 == PS2_ESC) //CHECK [ESC]
        {
          if(esc == 1)
          {
            msNo = -1;
            n = 0;
            esc = 0; digitalWrite(14, 0);
          }
          else
          { 
            esc = 1; digitalWrite(14, 1);
          }
        }
        else
        {
          keyBuf[i] = ch3; //PUSH
        }
      }
    }
    if(snd == 0)
    {
      if(shift == 1 || crLf == 1) //TOP PRIORITY (WHEN 2ND BYTE IS NEEDED)
      {
        chSend2();
      }
      else if(esc == 1 && msNo >= 0) //HANDLES MEMORY MESSAGES
      {
        if(digitalRead(18) == 1) //CHECK SW
        {
          msNo = -1;
          n = 0;
          goto exitTx;
        }
        while(keyBuf[0] != '\0') //ALWAYS CHECK KEYBOARD
        {
          //--keyBuf.read
          ch2 = keyBuf[0];
          for(i = 0; i < 4; i++)
          {
            keyBuf[i] = keyBuf[i + 1];
          }
          //--
          if(ch2 >= 48 && ch2 <= 57) //'0'-'9'
          {
            if(n < 7)
            {
              mnBuf[n] = ch2; //REGISTER ADDITIONAL MESSAGE-NUMBERS, 7 NUMBERS AT MAXIMUM
              n++;
            }
          }
          else if(ch2 >= 14 && ch2 <= 23) //'F1'-'F10'
          {
            if(n < 7)
            {
              mnBuf[n] = ch2; //REGISTER ADDITIONAL MESSAGE-NUMBERS, 7 NUMBERS AT MAXIMUM
              n++;
            }
          }
        }
        
        ch = ms[msNo][m]; //READ MESSAGE
        if(ch == '\0') //END OF MESSAGE
        {
          if(m == 0 && n == 0) //EMPTY CH
          {
            flshCnt = 0;
            empCh = true;
            esc = 0;
          }
          else if(n == 0) //NO MESSAGE-NUMBER, EXIT
          {
            esc = 0; digitalWrite(14, 0);
          }
          msNo = -1;
        }
        
        if(msNo >= 0) //--SEND MEMORY--
        {
          chTableT();
          chSend1();
          m++;
        }
      }
      else if(esc == 1 && n > 0) //COMPLETED SENDING <ONE> MESSAGE, AND OTHERS STILL REMAIN
      {
        ch = mnBuf[o]; //READ NUMBER
        if(ch >= 48 && ch <= 57) //'0'-'9'
        {
          msNo = ch - 48; //SPECIFY THE NEXT
          m = 0;
          o++;
        }
        else if(ch >= 14 && ch <= 23) //'F1'-'F10'
        {
          msNo = ch - 4; //SPECIFY THE NEXT
          m = 0;
          o++;
        }
        else if(ch == '\0') //END OF MESSAGE-NUMBERS, EXIT
        {
          n = 0;
          esc = 0; digitalWrite(14, 0);
        }
      }
      else if(keyBuf[0] != '\0') //COMPLETED SENDING <ALL> MESSAGES, AND KEYBOARD IS TYPED
      {
        if(digitalRead(18) == 1) //CHECK SW
        {
          goto exitTx;
        }
        //--keyBuf.read
        ch = keyBuf[0];
        for(i = 0; i < 4; i++)
        {
          keyBuf[i] = keyBuf[i + 1];
        }
        //--
        chConvt();
        if(ch != '\0')
        {
          chTableT();
          if(esc == 1)
          {
            if(ch >= 48 && ch <= 57) //'0'-'9'
            {
              msNo = ch - 48; //WHEN esc=1 AND A NUMBER KEY IS TYPED, START SENDING A NEW MESSAGE
              //--Ini
              m = 0;
              o = 0;
              for(int i = 0; i < 7; i++)
              {
                mnBuf[i] = '\0';
              }
              //--
            }
            else if(ch >= 14 && ch <= 23) //'F1'-'F10'
            {
              msNo = ch - 4; //WHEN esc=1 AND A FUNCTION KEY IS TYPED, START SENDING A NEW MESSAGE
              //--Ini
              m = 0;
              o = 0;
              for(int i = 0; i < 7; i++)
              {
                mnBuf[i] = '\0';
              }
              //--
            }
          }
          else if(esc == 0)
          {
            if(ch >= 14 && ch <= 23) //'F1'-'F10'
            {
              esc = 1;
              digitalWrite(14, 1);
              
              msNo = ch - 4; //WHEN esc=0 AND A FUNCTION KEY IS TYPED, START 20CH MEMORY MODE
              //--Ini
              m = 0;
              o = 0;
              for(int i = 0; i < 7; i++)
              {
                mnBuf[i] = '\0';
              }
              //--
            }
            else if(ch == PS2_DOWNARROW) //SAVE LAST WORD TO [F1]
            {
              int m;
              int i;
              int msNo = 10; //[F1]
              int endPos;
              
                   if(lcdType == 0){i = y * 16 + x;} //CALC POSITION
              else if(lcdType == 1){i = y * 20 + x;}
              endPos = i;
              
              //SEEK LAST WORD
              i--;
              while(c[i] == ' ' || c[i] == '\0' || c[i] == '\r' && i >= 0)
              {
                i--;
              }
              while(c[i] != ' ' && c[i] != '\0' && c[i] != '\r' && i >= 0)
              {
                i--;
              }
              i++;
              for(m = 0; m < 16; m++) //CLEAR [F1]
              {
                ms[msNo][m] = '\0';
              }
              m = 0;
              while(c[i] != ' ' && c[i] != '\0' && c[i] != '\r' && i < endPos) //SAVE LAST WORD + ONE SPACE
              {
                ms[msNo][m] = c[i];
                i++;
                m++;
                if(m == 16)
                {
                  break;
                }
              }
              if(m < 16)
              {
                ms[msNo][m] = ' ';
              }
              
              //DONE
              digitalWrite(14, 1);
              flshCnt = 0;
              f1Stord = true;
            }
            else if(ch == PS2_RIGHTARROW || ch == PS2_BACKSPACE) //IGNORE THESE KEYS
            {
              /////
            }
            else //--SEND FROM KEYBOARD--
            {
              chSend1();
            }
          }
        }
      }
      else
      {
        if(digitalRead(18) == 1) //CHECK SW
        {
          exitTx: //-LABEL-
          delay(370); //LONG TONE
          digitalWrite(15, 0); //PTT OFF
          Timer1.detachInterrupt(); //SOUND OFF
          esc = 0; digitalWrite(14, 0);
          snd = 0;
          tRx = 0;
        }
        else //NOTHING TO PROCESS, DIDDLE
        {
          if(ddl == 1 && ddCnt > ddItv)
          {
            fig_1 = 0;
            baudot = B11111; //LTR (DIDDLE)
            ti = 0; snd = 1;
          }
        }
      }
    }
  }
}

//   -- END OF THE SKETCH --
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////