The questioning conversations triggered my activities to finally tackle MCP23017 PortExpander.
My MCP23017 lived so far neglected life since about the time when @DrAzzy gave it a push and provided the MCP23017.js module. Having also a key pad at hand (with a broke key... argh...) left me with no excuse to walk the talk... (as 'kind-a' outlined in mentioned questioning conversation).
Here a good cut of working code (also attached as file for easy 'grab' and play for yourself:
// MCP32017I2CA4x4KPD9.js
//
// MCP32917 (MCP32908) PortExpander driving
// 4x4 Keypad using interrupt approach for key down
// and intervalled chack for key up
// simple (preferred) usage: -----
var d=`
MCP32917 (MCP32908) PortExpander driving 4x4 Keypad
using interrupt and setWatch vs. full scans to detect keyDown
I2C1.setup({scl:B8, sda:B9, bitrate:1000000});
var kpd = new Kpd(function(keypad,key,upTime,pressTime){
console.log(key,"pressed for", pressTime);
}).connect(I2C1,0,B0,A0).enable();
`;
// key pad config ----- (used Espruino-Wifi and built-in I2C)
// but can run on soft I2C aa well.
var i2a = 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)
, pxa = 0 // portexpander 3-bit address (0..7)
, pxi = B0 // interrupt - required w/ setWatch
, pxr = A0 // reset (active low, 'clean' alternatives work too)
, dbnc = 10 // debounce [ms] - optional
, uchk = 100 // upcheck [ms] - optional
;
// KPD - Key PaD definition -----
// constructor
//
// - downCB - down event callback (null if none); parms:
// - kpd - keypad instance
// - key - key number 0..15
// - time - time on down event (float from getTime())
// - upCB - up event callback (absent or null if none); parms:
// - kpd - keypad instance
// - key - key number 0..15
// - time - time on up event (float from getTime())
// - duration - press duration time (float, diff of up - down)
// - cancelled - truey when keypad got disabled while key down
// - (optional) debounce in ms - when is key to be considered stable
// - (optional) upcheck in ms - when is key considered to be released
var Kpd = function(upCB,downCB,debounce,upcheck) {
this.upCB = upCB;
this.downCB = downCB;
this.debounce = (debounce) ? debounce : 0;
this.upcheck = (upcheck) ? upcheck : 0;
this.down = false; // state / key pressed
this.key = -1; // last key
this.dwnT = 0; // getTime() of down event (press)
this.upT = 0; // getTime() of up event (release)
this.pressT = 0; // press duration (down)
this.watch = null; // watch sitting on interrupt
this.timeout = false; // timeout to do debouncing
this.enabled = false; // state / keypad enabled
}, p = Kpd.prototype;
// .connect() w/ setup i2c appliance, adjusted address (<<1) and interrupt
//
// NOTE: EXPECTS BANK=0 (after reset) - to switch to BANK=1 (once only)
// - i2c
// - addr - portextender 3-bit address 0..7
// - intr - interrupt on pin
// - rst - reset on pin (omittable w/ RC HW reset)
p.connect = function(i2c,addr,intr,rst) {
this._rst(rst); var ioc = 128; // IOCONfig: BANK=1, SEQ,
pinMode(intr,"input_pulldown");
this.intr = intr; // portexpander interrupt on pin
this.i2c = i2c; // i2c appliance setp on scl and sda pins
this.addr = 32+addr; // i2c address / adjusted and complemented
this._w(10,ioc) // IOCON - I/O EXPANDER CONFIGURATION: BANK=1,SEQ
._w( 0, // starting with reg 00 and 8-bit mode sequentially
[ 15 // 00 IODIR - I/O DIRection: 0/1 = out/inp
, 15 // 01 IPOL - INPUT POLARITY: 0/1 = equal/opposite
, 0 // 02 GPINTEN - INTERRUPT-ON-CHANGE: 0/1 = dis/en-able - FOR NOW
,240 // 03 DEFVAL - DEFAULT VALUE (for int trigger): don't care
, 0 // 04 INTCON - INTERRUPT-ON-CHANGE CONTROL: 0/1 = do not/compare
,ioc // 05 IOCON - I/O EXPANDER CONfig: BANK=1,... (as above)
, 15 // 06 GPPU - GPIO PULL-UP RESISTOR 100k: 0/1 = dis/en-abled
]);
return this;
};
p._rst = function(rst) { // private
if (rst !== undefined) { pinMode(rst,"output"); rst.reset(); }
this.rst = rst;
if (rst !== undefined) { rst.set(); }
};
// enable/disable (w/ arg false) keypad
p.enable = function(b) {
if ((b=((b===undefined)||b)) != this.enabled) {
return (b) ? this._enable() : this._disable(); }
return this;
};
// all private / for convenience; NOTE: EXPECT BANK=1
p._w = function(r,dArr) { this.i2c.writeTo(this.addr,r,dArr); return this; };
p._ro = function(amnt) { return this.i2c.readFrom(this.addr,amnt); };
p._r = function(r,amnt) { return this._w(r,[])._ro(amnt); };
// watch callback for key down // private; NOTE: EXPECTS BANK=1
p._down = function(state, time) {
this.watch = false;
var dwnT = getTime() // capture down time
, rnb = this._r(7,1)[0]; // 07 INTF: read interrupt flags,...
this._w(2,0); // 02 GPINTEN: ...disable ints,...
this.timeout = setTimeout(this._dChk.bind(this) // ...check deferred...
,this.debounce,dwnT,rnb); // ...down bits for row and col info
};
// key down check (after debaounce time) // private; NOTE: EXPECTS BANK=1
p._dChk = function(dwnT,rnb) {
this.timeout=false;
var rc=0, rb=1, r=0, cnb, cc=0, cb=239, c=0; // prep to...
while (++rc < 5 && !r) { // ...find 1st row bit for row info
if (rnb&rb) r = rc; else rb <<= 1; }
while (++cc < 5 && !c) { // ...find 1st col bit for col info
cnb = this._w(9,cb)._r(9,1)[0]&15; // 09 GPIO: write/read - scan col
if (cnb&rb) c = cc; else cb <<= 1; }
this._w(9,15); // put 'normal' (all outs on low)
if (r && c) { // row and col present, key still pressed
this.dwnT=dwnT; this.down=true; this.key=(r-1)*4+(c-1); this.upT=0;
this.timeout=setTimeout(this._uChk.bind(this),this.upcheck,true);
if (this.downCB) setTimeout(this.downCB,1,this,this.key,this.dwnT);
} else { // was a fluke/press within debounce
this.timeout=setTimeout(this._uChk.bind(this),this.upcheck,false);
}
};
// key up check (by upcheck interval) // private; NOTE: EXPECTS BANK=1
p._uChk = function(frst) {
var upT = getTime() // capture up time...
, dwn = this._r(9,1)[0]&15; // 09 GPIO: or any downs
if (frst || dwn) {
this.timeout=setTimeout(this._uChk.bind(this),this.upcheck,!!dwn);
} else {
this.timeout=false; this.pressT=(this.upT=upT)-this.dwnT;
this.watch = setWatch(this._down.bind(this),this.intr,{edge:"falling"});
this._w(2,15); // 02 GPINTEN - enable interrupts
if (this.down) {
this.down=false;
if (this.upCB) {
setTimeout(this.upCB,1,this,this.key,this.upT,this.pressT);
} }
}
};
p._enable = function() { // private; NOTE: EXPECTS BANK=1
this._w(2, 0) // 02 GPINTEN - disable interrupts / prec
._r(8, 1); // 08 INTCAP - INT CAPTURE reg to clear int
this._w(9,15) // 09 GPIO - G I/O reg: all outs low / prec
._w(2,15); // 02 GPINTEN - enable interrupts / prec
this.watch = setWatch(this._down.bind(this),this.intr,{edge:"falling"});
this.enabled = true;
return this;
};
p._disable = function() { // private; NOTE: EXPECTS BANK=1
this.enabled = false;
this.upT = getTime(); // take cancel/disable time
this._w(2,0).r(8,1); // 02 GPINTEN / 08 INTCAP - disable/clear ints
if (this.watch) { clearWatch(this.watch); this.watch = false; }
if (this.timeout) { clearTimeout(this.timeout); this.timeout = false; }
if (this.down) {
this.down = false; this.pressT = this.upT-this.dwnT;
if (this.upCB) { // disable while pressed, ...cancelled
setTimeout(this.upCB,1,this,this.key,this.upT,this.pressT,true); } }
return this;
};
// KPD ----- callbacks -----
// key down callback
var downCB = function(kpd, key, time) {
console.log(key,"... down at ",time); // key, down time
};
// key up callback (cancelled truy when disabled while pressing a key)
var upCB = function (kpd, key, time, duration, cancelled) {
if (!cancelled) { // key, upTime, pressDuration
console.log(key,". up at ",time," after ",duration);
} else { // key, upTime, pressDuration, ...cancelled by kpd.enable(false)
console.log("keypad got disabled while pressing key ",key
," for ",duration," secs at ",time);
}
};
// KPD ----- examples -----
var kpd;
function onInit() {
i2a.setup({scl:i2c, sda:i2d, bitrate:i2s});
// kpd = new Kpd(upCB).connect(i2a,pxa,pxi,pxr).enable(); // preferred
// kpd = new Kpd(null,downCB).connect(i2a,pxa,pxi,pxr).enable();
// kpd = new Kpd(upCB,downCB).connect(i2a,pxa,pxi,pxr).enable();
// kpd = new Kpd(upCB,downCB,dbnc,uchk) // full
// .connect(i2a,pxa<<1,pxi,pxr,dbnc).enable();
// var kpd = new Kpd().connect(i2a,pxa,pxi,pxr).enable(); // ciao Arduino
// var lastUpT = kpd.upT;
// setInterval(function(){
// if ( ! kpd.down && (kpd.upT !== lastUpT)) {
// console.log(kpd.key," up at ",(lastUpT = kpd.upT));
// } },50);
}
setTimeout(onInit,500); // for upload/development convenience (remove)
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
...continued...
The questioning conversations triggered my activities to finally tackle MCP23017 PortExpander.
My MCP23017 lived so far neglected life since about the time when @DrAzzy gave it a push and provided the MCP23017.js module. Having also a key pad at hand (with a broke key... argh...) left me with no excuse to walk the talk... (as 'kind-a' outlined in mentioned questioning conversation).
Here a good cut of working code (also attached as file for easy 'grab' and play for yourself:
...to be continued...
1 Attachment