Run lisp on Arduino using OpenBSD

I have been fascinated by the ulisp project for some time.

It seems attractive to be able to run a high level language on target using a REPL on a small embedded device.

So I thought I would try it out on an old Arduino board using OpenBSD as host.

These notes assumes OpenBSD 6.4 on the host.

Install arduino on OpenBSD

It turned out the running arduino on OpenBSD is really straight forward.

The process is explained in quite detail in Arduino Development using the OpenBSD CLI.

To install the environment just install package arduino.

# pkg_add arduino

See an intro to the package in:

less /usr/local/share/doc/pkg-readmes/arduino
less /usr/local/share/doc/pkg-readmes/avrdude

Deploy ulisp

Next I created a sample project

arduinoproject ulisp-uno
cd ulisp-uno

I first tried to build and deploy the helloworld.ino source from the link above copied into ulisp-uno.ino.

make

After connecting the board with a USB printer cable I could deploy the hex file.

doas make upload

doas (sudo alternative) is needed for USB access. This makes the arduino blink a LED in a nice and soothing pace.

Next I tried to build ulisp.

wget https://raw.githubusercontent.com/technoblogy/ulisp/master/ulisp.ino
mv ulisp.ino ulisp-uno.ino

Here I got some compile errors that needed to be fixed.

I also added some libraries to the Makefile.

LIBRARIES=SPI EEPROM

See https://github.com/peterljung/ulisp for details.

So after another make/upload ulisp was running on the Arduino.

To connect to the device i used cu serial terminal emulator.

cu -s 9600 -l /dev/cuaU0

See Using ulisp for some guidance on how to use the on target repl environment. Here is a sample session.

uLisp 2.6
314> (+ 451 42)
493

314> (defun b () (pinmode 13 t) (loop (digitalwrite 13 t) (delay 500) (digitalwrite 13 nil) (delay 500)))
b

279> (b)
Error: Escape!

The second example is the corresponding blinking hello world in lisp! You escape a running command with ~. You escape the cu serial terminal with ~. ENTER.

See ulisp Language Reference for a complete reference of available commands.

When you are satisfied with your program you can save the image (save-image) (i.e. current state) which you can optionally autorun on hardware reset. See Saving and loading images for more info.

Pretty awesome! But now I wonder if this is something to be used for real deployments?

You can certainly have some serious fun with this ...

Install ulisp on 8266 NodeMCU v0.9 on macOS and Arduino IDE

Install CH340 driver on macOS.

Install python3.

Install Arduino IDE.

Install support for NodeMCU board support in Arduino IDE.

Download ulisp and open .ino file in Arduino IDE.

Start serial monitor on 9600 baud.

uLisp 3.0
3071> (+ 1 1)
2

Done!

Try the examples.

Connect to local wifi.

(wifi-connect "SSID" "password")

Download time based on current IP.

(defun println (x s) (princ x s) (princ #\return s) (princ #\newline s))

(defun time ()
  (with-client (s "worldtimeapi.org" 80)
    (println "GET /api/ip HTTP/1.0" s)
    (println "Host: worldtimeapi.org" s)
    (println "Connection: close" s) 
    (println "" s)
    (loop (unless (zerop (available s)) (return)))
    (loop
     (when (zerop (available s)) (return))
     (princ (read-line s))
     (terpri))))

Request page.

(time)
HTTP/1.1 200 OK
Connection: close
Server: Cowboy
Date: Sat, 22 Feb 2020 16:57:43 GMT
Content-Length: 346
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=0, private, must-revalidate
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: 
Access-Control-Allow-Credentials: true
Via: 1.1 vegur
{
	"week_number":8,
	"utc_offset":"+01:00",
	"utc_datetime":"2020-02-22T16:57:44.231964+00:00",
	"unixtime":1582390664,
	"timezone":"Europe/Stockholm",
	"raw_offset":3600,
	"dst_until":null,
	"dst_offset":0,
	"dst_from":null,
	"dst":false,
	"day_of_year":53,
	"day_of_week":6,
	"datetime":"2020-02-22T17:57:44.231964+01:00",
	"client_ip":"212.85.95.32",
	"abbreviation":"CET"
}
nil

References