thethingbox-sensortag
Version:
node.js lib for the TI SensorTag
556 lines (432 loc) • 19.8 kB
JavaScript
/*jshint loopfunc: true */
var events = require('events');
var util = require('util');
var noble = require('thethingbox-noble');
//http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
var GENERIC_ACCESS_UUID = '1800';
var GENERIC_ATTRIBUTE_UUID = '1801';
var DEVICE_INFORMATION_UUID = '180a';
var IR_TEMPERATURE_UUID = 'f000aa0004514000b000000000000000';
var ACCELEROMETER_UUID = 'f000aa1004514000b000000000000000';
var HUMIDITY_UUID = 'f000aa2004514000b000000000000000';
var MAGNETOMETER_UUID = 'f000aa3004514000b000000000000000';
var BAROMETRIC_PRESSURE_UUID = 'f000aa4004514000b000000000000000';
var GYROSCOPE_UUID = 'f000aa5004514000b000000000000000';
var SIMPLE_KEY_UUID = 'ffe0';
var TEST_UUID = 'f000aa6004514000b000000000000000';
var OAD_UUID = 'f000ffc004514000b000000000000000';
var DEVICE_NAME_UUID = '2a00';
var APPEARANCE_UUID = '2a01';
var PERIPHERAL_PRIVACY_FLAG_UUID = '2a02';
var RECONNECTION_ADDRESS_UUID = '2a03';
var PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_UUID = '2a04';
var SYSTEM_ID_UUID = '2a23';
var MODEL_NUMBER_UUID = '2a24';
var SERIAL_NUMBER_UUID = '2a25';
var FIRMWARE_REVISION_UUID = '2a26';
var HARDWARE_REVISION_UUID = '2a27';
var SOFTWARE_REVISION_UUID = '2a28';
var MANUFACTURER_NAME_UUID = '2a29';
var REGULATORY_CERTIFICATE_DATA_LIST_UUID = '2a2a';
var PNP_ID_UUID = '2a50';
var IR_TEMPERATURE_CONFIG_UUID = 'f000aa0204514000b000000000000000';
var IR_TEMPERATURE_DATA_UUID = 'f000aa0104514000b000000000000000';
var ACCELEROMETER_CONFIG_UUID = 'f000aa1204514000b000000000000000';
var ACCELEROMETER_DATA_UUID = 'f000aa1104514000b000000000000000';
var ACCELEROMETER_PERIOD_UUID = 'f000aa1304514000b000000000000000';
var HUMIDITY_CONFIG_UUID = 'f000aa2204514000b000000000000000';
var HUMIDITY_DATA_UUID = 'f000aa2104514000b000000000000000';
var MAGNETOMETER_CONFIG_UUID = 'f000aa3204514000b000000000000000';
var MAGNETOMETER_DATA_UUID = 'f000aa3104514000b000000000000000';
var MAGNETOMETER_PERIOD_UUID = 'f000aa3304514000b000000000000000';
var BAROMETRIC_PRESSURE_CONFIG_UUID = 'f000aa4204514000b000000000000000';
var BAROMETRIC_PRESSURE_DATA_UUID = 'f000aa4104514000b000000000000000';
var BAROMETRIC_PRESSURE_CALIBRATION_UUID = 'f000aa4304514000b000000000000000';
var GYROSCOPE_CONFIG_UUID = 'f000aa5204514000b000000000000000';
var GYROSCOPE_DATA_UUID = 'f000aa5104514000b000000000000000';
var GYROSCOPE_PERIOD_UUID = 'f000aa5304514000b000000000000000';
var SIMPLE_KEY_DATA_UUID = 'ffe1';
function SensorTag(peripheral) {
this._peripheral = peripheral;
this._services = {};
this._characteristics = {};
this.uuid = peripheral.uuid;
this._peripheral.on('disconnect', this.onDisconnect.bind(this));
}
util.inherits(SensorTag, events.EventEmitter);
SensorTag.discover = function(callback, uuid) {
var startScanningOnPowerOn = function() {
if (noble.state === 'poweredOn') {
var onDiscover = function(peripheral) {
console.log("UUID : "+ peripheral.uuid);
console.log("localname : " + peripheral.advertisement.localName);
if (peripheral.advertisement.localName === 'SensorTag' &&
(uuid === undefined || uuid === peripheral.uuid)) {
noble.removeListener('discover', onDiscover);
noble.stopScanning();
var sensorTag = new SensorTag(peripheral);
callback(sensorTag);
}
};
noble.on('discover', onDiscover);
noble.startScanning([],true);
} else {
noble.once('stateChange', startScanningOnPowerOn);
}
};
startScanningOnPowerOn();
};
SensorTag.prototype.onDisconnect = function() {
this.emit('disconnect');
};
SensorTag.prototype.toString = function() {
return JSON.stringify({
uuid: this.uuid
});
};
SensorTag.prototype.connect = function(callback) {
this._peripheral.connect(callback);
};
SensorTag.prototype.disconnect = function(callback) {
this._peripheral.disconnect(callback);
};
SensorTag.prototype.discoverServicesAndCharacteristics = function(callback) {
this._peripheral.discoverAllServicesAndCharacteristics(function(error, services, characteristics) {
if (error === null) {
for (var i in services) {
var service = services[i];
this._services[service.uuid] = service;
}
for (var j in characteristics) {
var characteristic = characteristics[j];
this._characteristics[characteristic.uuid] = characteristic;
}
}
callback();
}.bind(this));
};
SensorTag.prototype.writeCharacteristic = function(uuid, data, callback) {
if(typeof this._characteristics[uuid] !== "undefined"){
this._characteristics[uuid].write(data, false, callback);
}
};
SensorTag.prototype.writePeriodCharacteristic = function(uuid, period, callback) {
period /= 10; // input is scaled by units of 10ms
if (period < 10) {
period = 10;
} else if (period > 255) {
period = 255;
}
this.writeCharacteristic(uuid, new Buffer([period]), callback);
};
SensorTag.prototype.notifyCharacteristic = function(uuid, notify, listener, callback) {
var characteristic = this._characteristics[uuid];
if(typeof characteristic !== "undefined"){
characteristic.notify(notify, function(state) {
if (notify) {
characteristic.addListener('read', listener);
} else
characteristic.removeListener('read', listener);
callback();
});
}
};
SensorTag.prototype.enableConfigCharacteristic = function(uuid, callback) {
this.writeCharacteristic(uuid, new Buffer([0x01]), callback);
};
SensorTag.prototype.disableConfigCharacteristic = function(uuid, callback) {
this.writeCharacteristic(uuid, new Buffer([0x00]), callback);
};
SensorTag.prototype.readDataCharacteristic = function(uuid, callback) {
this._characteristics[uuid].read(function(error, data) {
callback(data);
});
};
SensorTag.prototype.readStringCharacteristic = function(uuid, callback) {
this.readDataCharacteristic(uuid, function(data) {
callback(data.toString());
});
};
SensorTag.prototype.readDeviceName = function(callback) {
this.readStringCharacteristic(DEVICE_NAME_UUID, callback);
};
SensorTag.prototype.readSystemId = function(callback) {
this.readDataCharacteristic(SYSTEM_ID_UUID, function(data) {
var systemId = [
data.readUInt8(7).toString(16),
data.readUInt8(6).toString(16),
data.readUInt8(5).toString(16),
data.readUInt8(4).toString(16),
data.readUInt8(3).toString(16),
data.readUInt8(2).toString(16),
data.readUInt8(2).toString(16),
data.readUInt8(0).toString(16)
].join(':');
callback(systemId);
});
};
SensorTag.prototype.readModelNumber = function(callback) {
this.readStringCharacteristic(MODEL_NUMBER_UUID, callback);
};
SensorTag.prototype.readSerialNumber = function(callback) {
this.readStringCharacteristic(SERIAL_NUMBER_UUID, callback);
};
SensorTag.prototype.readFirmwareRevision = function(callback) {
this.readStringCharacteristic(FIRMWARE_REVISION_UUID, callback);
};
SensorTag.prototype.readHardwareRevision = function(callback) {
this.readStringCharacteristic(HARDWARE_REVISION_UUID, callback);
};
SensorTag.prototype.readSoftwareRevision = function(callback) {
this.readStringCharacteristic(SOFTWARE_REVISION_UUID, callback);
};
SensorTag.prototype.readManufacturerName = function(callback) {
this.readStringCharacteristic(MANUFACTURER_NAME_UUID, callback);
};
SensorTag.prototype.enableIrTemperature = function(callback) {
this.enableConfigCharacteristic(IR_TEMPERATURE_CONFIG_UUID, callback);
};
SensorTag.prototype.disableIrTemperature = function(callback) {
this.disableConfigCharacteristic(IR_TEMPERATURE_CONFIG_UUID, callback);
};
SensorTag.prototype.readIrTemperature = function(callback) {
this.readDataCharacteristic(IR_TEMPERATURE_DATA_UUID, function(data) {
this.convertIrTemperatureData(data, callback);
}.bind(this));
};
SensorTag.prototype.onIrTemperatureChange = function(data) {
this.convertIrTemperatureData(data, function(objectTemperature, ambientTemperature) {
this.emit('irTemperatureChange', objectTemperature, ambientTemperature);
}.bind(this));
};
SensorTag.prototype.convertIrTemperatureData = function(data, callback) {
// For computation refer : http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#IR_Temperature_Sensor
var ambientTemperature = data.readInt16LE(2) / 128.0;
var Vobj2 = data.readInt16LE(0) * 0.00000015625;
var Tdie2 = ambientTemperature + 273.15;
var S0 = 5.593 * Math.pow(10, -14);
var a1 = 1.75 * Math.pow(10 ,-3);
var a2 = -1.678 * Math.pow(10, -5);
var b0 = -2.94 * Math.pow(10, -5);
var b1 = -5.7 * Math.pow(10, -7);
var b2 = 4.63 * Math.pow(10,-9);
var c2 = 13.4;
var Tref = 298.15;
var S = S0 * (1 + a1 * (Tdie2 - Tref) + a2 * Math.pow((Tdie2 - Tref), 2));
var Vos = b0 + b1 * (Tdie2 - Tref) + b2 * Math.pow((Tdie2 - Tref), 2);
var fObj = (Vobj2 - Vos) + c2 * Math.pow((Vobj2 - Vos), 2);
var objectTemperature = Math.pow(Math.pow(Tdie2, 4) + (fObj/S), 0.25);
objectTemperature = (objectTemperature - 273.15);
callback(objectTemperature, ambientTemperature);
};
SensorTag.prototype.notifyIrTemperature = function(callback) {
this.notifyCharacteristic(IR_TEMPERATURE_DATA_UUID, true, this.onIrTemperatureChange.bind(this), callback);
};
SensorTag.prototype.unnotifyIrTemperature = function(callback) {
this.notifyCharacteristic(IR_TEMPERATURE_DATA_UUID, false, this.onIrTemperatureChange.bind(this), callback);
};
SensorTag.prototype.enableAccelerometer = function(callback) {
this.enableConfigCharacteristic(ACCELEROMETER_CONFIG_UUID, callback);
};
SensorTag.prototype.disableAccelerometer = function(callback) {
this.disableConfigCharacteristic(ACCELEROMETER_CONFIG_UUID, callback);
};
SensorTag.prototype.readAccelerometer = function(callback) {
this.readDataCharacteristic(ACCELEROMETER_DATA_UUID, function(data) {
this.convertAccelerometerData(data, callback);
}.bind(this));
};
SensorTag.prototype.onAccelerometerChange = function(data) {
this.convertAccelerometerData(data, function(x, y, z) {
this.emit('accelerometerChange', x, y, z);
}.bind(this));
};
SensorTag.prototype.convertAccelerometerData = function(data, callback) {
var x = data.readInt8(0) * 4.0 / 256.0;
var y = data.readInt8(1) * 4.0 / 256.0;
var z = data.readInt8(2) * 4.0 / 256.0;
callback(x, y, z);
};
SensorTag.prototype.notifyAccelerometer = function(callback) {
this.notifyCharacteristic(ACCELEROMETER_DATA_UUID, true, this.onAccelerometerChange.bind(this), callback);
};
SensorTag.prototype.unnotifyAccelerometer = function(callback) {
this.notifyCharacteristic(ACCELEROMETER_DATA_UUID, false, this.onAccelerometerChange.bind(this), callback);
};
SensorTag.prototype.setAccelerometerPeriod = function(period, callback) {
this.writePeriodCharacteristic(ACCELEROMETER_PERIOD_UUID, period, callback);
};
SensorTag.prototype.enableHumidity = function(callback) {
this.enableConfigCharacteristic(HUMIDITY_CONFIG_UUID, callback);
};
SensorTag.prototype.disableHumidity = function(callback) {
this.disableConfigCharacteristic(HUMIDITY_CONFIG_UUID, callback);
};
SensorTag.prototype.readHumidity = function(callback) {
this.readDataCharacteristic(HUMIDITY_DATA_UUID, function(data) {
this.convertHumidityData(data, callback);
}.bind(this));
};
SensorTag.prototype.onHumidityChange = function(data) {
this.convertHumidityData(data, function(temperature, humidity) {
this.emit('humidityChange', temperature, humidity);
}.bind(this));
};
SensorTag.prototype.convertHumidityData = function(data, callback) {
var temperature = -46.85 + 175.72 / 65536.0 * data.readUInt16LE(0);
var humidity = -6.0 + 125.0 / 65536.0 * (data.readUInt16LE(2) & ~0x0003);
callback(temperature, humidity);
};
SensorTag.prototype.notifyHumidity = function(callback) {
this.notifyCharacteristic(HUMIDITY_DATA_UUID, true, this.onHumidityChange.bind(this), callback);
};
SensorTag.prototype.unnotifyHumidity = function(callback) {
this.notifyCharacteristic(HUMIDITY_DATA_UUID, false, this.onHumidityChange.bind(this), callback);
};
SensorTag.prototype.enableMagnetometer = function(callback) {
this.enableConfigCharacteristic(MAGNETOMETER_CONFIG_UUID, callback);
};
SensorTag.prototype.disableMagnetometer = function(callback) {
this.disableConfigCharacteristic(MAGNETOMETER_CONFIG_UUID, callback);
};
SensorTag.prototype.readMagnetometer = function(callback) {
this.readDataCharacteristic(MAGNETOMETER_DATA_UUID, function(data) {
this.convertMagnetometerData(data, callback);
}.bind(this));
};
SensorTag.prototype.onMagnetometerChange = function(data) {
this.convertMagnetometerData(data, function(x, y, z) {
this.emit('magnetometerChange', x, y, z);
}.bind(this));
};
SensorTag.prototype.convertMagnetometerData = function(data, callback) {
var x = data.readInt16LE(0) * 2000.0 / 65536.0;
var y = data.readInt16LE(2) * 2000.0 / 65536.0;
var z = data.readInt16LE(4) * 2000.0 / 65536.0;
callback(x, y, z);
};
SensorTag.prototype.notifyMagnetometer = function(callback) {
this.notifyCharacteristic(MAGNETOMETER_DATA_UUID, true, this.onMagnetometerChange.bind(this), callback);
};
SensorTag.prototype.unnotifyMagnetometer = function(callback) {
this.notifyCharacteristic(MAGNETOMETER_DATA_UUID, false, this.onMagnetometerChange.bind(this), callback);
};
SensorTag.prototype.setMagnetometerPeriod = function(period, callback) {
this.writePeriodCharacteristic(MAGNETOMETER_PERIOD_UUID, period, callback);
};
SensorTag.prototype.enableBarometricPressure = function(callback) {
this.writeCharacteristic(BAROMETRIC_PRESSURE_CONFIG_UUID, new Buffer([0x02]), function() {
this.readDataCharacteristic(BAROMETRIC_PRESSURE_CALIBRATION_UUID, function(data) {
this._barometricPressureCalibrationData = data;
this.enableConfigCharacteristic(BAROMETRIC_PRESSURE_CONFIG_UUID, callback);
}.bind(this));
}.bind(this));
};
SensorTag.prototype.disableBarometricPressure = function(callback) {
this.disableConfigCharacteristic(BAROMETRIC_PRESSURE_CONFIG_UUID, callback);
};
SensorTag.prototype.readBarometricPressure = function(callback) {
this.readDataCharacteristic(BAROMETRIC_PRESSURE_DATA_UUID, function(data) {
this.convertBarometricPressureData(data, callback);
}.bind(this));
};
SensorTag.prototype.onBarometricPressureChange = function(data) {
this.convertBarometricPressureData(data, function(pressure) {
this.emit('barometricPressureChange', pressure);
}.bind(this));
};
SensorTag.prototype.convertBarometricPressureData = function(data, callback) {
// For computation refer : http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Barometric_Pressure_Sensor_2
var temp; // Temperature raw value from sensor
var pressure; // Pressure raw value from sensor
var S; // Interim value in calculation
var O; // Interim value in calculation
var p_a; // Pressure actual value in unit Pascal.
var Pa; // Computed value of the function
var c0 = this._barometricPressureCalibrationData.readUInt16LE(0);
var c1 = this._barometricPressureCalibrationData.readUInt16LE(2);
var c2 = this._barometricPressureCalibrationData.readUInt16LE(4);
var c3 = this._barometricPressureCalibrationData.readUInt16LE(6);
var c4 = this._barometricPressureCalibrationData.readInt16LE(8);
var c5 = this._barometricPressureCalibrationData.readInt16LE(10);
var c6 = this._barometricPressureCalibrationData.readInt16LE(12);
var c7 = this._barometricPressureCalibrationData.readInt16LE(14);
temp = data.readInt16LE(0);
pressure = data.readUInt16LE(2);
S = c2 + ((c3 * temp)/ 131072.0) + ((c4 * (temp * temp)) / 17179869184.0);
O = (c5 * 16384.0) + (((c6 * temp) / 8)) + ((c7 * (temp * temp)) / 524288.0);
Pa = (((S * pressure) + O) / 16384.0);
Pa /= 100.0;
callback(Pa);
};
SensorTag.prototype.notifyBarometricPressure = function(callback) {
this.notifyCharacteristic(BAROMETRIC_PRESSURE_DATA_UUID, true, this.onBarometricPressureChange.bind(this), callback);
};
SensorTag.prototype.unnotifyBarometricPressure = function(callback) {
this.notifyCharacteristic(BAROMETRIC_PRESSURE_DATA_UUID, false, this.onBarometricPressureChange.bind(this), callback);
};
SensorTag.prototype.setGyroscopePeriod = function(period, callback) {
this.writePeriodCharacteristic(GYROSCOPE_PERIOD_UUID, period, callback);
};
SensorTag.prototype.enableGyroscope = function(callback) {
var enableCode = this.buildGyroscopeEnableCode(true, true, true);
this.writeCharacteristic(GYROSCOPE_CONFIG_UUID, new Buffer([enableCode]), callback);
};
SensorTag.prototype.enableGyroscope = function(callback) {
var enableCode = this.buildGyroscopeEnableCode(true, true, true);
this.writeCharacteristic(GYROSCOPE_CONFIG_UUID, new Buffer([enableCode]), callback);
};
SensorTag.prototype.enableGyroscopeAxis = function(enableXAxis, enableYAxis, enableZAxis, callback) {
var enableCode = this.buildGyroscopeEnableCode(enableXAxis, enableYAxis, enableZAxis);
this.writeCharacteristic(GYROSCOPE_CONFIG_UUID, new Buffer([enableCode]), callback);
};
SensorTag.prototype.disableGyroscope = function(callback) {
this.disableConfigCharacteristic(GYROSCOPE_CONFIG_UUID, callback);
};
SensorTag.prototype.readGyroscope = function(callback) {
this.readDataCharacteristic(GYROSCOPE_DATA_UUID, function(data) {
this.convertGyroscopeData(data, callback);
}.bind(this));
};
SensorTag.prototype.onGyroscopeChange = function(data) {
this.convertGyroscopeData(data, function(x, y, z) {
this.emit('gyroscopeChange', x, y, z);
}.bind(this));
};
SensorTag.prototype.buildGyroscopeEnableCode = function(enableXAxis, enableYAxis, enableZAxis) {
var enableCode = 0;
if (enableXAxis) { enableCode |= 0x01; }
if (enableYAxis) { enableCode |= 0x02; }
if (enableZAxis) { enableCode |= 0x04; }
return enableCode;
};
SensorTag.prototype.convertGyroscopeData = function(data, callback) {
var x = data.readInt16LE(0) * (500.0 / 65536.0) * -1;
var y = data.readInt16LE(2) * (500.0 / 65536.0);
var z = data.readInt16LE(4) * (500.0 / 65536.0);
callback(x, y, z);
};
SensorTag.prototype.notifyGyroscope = function(callback) {
this.notifyCharacteristic(GYROSCOPE_DATA_UUID, true, this.onGyroscopeChange.bind(this), callback);
};
SensorTag.prototype.unnotifyGyroscope = function(callback) {
this.notifyCharacteristic(GYROSCOPE_DATA_UUID, false, this.onGyroscopeChange.bind(this), callback);
};
SensorTag.prototype.onSimpleKeyChange = function(data) {
this.convertSimpleKeyData(data, function(left, right) {
this.emit('simpleKeyChange', left, right);
}.bind(this));
};
SensorTag.prototype.convertSimpleKeyData = function(data, callback) {
var b = data.readUInt8(0);
var left = (b & 0x2) ? true : false;
var right = (b & 0x1) ? true : false;
callback(left, right);
};
SensorTag.prototype.notifySimpleKey = function(callback) {
this.notifyCharacteristic(SIMPLE_KEY_DATA_UUID, true, this.onSimpleKeyChange.bind(this), callback);
};
SensorTag.prototype.unnotifySimpleKey = function(callback) {
this.notifyCharacteristic(SIMPLE_KEY_DATA_UUID, false, this.onSimpleKeyChange.bind(this), callback);
};
module.exports = SensorTag;