Lisp for microcontrollers

A Lisp interpreter for Arduino, Adafruit M4, Micro Bit, and MSP430 boards.


Version 2.4 supports Adafruit's M4 boards

The ARM version of uLisp now includes support for Adafruit's M4 ATSAMD51-based series of boards: the Adafruit Metro M4, Adafruit ItsyBitsy M4, and Adafruit Feather M4. With 120MHz processors these are now the fastest platforms for running uLisp. For more information see Adafruit M4 Boards.

uLisp® is a version of the Lisp programming language specifically designed to run on microcontrollers with a limited amount of RAM. It currently supports the Arduino ATmega-based boards, Arduino ARM SAM/SAMD-based boards, Adafruit ARM SAMD51-based boards, BBC Micro Bit, and MSP430-based LaunchPad boards. You can use exactly the same uLisp program, irrespective of the platform.

Because uLisp is an interpreter you can type commands in, and see the effect immediately, without having to compile and upload your program. This makes it an ideal environment for learning to program, or for setting up simple electronic devices.

Lisp is a flexible language, with many advanced features that make it easier to program in than C or C++, so it's ideal for learning about fundamental programming concepts. It's also an ideal language for expressing complex ideas, such as teaching a robot to solve mazes or finding the shortest route on a map. As well as supporting a core set of Lisp functions uLisp includes Arduino extensions, making it ideal as a control language for the Arduino.

You can download the current version of uLisp free from the Download uLisp page.

uLisp projects

LCD2.jpgLCD character display

I2CClock2.jpgI2C clock

MoodLight2.jpgMood light

GraphicsDisplay.jpgGraphics display interface

GSMServer.jpguLisp GSM server

ScrollingDisplay2.jpgScrolling text display


RAM: At least 2 Kbytes.

Program memory: At least 32 Kbytes.

EEPROM, flash, or FRAM memory: If available, used for saving and loading the uLisp workspace.

The versions of uLisp for 16-bit platforms support integers between -32768 and 32767.

AVR version

The AVR version of uLisp supports the following boards:

Arduino Uno or other ATmega328-based cards. These will give you enough memory for a simple uLisp application using short symbol names. All the examples marked Uno will run on the Arduino Uno.

Arduino Mega 2560 or other ATmega2560-based boards. These will give you enough memory for a fairly complex application; for examples see AnimalsTweetmazeRoute finder, and Infinite precision arithmetic.

ATmega1284. Although there isn't an official Arduino board based on it, the ATmega1284 is easy to wire up on a prototyping board, and provides a generous 16 Kbytes RAM.

MSP430 version

The MSP430 version of uLisp supports the following boards:

MSP430 F5529 LaunchPad. This uses the flash memory for saving images, and provides enough memory for a fairly complex application.

MSP430 FR5969 LaunchPad. This version uses the FRAM for the workspace, and for saving images, giving a generous amount of memory.

MSP430 FR5994 LaunchPad. This version uses the FRAM for the workspace, and for saving images, giving a generous amount of memory.

32-bit platforms

The versions of uLisp for 32-bit platforms support integers between 2147483647 and -2147483648, and 32-bit floating-point numbers.

ARM version

The ARM version of uLisp supports the following boards:

Arduino Due. This board is based on the AT91SAM3X8E ARM Cortex-M3 core and provides 512 Kbytes of flash, 96 Kbytes of RAM, and an 84 MHz clock. It's currently the fastest uLisp platform.

Arduino Zero. This board is based on the SAMD21 ARM Cortex-M0+ core and provides 256 Kbytes of flash and 32 Kbytes of RAM.

Arduino MKRZero. This is similar to the Arduino Zero, based on the SAMD21 ARM Cortex-M0+ core and with 256 Kbytes of flash and 32 Kbytes of RAM. It incorporates an SD-card socket, allowing you to use an SD card for saving and loading uLisp images.

Adafruit M4 Boards. The Adafruit Metro M4, Adafruit ItsyBitsy M4, and Adafruit Feather M4 are each based on the ATSAMD51 120MHz ARM Cortex M4 microcontroller. They have similar features and performance; the main difference is the form-factor of each board.

BBC Micro BitThis is based on a Nordic Semiconductor nRF51822 ARM Cortex-M0 microcontroller. It runs at 16 MHz and provides 256 Kbytes of flash program memory and 16 Kbytes of RAM. It doesn't support saving and loading uLisp images.


The following table gives a summary of the performance of the different versions:

Platform Processor Clock Long symbols Objects Image GC time Benchmark
Arduino Uno, Arduino Nano ATmega328 16 MHz No 317 256 0.5 ms 59 secs
Arduino Mega 2560 ATmega2560 16 MHz Yes 1216 1024 2.1 ms 49 secs
Tiny Lisp Computer ATmega1284 16 MHz Yes 2816 1024 4.8 ms 48 secs
Arduino Due ATSAM3X8E 84 MHz Yes 10240 * 4.5 ms 7 secs
Arduino Zero ATSAMD21 48 MHz Yes 3072 * 1.2 ms 10 secs
Arduino MKRZero ATSAMD21 48 MHz Yes 3072 * 1.2 ms 13 secs
Adafruit Metro M4 ATSAMD51 120 MHz Yes 20480 8192 3.4 ms 4.2 secs
Adafruit ItsyBitsy M4 ATSAMD51 120 MHz Yes 20480 8192 3.4 ms 4.1 secs
Adafruit Feather M4 ATSAMD51 120 MHz Yes 20480 8192 3.4 ms 4.2 secs
BBC Micro Bit nRF51822 16 MHz Yes 1024 * 1.7 ms 33 secs
MSP430 F5529 LaunchPad MSP430F5529 25 MHz Yes 1280 1280 1.3 ms 20 secs
MSP430 FR5969 LaunchPad MSP430FR5969  16 MHz Yes 3072 1536 10.2 ms 60 secs
MSP430 FR5994 LaunchPad MSP430FR5994 16 MHz Yes 3072 1536 6.7 ms 41 secs

Objects gives the number of Lisp objects of storage available, each equivalent to 4 bytes on the 16-bit platforms and 8 bytes on the 32-bit platforms.

Image gives the number of cells that can be saved to non-volatile storage using save-image.
On the Adafruit M4 boards you can save an image to the on-board DataFlash chip.
* On the Arduino Due, Zero, and MKRZero you can save images to an SD card. The BBC Micro Bit doesn't support saving images.

GC time gives the time taken for a garbage collection.

Benchmark gives the time taken to run the tak benchmark; see Benchmarks

I don't currently recommend the ATmega32U4-based boards as there is an unresolved problem interfacing uLisp via the serial interface with these boards.


The language is generally a subset of Common Lisp, and uLisp programs should also run under Common Lisp.

Types supported: list, symbol, integer, character, string, and stream.

An integer is a sequence of digits, optionally prefixed with "+" or "-". Integers can be between -32768 and 32767 (or between 2147483647 to -2147483648 on the 32-bit platforms). You can enter integers in hexadecimal, octal, or binary with the notations #x2A, #o52, or #b101010, all of which represent 42.

The 32-bit platforms also support 32-bit floating-point numbers, and provide a full set of floating-point functions.

On platforms with more than 2 Kbytes of RAM arbitrary user-defined symbol names are supported. Any sequence that isn't an integer can be used as a symbol; so, for example, 12a is a valid symbol. On platforms with only 2 Kbytes symbol names can have up to three characters consisting of a-z and 0-9.

There is one namespace for functions and variables; in other words, you cannot use the same name for a function and a variable. uLisp provides tail-call optimization, so applications written using recursive functions can be as efficient as using iteration.

Strings can consist of an arbitrary sequence of ASCII characters. Strings can be unlimited length, and are automatically garbage-collected.

uLisp includes a mark and sweep garbage collector. Garbage collection takes under 1 msec on an Arduino Uno or under 3 msec on an Arduino Mega 2560 (see above table).

uLisp also includes a simple program editor (see Using the program editor), a trace facility, and a pretty printer (see Debugging in uLisp).


The following example illustrates how you might use uLisp.

After uploading uLisp to your Arduino board or ATmega chip you communicate it via the Serial Monitor. For more information see Using uLisp.

Suppose you have a red LED connected to the analogue output pin 9 on an Arduino Uno. Then you can type in the Lisp command:

(analogwrite 9 128)

to set the LED to 128, which corresponds to half brightness.

To save having to write this command every time you want to set the red LED you can define a function called red:

(defun red (x) (analogwrite 9 x))

Now you can achieve the same effect simply by writing:

(red 128)

In each case the LED changes immediately, as soon as you type in the command.

Suppose you've got a potentiometer connected to vary the voltage on the analogue input A0. You could define a function dim to make the potentiometer adjust the brightness of the LED with:

(defun dim () (loop (red (/ (analogread 0) 4)))

and run it by typing:


Finally, you could save the uLisp image to EEPROM, and specify that dim should be run on load, by entering:

(save-image 'dim)

When you reset the Arduino dim will now load and run automatically.

This is a simple example showing how uLisp allows you to build up complex programs from simpler components, testing each of the components as you go along.