iot4c
Published © GPL3+

Atari 65XE becomes modern keyboard

Retro Atari 65XE plus Arduino Leonardo as USB-keyboard for a modern computer. It will leave the opportunity to use this Atari in native mode.

IntermediateFull instructions provided2 hours4,960
Atari 65XE becomes modern keyboard

Things used in this project

Hardware components

Arduino Leonardo
Arduino Leonardo
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

Arduino sketch

Arduino
the Arduino sketch that need to flash to Arduino Leonard within Arduino ID
#include <Keypad.h> // with v.3.1.1 tested
#include <Keyboard.h> // with v.1.0.2 tested

const byte ROWS=8;
const byte COLS=9;
// key names for keyboard scanning
char keys[ROWS][COLS] = {
{'7',NO_KEY,'8','9','N','<','>','B','P'},
{'6',NO_KEY,'5','4','3','2','1','E',NO_KEY},
{'u',NO_KEY,'i','o','p','_','=','R',NO_KEY},
{'y',NO_KEY,'t','r','e','w','q','T',NO_KEY},
{NO_KEY,'j','k','l',';','+','*',NO_KEY,'C'},
{NO_KEY,'h','g','f','d','s','a','U',NO_KEY},
{'n',' ','m',',','.','/','A',NO_KEY,NO_KEY},
{NO_KEY,'F','b','v','c','x','z',NO_KEY,'S'}};

// key aliases for Alt/Ctrl/Shift+Ctrl(arrows)/without modifiers cases
char kb[ROWS][COLS] = {
{'7',NO_KEY,'8','9','0','<','>',KEY_BACKSPACE,NO_KEY},
{'6',NO_KEY,'5','4','3','2','1',KEY_ESC,NO_KEY},
{'u',NO_KEY,'i','o','p','_','=',KEY_RETURN,NO_KEY},
{'y',NO_KEY,'t','r','e','w','q',KEY_TAB,NO_KEY},
{NO_KEY,'j','k','l',';','+','*',NO_KEY,NO_KEY},
{NO_KEY,'h','g','f','d','s','a',KEY_CAPS_LOCK,NO_KEY},
{'n',' ','m',',','.','/',NO_KEY,NO_KEY,NO_KEY},
{NO_KEY,KEY_F1,'b','v','c','x','z',NO_KEY,NO_KEY}};

// key aliases for Shift case
char kbShft[ROWS][COLS] = {
{byte(39),NO_KEY,'@','(',')',NO_KEY,KEY_INSERT,KEY_DELETE,NO_KEY},
{'&',NO_KEY,'%','$','#','"','!',KEY_ESC,NO_KEY},
{'U',NO_KEY,'I','O','P','-','|',KEY_RETURN,NO_KEY},
{'Y',NO_KEY,'T','R','E','W','Q',KEY_TAB,NO_KEY},
{NO_KEY,'J','K','L',':',byte(92),'^',NO_KEY,KEY_LEFT_CTRL},
{NO_KEY,'H','G','F','D','S','A',KEY_CAPS_LOCK,NO_KEY},
{'N',' ','M','[',']','?',KEY_LEFT_ALT,NO_KEY,NO_KEY},
{NO_KEY,KEY_F1,'B','V','C','X','Z',NO_KEY,NO_KEY}};

// key aliases for Ctrl+Tab case
char kbM[ROWS][COLS] = {
{KEY_F7,NO_KEY,KEY_F8,KEY_F9,KEY_F10,KEY_F11,KEY_F12,KEY_BACKSPACE,NO_KEY},
{KEY_F6,NO_KEY,KEY_F5,KEY_F4,KEY_F3,KEY_F2,KEY_F1,'`',NO_KEY},
{'u',NO_KEY,'i','o','p',KEY_PAGE_UP,KEY_PAGE_DOWN,KEY_RETURN,NO_KEY},
{'y',NO_KEY,'t','r','e','w','q',NO_KEY,NO_KEY},
{NO_KEY,'j','k','l',';',KEY_HOME,KEY_END,NO_KEY,NO_KEY},
{NO_KEY,'h','g','f','d','s','a',KEY_CAPS_LOCK,NO_KEY},
{byte(252),' ','m','{','}','~',KEY_LEFT_ALT,NO_KEY,NO_KEY},
{NO_KEY,KEY_F1,'b','v','c','x','z',NO_KEY,KEY_LEFT_SHIFT}};

byte rowPins[ROWS] = {11, 12, 18, 19, 20, 21, 22, 23};
byte colPins[COLS] = {2, 3, 4, 5, 6, 7, 8, 9, 10};
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
int r, c;
bool isShft, isAlt, isCtrl, isM, isCSArrows, isTab, isPause;
bool isPress, isHold, isReleased;
bool PurgeM;
char kp;

void setup() {
  kpd.setDebounceTime(20);  // may need to be tuned
  r=c=0;
  isPress=isHold=isReleased=false;
  PurgeM=false;
  Keyboard.begin();
}

void loop() {
if (kpd.getKeys() || isHold || isShft || isM || isCSArrows || isAlt || isCtrl) {
  isShft=isAlt=isCtrl=isM=isCSArrows=isTab=isPause=false;
  kp=NO_KEY;
  
  // scan the whole key list
  for (int i=0; i<LIST_MAX; i++) {
    switch (kpd.key[i].kchar){
      case 'S': isShft=true; break;
      case 'A': isAlt=true; if (kpd.key[i].kstate==RELEASED) {isReleased=true; isPress=isHold=false;} break;
      case 'C': isCtrl=true; break;
      case 'P': isPause=true; break;
      case 'T': isTab=true; break;
      case NO_KEY: break;
      default: kp=kpd.key[i].kchar; break;
    }
    if (!isPause && (kp!=NO_KEY)) {
      switch (kpd.key[i].kstate) {
        case PRESSED: isPress=true; isHold=isReleased=false; break; 
        case HOLD: isHold=true; isPress=isReleased=false; break;
        case RELEASED: isReleased=true; isPress=isHold=false; break;
      }                
    }
  }
  
  // parse for modifiers and state
  if ((isShft || isCtrl || isAlt) && isPause) {isPause=false;}
  if (isShft && isCtrl) {isCtrl=false;}
  if (isCtrl && isTab) {isM=true; isCtrl=false; isTab=false;}
  if (isShft && isTab) {isCSArrows=true; isShft=false; isTab=false;}
  if (isTab) {
    kp='T';
    if (kpd.isPressed(kp)) {isPress=true; isHold=isReleased=false;} else {isReleased=true; isPress=isHold=false;}
  }
  if (isPause) {isPause=false;}

  // parse for only Alt and only Shift+Alt cases
  if (kp==NO_KEY) {
    if (isAlt && isReleased && !PurgeM) {
      if (isShft) {
        Keyboard.press(KEY_LEFT_ALT); Keyboard.press(KEY_LEFT_SHIFT); Keyboard.releaseAll();
        } else {
        Keyboard.press(KEY_LEFT_ALT); Keyboard.releaseAll();
      }
      while (kpd.findInList('A')>=0) {kpd.getKeys();}
      isReleased=false; PurgeM=false;
    }
    if (isAlt && isReleased && PurgeM) {
      while (kpd.findInList('A')>=0) {kpd.getKeys();}
      isReleased=false; PurgeM=false;
    }
  }

  // parse for keys with modifiers
  if (kp=='P') {kp=NO_KEY;}
  if (kp!=NO_KEY) {
    if (isPress || isHold) {
      if (isHold) {delay (40);} // may need to be tuned
      for (int rc=0; rc<ROWS; rc++) {
        for (int cc=0; cc<COLS; cc++)
        {
          if (kp==keys[rc][cc]) {r=rc; c=cc;};
        }
      }
      if (isShft && !isAlt) {
        Keyboard.print(kbShft[r][c]);
        delay (100);  // may need to be tuned
      }
      if (isCtrl) {
        switch (kb[r][c])
        {
          case '_': Keyboard.press(KEY_UP_ARROW); break;
          case '+': Keyboard.press(KEY_LEFT_ARROW); break;
          case '*': Keyboard.press(KEY_RIGHT_ARROW); break;
          case '=': Keyboard.press(KEY_DOWN_ARROW); break;
          case '>': Keyboard.press(KEY_LEFT_CTRL); Keyboard.print('>'); break;
          default: Keyboard.press(KEY_LEFT_CTRL); Keyboard.print(kb[r][c]); break;
        }
        while (kpd.findInList(kp)>=0) {kpd.getKeys();}
        isReleased=true;
      }
      if (isAlt) {
        Keyboard.press(KEY_LEFT_ALT);
        if (!isShft) {Keyboard.print(kb[r][c]);} else {Keyboard.print(kbShft[r][c]);}
        while (kpd.findInList(kp)>=0) {kpd.getKeys();}
        isReleased=true;
      }
      if (isM) {
        Keyboard.print(kbM[r][c]);
        while (kpd.findInList(kp)>=0) {kpd.getKeys();}
        isReleased=true;
      }
      if (isCSArrows) {
        switch (kb[r][c])
        {
          case '_': Keyboard.press(KEY_LEFT_SHIFT); Keyboard.press(KEY_UP_ARROW); break;
          case '+': Keyboard.press(KEY_LEFT_SHIFT); Keyboard.press(KEY_LEFT_ARROW); break;
          case '*': Keyboard.press(KEY_LEFT_SHIFT); Keyboard.press(KEY_RIGHT_ARROW); break;
          case '=': Keyboard.press(KEY_LEFT_SHIFT); Keyboard.press(KEY_DOWN_ARROW); break;
          default: Keyboard.press(KEY_LEFT_CTRL); Keyboard.press(KEY_LEFT_SHIFT); Keyboard.print(kb[r][c]); break;
        }
        while (kpd.findInList(kp)>=0) {kpd.getKeys();}
        isReleased=true;
      }
      if (!isShft && !isCtrl && !isAlt && !isM && !isCSArrows)
      {
        Keyboard.print(kb[r][c]);
      }
    }
    if (isReleased) {
      if (isAlt) {PurgeM=true;}
      Keyboard.releaseAll();
      isPress=isHold=isReleased=false;
    }
  }
}
}

Credits

iot4c

iot4c

10 projects • 2 followers

Comments