• 'After-completed-comment': Check out existing KeyPad.js module and its doc... and look at this conversation's code as a programming exercise... and a different style of doing things in JS...

    Adding a human interfac / user interface is always a challenge. Below code provides you with function to drive and sense a matrix key pad with any nuber of rows and columns directly with Espruino. You need no other things than a passive - bought or DIY - key pad. For DIY, you need only push buttons and wire them in a matrix and let each button connect its row with its column wire when pressed. Then you connect the row and column wires to Espruino GPIO pins.

    Schema of market available or DIY key pad with 12 keys (4 rows, 3 cols):

              col:0    col:1    col:2
                 A4       A5       A6
              
                  |        |        |
           btn __ | btn __ | btn __ |
             0 ||_|   1 ||_|   2 ||_|
               |  |     |  |     |  | 
    row:0 --------{--------(--------(---
       A0         |        |        |
           btn __ | btn __ | btn __ |
             3 ||_|   4 ||_|   5 ||_|
               |  |     |  |     |  | 
    row:1 --------{--------(--------(---
       A1         |        |        |
           btn __ | btn __ | btn __ |
             6 ||_|   7 ||_|   8 ||_|
               |  |     |  |     |  | 
    row:2 --------{--------(--------(---
       A2         |        |        |
           btn __ | btn __ | btn __ |
             9 ||_|  10 ||_|  11 ||_|
               |  |     |  |     |  | 
    row:3 --------{--------(--------(---
       A3         |        |        |
                  |        |        |
    

    No additional components are needed: GPIO pins provide the built-in pull-up/down resistors to supprt the press detection and scanning.

    Usage in your applicaiton is very simple - for example, for a 12-key pad driven with 4 row pins A0..3 and sensed with 3 column pins A4..6 (and default values for start enabled, watching falling edge, and 200[ms] debouncing):

    // var keypad = require("KeypadMDS").connect(... // when available as module 
    var keypad = keypadModule.connect([A0,A1,A2,A3],[A4,A5,A6],function(btn,tme){ 
       console.log("At " + tme + " btn # " + btn + " was pressed"); });
    

    Principle of operation is:

    • drive pins set to output and low on rows
    • sense pins set to input-pullup and set on watch for scan
    • any press (watch) triggers the scan (passing triggering column and time); scan does:
      • clears all watches
      • sets all drive pins to input
      • sets drive pins individually to output and checks triggering/sensing column pin
      • calculates button number form row(drive) and column(sense) index
      • puts all drive pins back to output and low and set sense pins on watch again
      • invokes callback passing calculated button number and time of press

    The code inlcudes some more functions to avoid overlapping and overruning events. It also includes two console log/debug statments in .scan()- lines 73 and 80 - to be removed for use in your project. Code is attached for easy download and use. Placed in 'project sandbox modules' folder (see Espruiono IDE setings), it can be pulled as KeypadMDS module with require("KeypadMDS").

    // Module for Drive/Sense Multiplexed Keypad 
    //
    // ...provided by allObjects - MIT Lic (C) 2015.
    //
    // Keypad
    // - supports any number of driving pins (rows)
    // - supports any number of sensing pins (cols)
    // - has enable(true/false) for controlled actions
    // - callback called w/ button # 0,1,2,3,... and time
    // - optional:
    //   - start disabled vs. default enabled (eval boolean)
    //   - driving high/watch_rising vs. default low/falling
    //   - custom debounce vs. 200[ms] default (and minimum)
    //
    //
    // Note: momentary-on push switches connect rows w/ cols
    //
    // Example: Keypad controlling AC (pins Espruino Pico)
    //
    // \    sense 
    //   \  pins: pin B1   pin B10
    //     \       |        |        
    // drive \     |        |
    // pins:   \   |        |
    //           \ |        |
    // pin B13 ----0:pwr----1:------ pwr on/off(toggle), N/C
    //             |        |
    // pin B14 ----2:tDwn---3:tUp--- target temp down  , up
    //             |        |
    // pin B15 ----4:fan----5:------ fan on/off(toggle), N/C
    //             |        |
    
    var keypadModule = (function(){
    
      var Keypad = function(dps,sps,cb,e,p,d) {
        this.dps = dps;
        this.sps = sps;
        this.cb = cb;
        this.p = p;
        this.d = d;
        this.e = e;
        this.pl = (p === "up") ? 1 : 0;
        this.np = 0;
        this.ws = null;
      }, p = Keypad.prototype;
    
      p.init = function(_this) {
        this.sps.forEach(function(sp,i){
          pinMode(sp,"input_pull" + _this.p);
        });
        this.drive(this);
        this.watch(this);
      };
      p.drive = function(_this){
        this.dps.forEach(function(dp){
          pinMode(dp,"output");
          digitalWrite(dp,1 - _this.p);
        });
      };
      p.watch = function(_this){
        this.ws = [];
        this.sps.forEach(function(sp,i){ var si = i;
          _this.ws.push( setWatch( 
                function(ep){ _this.scan(si,ep.time); }
              , sp
              , { repeat: false
                , edge: (_this.pl) ? "falling" : "rising"
                , debounce: _this.d } ) );
        });
        this.w = true;
      };
      p.scan = function(si,tp) {
        if (dbg) log(tp - this.np);
        this.ws.forEach(function(w,i){ clearWatch(w); });
        if (this.e && (tp > this.np)) {
          this.dps.forEach(function(dp){ pinMode(dp,"input"); });
          var b = -1, di, dl = this.dps.length, dp;
          var sp = this.sps[si], s = 1 - this.pl;
          for (di = 0; di < dl; di++) { 
            if (dbg) log("scan si: ",si," - di: ", di, " - s: ", s); 
            pinMode(dp = this.dps[di],"output");
            if (digitalRead(sp) === s) {
              b = di * this.sps.length + si; dl = 0;
            } pinMode(dp,"input");
          } this.drive(this); this.watch(this);
          this.np = getTime() + (this.d / 100);
          if (b >= 0) { this.cb(b,tp); } 
        } else { this.watch(this); }
      };
    
      p.enable = function(e) { this.e = !!e; };
    
      return ({connect:function(
         drivePins  // array of drive pins 
        ,sensePins  // array of sense pins;
        ,callback   // callback acceptin button # 0,1,2,...
        ,enabled    // optional, default = true
        ,pull       // optional, default = "up"
        ,debounce   // optional, default = 200, min = 200, [ms]
        ){
          var kp = new Keypad(drivePins,sensePins,callback
            , !enabled
            , ((pull === "up") ? pull : (pull === "down") ? pull : "up")
            , (    (isNaN(debounce)
                || ((debounce * 1) < 200)
              ) ? 200 : debounce * 1) );
           kp.init(kp);
           return kp;
      }});
    })();
    
    var dbg = true;
    var log = function() { console.log(arguments.join("")); };
    
    // var keypad = require("KeypadMDS").connect(
    var keypad = keypadModule.connect(
        [B13,B14,B15], [B1,B10], function(btn,tme){ // 0,1,2,3...
          if (dbg) log("" + tme + ": Button # ",btn," pressed: "
              , [ "pwr toggle","N/C"
                , "temp down" ,"temp up"
                , "fan toggle","N/C"
                ][btn] );
      }); 
    

    *Fedback is welcome. 'Exercise' was triggered by - paraphrased - How can I do some Arduino(-like) loop to monitor my hardware events...* (see conversation).


    3 Attachments

About

Avatar for allObjects @allObjects started