-
reset()
thensave()
has almost the same effect... as aclear()
would have.Would it be possible to actually make it this way? ...because after a first
save()
on a flashed Espruinoreset()
always leaves residues. Only a re-flash gets you back to the realy clean slate... state. The startup is different with the residues.What ere these few hundred bytes that still are saved anyway after a
reset ()
? -
microchip just announced:
** The Quickest Way to Get Started with PIC(R) Microcontrollers **
MPLAB Xpress Cloud-Based IDE is an online development environment that contains the most popular features of our award-winning MPLAB X IDE. This simplified & distilled application is a faithful reproduction of our desktop-based program, which allows users to easily transition between the two environments.
...see Microchip: MPLAB Xpress - Cloud-based IDE...
...big feet drag. Espruino leads!
Coding is just a little bit stuck in C and the Arduino Loop... Where is the modern Event Driven programming model easy accessible: on Espruino!
-
I think we might be getting sidetracked from APA102s though.
Was thinking that too... but did not want to start separate thread (yet).
Espruino has the concept of a 'name' and an actual value.
Yes, but they are 'stuck' with one another in 'block contiguous' memory. As soon as they can be / are separated, a JIT as sketched in previous posts can be achieved.
-
I did not study the variable handling close enough, but I expect a memory area where actually the metadata about the variable is kept - last but not least for garbage collection and the like - and that part stays put (has fixed address) - I expect or hope... I know: in science and technology, only fools hope :). But anyway...
The pointer overwriting the variable name in the source I was thinking of would point to the location of the meta data and not the location where the actual value is. Since I do not know how the space of the variable metadata is organized (where
var
creates the metadata), it may add some challenges. For example, if it is a linked list - just as so many things seem to be in Espruino (for better or worse) - it is still a look up... so no gain, just pain.The linked-list concept makes me just now think that the meta data may be a 'memory address in-line' header of the actual variable data, and all is a linked list with explicit (pointers) or with implicit (length) linking for the lookup. Separation of the variable meta data from the actual value data make a partial JIT possible... (That's what I did in my OO VMs in order to have resolved references... that are fast, random - array like - accessible).
There are usually not infinite places for a 'contiguous, growing/shrinking' memory space... and the separation asks just for one more... In addition, one more pointer has to be hosted per variable which points from the meta data to the value data. Furthermore, more discrete items in memory waste more bytes since they do not all align with the memory block boundaries... which confirms the truth that speed costs space. For some defined data types - such as int, boolean, float (and natively implemented objects of known fixed lengths), the meta data can also include the values and there is no need for pointing into the 'wild' (dynamically) managed memory space.
While writing this post, I conclude that Espruino does not separate variables' meta-data form variable data, and going for a separation is a major concept change: a new Theory of Operation.
@DrAzzy's idea is NOT THAT knee-jerk: remember Rockwell's 6502... a processor breath life into Commodore's PET and Apple I and on is still very a-live, and what did this thing do - more precisely - what where the architects of it thinking? Let's treat the first 256 memory locations / (single bit addresses) be handled differently: let's treat them as general registers! A brilliant hybrid choice for a time when (memory and processing) resources weren't commodities yet... and about the same I feel with Espruino. And I used the Wang Computers which used a similar approach: Variable names would always start with an uppercase and be followed by a digit 0 thru 9. Ending with a $ denoted one of the two only available data types: Strings. The other data type was a number (of course - last but not least it was 'a computer' - Wang 2200 T, starting out from a Wang 370 desk calculator). Wang 2200 T was a TTL implementation with 18 bit word format and 7483 based ALU and memory was blankets of blankets of RAM chip 'cards'.
Usually, i like pure, consistent implementations, but with what is at stake and can be achieved 'here' with very limited resources asks for a bit more flexibility, creativity,... (going for that single letter variable thing, may mess a bit with the minifiers..., but after all it could be quite a boost. It could though start a 'war of claims': who owns which variable... because they are global and exist only once!
-
What about...
For execution, the code is loaded into the RAM - and therefore it is modifiable - is it?. With some JIT compiler mind, the interpreter could after first lookup of a (long enough) variable (or function) referencing name override that name with a prefixed pointer. The prefix is some reserved byte(s) value which is telling the interpreter on subsequent interpretation that the lookup already happened. (May be there is no such special byte(s) left to make the interpreter recognize). Caveat: the delete of a variable becomes a bit tricky... and would thus not be allowed anymore.
-
-
EEPROM is more general term for Electrically Erasable Programmable Read Only Memory, an evolution of the EPROM (which had to be reased w/ UV light), an (functional) evolution from PROM - one time programmable only, an (functional) evolution from ROM - content 'hardwired'/set in stone on 'build time'.
There are various types of memory cells, wirings, and control logic in use in EEPROMs which make them either a byte-wise erasable / programmable (writeable) memory, versus a Flash one which can only block-wise erasing and (mostly) byte-wise programming.
Erasing means to set all bits to 1, and programming means make some 0. Depending on cell type it can be 0 and 1 vs. 1 and 0. Depending on the 'luxuriousness' of the driver available to the application code to drive the EEPROM, the erasing and programming and byte- and block-wise operation is transparent: it is just simple read and (re)write for the application. Elaborate drivers also take care of the balanced erasing (and programming), because the prevalent EEPROMS have an 10'000...100'000 erase/write cycle limit. Technologies though get better and better and some have practically unlimited cycles and can be used like RAM (MRAM, and to a lesser extent, FRAM). Today's SSD - Solid State ('Hard') Drives fair almost unlimited.
Flash vs. 'plain' EEPROM in a nutshell: Flash is a particular EEPROM (more details).
-
Since they are clocked and not timed, higher switch frequencies can be applied... adafruit sells them under the term DotStar LEDs https://www.adafruit.com/products/2241.
-
PICO and Espruino on ESP8266 are different stories... ESP8266 may block in the non-ESPRUINO part of the firmware while handling the communication... A PICO combined with an ESP8266 leaves the ESP8266 alone and is blocked only when doing some fast bit banging for software rather hardware/device communication, calculation,...
-
PICO: 25mA by pin, but not more than 120mA for all pins together...
More details: http://www.espruino.com/datasheets/STM32F401xD.pdf p.59
-
I'm not sure about an Internet request blocking..., because a callback is passed onto the request to handle the response.
With that said, other things can move on and do their thing.
Of course not every callback is asynchronous, but why ask for one and then just be sychronous? For a synchronous request, assigning the request result to a variable would be sufficient.
-
-
I'm not sure about the purpose of ESP8266 standalone/combined with Espruino board to be fully fledged Web (app) servers...
Yes, it is nice to have the http(s) protocol and then routing available to support modular application builds. MCs are usually used to 'just' gather some sensor's values and then transmit them in a lean way to the requester through a mediator, which complements the raw data with the shoe shine that is needed for a neat presentation. The transmitted document can be a complete html document but the 'UI-meat' comes from a third party - a UI resource server that provides the 'tons' of html(5 - html, css, js, any UI and other library/framework standard/custom code) to create a nice UI (and single page app) in the client / browser.
I'd like to keep the distributed MCs that gather the sensor data as lean as possible with the least of dependencies and changes, and then 'fully load' a classical Web (app) server with all the resources - and with CORS - it is even possible to write the data on those readily equipped servers, or flip the roles completely: deliver everything but the IoT sensor data from a regular Web (app) server and make an CORS enabled ajax call to ESP8266/Espruino to get the data.
There was a while ago this conversation about Shiny Web UI which was covering such subjects. @Snerkle, I don't have an exact understanding of all the requirements and constraints you are pursuing to satisfy, but as an MC I would be pretty overwhelmed...
In the this post I show an approach to keep the html doc sent from the IoT device as slim as possible without sacrificing the consumer UI.
In order to have it a standalone example that everyone can run just off the forum, I emulate the (temperature) sensor reading with this anonymous function in the setInterval() from lines 16 thru 23, which - when implemented - will make a slim ajax call (back) to the ESP8266/Espruino to just get the raw sensor data.
The UI library/framework I'm using in the example is dojotoolkit.org pulled in from ajax.googleapis.com. Any other smart library or framework will do too. Nice about dojo is the AMD implementation: the code in line 4 pulls only a minimum: the loader / require.js which then allows to pull into (and cache in) the browser what else is needed, as beginning with line 7.
The example the html body includes all UI layout... what makes it still a fatter 'file' then needed: putting the body content into an AMD module and load it from a regular Web (app) server - he same as one would put on the library/framework - allows even more a slimming down of what 'has to live in Espruino... which could the make an SD card obsolete... and all your related problems goners...
-
Great news! Entry model for Espruino...
@Gordon, did you ever think to put communication directly on pico? it has the space when moving the 0.05" pins as 0.1" into the rows to make PICO a 2x14 (28) pin JEDEC format 'thing'.
I know that in the past I suggested an after-production, user defined combination gives more flexibility, IoT things just need a wireless connection... but as a next option, it would be a good option...
-
...miss Espruino in Partnership and Software sections in https://en.wikipedia.org/wiki/Micro_Bit's entry. If Python is there, why not Javascript...
-
...switchable 'wrap/nowrap' would be another option ontop of at what position in line to wrap.
Some smart(er) editors do wrapping with context sensitive indenting of the 2nd to last line of the wrapped line.
Too long lines is anyway a bad thing for quickly reading and grasping... that's the reason news papers have decent narrow columns to create the least tiring experience. Code though is not linear like a novel, therefore some editors have collapse options for code blocks in order to give a better overview on available (vertical) screen space.
-
Two more cents... about save() and power-on reset / power cycle... actually only 1 cent for now, since this post covers only delays in detail. Callbacks are covered in a next post.
@Spocki and @MarcoM - and @Ollie, here you go:
Think about your electronics / hardware: some things you time - delay - with an RC-circuit, and some you setup for trigger - callback - on a rising or falling edge. That sums it up quite simply... and may be a bit too simply, but it is a good starting point.
Complexity arises then when you want to bring a lot of these into sequence and and more so when it is a mix and match. Not much different is it with software. The means are a bit different, but you still need to do the proper setup and do the enabling for the right time.
As you know from electronics, some ICs have power-on / reset sequencing /setup / stabilization dependencies, and you have to wait before you can 'use' the IC. Even Espruino with its STM MC and the elaborate Espruino firmware have a power on reset sequence. The sequence is a combination of various hardware and (low level / internal) software tasks, after which - at last - Espruino firmware invokes
onInit()
function. Therefore, you put the start of your code into this onInit() function (or in function(s) invoked within onInit(). onInit() was there from the beginning, but in recent releasesE.on("init",aFunction)
is available as alternative. All functions you want to have invoked on power-up / reset you define and register with, for example, something like this:var globalVarSetInFuncA = 0; var globalVarSetInFuncB = 0; function yourStartFuncA() { globalVarSetInFuncA = 10; } function yourStartFuncB() { globalVarSetInFuncB = globalVarSetInFuncA + 5; } function yourStartFuncC() { console.log(["A: ",globalVarSetInFuncA," , B:",globalVarSetInFuncA].join("")); } E.on("init",yourStartFuncA); E.on("init",yourStartFuncB); E.on("init",yourStartFuncC);
All function registered with
E.on("init",...)
are also invoked after Espruino is ready for you in the same sequence as you registered them. The output in the console is:A: 10, B: 15
Doing the same with
onInit()
instead ofE.on("init",...)
looks like this:var globalVarSetInFuncA = 0; var globalVarSetInFuncB = 0; function yourStartFuncA() { globalVarSetInFuncA = 10; } function yourStartFuncB() { globalVarSetInFuncB = globalVarSetInFuncA + 5; } function yourStartFuncC() { console.log(["A: ",globalVarSetInFuncA," , B:",globalVarSetInFuncA].join("")); } function onInit() { yourStartFuncA(); yourStartFuncB(); yourStartFuncC(); }
You can run the examples above one by one by pasting them into the editor pane of the Espruino Web IDE and then upload them to the board.
All the above works nicely - in intended sequence - when these functions are simple, blocking, synchronous function. Blocking means:
- While the function is running, nothing else of your code is running.
- The function completes all its related work.
- When function returns control, all results are available.
Unfortunately - and actually fortunately - the world is not that simple: there are functions that need some time to complete and they are implemented the way that they do not block the processor so that the processor can execute other things 'at the same time'... for the common benefit.
Some function may take specified time to execute, and some use some unspecified time - unspecified in the sense that they depend on something else to happen before they can complete their work. Such functions behave asynchronously, non-blocking, which means:
- While the 'function related work is done', other parts of your code can run.
- The function 'moves on' before all its related work is completed.
- When function returns control, not all expected results are available.
That puts the simple - linear, sequently - invocation into trouble!
Assume that the work of
yourStartFuncA()
in above example code takes a second to complete. For example, a sensor needs to 'warm up' before it can perform the (first) measurement and store its value inglobalVarSetInFuncA
. therefore, you need to give it time to do so. We emulate this usingfunction yourStartFuncA() { setTimeout("globalVarSetInFuncA = 10;", 1000); }
This adds some quirk to our simple initialization work, namely:
setTimeout("source code", timeoutTimeInMilliseconds)
:- Tells Espruino to 'delays' the execution of
globalVarSetInFuncA = 10;
by 1000 milliseconds. - Lets the function 'move on' before the execution of the assignment.
- Returns control before the result is available in
globalVarSetInFuncA
.
Which means - with given initialization code using
E.on("init",...)
oronInit() { ... }
- that our console output will show:A: 0, B: 5
Not exactly what we expect...
See yourself by using
onInit()
example with above setTimeout modification in yourStartFuncA().Indeed you will see the unexpected result... But:
We can easily fix that, because we know what really happens. We fix it by delaying 'everything' (in the initialization) after invocation of yourStartFuncA() for as much time it takes to complete the work of yourStartFuncA(). To be safe, we chose 1100 ms. For example (using
onInit()
):function onInit() { yourStartFuncA(); setTimeout("yourStartFuncB(); yourStartFuncC();", 1100); }
Run the the code with above modification in
onInit()
and you will get the expected console output.A: 10, B: 15
Having more than just one 'delaying' function in the initialization sequence will challenge the statement because we know what's going on therefore we can easily take care of dalays.... That's when other options have to come in, and that is a sequencer that allows timing...
For additional details see posts in conversations:
- How to carry out functions in a sequential manner
- sequencing - configure, require(), initializing w/ onInit() and save() ...and bind()
To make a timed initializer - using
gCtl
controller object - in link above: just put thecb()
callback invocation into the (or a)setTimeout()
with the delay as needed, and add the initializer to / register the initializer withgCtl
, just like you do for the other functions:// g(lobal)Log(ging) function: var gLog = function(v) { console.log(v); // NOTE: COMMENT THIS LINE FOR DISCONNECTED RUN }; // g(lobal) C(on)t(ro)l(ler) that handles: // - sequencing of initializers // - run workers after save() and power-cycle / power-on reset var gCtl = { is:[], i:-1 // (registered) i(initializer)s, index for iterating initializers , adi: function(i) { this.is.push(i); } // add/register initializer , ini: function(ok,str) { if (this.i >= 0) { // invoke registered initializers gLog("i[" + this.i + "]" + ((str)?str:"") + ": " + ((ok)?"ok":"failed")); } if (ok) { if (++this.i < this.is.length) { var _t = this; setTimeout(function(){_t.is[_t.i](function(ok,txt){_t.ini(ok,txt);});},1);} } else { /* initializer is[i]() failed */ /* code to handle failures... may be retries/start over... */ } } , run: function() { this.i = -1; this.ini(true); } // run() invoked in onInit() }; var globalVarSetInFuncA = 0; var globalVarSetInFuncB = 0; function yourStartFuncA(cb) { globalVarSetInFuncA = 10; cb(true,"yourStartFuncA"); } function yourStartFuncB(cb) { setTimeout(function(){ globalVarSetInFuncB = globalVarSetInFuncA + 5; cb(true,"yourStartFuncB"); }, 1000); } function yourStartFuncC(cb) { gLog(["A: ",globalVarSetInFuncA," , B:",globalVarSetInFuncA].join("")); cb(true,"yourStartFuncC"); } function yourContinuousOperationFunc(cb) { gLog("Now entering my continuous operation function"); //... } gCtl.adi(yourStartFuncA); gCtl.adi(yourStartFuncB); gCtl.adi(yourStartFuncC); gCtl.adi(yourContinuousOperationFunc); function onInit(){ gCtl.run(); }
NOTE 1: IN NO PLACE YOU SEE save() AS PART OF THE CODE. It is though entered and executed in the console AFTER UPLOADING THE CODE.
Note 2:
Holding on to the module with an extra variable as in the code in the post where the
gCtl
controller object was introduced, may inadvertently - depending on how the module is implemented - hold on to some extra memory. Instead of holding on to the module with an extra variable and later referencing to the mdoule using the variable in the typical.connect()
, again arequire()
could be executed, which - afaie(expect) - would go after the module cache and pull it from there. @Gordon may shed some light on possible memory issues. (In the client-side / browser world where for this approach requirejs.org's require() is used, a once (over the network) required module is cached (in a module cache) and can be referenced quickly again:require( ["modules/path/name1","modules/path/name2"] ,function(module1,module2){ var connection1 = module1.connect(...); module2.connect(connection1); // assume to be a statefull singleton ... });
In some other place, later on, module2 would deliver the very same module - but now connected - as above:
require( ["modules/path/name2"] ,function(module2){ module2.doXyz(...); ... });
node.js (nodejs.org)'s
require()
philosophy - what Espruino leans on - is similar, because it was born server-side and therefore most importantly synchronously vs asrequire()
in the browser, where everything is best pulled asynchronously in order to optimally use the network connections. Thebrowserify
technique is now available to use node.js oriented code also in the browser, in order to use the very same source code both client AND server side. Therefore - in order to share the same Javascript code on Espruino and on your servers, use node.js for your server side implementations rather than any .cgi or Java or PHP or Python or ... You can use Python if you have MicroPython instead of Espruino... MicroPython was born - mind, distance and time wise - not far away from Espruino ;-) - While the function is running, nothing else of your code is running.
-
@MarcoM, ...regarding sequencer...
Think about your electronics: some things you time - delay - with an RC-circuit, and some you setup for trigger on a rising or falling edge. That sums it up quite simply... may be a bit too simply, but it is a good starting point.
Post continues in this post as part of the conversation that deals with
save()
and related things in more details. -
@MarcoM, taking a look at ...explanation what save () does may help you to sort things out.
What I did not cover there is what has to be done to make sure that after reset / power cycle 'ALL things are there to resume normal operation'. For my stuff I use sequencer and initializer that makes that happen.
Decent SW design tries to separate concerns. Even though all things can abstractly be looked at as resources and treated equally - also sequence / time wise - they are not equal, and on top of it they have (inter)dependencies of existence and sequence / time. As if this is not enough complex yet, asynchronousity is added to the . Promises save you from callback hell, but they are not simple heaven either... ;-)
-
-
One disadvantage with the 01 is obviously how breadboard unfriendly it is.
Pretty easy to take care of that as explained in post Bist du nicht willig, dann branche ich Gewalt!.
-
-
Great explanation, and I thinks it - chaining small blocks - is a perfect compromise between contiguous bytes that need always a marks/sweep for memory reuse, even if declared as not-used/refd-anymore by refCount. I moved away from the refCount even though I had the unused memory peaces linked... but going always through the link and find an unused 'string' greater or equal the needs and re-link the remainder was not an option... last but not least because it would have challenged the virtual memory implementation...
In my memory manager (for MRAM/FRAM) I do though so but only limited: since only strings are used, I update as long as the string fits, and if shorter, it has to be short enough to also fit the linking for the remainder...
-
Interesting, nice little caveat...
If mark/sweep is/was trumping reference count (which it should anyway), then (small) 'floating rings/clusters' have/would have 'gone after a while/with many marks and sweeps' anyway... but obviously, leaving other objects with incorrect reference count.
In your breaking example, did
a
got freed? ...andoops
- even though global (I assume) - ended up with wrong reference count?if
a
not global(ly reachable), it would go on a mark/sweep (when mark/sweep has precedence over reference count).Early 90' when writing a oo vm in PL/1 on main pc for use on mainframe, I started out with reference counting... and it got pretty far... but only mark/sweep pulled true... so I abandoned reference count altogether (and would have lost the quick 99% freeing when memory would have been used with linked lists... ;-) ). Reference count was not even usable for validation/confirmation of freeing with single mark/sweep and multiples (I thought) I could not afford (next to the additional storage to remember that the last mark/sweep.
re cheapboard:
I can see the rational for having pins 360 degrees... but for working on a breadboard, it creates the same issues as PICO has. I prefer the DIP pinning, like the espruino_wifi. As a compromise for 360 pinning: if the short side ones are for programming, connecting to USB/Serial, that may work.
Putting headers 'downwards' on long side and headers 'upwards' on short sides, breadboard use is still possible - or - headers on long sides and wires - 'flexible headers' - with tinned (pins at the) end on the short sides: see @Gordon's breadboard friendly version of PICO, w/ picture).
re espruino_wifi:
A) What is the rational to put A8, A10 not on the outer line of pins?
B) I wondered if there is space left for 'wiring' and pads for serial FLASH / EEPROM / RAM / FRAM / MRAM ... since available memory is always a challenge...