The ZX81 was one of the iconic computers of the 80s. They made a computer with minimal hardware which still could do a lot of things. They came assembled or as do it yourself kit at a price of &69.95. Other home computers cost 5 times as much.
To safe money a 40 key mechanical foil keyboard was used. It is hard to type quickly on them but it was good enough to enter short programs. As the computer really had only 1k of free memory, programs and text couldn't be that long anyway. The machine was very popular and 1.5 millions of them were sold.
The ZX81 keyboards are still available today. Typical price is 20 Euros. I got mine from Ebay. Alternatively this seems to be the link to the source: https://www.sintech-shop.de/zx81-tastaturfolie/a-7626/.
If you want things with Arduinos that require keyboard input the ZX81 is a good option. It is alphanumeric, requires minimal electronic to scan and works on all kind of microcontrollers. They are easier to integrate than PS2 keyboards. They need no power when inactive and can be connected and disconnected when running.
This is a tutorial how to use them on an Arduino using a library I wrote as part of a bigger project.
How does the keyboard work?The ZX81 has 40 keys that arranged in 5 columns of 8 rows. The wires on the lefthand side are connected to the columns while the wires on the righthand side connect to the rows. Scanning the keyboard is done by reading from the 5 column pins with INPUT_PULLUP while setting the row pins to low one after the other. The result is a raw key code in the range from 0 to 39. The keyboard has a shift key which can be used to select upper case letters. Pressing Shift and Newline simultaneously was an Alt condition in the Sinclair. The symbols marked in red where activated.
For this work I used to key maps created by Tony Smith and published on his repo https://gist.github.com/smittytone/9604410.
To connect the keyboard directly to the UNO or MEGA, 13 pins are needed. The library has proposed pins for both controllers.
Preparing the keyboard (optional)To connect the keyboard to any kind of device I attached connectors to the column and row lines.
You need crimping pliers and the connector kit for this step.
Alternatively the wires can be plugged directly to a breadboard.
Connect the keyboard to the ArduinoNext connect the keyboard to the Arduino.
On an UNO I use the following pinout:
Columns: 2, 3, 4, 5, 6
Rows: 7, 8, 9, 10, 11, 12, A0=14, A1=15
On a mega the pins on the side are a good choice. I propose to use
Columns: 47, 45, 43, 41, 39
Rows: 37, 35, 33, 31, 29, 27, 25, 23
To read the keyboard the ZX81Keyboard library is needed. Download it from https://github.com/slviajero/ZX81Keyboard and unpack the ZIP file in your libraries folder Arduino/libraries (or use the ZIP file menu in the IDE).
If this is getting to technical, skip this section and proceed to the examples.
The library has the following public member functions
void begin(byte* pins);
bool available();
int read();
int peek();
void getkey();
This is analogous to the PS2Keyboard class.
In addition to this, four variables can be accessed
char lastKey;
uint8_t alt;
uint8_t state_shift;
uint8_t state_key;
To configure the keyboard, a ZX81Keyboard object must be created and the pins needed to be supplied in the begin() method.
peek() checks is a key is currently pressed. If the key is pressed, the ASCII value of the key is returned and stored in lastKey. Every call of peek() checks again and updates lastKey. If no key is pressed, 0 is returned.
read() checks is a key is pressed by consulting lastKey and/or calling peek(). Read then waits until the key released. Holding a key pressed blocks read().
available() is true is a key is stored in lastKey or peek() finds a pressed key. It is false if no key is pressed.
getkey() reads the keyboard but does not decode the key. state_key contains the key index between 0 and 39 if a key has been pressed. If contains 255 if no key has been pressed. state_shift is 0 if shift has not been pressed with a key and 1 if shift has been pressed.
The variable alt is set by peek(). It is 1 if shift and newline were both pressed else alt is 0. The variable is used to determine the correct ASCII value of the next key.
Example 1: Read the keyboard and show all informationOpen the example program simple_read.ino.
#include <ZX81Keyboard.h>
ZX81Keyboard keyboard;
void setup() {
/* start Serial */
Serial.begin(9600);
/* first the 8 rows, then the 5 columns or the keyboard */
/* a possible MEGA pinout */
const byte megapins[] = {37, 35, 33, 31, 29, 27, 25, 23, 47, 45, 43, 41, 39};
/* the UNO pinout */
const byte unopins[] = {7, 8, 9, 10, 11, 12, A0, A1, 2, 3, 4, 5, 6 };
keyboard.begin(megapins);
}
void loop() {
keyboard.getkey();
/* scan the keyboard */
if (keyboard.state_key != 255) {
Serial.print("Keycode found: ");
Serial.println(keyboard.state_key);
Serial.print("Shift state: ");
Serial.println(keyboard.state_shift);
Serial.print("ASCII value: ");
Serial.println(keyboard.peek());
Serial.print("Alt state: ");
Serial.println(keyboard.alt);
}
/* now read it */
char ch=keyboard.read();
if (ch != 0) {
Serial.print("Key: ");
if (ch > 31) Serial.write(ch);
Serial.println();
}
delay(100);
}
In setup() the pins are set in keyboard.begin(). For the proposed pin layouts of the UNO and MEGA there are variables unopins and megapins. Select your layout or define your own pin list and feed it to keyboard.begin().
After this, load the program to the Arduino.
Pressing a key on the keyboard should output the keycode, and all the other information. Releasing it should output "Key: " and the respective symbol.
Example 2: Read the keyboard and copy date to the Serial portOpen the example program serial.ino.
#include <ZX81Keyboard.h>
ZX81Keyboard keyboard;
void setup() {
/* start Serial */
Serial.begin(9600);
/* first the 8 rows, then the 5 columns or the keyboard */
/* a possible MEGA pinout */
const byte megapins[] = {37, 35, 33, 31, 29, 27, 25, 23, 47, 45, 43, 41, 39};
/* the UNO pinout */
const byte unopins[] = {7, 8, 9, 10, 11, 12, A0, A1, 2, 3, 4, 5, 6 };
keyboard.begin(megapins);
}
void loop() {
/* read and echo Serial*/
while(!keyboard.available()) delay(0);
if (char ch=keyboard.read()) Serial.write(ch);
/* this yields, needed on some platforms */
delay(0);
}
Set the correct pins and upload the program. It copies the received keys to the serial output. The Arduino becomes a serial keyboard. I use this method to add a keyboard to projects were the main microcontroller has a lot of work to do and no free pins. Typical layouts look like this:
The ZX81Keyboard library is part of my standalone computer project. It integrates with the Arduino IoT BASIC interpreter https://github.com/slviajero/tinybasic. A small computer based on an Arduino MEGA 256 and a 4 inch TFT would look like this
More on this project will be explained in another tutorial. Please read the Wiki https://github.com/slviajero/tinybasic/wiki for more information on these projects.
Technical notesThe keyboard library looks like the PS2Keyboard library from the point of view of the member functions, but it has a main difference. It uses no interrupts or timers. The keyboard must be polled continuously by the program. Only when peek() is called a scan is performed.
While this may look like a weakness, it really has some advantages. The program is portable, no energy is used when the keyboard is not polled. Timers and interrupts are available for other purposes and the circuitry is super simple.
The way the keyboard is connected and scanned resembles the original ZX81 circuits.
The keyboard is on the lower right corner. The 5 columns are pulled up to 5 V. They are also connected to SCL chip on the top left which does the scanning. The rows are set to LOW through the address bus and the columns are checked if they change from HIGH to LOW. The private member function scan() of the library does the same. After each scan, all pins are set back to INPUT (rows) or INPUT_PULLUP (columns). This way the keyboard is completely inactive and switched off.
When the keyboard is not read, the 8 row pins can be used for any other output or input as long as the column pins stay on INPUT.
The 5 column pins can be used for any other output as long as the row pins stay on INPUT.
In the pin layout above, pin D13 is avoided on purpose. Led pins can cause misreadings as the led circuitry can cause a false low reading. This effect depends on the resistance of the internal pullup and the led and is board dependent.
The library always reinitialises the pins before a scan. This makes pin reuse possible.
Mechanical keys need to be debounced. They produce a stable signal after some time but oscillate between HIGH and LOW before that. The library handles debouncing of the key state with a standard algorithm. Debounce delay is 10ms by default. Debouncing is interrupted after 100ms and a state "no key pressed" is return.
This project was featured on Hackaday https://hackaday.com/2022/10/24/one-of-the-worst-keyboards-ever-now-an-arduino-peripheral/. Thank you for it. The question was raised if the library runs on Mbed. Of course it runs on Mbed.
Comments