I had the idea to build a standalone computer resembling the old ZX81 computer when I first save that the ZX81 keyboards are still for sale at a reasonable price.
Some call them the worst keyboard ever. The old ZX81 home computer was a super low cost machine. I never had one back then but friends of mine did. I admire the machine from the engineering point of few. They build a fully functioning computer for a low price by making the most of the components.
I thought it would be worth to give it a try myself and start out with really low cost components. One is the keyboard.
The other low cost components I played around with are the ILI9486 displays for Arduinos.
They have a parallel port and use a lot of pins. On an UNO they cover the entire surface and there are no more pins left. On a MEGA they are also tricky to use. More about this later.
The ILI9486 cost around 15 Euros and are very common. The ones that come on the market in Ebay sometimes have pixel errors.
As the display and the keyboard need a lot of IO pins, an Arduino MEGA is the ideal basis for this system.
Why BASIC?The computer I am going to present here will be using BASIC. Why an outdated language from the 80s?
BASIC is an interactive language. Many of the simple physical computing applications we create on Arduino can be build with it faster than in Arduino's native C++. One can play with circuits, test interactively and finally run he program when everything is tested. The need for compile and upload is gone.
As the language is small, it runs on an UNO just as well as on bigger RP2040 based systems. The core layer of the BASIC interpreter masks the hardware and library complexity and differences of the different Arduino platforms.
And then, retro computing is fun.
Structure of the tutorialMost of the tutorial will be on integrating the components independent of the BASIC language. I hope it will also be useful for many readers.
The tutorial uses a step by step approach coming from the simple and easy to do to the more complex.
We will first upload the BASIC interpreter, connect the display and showcase the display capabilities.
An SD card for file storage will be connected next. This requires some tweaking of the libraries.
Connecting the keyboard will require some mechanical and electrical work. This is explained in the next section.
With the keyboard connected, the little computer will be standalone. It can run and be useful without help from a bigger machine. It has 6 kB of useable BASIC memory. This is far more than the original ZX81 with it 1kB. It also has 16bit graphics with a resolution of 480x320. Home computer user could only dream of it back then.
These first steps are easy to do and don't require complex electronics or software work.
The last step is the memory extension. This will use 64kB serial SRAM chips and a prototype board. This is the hardest part of the tutorial as it requires soldering and some electronics skill.
Getting started - display and ArduinoThe ILI9486 display have a parallel interface and use most of the standard Arduino UNO pins. They cover an UNO completely. Most of them are equipped with a SD card slot. This slot is connected to the pins 13-10 i.e. the standard SPI bus of the UNO. These pins can be reused if no SD card is plugged in.
For these parallel displays there is the MCUFRIEND_kbv library. It can be downloaded either from the repo https://github.com/prenticedavid/MCUFRIEND_kbv or with the Arduino library manager searching for MCUFRIEND. I use version 3.0.0 for this tutorial. Install the library and also install the Adafruit_GFX library.
Connect the display with the Arduino MEGA256 and look for the example program graphictest_kbv. You have to go to Files -> Examples -> MCUFRIEND_kbv in the Arduino IDE for all the examples. Compile and upload this example. You should see a graphics test running on the display now.
If this works you are ready for the next step.
Download and install the BASIC interpreterDownload the code from https://github.com/slviajero/tinybasic/tree/main/IoTBasic and copy it into a folder IotBasic in your Arduino folder.
Open the file IoTBasic.ino now and search for the code section with the language definition.
#define BASICFULL
#undef BASICINTEGER
#undef BASICSIMPLE
#undef BASICMINIMAL
#undef BASICTINYWITHFLOAT
Make sure that only BASICFULL is set on #define and all other settings are undefine. Do not change any other definitions in the code right now.
With this setting the full floating point BASIC interpreter with all the features is compiled.
Next, open the file hardware-arduino.h.
Search for the hardware definition section in the beginning.
#undef USESPICOSERIAL
#undef ARDUINOPS2
#undef ARDUINOUSBKBD
#undef ARDUINOZX81KBD
#undef ARDUINOPRT
#define DISPLAYCANSCROLL
#undef ARDUINOLCDI2C
#undef ARDUINONOKIA51
#undef ARDUINOILI9488
#undef ARDUINOSSD1306
#define ARDUINOMCUFRIEND
#undef ARDUINOGRAPHDUMMY
#undef LCDSHIELD
#undef ARDUINOTFT
#undef ARDUINOVGA
#define ARDUINOEEPROM
#undef ARDUINOI2CEEPROM
#undef ARDUINOEFS
#undef ARDUINOSD
#undef ESPSPIFFS
#undef RP2040LITTLEFS
#undef ARDUINORTC
#undef ARDUINOWIRE
#undef ARDUINOWIRESLAVE
#undef ARDUINORF24
#undef ARDUINOETH
#undef ARDUINOMQTT
#undef ARDUINOSENSORS
#undef ARDUINOSPIRAM
#undef STANDALONE
This rather lengthy section of definitions configures all the device drivers that BASIC has. For now, we only need DISPLAYCANSCROLL, ARDUINOMCUFRIEND, and ARDUINOEEPROM as defined. All other drivers should be #undef.
Compile and upload the sketch to you Arduino. It will need approximately 58 kB of flash memory in this configuration.
Open the serial monitor now. You should see a startup line and a command prompt
This means that you are running version 1.4a of the interpreter, have 6187 bytes of BASIC RAM and 4096 bytes of EEPROM for program storage.
Type CIRCLE 100, 100, 50 on the command prompt and watch the display. You should see a circle on the display now.
The next test would be to type PRINT &2, "Hello World" in the serial monitor. The text should show on the display as well.
If you want to do a little more, type in the program
10 COLOR RND(16)
20 FCIRCLE RND(480),RND(320),RND(50)
30 GOTO 10
The type RUN. You can interrupt the test program by typing '#' in the serial monitor.
The next step would be to connect the keyboard to the Arduino.
I wrote a full tutorial on how to use a ZX81 keyboard: https://create.arduino.cc/projecthub/sl001/read-a-zx81-keyboard-with-arduinos-and-build-things-with-it-0189bd
For this projects you only need to connect the keyboard with the correct pinout.
The five pins of the left connector go to the pins 47-39 and the eight pins of the right connector go the pins 37-23. We only use the odd pins of the Arduino. There is a reason for this which will be explained later.
Using breadboard wires is a good idea for a first test but I recommend something better. My setup looks like this:
I use a flexible cable to connect the keyboard to the Arduino.
Once this is done, you can again compile and upload the BASIC interpreter with the following settings in hardware-arduino.h.
#undef USESPICOSERIAL
#undef ARDUINOPS2
#undef ARDUINOUSBKBD
#define ARDUINOZX81KBD
#undef ARDUINOPRT
#define DISPLAYCANSCROLL
#undef ARDUINOLCDI2C
#undef ARDUINONOKIA51
#undef ARDUINOILI9488
#undef ARDUINOSSD1306
#define ARDUINOMCUFRIEND
#undef ARDUINOGRAPHDUMMY
#undef LCDSHIELD
#undef ARDUINOTFT
#undef ARDUINOVGA
#define ARDUINOEEPROM
#undef ARDUINOI2CEEPROM
#undef ARDUINOEFS
#undef ARDUINOSD
#undef ESPSPIFFS
#undef RP2040LITTLEFS
#undef ARDUINORTC
#undef ARDUINOWIRE
#undef ARDUINOWIRESLAVE
#undef ARDUINORF24
#undef ARDUINOETH
#undef ARDUINOMQTT
#undef ARDUINOSENSORS
#undef ARDUINOSPIRAM
#define STANDALONE
The settings ARDUINOZX81KBD and STANDALONE have been changed from #undef to #define.
After upload the command prompt should appear on the display. Try to type a few characters and look if they show on the keyboard.
Congratulations! You have just build a standalone computer from a keyboard, an Arduino MEGA and a simple TFT.
Use the SD cardThe TFT has a SD slot for the small SD cards. Using this on an Arduino MEGA256 is not really straightforward.
The reason for this is the pinout. The SD pins of the TFT shield are 13-10 because these are the SPI pins of an UNO. On a MEGA this pins have no SPI function.
The programmers of the SD library have build in a setting deep down in the library to correct this. Software SPI can be switched on in it by setting a definition. Unfortunately now C++ constructor exists to use this settings. You really need to dive down into the code and edit the library. This is awkward.
I created a library https://github.com/slviajero/SoftSD that has the right settings and can be installed in parallel to the Arduino standard SD library. It is identical to SD except that it used pins 13-10 for SD card access with software SDI. It is independent of the BASIC interpreter and can be used standalone. If you want to use it without the BASIC interpreter, the code would be
#include <SoftSD.h>
SD.begin();
All other commands and methods for SD work like with the standard library.
For the use in BASIC, search the line
#undef SOFTWARE_SPI_FOR_SD
in hardware-arduino.h and change it to
#define SOFTWARE_SPI_FOR_SD
After that you can activate ARDUINOSD,
#undef USESPICOSERIAL
#undef ARDUINOPS2
#undef ARDUINOUSBKBD
#define ARDUINOZX81KBD
#undef ARDUINOPRT
#define DISPLAYCANSCROLL
#undef ARDUINOLCDI2C
#undef ARDUINONOKIA51
#undef ARDUINOILI9488
#undef ARDUINOSSD1306
#define ARDUINOMCUFRIEND
#undef ARDUINOGRAPHDUMMY
#undef LCDSHIELD
#undef ARDUINOTFT
#undef ARDUINOVGA
#define ARDUINOEEPROM
#undef ARDUINOI2CEEPROM
#undef ARDUINOEFS
#define ARDUINOSD
#undef ESPSPIFFS
#undef RP2040LITTLEFS
#undef ARDUINORTC
#undef ARDUINOWIRE
#undef ARDUINOWIRESLAVE
#undef ARDUINORF24
#undef ARDUINOETH
#undef ARDUINOMQTT
#undef ARDUINOSENSORS
#undef ARDUINOSPIRAM
#define STANDALONE
recompile the BASIC interpreter and upload it to the Arduino.
Insert a micro SD card that has been formatted with a FAT filesystem.
and restart the Arduino with the reset button.
A set of file system commands are now available. CATALOG displays the files on the card. LOAD and SAVE load files from the card.
Testing graphics and the SD cardDownload the program https://github.com/slviajero/tinybasic/examples/00tutorial/mandelv.bas from my repo and put it on the SD card with the name mandelv.bas
.
Enter the card into the Arduino again, reset it and then type
catalog
The program should appear in the list printed now. Type
load "mandelv.bas"
run
The program now displays the Mandelbrot set on the display. This will take some time. 8bit computers cannot do this much faster.
Extending the memorySo far we have build a computer with approximately 6kB of memory. All the steps where fairly easy to do without much electronics work. Building the memory card is a bit harder as we have to solder a RAM chip and connectors to the prototype board. If you feel that his is getting too complicated you can skip this step and still have a nice BASIC home computer.
6kB is more than many of the first generation home computers ever had. The later generation of home computers, however, has up to 64 kB memory. This next step is about adding a serial memory chip to the computer.
The core component is a SPI SRAM chips. They are not easy to get. I buy mine from this store https://www.mouser.de/ProductDetail/Microchip-Technology-Atmel/23LC512-I-P. The manufacturer is ATMEL who also make the AVR chips of the Arduino. The chip has 64kB of memory organised in 8bit words. This is exactly what we need for a BASIC home computer.
If you are interested in how to use these chips in general, please look at the tutorial https://www.instructables.com/Interface-an-SRAM-bus-to-your-arduino/. It also explains the wiring we are going to use.
In addition to the chip we need a 8 pin socket, a 0.1µF capacitor and a 10kOhm resistor.
We place the chip at the lower right corner of the prototype board in a socket as close as possible to the SPI bus pins of the Arduino. These are the pins 53-50.
The pinout of the RAM chip looks like this:
Pin 1 is chip select (CS). This pin has to be pulled up to 5V with the 10kOhm resistor. Pin 4 is ground (Vss) and pin 8 is 5V (Vcc). These two pins are connected to the power pins of the prototype board. The capacitor is connected between them and placed closely to one of the power pins. It takes care of small voltage fluctuation and increases the stability of the circuitry.
After these preparations make the following connections:
Pin 1 of the chip (CS) goes to pin 53 of the Arduino (SS).
Pin 2 of the chip (SO) goes to pin 50 of the Arduino (MISO).
Pin 5 of the chip (SI) goes to pin 51 of the Arduino (MOSI).
Pin 6 of the chip (SCK) goes to pin 52 of the Arduino (SCK).
I use short wires and solder the connections directly on the prototype board. Please place the wires in a way that you don't cover the other IO pin connectors of the prototype board.
To connect the keyboard, we need to solder a socket to the pin connectors 23-47 of the prototype board. I place if at the back of the prototype board. This ways the keyboard wires come neatly from the back and don't cover the display or get in you way when you use the computer.
While I am at it I place a second connector at the front of the prototype board for the even pins 22-46.
These pins will be the available GPIO pins of the standalone computer.
With the keyboard connected the whole setup does now look like this:
To activate the RAM you need again recompile the BASIC interpreter, now with the settings
#undef USESPICOSERIAL
#undef ARDUINOPS2
#undef ARDUINOUSBKBD
#define ARDUINOZX81KBD
#undef ARDUINOPRT
#define DISPLAYCANSCROLL
#undef ARDUINOLCDI2C
#undef ARDUINONOKIA51
#undef ARDUINOILI9488
#undef ARDUINOSSD1306
#define ARDUINOMCUFRIEND
#undef ARDUINOGRAPHDUMMY
#undef LCDSHIELD
#undef ARDUINOTFT
#undef ARDUINOVGA
#define ARDUINOEEPROM
#undef ARDUINOI2CEEPROM
#undef ARDUINOEFS
#undef ARDUINOSD
#undef ESPSPIFFS
#undef RP2040LITTLEFS
#undef ARDUINORTC
#undef ARDUINOWIRE
#undef ARDUINOWIRESLAVE
#undef ARDUINORF24
#undef ARDUINOETH
#undef ARDUINOMQTT
#undef ARDUINOSENSORS
#define ARDUINOSPIRAM
#define STANDALONE
ARDUINOSPIRAM is now also enabled. Upload the BASIC interpreter to the Arduino and restart. 64kB of free memory will be reported at the startup message.
To test the RAM, type in the following commands after the command prompt appears:
A0=10
PRINT A0
The output should be 10. The variable A0 is placed on the variable heap memory. If the RAM connection works it will be recalled properly. Otherwise it will be output as NaN or 0.
Mounting the computer in a frameMy mechanics skills are limited, so I didn't build a good looking case for the computer. As it mainly lives in the lab, this is not really needed. A simple plywood stand is good enough.
The MEGA is mounted to the upper plywood board at an angle of 60 degrees. Then I place the keyboard on the lower board and fix it with two sided adhesive tape. The tape is elastic and helps to make the keyboard a little softer and easier to use.
Next comes the prototype board, wires come in from the back.
Now you are ready to go.
What's next?I wrote a number of tutorials on how to use the Arduino IoT BASIC interpreter and will not repeat this here. Please look at the following tutorials instead
https://create.arduino.cc/projecthub/sl001/basic-on-arduinos-5b4e24
Most of the Arduino standard examples are ported to basic and published in the examples section of the repo
https://github.com/slviajero/tinybasic/tree/main/examples
Best start with 00tutorial there.
There is a full manual https://github.com/slviajero/tinybasic/blob/main/MANUAL.md and a Wiki https://github.com/slviajero/tinybasic/wiki for further information.
A word about display hardware and SD cardsThe parallel ILI9486 devices in this tutorial are real low cost hardware. Devices on the hobbyist market are usually left over from production of larger series for applicances. Many are not quality tested or failed quality tests for professional use. When I buy from Chinese sources I usually order 3 and receive 2 useful devices. One is always damaged or faulty. Don't get frustrated when your display doesn't work. Try the graphics test and if this doesn't work, buy a new one.
The 3.5 inch 480x320 display is very common. There is a smaller version of it with 320x240 screen resolution and the same pinout. The MCUFRIEND library BASIC is based on supports the devices and detects them automatically. There is, however, one change needed in the code. Search for the display definition section in hardware-arduino.h. The code segement looks like this
#ifdef ARDUINOMCUFRIEND
#define DISPLAYDRIVER
#ifndef LCD_CS
#define LCD_CS A3
#endif
#ifndef LCD_CD
#define LCD_CD A2
#endif
#ifndef LCD_WR
#define LCD_WR A1
#endif
#ifndef LCD_RD
#define LCD_RD A0
#endif
#ifndef LCD_RESET
#define LCD_RESET A4
#endif
MCUFRIEND_kbv tft;
/* ILI in landscape */
const int dsp_rows=20;
const int dsp_columns=30;
char dspfontsize = 16;
uint16_t dspfgcolor = 0xFFFF;
uint16_t dspbgcolor = 0x0000;
void dspbegin() {
uint16_t ID = tft.readID();
if (ID == 0xD3D3) ID = 0x9481; /* write-only shield - taken from the MCDFRIEND demo */
tft.begin(ID);
tft.setRotation(1); /* ILI in landscape, 3: SD slot on right the side */
tft.setTextColor(dspfgcolor);
tft.setTextSize(2);
tft.fillScreen(dspbgcolor);
dspsetscrollmode(1, 4);
}
The number of rows and columns are defined here as constants. If you use the smaller display with a 16 bit font you need to reduce these numbers to 15 rows and 20 columns. Default foreground and background colours as well as the screen orientation can also changed in this code section. Changing the font size is also possible but requires more changes in the code.
SD cards are a second bit of hardware that can be tricky. If your SD cards doesn't work, reformat it on a Windows system. If this doesn't work, buy another card. Many SD cards are not well behaved on an SPI bus.
Comments