Software Buttons - Many buttons from just one hardware button

Posted on
Page
of 2
/ 2
Next
  • Update 2022.0410: see post #29
    Update 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
      this.disable(d);
     }
    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) { 
          clearWatch(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

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

    Constructor and methods:

    • SWBtn(fnc,btn,d) is the software button class or constructor method. It accepts three (3) parameters:

      1. fnc one parameter function to be invoked on press sequence detection.
      2. btn symbol identifying the button or input pin; default: BTN1 / B12.
      3. d boolean disabled; default: false; allows to create button in disabled state.

    • .disable(b) method disables button according to passed b boolean interpreted value.

    • .c(e) method is invoked on every button / pin state change - press or release.

    • .e() method is invoked on every sequence detection and invokes (timer delayed) the function provided at construction or as set later.

    • .C prototype object is the configuration object holding the timing values shared by all instantiated SWBtn software buttons.

    Basic usage examples:

    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k);
      });
    

    The code above creates an enabled software button. On press sequence detection it invokes the passed anonymous function with the pressed sequence as parameter value, for exmple "S" for one short press, "SS" for two, and "SL" for one short and one long press, etc. The implementation of the function writes the press sequence to the console:

    `
    1v64 Copyright 2014 G.Williams
    >echo(0);
    =undefined
    BTN1 detected S
    BTN1 detected SS
    BTN1 detected SL
    >
    `
    

    You may wonder why the button's returned key value for the press sequence is not a series of dots (.) and dashes (-). The reason is simple: the key as returned can be used as valid property or function name (of an object).

    For example, the following code - entered into the IDE command pane after sending the SWBnt class code to the Espruine - counts how many of the various sequences have been pressed,

    var presses = {};
    var mySWBtn = new SWBtn(function(k){ 
        presses[k] = ((presses[k]) ? presses[k] : 0) + 1;
      });
    

    and entering the following code into the IDE command pane gives a report about the counted various press sequences:

    for (var v in presses) console.log(
        [v," was pressed ",presses[v]," time(s)."].join("")
      );
    
    `
    S was pressed 6 time(s).
    L was pressed 5 time(s).
    LSL was pressed 1 time(s).
    SLLS was pressed 2 time(s).
    `
    

    To print this report and reset the counters on 'pressing' the "SSSSS" software button - 5 short presses of Btn1 - set following function on existing mySWBtn (or pass it on in a new creation).

    mySWBtn.f = function(k) {
        presses[k] = ((presses[k]) ? presses[k] : 0) + 1;
        if (k == "SSSSS") { // 'key' for triggering the presses report
            console.log("*** Presses since creation/last report:");
            for (var v in presses) console.log(
                [v," was pressed ",presses[v]," time(s)."].join("")
            );
            presses = {}; // reset counters
        }
      };
    

    The above example shows that the function can be set or changed on a button after its creation, and so can you also change the C(onfiguration) parameters for the timings - but remember, you will change the timing for every SWBtn - because they shard the one and only C(onfiguration).

    To control the RLL running led light shown in forum comment at What is LED1/A13? (addressing pins via variables),
    replace the Btn1 code with the SWBtn code, and replace the last line with

    var rllBtn = new SWBtn(function(k){ muej.pub(k); });
    

    Do disable and re-enable the button, invoke the .disable() method:

    rllBtn.disable(true); // disable
    
    rllBtn.disable(false); // re-enable
    
    

    Latter is the same as:

    rllBtn.disable(); // re-enable - :-o - oops...
    

    Above is not very obvious, but still as designed: the missing boolean argument evaluates to false... ;-)

    A button can be created in disabled state:

    var rllBtn = new SWBtn(function(k){ muej.pub(k); },true);
    

    This is especially helpful when other initialization processes have to happen or complete before user input can be accepted. To enable the button at desired point in time, just invoke the .disable() method:

    rllBtn.disable(false);  // enable
    
  • Thanks! It's really good to have broken this out into an Object of its own...

    Do you think you could try packaging it up into a module? Instructions here.

    I guess it might be handy to rename SWBtn.prototype.d to SWBtn.prototype.disable... It might make code using it a bit more readable...

  • .d() changed to - more readable - .disable().

    Certainly, I will package it up as a module.

  • SWBtn hardened and commented in preparation for publishing as a module.

    This is a Request For Comments (RFC). Feedback for code fixes and comment/wording and functional enhancements are very much appreciated.

    /* Copyright (c) 2014 allObjects. See the file LICENSE for copying permission. */
    
    /*
    Software Buttons - Many buttons from just one hardware button
    
    SWBtn class - instances of SWBtn makes many buttons out of 
    just one hardware button by detecting sequences of short and 
    long presses and passing them on to a user defined, dispatching 
    callback function.
    
    For example, one long press turns LED1 on, and one long and 
    one short turn it off. Two long presses and two long presses 
    followd by a short one do the same to LED2, etc.
    
    Each detected sequence is passed in form of a string of "S"s 
    and "L"s to the user provided callback function. The callback 
    function uses the string as a key to call assigned functions
    like dispatcher.
    
    Usage example:
    
    require("SWBtn");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log and/or dispatch
      });
    
    */
    
    /**
    SWBtn - SoftWare Butten 'class'
    - constructor - accepts these arguments:
      - fnc - (optional) anonymous, one argument accepting dispatching callback function
      - btn - (optional) button/pin id - default is BTN1
      - d   - (optional) boolean interpreted disable flag - default false
              Allows button to be creted in disabled state in order 
              for (other) initializations to complete before being
              enabled with .disable(0) / .disable(false) / .disable()
              method invocation.
    - instance properties:
      - f - storex passed or set dispatching callback function
      - b - storex button / pin id
      - d - stores disabled (status) of button
      - t - xgofdx timeout for sequence end / pause detection
      - k - stores build-up key store holding on to the "S"s and "L"s of a squence
      - w - stores hold on to the watch set with setWatch()
    */
    function SWBtn(fnc,btn,d) {
      this.f = (fnc) ? fnc : function(){};
      this.b = (btn) ? btn : BTN1;
      this.t = null;
      this.k = null;
      this.w = null;
      this.disable(d);
     }
    
    /**
    .C - Constants / Configuration - defining the timings 
    - shared by all instances of SWBtn:
      - B - integer - debounce [ms]
      - L - float   - min Long press [s]
      - P - integer - min Pause [ms]
      - D - integer - delay of fnc function invocation [ms]
      Pressing a button / keeping a pin low for .C.L seconds or more is detected
      as a long press when unpressed / released / pin turns high and and adds an 
      "L" to .k key (press sequnce) - otherwise a short press is detected and an 
      "S" is adde - and the .t timeout is set with .C.P Pause time and .e() ended
      call back for press sequence end detection and processing (invocation of
      user defined - ,k key decoding dispatch - function).
    */
    SWBtn.prototype.C =
      { B: 20
      , L: 0.250
      , P: 220
      , D: 10
      };
    
    /**
    .disable(b) - disables/enables button
    - method - accepts one argument
      - b - boolean - (optional) boolean interpreted disable flag - default false
      Used to (temporarily) disable the button (also used in constructor).
      It clears/sets the hardware / pin watch using clearWatch() / setWatch() functions.
      NOTE1: When button is disabled while press sequence is going on, sequence end is
        not detected, but partial sequence is still stored in .k key property (but does 
        not include an ongoing press / pin low). 
      NOTE2: The .k key propery is cleared (set to "") when button is (re-)enabled.
      NOTE3: any passed parameter that evaluates to false in an 'if (parameter)' and 
        omission of parameter enable the button: .disable(false), .disable(0),
        .disable(""), .disable(''), .disable(null), .disable(undefined), and .disable(),
        all these invocations enable the button... ;)
    */
    SWBtn.prototype.disable = function(b) {
      if (b) {
        if (this.w) { 
          this.d = true;
          clearWatch(this.w);
          this.w = null; 
          if (this.t) {
            clearTimeout(this.t);
            this.t = null;
          }
        }
      } else {
        if (!this.w) {
          this.d = false;
          this.k = "";
          var _this = this;
          this.w = setWatch(function(e){ _this.c(e); }, this.b
                     , { repeat:true, edge:"both", debounce:_this.C.B });
        }
      }
     };
    
    /**
    .c(e) - button/pin button/pin state change callback - invoked by Espruino
    - method - accepts one e event argument (object) 
      Espruino reference for .setWatch() defines e event object as:
      - time     - float   - time of this state change [s]
      - lastTime - float   - time of last such state change [s]
      - state    - boolean - current state of the button / pin
      Notes button/pin status and - on unpress/release state - 
      appends "L"(ong) or "S"(short) to .k key (sequence) and 
      sets .t timeout to .C.P Pause for sequence end detection
    */
    SWBtn.prototype.c = function(e){ // change of state - called by set watch
      if (e.state) {
        if (this.t) {
          clearTimeout(this.t);
          this.t = null;
        }
      } else {
        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);
      }
     };
    
    /**
    .e() - sequence ended timeout callback - invoked by .t timout set in .c(e)
    = method = accepts no arguments
      Marks detected end of press sequence and invekes user provided .f 
      callback function in a setTimeout() with .C.D delay.
    */
    SWBtn.prototype.e = function() {
      this.t = null;
      var _k = this.k;
      if (_k.length > 0) {
        this.k = "";
        var _this = this;
        setTimeout(function(){ _this.f(_k); },this.C.D);
      }
     };
    
  • Testing robustness of SWBtn's .disable() method

    Copy SWBtn and test code at the bottom of this post into edit pane and send it to Espruino.

    Basically, there will be intervals with enabled and disabled SWBtn. During and straddling those intervals you will perform press sequences.

    Be prepared to timely perform press sequences as instructed below while test code shows passed seconds in command window.

    The test code will do this:

    1. Starts mySWBtn in enabled state and shows passed seconds in command pane
    2. Disables mySWBtn after 7 seconds (7 seconds after start)
    3. Re-enables mySWBtn after 7 more seconds (14 seconds after start)

    You perform these timely sensitive press sequences:

    1. About 1 to 2 second after test code started, begin and complete a press sequence with a few short presses.
      2 . About 4 to 5 seconds after test code started, begin a press sequence with at least four (5) long presses that lasts beyond second 7 or 8 after start. Try to make at least three (3) or more long presses by second 7.
    2. Before 14 seconds after test code started, perform a complete press sequence with a few short presses. Note that press sequence has to be completed before second 10 after start.
    3. After 14 seconds the test code started, perform a press sequence with just one (1) or two (2) long presses.

    The output you will see in the command pane should look like this (on [1v67] - at this time the most recent version):

    `
    ...
    Second: 1
    Second: 2
    BTN1 detected SSS
    Second: 3
    Second: 4
    Second: 5
    Second: 6
    Second: 7
    mySWBtn disabled - Partial press sequence:  .k={LLL}
    Second: 8
    Second: 9
    Second: 10
    Second: 11
    Second: 12
    Second: 13
    Second: 14
    mySWBtn re-enabled
    Second: 15
    Second: 16
    BTN1 detected LL
    Second: 17
    Second: 18
    ...
    `
    

    ...which means, that:

    1. Partial long presses sequence is noticed on disablement
    2. Short presses sequence between second 7 and 14 is not noticed
    3. Press sequences after re-enablement are not affected by partial sequences

    Note1: press the reset button to stop the test code's seconds to keep going on. Disconnect is not enough (After reconnect, passed seconds output shows at once). Of course, powering off does stop it too,

    Note2: At one time I got Uncaught Error: Unknown Timeout in the command pane (plus details about where in the code it happened) - this happened before I got all my robustness ducks in a row. But it raised the question:

    Should clearTimeout(withInvalidTimeoutId) or clearTimeout(withTimedOutTimeoutId) even 'throw' this or any error? - afaik, JavaScript in browsers do not 'throw' any error under these circumstances... may be those implementations arn't up to spec (yet)... and what about me... - :( oooops! - ?

    For fun, extend (and post your) test that begins with a disabled button and shows that it is disabled and starts observing press sequences when enabled.

    Notice that when a press sequence is going on and the button is (re-)enabled, only the part that leaps into the (re-)enabled period is picked up... and if the (re-)enablement happens during a long press, this long press may be detected as a short press.

    So far, only software and a (external) button operator are used for the testing. Adding some hardware - such as a wire that connects an output (configured) pin with and input (configured) pin or even using another Espruino board - and creating a SWBtn on the output pin controlled input pin allows to write complete software driven testing of the SWBtn. For sure something worth to be tackled at a later point in time.

    var mySWBtn = new SWBtn(function(k){ // create mySWBtn (on BTN1 and enabled)
        console.log("BTN1 detected " + k);
      });
    var secs = 0, t1 = 7000, t2 = 7000; // define seconds and t1/t2 inteval timings
    setInterval(function() { // get the seconds going 1,2,3...
      secs = secs + 1;
      console.log("Second: " + secs);
     },1000);
    setTimeout(function(){ // get the intervals t1 and t2 going with disable and re-enable
      mySWBtn.disable(1);
      console.log(["mySWBtn disabled - Partial sequence: {",mySWBtn.k,"}"].join(""));
      setTimeout(function(){
        mySWBtn.disable();
        console.log("mySWBtn enabled");
      },t1); // 7 [s]
     },t2); // 7 [s]
    
  • how to use this in some simple example with LED function?
    i mean let say when 1 short press (SS) is detected then turn on LED1, when LL turn off etc.?

  • Example below assumes default button BTN1.

    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k);
        if/*sel*/ (k === "S") { LED1.set(); // one short press on
        } else if (k === "L") { LED1.reset(); // one long press off
        }
      });
    

    If you have a button on a pin - for example, A8 - use this:

    var mySWBtn = new SWBtn(function(k){
        console.log("Button on A8 detected " + k);
        if/*sel*/ (k === "S") { LED1.set(); // one short press on
        } else if (k === "L") { LED1.reset(); // one long press off
        }
      },A8);
    

    More sophisticated, you can go with this:

    var fs = // functions 
    { "S": function() { LED1.set(); }
    , "L": function() { LED1.reset(); }
    };
    var mySWBtn = new SWBtn(function(k){ if (fs[k]) fs[k](); }, A8);
    

    Note: Code only partially tested. ;-)

  • thank you, I'll test it

  • @allObjects did you ever submit this as a module?

  • Yes... but I posted on there twice asking if the webpage that goes with it could be fixed - no reply yet :)

    @allObjects would you mind fiddling with the documentation file?

    For now, it can be used with:

    var SWBtn = require("https://raw.githubusercontent.c­om/muet/EspruinoDocs/master/modules/SWBu­tton.js");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
      });
    
  • @Gordon, yes. My working w/ git needs a bit shoe-horning. - Thanks for the great example.

  • @allObjects - wow - this is very helpful and can be used as button extension for Puck.js

    I use it like this

    /* 
    S :  single short touch to switch on 
    SS : double short touch to switch off
    */  
    var SWBtn =require("https://raw.githubusercontent.­com/muet/EspruinoDocs/master/modules/SWB­utton.js");
    
    var mySWBtn = new SWBtn(function(k){
          console.log("BTN1 detected " + k); // log key press pattern
          if (k === "S"  )
            LED3.set();
          else if (k === "SS" ) 
            LED3.reset();
    });
    

    Thanks for sharing !

  • I was wondering how to code given behaviour:
    increment counter when button is pressed
    keep incrementing when button is held down

    such a behaviour is often implemented for setting hours etc.

    I've tried this:

    var presses=0;
    //presses count
    pinMode(A7, "input_pulldown");
    // software pulldown
    
    setWatch(function(){
      clearInterval();
      presses++;
      setInterval(function(){
        if(digitalRead(A7)) {
          presses++;
          console.log(presses);
        } else {clearInterval();}
      }, 500);
      console.log(presses);
    },A7,{ repeat : true, debounce : 30, edge : "rising" }  );
    

    It works... but it's clearing other intervals.
    Is there a cleaner/better way to code this?
    If you know how to code it better, or have your own solution please share

    Thanks

  • You can identify an interval by naming it
    and use the name to clear only that interval

    var id = setInterval(function () { print('foo'); }, 1000);
    
    clearInterval(id)
    

    https://www.espruino.com/Reference#l__gl­obal_setInterval

    https://www.espruino.com/Reference#l__gl­obal_clearInterval

  • @ancienthero, the idea of holding and measuring how long a button is held pressed conflicts with the idea of detecting series off short and long presses. To cover holding and holding how long beyond a short or long press has to be managed by some other module.

  • EDIT / Correction to previous post:

    @ancienthero, ...first shot, my answer in previous post is correct... BUT:

    A defined sequence of short and long presses could be the prefix to the desired behavior. After the introductory sequence - like a preamble - the sw button would shift gear into a state which only detects the press of the last button and then return back to regular button behavior...

    While listening to myself, I see that there is no need for any special thing, except a vary small change to the existing code / module: Passing the last press time to the function invoked - TO ALL INVOKED FUNCTIONS as a first argument... or pass the button object and have the button remember the time of the last press with a method to go for... or just have the button to remember the last press time.... Just remembering the last press time is the easiest to implement - no real change is needed for the interface or in the invocation pattern.

    Here the just two (2) changes in the module code and usage example how to take advantage of that new feature of the SWButton:

    Change in the constructor - add instance variable this.p = 0; to hold on to the time - in seconds - of the last press:

    function SWBtn(fnc,btn,d) {
      this.f = (fnc) ? fnc : function(){};
      this.b = (btn) ? btn : BTN1;
      this.t = null;
      this.k = null;
      this.w = null;
      this.p = 0; # will hold on to the time of the last press
      this.disable(d);
     }
    

    Capturing the last press time on watch event in (private) method .c(...) (~ line 131 in code in post #5:

    
    ...
    SWBtn.prototype.c = function(e){ // change of state - called by set watch
    ...
        this.k = this.k + (((this.p = e.time - e.lastTime )< this.C.L) ? "S" :"L"); // store p-ress time [s]
    ...
    

    Using the captured press time in the called function to, for example turn red LED1 on for the same time, so you can easy verify if it works:

    var SWBtn = require("https://raw.githubusercontent.c­om/muet/EspruinoDocs/master/modules/SWBu­tton.js");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "S"  ) { LED1.set(); setTimeout(function(){
                                  LED1.reset();},mySWBtn.p);
        } else if (k === "L"  ) { LED1.set(); setTimeout(function(){
                                  LED1.reset();},mySWBtn.p);
        } else if (k === "SS" ) { LED2.set(); setTimeout(function(){
                                  LED2.reset();},mySWBtn.p);
        } else if (k === "SL" ) { LED2.set(); setTimeout(function(){
                                  LED2.reset();},mySWBtn.p);
        } else if (k === "SLS") { LED1.set(); LED2.set(); setTimeout(function(){
                                  LED1.reset();LED2.reset();},mySWBtn.p);
        } else if (k === "SLS") { LED1.set(); LED2.set(); setTimeout(function(){
                                  LED1.reset();LED2.reset();},mySWBtn.p);
      });
    

    This mySWBtn behaves as follows:

    1. 1 short press turns the red LED1 on for the same time as the short press.
    2. 1 long press turns the red LED1 on for the same time as the long press- the long press has to be the minimum as defined in C.L.
    3. 1 short and a 2nd short press turns green LED2 on for the same time as the 2nd short press.
    4. 1 short and a 2nd - long - press turns green LED2 on for the same time as the 2nd - long - press.
    5. 1 short and 1 long press and then the 3rd - short - press turns both red and green LEDs on for the same time as the 3rd - short - press.
    6. 1 short and 1 long press and then the 3rd - long - press turns both red and green LEDs on for the same time as the 3rd - short - press.

    Calling the same function for both - short and long - last pressesmakes it possible to cover the complete time span...

    Practical application is setting a value higher / lower, duration longer or shorter in analogue mode rather than in discrete steps.

    If the pressing time should directly change something while press is still going on, then the very first suggestion of shifting from normal short-long detection mode into a separate mode could do so. The changes would be a bit more, but still manageable. I amy come back to this in a future post.

  • var SWBtn = require("https://raw.githubusercontent.c­om/muet/EspruinoDocs/master/modules/SWBu­tton.js");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
      });
    

    could anyone explain how to change default button pin?

  • @bigplik, you just pass a second argument into the constructor. The second argument is the button / pin and is optional. If absent, it is BTN1.

    var SWBtn = require("https://raw.githubusercontent.c­om/muet/EspruinoDocs/master/modules/SWBu­tton.js"); 
      , mySWBtn0 = new SWBtn( function(k) {
          console.log("BTN1 detected " + k); // log key press pattern
          if        (k === "L"  ) { LED1.set();
          } else if (k === "LS" ) { LED1.reset();
          } else if (k === "LL" ) { LED2.set();
          } else if (k === "LLS") { LED2.reset();
          }
        }   
       ,<MyButtonOrPin>
       ); 
    

    The constructor accepts three arguments of which latter two are optional:

    var SWBtn = require("https://raw.githubusercontent.c­om/muet/EspruinoDocs/master/modules/SWBu­tton.js");
    var mySWBtn = new SWBtn(function(s){
        console.log("BTN1 detected " + k); // log key press pattern
          if        (s === "L"  ) { LED1.set();
          } else if (s === "LS" ) { LED1.reset();
          } else if (s === "LL" ) { LED2.set();
          } else if (s=== "LLS") { LED2.reset();
          }
        }          // --- callback function called with string of Ls and Ss
                   //     representing the sequence of Long and Short presses
     , optBtnOrPin // --- button or pin to watch, optional, default is BTN1
     , optDisabled // --- boolean to start enabled or disabled, optional,
                   //     default is false (false-y, which means enabled)
     );
    

    If you construct the software button disabled, you can enable - as well as disable - it anytime in the code:

    mySWBtn.disable(false); // enable button
    mySWBtn.disable() // same as above: enable button
    mySWBtn.disable(true) // disable button
    

    With application code controlled enabling/disabling of the SW Button you can ensure unambiguous UI behavior.

    Since SWButton (what you get from module) is a constructor, you can setup multiple SW Buttons, like:

    var swBtn1 = new SWButton(...,pin3)       // sw btn 1 enabled on pin3
      , swBtn2 = new SWButton(...,pin12,true) // sw btn 2 disabled on pin 12
    

    To see the code, just open in the browser the url in the require. You will notice that it includes much more information - including inline doc and examples. SW button is quite 'open': you even can configure the (detection) timings meeting custom requirements:

    /* Copyright (c) 2014 Markus Muetschard. See MIT LICENSE for copying permission. */
    
    /*
    SWBtn class - instances of SWBtn makes many buttons out of 
    just one hardware button by detecting sequences of short and 
    long presses and passing them on to a user defined, dispatching 
    callback function.
    
    For example, one long press of Espruino onboard BTN1 turns LED1 
    on, and one long and one short turn it off. Two long presses and 
    two long presses followed by a short one do the same to LED2, etc.
    
    Each detected sequence is passed in form of a string of "S"s 
    and "L"s to the user provided callback function. The callback 
    function uses the string as a key to call assigned functions
    like dispatcher.
    
    NOTE: Works on 1v72+ due to export and usage pattern
    
    Usage examples:
    
    // 1st example just logs the key press pattern
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
      });
    
    // 2nd example tests key pattern and executes accordingly
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
      });
    
    // 3rd example avoids chained ifs by:
    var functions = // function names match key press pattern
    { L:   function(){ LED1.set();   }
    , LS:  function(){ LED1.reset(); }
    , LL:  function(){ LED2.set();   }
    , LLS: function(){ LED2.reset(); }
    };
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log detected key pattern and...
        if (functions[k]) { functions[k](); }; // ...dispatch if defined
      },BTN1,false);
    
    // 4th example assumes a push button on pin B2 connecting to +3.3
    pinMode(B2,"input_pulldown");
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("Btn on B2 detected " + k); // log key press pattern
      },B2);
    
    */
    
    /**
    SWBtn - SoftWare Butten 'class'
    - constructor - accepts these arguments:
      - f - (optional) anonymous, one argument accepting dispatching callback function
      - b - (optional) button/pin id - default is BTN1
      - d - (optional) boolean interpreted disable flag - default false
            Allows button to be creted in disabled state in order 
            for (other) initializations to complete before being
            enabled with .disable(0) / .disable(false) / .disable()
            method invocation.
    - instance properties:
      - f - storex passed or set dispatching callback function
      - b - storex button / pin id
      - d - stores disabled (status) of button
      - t - xgofdx timeout for sequence end / pause detection
      - k - stores build-up key store holding on to the "S"s and "L"s of a squence
      - w - stores hold on to the watch set with setWatch()
    */
    var SWBtn = function(f,b,d) {
      this.f = (f) ? f : function(){};
      this.b = (b) ? b : BTN1;
      this.t = null;
      this.k = null;
      this.w = null;
      this.disable(d);
     }
    
    /**
    .C - Constants / Configuration - defining the timings 
    - shared by all instances of SWBtn:
      - B - integer - debounce [ms]
      - L - float   - min Long press [s]
      - P - integer - min Pause [ms]
      - D - integer - delay of fnc function invocation [ms]
      Pressing a button / keeping a pin low for .C.L seconds or more is detected
      as a long press when unpressed / released / pin turns high and and adds an 
      "L" to .k key (press sequnce) - otherwise a short press is detected and an 
      "S" is adde - and the .t timeout is set with .C.P Pause time and .e() ended
      call back for press sequence end detection and processing (invocation of
      user defined - ,k key decoding dispatch - function).
    */
    SWBtn.prototype.C =
      { B: 20
      , L: 0.250
      , P: 220
      , D: 10
      };
    
    /**
    .disable(b) - disables/enables button
    - method - accepts one argument
      - b - boolean - (optional) boolean interpreted disable flag - default false
      Used to (temporarily) disable the button (also used in constructor).
      It clears/sets the hardware / pin watch using clearWatch() / setWatch() functions.
      NOTE1: When button is disabled while press sequence is going on, sequence end is
        not detected, but partial sequence is still stored in .k key property (but does 
        not include an ongoing press). 
      NOTE2: The .k key property is cleared (set to "") when button is (re-)enabled.
      NOTE3: any passed parameter that evaluates to false in an 'if (parameter)' and 
        omission of parameter enable the button: .disable(false), .disable(0),
        .disable(""), .disable(''), .disable(null), .disable(undefined), and .disable(),
        all these invocations enable the button... ;)
    */
    SWBtn.prototype.disable = function(b) {
      if (b) {
        if (this.w) { 
          this.d = true;
          clearWatch(this.w);
          this.w = null; 
          if (this.t) {
            clearTimeout(this.t);
            this.t = null;
          }
        }
      } else {
        if (!this.w) {
          this.d = false;
          this.k = "";
          var _this = this;
          this.w = setWatch( function(e){ _this.c(e); }, this.b
                     , { repeat:true, edge:"both", debounce:_this.C.B } );
        }
      }
     };
    
    /**
    .c(e) - button/pin button/pin state change callback - invoked by Espruino
    - method - accepts one e event argument (object) 
      Espruino reference for .setWatch() defines e event object as:
      - time     - float   - time of this state change [s]
      - lastTime - float   - time of last such state change [s]
      - state    - boolean - current state of the button / pin
      Notes button/pin status and - on unpress/release state - 
      appends "L"(ong) or "S"(short) to .k key (sequence) and 
      sets .t timeout to .C.P Pause for sequence end detection
    */
    SWBtn.prototype.c = function(e){ // change of state - called by set watch
      if (e.state) {
        if (this.t) {
          clearTimeout(this.t);
          this.t = null;
        }
      } else {
        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);
      }
     };
    
    /**
    .e() - sequence ended timeout callback - invoked by .t timeout set in .c(e)
    - method - accepts no arguments
      Marks detected end of press sequence and invokes user provided .f 
      callback function in a setTimeout() with .C.D delay.
    */
    SWBtn.prototype.e = function() {
      this.t = null;
      var _k = this.k;
      if (_k.length > 0) {
        this.k = "";
        var _this = this;
        setTimeout(function(){ _this.f(_k); },this.C.D);
      }
     };
    
    exports = SWBtn;
    
  • in reply to @allObjects
    sorry but still not clear,
    I used your parts of code above and still have some errors

    do I need some setWatch to make it run?

    is this example below enough to run or there is missing part?
    in my case this code doesn't detect any button

    var SWBtn = require("https://raw.githubusercontent.c­om/muet/EspruinoDocs/master/modules/SWBu­tton.js");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
      });
    
  • Assume, you have an Espruino Pico, and you have added a button on pin B3 to 3.3V, you proceed as follows:

    pinMode(B3,"input_pulldown");
    var SWBtn = require("SWButton");
      , mySWBtn = new SWBtn(function(k){
        console.log("Btn on B3 detected " + k); // log key press pattern
      },B3);
    

    mySWBtn is now working on pin B3. You need to set the pin mode, otherwise you do not get predictable results, because you put a watch on a float.

    PS: above example assumes also that you download / copy-paste the SWButton code from url on git into your modules folder with the name WSButton.js. You can of course keep the url in the require(...).

  • i have espruino Original, and with code I placed above it is not working with button installed on board, I can call this button from IDE by BTN and BTN1 (and button works with other examples), but not with the code I placed above

    i also try it like this, and still doesn't work

    pinMode(B12,"input_pulldown");//i understand B12 is corresponding to BTN or BTN1 on espruino code
    
    var SWBtn = require("https://raw.githubusercontent.c­om/muet/EspruinoDocs/master/modules/SWBu­tton.js");
    
    var mySWBtn = new SWBtn(function(k){
        console.log("Btn on B3 detected " + k); // log key press pattern
      },B12);
    
  • @bigplik, did you add a physical button on pin B3 or B12 as shown in https://www.espruino.com/Button ?

    Btw, on Original Espruino board, B12 is the same as BTN1.

    If this is not working, have no idea (yet) why not... :

  • ...checking right now with an Original Board, version 1.4b, which is on 2v08... but will get most recent after doing some tests w/ this combination. I'll let you know about how it goes.

  • Mystery solved... there is an issue with the setWatch() function: reference documentation of setWatch() has obviously changed. Originally, both pins AND built-in buttons had for edge option the same default: "both"... but at some time since this was built - almost 7 years ago Thu, Jul 24, 2014 1:25 AM • #1 - 'it' was decided to change that... and handle built-in buttons differently from pins... otherwise option object in Line 25 would include ..., edge:"both",...... last but not least, this code was developed on an original board using BTN1 / pin B12 as default and reference documentation valid at that time... haha... ---

    How to fix (quickly)? Clone the module to local and add the edge:"both" option...

    TL;DR:

    With the test rig below I just tested the original code - no line 144 (no edge option specified) - as inline module and got this output, which indicated that the button BTN1 works just as expected, but SWBtn just does not get events in button release... Then read the doc... n-times... until reading finally the fine print. This is the test output that shows that SWBtn never fires a .c(change state) method on button release:

    `____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v08 (c) 2019 G.Williams
    >
    >t1()
    =undefined
    >t2()
    =undefined
    >t3()
    SWBtn constructor: function(): function (k) {
      console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
    } , button/pin: undefined , disabled: undefined
    SWBtn disable(): disable: undefined
    =undefined
    >mySWBtn
    =SWBtn: {
      f: function (k) { ... },
      b: B12, t: null,
      k: "",
      w: 2, d: false }
    BTN1:  1
    SWBtn c(changeState)(): event: { "state": true, "lastTime": undefined, "time": 1621850579.22702693939, "pin": B12 }
    BTN1:  0
    BTN1:  1
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621850579.64894866943, "time": 1621850585.34660243988, "pin": B12 }
    BTN1:  0
    BTN1:  1
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621850585.73445510864, "time": 1621850588.07980346679, "pin": B12 }
    BTN1:  0
    BTN1:  1
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621850589.15429878234, "time": 1621850591.14645862579, "pin": B12 }
    BTN1:  0
    >mySWBtn
    =SWBtn: {
      f: function (k) { ... },
      b: B12, t: null,
      k: "",
      w: 2, d: false }
    >
    `
    

    After addin line 144 - incl. option ...,edge:"both",..., this is the output:

    `
    >t3()
    SWBtn constructor: function(): function (k) {
      console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
    } , button/pin: undefined , disabled: undefined
    SWBtn disable(): disable: undefined
    =undefined
    SWBtn c(changeState)(): event: { "state": true, "lastTime": undefined, "time": 1621851160.59780693054, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": false, "lastTime": 1621851160.59780693054, "time": 1621851161.43814468383, "pin": B12 }
    BTN1 detected L
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621851161.43814468383, "time": 1621851163.25969314575, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": false, "lastTime": 1621851163.25969314575, "time": 1621851164.56733226776, "pin": B12 }
    BTN1 detected L
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621851164.56733226776, "time": 1621851165.82536792755, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": false, "lastTime": 1621851165.82536792755, "time": 1621851165.95684528350, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621851165.95684528350, "time": 1621851166.10351085662, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": false, "lastTime": 1621851166.10351085662, "time": 1621851166.22873687744, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621851166.22873687744, "time": 1621851166.36725425720, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": false, "lastTime": 1621851166.36725425720, "time": 1621851166.48242568969, "pin": B12 }
    BTN1 detected SSS
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621851166.48242568969, "time": 1621851168.26646137237, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": false, "lastTime": 1621851168.26646137237, "time": 1621851168.62865352630, "pin": B12 }
    BTN1 detected L
    SWBtn c(changeState)(): event: { "state": true, "lastTime": 1621851168.62865352630, "time": 1621851169.11860752105, "pin": B12 }
    SWBtn c(changeState)(): event: { "state": false, "lastTime": 1621851169.11860752105, "time": 1621851169.33710670471, "pin": B12 }
    BTN1 detected S
    `
    

    Test rig and adjusted setWatch() in code - now also fixed on github:

    // SWButtonTest.js 
    
    var log = function() { if (lon) loc.log.apply(loc,arguments); } // log wrapper
      , loc = console  // logging device that understands all what Console does
      , lon = true     // log or not to log...
      ;
    
    var  SWBtn = // require("SWButton");
    
    // inline module emulation
    (function(){
    var exports = {};
    // ----- module begin
    /* Copyright (c) 2014 allObject. MIT LICENSE. */
    /*
    SWBtn class - instances of SWBtn makes many buttons out of 
    just one hardware button by detecting sequences of short and 
    long presses and passing them on to a user defined, dispatching 
    callback function.
    For example, one long press of Espruino onboard BTN1 turns LED1 
    on, and one long and one short turn it off. Two long presses and 
    two long presses followed by a short one do the same to LED2, etc.
    Each detected sequence is passed in form of a string of "S"s 
    and "L"s to the user provided callback function. The callback 
    function uses the string as a key to call assigned functions
    like dispatcher.
    NOTE: Works on 1v72+ due to export and usage pattern
    Usage examples:
    // 1st example just logs the key press pattern
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
      });
    // 2nd example tests key pattern and executes accordingly
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
      });
    // 3rd example avoids chained ifs by:
    var functions = // function names match key press pattern
    { L:   function(){ LED1.set();   }
    , LS:  function(){ LED1.reset(); }
    , LL:  function(){ LED2.set();   }
    , LLS: function(){ LED2.reset(); }
    };
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log detected key pattern and...
        if (functions[k]) { functions[k](); }; // ...dispatch if defined
      },BTN1,false);
    // 4th example assumes a push button on pin B2 connecting to +3.3
    pinMode(B2,"input_pulldown");
    var SWBtn = require("SWButton");
    var mySWBtn = new SWBtn(function(k){
        console.log("Btn on B2 detected " + k); // log key press pattern
      },B2);
    */
    /**
    SWBtn - SoftWare Butten 'class'
    - constructor - accepts these arguments:
      - f - (optional) anonymous, one argument accepting dispatching callback function
      - b - (optional) button/pin id - default is BTN1
      - d - (optional) boolean interpreted disable flag - default false
            Allows button to be creted in disabled state in order 
            for (other) initializations to complete before being
            enabled with .disable(0) / .disable(false) / .disable()
            method invocation.
    - instance properties:
      - f - stores passed or set dispatching callback function
      - b - stores button / pin id
      - d - stores disabled (status) of button
      - t - stores timeout for sequence end / pause detection
      - k - stores build-up key store holding on to the "S"s and "L"s of a squence
      - w - stores hold on to the watch set with setWatch()
    */
    var SWBtn = function(f,b,d) {
      if (lon) log("SWBtn constructor: function():",f,", button/pin:",b,", disabled:",d);
      this.f = (f) ? f : function(){};
      this.b = (b) ? b : BTN1;
      this.t = null;
      this.k = null;
      this.w = null;
      this.disable(d);
     };
    /**
    .C - Constants / Configuration - defining the timings 
    - shared by all instances of SWBtn:
      - B - integer - debounce [ms]
      - L - float   - min Long press [s]
      - P - integer - min Pause [ms]
      - D - integer - delay of fnc function invocation [ms]
      Pressing a button / keeping a pin low for .C.L seconds or more is detected
      as a long press when unpressed / released / pin turns high and and adds an 
      "L" to .k key (press sequnce) - otherwise a short press is detected and an 
      "S" is adde - and the .t timeout is set with .C.P Pause time and .e() ended
      call back for press sequence end detection and processing (invocation of
      user defined - ,k key decoding dispatch - function).
    */
    SWBtn.prototype.C =
      { B: 20
      , L: 0.250
      , P: 220
      , D: 10
      };
    /**
    .disable(b) - disables/enables button
    - method - accepts one argument
      - b - boolean - (optional) boolean interpreted disable flag - default false
      Used to (temporarily) disable the button (also used in constructor).
      It clears/sets the hardware / pin watch using clearWatch() / setWatch() functions.
      NOTE1: When button is disabled while press sequence is going on, sequence end is
        not detected, but partial sequence is still stored in .k key property (but does 
        not include an ongoing press). 
      NOTE2: The .k key property is cleared (set to "") when button is (re-)enabled.
      NOTE3: any passed parameter that evaluates to false in an 'if (parameter)' and 
        omission of parameter enable the button: .disable(false), .disable(0),
        .disable(""), .disable(''), .disable(null), .disable(undefined), and .disable(),
        all these invocations enable the button... ;)
    */
    SWBtn.prototype.disable = function(b) {
      if (lon) log("SWBtn disable(): disable:",b);
      if (b) {
        if (this.w) { 
          this.d = true;
          clearWatch(this.w);
          this.w = null; 
          if (this.t) {
            clearTimeout(this.t);
            this.t = null;
          }
        }
      } else {
        if (!this.w) {
          this.d = false;
          this.k = "";
          var _this = this;
          this.w = setWatch( function(e){ _this.c(e); }, this.b
                           , { repeat:true
                           , edge:"both" // <----- required for built-in buttons!
                           , debounce:_this.C.B } );
        }
      }
     };
    /**
    .c(e) - button/pin button/pin state change callback - invoked by Espruino
    - method - accepts one e event argument (object) 
      Espruino reference for .setWatch() defines e event object as:
      - time     - float   - time of this state change [s]
      - lastTime - float   - time of last such state change [s]
      - state    - boolean - current state of the button / pin
      Notes button/pin status and - on unpress/release state - 
      appends "L"(ong) or "S"(short) to .k key (sequence) and 
      sets .t timeout to .C.P Pause for sequence end detection
    */
    SWBtn.prototype.c = function(e){ // change of state - called by set watch
      if (lon) log("SWBtn c(changeState)(): event:",e);
      if (e.state) {
        if (this.t) {
          clearTimeout(this.t);
          this.t = null;
        }
      } else {
        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);
      }
     };
    /**
    .e() - sequence ended timeout callback - invoked by .t timeout set in .c(e)
    - method - accepts no arguments
      Marks detected end of press sequence and invokes user provided .f 
      callback function in a setTimeout() with .C.D delay.
    */
    SWBtn.prototype.e = function() {
      this.t = null;
      var _k = this.k;
      if (_k.length > 0) {
        this.k = "";
        var _this = this;
        setTimeout(function(){ _this.f(_k); },this.C.D);
      }
     };
    exports = SWBtn;
    // ----- module end
    return exports;
    })();
    
    var iId, wId, mySWBtn;
    
    function t1(runOrStopBoolen) {
      if (runOrStopBoolen || typeof runOrStopBoolen == "undefined") {
        if (iId) t1(false);
        iId = setInterval(function(){
          digitalWrite(LED1,digitalRead(BTN1)); }, 100);
      } else {
          if (iId) iId = clearInterval(iId);
      }
    }
    
    function t2(runOrStopBoolen) {
      if (runOrStopBoolen || typeof runOrStopBoolen == "undefined") {
        if (wId) t2(false);
        wId = setWatch(function(){
            loc.log("BTN1: ",digitalRead(BTN1)); }
          , BTN1,{repeat:true, edge:"both", debounce:10});
      } else {
          if (wId) wId = clearWatch(wId);
      }
    }
    
    function t3() {
      mySWBtn = new SWBtn(function(k){
        console.log("BTN1 detected " + k); // log key press pattern
        if        (k === "L"  ) { LED1.set();
        } else if (k === "LS" ) { LED1.reset();
        } else if (k === "LL" ) { LED2.set();
        } else if (k === "LLS") { LED2.reset();
        }
      });
    }
    
    function onInit() {
      t1(); // turns red LED1 on when BTN1/pin is pressed/high
      t2(); // logs event state on press and on release
      t3(); // starts SWButton on BTN1/pin B12 (B12 on original board is BTN1)
            // ...and SWButton code includes some logging....
    }
    
    setTimeout(onInit,999);
    

    After upload, playing with button BTN1, tests t1() and t2() work just fine... t2() had initially NO edge:"both" option and it failed, so I added it, and added it then also to the SWButton code... see line 144 and all is as expected...

    Reference documentation added some more fine print since inception of setWatch() 7+ years ago...

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

Software Buttons - Many buttons from just one hardware button

Posted by Avatar for allObjects @allObjects

Actions