Hi all,
I'm currently learning on how to build bluetooth connections with a device from an app. For that I downloaded the iOS version of the nerf Toolbox app, as my chip is also from nRF. There I want to send some String to the peripheral with the line:
bluetoothManager2!.send(text: "h\r\n" + "r\r\n")
to get the sensor values into my app. Note that I have to initiate the Firmware by h and r to send two set of values. I build that into the following code:
// NORScannerViewController.swift
//
import UIKit
import CoreBluetooth
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l > r
default:
return rhs < lhs
}
}
class NORScannerViewController: UIViewController, CBCentralManagerDelegate, UITableViewDelegate, UITableViewDataSource, NORBluetoothManagerDelegate {
let dfuServiceUUIDString = "00001530-1212-EFDE-1523-785FEABCD123"
let ANCSServiceUUIDString = "7905F431-B5CE-4E99-A40F-4B1E122D00D0"
//MARK: - ViewController Properties
var bluetoothManager : CBCentralManager?
var bluetoothManager2 : NORBluetoothManager?
var delegate : NORScannerDelegate?
var delegate2 : NORBluetoothManagerDelegate?
var filterUUID : CBUUID?
var peripherals : [NORScannedPeripheral]
var timer : Timer?
@IBOutlet weak var devicesTable: UITableView!
@IBOutlet weak var emptyView: UIView!
@IBAction func cancelButtonTapped(_ sender: AnyObject) {
self.dismiss(animated: true, completion: nil)
}
@objc func timerFire() {
if peripherals.count > 0 {
emptyView.isHidden = true
devicesTable.reloadData()
}
}
required init?(coder aDecoder: NSCoder) {
peripherals = []
super.init(coder: aDecoder)
}
func getRSSIImage(RSSI anRSSIValue: Int32) -> UIImage {
var image: UIImage
if (anRSSIValue < -90) {
image = UIImage(named: "Signal_0")!
} else if (anRSSIValue < -70) {
image = UIImage(named: "Signal_1")!
} else if (anRSSIValue < -50) {
image = UIImage(named: "Signal_2")!
} else {
image = UIImage(named: "Signal_3")!
}
return image
}
func didConnectPeripheral(deviceName aName: String?) {
}
func didDisconnectPeripheral() {
}
func peripheralReady() {
}
func peripheralNotSupported() {
}
func getConnectedPeripherals() -> [CBPeripheral] {
guard let bluetoothManager = bluetoothManager else {
return []
}
var retreivedPeripherals : [CBPeripheral]
if filterUUID == nil {
let dfuServiceUUID = CBUUID(string: dfuServiceUUIDString)
let ancsServiceUUID = CBUUID(string: ANCSServiceUUIDString)
retreivedPeripherals = bluetoothManager.retrieveConnectedPeripherals(withServices: [dfuServiceUUID, ancsServiceUUID])
} else {
retreivedPeripherals = bluetoothManager.retrieveConnectedPeripherals(withServices: [filterUUID!])
}
return retreivedPeripherals
}
/**
* Starts scanning for peripherals with rscServiceUUID.
* - parameter enable: If YES, this method will enable scanning for bridge devices, if NO it will stop scanning
* - returns: true if success, false if Bluetooth Manager is not in CBCentralManagerStatePoweredOn state.
*/
func scanForPeripherals(_ enable:Bool) -> Bool {
guard bluetoothManager?.state == .poweredOn else {
return false
}
DispatchQueue.main.async {
if enable == true {
let options: NSDictionary = NSDictionary(objects: [NSNumber(value: true as Bool)], forKeys: [CBCentralManagerScanOptionAllowDuplicatesKey as NSCopying])
if self.filterUUID != nil {
self.bluetoothManager?.scanForPeripherals(withServices: [self.filterUUID!], options: options as? [String : AnyObject])
} else {
self.bluetoothManager?.scanForPeripherals(withServices: nil, options: options as? [String : AnyObject])
}
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.timerFire), userInfo: nil, repeats: true)
} else {
self.timer?.invalidate()
self.timer = nil
self.bluetoothManager?.stopScan()
}
}
return true
}
//MARK: - ViewController Methods
override func viewDidLoad() {
super.viewDidLoad()
devicesTable.delegate = self
devicesTable.dataSource = self
let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
activityIndicatorView.hidesWhenStopped = true
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: activityIndicatorView)
activityIndicatorView.startAnimating()
let centralQueue = DispatchQueue(label: "no.nordicsemi.nRFToolBox", attributes: [])
bluetoothManager = CBCentralManager(delegate: self, queue: centralQueue)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.default, animated: true)
}
override func viewWillDisappear(_ animated: Bool) {
let success = self.scanForPeripherals(false)
if !success {
print("Bluetooth is powered off!")
}
UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.lightContent, animated: true)
super.viewWillDisappear(animated)
}
//MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return peripherals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let aCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//Update cell content
let scannedPeripheral = peripherals[indexPath.row]
aCell.textLabel?.text = scannedPeripheral.name()
if scannedPeripheral.isConnected == true {
aCell.imageView!.image = UIImage(named: "Connected")
} else {
let RSSIImage = self.getRSSIImage(RSSI: scannedPeripheral.RSSI)
aCell.imageView!.image = RSSIImage
}
return aCell
}
//MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
bluetoothManager!.stopScan()
self.dismiss(animated: true, completion: nil)
// Call delegate method
let peripheral = peripherals[indexPath.row].peripheral
self.delegate?.centralManagerDidSelectPeripheral(withManager: bluetoothManager!, andPeripheral: peripheral)
}
func centralManagerDidSelectPeripheral(withManager aManager: CBCentralManager, andPeripheral aPeripheral: CBPeripheral) {
// We may not use more than one Central Manager instance. Let's just take the one returned from Scanner View Controller
bluetoothManager2 = NORBluetoothManager(withManager: aManager)
bluetoothManager2!.delegate = self
bluetoothManager2!.connectPeripheral(peripheral: aPeripheral)
bluetoothManager2!.send(text: "h\r\n" + "r\r\n")
}
//MARK: - CBCentralManagerDelgate
func centralManagerDidUpdateState(_ central: CBCentralManager) {
guard central.state == .poweredOn else {
print("Bluetooth is porewed off")
return
}
let connectedPeripherals = self.getConnectedPeripherals()
var newScannedPeripherals: [NORScannedPeripheral] = []
connectedPeripherals.forEach { (connectedPeripheral: CBPeripheral) in
let connected = connectedPeripheral.state == .connected
let scannedPeripheral = NORScannedPeripheral(withPeripheral: connectedPeripheral, andIsConnected: connected )
newScannedPeripherals.append(scannedPeripheral)
}
peripherals = newScannedPeripherals
let success = self.scanForPeripherals(true)
if !success {
print("Bluetooth is powered off!")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// Scanner uses other queue to send events. We must edit UI in the main queue
DispatchQueue.main.async(execute: {
var sensor = NORScannedPeripheral(withPeripheral: peripheral, andRSSI: RSSI.int32Value, andIsConnected: false)
if ((self.peripherals.contains(sensor)) == false) {
self.peripherals.append(sensor)
}else{
sensor = self.peripherals[self.peripherals.index(of: sensor)!]
sensor.RSSI = RSSI.int32Value
}
})
}
}
Although, I don't get an error and the connection is done, I don't receive the sensor values, as obviously I did something wrong. Do you have any idea? To get it run I have to connect to the UART part of the nRF Toolbox app, then manually type in "h" and "r" and then the sensor data re read out.
Thank you very much!