BTService and BTDiscovery are separate classes because the represent objects with different purposes. For example:
BTDiscovery lives throughout the lifetime of the app. It is a CBCentralManagerDelegate which catches delegate callbacks like when a new peripheral has been discovered or connect or disconnected, etc.
-BTService is an instance (created by BTDiscovery) of a connected peripheral and only lives upon a live connection to specific peripheral and dies when disconnected. Technically you could have several instances of BTService. It is a CBPeripheralDelegate which letās BTService discover the Services and Characteristics of a connected peripheral. It also can receive callbacks when values change in GATT Characteristics and can write new values.
So this leads to your question of how to get a list of Bluetooth devices. Since BTDiscovery knows when new devices are discovered, have it send out a notification and let your tableview delegate update its local array and then call tableview.reloadData(). The cellForRowAt will grab up the new device and display to the user.
Comments to consider:
Inside your cellForRowAt and didSelectRowAt, you donāt need to cast indexPath to NSIndexPath. Its class type is IndexPath which already has the row property.
i.e.: label?.text = peripheral[indexPath.row].peripheral.name
Inside your cellForRowAt, I donāt recommend referencing a UILabel based on its tag in this situation. Either use custom cells and attach IBOutlet connections to storyboard,
OR
Use one of the non-custom cell options in the storyboard like Right Detail. Then you can reference the labels like this: cell.textLabel?.text = āsome nameā cell.detailTextLabel?.text = āsome other infoā
You need to consider how you are going to handle when BLE devices come in and out of range, or are turned on and shut back off. Also your tableview needs to update itself to give current status of connections (connected/disconnected).
Iām not trying to push my book on you, but it covers how to handle everything you are wanting to do plus more. It builds a complete working Bluetooth app from start to finish. The app is on the app store called Pad To PIC, if you want to take a peak at the functionality taught in the book. The book, Integrating iOS Bļ»æluetooth LE with PIC18 Miļ»æcrocontroļ»ællers, is available from my website at www.back-40.com. It is based on Objective C, BUTā¦ I send the book source code in the latest Swift 3 version upon request from buyers.
And lastly, as you are finding out, BLE development can have a steep learning curve at times. If you feel that you need professional help in this area, Iām a full time freelancer who develops projects like this on a daily basis. Feel free to contact me
Hello Owen,
great tutorial.
I made your app run with a iPhone 6, HM-10 Modul, Arduino and s servo.
The next step was to sent measured values as a string from Arduino to iPhone.
(My idea is to use my iPhone as
a bluetooth radiocontrol for modell car/ship with short range telemetry or
simple display measured values from Arduino)
So I added ādidUpdateValueForcharacteristicā to your BTService.swift. Yes it works, but
please see the comment in my image.
I am trying similar to receive data from Arduino to iPhone via BLE, below are code snippets based originally on the helpful tutorial. I am struggling with (among other things) where code should be located to point/connect a class to another class / ViewController. Any help would be GREATLY appreciated. I am new to Swift.
Xcode 8.2.1, Arduino Uno R3, Black Widow shield V1.1
ARDUINO SEND DATA
void loop() // Continuous loop
{
// See if new data is available
if(BLE_Shield.available()) {
BLE_Shield.write(āTestā); // send message
}
}
ā¦
LABEL
@IBOutlet weak var labelvalue: UILabel!
ā¦
IDENTIFY RX
let frequencyCharUUID = CBUUID(string: āA9CD2F86-8661-4EB1-B132-367A3434BC90ā)
ā¦
BLUETOOTH SERVICES
class BTService: NSObject, CBPeripheralDelegate {
var peripheral: CBPeripheral?
var frequencyCharacteristic: CBCharacteristic?
COMPILE ERROR! Value of type āBTServiceā has no member ālabelvalueā
I also tried sending RXstring to a function outside the BTService class to update the label but get the error message āInstance member ālabel valueā cannot be used on type āViewControllerāā
The Arduino code you posted will only transmit if it first receives something. Is this what you want? Maybe so for testingā¦
BTService isnāt a UIViewController class. Therefore it doesnāt have a UILabel object (and shouldnāt). So using āself.labelvalue.text = ā¦ā isnāt going to work as you have seen.
Soā¦ you want your UI to show the data received in the didUpdateValueFor function. You can do that a few different ways, but for this case Iād recommend using NSNotifications. The didUpdateValueFor function would post the notification and your UIViewController (which has the UILabel) would register to listen for the notification. And be sure to move the DispatchQueue.main.async code to the notification selector function to insure the UILabel text field is written to on the main thread.
But, how can we connect and get/send data for multiple peripherals that are the same?
For example, Iāve made my own heart rate monitor(s) with the Arduino and created a Swift class for the Bluetooth that sends the data as a delegate so I can simplify adding code to the ViewController. Works great (and works with the Polar Bluetooth HRM).
Basically I add the delegate and create an instanceā¦
class ViewController: UIViewController, BluetoothHRMDelegate {
var hrm = BluetoothHRM( )
Add the delegate function(s)ā¦
func bluetoothHRMHeartRate(_ value: String) {
lblHeartRate.text = value
}
Iād like to monitor the heart rate data from 3 peripherals to iOS. (3 Arduinos attached to 3 difference people) and not sure how to do this. Any suggestions on how to implement this as all the peripherals are identical?
Can I create an array of hrmPeripheral?
Can the centralManager keep track of multiple peripherals?
Can I create a hrm array in the ViewController (or hrm1, hrm2, hrm3) and somehow distinguish between them in the delegate call back - by adding some type of ID in the bluetoothHRMHeartRate call back or adding making my own ID and sending that with the init call of the BluetoothHRM (but what if central drops and connection and reconnects)?
Hi bleckett,
Even though all 3 devices support the same BLE Services & Characteristics, they will still have a unique peripheral identifier when discovered by Core Bluetooth. The identifier property of CBPeripheral will be the same for the same device regardless of the number of times connected/disconnected or timespan between connections.
Yes, you can create an array of hrmPeripheral. I recommend having the array as a property of the shared BTDiscovery instance since it needs to initialize an instance of BTService (CBPeripheralDelegate) for each device connected toā¦ and remove the device from the array when disconnected.
Yes, the centralManager supports multiple connections. But you need to keep track of which one transmitted new data by examining the identifier property.
Like I mentioned above, just use the built-in peripheral.identifier property. I donāt recommend creating your own.
Hiā¦ im using this code but I have a little problemā¦ I need to terminate the connection to mi BLE Peripheral and I canāt do it, I leave the app and the BLE keeps connected, I use a segue to go to another scene that automatically connect to star using your code.
Your code calls centralManager?.cancelPeripheralConnection(peripheral)
User manually turns off Bluetooth.
If you want to allow user to disconnect, then add a connect/disconnect button that calls centralManager?.connect(peripheral, options: nil) OR centralManager?.cancelPeripheralConnection(peripheral) based on the current connection status.
In order to communicate with the Arduino it uses a minimum of 2 pins for TX/RX. There are 2 additional pins that can be consumed (optionally) if the OPT_RESET & OPT_CONNECT lines are used. I recommend downloading the Black Widow datasheet for exact pin locations from my Downloads page. Once ordered, the Black Widow comes with a full schematic of the PCB.
I need to create a Class reference to the BTDiscovery? Or I can do it directly from my Swift class? Im relatively new to this languageā¦
Or maybe can I create a function in BTService with centralManager?.cancelPeripheralConnection(peripheral)
on it and the call it from my scene UIButton?
This should be no problem to reserve these 2-4 pins for the shield; my only concern is that the shield will physically block some of the Mega ports on the side of the board.
I need to do this with my Raspberry Pi Zero W. Can you help me please to understand, what and where I have to change in order to use your tutorial in my project with Raspberry Pi Zero W?
The goal is the same, I need to connect my iOS (Swift 3) application to my Raspberry Pi Zero W via BLE protocol and interacting with.
Since BTDiscovery needs to be alive the lifetime of the app, its setup as a singleton.
To cancel connection Iād do something similar to this inside BTDiscovery:
public func disconnectPeripheral(peripheral: CBPeripheral?) {
if let peripheral = peripheral {
// Put device on ignore array so autoconnect doesnāt kick in again.
ignoreAutoConnectDevices.append(peripheral)
centralManager?.cancelPeripheralConnection(peripheral)
}
}
Yeah from the images it looks like some ports will be covered.
You will either need to extend the shield up or make connections from the bottom of the Mega (i.e. desolder/resolder headers or similar).
If you plan to connect to the other back ports with another shield type, then maybe just plug the Black Widow on top of the other shield.
Iām currently trying to only receive data being sent over BLE.Print from my Arduino. So, I should be looking for the RX characteristic.
I can see the data being transmitted in the UART window on the Bluefruit LE App. I already can connect to the device in my own application. I got the UUIDs I need from the Nordic Semiconductor App.
I donāt know how to see the UART RX data on the iPhone app.