Cardputer uLisp Machine
16th November 2024
This article describes how to create a handheld Lisp computer from an M5Stack Cardputer Kit:
The Cardputer uLisp Machine, a handheld Lisp computer with a self-contained keyboard and display.
The M5Stack Cardputer is a card-sized portable computer based on the ESP32-S3, available for about £30/$30. It has a 240x135 colour TFT display that gives a scrolling display of up to 16 lines of 40 characters, an integrated 56-key keyboard, and an SD card socket. It's available direct from M5Stack [1], or from The Pi-Hut in the UK [2].
Introduction
The M5Stack Cardputer is based on an ESP32-S3 dual-core LX7 microprocessor, with 2.4 GHz Wi-Fi, Bluetooth 5 (LE), and 8M bytes of flash.
The display is a 1.14 inch ST7789V2 SPI Interface IPS LCD with a resolution of 240 x 135. The display uses the Arduino GFX library, and the firmware provides software scrolling.
The keyboard has 56 keys, providing a full ASCII character set. However, it's very tiny; small fingers are recommended.
The Cardputer is contained in a fairly rugged case, and includes a 120 mAh rechargeable battery on the main board and a 1400 mAh LiPo cell in the backpack. These can be charged from the USB port.
Note: Although the StampS3 module is a separate module in the Cardputer I recommend you don't attempt to remove it, as it is attached to the display internally, and you can easily damage the connector.
Factory firmware
The M5Stack Cardputer is supplied with several built-in applications. Installing uLisp will delete these applications, but you can reinstall them from the M5 Cardputer User Demo repository on GitHub: M5Cardputer-UserDemo.
Installing the Cardputer uLisp firmware
Install the M5Stack core
- Add the following M5Stack URL to the Additional Boards Manager URLs list in the Arduino IDE Preferences dialog box:
https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json
- In the Arduino IDE search for the M5Stack core in Boards Manager and install it. I used version 2.1.2.
- In the Arduino IDE search for the M5Cardputer library in Library Manager and install it. I used version 1.0.2.
It will prompt to install some dependencies. Click Install all to agree.
- Select M5Stack Arduino from the Board menu, and M5Cardputer from the submenu.
- Check that the subsequent options are set as follows:
USB CDC On Boot: "Enabled"
Flash Size: "8MB (64Mb)"
Partition Scheme: "8MB with spiffs (3MB APP/1.5MB SPIFFS)"
You can leave the other options at their defaults.
Install uLisp
- Download the Cardputer uLisp firmware from https://github.com/technoblogy/ulisp-cardputer.
- Connect the computer's USB port to the Cardputer and select the port from the Tools menu Port submenu.
- Choose Upload from the Sketch menu to upload uLisp.
You should then see the uLisp prompt on the Cardputer display, and be able to enter Lisp programs at the keyboard. You can also enter Lisp programs from the Arduino IDE Serial Monitor.
Larger font
A larger font option gives 9 lines of 30 characters per line:
The uLisp Cardputer with the larger font option selected.
To choose the larger font option uncomment the option:
#define largerfont
before uploading uLisp.
Cardputer uLisp Machine – Specification
Here's the full specification:
Size: 84 x 54 x 19.7 mm (2.7" x 3.9"); Weight: 93 g.
Display: 40 characters x 16 lines (or 30 characters x 9 lines with a larger font option).
Keyboard: Integrated 56-key keyboard providing upper and lower-case characters, digits, and symbols required by uLisp.
Memory available: 23750 Lisp objects.
Flash: 1.5M bytes of flash are reserved for use to save the Lisp workspace using save-image if the SD card interface is not used.
Processor: ESP32-S3
Clock speed: 240 MHz.
Language
uLisp, a subset of Common Lisp, with approximately 200 Lisp functions and special forms.
It also provides keywords, such as :input and :output, as a convenient way of entering Arduino constants.
For a full definition see uLisp Language Reference.
Types supported: list, symbol, integer, floating-point, character, string, stream, and array.
An integer is a sequence of digits, optionally prefixed with "+" or "-". Integers can be between -32768 and 32767. You can enter numbers in hexadecimal, octal, or binary with the notations #x2A, #o52, or #b101010, all of which represent 42.
The floating-point numbers have full 32-bit precision.
User-defined symbol names can have arbitrary names. Any sequence that isn't an integer can be used as a symbol; so, for example, 12a is a valid symbol.
There is one namespace for functions and variables; in other words, you cannot use the same name for a function and a variable.
uLisp includes a mark and sweep garbage collector. Garbage collection takes about 1 ms.
Entering programs
You can enter commands and programs by typing them at the keyboard, and pressing ↩. A keyboard buffer is provided that buffers a full screen of text, and you can use the ⌫ key to delete characters and correct typing mistakes. The editor includes parenthesis matching which automatically highlights matching brackets in green as you type in a program. This makes it much easier to enter a program correctly, and is especially helpful in telling you how many closing brackets to type at the end of defining a function.
Keyboard
To get upper-case letters, or the blue symbols above the digits and some other keys, hold down the Aa shift key while typing a key.
Escape
To escape from a running program press the G0 button to the right of the On/Off switch on the back panel.
Copying the last line
Press the tab key to echo the previous line typed after the uLisp prompt, allowing you to edit it.
Connecting to a computer
You can also connect the Cardputer to a computer via a USB cable. You can then use the Serial Monitor in the Arduino IDE to enter and edit programs as described in Using uLisp.
Sound
You can play notes through the internal speaker using the uLisp note function. For example, the following function plays a scale of C (the pin number is ignored):
(defun scale () (mapc (lambda (n) (note 0 n 4) (delay 500)) '(0 2 4 5 7 9 11 12)) (note))
The beep character beeps the speaker:
(write-byte 7)
SD Card
The Cardputer has an SD card socket that can be used to read from and write to SD cards from uLisp, and to save an image of the Lisp workspace. To return a list of the files on an SD card give the command directory:
> (directory) ("T1.bmp" "A1" "F2" "T2" "PIC.BMP" "ULISP.IMG" "Greeting.txt" "PIC2.BMP" "X1")
For details of using SD cards see SD card interface.
If you don't want to use the SD card, upload the firmware with the following line commented out with:
// #define sdcardsupport
The (save-image) command will then use the flash memory to save the workspace.
Graphics
You can draw graphics with the full range of graphics extensions; for example:
(defun damped () (cls) (dotimes (x 240) (let ((y (round (+ 80 (/ (* 40 (sin (/ x 3.1416))) (exp (/ x 36))))))) (draw-pixel x y 2016))) (loop))
which produces the graph of damped harmonic oscillation shown in the photograph at the start of this page. See Graphics extensions for details.
Disabling output to the Cardputer display
The terminal control codes 14 (Shift Out) and 15 (Shift In) allow you to turn off or on output to the Cardputer display. This is useful if you are displaying graphics on the display, and don't want it overwritten by the text output.
So to turn off output to the display:
(write-byte 14)
To turn it back on:
(write-byte 15)
Limitations
As discussed on the page ESP32-S2, ESP-S3, and ESP-C3 boards, there is an outstanding issue with the ESP32-S3 built-in USB support that makes it hang up if you try to enter a program of more than a few lines from the Serial Monitor on a Mac computer.
Resources
The Cardputer uLisp Machine firmware on GitHub: https://github.com/technoblogy/ulisp-cardputer
The M5Stack Cardputer documentation: Cardputer.
Acknowledgements
This firmware builds on the work done by @hasn0life in initially porting uLisp to the Cardputer.
- ^ M5Stack Cardputer Kit on M5Stack.
- ^ M5Stack Cardputer Kit on The Pi Hut.