iot4c
Published © GPL3+

Atari 65XE as USB-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 hours2,180
Atari 65XE as USB-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

7 projects • 2 followers

Comments