I am designing an application which will communicate with a TI LaunchPad through a BLE Mini by utilizing CoreBluetooth, however I am struggling to transmit data between my iPhone and the Red Bear BLE Mini. Currently, I am trying to send a command which will instruct the Launchpad to turn an LED on and off. I have implemented all of the CoreBluetooth methods and confirmed that I am connected to the BLE Mini. I also have discovered this device's singular service and the five characteristics associated with this device with my iPhone application, so I know I am connected to the device. After confirming this connection, I attempted to write to the write characteristic, but I'm not sure if I wrote to this characteristic properly. Also, I'm not sure how I should be encoding the data. I have tried both ascii and utf8. Here is what I wrote:
let string = "LED-ON"
let data = string.data(using: .ascii)
if manager.write_char != nil{
print("trying")
print(manager.write_char.uuid)
peripheral.writeValue(data!, for: manager.write_char, type: CBCharacteristicWriteType.withoutResponse)
The print statement confirms that this characteristic has the uuid that I defined as being the write characteristic
let WRITE_CHAR = "713D0004-503E-4C75-BA94-3148F18D941E"
I believe this is the correct write characteristic. I have also tried 71230003. On the Launchpad side, I have uploaded the following code:
int incomingByte = 0; // for incoming serial data
void setup() {
// put your setup code here, to run once:
Serial.begin(19200);
Serial1.begin(9600);
pinMode(7, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
//if (Serial1.available()){
// Serial.print("available");
//Read in data
Serial.print(Serial1.available());
Serial.print(" ");
Serial.print(Serial1.read());
Serial.print("\n");
if (Serial1.available() > 0) {
// read the incoming byte:
incomingByte = Serial1.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
In the serial monitor, all I receive are 0 from the available check and -1 from the read method both of which signify that no data is being received. Am I transmitting data to the BLE Mini correctly? If so, am I reading it incorrectly? Additionally, is there something I'm supposed to do to tell the BLE Mini to transmit data? Sorry for the long winded question. Thank you so much for the help.
Related
I'm pretty new to BLE and I'm making an application that requires Data objects to be transferred via BLE in rapid succession. So far I've tried a for loop to change the characteristic based on the index. It successfully changes the characteristic for the first value but fails on the rest. I'm currently working on a POC which has the code which can be seen below.
guard let characteristic = self.transferCharacteristic else { return }
for x in "Hello there" {
let data = Data("\(x)".data(using: .utf8)!)
let didSend = self.peripheralManager.updateValue(data, for: characteristic, onSubscribedCentrals: nil)
if didSend {
print("Sent: \(x)")
} else {
print("Couldn't send: \(x)")
}
}
The output of this is,
Sent: H
Couldn't send: e
Couldn't send: l
Couldn't send: l
.
.
.
Couldn't send: e
Couldn't send: r
Couldn't send: e
How can I achieve what I want to?
Is there a better way?
I'm aware that transaction are a few milliseconds, but have no idea how I can sync the characteristic change with the transaction speed, IF POSSIBLE.
Thank you in advance.
If you receive false for this call, you need to store the rest of your packets and wait until peripheralManagerIsReady(toUpdateSubscribers:) is called. Then you can queue additional changes until it returns false again. You cannot just do this in a loop without dealing with back pressure.
Depending on your use case, you may find it better to throw away intermediate packets until the manager is ready, or you may queue them, or you may return an error if there are too many dropped, or many other strategies. What back pressure strategy makes sense depends on your problem.
Don't forget that it's also possible for you to lose the connection at any time. BLE is a reliable transport, so you'll never get packets out of order, or skipped or corrupted packets, but that doesn't mean the packet will be delivered.
That said, this is a very inefficient way to send lots of data. Typically you should combine your packets into chunks (up to maximumUpdateValueLength) and send them in fewer writes. There's a lot of overhead in sending a packet. Being a reliable transport comes at significant cost.
I just started to study about RxBluetoothKit as easy solution to interact with BLE devices and I have very basic knowledge of Rx programing.
As i can see from examples, every time i have to write some characteristic i have to scan + establishConnection to Peripheral + discover Services and only then write and subscribe for confirmation of this specific Characteristic.
Same happen for read Characteristic.
If I understand correctly, this way I can subscribe only to one sequence/ connection at same time.
But what i need is to subscribe to Bluetooth state and to Peripheral connection state and to notify Characteristic, in addition i have send write commands to same Peripheral sometimes.
Need help to understand how should i handle this scenario by using RXBluetoothKit library?
Links to similar approachment on GitHub are welcomed.
Thank you!
This exact case isn't covered by RxBluetooth kit, so you'll have to manage this case by yourself. Not the most ideal, but you could go with something like this:
// Get an observable to the Peripheral, then share it so
// it can be used for multiple observing chains
let connectedPeripheral: Observable<Peripheral> = peripheral
.establishConnection()
.share(replay: 1, scope: .whileConnected)
// Establish a subscription to read characteristic first
// so no notifications are lost
let readDisposable = connectedPeripheral
.flatMap { $0.observeValueAndSetNotification(for: Characteristic.read) }
.subscribe()
// Write something to the write characteristic and observe
// responses in the chain above
let writeDisposable = connectedPeripheral
.flatMap { $0.writeValue(data, for: Characteristic.write, type: .withResponse) }
.subscribe()
The example above is just a gist, but the general idea should work since I'm doing a similar thing in a project of my own. Be careful to dispose the observables when done, either by .take or disposeBags.
We need our app to receive notifications from the OS when the connects or disconnects from a bluetooth audio device (specifically, the one in their car).
The app gets notified when the BT device initially connects, but it then immediately seems to disconnect and logs the error:
BTCentralManager::DisconnectedPeripheral > SoundCore mini(ATT)
ERROR Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us."
...and the "DisconnectedPeripheral" event is never actually fired.
We're unsure how to simply receive connect and disconnect events while the app is backgrounded.
Do we need to connect the peripheral with the central manager? We only need to know if the audio device is connected or not - we don't need to interact with it in any way.
The events never call a second time from the background, after getting the disconnect peripheral. Presumably because of the error message we are receiving.
Sample code below:
public class BTCentralManager : CBCentralManagerDelegate
{
public CBCentralManager centralManager;
public static CBPeripheral peripheral;
public BTCentralManager()
{
System.Diagnostics.Debug.WriteLine("BTCentralManager::Constructor > ");
centralManager = new CBCentralManager(this, new DispatchQueue("myqueue"),
new CBCentralInitOptions { ShowPowerAlert = true, RestoreIdentifier = "myRestoreIdentifier" });
NSUuid[] arr = { new NSUuid("7e9002be-547f-42bc-8d56-209736f70aa2") }; //Sound core mini
var devices = centralmanager.retrieveperipheralswithidentifiers(arr);
peripheral = devices.firstordefault();
//is the only way to trigger the events, we need to first connect the peripheral to central manager???
centralManager.connectPeripheral(peripheral, new PeripheralConnectionOptions
{
NotifyOnConnection = true,
NotifyOnDisconnection = true,
NotifyOnNotification = true
});
}
//Always is triggered inclusive in background
public override void UpdatedState(CBCentralManager central)
{
System.Diagnostics.Debug.WriteLine("BTCentralManager::UpdatedState > " + central.State.ToString());
}
//Only is triggered when the device is first time connected ( Inclusive in background)
public override void ConnectedPeripheral(CBCentralManager central, CBPeripheral peripheral)
{
System.Diagnostics.Debug.WriteLine("BTCentralManager::ConnectedPeripheral > " + peripheral.Name);
//After the connect made successfully I receive this error, and never connect again to the device
//Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us."
}
public override void DisconnectedPeripheral(CBCentralManager central, CBPeripheral peripheral, NSError error)
{
System.Diagnostics.Debug.WriteLine("BTCentralManager::DisconnectedPeripheral > " + peripheral.Name + " ERROR>" + error.Description);
}
public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral, NSDictionary advertisementData, NSNumber RSSI)
{
System.Diagnostics.Debug.WriteLine("BTCentralManager::DiscoveredPeripheral > " + peripheral.Name);
// base.DiscoveredPeripheral(central, peripheral, advertisementData, RSSI);
}
}
as far as I understand your question, you need to observe the availability of a bluetooth audio device. I had some investigations in this a couple of time ago and the result was not really satisfying me. Here's my conclusion:
1.) CoreBluetooth is intented to be used for Bluetooth 4.0 / Bluetooth Low Energy devices only. Many Bluetooth headphones or even car radios are still not bluetooth 4.x. So, unless you can rely on your user's bluetooth audio device to be 4.x, CoreBluetooth might be a waste of time.
2.) Your app wont be notified about a audio device being connected when your app is in background.
So far no good. But, there might be some approaches that may help.
1.) By using the CLLocationManager, you may start observing e.g. the compass (not the location to save battery) to get notified whenever the phone has been moved. Simply check the connected audio devices when the app is calling the CLLocationManagerDelegate. This is of course not very efficient, but it might work.
2.) use iBeacons and CLBeaconRegions if available. Place an iBeacon in the user's car and start observing the beacon.
I know, this is not exactly what you want to hear, but I'm afraid there's no straight forward solution for your problem.
cheers,
Peter
I have an iOS app which advertises itself successfully using a CBPeripheralManager. In a Chrome App, I have successfully discovered the device (hence obtaining its address).
I have added a service with a custom UUID to the peripheral manager. didAddService is called (after the peripheral manager is powered on), and so I assume that part is successful.
I call chrome.bluetoothLowEnergy.connect(device address) in the Chrome app. The callback function is called without issue, and so I believe the connection is successful. There is no function called within the app when the connection has occurred - should there be?
I then call chrome.bluetooth.getServices(device address) in the Chrome app. An array of services is returned, however the array is empty. I believe it should return an array of length 1, not 0.
Am I overlooking something? Why are no services being returned? Should any of the peripheralManager functions be called during these operations?
Connect code:
function connect(address) {
/* Connect persistently to the device. */
chrome.bluetoothLowEnergy.connect(address, {persistent: true}, function() {
if(chrome.runtime.lastError) {
/* If we click a green device, we get this. We should try and handle it
earlier. e.g. by removing the listener, or instead using the click
to disconnect the device. */
if(chrome.runtime.lastError.message == 'Already connected')
setDeviceConnected(address);
/* Print error and exit. */
console.log(chrome.runtime.lastError.message);
return;
}
console.log('Successfully connected to ' + address);
setDeviceConnected(address);
// getDeviceService();
getDeviceServices(address);
});
return true;
}
Get services code:
function getDeviceServices(address) {
chrome.bluetoothLowEnergy.getServices(address, function(services) {
console.log('services.length: ' + services.length);
});
}
Peripheral setup code:
- (void)setupPeripheral {
_serviceName = SERVICE_NAME;
_serviceUUID = [CBUUID UUIDWithString:SERVICE_UUID_STRING];
_characteristicUUID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID_STRING];
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
_characteristic = [[CBMutableCharacteristic alloc] initWithType:_characteristicUUID
properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable];
_service = [[CBMutableService alloc] initWithType:_serviceUUID primary:YES];
_service.characteristics = #[_characteristic];
NSLog(#"Peripheral set up");
}
Start advertising (add the service just before we start advertising):
/* Start advertising */
- (void)startAdvertising {
NSLog(#"Starting advertising...");
NSDictionary *advertisment = #{
CBAdvertisementDataServiceUUIDsKey : #[self.serviceUUID],
CBAdvertisementDataLocalNameKey: self.serviceName
};
[self addServices];
[self.peripheralManager startAdvertising:advertisment];
}
Call to start advertising within didUpdateState:
/* Did update state */
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
NSLog(#"peripheralStateChange: Powered On");
// When the bluetooth has turned on, start advertising.
[self startAdvertising];
break;
Update
On connecting to the peripheral app with OSX app LightBlue, an alert shows up on the app to request a pairing. Then LightBlue can read characteristics from the app. When connecting with the Chrome app, the pairing alert does not show up.
Having chatted to web-bluetooth developers at W3C, I have now been able to discover services from the device, by updating chrome from v51 to v52 (apparantly also v53 works).
However currently (though I am yet to try it out), reading characteristics is currently unsupported for OSX, as seen here.
(I have not yet looked into it but this may provide some options on how to overcome the issues described here.)
I've ran into this problem myself. I've developed a simple app that scans for devices and their services, but all devices found always report back an empty array of services. I have this problem on both OsX and ChromeOS.
There's an exception to this: when the device has previously been paired, I was able to get the services array on ChromeOS. This is confusing to me, since nothing I've read anywhere says a device has to be manually paired first, and there's no programatic way to pair a device one discovered.
EDIT: Upgrading to Chrome 53 fixed the problem. The site linked in louisdeb's answer is related to web-bluetooth, not chrome.bluetooth, so characteristics may actually work.
I have been reading a lot of posts here on the forum and I saw quite a few relating to my case. However I still don't have the clarity that I was looking for.
I want to connect to two CBPeripherals and to write data to both of them. From what I have read, I have the idea that before connecting to a second device I have to disconnect the current peripheral. Okay, so suppose I were to write a command onto one of the peripherals and then I want to write another command to the other one, will I have to disconnect from the current peripheral? If I did disconnect to connect to the other, will the previous command still hold effect? What are the best practises for this on iOS?
my bluetooth friend, first of all it isn't necessary to disconnect current Peripheral to connect another if u want to send both messages. But many apps limit number of connected devices(CBPeripheral) to 5 - 10, because more than 5-10 connected devices, can spontaneously be lost, I know about it a little (I worked only with 4 devices). For example:
[[RKCentralManager sharedManager] scanForPeripheralsWithServices:nil options:#{CBCentralManagerScanOptionAllowDuplicatesKey:#NO} onUpdated:^(RKPeripheral *peripheral)
{
//first of all u should start a scan
[[RKCentralManager sharedManager] connectPeripheral: peripheral options:nil onFinished:^(RKPeripheral * connectedperipheral, NSError *error)
{
//after u can connect to Peripheral immediately
[connectedperipheral discoverServices:nil onFinish:^(NSError *error)
{
// services - a collection of data and associated behaviors for accomplishing a function or feature of a device
[connectedperipheral discoverCharacteristics:nil forService: [connectedperipheral.services lastObject] onFinish:^(CBService *service, NSError *error)
{
//after u should take a characteristic - Represents a service's characteristic
CBCharacteristic * characteristic = service.characteristics[0];
//and at last u can write value in characteristic in which you are going to write down something
NSData * data = [NSData dataWithHexString: newstring];
CBCharacteristicWriteType type = CBCharacteristicWriteWithoutResponse;
[connectedperipheral writeValue:data forCharacteristic:characteristic type:type onFinish:nil];
}];
}];
}];
}];
The approximate scheme of sending the message for bluetooth device, it isn't obligatory to do an investment of methods, they can be distributed on actions.
You shouldn't worry about connection and sendings data
to several devices because it is work for CBCentralManager, if U use it correctly.
CBCentralManager objects are used to manage discovered or connected remote peripheral devices (represented by CBPeripheral objects), including scanning for, discovering, and connecting to advertising peripherals.
You can connect at once some devices and send them messages, and all be ok.
If you have questions, will try to answer.
This is good example, u can see how its work : https://github.com/ruiking/ble
About max count of devices https://stackoverflow.com/a/17282862/4912496