obniz
Version:
obniz sdk for javascript
383 lines (345 loc) • 11.4 kB
JavaScript
class FlickHat {
constructor() {
this.keys = ['vcc', 'gnd', 'sda', 'scl', 'reset', 'ts', 'led1', 'led2'];
this.requiredKeys = ['gnd', 'sda', 'scl', 'reset', 'ts'];
this.displayIoNames = {
//vcc: 'vcc', //5v
sda: 'sda',
scl: 'scl',
gnd: 'gnd',
reset: 'reset',
ts: 'ts',
};
}
static info() {
return {
name: 'FlickHat',
};
}
wired(obniz) {
this.obniz = obniz;
this.address = 0x42;
if (this.obniz.isValidIO(this.params.vcc)) {
this.obniz.getIO(this.params.vcc).drive('5v');
this.obniz.getIO(this.params.vcc).output(true);
}
this.obniz.getIO(this.params.gnd).output(false);
this.io_reset = this.obniz.getIO(this.params.reset);
this.io_reset.drive('3v');
this.io_ts = this.obniz.getIO(this.params.ts);
this.io_ts.drive('open-drain');
this.io_ts.pull('3v');
this.params.mode = 'master';
this.params.pull = '3v';
this.params.clock = 100 * 1000; //100KHz
//PeripheralI2C
this.i2c = this.obniz.getI2CWithConfig(this.params);
if (this.obniz.isValidIO(this.params.led1)) {
this.led1 = this.obniz.wired('LED', { anode: this.params.led1 });
}
if (this.obniz.isValidIO(this.params.led2)) {
this.led2 = this.obniz.wired('LED', { anode: this.params.led2 });
}
}
async start(callbackFwInfo) {
this.io_ts.pull('3v');
this.io_reset.output(false);
await this.obniz.wait(50);
this.io_reset.output(true);
await this.obniz.wait(50);
this.onfwinfo = callbackFwInfo;
this.fwInfo = {
fwValid: 0,
fwInfoReceived: false,
};
this.rotation = 0;
this.lastRotation = 0;
this.readSize = 132;
await this.polling();
await this.obniz.wait(200);
this.i2c.write(this.address, [
0x10,
0x00,
0x00,
0xa2,
0xa1,
0x00,
0x00,
0x00,
0x1f,
0x00,
0x00,
0x00,
0xff,
0xff,
0xff,
0xff,
]);
await this.obniz.wait(100);
this.i2c.write(this.address, [
0x10,
0x00,
0x00,
0xa2,
0x80,
0x00,
0x00,
0x00,
0x3f,
0x00,
0x00,
0x00,
0x3f,
0x00,
0x00,
0x00,
]);
}
_dataArray2string(data) {
let result = '';
for (let n of data) {
result += String.fromCharCode(n);
}
return result;
}
async polling(timeout) {
timeout = timeout || 3000; //default: 3s
//DataOutputConfigMask 2byte
// const maskDSPStatus = 1;
const maskGestureInfo = 1 << 1;
const maskTouchInfo = 1 << 2;
const maskAirWheelInfo = 1 << 3;
const maskXYZPosition = 1 << 4;
//SystemInfo 1byte
const sysPositionValid = 1;
const sysAirWheelValid = 1 << 1;
// const sysDSPRunning = 1 << 7;
let startTime = new Date();
let ts = true;
while (ts && new Date() - startTime < timeout)
ts = await this.io_ts.inputWait();
if (!ts) {
this.io_ts.pull('0v');
//await this.obniz.wait(1);
let data = await this.i2c.readWait(this.address, this.readSize);
let size = data[0];
// let flag = data[1];
let seq = data[2];
let msgID = data[3];
if (size != 0xff && size > 0) {
if (this.debugprint || this.obniz.debugprint) {
console.log(
'flickHat: ' + data.slice(0, size).map(v => '0x' + v.toString(16))
);
}
let configmask, sysinfo, gesture, touch, airwheel, statusInfo, fwInfo;
switch (msgID) {
case 0x91: //sensor data output
configmask = data[4] | (data[5] << 8); //little endian
// let timestamp = data[6]; // 200hz, 8-bit counter, max ~1.25sec
sysinfo = data[7];
// let dspstatus = data.slice(8, 10);
gesture = data.slice(10, 14);
touch = data.slice(14, 18);
airwheel = data.slice(18, 20);
// let xyz = data.slice(20, 26);
// let noisepow = data.slice(27, 30);
if (
gesture[0] == 255 &&
gesture[1] == 255 &&
gesture[2] == 255 &&
gesture[3] == 255
)
break;
if (configmask & maskXYZPosition && sysinfo & sysPositionValid) {
let xyz = {
//little endian
x: (data[20] | (data[21] << 8)) / 65536,
y: (data[22] | (data[23] << 8)) / 65536,
z: (data[24] | (data[25] << 8)) / 65536,
seq: seq,
};
this.xyz = xyz;
if (typeof this.onxyz == 'function') this.onxyz(xyz);
}
if (configmask & maskGestureInfo && gesture[0] > 0) {
this.lastGesture = gesture[0];
const gestures = [
['', '', ''], //no gesture
['garbage', '', ''],
['flick', 'west', 'east'], //2
['flick', 'east', 'west'], //3
['flick', 'south', 'north'], //4
['flick', 'north', 'south'], //5
['circle', 'clockwise', ''],
['circle', 'counter-clockwise', ''][('wave', 'x', '')],
['wave', 'y', ''],
['hold', '', ''],
];
for (let index in gestures) {
if (
index == gesture[0] &&
typeof this.ongestureall == 'function'
)
this.ongestureall({
action: gestures[index][0],
from: gestures[index][1],
to: gestures[index][2],
raw: gesture,
seq: seq,
});
if (
index == gesture[0] &&
gestures[index][0] == 'flick' &&
typeof this.ongesture == 'function'
)
this.ongesture({
action: 'gesture',
from: gestures[index][1],
to: gestures[index][2],
raw: gesture,
seq: seq,
});
}
}
if (
configmask & maskTouchInfo &&
!(touch[0] == 0 && touch[1] == 0) &&
touch[3] == 0
) {
//console.log('touch: ' + touch.map(v => '0x' + v.toString(16)));
let touchAction = touch[0] | (touch[1] << 8); //little endian
if (touchAction == 0xffff) break;
// let touchCount = touch[2] * 5; // touch counter value * 5[ms]
const actions = [
['touch', 'south'], //0
['touch', 'west'], //1
['touch', 'north'], //2
['touch', 'east'], //3
['touch', 'center'], //4
['tap', 'south'], //5
['tap', 'west'], //6
['tap', 'north'], //7
['tap', 'east'], //8
['tap', 'center'], //9
['doubletap', 'south'], //10
['doubletap', 'west'], //11
['doubletap', 'north'], //12
['doubletap', 'east'], //13
['doubletap', 'center'], //14
];
let touches = [];
let taps = [];
let doubletaps = [];
this.lastTouch = touchAction;
let comp = 1;
for (let index in actions) {
let value = actions[index];
if (touchAction & comp) {
//console.log(`touchAction:${touchAction.toString(16)}, comp:${comp.toString(16)}, index:${index}, group:${group}`);
switch (value[0]) {
case 'touch':
touches.push(value[1]);
break;
case 'tap':
taps.push(value[1]);
break;
case 'doubletap':
doubletaps.push(value[1]);
break;
default:
}
}
comp <<= 1;
}
if (touches.length > 0 && typeof this.ontouch == 'function')
this.ontouch({
action: 'touch',
positions: touches,
raw: touch,
seq: seq,
});
if (taps.length > 0 && typeof this.ontap == 'function')
this.ontap({
action: 'tap',
positions: taps,
raw: touch,
seq: seq,
});
if (
doubletaps.length > 0 &&
typeof this.ondoubletap == 'function'
)
this.ondoubletap({
action: 'doubletap',
positions: doubletaps,
raw: touch,
seq: seq,
});
}
if (configmask & maskAirWheelInfo && sysinfo & sysAirWheelValid) {
let delta = (airwheel[0] - this.lastRotation) / 32.0;
this.rotation += delta * 360.0;
this.rotation %= 360;
if (delta != 0 && delta > -0.5 && delta < 0.5) {
if (typeof this.onairwheel == 'function')
this.onairwheel({
delta: delta * 360.0,
rotation: this.rotation,
raw: airwheel,
seq: seq,
});
}
this.lastRotation = airwheel[0];
}
break;
case 0x15: //system status
statusInfo = {
msgId: data[4],
maxCmdSize: data[5],
error: data[6] | (data[7] << 8), //little endian
};
this.statusInfo = statusInfo;
if (this.debugprint || this.obniz.debugprint) {
console.log(
`flickHat: system status: {msgId: ${
statusInfo.msgId
}, maxCmdSize: ${statusInfo.maxCmdSize}, error: ${
statusInfo.error
}}`
);
}
break;
case 0x83: // farmware information
fwInfo = {
fwValid: data[4] == 0xaa,
hwRev: [data[5], data[6]],
paramStartAddr: data[7] * 128,
libLoaderVer: [data[8], data[9]],
libLoaderPlatform: data[10],
fwStartAddr: data[11] * 128,
fwVersion: this._dataArray2string(data.slice(12, 132)).split(
'\0'
)[0],
fwInfoReceived: true,
};
this.fwInfo = fwInfo;
if (typeof this.onfwinfo == 'function') this.onfwinfo(fwInfo);
this.readSize = 26;
break;
default:
console.error(
`unknown message: 0x${msgID.toString(16)}, data:${data
.slice(0, size)
.map(v => '0x' + v.toString(16))}`
);
}
}
this.io_ts.pull('3v');
//await this.obniz.wait(1);
}
}
}
if (typeof module === 'object') {
module.exports = FlickHat;
}