SD card interface

As of Version 2.0 uLisp includes an SD card interface to allow you to read from and write to SD cards. This will allow applications such as:

Logging data to an SD card.

Reading and processing data from an SD card.

Reading uLisp programs from an SD card and adding them to the current workspace.

For examples of these applications see Applications below.

The SD card interface allows you to have one file open for reading and one for writing, so you can process data from one file to another.

A compile option sdcardsupport at the start of the uLisp source, disabled by default, allows you to choose whether to include SD card support in uLisp. Including it slightly reduces the amount of workspace available for uLisp programs.

The SD card interface is not currently supported on the MSP430 boards.

Specifying the select pin

A #define in the uLisp source specifies which Arduino pin number is used for the select signal. For boards with a built-in SD card socket this will be set automatically. If you are using an external SD card interface you may need to edit the line:

#define SDCARD_SS_PIN 10

If you're using the Adafruit Data Logging shield [1] the correct value is 10.

Overview

The main function for working with SD cards is with-sd-card, which is similar to with-open-file in Common Lisp. In addition, all the input and output functions in uLisp now take an additional optional stream parameter, so you can use the functions read, print, princ, prin1, terpri, read-line, read-byte, write-line, write-string, and write-byte to read from or write to SD cards:

with-sd-card Arduino function

Syntax: (with-sd-card (stream filename [mode]) form*)

Evaluates the forms with stream bound to an sd-stream reading from or writing to the file filename.

The filename should be a string of up to 12 characters, consisting of a filename of up to 8 characters, a dot, and an extension of up to three characters.

The mode argument specifies whether the file is opened for reading or writing:

Mode Effect 0 or nil Read 1 Write-Append 2 Write-Overwrite

If the file already exists Append writes to the end of the file without affecting the existing contents, whereas Overwrite deletes any existing content first.

For example, the following command writes the string "Hello" to the SD card file "Greeting.txt":

(with-sd-card (str "Greeting.txt" 2) (print "Hello" str))

and the following command reads it back:

(with-sd-card (str "Greeting.txt") (read str))

Writing to an SD card

The functions print, prin1, princ, terpri, pprint, write-byte, write-string, and write-line can all be used to write to an SD card.

Reading from an SD card

The functions read, read-byte, and read-line can all be used to read from an SD card. If the end of the file has been reached, each of these functions returns nil.

Platform support

Arduino Uno

Unfortunately SD cards are not supported on the Arduino Uno because the standard Arduino SD card library makes uLisp too large to fit in the 32 Kbytes available on an Arduino Uno.

Arduino Mega 2560

The SD card support works nicely on an Arduino Mega 2560 with a suitable SD card shield, such as the Adafruit Datalogging Shield [2]:

ATmega1284

Not yet tested.

Arduino MKRZero

The Arduino MKRZero is an ideal platform for using SD cards with uLisp because it includes a built-in micro SD card reader:

Arduino Due

The SD card support works nicely on an Arduino Due with a suitable SD card shield, such as the Adafruit Datalogging Shield [3].

MSP430 platforms

Not yet supported.

Applications

Listing a text file

Here is a simple program to list the contents of a text file on the SD card:

(defun list (filename) (with-sd-card (str filename) (loop (let ((l (read-line str))) (unless l (return nothing)) (princ l) (terpri)))))

Data logging

The following program reads the analogue input A0 every 30 seconds and writes its value to the file data.txt. The value of a0 should be defined as the appropriate Arduino pin number: 54 on an Arduino Due, 15 on an Arduino Zero or MKRZero, or 0 on an Arduino Mega 2560:

(defun logger () (let ((a0 54) (time 0)) (with-sd-card (str "data.txt" 2) (dotimes (x 10) (for-millis (30000) (print (incf time)) (princ time str) (princ ":" str) (princ (analog-read a0 str)) (terpri str))))))

For example, you could use this program in conjunction with an TMP35/TMP36/TMP37 temperature sensor to log temperatures.

Working with two files at once

The SD card interface allows you to have one file open for reading and one for writing. For example, the following program creates a file numbers.txt containing the numbers 0 to 99, one per line. It then reads them in, one at a time, squares them, and writes the squares to a new file results.txt:

(defun test2 () ; Write some strings (print "Write numbers") (with-sd-card (s "numbers.txt" 2) (dotimes (x 100) (print x s))) ; Read them back in, square them, and write them out again (print "Process numbers") (with-sd-card (in "numbers.txt") (with-sd-card (out "results.txt" 2) (loop (let ((l (read in))) (when (null l) (return)) (print (* l l) out))))))

Loading functions from an SD card

The following function load will load a uLisp function definition from a text file, and evaluate it, adding it to the uLisp workspace:

(defun load (filename) (with-sd-card (s filename) (eval (read s))))

To demonstrate this, the following program writes the definition of a function sq to a file lisp.txt, then reads it back in, evaluates it, and uses it to find the square of 123:

(defun test3 () (print "Write program") (with-sd-card (s "lisp.txt" 2) (write-string "(defun sq (x) (* x x))" s)) (print "Load program") (with-sd-card (s "lisp.txt") (eval (read s))) (print (sq 123)))