What is LED1/A13? (addressing pins via variables)

Posted on
  • I'm very confused by the JS in Espruino for many reasons. I want to start off addressing this simple concern that I believe will also be useful to many others.

    I want to address pins whose names are stored in variables to make the code considerably cleaner.

    Consider this:

    led1 = function() {
      LED1.write(1);
      LED2.write(0);
      LED3.write(0);
      setTimeout(led2, 500);
    }
    
    led2 = function() {
      LED1.write(0);
      LED2.write(1);
      LED3.write(0);
      setTimeout(led3, 500);
    }
    
    led3 = function() {
      LED1.write(0);
      LED2.write(0);
      LED3.write(1);
      setTimeout(led1, 500);
    }
    
    setTimeout(led1, 500);
    

    That's pretty amateur. It would be some much cleaner/DRYer to do:

    clearInterval();
    
    led = function (n) {return (this)['LED'+n];}
    
    setInterval(function() {
      ledCount = 3;
      for (i=1;i<=ledCount;i++){
        l=led(i);
        if(l.read()){
          n = Math.wrap(i, ledCount) + 1;
        }
        led(i).write(0);
      }
      led(n).write(1);
    }, 500);
    

    It would be splendid if that would work in the same way this works:

    ono = function (n) {return (this)['ONO'+n];}
    ONO1 = 'a'
    ONO2 = 'b'
    ONO3 = 'c'
    
    ono(2)
    

    But it doesn't. It seems to require a limited switch/case.

    clearInterval();
    
    led = function(n) {
      switch (n){
        case 1:
          return LED1;
        case 2:
          return LED2;
        case 3:
          return LED3;
      }
    };
    
    setInterval(function() {
      ledCount = 3;
      for (i=1;i<=ledCount;i++){
        l=led(i);
        if(l.read()){
          n = Math.wrap(i, ledCount) + 1;
        }
        led(i).write(0);
      }
      led(n).write(1);
    }, 500);
    

    So, what and where are these LED variables an how can I address them programmatically?

  • Hi,

    The issue you have is that Espruino doesn't keep built-in variables/functions in the symbol table, because there's just not enough RAM. That means that while you can access a variable or function by name, you can't access it as if it were an actual member of the object. It's something I'm hoping to fix - there is a bug open for this but it's a reasonably big change.

    However nothing stops you from doing:

    leds = [LED1,LED2,LED3];
    leds[1].write(1);
    

    or even:

    leds = [LED1,LED2,LED3];
    digitalWrite(leds, 0b100);
    digitalWrite(leds, 0b010);
    digitalWrite(leds, 0b001);
    

    Or if you're still absolutely set on accessing everything by strings, you can do:

    led = function (n) { return eval("LED"+n); }
    

    Note that because Espruino executes from source, there isn't much overhead to running eval

    Hope that helps...

  • [1v64] - Hi,

    from a programming point of view I feel like Richard, and from a resourceful one I understand very well Gordon's implementation. Since obviously RAM is the constraining factor, I might cave in on some of my programming paradigms and expectations. I do not (yet) know engine implementation details, but could imagine a solution with a federated symbol table - (current) RAM symbol table and a ROM symbol table. Lookup - and many other things - will of course have to be adjusted. The 'many other things' is I guess what Gordon refers to by 'reasonably big change'.

    For myself - in the spirit of my user name - I solved the variable access challenge as follows:

    LED is a singleton or class object knows all about the LEDs and has a s(et) method accepting the led i(d) and a on/off (true/false) evaluating v(alue) as parameters:

    var LED = // leds 'class'/singleton object
    { ls: [LED1,LED2,LED3] // leds
    , s: function(i,v) { // 'value tolerant' set method
        this.leds[i].write( (v) ? 1 : 0);
      }
    };
    

    Switching LED2 on and LED3 off is achieved by the following statements:

    LED.s(1,1);
    LED.s(2);
    

    The LED1..LED3 are mapped to the *i*ds 0..2. If you like to preserve the index of the led, modify the set method to look like this:

        this.leds[i - 1].write( (v) ? 1 : 0 );
    

    The 'program' for a running light Red(LED1),Green(LED2),Blue(LED3) is implemented as an singleton object as well:

    var RLL = // running led lights 'class'/singleton object
    { i: 2
    , t: null 
    , r: function(s) { // run(1) = start / run(0) = stop
        LED.s(this.i,0);
        if (s) {
          this.i = (this.i < 2) ? this.i + 1 : 0;
          LED.s(this.i,1);
          this.t = setTimeout("RLL.r(1)",166);
        } else {
          clearTimeout(this.t);
        }
      } 
    };
    

    The commands RLL.r(1); and RLL.r(0); start and stop the running lights. To start and stop the running light with the espruino onboard BTN1 button, add a button up .c() check method that toggles .run() with start/stop. Note the start of the repetitive button checking by its immediate invocation after definition: RLL.c();. The immediate invocation of the repetitive button check can be left out, but has then to be issued as a command.

    Paste the complete code below into the edit area of the IDE, send it to Espruino, and push BTN1 button.

    var LED = // leds 'class'/singleton object
    { ls: [LED1,LED2,LED3] // leds
    , s: function(i,v) { // 'value tolerant' set
        this.ls[i].write((v)?1:0);
      }
    };
    var RLL = // running led lights
    { i: 2         // current LED index
    , t: null      // timeout
    , a: false     // running light state (active)
    , b: false     // last button state (pressed)
    , r: function(s) { // run(1) = start / run(0) = stop
        LED.s(this.i);
        if (s) {
          this.i = (this.i < 2) ? this.i + 1 : 0;
          LED.s(this.i,1);
          this.t = setTimeout("RLL.r(1)",166);
        } else {
          clearTimeout(this.t);
        }
      }
    , c: function() { // check BTN1 (detect 'up' and toggle start/stop
        var b = digitalRead(BTN1) == 1;
        if (this.b && !b) this.r(this.a = !this.a);
        this.b = b;
        setTimeout("RLL.c()",100);
      }
    };
    RLL.c();
    
  • Hi. Thanks for posting up. Having to have the array of LEDs is a bit of a pain...

    It's good that you reminded me - the 'reasonably big change' is actually done now - for instance you can now write process["memory"] and can get a pointer to the actual function. The fact that (this)['LED1']; still doesn't work is a total accident. I've just changed one line and fixed it - so as of version 1v68 this will now work :)

  • [1v68] - Hi,

    very cool! ...weeding out - or at least mitigating - the differences.

    Do I get this right:

    var f = process[memory];
    

    returns the function object (as would

    var f = process.memory;
    

    as well), and a following

    f();
    

    calls the process.memory function - invokes memory method on process object?

    Btw, using Espruino is a very capturing journey! Working on a (Espruino) JS survival micro framework and software buttons (BTN1). Latter would really benefit from being able to tap into/connect to hardware interrupts.

  • Yes, you're spot on about process.memory.

    Have you seen setWatch? That uses interrupts internally.

  • [1v64] - Hi,

    thanks for the friendly - (somewhat) embarrassing ;-) - 'hint' to look for setWatch(). I must have read '...that Espruino does not support hardware interrupts...' in a very early/old (or not very informed/educated) publication. That's why I came up with a software solution - shared below with - for now - only a few comments. I'm a bit embarrassed because my style of approaching a new language (extension) or library is to study the whole API/specs first before venturing into doing by mapping my programming patterns into particular target designs/implementations...

    But I just got the board from Adafruit and had to get my hands dirty to quench my curiosity. The setWatch() will not only simplify the code dramatically, but free up lots of cycles for other things than just watching and interpreting 'bug leg moves' and setting up and handling of timeouts. Interesting is the similarity of some of the parameters - last but not least the debouncer. The debouncer's implementation had to wait until my day job concluded. But with setWatch(), it may not even come to actual life. (So far, the hardware button is not worn out yet and therefore the software buttons works pretty predictable. More details following the code).

    Beside the button handling, the code has some interesting features. Implemented in a so called 'JS survival/micro framework' w/ single global muej are:

    1. Simple, variable controlled LED on/off switching led.(id,booleEvaluating) method.
    2. Software .tme w/out external components to get milliseconds since start-up.
    3. A .pub()(lish) / .sub()(scribe) mechanism for tight integration but loose coupling.
    4. An .itr(array,function) iterator method w/ function(element,index,array){...}
    5. Multiple software buttons using the only one onboard hardware BNT1 button.
    6. Running led light started/stopped and made faster/slower with one (1) single hardware but three (3) software buttons.

    And all this in almost pure, very easy to use, object-oriented fashion.

    var muej = // js survival framework - (f)markus@muet.com = FREE w/ reference
    { lds: [LED1,LED2,LED3]
    , led: function(i,v) { this.lds[i].write((v)?1:0); }
    , tme: 0
    , tms: function(y,m,d,H,M,S) { this.tme = new Date(y,m,d,H,M,S).getTime(); } // (?) 
    , tmr: function(p) { this.tme = this.tme + p; setTimeout("muej.tmr(10)",10); }
    , sbs: {}
    , sub: function(t,f) { var ss = this.sbs[t]; ss = (ss) ? ss : (this.sbs[t] = []); ss.push(f); }
    , pub: function(t,ps) { this.itr(this.sbs[t],function(s){ s.apply(null,ps); }); }
    , itr: function(a,f,s) { if (a) { var i = (s) ? s : 0; var m = a.length; while (i < m) {f(a[i],i,a); i++; } } }
    , log: function(vs) { console.log(vs.join(" ")); }
    };
    
    var RLL = // running led lights
    { P: 160       // on Period [ms]
    , i: 2         // current LED index
    , o: null      // on timeout
    , a: false     // running light state (active)
    , t: function() { this.r(this.a = !this.a); this.l(["i",(this.a) ? "on" : "off"]); } // toggle start/stop
    , f: function() { var p = this.P / 2; this.P = (p < 10) ? 10 : p; this.l(["i","faster",this.P]); } // faster
    , s: function() { this.P = this.P * 2; this.l(["i","slower",this.P]); } // slower
    , r: function(s) { // run(1) = start / run(0) = stop
        muej.led(this.i);
        if (s) {
          this.i = (this.i < 2) ? this.i + 1 : 0;
          muej.led(this.i,1);
          this.o = setTimeout("RLL.r(1)",this.P);
        } else {
          clearTimeout(this.o);
        }
      }
     ,l: function(vs) { muej.log(["RLL: "].concat(vs)); } 
    };
    
    var Btn1 = // SW buttons aka A. Morse by muet
    { B: 100       // min Short press / debounce [ms] - not used yet
    , S: 150       // max Short press [ms]
    , L: 250       // min Long press [ms]
    , P: 220       // min pause [ms]
    , b: 0         // last button bounce state ts [ms]
    , t: 0         // last butten state change ts [ms]
    , p: false     // last button state (pressed)
    , k: ""        // key
    , c: function() { // check BTN1 (detect 'up' and toggle start/stop
        var n = muej.tme; // now
        // muej.led(0,1); muej.led(0);  // dbg 1
        var p = digitalRead(BTN1) == 1;
        // muej.led(1,p); muej.led(1); // dbg 1 , 2
        if (p) { // pressed
            if (!this.p) { // was !pressed
              // muej.led(2,1); muej.led(2); // dbg 1, 2
              this.t = n;
              this.p = true;
          }
        } else { // not pressed
          if (this.p) { // was pressed
            // muej.led(2,1); muej.led(2); // dbg 1
            this.k = this.k + ((n - this.t < this.L) ? "S" :"L");
            this.t = n;
            this.p = false;
          } else { // was !pressed
            if (n - this.t > this.P && this.k.length > 0) {
              var _k = this.k; var _n = n; this.k = ""; this.t = n;
              // muej.led(0,1); muej.led(0); // dbg 2
              // console.log(_k); // dbg 3
              setTimeout(function(){ muej.pub(_k,[_n]); },1);
            }
          }
        }
        setTimeout("Btn1.c()",100);
      }
    };
    
    muej.tmr(0);
    muej.sub("S",function(){ RLL.t(); });
    muej.sub("SS",function(){ RLL.f(); });
    muej.sub("LL",function(){ RLL.s(); });
    Btn1.c();
    

    Originally, I had planned to make the code the subject of a multi part post/series with tutorial like comments... A perfect, fun tutorial that requires just the board and still have a UI and UI interaction. Now it is out there as a whole... and already outdated... :(]
    Time permitting, the series as tutorial may still happen... in a refactored form using setWatch(). For now, just some information about the implementation of the (quasi unlimited number of) software buttons:

    Most interesting is the button BTN1 handling and making it software-wise multiple buttons by loaning the ideas from http://en.wikipedia.org/wiki/Morse_code, developed 1836 by Samuel B. F. Morse... some 'things and thoughts' just never go out of style! - I was trained on it in the army in the signal corps.

    The Bnt1 object detects sequences of short and long presses. It decodes them into strings of "S"s and "L"s; for example, two short presses are decoded into "SS", and a short, long, and short press into "SLS". Once detected, the string is used as the topic to be published with some other parameters, such as the time. Any subscriber will be notified aka the subscribed function is invoked.

    In the running light RLL example, one short press ("S" topic) toggles the lights on and off. Two short presses ("SS" topic) make it run faster, and two long presses ("LL" topic) make it run slower. Try it yourself...

    PS: Gordon, did you notice the IDE's syntax coloring issue in line 8?

  • The whole interrupt thing is a bit confusing - but if you got the idea from the website, let me know where and I'll try and reword it... setWatch has existed since the very start of Espruino.

    Basically: You can't run JavaScript in an interrupt. But that doesn't stop the interpreter itself from using interrupts. When you use setWatch, the interpreter itself uses interrupts to accurately timestamp the event - which is then executed when the current bit of JavaScript finishes.

    For stuff like .itr - does that do something that .forEach doesn't?

    The multiple button thing is a great idea! It'd make a really cool module. Perhaps you could go properly morse code and change "SS" and "LL" to ".." and "--" :)

    I didn't notice a syntax highlighting issue... Could you post a screenshot?

  • The setWatch is absolutely clear... at least to me... that's why I said I'm a bit embarrassed not to look through all the provided functions before venturing into my own implementation.

    Regarding 'Basically': this became clear to me when some release notes mentioned, that the event buffer size was increased from 64 to 128. Before my work life, I was more a hardware mind - tinkering with germanium transistor OC72... (a black painted glass housing filled with transparent heat conducting paste) and I still love hardware, because you can touch it and you actually can 'see' when something goes wrong (...the heat conducting past developed bubbles... another kind of interrupt). Back to the interrupts we are talking about here: When starting to work with micro processors - Zilog Z8 - to build an in-cable protocol converter - interrupt serving time mattered for timing - and still does, just in a different way. Explaining the setWatch to myself would read like this:

    Set watch registers a JS function with the particular pin. Depending on the raise or falling or both, the core - or basic 'operating system' puts the JS function into a first-in-first-out (FIFO) buffer, from which the JS interpreter later pulls it and invokes it.

    Putting the registered function into a FIFO is - time wise - predictable and defines the rate of how many interrupts can be noticed until getting lost, and the FIFO idea assumes that over time all interrupts can be served, because interrupts come not continuously and therefore the serving can be queued. This may though have ramifications for the interrupt service routine (function) implementation with time dependencies, because with the deferred execution 'things' may already have changed again and have to be newly interrogated to make the proper decision. Catching key strokes have a different dependencies, and in order to handle time sensitive sequences, the time of the even is recorded in the FOFO as well.

    Above 'definition' raises several questions: can a pin have multiple watches set? For the sake of simplicity and basic usage I assume not... that's why I use the pub(lish)/sub(scribe) mechanism which allows me to register with one single setWatch the pub, which in its turn reaches any number of subscribers. Another question is: How can I check 'how full' the FIFO is in order to, for example, tell the user 'to slow down' his activities?

    .itr() does about the same as .forEach() within a few 'nuances':

    1. is not object-oriented - to take a 'bad' part head on
    2. performance is of course way inferior to .forEach() - an other drawback
    3. accepts a start index - helpful for an array that has some meta data about itself in the first (few) element(s), because length is not enough, and to make a new subclass is an overkill.
    4. the index of the element processed is passed to the callback function,... because it may be of interest - same as array may be passed for both, .itr() and .forEach(). With .forEach() getting to the index of the element in the callback function amounts to effort of the order of O2. If accomplishable 'natively'/in the interpreter. It would actually be interesting to look a comparison. On the other hand, I'm sure you - Gordon - have a good guts feeling top of your head, which one would win.

    Regarding, for example, "SLS" for S(hort)L(ong)S(hort) versus ".-.", was intentional: SLS can be use to directly address a method/function within an object, and furthermore, allows the dot (.) notation for topic paths in pub/sub. Btw, there is already code on the web that uses the ".-." notation for morse-texting:
    https://github.com/espruino/EspruinoDocsĀ­/blob/master/tutorials/Morse Code Texting.md, and another one that describes a lock coded using more codes: http://www.espruino.com/Single Button Combination Lock.

  • Notice the syntax coloring difference in line 8. It is a .sub method/function property whose name should show in turkish color. May be it is because there was an empty .sbs object property just before...


    1 Attachment

    • Screen Shot 2014-07-21 at 10.12.00 AM.png
  • can a pin have multiple watches set?

    Yes, you just use setWatch multiple times on the same pin and it works fine. You can even have different levels of debounce on each watch.

    How can I check 'how full' the FIFO is

    I'm afraid you can't. You can however use E.getErrorFlags() to see if the FIFO has got so full that data has been dropped.

    Syntax highlighting - that only happens online, and not on the Web IDE? For some reason the forum software thinks it is a reserved word :)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

What is LED1/A13? (addressing pins via variables)

Posted by Avatar for RichardBronosky @RichardBronosky

Actions