268 lines
6.1 KiB
JavaScript
268 lines
6.1 KiB
JavaScript
const i2c = require('i2c-bus'),
|
|
EventEmitter = require('events');
|
|
|
|
const MPR121_I2CADDR_DEFAULT = 0x5A,
|
|
MPR121_TOUCHSTATUS_L = 0x00,
|
|
MPR121_TOUCHSTATUS_H = 0x01,
|
|
MPR121_FILTDATA_0L = 0x04,
|
|
MPR121_FILTDATA_0H = 0x05,
|
|
MPR121_BASELINE_0 = 0x1E,
|
|
MPR121_MHDR = 0x2B,
|
|
MPR121_NHDR = 0x2C,
|
|
MPR121_NCLR = 0x2D,
|
|
MPR121_FDLR = 0x2E,
|
|
MPR121_MHDF = 0x2F,
|
|
MPR121_NHDF = 0x30,
|
|
MPR121_NCLF = 0x31,
|
|
MPR121_FDLF = 0x32,
|
|
MPR121_NHDT = 0x33,
|
|
MPR121_NCLT = 0x34,
|
|
MPR121_FDLT = 0x35,
|
|
MPR121_TOUCHTH_0 = 0x41,
|
|
MPR121_RELEASETH_0 = 0x42,
|
|
MPR121_DEBOUNCE = 0x5B,
|
|
MPR121_CONFIG1 = 0x5C,
|
|
MPR121_CONFIG2 = 0x5D,
|
|
MPR121_CHARGECURR_0 = 0x5F,
|
|
MPR121_CHARGETIME_1 = 0x6C,
|
|
MPR121_ECR = 0x5E,
|
|
MPR121_AUTOCONFIG0 = 0x7B,
|
|
MPR121_AUTOCONFIG1 = 0x7C,
|
|
MPR121_UPLIMIT = 0x7D,
|
|
MPR121_LOWLIMIT = 0x7E,
|
|
MPR121_TARGETLIMIT = 0x7F,
|
|
MPR121_GPIODIR = 0x76,
|
|
MPR121_GPIOEN = 0x77,
|
|
MPR121_GPIOSET = 0x78,
|
|
MPR121_GPIOCLR = 0x79,
|
|
MPR121_GPIOTOGGLE = 0x7A,
|
|
MPR121_SOFTRESET = 0x80;
|
|
|
|
|
|
class MPR121 extends EventEmitter {
|
|
|
|
constructor(address, bus, irq, interval) [
|
|
|
|
super();
|
|
|
|
this.address = address || MPR121_I2CADDR_DEFAULT;
|
|
this.bus = Number.isInteger(bus) ? bus : 1;
|
|
this.irq = irq || false;
|
|
this.interval = interval || 100;
|
|
|
|
this.state = [false, false, false, false, false, false, false, false, false, false, false, false];
|
|
this.device = false;
|
|
this.ready = false;
|
|
this.timer = false;
|
|
|
|
this.init().then(this.reset).then(this.configure).then(this.startPolling);
|
|
|
|
}
|
|
|
|
init() {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
this.device = i2c.open(this.bus, (err) => {
|
|
if(err) return reject(err);
|
|
resolve();
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
reset() {
|
|
|
|
return this.writeByte(MPR121_SOFTRESET, 0x63)
|
|
.then(() => {
|
|
return new Promise((resolve, reject) => {
|
|
setTimeout(resolve, 10);
|
|
});
|
|
})
|
|
.then(this.writeByte(MPR121_ECR, 0x00));
|
|
|
|
}
|
|
|
|
configure() {
|
|
|
|
return this.checkDevice()
|
|
|
|
// Set threshold for touch and release to default values.
|
|
.then(this.set_thresholds(12, 6))
|
|
|
|
// Configure baseline filtering control registers.
|
|
.then(this.writeByte(MPR121_MHDR, 0x01))
|
|
.then(this.writeByte(MPR121_NHDR, 0x01))
|
|
.then(this.writeByte(MPR121_NCLR, 0x0E))
|
|
.then(this.writeByte(MPR121_FDLR, 0x00))
|
|
.then(this.writeByte(MPR121_MHDF, 0x01))
|
|
.then(this.writeByte(MPR121_NHDF, 0x05))
|
|
.then(this.writeByte(MPR121_NCLF, 0x01))
|
|
.then(this.writeByte(MPR121_FDLF, 0x00))
|
|
.then(this.writeByte(MPR121_NHDT, 0x00))
|
|
.then(this.writeByte(MPR121_NCLT, 0x00))
|
|
.then(this.writeByte(MPR121_FDLT, 0x00))
|
|
|
|
// Set other configuration registers.
|
|
.then(this.writeByte(MPR121_DEBOUNCE, 0))
|
|
.then(this.writeByte(MPR121_CONFIG1, 0x10)) // default, 16uA charge current
|
|
.then(this.writeByte(MPR121_CONFIG2, 0x20)) // 0.5uS encoding, 1ms period
|
|
|
|
// Enable all electrodes.
|
|
.then(this.writeByte(MPR121_ECR, 0x8F)) // start with first 5 bits of baseline tracking
|
|
|
|
.then(() => {
|
|
this.ready = true;
|
|
this.emit('ready');
|
|
});
|
|
|
|
}
|
|
|
|
checkDevice() {
|
|
|
|
return this.readByte(MPR121_CONFIG2)
|
|
.then((c) => {
|
|
|
|
if(c != 0x24)
|
|
return Promise.reject(`MPR121 Error - device not found. Check address, bus and wiring. (${c} != 36)`);
|
|
|
|
return Promise.resolve();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
setThresholds(touch, release) {
|
|
|
|
if(touch < 0 || touch > 255) return Promise.reject();
|
|
if(release < 0 || release > 255) return Promise.reject();
|
|
|
|
let promises = [];
|
|
|
|
for(let i = 0; i <= 12; i++) {
|
|
promises.push(this.writeByte(MPR121_TOUCHTH_0 + 2 * i, touch));
|
|
promises.push(this.writeByte(MPR121_RELEASETH_0 + 2 * i, release));
|
|
}
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
|
|
|
startPolling() {
|
|
|
|
if(! this.ready) return this.on('ready', this.startPolling);
|
|
if(! this.interval) return;
|
|
|
|
this.timer = setInterval(() => {
|
|
this.touched().then(updateState);
|
|
}, this.interval);
|
|
|
|
}
|
|
|
|
stopPolling() {
|
|
|
|
if(! this.timer) return;
|
|
|
|
clearInterval(this.timer);
|
|
this.timer = false;
|
|
|
|
}
|
|
|
|
updateState(touched) {
|
|
|
|
this.state.forEach((previous, i) => {
|
|
|
|
const current = (touched & (1 << i)) > 0;
|
|
|
|
if(previous == current) return;
|
|
|
|
this.state[i] = current;
|
|
|
|
// emit pin number for touch and release
|
|
if(current)
|
|
this.emit('touch', i);
|
|
else
|
|
this.emit('release', i);
|
|
|
|
// emit state on pin number
|
|
this.emit(i, current);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
filteredData(pin) {
|
|
if(pin < 0 || pin >= 12) return Promise.reject();
|
|
return this.readWord(MPR121_FILTDATA_0L + (pin * 2));
|
|
}
|
|
|
|
baselineData(pin) {
|
|
|
|
if(pin < 0 || pin >= 12) return Promise.reject();
|
|
|
|
return this.readByte(MPR121_BASELINE_0 + pin)
|
|
.then((bl) => {
|
|
return Promise.resolve(bl << 2);
|
|
});
|
|
|
|
}
|
|
|
|
touched() {
|
|
|
|
return this.readWord(MPR121_TOUCHSTATUS_L)
|
|
.then((t) => {
|
|
return Promise.resolve(t & 0x0FFF);
|
|
});
|
|
|
|
}
|
|
|
|
isTouched = function(pin) {
|
|
|
|
if(! ready) return false;
|
|
if(pin < 0 || pin >= 12) return false;
|
|
|
|
return this.state[pin];
|
|
|
|
}
|
|
|
|
readByte(reg) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
this.device.readByte(this.address, reg, (err, b) => {
|
|
if(err) return reject(err);
|
|
resolve(b);
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
readWord(reg) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
this.device.readByte(this.address, reg, (err, w) => {
|
|
if(err) return reject(err);
|
|
resolve(w);
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
writeByte(reg, value) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
this.device.writeByte(this.address, reg, value & 0xFF, (err) => {
|
|
if(err) return reject(err);
|
|
resolve();
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|