1

I am trying to send data from my iPhone to bluetooth device which is connected to an Arduino. I know the bluetooth device works fine because I used the app nRF connect and sent data from there to the bluetooth which the Arduino read.

I think the way my app is organised is creating some problems. As it stands there are three view controllers.

The first view controller is a homepage screen which does not have much on it. You can connect the the second an third view controller from the homepage. The second view controller is a connection screen for the bluetooth The third view controller is an action screen with buttons

The second view controller is a table view with a scan option to look for the available bluetooth devices. i use this code:

import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, UITableViewDataSource, UITableViewDelegate
{

//MARK: Variables
    //central manager
    var manager: CBCentralManager?

    //peripheral manager
    var peripheral: CBPeripheral?

    //HM-10 service code
    let HMServiceCode = CBUUID(string: "0xFFE0")

    //HM-10 characteristic code
    let HMCharactersticCode = CBUUID(string: "0xFFE1")

    //array to store the peripherals
    var peripheralArray:[(peripheral: CBPeripheral, RSSI: Float)] = []

    //for timing..obvs
    var timer: Timer!

//MARK: IBOutlets
    //if cancel is pressed go back to homepage
    @IBAction func cancelButton(_ sender: Any)
    {
        performSegue(withIdentifier: "segueBackwards", sender: nil)
    }

    //this is for the tableview so that you can reference it
    @IBOutlet var tableView: UITableView!

    //allow for disabling the scanning button whilst scanning
    @IBOutlet var scanningButton: UIBarButtonItem!

    //loads the centralmanager delegate in here
    override func viewDidLoad()
    {
        manager = CBCentralManager(delegate: self, queue: nil)
        tableView.delegate = self
        tableView.dataSource = self
        super.viewDidLoad()
        if(peripheral != nil)
            {
                disconnectPeripheral()
            }
    }

    //nothing
    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
    }

//MARK: cancel any preexisting connection - this still needs to be done
    func disconnectPeripheral()
    {
        manager?.cancelPeripheralConnection(peripheral!)
    }

//MARK: Bluetooth central

    //required centralmanager component. Text for what power state currently is
    func centralManagerDidUpdateState(_ central: CBCentralManager)
    {
        var consoleMsg = ""
        switch (central.state)
        {
        case.poweredOff:
            consoleMsg = "BLE is Powered Off"
            scanningButton.isEnabled = false
            alert()

        case.poweredOn:
            consoleMsg = "BLE is Powered On"
            scanningButton.isEnabled = true

        case.resetting:
            consoleMsg = "BLE is resetting"

        case.unknown:
            consoleMsg = "BLE is in an unknown state"

        case.unsupported:
            consoleMsg = "This device is not supported by BLE"

        case.unauthorized:
            consoleMsg = "BLE is not authorised"
        }
        print("\(consoleMsg)")
    }

//MARK: Alert if Bluetooth is not turned on
    func alert ()
    {
        //main header
        let title = "Bluetooth Power"

        //the little debrief below the main title
        let message = "Please turn on Bluetooth to use this app"

        //text in the text box
        let text = "OK"

        //this says what the title and message is
        let alert = UIAlertController(title: title, message: message , preferredStyle: UIAlertControllerStyle.alert)

        //add button for the answer
        let okayButton = UIAlertAction(title: text, style: UIAlertActionStyle.cancel, handler: nil)
        alert.addAction(okayButton)

        //show the alert
        present(alert, animated: true, completion: nil)
        print("said ok on button to turning on bluetooth")
    }

//MARK: Connection to bluetooth
    //once scanned this will say what has been discovered - add to peripheralArray
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
    {
        for existing in peripheralArray
        {
            if existing.peripheral.identifier == peripheral.identifier {return}
        }
        //adding peripheral to the array
        let theRSSI = RSSI.floatValue 
        peripheralArray.append(peripheral: peripheral, RSSI: theRSSI)
        peripheralArray.sort { $0.RSSI < $1.RSSI }
        print("discovered peripheral")
        tableView.reloadData()
        print("There are \(peripheralArray.count) peripherals in the array")
    }

    //create a link/connection to the peripheral
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
    {
        peripheral.discoverServices(nil) //may need to remove this not sure it does much
        print("connected to peripheral")
    }

    //disconnect from the peripheral
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)
    {
        print("disconnected from peripheral")
        stopScanning()
    }

    //if it failed to connect to a peripheral will tell us (although not why)
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
    {
        print("failed to connect to peripheral")
        stopScanning()
    }

//MARK: scanning
    //press scan button to initiate scanning sequence
    @IBAction func scanButton(_ sender: Any)
    {
        startTimer()
    }

    //start scanning for 5 seconds
    func startTimer()
    {
        //after 5 seconds this goes to the stop scanning routine
        timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(ViewController.stopScanning), userInfo: nil, repeats: true)
        print("Start Scan")
        manager?.scanForPeripherals(withServices: [HMServiceCode], options: nil)
        scanningButton.isEnabled = false
    }
    //stop the scanning and re-enable the scan button so you can do it again
    func stopScanning()
    {
        timer?.invalidate()
        print("timer stopped")
        manager?.stopScan()
        print("Scan Stopped")
        print("array items are: \(peripheralArray)")
        print("peripheral items are: \(peripheral)")
        print("manager items are: \(manager)")

        scanningButton.isEnabled = true

    }

//MARK: Table View
    //number of sections the table will have
    func numberOfSections(in tableView: UITableView) -> Int
    {
        return 1
    }

    //number of rows each section of the table will have
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return peripheralArray.count
    }

    //the way the data will be displayed in each row for the sections
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let BluetoothNaming = peripheralArray[indexPath.row].peripheral.name
        cell.textLabel?.text = BluetoothNaming

        return cell
    }

    //what happens when we select an item from the bluetooth list
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    {
        //tableView.deselectRow(at: indexPath, animated: true)
        stopScanning()
        peripheral = peripheralArray[(indexPath as NSIndexPath).row].peripheral
        print ("connecting to peripheral called \(peripheral)")

        //store the name of the connected peripeheral
        let connectedPeripheral = peripheral
        manager?.connect(connectedPeripheral!, options: nil)
        performSegue(withIdentifier: "segueBackwards", sender: nil)
    }

//MARK: change label name
    override func prepare(for segue: UIStoryboardSegue, sender: Any?)
    {
        if segue.identifier == "segueBackwards"
        {
            if let indexPath = self.tableView.indexPathForSelectedRow
            {
                //storing the name of the peripheral and then saving to send to Homepage
                let peripheral = peripheralArray[indexPath.row].peripheral.name
                let vc = segue.destination as! HomepageViewController
                vc.selectedName = peripheral

                //this is to unselect the row in the table
                tableView.deselectRow(at: indexPath, animated: true)
            }
        }
    }

//MARK: end
}

All in all the second page I can connect to a bluetooth device. I then return to the the home screen. From here I then go to the third view controller which is to control actions for my app. I have it set so that you click a button and then that allows you to send data to the bluetooth. However for whatever reason it is, I am unable to send data. the button click works, but data is not sent.

Here is my code for the third view controller:

    import UIKit
    import CoreBluetooth

    class SelectionViewController: UIViewController, CBPeripheralDelegate
    {

    //MARK: Variables
        var mainPeripheral: CBPeripheral?
        let UuidSerialService = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
        let UuidTx =            "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
        let UuidRx =            "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
        let txCharacteristicUUID =  "0000ffe0-0000-1000-8000-00805f9b34fb"
        let txServiceUUID =         "0000ffe1-0000-1000-8000-00805f9b34fb"

        /// Whether to write to the HM10 with or without response.
        /// Legit HM10 modules (from JNHuaMao) require 'Write without Response',
        /// while fake modules (e.g. from Bolutek) require 'Write with Response'.
        var writeType: CBCharacteristicWriteType = .withoutResponse
        var writeCharacteristic: CBCharacteristic?

    //MARK: IBOutlets

        @IBOutlet var whiteButtonControl: UIButton!

        @IBAction func doneButton(_ sender: Any)
        {
            performSegue(withIdentifier: "segueControltoHome", sender: nil)
        }

    //MARK: preset buttons
        @IBAction func whiteButton(_ sender: Any)
        {
            let value : UInt8 = 75
            let data = Data([value])
            mainPeripheral?.writeValue(data, for: writeCharacteristic!, type: writeType)
            print("Button pressed")
        }

//MARK: Peripheral Control
    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)
    {
        var consoleMsg = ""
        switch (peripheral.state)
        {
        case.poweredOff:
            consoleMsg = "Peripheral is Powered Off"

        case.poweredOn:
            consoleMsg = "Peripheral is Powered On"

        case.resetting:
            consoleMsg = "Peripheral is resetting"

        case.unknown:
            consoleMsg = "Peripheral is in an unknown state"

        case.unsupported:
            consoleMsg = "This device is not supported by Peripheral"

        case.unauthorized:
            consoleMsg = "Peripheral is not authorised"
        }
        print("\(consoleMsg)")
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
    {
        // discover the 0xFFE1 characteristic for all services (though there should only be one)
        for service in peripheral.services!
        {
            peripheral.discoverCharacteristics([CBUUID(string: "FFE1")], for: service)
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
    {
        // check whether the characteristic we're looking for (0xFFE1) is present - just to be sure
        print("reading this part")
        for characteristic in service.characteristics!

        {
            if characteristic.uuid == CBUUID(string: "FFE1")
            {
                // subscribe to this value (so we'll get notified when there is serial data for us..)
                peripheral.setNotifyValue(true, for: characteristic)
                print ("subscribed to this value")

                // keep a reference to this characteristic so we can write to it
                writeCharacteristic = characteristic
                print("characteristic is fine")
            }
        }
    }

I think part of the problem is that the peripheral service and characteristic are not connecting to the central? Does this sound right? At the moment I am looking to send the value 75 to the bluetooth which the Arduino can then read. Have i done something wrong with the peripheral delegate? What do i need to do to make it be able to send data

thanks

Tejkaran Samra
  • 96
  • 1
  • 14

2 Answers2

0

@Larme is right. Create a singleton class to manage all the Core Bluetooth action. Then, create an instance of that singleton wherever you want.

I've created an example of a singleton class with Core Bluetooth features here, https://github.com/alextarrago/bletest.

Alex Tarragó
  • 956
  • 8
  • 16
  • Hi Alex, thanks for the githb link. I am reasonably new to Swift, what would i do with the singleton? How would i incorporate that to my work? – Tejkaran Samra Feb 03 '17 at 11:24
  • Just use the Singleton class to create all the methods and actions you want to perform on your BLE devices. Then create an instance of that singleton class on every class you need. Obviously, you need to create a protocol (or similar) to communicate between the singleton and your other controllers. – Alex Tarragó Feb 03 '17 at 11:25
  • Ok cool, I will try to do that. create the singleton with all the BLE based stuff i want to do. then create that instance in all the **view controllers** right? Will need to look up protocols, because I'm not so familiar with them. Any guidance? – Tejkaran Samra Feb 03 '17 at 11:31
  • Not in all the View Controllers, only on the ones you really need to access them. Sure, search Delegation. https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html – Alex Tarragó Feb 03 '17 at 11:33
  • Ok, brilliant, thanks Alex. Any way i can contact you if i run into trouble? – Tejkaran Samra Feb 03 '17 at 11:36
  • You can post your questions of a specific topic here at StackOverflow. I'll track them. – Alex Tarragó Feb 03 '17 at 11:38
  • Ok, thanks. I will do my best to limit the questions when and where possible – Tejkaran Samra Feb 03 '17 at 11:40
  • @TejkaranSamra Singleton is a Design Pattern, it's not proper to Objective-C, Swift or iOS, it existed in a lot a languages. You can do a search that will explain it. – Larme Feb 03 '17 at 12:34
0

Your characterictics UUID is wrong

if characteristic.uuid == CBUUID(string: "FFE1")

characteristic = FFFE0

change this -> if characteristic.uuid == CBUUID(string: "FFE0")

SYK
  • 1