Thursday, 31 December 2015

Build Your Own AZ15

Why not round out the year be building your AZ15, the Raspberry Pi, Arduino and ZX81 keyboarded wonder of a mini computer.

There have been a number of requests asking where to get the case, all the parts etc, so after ironing out a couple of minor issues I've made the Case available on Shapeways, and below is detailed most of everything else you'll require to complete your own AZ15 project.

This little is adventure has always been about achieving a fun build in the spirit of  "just because it's possible I think I'll give it a go", so please  don't expect this thing to be a cheep enterprise (3D Printing the case is the largest single cost factor here). This project is a learning experience and one undertaken to just see what could be achieved with 3D printing, off the shelf Arduino components and some hard graft.

Before you rush of and purchase everything, keep in mind that this is also not a professional product and doesn't come with a warranty, though if you ask nicely I'll do my best to provide some help along the way, should you decide to give it a go.

Major Case Components List


These are the major components, the ones responsible for all the heavy lifting, style substance and the impress (or not) your friends bits at the heart of the AZ15. 

Major& Miscellaneous Parts Shopping List


Amount Part Type Properties
1 AZ15 Computer Case Main AZ15 Case to hold the Raspberry Pi2 and Keyboard Converter Board etc
1 AZ15 Computer Case Lid For a Complete case the lid component is also required
1 Raspberry Pi2 Specifically a Raspberry Pi2 is required to fit inside the AZ15 Case.
1 LeoStick Freetronics LeoStick (Arduino Compatible)
1 ZX81 Keyboard ZX8-KDLX - PCB replacement keyboard for ZX81. These can be purchase fully assembled or un-assembled depending on preference. There is also a ZX80 variant available, compatible with the AZ15 project, if you're feeling super retro. (some minor changes to Arduino code to match the layout may be required).
1 Rubber Feet Rubber Feet, Small Stick On, Size: 12 x 12mm- Height 6mm
1 Compact Right Angled USB Cable This goes inside the AZ15 case and connects the Pi to the Leostick. A 15cm length of cable should be plenty. I got one of these from Ebay.
1 Mini USB WIFI Dongle Optional, for plugging into the internal USB port.
1 Double Sided Tape Black Double sided tape for mounting the ZX81 Keyboard into the case keyboard tray.
1 Hot Glue Hot glue and glue gun, used to permanently attach LEDs to case.


Converter Board Components and Minor Case Furnishings


In this section is listed everything required to build the Converter Board. I've linked to the exact components I used in my original board for clarification. You should be able to match the linked components to you're local supplier if needed.

Shopping List


Amount Part Type Properties
1 Molex 22-02-3053 header - 5 pins hole size 1.0mm,0.508mm; row single; package THT; pins 5; form ♀ (female); variant variant 4; pin spacing 0.1in (2.54mm)
1 Molex 22-02-3083 header - 8 pins hole size 1.0mm,0.508mm; row single; package THT; pins 8; form ♀ (female); variant variant 5; pin spacing 0.1in (2.54mm)
4 Locking male header - 2 pins 2 Pin 0.1 Straight Locking Header - 2.54 pitch - Single; form ♂ (male); pin spacing 0.1in (2.54mm)
2 Generic male header - 14 pins hole size 1.0mm,0.508mm; row single; package THT; pins 14; form ♂ (male); pin spacing 0.1in (2.54mm)
4 Header with Crimp Pins - 2 pins 2 Pin 0.1 Header with Crimp Pins - 2.54 pitch (2.54mm). Connect LEDs and switch to Board
3 Red (633nm) LED color Red (633nm); package 5 mm [THT]; leg yes. Use hookup wire and Header to Connect to Board and Mounting in Case
1 LeoStick As above, don't panic, you don't require 2 of these. Assemble separately with female header at bottom. Plugs into J7 and J8
3 220Ω Resistor tolerance ±5%; package THT; bands 4; resistance 220Ω; pin spacing 400 mil
1 10kΩ Resistor tolerance ±5%; package THT; bands 4; resistance 10kΩ; pin spacing 400 mil
1 Round Pushbutton switching circuit SPST; default state Normally Open. Mounting hole 12mm
1 Hook Up Wire Various Colours, For connecting LEDs and Switch to Board via Headers
1 Heatshrink For isolating LEDs, prevent contact with Raspberry PI etc


Converter Board Assembly


ZX81 Keyboard Converter Board PCB Layout.
There is not a great deal to assemble on the
Converter Board, just a couple of resistors, pin headers and the Molex sockets, over all is a very basic solder job.

All the files required files for ordering a PCB from a fabrication house are contained in AZ15_PCB_layout.zip. Or do as I did, and use the same files to to create your own PCB. I outlined the basic process earlier in the blog. Extra details on construction of the Converter Board have also been documented previously.

Of course you'll nee the latest Arduino sketch to fuel the Leostick, and that can be found in the zx81usbkeyboard_20151.tar.gz file.

Components highlighted in bold green, under Properties, are not soldered / attached directly to the converter board. These components are for attaching via hookup wires and headers to the board, and are mounted in or on the AZ15 Case.

Assembly List


Label Part Type Properties
J1 Molex 22-02-3053 header - 5 pins hole size 1.0mm,0.508mm; row single; package THT; pins 5; form ♀ (female); variant variant 4; pin spacing 0.1in (2.54mm)
J2 Molex 22-02-3083 header - 8 pins hole size 1.0mm,0.508mm; row single; package THT; pins 8; form ♀ (female); variant variant 5; pin spacing 0.1in (2.54mm)
J3 Locking male header - 2 pins 2 Pin 0.1 Straight Locking Header - 2.54 pitch - Single; form ♂ (male); pin spacing 0.1in (2.54mm)
J4 Locking male header - 2 pins 2 Pin 0.1 Straight Locking Header - 2.54 pitch - Single; form ♂ (male); pin spacing 0.1in (2.54mm)
J5 Locking male header - 2 pins 2 Pin 0.1 Straight Locking Header - 2.54 pitch - Single; form ♂ (male); pin spacing 0.1in (2.54mm)
J6 Locking male header - 2 pins 2 Pin 0.1 Straight Locking Header - 2.54 pitch - Single; form ♂ (male); pin spacing 0.1in (2.54mm)
J7 Generic male header - 14 pins hole size 1.0mm,0.508mm; row single; package THT; pins 14; form ♂ (male); pin spacing 0.1in (2.54mm)
J8 Generic male header - 14 pins hole size 1.0mm,0.508mm; row single; package THT; pins 14; form ♂ (male); pin spacing 0.1in (2.54mm)
LED1 Red (633nm) LED color Red (633nm); package 5 mm [THT]; leg yes
LED2 Red (633nm) LED color Red (633nm); package 5 mm [THT]; leg yes
LED3 Red (633nm) LED color Red (633nm); package 5 mm [THT]; leg yes
LeoStick Arduino Assemble separately with female header at bottom. Plugs into J7 and J8
R1 220Ω Resistor tolerance ±5%; package THT; bands 4; resistance 220Ω; pin spacing 400 mil
R2 220Ω Resistor tolerance ±5%; package THT; bands 4; resistance 220Ω; pin spacing 400 mil
R3 220Ω Resistor tolerance ±5%; package THT; bands 4; resistance 220Ω; pin spacing 400 mil
R4 10kΩ Resistor tolerance ±5%; package THT; bands 4; resistance 10kΩ; pin spacing 400 mil
S1 Round Pushbutton switching circuit SPST; default state Normally Open. Mounting hole 12mm

Wednesday, 9 December 2015

AZ15 and a Tale of two Features

Feature One: As seen on the GuardianWitness


A big thanks to the The Guardian and Matthew Holmes in particular, for featuring this humble Raspberry Pi, Arduino and ZX81 keyboard project in the article Best reader Raspberry Pi projects – and some of the most pointless. There are loads of other interesting Pi related constructions to peruse through, the article is a fun sample of all the ways people are finding to enjoy the Pi. I particularly enjoyed the Raspberry Pi hacked into a Holga 120 Film Camera, and its taking of photos old school.

The AZ15, as seen in all the best online newspapers.


Feature Two: New Keyboard Functionality


The nice thing about making something yourself is that you can continue to work on it, even if to all external appearances the project may seem complete.

Having used the AZ15 for almost two weeks now I decided to add a little extra functionality, addressing some minor usability issues, the main one being my forgetting to switch between Emulator and Standard keyboard modes. When using a ZX81 emulator with the keyboard, the button on the right side of the case is pressed, this changes the keyboard mode, and the LEDs on top visually tell you what mode you're in. This is all fine, except I keep forgetting to press the button.

The solution is simple enough, have the keyboard change modes automatically. Nicely the Raspberry Pi can talk to the LeoStick via serial link and vice versa. A couple of commands directed at the serial interface before launching an Emulator automatically selecting the correct keyboard mode and things soon become more user friendly.

For example, emulator mode can be selected and a notification bell passed with the following redirection to the serial port the LeoStick / Keyboard is attached to.

echo emulator bell > /dev/ttyACM0

Putting the above in a start script that launches the emulator and then sets all back to normal after exiting solves my main issues.



#!/bin/bash
# Put keyboard in Emulator Mode
echo emulator bell > /dev/ttyACM0
# Launch the sz80 emulator
sz81
# Set keyboard back to Standard Mode
echo standard > /dev/ttyACM0

If the USB port assignment changes around, "dmesg" could always be used to parse for the correct device.


Command Line, Mode and Options Selection
BELLSound the LeoStick Piezo. Could be used to forward audio system notifications etc. 
BEEP OFFTurn off Keyboard Sounds. Sounds are off by default.
BEEP ONTurn on Keyboard Sounds. Similar to the keyboard clicks made by ZX Spectrum when typing. The keyboard only emits clicks in Standard mode. In Emulation mode sound is always off.
DEVICEReturns message "ZX81_KEYS" to the console. Useful if unsure you have the correct serial port. You must be monitoring incoming streams to get the return message. eg cat /dev/ttyACM0
EMULATORSwitch the keyboard into emulator mode.
STANDARDSwitch the keyboard into standard mode.


The latest Arduino sketch can be downloaded here: http://zx81.dasteph.com/files/zx81usbkeyboard_20151205.tar.gz

Friday, 27 November 2015

Presenting the AZ15, a not Actuall Clone of a ZX81 Assembled

I was confident it would look pretty cool, yet once the case arrived (after some timely industrial action at customs and excise) my expectations were exceeded by far. It does in fact look damn amazing, this 3D printing thing and by extension Shapeways ability to turn home design into reality is brilliant.


As expected the surface on the final product is lightly rough to touch and has a non-gloss finish. It kind of reminds me of suede, if suede assumed a plasticky form. You can still see the layers where the object has been built up but that certainly doesn't detract from the looks of the AZ15 case. Invariably I couldn't wait to put it all together, and thus the beginning of the end phase ensued.

Firstly I gathered all the disparate parts together. Internally the case had the following items to be installed, a Raspberry Pi 2, the LeoStick and Converter board, a right-angled usb cable to connect to the LeoStick, a WIFI dongle, LEDs and the mode selector switch. The keyboard would sit nicely on the tray, with it's cable sliding through a gap provided at the upper right corner much, like on the original ZX81.


The Pi, USB cable and WIFI dongle were installed first. I was relived that this all fit exactly as planned, being that the AZ15 case was designed specifically to hide one of the USB port banks and thus give the appearance of one complete unit to the casual observer.


Next the LEDs were hot glued into position, the mode switch installed and the keyboard converter board were inserted. Working in the confined space was a little challenging but everything so far went in without to much difficultly. As I'd made the converter myself and therefore drilled the mounting holes by hand, the mounting points were designed to allow a little slip and slide for easy adjustment. The one thing I hadn't counted on was not being able to affix the bolt heads due to access issues at the far right corners, luckily for me the drill holes in the converter board are nice and tight, so the bolts screwed in and held the board firmly enough.


Time to plug in the brains of the keyboard ie. the LeoStick. It's all a tight little fit, leaving just enough room to smuggle in the cables coming directly from the keyboard. The keyboard was mounted with some doubled sided tape on the keyboard tray.


Flipping the case over, some handy dandy rubber feet are slotted into position to provide much needed non slipping action, necessary when typing on a keyboard of diminutive weight and size.


Now it was a simple matter of plonking the lid on, except that the plonking didn't quite plonk as one might have expected. Unfortunately I'd made the little tabs on the side of the lid slight to small, meaning that the lid slides about a little bit to much. The immediate solution was to use some tiny strips of double sided tape to hold the movable lid in position. At a later date I might opt to get the lid reprinted and permanently fix this relatively minor issue.


So that's it, time for some sitting back a marveling at my creation. I'm very pleased with the final look of the AZ15 case, and hopefully I've managed to do the original ZX81 design justice and possibly at least slightly amuse Rick Dickinson the creator of the ZX81's iconic looks.


Monday, 9 November 2015

Final Case Design Shipped to Shapeways

Az15 Raspberry Pi Computer / LeosStick Computer with ZX81 Keyboard
AZ15 Deign Impression
The final AZ15 case design has been shipped off to Shapeways for printing and the nervous wait for a tangible object to arrive in the post has begun. All being well, the physical product will work as designed; meeting all expectations generated by the on screen version of reality.

The case was designed using the modeler AC3D, I've been using this tool for various projects over a number of years (though never for a 3d printed model before), so am pretty comfortable using it. A Raspberry PI 2 / B+ model was sourced from Thingiverse to construct the case around. I can't locate the exact model I used now, however there are a number to chose from including this one by jayftee. It's always a bonus when somebody else has done the hard work of modeling and exacting object for you.

AZ15 Case Design, Side View
Some decisions taken early on that dictated the shape and size of the AZ15 case. The choice not to de-solder the USB and Ethernet ports from Raspberry PI 2 lent extensively to the final design. The PI 2 has four USB ports, and I've designed the case such that two of these ports are only available internally. This layout allows the Leostick to connect internally and invisibly to one of these ports, giving the overall external appearance of one complete unit. The other internally USB port could be used to house a WIFI or Bluetooth adapter.

AZ15 Case Design, Rear View
The choice of configuration for the USB ports has given a rather unique cut-out appearance at the back of the computer, a nice little design touch that also helps maintain a compact and tidy feel when external USB devices are plugged in at the side.

From the outset I hadn't intended to clone a ZX81 case, the original being a design classic in its own right. What I'm after is something reminiscent of a classic 80s microcomputer but with a contemporary feel. A kind of modern clone. Hopefully within the next week or so I'll find out just how successful I've been.


Wednesday, 4 November 2015

Some More Arduino Code for the ZX81 Keyboard

I've made  a fair number of changes to the ZX81 keyboard Arduino sketch. The most noticeable being that the code is now broken into multiple files / libraries; all feeling a little more C++ like. Of course real microcontroller programmers use C, but we wont discus that here.

In general the keyboard functions as earlier. The main physically tangible improvement being that the keyboard performs a whole lot better in ZX81 emulators. It's not perfect and it's definitely not ideal for playing real time games with, though It works as advertised when typing normally, ie. using the keyboard to program with.

Emulator comparability wise, the keyboard will work with  sz81 and ZEsarUX. I'd recommend using sz81, as the keyboard still functions best with that particular emulator. I've also had a go at using Fuse with the keyboard, and to the extent that a ZX81 keyboard can be used on ZX Spectrum it functions okay, though it is missing those couple of crucial extra keys for proper Spectrum emulation.

When using the keyboard in standard mode as a regular USB input device in Linux or with a Raspberry Pi there are no issues, unfortunately, as with the emulators, games that require SDL libraries suffer from the "keys not being registered when you'd like them to be" syndrome. This issue I'm afraid may be down to problems with the Arduino USB Keyboard library and beyond my immediate control.

Presented For Your Amusement, Code Caught in the Wild


Main Program (zx81usbkeyboard.ino)

As the name on the implies, here be the general program.


// **************************************************************************
// **** ZX81 USB Keyboard for Funtronics LeoStick (based on a Leonardo). ****
// **** zx81usbkeyboard.ino                                              ****
// **************************************************************************
// ** David Stephenson 2015-11-03  **
// **                              **
// ** Originally based on code by: **
// ** Dave Curran  2013-04-27      **
// ** Tony Smith 2014-02-15        **
// **********************************

 #include "Arduino.h"
 #include "zx81keyboard.h"
 #include "zx81modestate.h"
 
// ************************
// *** Global Variables ***
// ************************

// Setup Global Variables for keyboard.
ZxKeyBoard MyKeyboard;

// Setup LeoStick pins for mode switch and indicator LEDs.
ModeState MyModeState(A5,A0,A1,A2);

// Setup LeoStick pins for Keyboard rows and columns.
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;
 
 // Set Keyboard Mode.
 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 == STANDARD){
     // 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, eMyKeyMode);
       if (bKeyPressed == KEY_RETURN) { 
        eMyKeyState = FUNCTION;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
       if (bKeyPressed == '9') {
        eMyKeyState = GRAPHICS;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
      break;
       
      case FUNCTION:
       bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, NORMAL_SHIFTED, eMyKeyMode);
       if (bKeyPressed == KEY_RETURN) { 
        eMyKeyState = NORMAL;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
       if (bKeyPressed == '9') {
        eMyKeyState = GRAPHICS;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
      break;
       
      case GRAPHICS:
       bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, GRAPHICS_SHIFTED, eMyKeyMode);
       if (bKeyPressed == KEY_RETURN) { 
        eMyKeyState = FUNCTION;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
       if (bKeyPressed == '9') {
        eMyKeyState = NORMAL;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(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, eMyKeyMode);
    }
    
    if (bKeyPressed > 0 ) {
     //Serial.write(bKeyPressed);
     if (eMyKeyMode == EMULATOR){
      if (bKeyPressed && boShifted) Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(bKeyPressed);
      // Many Emulators don't use standard OS keyboard routines and require a pause
      // for key to register. There is some variance, but 100ms seems to cover most.  
      delay(DEBOUNCE_DELAY/2);
     } else {
      if (eMyKeyState == GRAPHICS && bKeyPressed > 96 && bKeyPressed < 123){
       if (boShifted){
        Keyboard.press(KEY_LEFT_ALT);
       } else {
        Keyboard.press(KEY_LEFT_CTRL);
       }
      }
      Keyboard.press(bKeyPressed);
      // Some audio feedback if not in EMULATOR mode.
      // Kill the line if you hate beepy beep beeps.
      tone(11, 31, 20);
     }
     
     Keyboard.releaseAll();
    }
    
   } else {
    MyKeyboard.KeyPressReset(bRow, bCol);
   }
  }
  pinMode(bRowPins[bRow], INPUT);
 }
 
 digitalWrite(bRowPins[SHIFT_ROW], LOW);
 
 // Update LED panel and check for Mode change switch press.
 MyModeState.SetState(eMyKeyState);
 
 }

Keyboard Class (zx81keyboard.h)

Main guts of the keyboard setup and initial handling is in here.


// ***********************
// ** zx81keyboard.h    **
// ** Class Definitions **
// ** For ZX81 keyboard **
// ***********************

enum KEYMODES {EMULATOR, STANDARD};
enum KEYSTATES {NORMAL, NORMAL_SHIFTED, GRAPHICS, GRAPHICS_SHIFTED, FUNCTION};

#define NUM_ROWS 8
#define NUM_COLS 5 

#define SHIFT_COL 4
#define SHIFT_ROW 5
//#define FUNCTION_COL 4
//#define FUNCTION_ROW 6
//#define GRAPHICS_COL 3
//#define GRAPHICS_ROW 2

#define DEBOUNCE_DELAY 200 //debounce countdown value.
#define DEBOUNCE_DELAY_REPEAT 600

// Defines a single key and its 4 possible Major KEYSTATES: NORMAL, NORMAL_SHIFTED, GRAPHICS, GRAPHICS_SHIFTED
// Returns 5 KEYSTATES, adds FUNCTION: Caps Lock).
class ZxKey  {
 private:
  // Standard Keyboard Characters for Normal PC Use
  byte bNormal;   // Standard ZX keyboard only in lower case. EMULATOR KEYMODE should use bNormal only.
  // 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 should be used with CTL characters. Numbers = F1 - F10 etc
  byte bGraphicsShifted; // Standard ZX keyboard but should be used with ALT characters. Numbers = F11 - F12, 5 = home, 6 PGUP etc 
  
  // Monitor Keys last press / activity.
  short iDebounceCount = DEBOUNCE_DELAY;
  short iDebounceCountHighest = iDebounceCount;

  byte KeyValue(KEYSTATES eKeystate) {
   // Get Keyboard character for correct Key state.
   byte bKeyValue = 0;

   switch (eKeystate) {
    case FUNCTION:
     bKeyValue = bNormal;
     // Adjust for Capital Letters
     if (bKeyValue > 96 && bKeyValue < 123){
      bKeyValue = bKeyValue - 32;
     }
     break;
     
    case NORMAL_SHIFTED:
     bKeyValue = bNormalShifted;
     break;
     
    case GRAPHICS:
     bKeyValue = bGraphics;
     break;
     
    case GRAPHICS_SHIFTED:
     bKeyValue = bGraphicsShifted;
     break;
     
    default:
     // Standard normal and Emulator mode keyboard.
     // Shift key modifier used later to select functions etc in a ZX81 Emulator.
     bKeyValue = bNormal;
     break;
   }
   return bKeyValue;
  }
  
 public:
  ZxKey (byte, byte, byte, byte);
   
  byte KeyPressDetected(KEYSTATES eKeystate, KEYMODES eKeymode){
   byte bKeyValue = 0;
   iDebounceCount--;
   if (iDebounceCount == 0){
    switch (iDebounceCountHighest) {
     case DEBOUNCE_DELAY:
      iDebounceCount = DEBOUNCE_DELAY_REPEAT;
      iDebounceCountHighest = iDebounceCount;
     break;
     
     case DEBOUNCE_DELAY_REPEAT:
      if (eKeymode == EMULATOR){
       iDebounceCount = DEBOUNCE_DELAY;
      } else {
       iDebounceCount = DEBOUNCE_DELAY / 2;
      }
     break;
     
     default:
      iDebounceCount = DEBOUNCE_DELAY;
     break;
    }
    bKeyValue = KeyValue(eKeystate);
   }
   return bKeyValue;
  }
  
  void KeyPressReset(){
   iDebounceCount = DEBOUNCE_DELAY;
   iDebounceCountHighest = iDebounceCount;
  }
 
  void KeyDisable(){
   iDebounceCount = -1;
  }
};


// ZxKey constructor
ZxKey::ZxKey (byte Normal, byte NormalShifted, byte Graphics, byte GraphicsShifted){
 bNormal = Normal;
 bNormalShifted = NormalShifted;
 bGraphics = Graphics;
 bGraphicsShifted = GraphicsShifted;
}


// 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, KEYMODES eKeymode){
   return keyMap[bRow][bCol].KeyPressDetected(eKeystate, eKeymode);
  }
  
  void KeyPressReset(byte bRow, byte bCol){;
   keyMap[bRow][bCol].KeyPressReset();
  }
  
  void KeyDisable(byte bRow, byte bCol){;
   keyMap[bRow][bCol].KeyDisable();
  }
};

Mode Switch & LED Class (zx81modestate.h)

This ones still a work in progress, I'm wanting to add some more functionality to the Mode selection button at some point. Works well enough for the moment.


// *****************************
// ** zx81modestate.h         **
// ** Class Definitions       **
// ** For Mode Switch  & LEDs **
// *****************************

// 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(DEBOUNCE_DELAY_REPEAT);
   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);
 
}

Wednesday, 28 October 2015

A Little Case With a Name

It's taken longer than I'd have liked, yet the case design is nearing completion. A couple more days and some oddly grabbed hours should see the final design ready for 3d printing at Shapeways.

The Computer finally has a name of sorts, AZ15, it's a little bit referential, and a little bit obscure. In line with the original ZX81, the naming convention is drawn from the CPU name, a futuristic sounding second letter and the year of (very limited) release.
  • A: Standing for the CPU names at the heart of both the Raspberry Pi2 and the Leostick, the ARMv7 and ATmega32U4 respectively.
  • Z: The futuristiclly optimistic last letter of the alphabet, which is also referential to the Z in ZX81
  • 15: The year of very limited release.

Concept Art for the (amazing) AZ15

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..............