Skip to content

Instantly share code, notes, and snippets.

@TooTallNate
Last active August 29, 2015 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TooTallNate/8111001d7abe21220ccc to your computer and use it in GitHub Desktop.
Save TooTallNate/8111001d7abe21220ccc to your computer and use it in GitHub Desktop.
Touchscreen class for the PiTFT screen for Raspberry Pi
var fs = require('fs');
var Struct = require('ref-struct');
var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
// https://www.kernel.org/doc/Documentation/input/event-codes.txt
// https://www.kernel.org/doc/Documentation/input/input.txt
// /usr/include/linux/input.h
/*
* Event types
*/
var EV_SYN = 0x00;
var EV_KEY = 0x01;
var EV_ABS = 0x03;
/*
* Absolute axes
*/
var ABS_X = 0x00;
var ABS_Y = 0x01;
var ABS_PRESSURE = 0x18;
/*
* Keys and buttons
*/
var BTN_TOUCH = 0x14a;
// struct input_event {
// struct timeval time;
// unsigned short type;
// unsigned short code;
// unsigned int value;
// };
var time_t = 'long';
var suseconds_t = 'long';
var timeval = Struct({
tv_sec: time_t,
tv_usec: suseconds_t
})
var input_event = Struct({
time: timeval,
type: 'ushort',
code: 'ushort',
value: 'uint'
});
// Expose as a nice JavaScript API
function Touchscreen (filename) {
this.onOpen = this.onOpen.bind(this);
this.onRead = this.onRead.bind(this);
this.input = new input_event;
this.buffer = this.input.ref();
this.filename = filename || '/dev/input/touchscreen';
this.touch = {
identifier: 1,
timeStamp: 0,
type: 'touchstart',
screenX: 0,
screenY: 0,
force: 0,
};
fs.open(this.filename, 'r', this.onOpen);
}
inherits(Touchscreen, EventEmitter);
Touchscreen.prototype.onOpen = function (err, fd) {
if (err) return this.emit('error', err);
this.fd = fd;
this.emit('open');
this.startRead();
};
Touchscreen.prototype.startRead = function () {
fs.read(this.fd, this.buffer, 0, input_event.size, null, this.onRead);
};
Touchscreen.prototype.onRead = function (err, bytesRead) {
if (err) return this.emit('error', err);
if (bytesRead !== input_event.size) {
return this.emit('error', new Error('only read ' +
bytesRead + ' bytes, expected ' + input_event.size));
}
switch (this.input.type) {
case EV_SYN:
this.touch.timeStamp = (this.input.time.tv_sec * 1000) + ((this.input.time.tv_usec / 1000) | 0);
this.emit(this.touch.type, this.touch);
if (this.touch.type === 'touchstart') {
this.touch.type = 'touchmove';
}
break;
case EV_KEY:
switch (this.input.code) {
case BTN_TOUCH:
if (this.input.value) {
// finger down
this.touch.type = 'touchstart';
} else {
// finger up
this.touch.type = 'touchend';
}
break;
default:
return this.emit('error', new Error('bad EV_KEY code! ' + this.input.code));
}
break;
case EV_ABS:
switch (this.input.code) {
// X and Y axes are rotated 90 degrees on PiTFT, so
// ABS_X is really the Y-axis and ABS_Y is the X-axis
// (when the PiTFT is rotated horizontally)
case ABS_X:
this.touch.screenY = (this.input.value / 4095) * 240;
break;
case ABS_Y:
// X axis origin is to the right for some reason, so we have to
// swap the value to begin on the left side by taking 320 - value
this.touch.screenX = 320 - ((this.input.value / 4095) * 320);
break;
case ABS_PRESSURE:
this.touch.force = this.input.value / 255;
break;
default:
return this.emit('error', new Error('bad EV_ABS code! ' + this.input.code));
}
break;
default:
console.error(this.input);
break;
}
if (this.fd) this.startRead();
};
Touchscreen.prototype.close = function (callback) {
fs.close(this.fd, callback);
this.fd = undefined;
};
// Sample usage
var ts = new Touchscreen();
ts.once('open', function () {
console.log('Touch me!');
});
ts.on('touchstart', console.log.bind(console, 'touchstart'));
ts.on('touchmove', console.log.bind(console, 'touchmove'));
ts.on('touchend', console.log.bind(console, 'touchend'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment