-
So at the moment, you have a bunch of yellow espruino nodes, and all of those nodes run on Espruino itself - connected via MQTT?
Exactly. The nodes on espruino communicate with nodes in the node-red process (and with nodes on other espruiono boards) via MQTT. Local communication just uses function calls or setTimeout(.., 0) calls to break the tail recursion.
I did consider gateway nodes but I found that I didn't really need that and that it's more elegant to do some magic where the wires are connected internally. I did not consider running arbitrary nodes on espruino for two reasons. One is resource consumption: I'm trying to keep the espruino side small. The other is that I don't see the use for it (I'm probably blind). I went down the sidebar in node-red and asked which of the std nodes I would run on espruino and I came up pretty empty. If I take the example of a DHT21 temperature sensor, do I really want to be able to write one implementation of a node that uses two different DHT21 modules which happen to have the same interface but are implemented very differently in espruino vs node-red? Maybe I'm missing the point?
-
-
I'm continuing to have issues that I believe are related to using
bind()
. The call of the function returned crashes. I don't know how to repro it in a simple situation yet. I do have a stack back-trace. The assertion is in jsvRef:ASSERT(var && jsvHasRef(var)) FAILED AT src/jsvar.c:622
The lower part of the stack is (top-down):
jspePostfixExpression at /big/home/src/esp8266/Espruino/src/jsparse.c:1424 jspeFactorFunctionCall at /big/home/src/esp8266/Espruino/src/jsparse.c:1157 jspeFunctionCall at /big/home/src/esp8266/Espruino/src/jsparse.c:618 jsvCopy at /big/home/src/esp8266/Espruino/src/jsvar.c:2051 jsvRef at /big/home/src/esp8266/Espruino/src/jsvar.c:622
Any clue? How can I troubleshoot this? Any way I can find out which JS statement caused this?
-
-
What I like about node-red's approach is that it's quite high-level plumbing. You don't write algorithms visually, instead those go into "function" nodes as code text. So what you end up visually is large components/modules that you plumb together. It makes it easy to take some value floating around in there, such as a temperature, and wire that up to some new nodes that record it, alert on extremes, chart it, plop it onto a dashboard, etc.
-
I've been using node-red for a while to act as the "server-side" component of various Espruino nodes I run, but I've been wanting to try and integrate things further, which is is to run actual node-red nodes on Espruino itself. That's espruino-red. If you're interested I wrote a long post on the node-red group: https://groups.google.com/forum/#!topic/node-red/pJXwdl2UMQ8 and there's a pretty detailed readme in the repo: https://github.com/jeelabs/espruino-red
Warning: it's all a proof-of-concept that this stage (but it does really work). -
-
To load modules automatically at boot or reset, use the following:
// Copyright (c) 2015 Thorsten von Eicken. MIT License E.setBootCode( "(function(){" + "var FL=require('Flash');var FR=FL.getFree()[2];var FB=FR.addr;var FS=4096;" + "var FN=FR.length/FS;var FO=1075838976;" + "for(var c=0;c<FN;c++){" + "var b=FB+c*FS;var a=FL.read(4,b);var d=a[0]<<2;var e=b+4+d;var f=a[1]<<4;" + "if(a[0]==0||a[1]==0||a[2]!=165||a[3]!=195){" + "console.log(' nothing at',b.toString(16));continue;}" + "var g=E.toString(FL.read(d,b+4)).trim();" + "console.log(' memoryArea',e.toString(16),f,g)," + "Modules.addCached(g,E.memoryArea(FO+e,f));" + "}"+ "})();", true);
it uses no JSvars itself once executed. Caveat: currently boot code doesn't work on the esp8266 due to https://github.com/espruino/Espruino/issues/891
-
Something I do with the esp8266 is to sync the clock to UTC using NTP and then receive periodic MQTT messages with the TZ offset. By separating the two I end up with a nice monotonic UTC clock for calculations and when I want to display time I add the TZ offset. Sync'ing the TZ offset is not time critical, so no special protocol necessary (I happen to use MQTT).
-
I finally spent some time to grok E.memoryArea so I can save code in flash and execute code from flash. This achieves two goals: have more space for code and reduce upload time when testing 'cause modules can remain loaded.
The way it works is that on esp8266 modules with at least 1MB of flash there are 9 flash pages of 4KB free (actually only 5 on modules with exactly 1MB flash). I wrote a little JS module that stores one JavaScript module per flash page indexed by name. So, as long as the minified size of the module is <4KB (minus the length of the module name and a few extra bytes) you can store the module code in one of the flash pages. Then another smaller module loads all the modules that are stored into the Modules cache so when you require them in the main app they're already there and are executed from flash.
The savings are mitigated a little bit by the fact that all the functions that are defined in the module still need to be represented in memory. Here's an example:
// encode a string for mqtt by prefixing the length (16-bits) function mqttStr(str) { return sfcc(str.length >> 8, str.length&255) + str; };
This turns into (after minimizing):
#380[r1,l2] Name String [2 blocks] "mqttStr" #382[r1,l1] Function { return #383[r1,l2] Name Param "a" undefined #385[r1,l2] Name String [1 blocks] "\xFFcod" #384[r1,l1] NativeString [1 blocks] "sfcc(a.length>>8,a.length&255)+a;" #386[r1,l2] Name String [1 blocks] "\xFFsco" #270[r14,l2] ...
In this case the function header uses 2 JSvars, the parameter 1, the pointer to the code 1, and one more for I forget what. The function body is 33 characters long, which would have taken 4 JSvars, I believe. So we end up needing 5 instead of 9 JSvars. Functions that have longer bodies have more savings...
To make all this usable I modified the espruino-cli to accept a
-u module
argument, which minifies the module and uploads it to flash. So to prep an esp8266 for an app that uses 3 modules I'll run something like:espruino-cli -m -s espruino:23 -u MQTT espruino-cli -m -s espruino:23 -u Nextion espruino-cli -m -s espruino:23 -u MCP23008_tve
Under the covers this does the same minification as a
require(moduleName)
would do but instead of runningModules.addCached(moduleName, moduleText)
it runsrequire("FlashString").save(moduleName, moduleText)
to save the module into flash.To run my app, I modify the
requires
so espruino-cli doesn't think it needs to pull-in and upload the modules. A tiny bit of trickery suffices:require("module")
becomesr=require; r("module")
. All this needs to be prefixed by a very small module to actually load everything from flash. That's done by addingrequire("FlashLoader")()
. So what happens is that the FlashLoader module gets uploaded with the app, the FlashLoader restores all the modules from flash into the Modules cache, and then require finds them there. Phew!The storage of the modules in flash is extremely simple. Each page starts with a 4-byte header that contains the length of the module name, the length of the module text, and a magic number. This is followed by the module name and the module text. That's it.
If you want to play with any of this, the FlashString module is at https://github.com/tve/EspruinoModules/blob/master/FlashString.js (it knows nothing about Javascript modules, as far as its concerned it just stores named strings in flash). The FlashLoader module is at https://github.com/tve/EspruinoModules/blob/master/FlashLoader.js and my version of the espruino-cli is at https://github.com/tve/EspruinoTools. Note that the cli changes are not strictly required, it's fairly easy to copy and paste stuff to upload a module manually.
-
-
-
-
-
I'm still stumped. What I'm trying to do is call
Telnet.setConsole(true)
in order to prevent the console from jumping back to Serial1 as soon as I disconnect. I have a Nextion display on Serial1 so I have to prevent anything from being output there that is console-related.I'm actually not sure that setting the console to Telnet works. I think it'll block once the buffer is full. If I do
Serial2.setConsole(true)
it's OK in that characters just go out that device and there's nothing listening, but the problem is that then it's unrecoverable via telnet. Maybe something I could do is when disconnecting telnet to only reset the console to the "old_console" ifconsole==EV_TELNET
. This way I can doSerial2.setConsole(false)
, have stuff go out there (which is the debug TTY anyway) but not prevent reconnecting via Telnet. -
I renamed the class to TelnetConsole, but that didn't do the trick. I also noticed https://github.com/espruino/Espruino/blob/master/scripts/build_tern_json.js#L81-L82 which hopefully just affects docs generation?
Edit: the renaming does work. Maybe "TelnetServer" is more appropriate, though.
-
-
I'm a bit puzzled abouit the Telnet serial device. A static object is defined in https://github.com/espruino/Espruino/blob/master/src/jswrap_serial.c#L152-L160 but on the esp8266 I don't actually get that device.
Telnet
just doesn't exist. I can fortunately get to the device asrequire("Telnet").Telnet
. Why is that?Update: mhh, doesn't look like
require("Telnet")
does anything useful :-( -
-
-
-
I can repro on Linux: https://github.com/espruino/Espruino/issues/885
-
Well, a bunch of hours of tracing the call chain and determining where the lock on the connection/socket gets incremented this is what I've found out:
- socketClientConnectionsIdle line 484 calls socketClientPushReceiveData
- socketClientPushReceiveData 440 calls jswrap_stream_pushData
- jswrap_stream_pushData 80 calls jsiExecuteEventCallback
the number of locks is incremented by one when jsiExecuteEventCallback returns - jsiExecuteEventCallback 1558 calls jspExecuteFunction
- jspExecuteFunction 2542 calls jspeFunctionCall
...
I can see clearly that every time I receive an MQTT message my on("data") event handler gets called and when socketClientConnectionsIdle is abouit to return the lock count on the socket is bumped up by one. So after about 15 messages that's it, the assertion fires.
- socketClientConnectionsIdle line 484 calls socketClientPushReceiveData
-
I'm continuing to have assertion failures. A different JS program now hits
assert(jsvGetLocks(var) < JSV_LOCK_MAX)
in jsvLockAgain. I added code to calljsvGetPathTo
before the assertion hits and in that function it hits the same assertion in jsvLock. Most likely jsvGetPathTo tries to lock the same var as it traverses the hierarchy. Is there any other way to get information about which variable this is and/or where in the JS code this is being encountered? Is there a way to print the JS callstack?Update: I added a decrement of the lock before calling jsvGetPathTo so it doesn't hit the assertion failure. Now it prints:
▒.HttpCC["0"]
. After some poking around, I conclude that that's my MQTT client connection.
Yes. That's correct.