Subscribing to characteristics
Hi,
I'm having trouble subscribing to characteristics and the documentation doesn't explain how. Following the XCode Core Bluetooth documentation isn't helping.
I am trying to subscribe to a couple of services on a BT device, battery and immediate alert.
I'm successfully scanning for devices, finding the device, getting its data, listing services and getting the characteristics.
What I need to do from here is to subscribe to the characteristics I need and receive callbacks whenever the battery level changes or the alert level changes on the BT device. I've tried a number of approaches but none seem to be working.
Code up to subscription below:
// -----------------------------------------------------------------------------
// Peripheral Connect Callback
// -----------------------------------------------------------------------------
centralManager.addEventListener('didConnectPeripheral', function(e)
{
Ti.API.info('Connected to Periperal...');
connectedPeripheral = e.peripheral;
connectedPeripheral.addEventListener('didUpdateValueForCharacteristic', function(e) {
Ti.API.info('didUpdateValueForCharacteristic');
Ti.API.info(e);
});
connectedPeripheral.addEventListener('didDiscoverServices', function(e)
{
Ti.API.info('Service Discovery...');
p = e.peripheral;
Ti.API.info('peripheral has', p.services.length, 'service(s)');
p.services.forEach(function(service)
{
Ti.API.info('service: ', service.uuid);
// Assign battery service
if(service.uuid == gattBattery)
{
Ti.API.info("BatteryService Found");
serviceBattery = service;
p.addEventListener('didDiscoverCharacteristicsForService', function(e)
{
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic)
{
Ti.API.info('Characteristic ID:', characteristic.uuid);
// Want to subscribe to this characteristic and receive value changes from peripheral
if(characteristic.uuid == '2A19')
{
}
});
});
p.discoverCharacteristicsForService({
characteristics : [],
service : service
});
}
});
});
// Discover services
connectedPeripheral.discoverServices();
});
Btw, I think the documentation and example lacks basic info like this. So if anyone can help with this problem then I'll submit working code to the project so that the example/docs are more complete.
Hi how can read data from HM-10 BLE device?
This is my log:
[INFO] : testBluetooth/1.0 (6.2.1.301ffa4)
[INFO] : didUpdateState
[INFO] : Powered On
[INFO] : didDiscoverPeripheral
[INFO] : HMSoft
[INFO] : connecting
[INFO] :
[INFO] : didConnectPeripheral
[INFO] : connected to HMSoft
[INFO] :
[INFO] : didDiscoverServices
[INFO] : peripheral has 1 service(s)
[INFO] : service: FFE0
[INFO] : calling discoverCharacteristicsForService
[INFO] :
[INFO] : didDiscoverCharacteristicsForService
[INFO] : service: FFE0
[INFO] : characteristic: FFE1
Is anyone monitoring this forum?
As I am not making example-codes for all kind of possible BLE-use-cases, either something or my or your end is incorrect. Please feel free to submit a fix if something does not work for you.
OK, I followed the core bluetooth examples and when I try to subscribe to either the battery or immediate alert service by setting notify value to true, I am not getting state change callbacks.
here is my code for attempting to get the value and state changes for the battery service:
centralManager.addEventListener('didDiscoverPeripheral', function(e)
{
if(e.peripheral.name == peripheralName)
{
Ti.API.info('Peripheral ' + e.peripheral.name + ' Found...');
centralManager.stopScan();
peripheralRSSI = e.rssi;
var options = { notifyOnConnection : true, notifyOnDisconnection : true, notifyOnNotification : true };
centralManager.connectPeripheral(e.peripheral, options);
}
});
// -----------------------------------------------------------------------------
// Peripheral Connect Callback
// -----------------------------------------------------------------------------
centralManager.addEventListener('didConnectPeripheral', function(e)
{
Ti.API.info('Connected to Periperal...');
connectedPeripheral = e.peripheral;
connectedPeripheral.addEventListener('didDiscoverServices', function(e)
{
Ti.API.info('Service Discovery...');
p = e.peripheral;
p.addEventListener('didUpdateValueForCharacteristic', function(e)
{
Ti.API.info('didUpdateValueForCharacteristic');
Ti.API.info(e);
});
Ti.API.info('peripheral has', p.services.length, 'service(s)');
p.services.forEach(function(service)
{
// Assign battery service
if(service.uuid == gattBattery)
{
Ti.API.info("BatteryService Found: " + service.uuid);
serviceBattery = service;
p.addEventListener('didDiscoverCharacteristicsForService', function(e)
{
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic)
{
Ti.API.info('Characteristic ID:', characteristic.uuid);
// Want to subscribe to this characteristic and receive value changes from peripheral
if(characteristic.uuid == '2A19')
{
p.setNotifyValueForCharacteristic(true, characteristic);
p.readValueForCharacteristic(characteristic);
Ti.API.info('Characteristic Value:', characteristic.value);
}
});
});
p.discoverCharacteristicsForService({
characteristics : [],
service : service
});
}
// Assign immediate alert service
/*
if(service.uuid == gattImmediateAlert)
{
Ti.API.info("Immediate Alert Found: " + service.uuid);
serviceImmediateAlert = service;
p.addEventListener('didDiscoverCharacteristicsForService', function(e)
{
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic)
{
Ti.API.info('Characteristic ID:', characteristic.uuid);
// Subscribe to this characteristic and receive value changes from peripheral
if(characteristic.uuid == '2A06')
{
p.setNotifyValueForCharacteristic(true, characteristic);
p.readValueForCharacteristic(characteristic);
Ti.API.info('Characteristic Value:', characteristic.value);
}
});
});
p.discoverCharacteristicsForService({
characteristics : [],
service : service
});
}
*/
});
});
// Discover services
connectedPeripheral.discoverServices();
});
// -----------------------------------------------------------------------------
// Central Manager Updated State
// -----------------------------------------------------------------------------
centralManager.addEventListener('didUpdateState', function(e)
{
Ti.API.info('Central Manager State Updated:');
switch (e.state) {
case BLE.MANAGER_STATE_RESETTING:
Ti.API.info('Resetting');
break;
case BLE.MANAGER_STATE_UNSUPPORTED:
Ti.API.info('Unsupported');
break;
case BLE.MANAGER_STATE_UNAUTHORIZED:
Ti.API.info('Unauthorized');
break;
case BLE.MANAGER_STATE_POWERED_OFF:
Ti.API.info('Powered Off');
break;
case BLE.MANAGER_STATE_POWERED_ON:
Ti.API.info('Powered On');
break;
case BLE.MANAGER_STATE_UNKNOWN:
default:
Ti.API.info('Unknown');
break;
}
});
and output:
[INFO] : Central Manager State Updated:
[INFO] : Powered On
[INFO] : Peripheral "Security Tag" Found...
[INFO] : Connected to Periperal...
[INFO] : Service Discovery...
[INFO] : peripheral has 6 service(s)
[INFO] : BatteryService Found: 180F
[INFO] : Characteristic ID: 2A19
[INFO] : Characteristic Value: I
I'm not sure why the characteristic value is showing as I, and the immediate alert service:
// Assign immediate alert service
if(service.uuid == gattImmediateAlert)
{
Ti.API.info("Immediate Alert Found: " + service.uuid);
serviceImmediateAlert = service;
p.addEventListener('didDiscoverCharacteristicsForService', function(e)
{
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic)
{
Ti.API.info('Characteristic ID:', characteristic.uuid);
// Subscribe to this characteristic and receive value changes from peripheral
if(characteristic.uuid == '2A06')
{
p.setNotifyValueForCharacteristic(true, characteristic);
p.readValueForCharacteristic(characteristic);
Ti.API.info('Characteristic Value:', characteristic.value);
}
});
});
p.discoverCharacteristicsForService({
characteristics : [],
service : service
});
}
output:
Connected to Periperal...
[INFO] : Service Discovery...
[INFO] : peripheral has 6 service(s)
[INFO] : Immediate Alert Found: 1802
[INFO] : Characteristic ID: 2A06
[INFO] : Characteristic Value: [object TiBlob]
When i press the button on the peripheral that changes the Immediate Alert State I am not getting a value change callback response. So it appears that its not subscribing to the characteristic, or the callback isnt working.
There are a couple of issues in your code, one sinificant is:
p.setNotifyValueForCharacteristic(true, characteristic);
p.readValueForCharacteristic(characteristic);
Ti.API.info('Characteristic Value:', characteristic.value);
You cannot set, get and display a value synchronously.
Writing will fire didUpdateNotificationStateForCharacteristic (just exposed as a new event), writing will fire didUpdateValueForCharacteristic. BLE is highly asynchronous and event-driven, please check the exposed events to receive the correct values and become familiar with the general event-driven programming.
Hi FizixRichard have you solved?
No I haven't, I'm looking through the BTLE core docs and examples as per hansemannn's suggestion. However it doesn't seem to mesh with this API. In the examples it appears as though they are subscribing to the service characteristic and then listening for state changes from the device. Similarly with that expensive titanium BT plugin, the heart rate sensor just subscribes to the characteristic and then listens for value change notifications. It feels like there is something "missing" that's going on behind this that I'm not seeing.
Hi hansemannn what is the right code for read Characteristic notifications from BLE device? I have 1 service (FFE0) with 1 Characteristic (FFE1). Can you write sample code? Thanks in advance.
If I can figure it out I'll post code and an example to the project. Still, any help would be greatly appreciated.
p.addEventListener('didUpdateValueForCharacteristic', _didUpdateValueForCharacteristic);
p.setNotifyValueForCharacteristic(true, characteristic);
function _didUpdateValueForCharacteristic(e) {
Ti.API.info('didUpdateValueForCharacteristic');
Ti.API.info(e);
}
every time the peripheral updates value, _didUpdateValueForCharacteristic will be called
Hi wilson, this is my code (from app.js example):
var BLE = require('ti.bluetooth'); var peripheralManager;
var win = Ti.UI.createWindow({ backgroundColor : '#fff' });
var btn1 = Ti.UI.createButton({ title : 'Start scan', top : 40 });
var centralManager = BLE.createCentralManager(); var peripheralManager = BLE.createPeripheralManager();
btn1.addEventListener('click', function() { if (centralManager.isScanning()) { alert('Already scanning, please stop scan first!'); return; } else if (centralManager.getState() != BLE.MANAGER_STATE_POWERED_ON) { alert('The BLE manager needs to be powered on before. Call initialize().'); return; }
centralManager.startScan();
Ti.API.info('Scanning....');
// Optional: Search for specified services
// centralManager.startScanWithServices(['384DF4C0-8BAE-419D-9A65-2D67942C2DB7']);
});
var btn2 = Ti.UI.createButton({ title : 'Stop scan', top : 100 });
btn2.addEventListener('click', function() { if (!centralManager.isScanning()) { alert('Not scanning!'); return; } Ti.API.info('Stop scan...'); centralManager.stopScan(); });
/**
- Central Manager Events */
centralManager.addEventListener('didDiscoverPeripheral', function(e) { console.log('didDiscoverPeripheral'); console.log(e.peripheral.name);
if ((e.peripheral.name != null) && e.peripheral.name.indexOf('HMS') == 0) {
Ti.API.info('Stop scan...');
centralManager.stopScan();
console.log('connecting');
var options = {
notifyOnConnection : true,
notifyOnDisconnection : true,
notifyOnNotification : true
};
centralManager.connectPeripheral(e.peripheral);
}
});
centralManager.addEventListener('didUpdateState', function(e) { Ti.API.info('didUpdateState');
switch (e.state) {
case BLE.MANAGER_STATE_RESETTING:
Ti.API.info('Resetting');
break;
case BLE.MANAGER_STATE_UNSUPPORTED:
Ti.API.info('Unsupported');
break;
case BLE.MANAGER_STATE_UNAUTHORIZED:
Ti.API.info('Unauthorized');
break;
case BLE.MANAGER_STATE_POWERED_OFF:
Ti.API.info('Powered Off');
break;
case BLE.MANAGER_STATE_POWERED_ON:
Ti.API.info('Powered On');
break;
case BLE.MANAGER_STATE_UNKNOWN:
default:
Ti.API.info('Unknown');
break;
}
});
centralManager.addEventListener('didConnectPeripheral', function(e) { Ti.API.info('Connected to Periperal...');
connectedPeripheral = e.peripheral;
connectedPeripheral.addEventListener('didDiscoverServices', function(e) {
Ti.API.info('Service Discovery...');
p = e.peripheral;
Ti.API.info('peripheral has', p.services.length, 'service(s)');
p.services.forEach(function(service) {
// Assign service
if (service.uuid == 'FFE0') {
Ti.API.info("service Found: " + service.uuid);
p.addEventListener('didDiscoverCharacteristicsForService', function(e) {
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic) {
Ti.API.info('Characteristic ID:', characteristic.uuid);
// Want to subscribe to this characteristic and receive value changes from peripheral
if (characteristic.uuid == 'FFE1') {
p.setNotifyValueForCharacteristic(true, characteristic);
}
});
});
p.discoverCharacteristicsForService({
characteristics : [],
service : service
});
}
});
});
// Discover services
connectedPeripheral.discoverServices();
});
centralManager.addEventListener('willRestoreState', function(e) { Ti.API.info('willRestoreState'); Ti.API.info(e); });
centralManager.addEventListener('didFailToConnectPeripheral', function(e) { Ti.API.info('didFailToConnectPeripheral'); Ti.API.info(e); });
/**
- Peripheral Manager Events */
peripheralManager.addEventListener('didUpdateState', function(e) { Ti.API.info('didUpdateState'); Ti.API.info(e); });
peripheralManager.addEventListener('willRestoreState', function(e) { Ti.API.info('willRestoreState'); Ti.API.info(e); });
peripheralManager.addEventListener('didStartAdvertising', function(e) { Ti.API.info('didStartAdvertising'); Ti.API.info(e); });
peripheralManager.addEventListener('didAddService', function(e) { Ti.API.info('didAddService'); Ti.API.info(e); });
peripheralManager.addEventListener('didSubscribeToCharacteristic', function(e) { Ti.API.info('didSubscribeToCharacteristic'); Ti.API.info(e); });
peripheralManager.addEventListener('didUnsubscribeFromCharacteristic', function(e) { Ti.API.info('didUnsubscribeFromCharacteristic'); Ti.API.info(e); });
peripheralManager.addEventListener('didReceiveReadRequest', function(e) { Ti.API.info('didReceiveReadRequest'); Ti.API.info(e); });
peripheralManager.addEventListener('didReceiveWriteRequests', function(e) { Ti.API.info('didReceiveWriteRequests'); Ti.API.info(e); });
peripheralManager.addEventListener('readyToUpdateSubscribers', function(e) { Ti.API.info('readyToUpdateSubscribers'); Ti.API.info(e); }); peripheralManager.addEventListener('didUpdateValueForCharacteristic', _didUpdateValueForCharacteristic);
function _didUpdateValueForCharacteristic(e) { Ti.API.info('didUpdateValueForCharacteristic'); Ti.API.info(e); }
win.add(btn1); win.add(btn2); win.open();
and this is my output: -- Start application log ----------------------------------------------------- [INFO] : testBluetooth/1.0 (6.2.1.301ffa4) [INFO] : didUpdateState [INFO] : Powered On [INFO] : Scanning.... [INFO] : didDiscoverPeripheral [INFO] : HMSoft [INFO] : Stop scan... [INFO] : connecting [INFO] : Connected to Periperal... [INFO] : Service Discovery... [INFO] : peripheral has 1 service(s) [INFO] : service Found: FFE0 [INFO] : Characteristic ID: FFE1
what is wrong?
....this is that my module send:
1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:48+10:00 1999-12-01T10:52:49+10:00 1999-12-01T10:52:49+10:00 1999-12-01T10:52:49+10:00
simple timestamp....
woah, this is a lot, haha, maybe you tell me what are you trying to do and what doesn't work, so that i can pin point the problem and save some time
i have a simple project where my device (HM-10) send data to mobile (similar serial communication). I need to subscribe service and characteristic (FFE0-->FFE1) and print data. If i use app LightBlue i can read HMSoft is my peripheral FFE0 is service FFE1 is characteristic. When i put Listen for notifications i receive data from device it is possible to do this? Thanks
yes you can, so I assume you can get the characteristic now? i think the problem of your code is the way you listen to _didUpdateValueForCharacteristic.
try listen it here:
p.addEventListener('didDiscoverCharacteristicsForService', function(e) {
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic) {
Ti.API.info('Characteristic ID:', characteristic.uuid);
// Want to subscribe to this characteristic and receive value changes from peripheral
if (characteristic.uuid == 'FFE1') {
p.addEventListener('didUpdateValueForCharacteristic', _didUpdateValueForCharacteristic);
p.setNotifyValueForCharacteristic(true, characteristic);
}
});
});
This is the code: centralManager.addEventListener('didConnectPeripheral', function(e) { Ti.API.info('Connected to Periperal...'); connectedPeripheral = e.peripheral; connectedPeripheral.addEventListener('didDiscoverServices', function(e) { Ti.API.info('Service Discovery...'); peripheralManager = e.peripheral; Ti.API.info('peripheral has', peripheralManager.services.length, 'service(s)'); peripheralManager.services.forEach(function(service) { // Assign service if (service.uuid == 'FFE0') { Ti.API.info("service Found: " + service.uuid); peripheralManager.addEventListener('didDiscoverCharacteristicsForService', function(e) { var characteristics = e.service.characteristics; characteristics.forEach(function(characteristic) { Ti.API.info('Characteristic ID:', characteristic.uuid); // Want to subscribe to this characteristic and receive value changes from peripheral if (characteristic.uuid == 'FFE1') { peripheralManager.addEventListener('didUpdateValueForCharacteristic', _didUpdateValueForCharacteristic); peripheralManager.setNotifyValueForCharacteristic(true, characteristic); } }); });
peripheralManager.discoverCharacteristicsForService({
characteristics : [],
service : service
});
}
});
});
// Discover services
connectedPeripheral.discoverServices();
});
the result is the same
you should be more specific, or I can't help you
This isn't working for me.
p.addEventListener('didDiscoverCharacteristicsForService', function(e)
{
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic)
{
Ti.API.info('Characteristic ID:', characteristic.uuid);
if(characteristic.uuid == IA_STATE_UUID)
{
Ti.API.info('Alert Level Characteristic Found');
Ti.API.info(characteristic.value);
//p.setNotifyValueForCharacteristic(true, characteristic);
function _didUpdateValueForCharacteristic(e) {
Ti.API.info('didUpdateValueForCharacteristic');
Ti.API.info(e);
}
p.addEventListener('didUpdateValueForCharacteristic', _didUpdateValueForCharacteristic);
p.setNotifyValueForCharacteristic(true, characteristic);
}
});
});
output:
Connected to Periperal...
[INFO] : Service Discovery...
[INFO] : peripheral has 6 service(s)
[INFO] : Immediate Alert Service Found
[INFO] : Characteristic ID: 2A06
[INFO] : Alert Level Characteristic Found
[INFO] : [object TiBlob]
I have a keyfob type peripheral device with a button on it, pressing the button sets the alert level between off, low and high alerts. My app needs to know the current alert level. According to the documentation for the device I can write the alert level or be notified of the alert level. Unfortunately the docs dont explain how.
Using the example you've provided, pressing the button returns no response.
@FizixRichard my fault, try this:
p.addEventListener('didDiscoverCharacteristicsForService', function(e) {
var peripheral = e.peripheral;
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic) {
Ti.API.info('Characteristic ID:', characteristic.uuid);
if (characteristic.uuid == IA_STATE_UUID) {
Ti.API.info('Alert Level Characteristic Found');
Ti.API.info(characteristic.value);
//p.setNotifyValueForCharacteristic(true, characteristic);
function _didUpdateValueForCharacteristic(e) {
Ti.API.info('didUpdateValueForCharacteristic');
Ti.API.info(e);
}
peripheral.addEventListener('didUpdateValueForCharacteristic', _didUpdateValueForCharacteristic);
peripheral.setNotifyValueForCharacteristic(true, characteristic);
}
});
});
No change, its not notifying me ><
Did it go into 'Alert Level Characteristic Found' here?
Here is the code:
p.addEventListener('didDiscoverCharacteristicsForService', function(e) {
var peripheral = e.peripheral;
var characteristics = e.service.characteristics;
characteristics.forEach(function(characteristic) {
Ti.API.info('Characteristic ID:', characteristic.uuid);
if (characteristic.uuid == IA_STATE_UUID) {
Ti.API.info('Alert Level Characteristic Found');
Ti.API.info(characteristic.value);
//p.setNotifyValueForCharacteristic(true, characteristic);
function _didUpdateValueForCharacteristic(e) {
Ti.API.info('didUpdateValueForCharacteristic');
Ti.API.info(e);
}
peripheral.addEventListener('didUpdateValueForCharacteristic', _didUpdateValueForCharacteristic);
peripheral.setNotifyValueForCharacteristic(true, characteristic);
}
});
});
p.services.forEach(function(service)
{
Ti.API.info("Service Found: " + service.uuid);
if (service.uuid == IA_SERVICE_UUID)
{
Ti.API.info('Immediate Alert Service Found');
p.discoverCharacteristicsForService({characteristics : [], service: service});
}
});
Yep, same as before.
One interesting thing. It kinda works if I change the service/characteristic to the GAP Profile on service: 00001C00-D102-11E1-9B23-000EFB0000A7, characteristics 00001C0F-D102-11E1-9B23-000EFB0000A7 and 00001C01-D102-11E1-9B23-000EFB0000A7
If I attempt to listen to these then I get a response when I press the button:
didUpdateValueForCharacteristic
[INFO] : {
[INFO] : bubbles = 1;
[INFO] : cancelBubble = 0;
[INFO] : characteristic = "[object TiBluetoothCharacteristic]";
[INFO] : error = "<null>";
[INFO] : peripheral = "[object TiBluetoothPeripheral]";
[INFO] : source = "[object TiBluetoothPeripheral]";
[INFO] : type = didUpdateValueForCharacteristic;
[INFO] : }
[INFO] : didUpdateValueForCharacteristic
[INFO] : {
[INFO] : bubbles = 1;
[INFO] : cancelBubble = 0;
[INFO] : characteristic = "[object TiBluetoothCharacteristic]";
[INFO] : error = "<null>";
[INFO] : peripheral = "[object TiBluetoothPeripheral]";
[INFO] : source = "[object TiBluetoothPeripheral]";
[INFO] : type = didUpdateValueForCharacteristic;
[INFO] : }
I assume one callback for each of the two characteristics. This service is labelled as the Find Me Profile but is undocumented in their docs and I assume bespoke. The reason it only kind of works is because I don't know whether this is off, low or high alert and it only fires once, if I press the button again it does not trigger.
are you using the latest version? 1.3.0
1.2.2, I didn't notice a 1.3.0 update, I'll just update and see what happens
1.3.0 accepts multiple button presses on the proprietary service. Immediate Alert doesn't activate callbacks though.
But, I'm getting callbacks so thank you!
Two questions though:
- How would I get the new value in the callback?
- How do this for multile services though, if I try to do two services then it only fetches characteristics for the last service in the list?