Wednesday, 30 September 2015

PCB Mounted and Ready to Go

I haven't found a lot of time over the past couple of weeks, still I have managed to finish off the PCB and furnish it with components.

Assembled keyboard interface, with mounted LeoStick
The LeoStick is mounted on some pin headers, leaving height enough for a USB cable to be attached once the PCB is mounted in a case alongside a Raspberry Pi.

Underneath the LeoStick there is also space for a couple of resistors. The LEDs and a Momentary switch are attached to some more headers via some wires, and these will be mounted into the eventual case at convenient locations.

I ended up ordering some Molex connectors suitable for mounting a standard ZX81 membrane keyboard. The ZX8-KDLX I procured earlier is designed to quite happily plug into a ZX81 as a replacement keyboard. Using the Molex connectors rather than say IDC connectors, as could have easily been done, leaves options open if I ever decide to fully embrace the retro and go all membraney.

So now it's on to some case designing, as well as going back and reworking of the Arduino Sketches.


Wednesday, 16 September 2015

Creating a PCB for Mounting the Keyboard Controller

The one of the goals of this keyboard project is to house all the components in a ZX81 inspired case. To help achieve that end, I'll need to mount the LeoStick and keyboard circuit onto something a little more permanent than a breadboard and I decided to make a relatively diminutive PCB to do this job.

This leads to the question: With the advent of easy to access PCB fabrication houses, is there any point in fabricating your own board? I was initially tempted to outsource the manufacturing but in the end decided to roll my own PCB for a couple of reasons.

Firstly, the PCB I need is not particularly complicated. It's a one sided board simply used to hold some resistors and header pins, an easy candidate for home fabrication. Note you could just as easily used some strip board to achieve a similar (if only slightly larger) result.

Secondly and most importantly, this journey is about having fun, not about saving time or always doing something the most convenient way. There are some simple pleasures derived from designing, creating and building something from start to finish. Though I concede that I am being a little selective in what I'm counting as DIY, in the context of the project as a whole.

Etched and Drilled PCB for Mounting the Controller Parts.
I hadn't made a PCB for a number of years but it's not a particularly daunting process. Even if you've never made a PCB before, despite what you may have read, it's not difficult and you don't require much in the way of specialised equipment. This is especially true if you're fabricating a basic one sided PCB. I made mine with the following minimal equipment.


The process is straight forward. Just follow the instructions that come with the Press N Peel and the PCB making kit and you can't go wrong.

  • Design your your PCB in Fritzing, Eagle Cad or similar.
  • Print out onto the Press N Peel PCB Film.
  • Use the Hacksaw to cut your PCB to size.
  • Apply the Press N Peel to the PCB.
  • Etch the PCB using all the tools in the PCB making kit.
  • Place the drill bit in the flexible shaft of the Rotary tool for easy control, then with a steady hand drill the holes.
  • The PCB is now ready to populate.

Check out this Indestructible for a more fully fleshed out description of the above.

Sunday, 13 September 2015

A Fritzing Layout and Some LeoStick Keyboard Code

Thought I'd end this weekend with a little code and a Fritzing layout.

A Breadboard Layout

The Fritzing layout is obviously for a LeoStick, though there is nothing stopping the basic layout (or the code) from working on the Leonardo.

Fritzig Layout for ZX81 Keyboard and a LeoStick.

A Little Code to Make it Go

The Code below incorporates everything described in my previous post, Multiple Layers and Keyboard Mode Selection. I've tested keyboard in emulator mode on a LINUX box, also on a PI running Raspbian, both tests using the sz81 emulator, and the keyboard works to my immediate satisfaction. I'm also pretty happy with the keyboards functionality in normal PS2 mode.

That said, there are some issues with an other emulator I've tried, ZEsarUX, where not every key press is registered. I'm not sure why this is as yet, I'll keep working, hopefully some resolution will be come to hand.

 // **************************************************************************  
 // **** ZX81 USB Keyboard for Funtronics LeoStick (based on a Leonardo). ****  
 // **************************************************************************  
 // ** David Stephenson 2015-10-04  **  
 // **                              **  
 // ** Originally based on code by: **  
 // ** Dave Curran 2013-04-27       **  
 // ** Tony Smith 2014-02-15        **  
 // **********************************  
 enum KEYMODES {EMULATOR, STANDARD};  
 enum KEYSTATES {NORMAL, NORMAL_SHIFTED, FUNCTION, GRAPHICS, GRAPHICS_SHIFTED};  
 #define NUM_ROWS 8  
 #define NUM_COLS 5   
 #define SHIFT_COL 4  
 #define SHIFT_ROW 5  
 #define DEBOUNCE_VALUE 220  
 #define REPEAT_DELAY 660  
 // ***********************  
 // ** Class Definitions **  
 // ***********************  
 // Defines a single key and its 4 possible Major states.  
 class ZxKey {  
   private:  
     // Standard Keyboard for Normal PC Use  
     byte bNormal;      // Standard ZX keyboard only in lower case. EMULATOR KEYMODE uses only bNormal.  
     // Extra Modes for using keyboard as a normal(ish) PS2 / USB device.  
     byte bNormalShifted;  // Red Symbols & Words (replaced by Symbols)   
     byte bGraphics;      // Standard ZX keyboard but CTL characters. Number = F1 - F10 etc  
     byte bGraphicsShifted;  // Standard ZX keyboard but ALT characters. Number = F11 - F12, 5 = home, 6 PGUP etc   
     // Monitor Keys last press / activity.  
     int iDebounceCount;  
     int iDebounceCountHighest;  
   public:  
     ZxKey (byte, byte, byte, byte);  
     // Return Key Values for specific States.  
     byte getKeyNormal(){  
       return bNormal;  
     }  
     byte getKeyNormalShifted(){  
       return bNormalShifted;  
     }  
     byte getKeyGraphics(){  
       return bGraphics;  
     }  
     byte getKeyGraphicsShifted(){  
       return bGraphicsShifted;  
     }  
     int setDebounceCount(int DebounceCount){  
       // Set autorepeat value lower if DebounceCount == iDebounceCountHighest  
       // eg on first debounce set to REPEAT_DELAY on next repeat timing will be initial DEBOUNCE_VALUE / 2  
       if (iDebounceCountHighest == DebounceCount ){  
         iDebounceCount = DEBOUNCE_VALUE / 2;  
       } else {  
         iDebounceCount = DebounceCount;  
         iDebounceCountHighest = DebounceCount;  
       }  
       //iDebounceCount = DebounceCount;  
     }  
     int resetDebounceCount(){  
       iDebounceCount = DEBOUNCE_VALUE;  
       iDebounceCountHighest = 0;  
     }  
     // iDebounceCount counts down to 0 from DEBOUNCE_VALUE (or REPEAT_DELAY)  
     int iDecreaseDebounceCount(){  
       iDebounceCount--;  
     }  
     int getDebounceCount(){  
       return iDebounceCount;  
     }  
 };  
 // ZxKey constructor  
 ZxKey::ZxKey (byte Normal, byte NormalShifted, byte Graphics, byte GraphicsShifted){  
   bNormal = Normal;  
   bNormalShifted = NormalShifted;  
   bGraphics = Graphics;  
   bGraphicsShifted = GraphicsShifted;  
   iDebounceCount = DEBOUNCE_VALUE;  
 }  
 // Defines entire keyboard, includes ZxKey class.  
 class ZxKeyBoard {  
   private:  
   // Setup 4 versions of keyboard states into keyboard.  
   // Keyboard mapped for US, might need changing for other configurations eg. UK keyboard.  
   ZxKey keyMap[NUM_ROWS][NUM_COLS] =   
     {  
       {{'5',KEY_LEFT_ARROW,KEY_F5,KEY_HOME},{'4','%',KEY_F4,KEY_INSERT},{'3','#',KEY_F3,KEY_ESC},{'2','@',KEY_F2,KEY_F12},{'1','!',KEY_F1,KEY_F11}},  
       {{'t','_','t','t'},{'r','&','r','r'},{'e','^','e','e'},{'w','`','w','w'},{'q','~','q','q'}},  
       {{'6',KEY_DOWN_ARROW,KEY_F6,KEY_PAGE_DOWN},{'7',KEY_UP_ARROW,KEY_F7,KEY_PAGE_UP},{'8',KEY_RIGHT_ARROW,KEY_F8,KEY_END},{'9','9',KEY_F9,'9'},{'0',KEY_BACKSPACE,KEY_F10,'0'}},  
       {{'g','\\','g','g'},{'f','}','f','f'},{'d','{','d','d'},{'s',']','s','s'},{'a','[','a','a'}},  
       {{'y','|','y','y'},{'u','$','u','u'},{'i','(','i','i'},{'o',')','o','o'},{'p','"','p','p'}},  
       {{'v','/','v','v'},{'c','?','c','c'},{'x',';','x','x'},{'z',':','z','z'},{0,0,0,0}},  
       {{'h','\'','h','h'},{'j','-','j','j'},{'k','+','k','k'},{'l','=','l','l'},{KEY_RETURN,KEY_RETURN,KEY_RETURN,KEY_RETURN}},  
       {{'b','*','b','b'},{'n','<','n','n'},{'m','>','m','m'},{'.',',','.','.'},{' ',KEY_TAB,'£',' '}}  
     };  
   public:  
     // Return from ZxKey matching specified state.  
     byte bKeyPress(byte bRow, byte bCol, KEYSTATES eKeystate){  
       // Value of Pressed key to Return.  
       byte bPressedKey =0;  
       // Get Keyboard character for correct Key state.  
       switch (eKeystate) {  
         case FUNCTION:  
           bPressedKey = keyMap[bRow][bCol].getKeyNormal();  
           // Adjust for Capital Letters  
           if (bPressedKey > 96 && bPressedKey < 123){  
             bPressedKey = bPressedKey - 32;  
           }  
           break;  
         case NORMAL_SHIFTED:  
           bPressedKey = keyMap[bRow][bCol].getKeyNormalShifted();  
         break;  
         case GRAPHICS:  
           bPressedKey = keyMap[bRow][bCol].getKeyGraphics();  
         break;  
         case GRAPHICS_SHIFTED:  
           bPressedKey = keyMap[bRow][bCol].getKeyGraphicsShifted();  
         break;  
         default:  
           // Standard normal and Emulator mode keyboard.  
           // Shift key modifier used later to select functions etc in a ZX81 Emulator.  
           bPressedKey = keyMap[bRow][bCol].getKeyNormal();  
         break;  
       }  
       // Keep track of length of key presses before returning a value.  
       if ( keyMap[bRow][bCol].getDebounceCount() == 0 ){  
         // Set repeat rate if key held down  
         keyMap[bRow][bCol].setDebounceCount(REPEAT_DELAY);  
         return bPressedKey;  
       } else {  
         keyMap[bRow][bCol].iDecreaseDebounceCount();  
         return 0;  
       }  
     }  
     byte bKeyRelease(byte bRow, byte bCol){;  
       keyMap[bRow][bCol].resetDebounceCount();  
       return 0;  
     }  
     byte bKeyDisable(byte bRow, byte bCol){;  
       keyMap[bRow][bCol].setDebounceCount(-1);  
       return 0;  
     }  
 };  
 // Defines LED / Mode switch panel and its behaviour.  
 // Three LEDs are configured to report on keyboard mode and states.  
 //   
 // In STANDARD mode:  
 //    Left LED on = GRAPHICS state.  
 //    Middle LED on = FUNCTION state.  
 //    Right LED on = NORMAL state.  
 //  
 // In EMULATOR mode:  
 //    Left and Right LEDs = on.  
 class ModeState {  
   private:  
     KEYMODES eKeyboardMode = STANDARD;  
     KEYSTATES eKeyboardState = NORMAL;  
     byte bModeSwitchPin;  
     byte bGraphicsPin;  
     byte bFunctionPin;  
     byte bModePin;  
     bool boDebounce = false;  
     byte SetMode(){  
       if (digitalRead(bModeSwitchPin) == HIGH && boDebounce == false) {  
         Serial.println("high ");  
         if (eKeyboardMode == STANDARD){  
           eKeyboardMode = EMULATOR;  
         } else {  
           eKeyboardMode = STANDARD;  
         }  
         delay(REPEAT_DELAY);  
         boDebounce = true;  
       } else if (digitalRead(bModeSwitchPin) == LOW) {  
         boDebounce = false;  
       }  
     }  
   public:  
     ModeState (byte, byte, byte, byte);  
     byte SetState(KEYSTATES eKeystate){  
       eKeyboardState = eKeystate;  
       // Check and set mode if Mode switch is pressed  
       SetMode();  
       if (eKeyboardMode != EMULATOR){  
         switch (eKeyboardState) {  
           case FUNCTION:  
             digitalWrite(bGraphicsPin, LOW);  
             digitalWrite(bFunctionPin, HIGH);  
             digitalWrite(bModePin, LOW);  
             break;  
           case GRAPHICS:  
             digitalWrite(bGraphicsPin, HIGH);  
             digitalWrite(bFunctionPin, LOW);  
             digitalWrite(bModePin, LOW);  
             break;  
           default:  
             digitalWrite(bGraphicsPin, LOW);  
             digitalWrite(bFunctionPin, LOW);  
             digitalWrite(bModePin, HIGH);  
             eKeyboardState = NORMAL;  
             break;  
         }  
       } else {  
         digitalWrite(bGraphicsPin, HIGH);  
         digitalWrite(bFunctionPin, LOW);  
         digitalWrite(bModePin, HIGH);  
         eKeyboardState = NORMAL;  
       }  
     }  
     KEYMODES GetMode(){  
       return eKeyboardMode;  
     }  
     KEYSTATES GetState(){  
       return eKeyboardState;  
     }  
 };  
 // ModeState constructor.  
 ModeState::ModeState (byte ModeSwitchPin, byte GraphicsPin, byte FunctionPin, byte ModePin){  
   bModeSwitchPin = ModeSwitchPin;  
   bGraphicsPin = GraphicsPin;  
   bFunctionPin = FunctionPin;  
   bModePin = ModePin;  
   pinMode(bModeSwitchPin, INPUT);  
   pinMode(bGraphicsPin, OUTPUT);  
   pinMode(bFunctionPin, OUTPUT);  
   pinMode(bModePin, OUTPUT);  
 }  
 // ************************  
 // *** Global Variables ***  
 // ************************  
 // Setup Global Variables for keyboard.  
 ZxKeyBoard MyKeyboard;  
 // Setup mode switch and indicator LEDs.  
 ModeState MyModeState(A5, A0, A1, A2);  
 // Setup Keyboard row and column pins.  
 const byte bColPins[NUM_COLS] = {13, 12, 10, 9, 8};  
 const byte bRowPins[NUM_ROWS] = {7, 6, 5, 4, 3, 2, 1, 0};  
 // ******************  
 // *** Main Setup ***  
 // ******************  
 void setup() {  
   // Set all Keyboard pins as inputs and activate pull-ups.  
   for (byte bColCount = 0 ; bColCount < NUM_COLS ; bColCount++)  
   {  
     pinMode(bColPins[bColCount], INPUT);  
     digitalWrite(bColPins[bColCount], HIGH);  
   }  
   // Set all Keyboard pins as inputs.  
   for (byte bRowCount = 0 ; bRowCount < NUM_ROWS ; bRowCount++)  
   {  
     pinMode(bRowPins[bRowCount], INPUT);  
   }  
   // initialize control over the keyboard.  
   Serial.begin(9600);  
   Keyboard.begin();  
 }  
 // ************************  
 // *** Main loop        ***  
 // *** Lets get Busy-ah ***  
 //*************************  
 void loop() {  
   bool boShifted = false;  
   byte bKeyPressed = 0;  
   KEYMODES eMyKeyMode = MyModeState.GetMode();  
   KEYSTATES eMyKeyState = MyModeState.GetState();  
   // Check for the Shift key being pressed.  
   pinMode(bRowPins[SHIFT_ROW], OUTPUT);  
   if (digitalRead(bColPins[SHIFT_COL]) == LOW) boShifted = true;  
   pinMode(bRowPins[SHIFT_ROW], INPUT);  
   for (byte bRow = 0 ; bRow < NUM_ROWS ; bRow++)  
   {  
     // Run through the rows, turn them on.  
     pinMode(bRowPins[bRow], OUTPUT);  
     digitalWrite(bRowPins[bRow], LOW);  
     for (byte bCol = 0 ; bCol < NUM_COLS ; bCol++)  
     {   
       if (digitalRead(bColPins[bCol]) == LOW)  
       {  
         if (boShifted && eMyKeyMode != EMULATOR){  
           // Select correct Keyboard layout for current state For STANDARD mode.   
           // Shift alters the Current state selection. eg. gets Red Shift symbols in Normal State.   
           switch (eMyKeyState) {  
             case NORMAL:          
               bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, NORMAL_SHIFTED);                    
               if (bKeyPressed == KEY_RETURN) {   
                 eMyKeyState = FUNCTION;  
                 bKeyPressed = 0;  
                 MyKeyboard.bKeyDisable(bRow, bCol);  
               }  
               if (bKeyPressed == '9') {  
                 eMyKeyState = GRAPHICS;  
                 bKeyPressed = 0;  
                 MyKeyboard.bKeyDisable(bRow, bCol);  
               }        
               break;  
             case FUNCTION:  
               bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, NORMAL_SHIFTED);  
               if (bKeyPressed == KEY_RETURN) {   
                 eMyKeyState = NORMAL;  
                 bKeyPressed = 0;  
                 MyKeyboard.bKeyDisable(bRow, bCol);  
               }  
               if (bKeyPressed == '9') {  
                 eMyKeyState = GRAPHICS;  
                 bKeyPressed = 0;  
                 MyKeyboard.bKeyDisable(bRow, bCol);  
               }  
               break;  
             case GRAPHICS:  
               bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, GRAPHICS_SHIFTED);  
               if (bKeyPressed == KEY_RETURN) {   
                 eMyKeyState = FUNCTION;  
                 bKeyPressed = 0;  
                 MyKeyboard.bKeyDisable(bRow, bCol);  
               }  
               if (bKeyPressed == '9') {  
                 eMyKeyState = NORMAL;  
                 bKeyPressed = 0;  
                 MyKeyboard.bKeyDisable(bRow, bCol);  
               }  
               break;  
           }  
         } else {  
           // Emulator keyboard, the keyboard states are controlled by a ZX81 emulator.  
           // Keyboard mimics unaltered PS2 Keyboard presses as expected by an emulator.  
           bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, eMyKeyState);  
         }  
         if (bKeyPressed > 0 ) {  
           //Serial.write(bKeyPressed);  
           if (eMyKeyMode == EMULATOR && bKeyPressed && boShifted) Keyboard.press(KEY_LEFT_SHIFT);  
           if (eMyKeyMode != EMULATOR && eMyKeyState == GRAPHICS && bKeyPressed > 96 && bKeyPressed < 123){  
             if (boShifted){  
               Keyboard.press(KEY_LEFT_ALT);  
             } else {  
               Keyboard.press(KEY_LEFT_CTRL);  
             }  
           }  
           Keyboard.press(bKeyPressed);  
           Keyboard.releaseAll();  
           //tone(11, 31, 20);  
         }  
       } else {  
         MyKeyboard.bKeyRelease(bRow, bCol);  
       }  
     }  
     pinMode(bRowPins[bRow], INPUT);  
   }  
   digitalWrite(bRowPins[SHIFT_ROW], LOW);  
   // Update LED panel and check for Mode change switch press.  
   MyModeState.SetState(eMyKeyState);  
 }  

Wednesday, 9 September 2015

Multiple Layers and Keyboard Mode Selection

After deciding last post that the keyboard code required separate emulation and standard PS2 layers, I got to work on some test code and some additions to the LeoStick circuit adding some visual clues and a mode switch button. The result, while still in need of a lot of work to get functioning as I would like, works quite okay for the moment.

So what have we added exactly? There are now 2 major keyboard layers, selectable via a momentary switch. Emulation mode, is for use with ZX81 emulator software and simply passes through key presses as expected by an emulator. The Second mode, or Standard mode attempts to provide the functionality of a PS2 keyboard.

To access a large portion of a PS2 keyboards functionality, various modes are selectable by using the standard mode keys on the ZX81 keyboard.

Three LEDs are used to indicate the keyboard layer selected and while in the Standard layer they also serve to indicate what mode the keys are in. This is not required in emulator mode as the emulator provides it's own visual feedback.

I haven't decided on a final layout as yet, as the underlying code is still in a state of flux.

Normal Layer: Keyboard Mode and Function Selection
NORMALNormal mode.
SHIFTSymbols in Red are selected. Where these red keys are commands, for example 'EDIT or SLOW' they have been replaced by another symbol. All common symbols are now present on the keyboard.
SHIFT,FUNCTIONChanges to Function mode. This selects upper case characters. Pressing the SHIFT key in this mode will select symbols as normal.
SHIFT,GRAPHICSAll the number keys are now there equivalent 'Fx' key, ie. '1' becomes 'F1'. Letter become 'CTRL Letter'. Holding down the SHIFT key in Graphics mode changes the letter keys to 'ALT Letter', number keys '1' and '2' become 'F11' & 'F12'.


A small demo of the keyboard in action.

Sunday, 6 September 2015

The New ZX81 Keyboard with a LeoStick

In my original tests I used an Arduino UNO, and tested basic keyboard functionality using a mini push-button keyboard via the serial port. The circuit and code used for these tests was pretty much a copy from Dave Curran's (Tynemouth Software) site.

I had intended to have the ZX81 keyboard control a Raspberry PI using the serial in, on the PI's GPIO header, and then acting as a PS2 keyboard via that serial connection. While this seems like it should be possible and basic serial communication was easily achievable, I didn't have much luck getting it to work with the PS2 protocol. For anybody with more fortitude than I, inputattach seems to hold they key.  In the end I fell back on an easier option, that of using USB.

A LeoStick acting as a ZX81 keyboard controller
The UNO doesn't have HID capabilities normally, though this can be worked around, however the Arduino Leonardo does have HID functionality out of the box. This then seemed like an obvious choice of board to use. I didn't have a Leonardo, but I did have something similar, yet smaller to hand, a Freetronics LeoStick.

Not being the first person to do something is usually pretty beneficial, and in the case of interfacing ZX81 keyboards, Arduinos and Raspberry PI's doubly so. Not only has the Leonardo been used to do just this previously, there is already a complete 'how to', thanks to Smittytone and The Sinclair ZX81: a Raspberry Pi retro restyle – Part 1 blog entry.

The complete code listing for the Leonardo and instructions are in the Smittytone blog, so I wont reproduce them here. I did have to make one tiny alteration to get things working with the LeoStick, as Pin 11 is linked to an inbuilt piezo on the LeoStick. You simply need to change the following in the code and wiring up accordingly.

Change from:
// Define the row and column pins
byte colPins[NUM_COLS] = {13, 12, 11, 10, 9};
Change to:
// Define the row and column pins
byte colPins[NUM_COLS] = {13, 12, 10, 9, 8};

The keyboard works perfectly with the LeoStick as is, however the functionality of the Arduino code is not complete. There is no emulator mode / layer, and there are a lot of key codes missing that are required for a complete PS2 keyboard replacement. With the limited number of keys available on the ZX81 keyboard it wont be easy, however the manner in which keyboard is used in a ZX81 provides the answer to this problem.

Each Key on the ZX81 keyboard has several modes of operation, you can switch these modes by pressing the Shift key and either a combination of Shift, Function or Shift, Graphics keys. There are a total of 6 possible modes to set the keyboard to, this should give plenty of options for filling out most of the required PS2 keys. Of course it wont be the most efficient of keyboard mappings, but then that was always going to be a problem. 

One last thing, proving just how these things go kind of go around in circles, there is a blog entry on the Freetronics site, commenting on the possibility of using a LeoStick to drive a ZX81 keyboard.

Friday, 4 September 2015

Procuring a Keyboard that Looks the Part

Any self respecting quasi ZX81 recreation needs a proper ZX81 keyboard, and by proper we mean totally crap by any modern (or even period) idea of what a keyboard should be like. In short the ZX81 uses a 40-key membrane keyboard completely lacking in any tactile feel, it's almost as bad as an iPone (with haptic feedback turned off). Luckily ZX81 replacement keyboard membranes are in high enough demand that you can procure brand new ones.

The ZX8-KDLX replacement Keyboard for a ZX81 by PokeMon
Sell My Retro has your back for keyboards, there are a couple of choices, a classic membrane version sold by RWAP Software, and the model I ended up going for the ZX8-KDLX sold by PokeMon. The ZX8-KDLX has 40 SMD tactile switches (yah tactile) mounted under a top cover. Basically you get all the look of a ZX81 keyboard with a little extra feel.

All up the ZX8-KDLX is a pretty nifty little keyboard, if your after a more bouncy approach to typing on some period hardware without loosing the look, then I'd highly recommend this bit of kit. For bonus points it'll suit my own project perfectly.

Starting the Sinclair ZX81 Keyboard Adventure

After stumbling on Elite Systems Recreated Spectrum recently, I got to thinking why not do something similar for the ZX81. Not the making of some sort of commercial product part, rather the quasi recreation component of the equation.

ZX81 Arduino Keyboard, based on Dave Curran's design
Now there isn't a exactly a lack of decidedly brilliant modern ZX81 interpretations, clones and lookalikes out there in the wild, many a capable electronic / computer boffin has seen to that. That's not really the point, I'm simply working on a personal project recreating the wheel or in this case a ZX81ish type thing.

So some early clarifications, the project isn't about re-engineering a ZX81 from the ground up, it's not even going to be about re-creating the exact look of the original, it's simply about creating something with passing physical appearance resemblance and finding a (non)practical use for ZX81 like computer in a case.

All of which leads nicely to why it's a ZX81 based on a Adrduino and a Raspberry Pi 2. So yes, in large part this is just another shove a PI into case and relive the glory days of
microcomputers with the use of an emulator kinda project.

As we've established we're not breaking entirely new ground here, and frankly I was pretty happy to find some precedents. The first place I came across was Dave Curran's excellent site, where he's been documenting and constructing similar devices to what I'd partially envisaged for the ZX81 and other micros for a number of years.

Now having found a starting point, some rather useful code and diagrams on Dave's site, I went about constructing the first part of the project, a test bed or the prototype of the things to come, as seen pictured and working in this very post.

It should be easy from now yes? Well actually no, there is a lot more to do..............