Lisp for the Arduino and MSP430

News!

Version 1.9 released

uLisp Version 1.9 adds a number of new features, including: support for characters and the character functions charchar-codecode-char, and characterp; additional string functions string<, string>, read-from-string, prin1-to-string, and princ-to-string; the additional I/O functions read-line, write-line, and write-string which support input/output via serial, SPI, and I2C where appropriate; and a sort function.

It also includes several bug fixes.

uLisp® is a version of the Lisp programming language specifically designed to run on processors with a limited amount of RAM, such as the ATmega-based Arduino boards and MSP430-based LaunchPad boards. Because it's an interpreter you can type commands in, and see the effect immediately, without having to compile and download your program. This makes it an ideal environment for learning to program, or for setting up simple electronic devices.

Lisp is a rich 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. Download the latest Arduino IDE from arduino.cc, or the latest Energia IDE from energia.nu.

Requirements

RAM: At least 2 Kbytes.

Program memory: At least 32 Kbytes (the program currently uses about 29 Kbytes of program memory on the Arduino and 31 Kbytes on the MSP430).

EEPROM, flash, or FRAM memory: Used for saving and loading the uLisp workspace.

Recommended platforms

Arduino Uno or other ATmega328-based cards. These will give you enough memory for a simple uLisp application using short symbol names; for examples see Mood lightSimon game, and I2C clock.

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.

MSP430F5529 LaunchPad. This uses the flash memory for saving images, and provides enough memory for a fairly complex application; for examples see AnimalsTweetmazeRoute finder, and Infinite precision arithmetic.

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

The following table gives a summary of the different versions:

Platform Long symbols Cells Image GC time Benchmark
ATmega328 No 317 254 1 msec 72 sec
ATmega2560 Yes 1280 893 2.6 msec 54 sec
ATmega1284 Yes 2880 893 5.3 msec 60 sec
MSP430F5529 Yes 1280 1280 8.2 msec 30 sec
MSP430FR5969 Yes 3328 1664 14.4 msec 60 sec

Cells gives the number of Lisp cells of storage available, equivalent to 4 bytes.

Image gives the number of cells that can be saved to non-volatile storage using save-image.

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 an unresolved problem interfacing uLisp via the serial interface with these boards.

Specification

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

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

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.

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.

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.

Example

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:

(dim)

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.