Johnny-five: Raw 0-1023 vals from MPR121?

Created on 11 Nov 2018  路  25Comments  路  Source: rwaldron/johnny-five

I have 4 MPR121s connected and working (thanks to some of your sample code), but I've hit a snag as I move forward with what I need to get done. My sense is that it's no supported, but I wanted to see if I'm wrong and/or there's a workaround.

I'm trying to get the raw data (0-1023) from the sensors in a continuous stream (so I can use all 48 data points in a series of dynamic visualizations), but I'm not finding a way to do that in the documentation or in any forums. Is there a way to get the raw vals from an MPR121?

Thanks for any advice/guidance you can offer - Dave

Keypad stale

Most helpful comment

Here's a hack that will get you past that problem while I work on the real solution. We are reading more data than necessary since we are starting at 0 and pulling just the data we need from the resulting array:

````js
const five = require("johnny-five");
const board = new five.Board();

board.on("ready", function() {

const touchpad = new five.Touchpad({
controller: "MPR121",
address: 0x5A
});

const getRawValues = () => {
this.io.i2cRead(0x5A, 0x04, 0x1c, function(bytes) {
let result = [];
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1+4], bytes[i2+4]));
}
console.log(result);
});
};

getRawValues();

});
````

All 25 comments

Hi @dmrieder

I took a quick look and the Johnny-Five keypad library only reads the first two bytes of the register which just gives the on/off state of the keys. The data is available from the MPR121 though. You just need to add a method that does the read you need (0x04 through 0x1D). Your project sounds totally cool and I'd be happy to help, but I won't be able to do anything with it for a day or two.

Thank you! I really appreciate the interest and help to add this to the library. Looking forward to it - and happy to help, if I can.

I don't know if this is something we would add to the keyboard class, so I'm just going to work up a code example of how I think it could work for you. Note that I don't have an MPR121 so I won't be able to test it but hopefully it's close enough to get you rolling.

````js
const five = require("johnny-five");
const board = new five.Board();

board.on("ready", function() {

const touchpad = new five.Touchpad({
controller: "MPR121",
address: 0x5A
});

const getRawValues = () => {
let result = [];
this.io.i2cRead(0x5A, 0x04, 24, function(bytes) {
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1], bytes[i2]));
}
});
return result;
};

console.log(getRawValues());

});
````

Reference doc

Thanks for doing this. I got an error, and I'm not sure how to correct it. If you have a moment, maybe it's an easy fix? (Or maybe it won't work w/Arduino?). It looks like it has to do with the i2cRead() method. Thanks again.

>

C:\Users\dmrieder\nodestuff\node_modules\johnny-five\raw.js:39
this.io.i2cRead(0x5A, 0x04, 24, function(bytes) {
^

TypeError: Cannot read property 'i2cRead' of undefined
at getRawValues (C:\Users\dmrieder\nodestuff\node_modules\johnny-five\raw.js:39:15)
at Keypad.a.on (C:\Users\dmrieder\nodestuff\node_modules\johnny-five\raw.js:47:17)
at emitOne (events.js:116:13)
at Keypad.emit (events.js:211:7)
at Keypad. (C:\Users\dmrieder\nodestuff\node_modules\johnny-five\lib\keypad.js:637:12)
at Array.forEach ()
at Keypad. (C:\Users\dmrieder\nodestuff\node_modules\johnny-five\lib\keypad.js:636:19)
at invokeFunc (C:\Users\dmrieder\nodestuff\node_modules\lodash.debounce\index.js:160:19)
at trailingEdge (C:\Users\dmrieder\nodestuff\node_modules\lodash.debounce\index.js:207:14)
at Timeout.timerExpired [as _onTimeout] (C:\Users\dmrieder\nodestuff\node_modules\lodash.debounce\index.js:195:14)

<<<<<<

It has to do with scoping of this on line 39 in your file. I'm guessing you've wrapped it in another function that has its own this. Here's an easy fix:

js board.io.i2cRead(0x5A, 0x04, 24, function(bytes) {

Replace board with whatever variable you've assigned your board to, if it's not board.

Progress, but result is an empty array. When I test for length, it comes back as 0. Is this because it's not possible to access those registers? Thanks again for any help you can offer.

Here's my entire file w/your code included in it. Your function is at the bottom (remade into named function):
````js
const five = require("johnny-five");
const board = new five.Board;

board.on("ready", () => {
const touchpad = new five.Touchpad({
controller: "MPR121_SHIELD",

});

const controller = "MPR121_SHIELD";

const a = new five.Touchpad({
address: 0x5A,
controller
});

const b = new five.Touchpad({
address: 0x5B,
controller
});
const c = new five.Touchpad({
address: 0x5C,
controller
});

const d = new five.Touchpad({
address: 0x5D,
controller
});

a.on("press", (event) => {

console.log(getRawValues());

});

b.on("press", (event) => {

console.log(getRawValues());

});

c.on("press", (event) => {

console.log(getRawValues());

});

d.on("press", (event) => {

console.log(getRawValues());

});

});

function getRawValues() {
let result = [];
board.io.i2cRead(0x5A, 0x04, 24, function(bytes) {
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1], bytes[i2]));
}
});

console.log(result.length);
return result;
};
````

Let me look into the configuration details to make sure we don't have to do something different there, but the way it's written above will only read touchpad "a".

Try this (quick and dirty) option:

````js
const five = require("johnny-five");
const board = new five.Board;

board.on("ready", () => {
const touchpad = new five.Touchpad({
controller: "MPR121_SHIELD",

});

const controller = "MPR121_SHIELD";

const a = new five.Touchpad({
address: 0x5A,
controller
});

const b = new five.Touchpad({
address: 0x5B,
controller
});
const c = new five.Touchpad({
address: 0x5C,
controller
});

const d = new five.Touchpad({
address: 0x5D,
controller
});

a.on("press", (event) => {

console.log(getRawValues(0x5A));

});

b.on("press", (event) => {

console.log(getRawValues(0x5B));

});

c.on("press", (event) => {

console.log(getRawValues(0x5C));

});

d.on("press", (event) => {

console.log(getRawValues(0x5D));

});

});

function getRawValues(address) {
let result = [];
board.io.i2cRead(address, 0x04, 24, function(bytes) {
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1], bytes[i2]));
}
console.log(Device at ${address}: ${result.length});
});

return result;
};
````

I'll go read the docs more closely and see if I missed something.

Thank you. I really appreciate your time on this. If it helps you, when I ran the new code you just posted, after I pressed one of the pins, I got values like the following:

Device at 92: 972
Device at 93: 4980
Device at 93: 4908
Device at 93: 4992
Device at 93: 4920
Device at 92: 2064
Device at 92: 2052
Device at 92: 1836
Device at 92: 984
Device at 93: 5004
Device at 93: 4932
Device at 93: 5016
Device at 93: 4944
Device at 92: 2076
Device at 92: 2064
Device at 92: 1848
Device at 92: 996
Device at 93: 5028
Device at 93: 4956
Device at 93: 5040
Device at 93: 4968
Device at 92: 2088
Device at 92: 2076
Device at 92: 1860
Device at 92: 1008

Wow, those array lengths are not at all what I expected. I鈥檒l try and replicate (as best I can) later this evening.

Okay, I see a couple of things I did wrong before. Let's try this simpler test:

````js
const five = require("johnny-five");
const board = new five.Board({samplingInterval: 500});

board.on("ready", () => {
const a = new five.Touchpad({
address: 0x5A,
controller: "MPR121_SHIELD"
});

board.io.i2cRead(0x5A, 0x04, 24, function(bytes) {
let result = [];
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1], bytes[i2]));
}
console.log(result);
});

});
````

@dmrieder Replicating was harder than I expected so I've just ordered an MPR121. MIght not be here for a while due to holidays. Did you have a chance to try that simpler test?

@dtex I finally had a chance to try out the most recent function. I'm not sure that the results are a lot better, unfortunately. I tried using the simpler program you wrote above, but it threw an error, so I just put the part that you'd changed in the code that had worked just before it.

Thank you for purchasing the sensor! I know you all are busy, but I hope that a method can be written/added to the library, so I can easily grab the raw vals from the MPR121 for a book-length project this year and next.

Here's a sample of the output:

90
[ 1020,
0,
817,
818,
648,
684,
773,
773,
766,
755,
763,
797,
1020,
0,
817,
818,
648,
684,
773,
773,
766,
755,
763,
797,
1020,
0,
817,
818,
648,
684,
773,
773,
766,
755,
763,
797,
1020,
0,
817,
818,
648,
684,
773,
773,
766,
755,
763,
797,
1020,
0,
817,
818,
648,
684,
773,
773,
766,
755,
763,
797,
1020,
0,
817,
817,
534,
671,
761,
762,
758,
741,
755,
781,
1020,
0,
817,
817,
534,
671,
761,
762,
758,
741,
755,
781,
1020,
0,
817,
817,
534,
671,
761,
762,
758,
741,
755,
781,
1020,
0,
817,
817,
... 440 more items ]

I had my finger pressed down on 3-4 pins out of the 12 on sensor 90, so while the numbers look like they are generally in the 5v range, I'm not seeing lower vals where I had the pin activated.

Here's the code:

const five = require("johnny-five");
const board = new five.Board;

board.on("ready", () => {
const touchpad = new five.Touchpad({
controller: "MPR121_SHIELD",

});

const controller = "MPR121_SHIELD";

const a = new five.Touchpad({
address: 0x5A,
controller
});

const b = new five.Touchpad({
address: 0x5B,
controller
});
const c = new five.Touchpad({
address: 0x5C,
controller
});

const d = new five.Touchpad({
address: 0x5D,
controller
});

a.on("press", (event) => {

console.log(getRawValues(0x5A));

});

b.on("press", (event) => {

console.log(getRawValues(0x5B));

});

c.on("press", (event) => {

console.log(getRawValues(0x5C));

});

d.on("press", (event) => {

console.log(getRawValues(0x5D));

});

});

function getRawValues(address) {
let result = [];
board.io.i2cRead(address, 0x04, 24, function(bytes) {
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1], bytes[i2]));
}
console.log("----------------------");
console.log(address);
console.log(result);

});

return result;
};

Ya, I'm doing something very wrong. Those array lengths don't make any sense to me. I went ahead and ordered my own MPR121 to test with. It should be here in the next day or two. I'll keep you posted.

Looking forward it.

My board arrived and I got it all soldered/wired up. The array length thing is fixed. It was one of those SMH kinda bugs (scope of result). I'm getting good results with this code, except on pins 10 and 11. Those two must be configured differently. I'll dig into the datasheet and compare with the J5 initialization code. For now:

````js
const five = require("johnny-five");
const board = new five.Board();

board.on("ready", function() {

const touchpad = new five.Touchpad({
controller: "MPR121",
address: 0x5A
});

const getRawValues = () => {
this.io.i2cRead(0x5A, 0x04, 0x18, function(bytes) {
let result = [];
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1], bytes[i2]));
}
console.log(result);
});
return result;
};

getRawValues();

});
````

Thanks for sticking with this!

I hooked up a single sensor (shield) for this test:
https://www.adafruit.com/product/2024

Here is some sample output (I was pressing down on 6 (vals in the 50s) and 9 (vals in the 40s):

[ 576, 0, 812, 812, 817, 814, 809, 809, 56, 809, 804, 41 ]
[ 576, 0, 812, 812, 817, 814, 809, 809, 56, 809, 804, 41 ]
[ 576, 0, 812, 812, 817, 814, 809, 809, 56, 809, 804, 41 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 813, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 814, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 814, 809, 809, 56, 809, 804, 40 ]
[ 576, 0, 812, 812, 817, 814, 809, 809, 56, 809, 804, 40 ]

It looks to me like the indices are off? Indices 0 and 1 are wonky, but the inputs for 10 and 11 aren't saved in the array that logs to the screen.

Summary:

0 gives me a diff val as I press 0-11 (and no idea is a val for)
1 always zero
2 corresponds to input 0
3 corresponds to input 1
4 corresponds to input 2
5 corresponds to input 3
6 corresponds to input 4
7 corresponds to input 5
8 corresponds to input 6
9 corresponds to input 7
10 corresponds to input 8
11 corresponds to input 9

Inputs 10 and 11 do not show up in the array written to the screen. They do change the val of indice 0, but no idea what the vals are related to...

I will try with the 4 I have connected in the next post/reply. The four use this breakboard:
https://www.adafruit.com/product/1982

Thanks again.

I was using the Adafruit board. Same results.

Let me know when you think you've done what you can. Would love to have fully functional, but maybe you are running out of time for this request. Thanks again for all you've done!

The second param (register address) on i2cRead is being ignored. Looks like a bug in firmata.js, firmata protocol, or firmata itself. Investigating on that side now.

Here's a hack that will get you past that problem while I work on the real solution. We are reading more data than necessary since we are starting at 0 and pulling just the data we need from the resulting array:

````js
const five = require("johnny-five");
const board = new five.Board();

board.on("ready", function() {

const touchpad = new five.Touchpad({
controller: "MPR121",
address: 0x5A
});

const getRawValues = () => {
this.io.i2cRead(0x5A, 0x04, 0x1c, function(bytes) {
let result = [];
for (let i = 0; i < 12; i++) {
result.push(five.Fn.uint16(bytes[i2+1+4], bytes[i2+4]));
}
console.log(result);
});
};

getRawValues();

});
````

Just tried it out w/the single shield, and it's working. :) Gonna try it w/the the 4 sensor set-up a little later. Thanks! Looking forward to the real solution you mentioned.

It may be that it's not a firmata issue after all. It is conceivable that this device doesn't allow you to specify a read register in order to ensure that a complete "moment in time" is reported. Still digging.

Okay, so I took J5 out of the picture with this code:

https://gist.github.com/dtex/b0e22a8058ec95b24ea82d5fd875ed7a

But it stil doesn't read from the correct register. Based on Adafruits code, I can see that it should be reading from the correct registers and I confirmed that the firmata sketch on the board is sending the desired register address just before the read, so I'm kinda stumped. Don't hold your breath on the "real" solution. I have no idea what's going on.

Use the hack above and you should be good to go.

Thanks again for all the time and energy you put into this - especially today! I'll let you know when the project this will be based on is ready for publication (hopefully this summer, or else fall).

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Zhairgling picture Zhairgling  路  3Comments

ZacB picture ZacB  路  8Comments

hxlnt picture hxlnt  路  6Comments

teuteuguy picture teuteuguy  路  4Comments

moeinrahimi picture moeinrahimi  路  4Comments