- This module brings Bluetooth Low Energy into the mobile apps for titanium app developers.
- Using Bluetooth Low Energy module, developers can bring feature like:
- Act as BLE Central :
- Central can scan nearby peripheral, connect and exchange data with the peripherals
- Central can subscribe with peripheral to get latest updates for peripheral
- Act as BLE Peripheral:
- Peripheral can advertise services, connect and exchange data with multiple central.
- Use L2CAP Channel:
- L2CAP is introduced with IOS 11 and Android 10, its used to transfer large amount of data between central and peripheral at real time.
- Main use case addressed by this module is Exchange of Data and Communicating with Central and Peripherals that supports Bluetooth Low Energy.
- Set the
<module>
element in tiapp.xml, such as this:
<modules>
<module platform="android">appcelerator.ble</module>
</modules>
- To access this module from JavaScript, you would do the following:
var BLE = require("appcelerator.ble");
The BLE variable is a reference to the Module object.
- Edit the
plist
with followinguses-permission
element to the ios plist section of the tiapp.xml file.
<ti:app>
<ios>
<plist>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>usage description string</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>usage description string</string>
</plist>
</ios>
</ti:app>
- If your app needs to run in background to perform certain Bluetooth-related tasks, Edit the
plist
with followinguses-permission
element to the ios plist section of the tiapp.xml file.
<ti:app>
<ios>
<plist>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>
</plist>
</ios>
</ti:app>
- Set the
<module>
element in tiapp.xml, such as this: - Edit the
plist
with followinguses-permission
element to the ios plist section, if your are adding iBeacon Scan
<ti:app>
<ios>
<plist>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Allow Location permission</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Allow Location permission</string>
</plist>
</ios>
</ti:app>
- Set the
<module>
element in tiapp.xml, such as this:
<modules>
<module platform="ios">appcelerator.ble</module>
</modules>
- To access this module from JavaScript, you would do the following:
var BLE = require("appcelerator.ble");
The BLE variable is a reference to the Module object.
-
Use
initCentralManager
to create Central Managervar centralManager = BLE.initCentralManager();
-
Check for
didUpdateState
event forcentralManager
status -
Once
centralManager
is inBLE.MANAGER_STATE_POWERED_ON
state, scan for peripherals usingstartScan
centralManager.startScan();
-
Use
peripherals
property to get all discovered peripheralsvar peripherals = centralManager.peripherals;
-
Use
connect
to connect to peripheralcentralManager.connectPeripheral({ peripheral: peripheral, options: { [BLE.CONNECT_PERIPHERAL_OPTIONS_KEY_NOTIFY_ON_CONNECTION]: true, [BLE.CONNECT_PERIPHERAL_OPTIONS_KEY_NOTIFY_ON_DISCONNECTION]: true } });
-
Use
isConnected
to check if connectedperipheral.isConnected
-
Use
discoverServices
to discover servicesperipheral.discoverServices();
result will be return in
didDiscoverServices
eventperipheral.addEventListener('didDiscoverServices', function (e) {});
-
Use
discoverCharacteristics
peripheral.discoverCharacteristics({ service: service });
result will be return in
didDiscoverCharacteristics
eventconnectedPeripheral.addEventListener('didDiscoverCharacteristics', function (e) {});
-
Use
subscribeToCharacteristic
andunsubscribeFromCharacteristic
to subscribe or unsubscribeperipheral.subscribeToCharacteristic({ characteristic: charactersticObject }); peripheral.unsubscribeFromCharacteristic({ characteristic: charactersticObject });
-
Use
cancelPeripheralConnection
to disconnect the connectioncentralManager.cancelPeripheralConnection({ peripheral: peripheral });
-
As the module currently provides support to act only as central for the Android, hence to test the example application, user can use any heart-rate peripheral or the peripheral simulator in order to do the connection and data-exchange with the central.
-
Use
initCentralManager
to create Central Managervar centralManager = BLE.initCentralManager();
-
Check for
didUpdateState
event forcentralManager
status -
Once
centralManager
is inBLE.MANAGER_STATE_POWERED_ON
state, scan for perpherals usingstartScan
centralManager.startScan();
-
Use
peripherals
property to get all discovered peripheralsvar peripherals = centralManager.peripherals;
-
Use
connect
to connect to peripheralcentralManager.connectPeripheral({ peripheral: peripheral, options: { [BLE.CONNECT_PERIPHERAL_OPTIONS_KEY_NOTIFY_ON_CONNECTION]: true, [BLE.CONNECT_PERIPHERAL_OPTIONS_KEY_NOTIFY_ON_DISCONNECTION]: true } });
-
Use
isConnected
to check if connectedperipheral.isConnected
-
Use
discoverServices
to discover servicesperipheral.discoverServices();
result will be return in
didDiscoverServices
eventperipheral.addEventListener('didDiscoverServices', function (e) {});
-
Use
discoverCharacteristics
peripheral.discoverCharacteristics({ service: service });
result will be return in
didDiscoverCharacteristics
eventconnectedPeripheral.addEventListener('didDiscoverCharacteristics', function (e) {});
-
Use
subscribeToCharacteristic
andunsubscribeFromCharacteristic
to subscribe or unsubscribeperipheral.subscribeToCharacteristic({ characteristic: charactersticObject }); peripheral.unsubscribeFromCharacteristic({ characteristic: charactersticObject });
-
Get
psmIdentifier
fromdidUpdateValueForCharacteristic
event and openchannel
peripheral.addEventListener('didUpdateValueForCharacteristic', function (e) { if (e.errorCode !== null) { alert('Error while didUpdateValueForCharacteristic' + e.errorCode + '/' + e.errorDomain + '/' + e.errorDescription); return; } let value = e.value.toString(); if (value) { e.sourcePeripheral.openL2CAPChannel({ psmIdentifier: Number(e.value.toString()) }); } });
-
Get
channel
object fromdidOpenChannel
event and set eventonDataReceived
for received data andonStreamError
for stream errorsconnectedPeripheral.addEventListener('didOpenChannel', function (e) { if (e.errorCode !== null) { alert('Error while opening channel' + e.errorCode + '/' + e.errorDomain + '/' + e.errorDescription); return; } channel = e.channel; channel.addEventListener('onDataReceived', function (e) { var data = e.data; }); channel.addEventListener('onStreamError', function (e) { alert('Error ' + e.errorCode + '/' + e.errorDomain + '/' + e.errorDescription); }); });
-
Use
write
function from channel to write valuesvar newBuffer = Ti.createBuffer({ value: 'hello world' }); channel.write({ data: newBuffer });
-
Use
cancelPeripheralConnection
to disconnect the connectioncentralManager.cancelPeripheralConnection({ peripheral: peripheral });
-
Use
close
function to close channelchannel.close();
-
Use
initPeripheralManager
to create Peripheral Managervar peripheralManager = BLE.initPeripheralManager();
-
Use
createMutableCharacteristic
to create charracteristiccharProperties = [ BLE.CHARACTERISTIC_PROPERTIES_READ, BLE.CHARACTERISTIC_PROPERTIES_WRITE_WITHOUT_RESPONSE, BLE.CHARACTERISTIC_PROPERTIES_NOTIFY ]; charPermissions = [ BLE.CHARACTERISTIC_PERMISSION_READABLE, BLE.CHARACTERISTIC_PERMISSION_WRITEABLE ]; var characteristic = BLE.createMutableCharacteristic({ uuid: characteristicUUID, properties: charProperties, permissions: charPermissions });
-
Use
addService
to add serviceservice = peripheralManager.addService({ uuid: serviceUUID, primary: true, characteristics: [ characteristic ] });
-
Once
peripheralManager
is inBLE.MANAGER_STATE_POWERED_ON
state, start advertising usingstartAdvertising
var name = IOS ? 'BLE-Sample' : true; peripheralManager.startAdvertising({ localName: name, serviceUUIDs: servicesUUIDs });
-
Use
updateValue
to update charracteristic valuevar buffer = Ti.createBuffer({ value: 'hello world' }); peripheralManager.updateValue({ characteristic: characteristic, data: buffer, central: centrals });
-
Use
stopAdvertising
to stop advertisingperipheralManager.stopAdvertising();
-
Use
closePeripheral
to close the peripheral after it is done with the peripheral operations. (Android only)
peripheralManager.closePeripheral();
-
Use
initPeripheralManager
to create Peripheral Managervar peripheralManager = BLE.initPeripheralManager();
-
Use
createMutableCharacteristic
to create charracteristicvar characteristic = BLE.createMutableCharacteristic({ uuid: BLE.CBUUID_L2CAPPSM_CHARACTERISTIC_STRING, properties: [ BLE.CHARACTERISTIC_PROPERTIES_READ, BLE.CHARACTERISTIC_PROPERTIES_INDICATE ], permissions: [ BLE.CHARACTERISTIC_PERMISSION_READABLE ] });
-
Use
addService
to add servicevar service = peripheralManager.addService({ uuid: serviceUUID, primary: true, characteristics: [ characteristic ] });
-
Once
peripheralManager
is inBLE.MANAGER_STATE_POWERED_ON
state, usepublishL2CAPChannel
to publish channel and start advertising usingstartAdvertising
peripheralManager.publishL2CAPChannel({ encryptionRequired: false }); peripheralManager.startAdvertising({ localName: name, serviceUUIDs: servicesUUIDs });
-
Update
psmIdentifier
to characteristic indidPublishL2CAPChannel
eventperipheralManager.addEventListener('didPublishL2CAPChannel', function (e) { var psmBuffer = Ti.createBuffer({ value: e.psm + '' }); manager.updateValue({ characteristic: characteristic, data: psmBuffer, central: centrals }); });
-
Get Channel from
didOpenL2CAPChannel
event and setonDataReceived
event to read values andonStreamError
event for check stream errorsperipheralManager.addEventListener('didOpenL2CAPChannel', function (e) { var channel = e.channel; channel.addEventListener('onDataReceived', function (e) { var data = e.data; }); channel.addEventListener('onStreamError', function (e) {}); });
-
Use
write
function from channel to write valuesvar newBuffer = Ti.createBuffer({ value: 'hello world' }); channel.write({ data: newBuffer });
-
Use
close
function to close channelchannel.close();
-
Use
stopAdvertising
to stop advertisingperipheralManager.stopAdvertising();
-
Use
closePeripheral
to close the peripheral after it is done with the peripheral operations. (Android only)
peripheralManager.closePeripheral();
-
Use
initPeripheralManager
to create Peripheral Managervar peripheralManager = BLE.initPeripheralManager();
-
Use
createBeaconRegion
to create BeaconRegionvar beaconRegion = BLE.createBeaconRegion({ uuid: '135C8F13-6A2D-46ED-AA71-FB956FC23742', major: 1, minor: 100, identifier: 'com.appcelerator.BluetoothLowEnergy.beacon' });
-
Once
peripheralManager
is inBLE.MANAGER_STATE_POWERED_ON
state, start advertising usingstartAdvertisingBeaconRegion
peripheralManager.startAdvertisingBeaconRegion({ beaconRegion: beaconRegion });
-
Edit the
plist
with followinguses-permission
element to the ios plist section<ti:app> <ios> <plist> <key>NSLocationWhenInUseUsageDescription</key> <string>Allow Location permission</string> <key>NSLocationAlwaysUsageDescription</key> <string>Allow Location permission</string> </plist> </ios> </ti:app>
-
Use
initPeripheralManager
to create Region Managervar regionManager = BLE.createRegionManager();
-
Use
requestWhenInUseAuthorization
to request location permissionregionManager.requestWhenInUseAuthorization();
-
Use
createBeaconRegion
to create BeaconRegionvar beaconRegion = BLE.createBeaconRegion({ uuid: '135C8F13-6A2D-46ED-AA71-FB956FC23742', major: 1, minor: 100, identifier: 'com.appcelerator.BluetoothLowEnergy.beacon' });
-
Once
regionManager
is inBLE.LOCATION_MANAGER_AUTHORIZATION_STATUS_AUTHORIZED_WHEN_IN_USE | BLE.LOCATION_MANAGER_AUTHORIZATION_STATUS_AUTHORIZED_ALWAYS
state, usestartRegionMonitoring
to start monitoring and start ranging usingstartRangingBeaconsInRegion
regionManager.startRegionMonitoring({ beaconRegion: beaconRegion }); regionManager.startRangingBeaconsInRegion({ beaconRegion: beaconRegion });
-
Get ranged beacons from
didRangeBeacons
event and checkproximity
andaccuracy
to check beacon locationvar didRangeBeacons = (e) => { var becaons = e.beacons; if (becaons.length === 0) { alert('No beacon in range'); return; } var proximity = becaons[0].proximity; var accuracy = becaons[0].accuracy; switch (proximity) { case BLE.BEACON_PROXIMITY_UNKNOWN: alert('Beacon Location : UNKNOWN'); break; case BLE.BEACON_PROXIMITY_IMMEDIATE: alert('Beacon Location : IMMEDIATE (approx. ' + accuracy + 'm)'); break; case BLE.BEACON_PROXIMITY_NEAR: alert('Beacon Location : NEAR (approx. ' + accuracy + 'm)'); break; case BLE.BEACON_PROXIMITY_FAR: alert('Beacon Location : FAR (approx. ' + accuracy + 'm)'); break; default: alert('Beacon Location : UNKNOWN'); break; } }; regionManager.addEventListener('didRangeBeacons', didRangeBeacons);
-
Use
stopRegionMonitoring
to stop monitoring and stop ranging usingstopRangingBeaconsInRegion
regionManager.stopRegionMonitoring({ beaconRegion: beaconRegion }); regionManager.stopRangingBeaconsInRegion({ beaconRegion: beaconRegion });
-
you can access bytes from TiBuffer using:
for (i = 0; i < buffer.length; i++) { var byte = buffer[i]; }
- Please see the
example/
folder. - Please see the
example/ImageTransferUsingChannelStream
folder for how to use channel stream API's to transfer bigger data like images. - Please see the
example/beacon
folder for iBeacon sample.
- This behaviour is observed on certain android devices. While starting the BLE scan, make sure the location service is turned-on in order to receive the scan results.
- It is observed with certain fitness watches (may be other BLE hardware too) that upon connecting them with android-central application, the connection gets auto-disconnected after certain period of time(ranging from immediately to up-to 50s or more). The fix is to first pair your peripheral-device(watch or any other hardware) with android device via Settings->Bluetooth screen and then do the connection procedure from central-application.
- Beacon do not have background support
Simply run appc run -p ios --build-only
and appc run -p android --build-only
which will compile and package your module.
Copy the module zip file into the root folder of your Titanium application or in the Titanium system folder (e.g. /Library/Application Support/Titanium
).
Axway
Copyright (c) 2020 by Axway, Inc. Please see the LICENSE file for further details.