• ... did not share the code I used... let me do it in the state when I hit the wall with the spike caused by Espriuno pin mode change. It is now - 1v95 - fixed. Did not have the time to resume... :( ...yet... :]. Note the rewrite of the rewrite of the MCP23017 module... with the new name MCP23017i2c16w - MCP2317 in i2c 16 bit (default) mode and w atch-able interrupts.

    // MCP23017i2c16watched7.js
    //
    // Work in progress: Adding setWatch/clearWatch
    // to MCP23017.js module pins by allObjects
    
    // NOTE: MCP23017 w/ i2c in 16 bit mode and interrupt watched;
    //       Reset / default mode --- BANK=0, MIRROR, SEQOP:
    //       16 bit, mirror interrupt, sequential r/w 
    //       Port A = LSByte and Port B = MSByte of 16 bit word
    
    var exports = {};  // module business
    Modules.addCached("MCP23017i2c16w", function() {
    
    exports.connect = function(i2,rst,i2a,irq,ioc) {
        return new MCP23017i2c16w(i2,rst,i2a,irq,ioc);
    };
    
    // PortExpander (PX) MCP23017 w/ i2c interface and watched irq
    
    // constructor
    var MCP23017i2c16w = function
      (i2  // Espruino (ESP) i2c appliance driving PortExpander (PX)
      ,rst // (optional/falsy) ESP pin PX's Reset pin is connected to
      ,i2a // 3 bit (0..7) PX Address
      ,irq // ESP pin PX's Interrupt pin is connected to
      ,ioc // (optional) IOCONfig, enforced to 16bit, mirror, seq r/w
    ){this.i2  = i2;         // ESP i2c appliance
      if (rst!==null) rst.write(0); // activate PX reset if ESP pin 
      if ((this.rst=rst)!==null) rst.write(1); // de-activate  PX rst
      this.i2a = (i2a)?32+i2a:32; // PX i2c addr (7 bit)
      this.irq = irq;        // ESP interrupt pin (w)
      if ((irq!==undefined)&&(irq!==null)) irq.mode("input_pullup");
      this.ioc = ((ioc)?ioc&22:0)|64; // enforced 16b, mirr, seq r/w
      this.wR(10,this.ioc);  // IOCONfig reg BANK=0, MIRROR, SEQOP
      this.io  = 65535;      // IODIR BA regs int val, 0/1=out/input
      this.pu  = 0;          // GPPU  BA regs int val, 0/1=no/pull-up
      this.ol  = 0;          // OLAT  BA regs int val, 0b16 (0.16.0)
      this.pMs = ["output","input","input_pullup"];
      this.wCB = this._wCb.bind(this); // watch cb bound to this (w)
      this.wId = null; // watch ID (w)
      this.ie  = 0;    // GPINTEN BA regs (IRQ/INTerrupt ENAbled) (w)
      this.ps  = [];   // pins B7..B0,A7..A0 for iteration (w)
      var i=65536,j=16,p; while(i>>=1){ // create Pins (P) B7..0,A7..0 
        this[((--j>7)?"B":"A")+j%8] = (p = new PXP // new Pin|Port:
          (this  // ref to MCP23017 - owning PortExpander (PX)
          ,i     // id of Pin|Port  - (single bit in 16 bit value)
          )); this.ps.splice(0,0,p); } // ...; (w)
    }, x = MCP23017i2c16w.prototype;
    
    x.read=function // read all P's @ once (a word, BA of an integer)
    (){return this.rR(18); }; // all 16 PX Ps @ once (8..15,0..7)
    
    x.write=function // write all P's @ once (a word, BA of an int)
        (w // a 16 bit word (2 LSBytes BA of the integer)
    ){return this.wR(18,w); };
    
    x.rBA=function // read all P's @ once as ByteArray/Uint8Array[2]
    (){return this.rRBA(18); };
    
    x.rR=function // read from r AB a word (2 bytes) as (BA) integer
        (r // reg in 16 bit / BANK=0 mode and evens of 0x00..0x15
    ){this.i2.writeTo(this.i2a,r);        // select reg to read from
      var w=this.i2.readFrom(this.i2a,2); // read Uint8Array[2] 
      return w[0]|w[1]<<8; };             // return int (BA)
    
    x.wR=function // write to AB r a word (2 LSBytes BA of an int)
        (r // reg in 16 bit / BANK=0 mode and evens of 0x00..0x15
        ,w // word as integer (2 LSBytes of an integer)
    ){this.i2.writeTo(this.i2a,r,[w&255,w>>8­]); // w A,B of (BA) int
      return this; };
      
    x.rRBA=function // read from r AB 2 bytes (a word) as Byte Array
        (r // reg in 16 bit / BANK=0 mode and evens of 0x00..0x15
    ){this.i2.writeTo(this.i2a,r);            // select reg to read
      return this.i2.readFrom(this.i2a,2); }; // ret read Uint8Arr[2]
    
    x.wRBOA=function // w n bytes (byte|array in 8|16b, no|seq r/w 
        // always starts/ends w/ cfg @ connect (16b,BANK=0, seq r/w)
        (ioc // can only contol BANK (128), SEQOP (32), DISSLW (16)
        ,r   // register w/ BANK=x as given by ioc
        ,boa // byte or array (write 1 or n bytes)
    ){this.i2.writeTo(10,(ioc&176)|(this.ioc­&70)); // MIR,ODR,INTPOL
      this.i2.writeTo(this.i2a,r,boa); // write byte or array of bts
      this.i2.writeTo(this.i2a,(ioc&128)?5:10,­this.ioc); // ioc back
      return this;};
    
    x.pM=function // set Pin mode by P's id: IODIR i/o, GPPU pull-up
        (i // id of Pin (single bit in 16 bit value)
        ,m // mode of Pin: ""output"|input"|"input_pullup"
    ){var x=this.pMs.indexOf(m);
      if (x<0) { throw "Pin mode '"+m+"' invalid ("+this.pMs+")"; }
      this.wR( 0,(x&1) ? this.io&=~i : this.io|= i );
      this.wR(12,(x&2) ? this.pu|= i : this.pu&=~i );
      return this; };
    
    x.gPM=function // get pin mode by P id
        (i // id of Pin (single bit in 16 bit value)
    ){var v=this.rR(0)&i; // IODIR i/o bit, if 0 GPPUP pull-up bit
      return ((!v)?this.pMs[0]:this.pMs[1+(this.rR(12­)&i)?1:0]); };
    
    x.rP=function // read P's pin input value by P id
        (i // id of Port (single bit in 16 bit value)
    ){return (this.rR(18)&i) ? true : false; };
    
    x.wP=function // write P's pin output (latched) value by P id
        (i // id / value of Port (single bit in 16 bit value)
        ,v // truey/falsy output value
    ){this.wR(18,this.ol=(this.ol&~i)|(v)?i:­0); return this; };
    
    x.wX=function // write to all 16 PX Ps @ once (0..15, A+B 0..7)
        (v // value as int (16 bit value)
    ){this.wR(18,this.ol=v); return this; };
    
    x.gP = function // get pin by ID i (w)
        (i // id of Port (single bit in 16 bit value)
        ,p // do not supply (implicit var p)
    ){return this.ps.reduce((pr,px)=>{
          return (pr===undefined)?(px.i===i)?px:pr:pr; },p); };
    
    // setWatch / fire watch / clearWatch additions to PX 
    x.sPW=function // set pin watch (w)
        (cb // watch callback
        ,p  // pin / port
        ,os // watch options
    ){var i=p.i;
      if ((this.ie&i)||(!this.io&i)) throw "Portexpander pin "+i
        +" already watched or not in input or input_pullup mode";
    l(".sPW(): enable interrupt on pin "+i);
      (os=(p.wOs=os||{}))._cb=cb; // set/create os obj w/ cb,...
      os._r=!!os.repeat;          // ...watch opt repeat,...
      os._e=(os.edge=="rising" )?true       // ...edge rising
           :(os.edge=="falling")?false      // ...edge falling
           :                     undefined; // ...edge both
      os._t=-1;                             // ...irg last time
      if (this.ie) { // already irq enabled pins: clear irq watch
        this.wId = clearWatch(this.wId);
    l(".sPW(): ad pin "+i+" to other watched ones");
      } else { // this is first (and only) irq enabled Pin p
    l(".sPW(): add pin "+i+" as 1st watched");
      }
      this.wR(4,this.ie|=i); // enable irqs incl Pin p w/ ID i
      this.rR(16); // clear pend irgs INTCAP, setWatch on .irq
      this.wId=setWatch(this.wCB,this.irq,{rep­eat:true, edge:"falling",debounce:0});
      return p; };
    
    x._wCb=function(s,t,lt){ // internal watch irq callback (w)
      var irqLast=this.irq.read()
        , iFs=this.rR(14) // read irq flags INTF AB regs
        , iSs=this.rR(16) // read irq states INTCAP, clears irq
    //    , i=this.wR(4,0), os;  // dis irq GPINTEN (v decl i,os)
        , os;
    l("._wCb: iFs="+iFs+" iSs="+iSs+" irq:"+irqLast+"/"+this.irq.read());
      this.wId=clearWatch(this.wId);
      // this.wId=null; // dump ID of irq watch since it happend
      this.ps.forEach(function(p){ // iterate all pins for iFs
    l("._wCB: " + p.i);
        if (iFs&p.i) { i=p.i;os=p.wOs;lt=0; // p i caused irq
          if (os._e===undefined) { // watch opt edge = both
    l("._wCB: irq on pin "+i+" edge:both");
            lt=os.t;setTimeout(os._cb,1,iSs&i===i,os­.t=t,lt);
          } else if (os._e) {      // watch opt edge = rising
    l("._wCB: irq on pin "+i+" edge:rising");
            if (iSs&i) {           // ...and state turned true
              lt=os.t;setTimeout(os._cb,1,true,os.t=t,­lt); }
          } else {                 // watch opt edge = falling
    l("._wCB: irq on pin "+i+" edge:falling");
            if (i&~iSs) {          // ...and state turned false
              lt=os.t;setTimeout(os._cb,1,false,os.t=t­,lt); } }
          if (lt && ! os._r) {     // watch opt ! rep
    l("._wCB: remove irq from pin "+i);
            this.ie&=~i; delete p.wOs; } // remove watch from p
      } },this);
      if (this.ie) { // still irq enabled pin(s)
    l("._wCb: enable interrupts for " + this.ie);
        this.wR(4,this.ie); // re-enable irqs on 'these' pins
        this.rR(16); // clear pend irgs INTCAP, setWatch on .irq
        this.wId=setWatch(this.wCB,this.irq,{rep­eat:true, edge:"falling",debounce:0});
      } };
    
    x.cPW=function // claer watch (w)
        (p // watch handle = setWatch result (= PX P pin)
    ){ if (p) { var i=p.i; if (this.ie&i) {
    l(".cPW: disable interrupt for pin "+i);
      this.wR(4,this.ie&=~i); delete p.wOs; // rm watch from i
      if ( ! this.ie) {
    l(".cPW: remove watch since no pin is watched anymore");
        clearWatch(this.wId);this.wId=null; } } } };
    
    // Port / Pin (P/PXP) of PortExpander (PX) - Port 'class'
    var PXP = function
      (x // owning PortEexpander (PX)
      ,i // id of Pin | Port (P) (single bit in 16 bit value)
    ){this.x=x; // owning expander
      this.i=i;
    }, p = PXP.prototype;
    
    p.set  =function() { return this.x.wP(this.i,1); };
    p.reset=function() { return this.x.wP(this.i,0); };
    p.write=function(v){ return this.x.wP(this.i,v); };
    p.read =function() { return (this.x.rR(18)&this.i)?true:false;};
    p.mode =function(m){ return this.x.pM(this.i,m); };
    p.getMode=function(){return this.x.gPM(this.i);  };
    
    // add watchable to PXP (PorteXpander Pin)
    p.setWatch = function
      (cb // watch callback
      ,os // watch options
    ){l("PXP.setWatch():");
      return this.x.sPW(cb,this,os); };
    p.clearWatch = function
      (wId // watch Id (PX P pin object / this)
    ){this.x.cPW(wId); };
    
    
    });
    
    
    // ***** modified setWatch / clearWatch
    
    (function() {
     var _setWatch = setWatch;
     global.setWatch = function(cb,pin,opts) {
       return (pin instanceof Pin)
        ? _setWatch(cb,pin,opts)
        : pin.setWatch(cb,opts);
     };
    }());
    
    (function() {
     var _clearWatch = clearWatch;
     global.clearWatch = function(id) {
       return (!isNaN(id))
        ? _clearWatch(id)
        : id.clearWatch(id);
     };
    }());
    
    // ***** application / test begin *****
    
    // --- configuration (Espruno(-WiFi))
    
    var i2   = I2C1 // i2c appliance portexpander is connected to
      , i2c  =   B8 // i2c clock
      , i2d  =   B9 // i2c data
      , i2s  = 100000 // i2c speed (bitrate per second 100/400/1700k)
      , i2a  =    0 // portexpander 3-bit address (0..7)
      , irq  =   B0 // interrupt - required w/ setWatch
      , rst  =   A0 // reset (active low, 'clean' alternatives work too)
      , x    = null // portexpander instance
      , xMN  = "MCP23017i2c16w"  // portexpander module name
      , xM   = require(xMN)      // portexpander module
      ;
    
    // --- init / app ---
    
    function onInit() {
      i2.setup({scl:i2c, sda:i2d, bitrate:i2s});
      x = xM.connect(i2,rst,i2a,irq); // opt ioc ommitted / default
      rr(); // run readWatch BTN / C13 (Espruino-Wifi)
      rg(); // run greenWatch x.A7 falling
    }
    
    var redWatch = null;
    function rr() { // run red LED1 toggle when BTN released
      // setWatch w/ built-in port BTN (...C13 - Espruino-Wifi)
      var redOn = false; // red LED on
      redWatch = setWatch(function(s,t,lt) {
          digitalWrite(LED1,redOn = !redOn);
        }, BTN, { repeat:true, edge:"rising", debounce:10 }); }
    function hr() { clearWatch(redWatch); } // halt red LED1 toggling
    
    var greenWatch = null;
    function rg() { // run green LED2 toggle when x.A7 disconn from GND
      // setWatch w/ Portexpander Port x.A7 (w/ pullup)
      x.A7.mode("input_pullup");
      var greenOn = false; // green LED on
      greenWatch = setWatch(function(s,t,lt) {
          digitalWrite(LED2,greenOn = !greenOn);
        }, x.A7, { repeat:true, edge:"falling" }); }
    function hg() { clearWatch(greenWatch); } // halt green LED2 toggling
    
    var redWatch2 = null;
    function rr2() { // run red LED1 toggle when x.A6 disconn from GND
      // setWatch w/ Portexpander Port x.A6 (w/ pullup)
      x.A6.mode("input_pullup");
      var redOn = false; // red LED on
      redWatch2 = setWatch(function(s,t,lt) {
          digitalWrite(LED1,redOn = !redOn);
        }, x.A6, { repeat:true, edge:"falling" }); }
    function hr2() { clearWatch(redWatch2); } // halt red LED1 toggling
    
    
    // *** development / test helper / conveniences;
    //     test functions to be invoked from console.
    
    // --- log function (use like console.log())
    function l() { console.log.apply(console,arguments); }
    
    // --- log With Time (use like console.log()) - currently not in use
    function lWT() {
      var args=[getTime()]; var i=-1; j=arguments.length;
      while(++i<j) args.push(arguments[i]);                      
      console.log.apply(console,args); }
    
    // --- r() start reading every rIT[ms] all ports in 16 bit mode
    //         and - on change - log value
    // --- h() stop (halt) reading ports and logging value
    var rIT=500, tId = false, iVLast = -1, iV;
    function r() { // run - continuously reading all ports in 16 bit mode
      if (!tid)
        tId = setInterval(function(){
            iV = x.read();
            if (iV != iVLast) console.log(iVLast = iV);
        },rIT); } }
    function h() { if (tId) tId = clearInterval(tId); } // halt - cont reading
    
    // --- rm() run monitoring every mIT[ms] and log on change
    // --- hm() stop (halt) monitoring
    var mIT=100, mId = false, iXA7=null,sIRQ=null,sFs=-1,v,s;
    B13.mode("input"); // probe on x.A7
    function rm() { // run - monitoring
      mId = setInterval(function(){ s="";
          v=B13.read(); if (v!=iXA7) s+=" iXA7:"+(iXA7=v);
          v=irq.read(); if (v!=sIRQ) s+=" sIRQ:"+(sIRQ=v);
          v=x.rR(14);   if (v!=sFs)  s+=" sFs:" +(sFs =v);
          if (s.length>0) console.log(s);
        }, mIT);
    }
    function hm() { if (mId) mId = clearInterval(mId); }  // halt - monitoring
    
    // --- tg() trigger green LED2 flashing watch
    B14.mode="output"; B14.set(); // driving x.A7 (on input_pullup)
    function tg() { // trigger green_watch x.A7
      digitalPulse(B14,0,0.5); }
    
    // --- rtg(n) n repeated triggers every rtIT[ms] of green LED2 flashing watch
    var rtIT=1000;
    function rtg(n) { // repeated tg() - green trigger
      n = ((n) ? n : 10) + 1; l(n);
      var rtgId = setInterval(function(){
        tg(); if (!--n) clearInterval(rtgId); },rtIT); }
    
    
    // *** auto start on upload
    setTimeout(onInit,500);
    

    Still to be retested w/ 1v95

About

Avatar for barbiani @barbiani started