Header image Ingenieur, Vater, Heimwerker, Problemlöser

26 März, 2016

Deine eigene LISP Machine

TL;DR:

Wer wollte nicht schon immer mal eine Lisp-Maschine mit einem echten RTOS sein Eigen nennen? Wenn möglich schön handlich, mit Wifi, serieller Console über USB und kosten darf es auch nicht mehr als 5 EUR. Mit dem beliebten ESP8266 von espressif ist das tatsächlich möglich.

Jonas Karlsson (yesoc) hat mit seinem Projekt esp-lisp einen Lisp Interpreter für den ESP8266 geschrieben. Der Funktionsumfang ist für sein Beta-Stadium schon beträchtlich. Die Lisp-Implementierung ist bereits gut ausgereift und bietet schon rudimentäre Interaktion mit dem ESP8266-Board selbst (IO-Pins, WiFi).

Da der ESP8266 nicht direkt an USB angeschlossen werden kann, empfiehlt es sich, ein fertiges Dev-Board wie das Wemos D1 Mini oder das CH340 NodeMCU V3 zu verwenden. Diese können direkt mit einem USB-Kabel an den PC angeschlossen werden.

ESP8266

ESP SDK compilieren

Achtung: Alle Befehle müssen in einer bash Shell ausgeführt werden.

Als erstes werden wir das ESP Open SDK herunterladen und compilieren. Es enthält ein paar Hilftfunktionen, die wir für die weiteren Schritte benötigen. Evtl. müssen noch ein paar Pakete wie gperf oder make installiert werden. Bei mir war noch die Umgebungsvariable $LD_LIBRARY_PATH gesetzt, diese musste ich zuvor noch leeren. Vor dem compilieren muss (Stand 15.03.2016) noch eine Datei gepatched werden, damit der Compilevorgang abgeschlossen werden kann.

git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
cd esp-open-sdk
make STANDALONE=n

Im Anschluss muss die $PATH Variable angepasst werden, so dass die darauffolgenden Befehle die gerade compilierten Tools finden. Es lohnt sich, alle folgenden Komponenten/Projekte in einem Unterverzeichnis auszuchecken. Am Ende sollten drei Unterverzeichnisse vorhanden sein. In diesem Unterverzeichnis muss zudem eine Datei mit dem Namen path-add-esp angelegt und mit folgendem Inhalt gefüllt werden:

export PATH=/home/aaron/workbench/esp-open-sdk/xtensa-lx106-elf/bin:$PATH

RTOS ESP8266 SDK beschaffen

Im Anschluss benötigen wir das auf FreeRTOS basierende Framework für den ESP8266. Dies ist die Basis für esp-lisp. Hiervon benötigen wir hauptsächlich die Source-Files, compilieren müssen wir hier nichts. Dies folgt im nächsten Schritt.

cd ..
git clone --recursive https://github.com/SuperHouse/esp-open-rtos

Eine kleine Anpassung müssen wir aber auch hier machen. In der Datei include/ssid_config.h muss die Zeile mit dem #warning entfernt werden. Die WiFi-Daten müssen wir nicht angeben.

esp-lisp compilieren

Zuletzt brauchen wir noch den esp-lisp Code selbst. Und auch hier müssen wir eine kleine Änderung vornehmen, wenn python in Version 3.x installiert ist (bei ArchLinux der Fall). Hierzu einfach die Datei xtensa-lx106-elf/bin/esptool.py öffnen und in der ersten Zeile python durch python2.7 austauschen. Das praktische Script run erledigt dann den Rest für uns. Mit make flash kann dann zuletzt die Firmware auf den ESP8266 aufgespielt werden (ein an USB angeschlossener ESP8266 vorausgesetzt).

cd ..
git clone https://github.com/yesco/esp-lisp.git
cd esp-lisp
./run
make flash

Starten

Die Serielle Verbindung zur Lisp-Maschine kann mit dem Script mcu aufgebaut werden (das Programm screen muss installiert sein).

./mcu

Nach einem Druck auf Enter wird man mit einer REPL begrüßt.

lisp> (define fib (lambda (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))))
#fib
lisp> (fib 10)
89

Ziemlich cool ist die trace-Funktion (einschalten mit trace on). Hier kann die Schrittweise Evaluation sehr anschaulich angezeigt werden. Das hilft vor allem beim Erlernen von Lisp.

lisp> trace on
lisp> (fib 2)
---> (fib 2)
     ENV  
  ---> (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
       ENV n=2  
    ---> (#< n 2)
         ENV n=2  
    nil <--- (#< n 2)
    ---> (#+ (#fib (#- n 1)) (#fib (#- n 2)))
         ENV n=2  
      ---> (#fib (#- n 1))
           ENV n=2  
        ---> (#- n 1)
             ENV n=2  
        1 <--- (#- n 1)
        ---> (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
             ENV n=1  
          ---> (#< n 2)
               ENV n=1  
          t <--- (#< n 2)
        1 <--- (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
      1 <--- (#fib (#- n 1))
      ---> (#fib (#- n 2))
           ENV n=2  
        ---> (#- n 2)
             ENV n=2  
        0 <--- (#- n 2)
        ---> (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
             ENV n=0  
          ---> (#< n 2)
               ENV n=0
          t <--- (#< n 2)
        1 <--- (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
      1 <--- (#fib (#- n 2))
    2 <--- (#+ (#fib (#- n 1)) (#fib (#- n 2)))
  2 <--- (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
2 <--- (#fib 2)
2

Ich habe für meine LISP-Machine ein kleines Gehäuse gedruckt. Im Innern arbeitet ein winziges Dev-Board, das ich mit ein paar Tropfen Sekundenkleber eingeklebt habe. Eine WS2812 RGB-LED, die auf dem Dev-Board praktischerweise aufgelötet ist, beleuchtet das Innere.

lisp machine

Bisher ist mein Gehäuse nur grundiert und es fehlt noch die Bemalung und die Aufschriften. Dennoch funktioniert der Winzling schon wie das Vorbild.

lisp machine

Erstaunlich, dass vor 30 Jahren solche Maschinen unbezahlbar und groß wie ein Kleiderschrank waren. Heute ist es nur noch Spielzeug und passt zwischen zwei Finger.