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.
Related
I am using an iPhone as my central. Is it possible to use CoreBluetooth to connect to a Google Home, Amazon Echo, or really any non Bluetooth LE devices as a peripheral in order to read said peripheral's RSSI? I do not need to connect to access any services; I'm doing a research project, and simply need to be able to use an iPhone as a sensor for RSSI values, so to speak. Right now I have this code, which scans for and discovers peripherals that are advertising indiscriminately.
//
// BLEManager.swift
// RSSIappSwiftUI
//
// Created by bee-mcc on 3/30/21.
//
import Foundation
import CoreBluetooth
//will hold the information needed about Our Peripherals
struct Peripheral: Identifiable {
let id: Int
let name: String
let rssi: Int
}
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate{
//the object that represents the Bluetooth controls of the iphone
var myCentral: CBCentralManager!
#Published var peripherals = [Peripheral]()
//var letting us know if myCentral has bluetooth switched on
#Published var isSwitchedOn = false
override init() {
super.init()
myCentral = CBCentralManager(delegate: self, queue: nil)
myCentral.delegate = self
}
//this is called whenever the central manager updates its state
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
isSwitchedOn = true
}
else {
isSwitchedOn = false
}
}
//this is called any time a central manager discovers any peripherals
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"
}
let newPeripheral = Peripheral(id: peripherals.count, name: peripheralName, rssi: RSSI.intValue)
print(newPeripheral)
peripherals.append(newPeripheral)
}
//buttons to start and stop scanning
func startScanning() {
print("startScanning")
myCentral.scanForPeripherals(withServices: nil, options: nil)
}
func stopScanning() {
print("stopScanning")
myCentral.stopScan()
}
}
I would like to somehow be able to connect to a bluetooth device by specifying it's MAC address in the code somewhere, and then to collect the device with that MAC address's RSSI with regards to the iPhone.
Thanks in advance!
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.
I am writing an app that allows me to send an image through BLE to the app of someone else.
In order to test it out properly, I would need to be able to connect to an device, but my scanned peripherals are always disconnected and have nil as value. I am uncertain as to whether I am doing this right. I have been reading through guides and I am following the right procedure so could someone point out to me what I might be doing wrong? What are the peripherals I'm detecting?
output:
<CBPeripheral: 0x165b9c90, identifier = 6B74A074-6F5B-0E3A-94EB-6E7BB890569C, name = (null), state = disconnected>
<CBPeripheral: 0x165a0940, identifier = A35AC32E-5668-BD0D-4DBC-D4BF959B9242, name = (null), state = disconnected>
<CBPeripheral: 0x166a8220, identifier = 4D9FA1A1-0090-465F-B53D-363B0F3BBD27, name = (null), state = disconnected>
Edit: Added more code
Here is my code
import Foundation
import CoreBluetooth
import UIKit
class BluetoothController: UITableViewController, CBCentralManagerDelegate, CBPeripheralManagerDelegate, CBPeripheralDelegate {
var centralManager: CBCentralManager!
var peripheralManager: CBPeripheralManager!
var service : CBMutableService!
let uuid:CBUUID = CBUUID(string: "09d921ff-b80c-47b1-bc2b-5bbaadf62010")
let charaUuid:CBUUID = CBUUID(string: "09d921ff-b80c-47b1-bc2b-5bbaadf62021")
override func viewDidLoad() {
print("Initialzing managers")
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
centralManager = CBCentralManager(delegate: self, queue: nil)
service = CBMutableService(type: uuid, primary: true) //<--Probably not causing it but without a service...
let properties: CBCharacteristicProperties = [.notify, .read, .write]
let permissions: CBAttributePermissions = [.readable, .writeable]
let characteristic = CBMutableCharacteristic(
type: charaUuid,
properties: properties,
value: nil,
permissions: permissions)
service.characteristics = [characteristic];
peripheralManager.add(service)
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
print("peripheral state updated")
print("\(peripheral.description)")
}
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?)
{
print("started advertising")
}
// Check if the bluetooth is powered on
// Start looking for devices
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("Scanning for devices")
centralManager.scanForPeripherals(withServices: nil, options: nil)
startAdvert()
} else {
print("Bluetooth not available.")
}
}
func startAdvert(){
let advertisingData = [CBAdvertisementDataLocalNameKey:"Test Device", CBAdvertisementDataServiceUUIDsKey: uuid] as [String : Any]
peripheralManager.startAdvertising(advertisingData)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
print(peripheral)
//didReadPeripheral(peripheral, rssi: RSSI)
}
}
As Paulw11 notes, you're not connected because you never call connect(). You're discovering random BLE devices around you that have nothing to do with your app. That's because you're scanning for all possible services:
centralManager.scanForPeripherals(withServices: nil, options: nil)
You should almost never do that. It's bad for the battery and it's rarely what you want. You want to scan for your service.
centralManager.scanForPeripherals(withServices: [uuid], options: nil)
And then when you discover other devices advertising your service, then you need to connect to them.
To #WholeCheese's comment, the details of the question didn't really relate to the nil name, but to address the question in the title, you will not see a name from advertising if the devices does not advertise a local name over BLE. This is quite common. There is not a lot of space in the advertising packet for data (roughly 30 bytes), and local names can be quite large.
If you're building a scanning app and want to display those names, you'll need to connect to the device. At that point, Core Bluetooth will read the GAP name (rather than the advertised Local Name). In most cases the GAP names should be there (it's not promised from all devices, but it generally should be there). Core Bluetooth caches a lot of information, so if you've ever connected to the device before, it is possible that peripheral.name will be filled out without connecting even if it's not advertised. But if it isn't, you'll need to connect.
Working on iBecon signal using Core Bluetooth i am able to search with CBCentralManager scan optionn nil :-
Shared.sharedInstance.centralManager?.scanForPeripherals(withServices: nil, options:[CBCentralManagerScanOptionAllowDuplicatesKey:true])
But when i provide my desirable service ID i.e :-
Shared.sharedInstance.centralManager?.scanForPeripherals(withServices: [serviceID], options:[CBCentralManagerScanOptionAllowDuplicatesKey:true])
it never calls didDiscoverPeripheral Delegate method, I need to scan the peripheral in background mode too and according to apple documentation you need to provide service id explicitly whenever you need to scan in background mode. Anyone can help what i am doing wrong here.
I am using like this,
connect on button click event
and use CBCentralManagerDelegate, CBPeripheralDelegate Delegate
func connectDevice(sender:UIButton){
if peripheral != nil {
manager.cancelPeripheralConnection(peripheral)
manager = CBCentralManager(delegate: self, queue: nil)
}
}
func centralManagerDidUpdateState(central: CBCentralManager) {
if central.state == CBCentralManagerState.PoweredOn {
central.scanForPeripheralsWithServices(nil, options: nil)
} else {
self.showAlert(Messages().alert , message: "Bluetooth is not on.")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
let device = (advertisementData as NSDictionary).objectForKey(CBAdvertisementDataLocalNameKey) as? NSString
print(device)
if device?.containsString(BEAN_NAME) == true {
self.manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connectPeripheral(peripheral, options: nil)
}
}
I am quite new to iOS programming and also to bluetooth protocol.
I have found a sample code written in swift and trying to modify it to work with my own bluetooth module. The module I have is DBM01 from dorji.
The service I need to use is FFF0 and the characteristics is FFF1 for sending a ASCII value.
When I use the LightBlue app on my macbook and connect to the board I have designed, which has the DBM01 module on it, I can send the char value of "1" and I get the expected response (Turning on a LED) and when I send value of "0" it turns the LED off.
Now with the code I have, I can connect to the DBM01 module. I can print its name. However, I cannot disconnect from it with the following function. I am also not sure if this is for disconnecting from the device or it is called automatically when the device is disconnected. Regardless, it does not work either way.
func centralManager(central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: NSError?)
My main problem is I really didn't understand how I specify the service and the characteristics that I am interested in and connect to specific device that has them.
I am also unable to send a message. When I try I got the predefined error, since the following condition did't hold
if writeCharacteristic != nil
Below is my full code.
Appreciate if you can point out where I am doing wrong and how I can achieve connecting to specific device with specific service and characteristics information and sending data.
//
// ViewController.swift
// bleSwift
//
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralManager: CBCentralManager!
var peripheral: CBPeripheral!
var writeCharacteristic: CBCharacteristic!
var service: CBService!
var characteristic: CBCharacteristic!
var bluetoothAvailable = false
let message = "1"
#IBOutlet weak var deviceName: UILabel!
#IBOutlet weak var ServiceName: UILabel!
#IBOutlet weak var CharacteristicsName: UILabel!
func centralManagerDidUpdateState(central: CBCentralManager)
{
print("Checking state")
switch (central.state)
{
case .PoweredOff:
print("CoreBluetooth BLE hardware is powered off")
case .PoweredOn:
print("CoreBluetooth BLE hardware is powered on and ready")
bluetoothAvailable = true;
case .Resetting:
print("CoreBluetooth BLE hardware is resetting")
case .Unauthorized:
print("CoreBluetooth BLE state is unauthorized")
case .Unknown:
print("CoreBluetooth BLE state is unknown");
case .Unsupported:
print("CoreBluetooth BLE hardware is unsupported on this platform");
}
if bluetoothAvailable == true
{
discoverDevices()
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber)
{
// Stop scanning
self.centralManager.stopScan()
print("Stopped Scanning")
// Set as the peripheral to use and establish connection
//self.peripheral = peripheral
//self.peripheral.delegate = self
//self.centralManager.connectPeripheral(peripheral, options: nil)
peripheral.discoverServices([CBUUID(string: "FFF0")])
print("CONNECTED!!")
print(peripheral.name)
deviceName.text = peripheral.name
}
func discoverDevices() {
print("Discovering devices")
centralManager.scanForPeripheralsWithServices(nil, options: nil)
}
#IBAction func disconnectDevice(sender: AnyObject) {
func centralManager(central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: NSError?)
{
print("CONNECTION WAS DISCONNECTED")
deviceName.text = "Disconnected"
}
}
#IBAction func Scan(sender: AnyObject)
{
print("Scan")
centralManager = CBCentralManager(delegate: self, queue: nil)
}
#IBAction func Send(sender: AnyObject)
{
let data = message.dataUsingEncoding(NSUTF8StringEncoding)
if writeCharacteristic != nil
{
print("Sent")
peripheral!.writeValue(data!, forCharacteristic: writeCharacteristic, type: CBCharacteristicWriteType.WithoutResponse)
}
else
{
print("Couldn't Send")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In order to send data to your ble peripheral, you should:
Start scanning.
In your ble central manager delegate you'll recieve didDiscoverPeripheral callback with discovered peripheral. Set yourself as its delegate and connect to it: centralManager.connectPeripheral(...)
Receive didConnectPeripheral in delegate. Now call discoverServices for this peripheral.
Recieve didDiscoverServices in your delegate. Finaly, call discoverCharacteristics for your service.
You'll recieve characteristic in didDiscoverCharacteristic delegate method. After that, you'll be able to send data to exact characteristic of your peripheral.
To disconnect peripheral, call method centralManager.cancelPeripheralConnection(...)