ulisp
ulisp copied to clipboard
How to get it out of REPL mode?
Copied from technoblogy/ulisp-esp#54
I'm planning on using Microlisp as a scripting language for a robot I am building, and I want to automatically run code on a microSD card upon startup instead of launching the REPL, so that I don't need to connect to it over serial to do anything. (I will be using this ESP32 board if that helps.)
From what I have read in the documentation, I need to add code to the LispLibrary at the top, uncomment the #defines to enable SD card support, add in my C functions at the bottom, and then what do I need to change in the setup() and loop() to get it out of REPL mode?
I don't want to use the (load-image) support because it would become corrupted if I edit the C code and I would have to tediously load all the Lisp all over again and then (save-image 'main) or something like that every time. I just want to be able to flash the firmware onto the ESP32, plug in a microSD card with a bunch of .lisp files on it, and have it run itself.
I know I could have posted this in the forums, but I really don't want to have to create a brand-new password-protected account just for one stupid question.
If this is already in the documentation, please point me to the appropriate page. My apologies in advance if this is the case.
I'm planning on using Microlisp as a scripting language for a robot I am building,
Great! That was one of my original motivations for developing uLisp, as the thought of writing a robot control language in C was definitely unappealing. By the way, I assume you mean uLisp, not Microlisp.
I want to automatically run code on a microSD card upon startup
There are two ways to load Lisp functions into uLisp. One is as a Lisp image, which is a compacted image of the workspace in the state it had when you saved it, and the other is as the source of Lisp functions, either using the LispLibrary, or as a Lisp source file on an SD Card. This is documented here:
http://forum.ulisp.com/t/loading-ulisp-programs-from-an-sd-card/510
Also, see:
http://forum.ulisp.com/t/load-ulisp-program-from-a-file-on-an-sd-card/1011
I don't want to use the (load-image) support because it would become corrupted if I edit the C code
On the ESP32 the Lisp image saved with (save-image) is saved into flash memory using LittleFS, so it won't be corrupted if you edit the C code, unless you change the table of functions.
Hope that helps!
it won't be corrupted if you edit the C code, unless you change the table of functions.
Well, that's what I was planning on doing :smile: Maybe I should have said "when" I edit it.
Anyway, I think my best attempt at non-REPL mode would be:
#define lisplibrary
#define sdcardsupport
const char LispLibrary[] PROGMEM =
"(defun load (filename)"
" (with-sd-card (f filename)"
" (loop"
" (let ((form (read f)))"
" (unless form (return))"
" (eval form)))))"
"(defun main ()"
" (load \"main.lisp\"))";
void ulisp_init() { initworkspace(); initenv(); initsleep(); }
void setup() {
Serial.begin(9600);
while (!Serial);
// other setup
ulisp_init();
}
const char main[] PROGMEM = "(main)";
int mainptr = 0;
int ghardcoded() { return mainptr >= 6 ? 0 : (int)main[mainptr++]; }
void loop() {
object* main = read(ghardcoded);
push(main, GCStack);
eval(main, nil);
pop(GCStack);
}
Please correct me if that is wrong.
EDIT: sorry for all the editing!!
I've been fooling around with ulisp-esp a bit and I have this code that in theory should work:
void sdmain() {
#if defined(sdcardsupport)
#if defined(LITTLEFS)
SDgfile = LittleFS.open("/main.lisp", FILE_READ);
#else
SDgfile = FS.open("/main.lisp", FILE_READ);
#endif
if (!SDgfile) error2(NIL, PSTR("problem autorunning from SD card file"));
object* main = read(SDread);
push(main, GCStack);
eval(main, NULL);
pop(GCStack);
#else
#error Need SD support to run from SD!
#endif
}
void loop () {
if (!setjmp(exception)) {
#if defined(resetautorun)
volatile int autorun = 12; // Fudge to keep code size the same
#else
volatile int autorun = 13;
#endif
if (autorun == 12) autorunimage();
}
// Come here after error
delay(100); while (Serial.available()) Serial.read();
clrflag(NOESC); BreakLevel = 0;
for (int i=0; i<TRACEMAX; i++) TraceDepth[i] = 0;
#if defined(sdcardsupport)
SDpfile.close(); SDgfile.close();
#endif
#if defined(lisplibrary)
if (!tstflag(LIBRARYLOADED)) { setflag(LIBRARYLOADED); loadfromlibrary(NULL); }
#endif
client.stop();
+ sdmain()
repl(NULL);
}
--- but is doesnn't. It always gets to "Error: problem autorunning from SD card file", even though /main.lisp exists and contains valid uLisp code. Do you have any idea what is wrong here?
It doesn’t seem to be able to open the file on the SD card, even before it tries to eval it. Try opening the file and printing it with Serial.print().
Try calling sdmain() without all the other stuff in loop().
Let me know how you get on, David
A quick test with (with-sd-card) proved that uLisp could open the file, but when it went to autorun it, it couldn't.
I recompiled without the sdmain() and #define resetautorun and via the serial monitor saved an image that would load the file. And for some odd reason (save-image) couldn't write to the SD card either.
I traced that down to (with-sd-card) calling SD.begin() letting the SD library autodetect the SD card pin, but in saveimage, loadimage, and autorunimage call SD.begin(SDCARD_SS_PIN), and for some reason on many of the platforms SDCARD_SS_PIN is defined as pin 13, which is the LED, not the SD card. So I removed the SDCARD_SS_PIN, resaved an image (it worked this time) -- still no joy, wouldn't autorun.
Then it dawned on me all I needed was this. No sdmain()!
#define lisplibrary
const char LispLibrary[] PROGMEM = "(with-sd-card (file \"main.lisp\") (loop (let ((form (read file))) (unless form (return)) (eval form))))";
That worked!
Yes, that's a good solution.