Send informations with Bluetooth in Swift IOS language - ios

I'm actually in informatics school and I have some trouble with the connection between my own self signed app coded in Swift(Xcode 11.3 and for IOS 13.2 device) and a BLE module SH-HC-08 plugged on an Arduino. (UUID : FE60E626-C58C-69F5-324F-43C7C409A93D)
I'm totally new into swift language and self-learning this way of thinking in this language.
I'm using CoreBluetooth to detect if the Bluetooth on my iPhone is On or off.
I can't actually check if my peripheral is detected, and can't send any information by my app.
(don't understand how to configure the methods in CoreBluetooth)
I downloaded apps caled LightBlue and BluetoothLE on app store. The first one give me fews information about my Bluetooth device and the second one allow me to sends Ascii Strings to my Arduino by my Bluetooth module.
LightBlue information 1:
LightBlue information 2:
BluetoothLE app:
As I said before, i already :
Import CoreBluetooth
Add CBCentralManagerDelegate & CBPeripheralDelegate to my class ViewController
instantiate CBCentralManager and CBPeripheral
setup the centralManagerDidUpdateState function
CoreBluetooth code:
import UIKit
import CoreBluetooth
var texteEnvoyer:String?
var test = false
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager:CBCentralManager!
var module : CBPeripheral!
#IBOutlet weak var labelConnection: UILabel!
#IBOutlet weak var switchConnection: UISwitch!
#IBOutlet weak var labelTextField: UITextField!
#IBOutlet weak var labelTest: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
refresh()
manager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
if(central.state == .poweredOff)
{
let alertController = UIAlertController(title: "Bluetooth", message: "Le bluetooth est déconnecté, veuillez l'activer et vous connecter au module.", preferredStyle: UIAlertController.Style.alert)
alertController.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
present(alertController, animated: true, completion: nil)
test = false
}
if(central.state == .poweredOn)
{
test = true
}
refresh()
}
I tried to play with the others methods like didDiscoverPeripheral or didConnected but I don't understand what i do and can't find any help to send information (preferred in String) by a self made application with bluetooth.
I tried that to discover my module but I don't have any "print" in my logs and don't know if my request was done or not... if I do it well or not too.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Name: \(peripheral.name)")
self.module = peripheral
self.module.delegate = self
if(peripheral.name == "SG-HC-08"){
manager.stopScan()
manager.connect(self.module, options: nil)
print(peripheral.name)
}
}
Someone have any experience with that or any help to give it will help me so much.
Thanks

Jikko,
I would be careful with using Global Variables.
Your global variables are the two declared outside of your class scope.
var texteEnvoyer:String?
var test = false
These can sometimes cause issues with your program such as memory leaks and retention. Im not saying that this is the case for you. But if you continue to develop and publish applications this may be something to rethink. Im sure for your purposes now this is totally fine.
To your point of "nothing in the console form the peripheral" we can try adding in some print statements to help us debug what is actually happening.
For starters:
func centralManagerDidUpdateState(_ central: CBCentralManager){
//.....
print("ENTERED THE FUNCTION")
if(peripheral.name == "SG-HC-08"){
manager.stopScan()
manager.connect(self.module, options: nil)
print("REACHED HERE")
}
}
IF in the console you see "REACHED HERE" then we know the code was called and executed. However this does not mean a successful connection.
If we do not see the print statement "ENTERED THE FUNCTION" , or "REACHED HERE" then that means there is a basic error. The function was never called or executed.
I would first create a function like so:
func startScan(){
if let central = manager {
central.scanForPeripheralsWithServices(nil, options: nil)
print("started Scanning")
}
}
And then in viewDidLoad()
call the start scan
override func viewDidLoad(){
super.viewDidLoad(animated)
startScan()
}
And then once you have discovered a peripheral use the didDiscoverPeripheral method (which will be called first)
func centralManager(_ central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
print("DISCOVERED PERIPHERAL", peripheral)
// manager.connectPeripheral(peripheral, options: nil)
}
You can then connect to it once discovered.
Hope this helps
EDIT ----
try adding in these two lines to your function.
peripheral.delegate = self
peripheral.discoverServices(nil)
like so
func centralManager(_ central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
print("DISCOVERED PERIPHERAL", peripheral)
// manager.connectPeripheral(peripheral, options: nil)
}
I forgot to add in the underscore "_" to the function parameters
func centralManager(_ central: ....) {}
--------------------^

Please find some informations on how delegation (1) and BLE (2) work.
1) When you instantiate your CBCentralObject manager, you pass your view controller (self) as the delegate of the central manager which means that some Bluetooth events handled by your central manager will be forwarded to your view controller.
In your case, it seems that you didn't implement the centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) method which is called every time a new peripheral is discovered.
Once you get the discovered peripheral, you can continue the process with the other delegates methods
2) If you want to communicate with a BLE device you have several steps to follow:
discover the device
connect to it
discover services and
characteristics
Then you can write data on any characteristic you want, or subscribe to listen to data emitted by your BLE device.

Related

Connecting IOS app to Raspberry Pi 4 over bluetooth

I created a new app with swiftUI and trying to use coreBluetooth library to connect Raspberry Pi 4. I searched all the google and I stuck at connecting Raspberry Pi 4. I can find all the devices (peripherals) but I'm not able to connect them.
Here is the code;
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate {
var myCentral: CBCentralManager!
#Published var isSwitchedOn = false
#Published var peripherals = [Peripheral]()
override init() {
super.init()
myCentral = CBCentralManager(delegate: self, queue: nil)
myCentral.delegate = self
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
isSwitchedOn = true
}
else {
isSwitchedOn = false
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
var peripheralName: String!
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
}
else {
peripheralName = "Unknown"
}
if peripheral.identifier.uuidString == "6D915395-3E79-0072-22A3-009DDC331F7C" {
myCentral.stopScan()
myCentral.connect(peripheral, options: nil)
print("okokokokok")
}
let newPeripheral = Peripheral(id: peripherals.count, name: peripheralName, rssi: RSSI.intValue)
print(newPeripheral)
peripherals.append(newPeripheral)
}
func centralManager(central: CBCentralManager!,didConnectPeripheral peripheral: CBPeripheral!)
{
peripheral.discoverServices(nil)
print("Connected")
print(peripheral.name!)
print(peripheral.services!)
}
}
Deleted some of code parts bc of stackoverfow
Your problem for the connection was not holding a reference to the CBPeripheral object to which you want to connect. So you've solved it by referencing to it in your BLEManager class.
Now how can you send and get data to / from your Raspberry? Well you must implement the CBPeripheralDelegate in your BLEManager class.
After the central manager finishes discovering the available services it will call didDiscoverServices method of CBPeripheralDelegate; where you need to invoke discoverCharacteristics for the discovered service.
Again, when characteristics has been discovered for the given service with the discoverCharacteristics(_:for:) method which you invoked in the didDiscoverServices callback, you can set the notify value if you want to know about characteristic updates.
After the characteristics has been discovered you can send values by using writeValue method of CBPeripheral instance to which you've referenced it when connected.
When you send data from the Raspberry for a certain charachteristic, the didUpdateValueFor method of CBPeripheralDelegate will get called. In this callback; you can obtain the received data from the CBCharacteristic.value property. Note that the value property is optional, hence you need to handle it with care.

Catch bluetooth connect event while app is in background/closed

I'm following some of code examples I found to achieve this, but it doesn't seem to work. I enabled "Uses Bluetooth LE accessories" in plist file.
#objc(StartStop) class StartStop : CDVPlugin,CBCentralManagerDelegate {
var centralManager: CBCentralManager!
public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral){
print("MLOG Did connect!!")
}
override func pluginInitialize(){
print("MLOG initialize : ")
centralManager = CBCentralManager(delegate: self, queue: nil)
}
I get events for centralManagerDidUpdateState and didDiscover, but nothing for connecting/disconnecting device. This doesn't even work with app in foreground, I haven't yet got to testing it in closed/background mode.

iOS: Is it possible to send a string to a computer via bluetooth just by giving it the MAC address

Im new to programming in swift, & I need help with working with bluetooth.
Im working on a project that involves sending a string to a computer via bluetooth, and Im able to enter the receiving device's MAC address beforehand so it has that to know where to send it.
My only problem at this stage is connecting to said device, & sending the data. I tried looking up tutorials, but they were either for Android (Which I already got working, I need one for iOS now), or they were about how to connect via service UUID (what?).
Heres the code I have so far:
import UIKit
import CoreBluetooth
class transmitter: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager:CBCentralManager!
var peripheral:CBPeripheral!
let SCRATCH_UUID = UUID.init(uuidString: "00001101-0000-1000-8000-00805F9B34FB")
let SERVICE_UUID = CBUUID(string: "00001101-0000-1000-8000-00805F9B34FB")
override func viewDidLoad() {
super.viewDidLoad()
// Define manager
manager = CBCentralManager(delegate: self, queue: nil)
print(globals.data)
// Do any additional setup after loading the view.
}
#IBOutlet weak var console: UILabel!
// Check if teh bluetooth is enabled
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == CBManagerState.poweredOn {
central.scanForPeripherals(withServices:nil, options: nil)
print (central.isScanning)
console.text = String(describing: central.retrievePeripherals(withIdentifiers: [SCRATCH_UUID!]))
} else {
//print("Bluetooth not available.")
let alert = UIAlertController(title: "Bluetooth unavalible", message: "Bluetooth is unavalibe for this device. Is it even turned on?", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
// Pair with device....
// TODO: Change to be based on MAC Address instead of name...?
private func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
let device = (advertisementData as NSDictionary).object(forKey: CBAdvertisementDataLocalNameKey) as? NSString
// console.text = peripheral.name
/*
if device?.contains(globals.macAddress) == true {
self.manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connect(peripheral, options: nil)
}
*/
}
//
// The rest is copied from a tutorial
//
// Once you are connected to a device, you can get a list of services on that device.
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
peripheral.discoverServices(nil)
}
// Once you get a list of the services offered by the device, you will want to get a list of the characteristics. You can get crazy here, or limit listing of characteristics to just a specific service. If you go crazy watch for threading issues.
private func peripheral(peripheral: CBPeripheral,didDiscoverServices error: NSError?) {
for service in peripheral.services! {
let thisService = service as CBService
if service.uuid == SERVICE_UUID {
peripheral.discoverCharacteristics(nil, for: thisService)
}
}
}
// There are different ways to approach getting data from the BLE device. One approach would be to read changes incrementally. Another approach, the approach I used in my application, would be to have the BLE device notify you whenever a characteristic value has changed.
private func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
for characteristic in service.characteristics! {
let thisCharacteristic = characteristic as CBCharacteristic
if thisCharacteristic.uuid == SERVICE_UUID {
self.peripheral.setNotifyValue(true, for: thisCharacteristic)
}
}
}
// This is an optional step, but hey, let us be good programmers and clean up after ourselves. Also a good place to start scanning all over again.
private func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
central.scanForPeripherals(withServices: nil, options: nil)
}
}
What I was trying to do at this stage was:
Check to make sure bluetooth is enabled (Works)
List devices available, as I was not able to connect via MAC address (Fails)
What would be nice though is that if I didn't have to do step 2, & just connect via the provided MAC address instead of scanning for devices, which didn't show any.
Help?
Ok, so it seems that SPP is not supported. Crap :/
Ill look into what both John Doe & Paulw11 suggested
Thanks!

didDiscoverPeripheral called very slowly

I have a question regarding CoreBluetooth programming in Mac OS X. I have some BLE beacons here that I want to detect. When I execute similar code on my iPhone, it seems to work just fine, but when I try this on my Mac, it seems to only notify of beacons in the neighbourhood every two seconds or so (or even more, but at least 2 seconds). The beacons are beaconing every 500ms, so I would expect to be notified every 500ms.
Is there someone out there who knows how I can tell Mac OS to be a little stricter with reporting the beacons?
I must say I'm quite new to Swift, so all comments on style will also be appreciated.
import Foundation
import CoreBluetooth
class CentralManagerDelegate : NSObject, CBCentralManagerDelegate {
func centralManagerDidUpdateState(central: CBCentralManager) {
if central.state == .PoweredOn {
central.scanForPeripheralsWithServices(nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey : "YES"])
print("Started scanning")
}
else {
central.stopScan()
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Beacon found with RSSI: " + RSSI.stringValue);
}
}
var _centralManagerDelegate = CentralManagerDelegate()
var _centralManager = CBCentralManager(delegate: _centralManagerDelegate, queue: dispatch_get_main_queue())
while true {
dispatch_main()
}

iOS Bluetooth delegate connect function not being called

I'm trying to listen to all Bluetooth connect/disconnect events. Even though the delegate's centralManagerDidUpdateState function is called, nothing happens when I connect or disconnect Bluetooth devices.
I'm confused as to what's going wrong. I initialize the Central Manager/delegate like this:
var btDelegate: CBCentralManagerDelegate = BluetoothDelegate()
var btManager = CBCentralManager(delegate: btDelegate, queue: nil)
BluetoothDelegate is defined like so:
import Foundation
import CoreBluetooth
class BluetoothDelegate : NSObject, CBCentralManagerDelegate {
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
println("connect") //this line is not called
}
func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
println("disconnect") //this line is not called
}
func centralManagerDidUpdateState(central: CBCentralManager!) {
println("state update") //this line is called
}
}
Note: I can continuously receive more state update events such as when I toggle Bluetooth, even though connect and disconnect are not called.
From your code it looks like you haven't started scanning for peripherals. Once you have confirmed that your central is in powered on state from centralManagerDidUpdateState method you should start scanning for your peripherals.
(The bluetooth devices you connected from bluetooth settings panel are irrelevant. You can't have access to them. (as far as I know) Incase you want to scan and find out your device on your own below code will help)
func centralManagerDidUpdateState(central: CBCentralManager!) {
switch (central.state) {
case CBCentralManagerState.PoweredOff:
break
case CBCentralManagerState.PoweredOn:
startScan() // start scanning once the bluetooth is On
break
default:
break
}
}
And your startScan method will be (You can provide service UUID, use nil if you want all )
func startScan(){
if let central = btManager {
central.scanForPeripheralsWithServices(nil, options: nil)
println("started Scanning")
}
}
After that whenever you discover a peripheral didDiscoverPeripheral method will be called first.
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
println(peripheral)
// btManager.connectPeripheral(peripheral, options: nil)
}
From there you collect the peripheral and then invoke connectPeripheral method of CBCentralManager. If the connection is successful didConnectPeripheral method will be called.
You should go through this documentation for complete details

Resources