To set up a REPL that has cljs.core
available, we need to re-partition the ESP32 and allocate more memory in Espruino for "JsVars".
The default Espruino bootloader.bin
, pre-built variants of partitions_esprinuo.bin
and rebuilt espruino_esp32.bin
, and the ClojureScript runtime core.bin
are also available here.
bootloader.bin
gets flashed to 0x1000
, partitions_espruino.bin
gets flashed to 0x8000
, and espruino_esp32.bin
gets flashed to 0x10000
as per a usual ESP32 Espruino setup, and we put the ClojureScript runtime at 0x2C0000
:
Assuming port is /dev/cu.SLAB_USBtoUART
and that you can communicate via USB at 2 Mbits/s, execute this commands:
esptool.py --port /dev/cu.SLAB_USBtoUART --baud 2000000 write_flash 0x1000 bootloader.bin 0x8000 partitions_espruino.bin 0x10000 espruino_esp32.bin 0x2C0000 core.bin
Below is additional description on how these images are built in case you want to experiment for yourself.
Here is more detail on how the above bin
images were created.
First, re-partition so that we have sufficient space to put cljs.core
into the "boot ROM". The default partition table is here and you can see that js_code
has by default 256 KiB, which isn't enough to hold cljs.core
.
To accomplish this, we will allocate 1 MiB for js_code
by changing the storage
partition so that instead of starting at 0x300000
, it starts at 0x3C0000
.
To make the new partitions image involves cloning EspruinoBuildTools, editing the esp32/build/app/partitions_espruino.csv
file in it like so
-js_code,data,0,0x2C0000,256K
-storage,data,0,0x300000,1024K
+js_code,data,0,0x2C0000,1024K
+storage,data,0,0x3C0000,256K
and then running the build-partition.sh
script in that repository.
To rebuild the Espruino engine, in the Espruino
reporsitory, change boards/ESP32.py
to specify the larger partition:
- 'pages' : 64,
+ 'pages' : 256,
Additionally, when loading cljs.core
we will need around 28,000 JsVars, so you can edit targets/esp32/main.c
to allocate more:
- if(heapVars > 20000) heapVars = 20000; //WROVER boards have much more RAM, so we set a limit
+ if(heapVars > 55000) heapVars = 55000; //WROVER boards have much more RAM, so we set a limit
This js_code
bin has cljs.core
in it:
It was produced from this JavaScript:
This cljs.core
JavaScript is taken from a release build of Planck (so that it has :simple
applied to it), and it has been subsequently edited to at least set up some of the needed goog
and cljs.core
objects near the top. It has also had the problematic break labels removed.
You'd flash core.bin
to 0x2C0000
, just as main.bin
was done in this earlier gist.
Once you have flashed your ESP32 with the new partitions, engine and cljs.core
binaries, connect to it in the Espruino Web IDE. Give it some time to connect as it is now loading cljs.core
at boot (approximately 15 seconds).
You can confirm that the new binaries and core is in place (55000 JsVars available, and 27674 being used by cljs.core
). Connect to the ESP32 using USB and a terminal emulator, such as screen
:
screen /dev/cu.SLAB_USBtoUART 115200
Then you can interact:
>process.memory()
={ free: 27326, usage: 27674, total: 55000, history: 3,
gc: 0, gctime: 366.053 }
Ensure that your ESP32 is set up to connect to your WiFi.
var ssid = 'YOUR_SSID';
var password = 'YOUR_SSID_PASSWORD';
var wifi = require('Wifi');
wifi.connect(ssid, {password: password}, function() {
console.log('Connected to Wifi. IP address is:', wifi.getIP().ip);
wifi.save(); // Next reboot will auto-connect
});
Then evaluate the following JavaScript in the connected terminal to set it up so that it can accept REPL connections:
cljs.user = {};
var accum = "";
var evalme = false;
var server = require("net").createServer(function(c) {
console.log("New REPL Connection");
current_c = c;
c.on("data", function(data) {
//console.log(">" + data);
if (data.startsWith("(function (){try{return cljs.core.pr_str")) {
evalme = true;
}
if (data.endsWith("\0")) {
if (evalme) {
//console.log("evaluating: " + accum + data);
try {
c.write(
JSON.stringify({ status: "success", value: eval(accum + data) })
);
} catch (e) {
c.write(JSON.stringify({ status: "exception",
value: ""+e,
stacktrace: e.stack}));
}
accum = "";
evalme = false;
c.write("\0");
} else if (data === ":cljs/quit") {
c.end();
} else {
c.write(JSON.stringify({ status: "success", value: "true" }));
accum = "";
evalme = false;
c.write("\0");
}
} else {
accum = accum + data;
}
});
});
server.listen(53000);
print("Ready for REPL connections.")
Also, even though cljs.core
will now be available in its entirety via core.bin
loaded from the js_code
partition, I haven't dealt with cljs.core
's dependencies on Google Closure Libraries, but if you then evaluate the following JavaScript in the Espruino Web IDE left panel, you will have enough functionality to use a REPL:
goog.string.StringBuffer = function (a,c){null!=a&&this.append.apply(this,arguments)};
goog.string.StringBuffer.prototype.append = function (a,c,d){this.buffer_+=String(a);if(null!=c)for(var b=1;b<arguments.length;b++)this.buffer_+=arguments[b];return this};
goog.string.StringBuffer.prototype.toString = function (){return this.buffer_};
goog.typeOf = function (a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
else if("function"==b&&"undefined"==typeof a.call)return"object";return b};
goog.isString = function (a){return"string"==typeof a};
goog.isFunction = function (a){return"function"==goog.typeOf(a)};
goog.string.StringBuffer.prototype.buffer_ = '';
Then to connect with Ambly, first, on macOS advertise the IP address of the ESP32 WROVER (replace 10.0.1.55 with the IP address your WROVER has) by running this (and leaving it running---later this will be built directly into the ESP32 WROVER code so that it advertises itself):
dns-sd -P "Ambly ESP32 WROVER" _http._tcp local 53001 ambly.local 10.0.1.18
Then run some modified Ambly code from its ESP32
branch via
clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.520"} ambly {:git/url "https://github.com/mfikes/ambly" :sha "7e84e590aeb66db09dd76e55ef701bef97bc3145"}}}' -m cljs.main -re ambly
This will start up the discovery process (seeing the IP and port you've advertised above via dns-sd
), connect you to your ESP32 and initiate a REPL session:
[1] ESP32 WROVER
[R] Refresh
Choice: 1
Connecting to ESP32 WROVER ...
ClojureScript 1.10.520
cljs.user=> 3
3
cljs.user=>
This looks like fun thanks for the excellent documentation Mike