Skip to content

Instantly share code, notes, and snippets.

@lbrenman
Last active August 29, 2015 14:09
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 lbrenman/8eea975abddee8fdb62d to your computer and use it in GitHub Desktop.
Save lbrenman/8eea975abddee8fdb62d to your computer and use it in GitHub Desktop.
Appcelerator Titanium Alloy Bluetooth LE Weather Station using TI CC2541 SmartTag and Logical Labs BT LE Module
// The contents of this file will be executed before any of
// your view controllers are ever executed, including the index.
// You have access to all functionality on the `Alloy` namespace.
//
// This is a great place to do any initialization for your app
// or create any global variables/functions that you'd like to
// make available throughout your app. You can easily make things
// accessible globally by attaching them to the `Alloy.Globals`
// object. For example:
//
// Alloy.Globals.someGlobalFunction = function(){};
Alloy.Globals.audioOn=false;
Alloy.Globals.farenheitOn=false;
Alloy.Globals.firstRun=true;
".container": {
backgroundColor:"white",
fullscreen: true,
navBarHidden: true
}
".colorLineVw": {
height: 1,
bottom: 0,
backgroundColor: "#f44d4a"
}
"#activityIndicatorVw": {
backgroundColor: "#D0000000",
borderRadius: 8,
borderWidth: 2,
borderColor: "gray",
height:60,
width:200
}
"#activityIndicator": {
style: Ti.UI.ActivityIndicatorStyle.DARK,
color: "white",
indicatorColor: "white",
height:Ti.UI.SIZE,
width:Ti.UI.SIZE
}
"#activityIndicator[platform=ios]": {
style: Ti.UI.iPhone.ActivityIndicatorStyle.BIG_DARK
}
var BluetoothLE = require('com.logicallabs.bluetoothle');
var connectedPeripheral;
var BTReady = false;
// var index=0;
var BarometricPressureCalibrationValue;
var scanRunning = false;
var scanStatus, peripheralStatus, peripheralName;
var centralErrorCallback, centralOffCallback,
centralOnCallback;
var centralStateChangeCallbackAdded;
var callback;
var
BASE_UUID = '00000000-0000-1000-8000-00805F9B34FB';
KEY_SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb",
KEY_DATA_UUID = "0000ffe1-0000-1000-8000-00805f9b34fb",
IR_TEMP_SERVICE_UUID = 'F000AA00-0451-4000-B000-000000000000',
IR_TEMP_DATA_CHAR_UUID = 'F000AA01-0451-4000-B000-000000000000',
IR_TEMP_CONFIG_CHAR_UUID = 'F000AA02-0451-4000-B000-000000000000',
HUMIDITY_SERVICE_UUID = 'F000AA20-0451-4000-B000-000000000000',
HUMIDITY_DATA_CHAR_UUID = 'F000AA21-0451-4000-B000-000000000000',
HUMIDITY_CONFIG_CHAR_UUID = 'F000AA22-0451-4000-B000-000000000000',
BARPRESSURE_SERVICE_UUID = 'F000AA40-0451-4000-B000-000000000000',
BARPRESSURE_DATA_CHAR_UUID = 'F000AA41-0451-4000-B000-000000000000',
BARPRESSURE_CONFIG_CHAR_UUID = 'F000AA42-0451-4000-B000-000000000000';
BARPRESSURE_CALIBRATION_CHAR_UUID = 'F000AA43-0451-4000-B000-000000000000';
function twosComp(value, bitCount) {
Ti.API.debug("cc2541_btle: twosComp()");
if (value < Math.pow(2, bitCount - 1)) {
return value;
}
return value - Math.pow(2, bitCount);
}
function buildCalibrationInt(loByte, hiByte) {
Ti.API.debug("cc2541_btle: buildCalibrationInt()");
return ((((hiByte) & 0x00FF) << 8)) + ((loByte) & 0x00FF);
}
function getBarPressureValue(buffer, calibrationVal) {
Ti.API.debug("cc2541_btle: getBarPressureValue()");
var c1 = buildCalibrationInt(calibrationVal[0], calibrationVal[1]);
var c2 = buildCalibrationInt(calibrationVal[2], calibrationVal[3]);
var c3 = buildCalibrationInt(calibrationVal[4], calibrationVal[5]);
var c4 = buildCalibrationInt(calibrationVal[6], calibrationVal[7]);
var c5 = twosComp(buildCalibrationInt(calibrationVal[8], calibrationVal[9]),16);
var c6 = twosComp(buildCalibrationInt(calibrationVal[10], calibrationVal[11]),16);
var c7 = twosComp(buildCalibrationInt(calibrationVal[12], calibrationVal[13]),16);
var c8 = twosComp(buildCalibrationInt(calibrationVal[14], calibrationVal[15]),16);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c1 = "+c1);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c2 = "+c2);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c3 = "+c3);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c4 = "+c4);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c5 = "+c5);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c6 = "+c6);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c7 = "+c7);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c8 = "+c8);
Ti.API.debug("cc2541_btle: getBarPressureValue(), buffer.length = "+buffer.length);
if (buffer.length < 4) {
Ti.API.debug('cc2541_btle: getBarPressureValue(), Invalid pressure values.');
return -1;
}
// var Pr = Ti.Codec.decodeNumber({
// source: buffer,
// position: 0,
// type: Ti.Codec.TYPE_SHORT,
// byteOrder: Ti.Codec.LITTLE_ENDIAN
// });
// var Tr = Ti.Codec.decodeNumber({
// source: buffer,
// position: 2,
// type: Ti.Codec.TYPE_SHORT,
// byteOrder: Ti.Codec.LITTLE_ENDIAN
// });
// var Pr = twosComp(buffer[2] + 256 * buffer[3], 16);
// var Tr = twosComp(buffer[0] + 256 * buffer[1], 16);
var Pr = buffer[2] + 256 * buffer[3];
var Tr = buffer[0] + 256 * buffer[1];
Ti.API.debug('cc2541_btle: getBarPressureValue(), raw temp = '+Tr);
Ti.API.debug('cc2541_btle: getBarPressureValue(), raw pressure = '+Pr);
//http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Barometric_Pressure_Sensor
/* Conversion algorithm for barometer pressure (hPa)
*
* Formula from application note, rev_X:
* Sensitivity = (c3 + ((c4 * Tr) / 2^17) + ((c5 * Tr^2) / 2^34))
* Offset = (c6 * 2^14) + ((c7 * Tr) / 2^3) + ((c8 * Tr^2) / 2^19)
* Pa = (Sensitivity * Pr + Offset) / 2^14
*/
var S = (c3 + ((c4*Tr)/131072) + ((c5*Tr*Tr)/17179869184));
var O = (c6*16384) + ((c7*Tr)/8) + ((c8*Tr*Tr)/524288);
var Pa = (S*Pr+O)/16384;
return Pa/139.12;
// return Pa;
}
function getBarPressureValue_(buffer, calibrationVal) {
Ti.API.debug("cc2541_btle: getBarPressureValue()");
var c0 = buildCalibrationInt(calibrationVal[0], calibrationVal[1]);
var c1 = buildCalibrationInt(calibrationVal[2], calibrationVal[3]);
var c2 = buildCalibrationInt(calibrationVal[4], calibrationVal[5]);
var c3 = buildCalibrationInt(calibrationVal[6], calibrationVal[7]);
var c4 = twosComp(buildCalibrationInt(calibrationVal[8], calibrationVal[9]),16);
var c5 = twosComp(buildCalibrationInt(calibrationVal[10], calibrationVal[11]),16);
var c6 = twosComp(buildCalibrationInt(calibrationVal[12], calibrationVal[13]),16);
var c7 = twosComp(buildCalibrationInt(calibrationVal[14], calibrationVal[15]),16);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c0 = "+c0);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c1 = "+c1);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c2 = "+c2);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c3 = "+c3);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c4 = "+c4);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c5 = "+c5);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c6 = "+c6);
Ti.API.debug("cc2541_btle: getBarPressureValue(), c7 = "+c7);
Ti.API.debug("cc2541_btle: getBarPressureValue(), buffer.length = "+buffer.length);
if (buffer.length < 4) {
Ti.API.debug('cc2541_btle: getBarPressureValue(), Invalid pressure values.');
return -1;
}
var t_r = Ti.Codec.decodeNumber({
source: buffer,
position: 2,
type: Ti.Codec.TYPE_SHORT,
byteOrder: Ti.Codec.LITTLE_ENDIAN
});
var p_r = Ti.Codec.decodeNumber({
source: buffer,
position: 0,
type: Ti.Codec.TYPE_SHORT,
byteOrder: Ti.Codec.LITTLE_ENDIAN
});
Ti.API.debug('cc2541_btle: getBarPressureValue(), raw temp = '+t_r);
Ti.API.debug('cc2541_btle: getBarPressureValue(), raw pressure = '+p_r);
//http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Barometric_Pressure_Sensor
var t_a = (100*((c0*t_r/256) + (c1*64)))/65536;
var S = c2 + (c3*t_r/131072) + ((c4*t_r/32768)*t_r)/524288;
var O = c5*16384 + (c6*t_r/8) + ((c7*t_r/32768)*t_r)/16;
var p_a = (S*p_r+O)/16384;
//return p_a/310.228; // off by a factor of 310
return p_a;
}
function getHumidityValue(buffer) {
Ti.API.debug("cc2541_btle: getHumidityValue()");
var V;
if (buffer.length < 4) {
Ti.API.debug('cc2541_btle: Invalid humidity values.');
return -1;
}
var humidity = twosComp(buffer[2] + 256 * buffer[3], 16);
humidity = humidity & ~0x0003;
v = -6.0 + humidity*125.0/65536;
return Math.abs(v);
}
function getTemperatureValues(buffer) {
Ti.API.debug("cc2541_btle: getTemperatureValues()");
// Self-explanatory calculation courtesy of Texas Instrument
var ambTemp, objTemp, Vobj2, Tdie2, S0, a1, a2, b0, b1, b2,
c2, Tref, S, Vos, fObj, Tobj;
if (buffer.length < 4) {
Ti.API.debug('Invalid temperature values.');
return -1;
}
objTemp = twosComp(buffer[0] + 256 * buffer[1], 16);
ambTemp = twosComp(buffer[2] + 256 * buffer[3], 16) / 128.0;
Vobj2 = objTemp * 0.00000015625;
Tdie2 = ambTemp + 273.15;
S0 = 6.4 * Math.pow(10,-14);
a1 = 1.75 * Math.pow(10,-3);
a2 = -1.678 * Math.pow(10,-5);
b0 = -2.94 * Math.pow(10,-5);
b1 = -5.7 * Math.pow(10,-7);
b2 = 4.63 * Math.pow(10,-9);
c2 = 13.4;
Tref = 298.15;
S = S0*(1+a1*(Tdie2 - Tref)+a2 * Math.pow((Tdie2 - Tref),2));
Vos = b0 + b1*(Tdie2 - Tref) + b2 * Math.pow((Tdie2 - Tref),2);
fObj = (Vobj2 - Vos) + c2 * Math.pow((Vobj2 - Vos),2);
Tobj = Math.pow(Math.pow(Tdie2,4) + (fObj/S), 0.25);
Tobj = Tobj - 273.15;
//ambTemp = 32+ambTemp*9/5;
//objTemp = 32+objTemp*9/5;
return {
ambTemp: Math.round(ambTemp *100) / 100.0,
objTemp: Math.round(Tobj * 100) / 100.0
};
}
function iosCentralStateChangeCallback(e) {
Ti.API.debug("cc2541_btle: iosCentralStateChangeCallback()");
switch (e.state) {
case BluetoothLE.CENTRAL_MANAGER_STATE_UNKNOWN:
if (centralErrorCallback) {
centralErrorCallback('Bluetooth LE state is resetting.');
}
break;
case BluetoothLE.CENTRAL_MANAGER_STATE_RESETTING:
if (centralErrorCallback) {
centralErrorCallback('Bluetooth LE is resetting.');
}
break;
case BluetoothLE.CENTRAL_MANAGER_STATE_UNSUPPORTED:
if (centralErrorCallback) {
centralErrorCallback('Bluetooth LE is not supported.');
}
break;
case BluetoothLE.CENTRAL_MANAGER_STATE_UNAUTHORIZED:
if (centralErrorCallback) {
centralErrorCallback('Bluetooth LE is not authorized.');
}
break;
case BluetoothLE.CENTRAL_MANAGER_STATE_POWERED_OFF:
if (centralOffCallback) {
centralOffCallback();
}
break;
case BluetoothLE.CENTRAL_MANAGER_STATE_POWERED_ON:
if (centralOnCallback) {
centralOnCallback();
}
break;
}
}
function androidCentralStateChangeCallback(e) {
Ti.API.debug("cc2541_btle: androidCentralStateChangeCallback()");
switch (e.state) {
case BluetoothLE.STATE_TURNING_OFF:
Ti.API.debug('Bluetooth is turning off.');
if (centralOffCallback) {
centralOffCallback();
}
break;
case BluetoothLE.STATE_OFF:
break;
case BluetoothLE.STATE_TURNING_ON:
break;
case BluetoothLE.STATE_ON:
if (centralOnCallback) {
centralOnCallback();
}
break;
}
}
function initBluetoothCentral(params) {
Ti.API.debug("cc2541_btle: initBluetoothCentral()");
centralErrorCallback = params.errorCallback;
centralOffCallback = params.offCallback;
centralOnCallback = params.onCallback;
if (!centralStateChangeCallbackAdded) {
centralStateChangeCallbackAdded = true;
if (OS_ANDROID) {
BluetoothLE.addEventListener(
'stateChanged', androidCentralStateChangeCallback);
} else {
BluetoothLE.addEventListener(
'centralManagerStateChange', iosCentralStateChangeCallback);
}
}
if (OS_ANDROID) {
if (BluetoothLE.isSupported()) {
Ti.API.debug('Bluetooth is supported!');
if (BluetoothLE.isEnabled()) {
Ti.API.debug('Bluetooth is already enabled!');
params.onCallback();
} else {
Ti.API.debug('Bluetooth is disbled; enabling now.');
// This will eventually fire a stateChanged event with state set to
// STATE_ON, at which point we start the scanning.
BluetoothLE.enable();
}
} else {
params.errorCallback('Bluetooth LE is not supported.');
}
} else {
BluetoothLE.initCentralManager({
restoreIdentifier: params.restoreIdentifier,
// If you set this to false, the user won't be notified if Bluetooth
// is off -- and you will receive CENTRAL_MANAGER_STATE_POWERED_OFF
// status event instead of CENTRAL_MANAGER_STATE_POWERED_ON.
showPowerAlert: true
});
}
};
function shutdownBluetoothCentral(params) {
Ti.API.debug("cc2541_btle: shutdownBluetoothCentral()");
BluetoothLE.stopScan();
if (OS_ANDROID) {
BluetoothLE.disable();
} else {
BluetoothLE.releaseCentralManager();
}
};
function expandUUID(uuid) {
Ti.API.debug("cc2541_btle: expandUUID()");
var result;
if (uuid.length === 4) {
result = BASE_UUID.substring(0, 4) + uuid +
BASE_UUID.substring(8, BASE_UUID.length);
} else {
result = uuid;
}
return result;
}
function uuidMatch(uuid1, uuid2) {
Ti.API.debug("cc2541_btle: uuidMatch()");
return expandUUID(uuid1).toLowerCase() === expandUUID(uuid2).toLowerCase();
}
function canSubscribeTo(characteristic) {
Ti.API.debug("cc2541_btle: canSubscribeTo()");
return characteristic.properties & BluetoothLE.CHAR_PROP_NOTIFY ||
characteristic.properties & BluetoothLE.CHAR_PROP_INDICATE;
}
function cancelConnection() {
Ti.API.debug("cc2541_btle: cancelConnection()");
if (connectedPeripheral) {
Ti.API.debug('cc2541_btle: Cancelling previous connection.');
BluetoothLE.cancelPeripheralConnection(connectedPeripheral);
connectedPeripheral = null;
}
}
BluetoothLE.addEventListener('moduleReady', function(e) {
Ti.API.debug("cc2541_btle: BluetoothLE moduleReady event");
BTReady = true;
initBluetoothCentral({
onCallback: function() {
startScan();
},
offCallback: function() {
stopScan();
cancelConnection();
},
errorCallback: function(desc) {
Ti.API.debug(desc);
}
});
});
BluetoothLE.addEventListener('discoveredPeripheral', function(e) {
Ti.API.debug("cc2541_btle: BluetoothLE discoveredPeripheral event, e = "+JSON.stringify(e));
if (e.peripheral.name !== 'TI BLE Sensor Tag' &&
e.peripheral.name !== 'SensorTag')
{
Ti.API.debug('Name of peripheral (' +
e.peripheral.name + ') does not match!');
return;
}
if (!connectedPeripheral) {
Ti.API.debug('cc2541_btle: Discovered peripheral: ' +
e.peripheral.name + '/' + e.peripheral.address);
stopScan();
BluetoothLE.connectPeripheral({
peripheral: e.peripheral,
autoConnect: false
});
} else {
Ti.API.debug('cc2541_btle: Received discoveredPeripheral event for previously discovered peripheral.');
}
});
var digestServices = function(e) {
Ti.API.debug("cc2541_btle: digestServices, services = ");
var services = e.source.services;
if (!services) {
return;
}
Ti.API.debug('Peripheral has ' + services.length + ' services');
services.forEach(function(service) {
Ti.API.debug('cc2541_btle: digestServices Discovered service ' + service.UUID);
if (uuidMatch(service.UUID, IR_TEMP_SERVICE_UUID)) {
Ti.API.debug('cc2541_btle: digestServices Found IR temperature service!');
e.source.discoverCharacteristics({
service: service
});
} else if (uuidMatch(service.UUID, HUMIDITY_SERVICE_UUID)) {
Ti.API.debug('cc2541_btle: digestServices Found Humidity service!');
e.source.discoverCharacteristics({
service: service
});
} else if (uuidMatch(service.UUID, BARPRESSURE_SERVICE_UUID)) {
Ti.API.debug('cc2541_btle: digestServices Found Barometric Pressure service!');
e.source.discoverCharacteristics({
service: service
});
} else if (uuidMatch(service.UUID, KEY_SERVICE_UUID)) {
Ti.API.debug('cc2541_btle: digestServices Found key service!');
e.source.discoverCharacteristics({
service: service
});
}
});
};
var digestCharacteristics = function(e) {
Ti.API.debug("cc2541_btle: digestCharacteristics");
var characteristics;
if (e.errorCode !== undefined) {
Ti.API.debug('cc2541_btle: digestCharacteristics Error while discovering characteristics: ' +
e.errorCode + '/' + e.errorDomain + '/' +
e.errorDescription);
return;
}
characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic) {
Ti.API.debug('cc2541_btle: digestCharacteristics characteristic.UUID: ' + characteristic.UUID);
if (uuidMatch(characteristic.UUID, IR_TEMP_CONFIG_CHAR_UUID)) {
Ti.API.debug('cc2541_btle: digestCharacteristics Found IR temperature config characteristic, will write...');
connectedPeripheral.writeValueForCharacteristic({
data: Ti.createBuffer({ value: 0x01, type: Ti.Codec.TYPE_BYTE}),
characteristic: characteristic,
type: BluetoothLE.CHARACTERISTIC_TYPE_WRITE_WITH_RESPONSE
});
} else if (uuidMatch(characteristic.UUID, IR_TEMP_DATA_CHAR_UUID)) {
if (canSubscribeTo(characteristic)) {
Ti.API.debug('cc2541_btle: digestCharacteristics Found IR temperature data characteristic, will subscribe...');
connectedPeripheral.subscribeToCharacteristic(characteristic);
} else {
Ti.API.debug('cc2541_btle: digestCharacteristics Found IR temperature data characteristic but can\'t subscribe...');
}
} else if (uuidMatch(characteristic.UUID, HUMIDITY_CONFIG_CHAR_UUID)) {
Ti.API.debug('cc2541_btle: digestCharacteristics Found Humidity config characteristic, will write...');
connectedPeripheral.writeValueForCharacteristic({
data: Ti.createBuffer({ value: 0x01, type: Ti.Codec.TYPE_BYTE}),
characteristic: characteristic,
type: BluetoothLE.CHARACTERISTIC_TYPE_WRITE_WITH_RESPONSE
});
} else if (uuidMatch(characteristic.UUID, HUMIDITY_DATA_CHAR_UUID)) {
if (canSubscribeTo(characteristic)) {
Ti.API.debug('cc2541_btle: digestCharacteristics Found Humidity data characteristic, will subscribe...');
connectedPeripheral.subscribeToCharacteristic(characteristic);
} else {
Ti.API.debug('cc2541_btle: digestCharacteristics Found Humidity data characteristic but can\'t subscribe...');
}
} else if (uuidMatch(characteristic.UUID, BARPRESSURE_CONFIG_CHAR_UUID)) {
Ti.API.debug('cc2541_btle: digestCharacteristics Found Barometric Pressure config characteristic, will write 1...');
connectedPeripheral.writeValueForCharacteristic({
data: Ti.createBuffer({ value: 0x01, type: Ti.Codec.TYPE_BYTE}),
characteristic: characteristic,
type: BluetoothLE.CHARACTERISTIC_TYPE_WRITE_WITH_RESPONSE
});
// get calibration data - http://www.byteworks.us/Byte_Works/Blog/Entries/2012/10/31_Accessing_the_Bluetooth_low_energy_Barometer_on_the_TI_SensorTag.html
Ti.API.debug('cc2541_btle: digestCharacteristics Found Barometric Pressure config characteristic, will write 2...');
connectedPeripheral.writeValueForCharacteristic({
data: Ti.createBuffer({ value: 0x02, type: Ti.Codec.TYPE_BYTE}),
characteristic: characteristic,
type: BluetoothLE.CHARACTERISTIC_TYPE_WRITE_WITH_RESPONSE
});
} else if (uuidMatch(characteristic.UUID, BARPRESSURE_DATA_CHAR_UUID)) {
if (canSubscribeTo(characteristic)) {
Ti.API.debug('cc2541_btle: digestCharacteristics Found Barometric Pressure data characteristic, will subscribe...');
connectedPeripheral.subscribeToCharacteristic(characteristic);
} else {
Ti.API.debug('cc2541_btle: digestCharacteristics Found Barometric Pressure data characteristic but can\'t subscribe...');
}
} else if (uuidMatch(characteristic.UUID, BARPRESSURE_CALIBRATION_CHAR_UUID)) {
// get calibration data - http://www.byteworks.us/Byte_Works/Blog/Entries/2012/10/31_Accessing_the_Bluetooth_low_energy_Barometer_on_the_TI_SensorTag.html
Ti.API.debug('cc2541_btle: digestCharacteristics Found Barometric Pressure calibration characteristic, will read...');
connectedPeripheral.readValueForCharacteristic(characteristic);
} else if (uuidMatch(characteristic.UUID, KEY_DATA_UUID)) {
if (canSubscribeTo(characteristic)) {
Ti.API.debug('cc2541_btle: digestCharacteristics Found key data characteristic, will subscribe...');
connectedPeripheral.subscribeToCharacteristic(characteristic);
} else {
Ti.API.debug('cc2541_btle: digestCharacteristics Found key data characteristic but can\'t subscribe...');
}
}
});
};
var digestNewCharValue = function(e) {
Ti.API.debug("cc2541_btle: digestNewCharValue");
var tempValues;
var humidityValue;
var barPressureVal;
if (e.characteristic) {
if (e.errorCode !== undefined) {
Ti.API.debug('cc2541_btle: digestNewCharValue - Error while reading char ' + e.characteristic.UUID + ' ' +
e.errorCode + '/' + e.errorDomain + '/' +
e.errorDescription);
} else {
if (uuidMatch(e.characteristic.UUID, IR_TEMP_DATA_CHAR_UUID)) {
tempValues = getTemperatureValues(e.characteristic.value);
Ti.API.debug('cc2541_btle: digestNewCharValue - Ambient temperature: ' + tempValues.ambTemp);
Ti.API.debug('cc2541_btle: digestNewCharValue - Object temperature: ' + tempValues.objTemp);
if(callback) {callback({
code: "tempVal",
value: tempValues.ambTemp
});}
} else if (uuidMatch(e.characteristic.UUID, HUMIDITY_DATA_CHAR_UUID)) {
Ti.API.debug('cc2541_btle: digestNewCharValue - Humidity (Raw value): ' + e.characteristic.value);
humidityValue = getHumidityValue(e.characteristic.value);
Ti.API.debug('cc2541_btle: digestNewCharValue - Humidity: ' + humidityValue);
if(callback) {callback({
code: "humidityVal",
value: humidityValue
});}
} else if (uuidMatch(e.characteristic.UUID, BARPRESSURE_DATA_CHAR_UUID)) {
Ti.API.debug('cc2541_btle: digestNewCharValue - Barometric Pressure (Raw value): ' + e.characteristic.value);
if(BarometricPressureCalibrationValue){
barPressureVal = getBarPressureValue(e.characteristic.value, BarometricPressureCalibrationValue);
Ti.API.debug('cc2541_btle: digestNewCharValue - Barometric Pressure: ' + barPressureVal);
if(callback) {callback({
code: "pressureVal",
value: barPressureVal
});}
}
} else if (uuidMatch(e.characteristic.UUID, BARPRESSURE_CALIBRATION_CHAR_UUID)) {
Ti.API.debug('cc2541_btle: digestNewCharValue - Barometric Pressure Calibration (Raw value): ' + e.characteristic.value);
BarometricPressureCalibrationValue = e.characteristic.value;
Ti.API.debug('cc2541_btle: digestNewCharValue - Barometric Pressure Calibration (Raw value) length: ' + e.characteristic.value.length);
} else if (uuidMatch(e.characteristic.UUID, KEY_DATA_UUID)) {
Ti.API.debug('cc2541_btle: digestNewCharValue - KEY_DATA_UUID received, e.characteristic.value[0] = '+e.characteristic.value[0]);
} else {
Ti.API.debug('cc2541_btle: digestNewCharValue - Received value for char ' + e.characteristic.UUID.toLowerCase() +
': ' + e.characteristic.value);
}
}
} else {
Ti.API.debug('cc2541_btle: digestNewCharValue - Received updatedValueForCharacteristic event without characteristic object.');
if (e.errorCode) {
Ti.API.debug('cc2541_btle: digestNewCharValue - Error while reading char: ' +
e.errorCode + '/' + e.errorDomain + '/' +
e.errorDescription);
}
}
};
BluetoothLE.addEventListener('connectedPeripheral', function(e) {
Ti.API.debug("cc2541_btle: BluetoothLE connectedPeripheral event, name = "+e.peripheral.name+", address = "+e.peripheral.address);
Ti.API.debug("cc2541_btle: BluetoothLE connectedPeripheral event, e = "+JSON.stringify(e));
if (connectedPeripheral && !connectedPeripheral.equals(e.peripheral)) {
cancelConnection();
}
if(callback) {callback({
code: "connected"
});}
connectedPeripheral = e.peripheral;
connectedPeripheral.discoverServices();
connectedPeripheral.addEventListener('discoveredServices', digestServices);
connectedPeripheral.addEventListener('discoveredCharacteristics', digestCharacteristics);
connectedPeripheral.addEventListener('updatedValueForCharacteristic', digestNewCharValue);
});
BluetoothLE.addEventListener('failedToConnectPeripheral', function(e) {
Ti.API.debug("cc2541_btle: BluetoothLE failedToConnectPeripheral event");
});
BluetoothLE.addEventListener('disconnectedPeripheral', function(e) {
Ti.API.debug("cc2541_btle: BluetoothLE disconnectedPeripheral event");
if(callback) {callback({
code: "disconnected"
});}
if (connectedPeripheral) {
connectedPeripheral.removeEventListener(
'discoveredServices', digestServices);
connectedPeripheral.removeEventListener(
'discoveredCharacteristics', digestCharacteristics);
connectedPeripheral.removeEventListener(
'updatedValueForCharacteristic', digestNewCharValue);
}
});
function startScan() {
Ti.API.debug('cc2541_btle: startScan(), scanRunning = '+scanRunning);
if (!scanRunning) {
scanRunning = true;
cancelConnection();
BluetoothLE.startScan();
}
}
function stopScan() {
Ti.API.debug('cc2541_btle: stopScan(), scanRunning = '+scanRunning);
BluetoothLE.stopScan();
Ti.API.debug('Stopped scanning.');
scanRunning = false;
}
exports.init = function(_callback) {
callback = _callback;
};
exports.scanStart = function() {
Ti.API.debug('cc2541_btle: scanStart()');
startScan();
};
exports.scanStop = function() {
Ti.API.debug('cc2541_btle: scanStop()');
cancelConnection();
};
{
"global": {
"theme":"whitetheme"
},
"env:development": {},
"env:test": {},
"env:production": {},
"os:android": {},
"os:blackberry": {},
"os:ios": {},
"os:mobileweb": {},
"dependencies": {}
}
var bt = require('cc2541_btle');
var animationDuration = 300;
var waitDuration = 3000;
var connectionWaitDuration = 15000;
var myTimeout, connectionTimeout, titleViewOn=true;
var isBtConnected = false;
var connectSound = "/audio/ding1.mp3";
var disconnectSound = "/audio/FoleySwitchMetalSwitch1.mp3";
function startConnectionTimer() {
Ti.API.debug("index: startConnectionTimer");
connectionTimeout = setTimeout(displayConnectionWarning, connectionWaitDuration);
}
function displayConnectionWarning() {
Ti.API.debug("index: displayConnectionWarning");
$.activityIndicator.hide();
$.activityIndicatorVw.visible=false;
alert("SensorTag not found\n\nPlease turn SensorTag on.");
}
function settingsBtnClick(e) {
Ti.API.debug("index: settingsBTNClick");
animateTitleOut();
var infoController = Alloy.createController('info',{}).getView().open();
}
function connectBtnClick(e) {
Ti.API.debug("index: connectBtnClick, isBtConnected = "+isBtConnected);
if(isBtConnected) {
bt.scanStop();
} else {
bt.scanStart();
startConnectionTimer();
$.activityIndicator.show();
$.activityIndicatorVw.visible = true;
}
}
function animateTitleOut(){
var animation = Titanium.UI.createAnimation({
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN,
duration: animationDuration
});
animation.top = -$.titleVw.height;
animation.addEventListener('complete',function(){
titleViewOn = false;
});
$.titleVw.animate(animation);
}
function animateTitleIn(){
var animation = Titanium.UI.createAnimation({
curve: Titanium.UI.ANIMATION_CURVE_EASE_IN,
duration: animationDuration
});
animation.top = 0;
animation.addEventListener('complete',function(){
titleViewOn = true;
myTimeout = setTimeout(animateTitleOut, 2*waitDuration);
});
$.titleVw.animate(animation);
}
$.mainVw.addEventListener('click', function(e) {
Ti.API.debug("index: mainVw click event heard");
clearTimeout(myTimeout);
if(titleViewOn) {
animateTitleOut();
} else {
animateTitleIn();
}
});
function btConnected() {
Ti.API.debug("index: btConnected");
isBtConnected = true;
$.connectBtn.backgroundImage = "/images/connected.png";
$.footerIv.image = "/images/connected_footer.png";
}
function btDisconnected() {
Ti.API.debug("index: btDisconnected");
isBtConnected = false;
$.connectBtn.backgroundImage = "/images/disconnected.png";
$.footerIv.image = "/images/disconnected_footer.png";
}
function btResponseHandler(e) {
Ti.API.debug("index: btResponseHandler, e = "+JSON.stringify(e));
switch (e.code) {
case "tempVal":
if(Alloy.Globals.farenheitOn){
var fvalue = 32+((e.value*9)/5);
$.tempRow.setWeatherItemValueText(fvalue.toFixed(1)+"\u00B0"+"F");
} else {
$.tempRow.setWeatherItemValueText(e.value.toFixed(1)+"\u00B0"+"C");
}
break;
case "humidityVal":
$.humidityRow.setWeatherItemValueText(e.value.toFixed(1)+"%");
break;
case "pressureVal":
$.pressureRow.setWeatherItemValueText(e.value.toFixed(1));
break;
case "connected":
if(Alloy.Globals.audioOn){
var player = Ti.Media.createSound({url: connectSound});
player.play();
}
btConnected();
clearTimeout(connectionTimeout);
$.activityIndicator.hide();
$.activityIndicatorVw.visible=false;
break;
case "disconnected":
if(Alloy.Globals.audioOn){
var player = Ti.Media.createSound({url: disconnectSound});
player.play();
}
btDisconnected();
break;
}
}
myTimeout = setTimeout(animateTitleOut, waitDuration);
startConnectionTimer();
if ((Ti.Platform.osname === 'android' && Ti.Platform.version >= '4.3') ||
((Ti.Platform.osname === 'iphone' || Ti.Platform.osname === 'ipad') &&
Ti.Platform.version >= '5.0')) {
bt.init(btResponseHandler);
} else {
alert("This application only runs on devices with Bluetooth capabilites.");
}
$.index.open();
$.activityIndicator.show();
$.activityIndicatorVw.visible=true;
(function(){
var props = Ti.App.Properties.listProperties();
Ti.API.debug("props = "+props);
Ti.API.debug("props.length = "+props.length);
for (var i=0, ilen=props.length; i<ilen; i++){
var value = Ti.App.Properties.getString(props[i]);
Ti.API.debug(props[i] + ' = ' + value);
if(props[i]==='farenheitOn'){
value = Ti.App.Properties.getBool(props[i]);
Ti.API.debug('farenheitOn = ' + value);
Alloy.Globals.farenheitOn = value;
} else if(props[i]==='audioOn'){
value = Ti.App.Properties.getBool(props[i]);
Ti.API.info('audioOn = ' + value);
Alloy.Globals.audioOn = value;
} else if(props[i]==='didRunAlready'){
Alloy.Globals.firstRun = false;
}
}
if(Alloy.Globals.firstRun) {
Ti.App.Properties.setBool('didRunAlready', true);
alert("My Weather Station requires a Texas Insruments CC2541 SensorTag.\n\nThis message only appears once.");
}
})();
".container": {
backgroundColor:"white",
fullscreen: true,
navBarHidden: true
}
"Label": {
width: Ti.UI.SIZE,
height: Ti.UI.SIZE,
color: "#000",
font: {
fontFamily: 'Roboto-Regular'
}
}
"#titleVw": {
height: 44,
backgroundColor: "#f8f8f8",
zIndex: 2,
top: 0
}
"#titleVwLbl": {
color: "black",
font: {
fontSize: 18,
fontFamily: 'Roboto-Regular'
}
}
"#mainVw": {
height: Ti.UI.FILL,
backgroundColor: "#ededed",
layout: "vertical"
}
".titleVwBtn": {
width: 38,
height: 33
}
"#connectBtn": {
backgroundImage: "/images/disconnected.png",
left: 5
}
"#settingsBtn": {
backgroundImage: "/images/settings.png",
right: 5
}
".separatorSpaceVw": {
height: "5%"
}
"#headerSpaceVW": {
height: 50
}
".footerVW": {
height: 50
}
"#footerVwLbl": {
right: 10,
bottom: 2,
color: "black",
font: {
fontSize: 12,
fontFamily: 'Roboto-Thin'
}
}
"#footerIv": {
image: "/images/disconnected_footer.png",
width: 18,
height: 18,
left: 10,
bottom: 2
}
<Alloy>
<Window class="container">
<View id="titleVw">
<Button id="connectBtn" class="titleVwBtn" onClick="connectBtnClick" />
<Label id="titleVwLbl">My Weather Station</Label>
<Button id="settingsBtn" class="titleVwBtn" onClick="settingsBtnClick" />
<!-- <View class="colorLineVw"/> -->
</View>
<View id="mainVw">
<!-- <Label id="label" onClick="doClick">Hello, World</Label> -->
<View id="headerSpaceVW"/>
<Require src='weatherItemRow' id='tempRow' iconImage="/images/temp.png" weatherItemValueText="--.-\u00B0" title="TEMPERATURE" />
<View class="separatorSpaceVw"/>
<Require src='weatherItemRow' id='humidityRow' iconImage="/images/humidity.png" weatherItemValueText="--.--%" title="RELATIVE HUMIDITY" />
<View class="separatorSpaceVw" />
<Require src='weatherItemRow' id='pressureRow' iconImage="/images/pressure.png" weatherItemValueText="----.-" title="BAROMETRIC PRESSURE" />
<!-- <View class="separatorSpaceVw" /> -->
<View class="footerVw">
<Label id="footerVwLbl">My Weather Station</Label>
<ImageView id="footerIv" />
</View>
</View>
<View id="activityIndicatorVw" visible="false">
<ActivityIndicator id="activityIndicator" message="Connecting..."/>
</View>
</Window>
</Alloy>
$.closeBtn.addEventListener("click", function(e) {
Ti.API.info("Info: Close Button click event");
$.info.close();
});
if(Alloy.Globals.audioOn) {
$.audioSw.value = Alloy.Globals.audioOn;
} else {
$.audioSw.value = false;
}
if(Alloy.Globals.farenheitOn) {
$.farenheitSw.value = Alloy.Globals.farenheitOn;
} else {
$.farenheitSw.value = false;
}
$.farenheitSw.addEventListener('change', function(e){
Ti.App.Properties.setString('farenheitOn', $.farenheitSw.value);
Alloy.Globals.farenheitOn = $.farenheitSw.value;
});
$.audioSw.addEventListener('change', function(e){
Ti.App.Properties.setString('audioOn', $.audioSw.value);
Alloy.Globals.audioOn = $.audioSw.value;
});
"#info": {
backgroundColor:"white",
fullscreen: true,
navBarHidden: true
}
"#titleVw": {
height: 44,
backgroundColor: "#f8f8f8",
zIndex: 2,
top: 0
}
"#titleVwLbl": {
color: "black",
font: {
fontSize: 18,
fontFamily: 'Roboto-Regular'
}
}
"#closeBtn":{
width: 32,
height: 32,
right: 5,
image:'/images/closeBtn.png'
}
"Label":{
color: "black",
font: {fontFamily:'Roboto-Regular', fontSize:"14dp"}
}
".infoLabel":{
top: 20,
color: "black",
height : 'auto',
width : '95%',
horizontalWrap: true,
textAlign:Ti.UI.TEXT_ALIGNMENT_CENTER,
font: {fontFamily:'Roboto-Regular', fontSize:"14dp"}
}
".infoLabelSmall":{
top: 20,
color: "black",
height : 'auto',
width : '95%',
horizontalWrap: true,
textAlign:Ti.UI.TEXT_ALIGNMENT_CENTER,
font: {fontFamily:'Roboto-Regular', fontSize:"10dp"}
}
"#logoIV": {
//image: "/images/appc_logo_white.png", // use on dark backgrounds
image: "/images/appc_logo_black.png", // use on light backgrounds
width: 138,
height: 29
}
".settingsSw": {
left: 5,
width: "50%"
}
".settingsSw[platform=android]": {
style: Titanium.UI.Android.SWITCH_STYLE_CHECKBOX
}
<Alloy>
<Window class="container" id="info" modal="true">
<View layout='vertical'>
<View id='titleVw'>
<ImageView id='closeBtn' />
<Label id="titleVwLbl" class='titleVwLbl'>About My Weather Station</Label>
<View class="colorLineVw"/>
</View>
<ScrollView showVerticalScrollIndicator="false" showHorizontalScrollIndicator="false" height='Ti.UI.FILL' width='90%' layout='vertical'>
<Label class='infoLabel'>My Weather Station is a personal weather application that leverages the Texas Instruments CC2541 Bluetooh LE SensorTag \n\nAttributions:\nLogical Labs Titanium Bluetooth LE Module\nFlatIcons - Creative Commons BY 3.0\n
</Label>
<Label class='infoLabel'>No personally identifiable information is required or collected by this application.\n</Label>
<Label class='infoLabel'>Powered by</Label>
<ImageView id="logoIV" />
<Label class='infoLabelSmall'>Appcelerator® is a trademark of Appcelerator, Inc. and is used under license.\n\n</Label>
<View id="settings" height="Ti.UI.SIZE" layout="vertical">
<View layout="horizontal" height="Ti.UI.SIZE" >
<View height="40" width="50%" top="5">
<Label right="5">Farenheit:</Label>
</View>
<View height="40" width="45%" top="5">
<Switch id="farenheitSw" class="settingsSw"></Switch>
</View>
<View height="40" width="50%" top="5">
<Label right="5">Sound:</Label>
</View>
<View height="40" width="45%" top="5">
<Switch id="audioSw" class="settingsSw"></Switch>
</View>
</View>
</View>
</ScrollView>
</View>
</Window>
</Alloy>
<?xml version="1.0" encoding="UTF-8"?>
<ti:app xmlns:ti="http://ti.appcelerator.org">
<id>com.appcelerator.mws</id>
<name>MWS</name>
<version>1.0</version>
<publisher>lbrenman</publisher>
<url>http://</url>
<description>undefined</description>
<copyright>2014 by lbrenman</copyright>
<icon>appicon.png</icon>
<fullscreen>false</fullscreen>
<navbar-hidden>false</navbar-hidden>
<analytics>true</analytics>
<guid>XXXXXX</guid>
<property name="ti.ui.defaultunit" type="string">dp</property>
<ios>
<min-ios-ver>6.0</min-ios-ver>
<plist>
<dict>
<key>UISupportedInterfaceOrientations~iphone</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>
<key>UIRequiresPersistentWiFi</key>
<false/>
<key>UIPrerenderedIcon</key>
<false/>
<key>UIStatusBarHidden</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
</dict>
</plist>
</ios>
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
<application android:theme="@style/Theme.NoActionBar">
<activity
android:configChanges="keyboardHidden|orientation"
android:name="org.appcelerator.titanium.TiActivity" android:screenOrientation="portrait"/>
</application>/&gt;
<uses-sdk
android:minSdkVersion="14" android:targetSdkVersion="19"/>
</manifest>
</android>
<mobileweb>
<precache/>
<splash>
<enabled>true</enabled>
<inline-css-images>true</inline-css-images>
</splash>
<theme>default</theme>
</mobileweb>
<modules>
<module platform="android">com.logicallabs.bluetoothle</module>
<module platform="iphone">com.logicallabs.bluetoothle</module>
</modules>
<deployment-targets>
<target device="android">true</target>
<target device="blackberry">false</target>
<target device="ipad">true</target>
<target device="iphone">true</target>
<target device="mobileweb">false</target>
</deployment-targets>
<sdk-version>3.4.0.GA</sdk-version>
<plugins>
<plugin version="1.0">ti.alloy</plugin>
</plugins>
</ti:app>
var args = arguments[0] || {};
$.weatherIconIv.image = args.iconImage || "/images/temp.png";
$.weatherItemLbl.text = args.weatherItemValueText || "N/A"
$.titleVwLbl.text = args.title || "Temperature";
$.setWeatherItemValueText = function (val) {
$.weatherItemLbl.text = val;
};
".weatherItemVw": {
height: "25%",
//width: "95%",
right: 10,
left: 10,
backgroundColor: "white",
layout: "vertical",
borderWidth: 1,
borderColor: "#cfcfcf"
}
".titleVw": {
height: "40",
backgroundColor: "#f5f5f5"
}
".titleVwLbl": {
top: 10,
left: 5,
color: "#707070",
font: {
fontSize: 14,
fontFamily: 'Roboto-Regular'
}
}
".subVw": {
width: "50%",
height: Ti.UI.FILL
//backgroundColor: "gray"
}
"#weatherItemLbl": {
right: 10,
color: "#707070",
font: {
fontSize: 40,
fontFamily: 'Roboto-Regular'
}
}
".weatherItemIv": {
width: 66,
height: 66,
left: 10
}
<Alloy>
<View class="weatherItemVw">
<View class="titleVw">
<Label id="titleVwLbl" class="titleVwLbl" />
</View>
<View layout="horizontal" height="Ti.UI.FILL">
<View class="subVw">
<ImageView id="weatherIconIv" class="weatherItemIv"/>
</View>
<View class="subVw">
<Label id="weatherItemLbl">70F</Label>
</View>
</View>
</View>
</Alloy>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment