• Note: updated code in the replies / incl most recent 2021.0524: option edge:"both" added

    Inspired by the great number of user input/output elements - BTN1 button
    and LEDs LED1, LED2, and LED3 - on the Espruino board right out-of-box,
    my first hands-on experience and fun was coding running led lights -
    red-green-blue-... - controlled by the BTN1 button.

    Light show control 'buttons' include:

    1. Start the light show.
    2. Stop the light show.
    3. Make the light show run faster.
    4. Make the light show run slower.

    For cycling through the LEDs using variable addressing vs. cascaded if then else-if,
    I found related forum entry at http://forum.espruino.com/conversations/­573 -
    What is LED1/A13? (addressing pins via variables). Below you find the code
    for the RLL Running LED Light singleton/class implementation with variable defined LED access and operations controlling buttons.

    The running led light RLL implementation began as a torso that just cycled through
    the LEDs on loading of the code.

    In order to control the light show, following methods got added to RLL singleton/class:

    1. .r(boolean) for run(true/false) aka start and stop: RLL.r(true); RLL.r(false);
    2. .t() for toggling start and stop the light show: RLL.t();
    3. .f() for making the light show run faster: RLL.f();
    4. .s() for making the light show run slower: RLL.s();

    Start/stop / toggling between start/stop was easy by just detecting any press -
    actually release (and stay released for some time) - of the BTN1 button.
    Detection of a transition from pressed to released with staying released for a
    defined debouncing time is managed by a timer with ongoing setTimeout and
    comparing previous versus current button BTN1 state / digitalRead(BTN1).

    Inspired by the lots of timers and setTimeouts that made the running led lights and
    the start/stop/toggle button work, I wanted to use them also for not just only noticing
    button presses and eventually counting them, but also for detecting the duration of
    the presses and sequences of such presses.

    Sequences of short and long presses would allow me to create with one hardware button many software button or commands to control multiple things with one single control. The same thing did Samuel B. F. Morse - see http://en.wikipedia.org/wiki/Morse_code - tocode letters, digits, and special characters into simple sequence of short and long "on"s, send those over a (telephone) line (wire), and create short and long dashes by a pen pushed down on a moving paper stripe by a solenoid.

    Mislead by an inaccurate, early publication stating that Espruino would not support the use of interrupts generated by pin state transitions, I built everything in
    software using just timers / setTimout() and testing pin states w/ digitalRead().

    @Gordon's gentle nudge - Have you seen setWatch? - made me refactor and extend the code in order to became a general purpose SWBtn software button component - which soon will be available as a module.

    function SWBtn(fnc,btn,d) { // SoftWare Butten - constructor args:
      // fnc function to call, btn (default: BTN1), disabled (default: false)
      this.f = (fnc) ? fnc : function(){};
      this.b = (btn) ? btn : BTN1;
      this.t = null; // timeout after last release
      this.k = "";   // key build-up (sequence of S and L aka Morse's dots and dashes)
      this.w = null; // set watch
    SWBtn.prototype.C = // config
      { B: 20    // debounce [ms]
      , L: 0.250 // min Long press [s]
      , P: 220   // min Pause [ms]
      , D: 10    // fnc exec delay [ms]
    SWBtn.prototype.disable = function(b) { // disable
      if (b) { 
        if (this.w) { 
          this.w = null; 
      } else {
        if (!this.w) {
          var _this = this;
          this.w = setWatch(function(e){ _this.c(e); }, this.b
              , { repeat:true, edge:"both", debounce:_this.C.B });
    SWBtn.prototype.c = function(e){ // change of state - called by set watch
      // e = {time:float, lastTime:float, state:bool}.
      if (e.state) { // press
        if (this.t) clearTimeout(this.t);
      } else { // release
        this.k = this.k + ((e.time - e.lastTime < this.C.L) ? "S" :"L");
        var _this = this;
        this.t = setTimeout(function(){ _this.e(); }, this.C.P);
    SWBtn.prototype.e = function() { // ended - called by timeout > Pause
      this.t = null;
      var _k = this.k;
      if (_k.length > 0) {
        this.k = "";
        var _this = this;
        setTimeout(function(){ _this.f(_k); },this.C.D);

    (1 of 2) - to be continued...
    Software Buttons - Many buttons from just one hardware button


Avatar for allObjects @allObjects started