Reading long attributed value from bluetooth characteristics - ios

public func peripheral(_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor
service: CBService, error: Error?) {
if (service.uuid.uuidString == "FFA1") {
for characteristic in service.characteristics! {
peripheral.setNotifyValue(true, for: characteristic)
if (characteristic.uuid.uuidString == "FFF3") {
peripheral.readValue(for: characteristic)
print("Found Device Name Characteristic")
print("\(characteristic)")
if let value = characteristic.value {
print("data: \(value as NSData)")
}
}
}
}
}
I am able to read only 22 bytes of data from the above code. But the peripheral device is sending 128 bytes of data. I wanted to read the 128 bytes data at once instead of reading it by chunks.

Related

Getting CBATTErrorDomain Code=6 The request is not supported error while writing value to peripheral

I created my custom peripheral to write data to peripheral and tried to write data from my central.
when write value function is executed i am getting request is not supported error.
Here is my code.Hope you understand my problem.
Looking for the solution to fix.
Thanks in advance.
connectedPeripheral?.writeValue(data, for: characteristic, type: .withResponse)
Setup up my custom BLEPeripheral and startAdvertising in Peripheral
// MARK: CBPeripheralManagerDelegate
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
setup()
} else {
print("peripheral is not available: \(peripheral.state.rawValue)")
}
}
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
if let error = error {
print("Could not add service: \(error.localizedDescription)")
} else {
print("peripheral added service. Start advertising")
let advertisementData: [String: Any] = [
CBAdvertisementDataServiceUUIDsKey: [CBUUID(string: BLEIdentifiers.serviceIdentifier)],
CBAdvertisementDataLocalNameKey: "BLE Sensor" // This key will not be transmitted when app is backgrounded
]
manager.startAdvertising(advertisementData)
}
}
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
if let error = error {
print("Could not start advertising: \(error.localizedDescription)")
} else {
print("peripheral started advertising")
}
}
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
print("Did receive read request: \(request)")
if !request.characteristic.uuid.isEqual(characteristic.uuid) {
peripheral.respond(to: request, withResult: .requestNotSupported)
} else {
guard let value = characteristic.value else {
peripheral.respond(to: request, withResult: .invalidAttributeValueLength)
return
}
if request.offset > value.count {
peripheral.respond(to: request, withResult: .invalidOffset)
} else {
request.value = value.subdata(in: request.offset..<value.count-request.offset)
peripheral.respond(to: request, withResult: .success)
}
}
}
func setup() {
let characteristicUUID = CBUUID(string: BLEIdentifiers.characteristicIdentifier)
characteristic = CBMutableCharacteristic(type: characteristicUUID, properties: [.read, .write,.notify], value: nil, permissions: [.readable,.writeable])
let descriptor = CBMutableDescriptor(type: CBUUID(string: CBUUIDCharacteristicUserDescriptionString), value: "BLESensor prototype")
characteristic.descriptors = [descriptor]
let serviceUUID = CBUUID(string: BLEIdentifiers.serviceIdentifier)
let service = CBMutableService(type: serviceUUID, primary: true)
service.characteristics = [characteristic]
manager.add(service)
}
Writing data to Peripheral in Central
func writeDataToPeripheral(data: Data){
if let characteristics = ctService?.characteristics {
for characteristic in characteristics {
if characteristic.uuid == CBUUID(string: BLEIdentifiers.characteristicIdentifier) {
if characteristic.properties.contains(.write) {
connectedPeripheral?.writeValue(data, for: characteristic, type: .withResponse)
}
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
print("Error discovering didWriteValueFor: error", error.debugDescription)
//Getting Error Domain=CBATTErrorDomain Code=6 "The request is not supported."
return
}
print("Message sent")
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let error = error {
print("peripheral failed to discover services: \(error.localizedDescription)")
} else {
peripheral.services?.forEach({ (service) in
print("service discovered: \(service)")
peripheral.discoverCharacteristics([CBUUID(string: BLEIdentifiers.characteristicIdentifier)], for: service)
})
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let error = error {
print("NSA A peripheral failed to discover characteristics: \(error.localizedDescription)")
} else {
ctService = service
service.characteristics?.forEach({ (characteristic) in
print("NSA A characteristic discovered: \(characteristic)")
if characteristic.uuid == CBUUID(string: BLEIdentifiers.characteristicIdentifier) {
// keep a reference to this characteristic so we can write to it
writeCharacteristic = characteristic
}
if characteristic.properties.contains(.read) {
peripheral.readValue(for: characteristic)
}
peripheral.discoverDescriptors(for: characteristic)
})
}
}
In order to support writes, you must implement the didReceiveWrite method in your CBPeripheralManagerDelegate.
Since you don't have this method, you get a "not supported" response to your write request.

Read and write data in BLE for LED turn on/off in swift

I am developing BLE with iOS.
I am using BLE service for LED turn on / off. I am able to read data but I am unable to send data to BLE device.
When I send 00 to BLE, LED should be turn off and when i send 01 , LED should be turn on for the BLE device.
Here is my code snippet.
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let characterArray = service.characteristics as [CBCharacteristic]! {
for cc in characterArray {
if(cc.uuid.uuidString == "FEE1") { //properties: read, write
//if you have another BLE module, you should print or look for the characteristic you need.
myCharacteristic = cc //saved it to send data in another function.
//writeValue()
peripheral.readValue(for: cc) //to read the value of the characteristic
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if (characteristic.uuid.uuidString == "FEE1") {
let readValue = characteristic.value
print(readValue as Any)
let value = (readValue! as NSData).bytes.bindMemory(to: Int.self, capacity: readValue!.count).pointee //used to read an Int value
print (value)
}
}
//if you want to send an string you can use this function.
func writeValue() {
if isMyPeripheralConected { //check if myPeripheral is connected to send data
let dataToSend: Data = "01".data(using: String.Encoding.utf8)!
print(dataToSend)
myBluetoothPeripheral.writeValue(dataToSend as Data, for: myCharacteristic, type: CBCharacteristicWriteType.withoutResponse) //Writing the data to the peripheral
} else {
print("Not connected")
}
}
How can i do it?
swift 3
var arrayReadWriteChar = [CBCharacteristic]()
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for newChar: CBCharacteristic in service.characteristics!{
if newChar.uuid.uuidString == "FEE1"{
self.arrayReadWriteChar.append(newChar)
periphreal.readValue(for: newChar)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("didUpdateValueForChar", characteristic)
if let error1 = error{
print(error1)
}
else{
let value = [UInt8](characteristic.value!)
print(value) //whole array
print(value[0]) //array object of index 0
}
}
func writeValue(){
if isMyPeripheralConected {
let dataToSend: Data = "01".data(using: String.Encoding.utf8)!
print(dataToSend)
let command:[UInt8] = [0x01]
let sendData:Data = Data(command)
myBluetoothPeripheral.writeValue(sendData, for: self.arrayReadWriteChar[0], type: .withResponse)
}
else {
print("Not connected")
}
}

iOS Bluetooth Low Energy (BLE) not discovering TX characteristic

I am using an an arduino feather BLE board and trying to create an iOS app that can send data to the board over BLE. I can connect but I can't get access to the txCharacteristic
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {
writableCharacteristic = characteristic
}
// if(characteristic.uuid == CBUUID(string: "6e400002-b5a3-f393-e0a9-e50e24dcca9e"))
// {
// txCharacteristic = characteristic
// }
var txUUID = CBUUID(string: "6e400002-b5a3-f393-e0a9-e50e24dcca9e")
let temp = characteristic.uuid.uuidString;
switch characteristic.uuid {
case txUUID:
txCharacteristic = characteristic;
break;
default:
break;
}
peripheral.setNotifyValue(true, for: characteristic)
}
}
This code works, but only discovers the following UUIDs:
temp String "00001532-1212-EFDE-1523-785FEABCD123"
temp String "00001531-1212-EFDE-1523-785FEABCD123"
temp String "00001534-1212-EFDE-1523-785FEABCD123"
I have figured out that these UUID's are DFU UUIDs. How can I discover the txCharacteristic instead?
Added for more information how I am calling discoverCharacteristics():
extension SimpleBluetoothIO: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else {
return
}
targetService = services.first
if let service = services.first {
targetService = service
peripheral.discoverCharacteristics(nil, for: service)
}
}
I was able to figure this out, by reading info at this website:
https://learn.adafruit.com/getting-started-with-the-nrf8001-bluefruit-le-breakout/adding-app-support
Particularly this part (notice that UART is the service and TX and RX are characteristics of the service):
UART Service UUID: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
TX Characteristic UUID: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E
RX Characteristic UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E
So I needed to call peripheral.discoverServices() with the UART Service ID. In my example below self.serviceUUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
//peripheral.discoverServices(nil)
//peripheral.discoverServices([CBUUID(string: "6e400001-b5a3-f393-e0a9-e50e24dcca9e")])
// Disover peripheral with service UUID that was given to us during initialization (self.serviceUUID)
peripheral.discoverServices([CBUUID(string: self.serviceUUID)])
}
Then in didDiscoverCharacteristicsFor service I needed to look for the TX Characteristic ID:
// Did Discover Characteristics
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {
writableCharacteristic = characteristic
}
// TX Characteristic UUID: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E
// https://learn.adafruit.com/getting-started-with-the-nrf8001-bluefruit-le-breakout/adding-app-support
let txUUID = CBUUID(string: "6e400002-b5a3-f393-e0a9-e50e24dcca9e")
switch characteristic.uuid {
case txUUID:
txCharacteristic = characteristic;
break;
default:
break;
}
peripheral.setNotifyValue(true, for: characteristic)
}
}

Writing data to bluetooth peripheral fails

I have a program running on a Windows PC which sends/receives data over a COM port. The data is transmitted over Bluetooth via a HM10 Bluetooth module.
I'm able to discover the peripheral, connect to it, discover its services and characteristics all successfully. However my issue is with sending data. The central is my iPhone. The PC acts as the peripheral.
First my code.
import CoreBluetooth
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var peripheralNameLabel: UILabel!
#IBOutlet weak var noOfServicesLabel: UILabel!
#IBOutlet weak var sendBytesButton: UIButton!
#IBOutlet weak var sendStringButton: UIButton!
#IBOutlet weak var responseTextView: UITextView!
private let serviceUUID = CBUUID(string: "FFE0")
private var characteristicUUID = CBUUID(string: "FFE1")
private var manager: CBCentralManager!
private var peripheral: CBPeripheral!
private var characteristic: CBCharacteristic!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
#IBAction func didTapSendBytesButton(sender: UIButton) {
let bytes: [UInt8] = [0x35, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
let data = NSData(bytes: bytes, length: bytes.count)
peripheral.writeValue(data, forCharacteristic: characteristic, type: .WithResponse)
}
#IBAction func didTapSendStringButton(sender: UIButton) {
let string = "5100000000"
if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
peripheral.writeValue(data, forCharacteristic: characteristic, type: .WithResponse)
} else {
sendStringButton.enabled = false
sendStringButton.setTitle("😟", forState: .Normal)
}
}
}
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(central: CBCentralManager) {
print(#function)
switch central.state {
case .Unsupported:
print("Unsupported")
case .Unauthorized:
print("Unauthorized")
case .PoweredOn:
print("Powered On")
navigationItem.title = "Connecting..."
central.scanForPeripheralsWithServices([serviceUUID], options: nil)
case .Resetting:
print("Resetting")
case .PoweredOff:
print("Powered Off")
case .Unknown:
print("Unknown")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print(#function)
print("Discovered \(peripheral.name) at \(RSSI)")
peripheralNameLabel.text = peripheral.name
if peripheral.name == nil || peripheral.name == "" {
return
}
if self.peripheral == nil || self.peripheral.state == .Disconnected {
self.peripheral = peripheral
central.connectPeripheral(peripheral, options: nil)
central.stopScan()
}
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print(#function)
navigationItem.title = "Connected!"
sendBytesButton.enabled = true
sendStringButton.enabled = true
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
self.peripheral = nil
central.scanForPeripheralsWithServices(nil, options: nil)
}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print(#function)
self.peripheral = nil
}
}
extension ViewController: CBPeripheralDelegate {
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print(#function)
guard let services = peripheral.services else {
return
}
noOfServicesLabel.text = "\(services.count)"
for service in services {
print(service.UUID)
if service.UUID == serviceUUID {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
print(#function)
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
print("characteristic: \(characteristic.UUID)")
if characteristic.UUID == characteristicUUID {
self.characteristic = characteristic
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
}
}
}
func peripheral(peripheral: CBPeripheral, didWriteValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print(#function)
print(error)
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print(#function)
if characteristic.UUID == characteristicUUID {
print("Got reply from: \(characteristic.UUID)")
if let data = characteristic.value, let string = String(data: data, encoding: NSUTF8StringEncoding) {
responseTextView.text = string
} else {
print("No response!")
}
}
}
}
I'm supposed to send a byte array like this,
0x35 0x31 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
to the peripheral and if the peripheral successfully receives it, I get this as a response,
0x43 0x41 0x4E 0x20 0x31 0x31 0x2F 0x35 0x30 0x30 0x0D 0x0A.
How it's supposed to look like.
This is what really happens.
Data packets are broken into pieces when transferred. Therefore I don't get the success response.
Weird part is sometimes, very rarely it actually works! The data gets sent properly (like shown in the very first image) and I receive the success response. But failures occur more often than not. Like 99% of the time.
I tried both ways, sending data as a byte array (didTapSendBytesButton()) and sending it as a string converted (didTapSendStringButton()). Both resulted in the same.
Also tested it with app called Bluetooth Serial. Same result.
I can't figure out why this is happening.
You are sending less than 20 bytes, so the Bluetooth data will be sent in a single transmission.
The problem is on your receiving side, or actually how you have structured your communications.
A serial port can only send or receive a single byte at a time, so even though iOS sends all of the bytes at once (this is a side-effect how how serial ports are emulated over GATT), Windows has to present them to the virtual COM port driver one at a time. Windows has buffering on COM ports so that bytes aren't lost if your program doesn't read fast enough, which is why you see more than one byte per "RX" but there is no concept of a "packet" in the COM driver, so it isn't aware of how many bytes were sent or that it should wait until all have been received and deliver them as one group.
The ultimate answer is that you need to modify your message so that it has some sort of delimiter (even a simple \n) that lets the receiving program know that the end of a message has been received. It can then validate the message and respond appropriately.
Or, if you can't control the receiving program, and that program works with other sending code, then I suspect you have a problem with the data you are sending, because the fact that the serial bytes are split acros multiple RX calls is normal and the receiving program must have been written to handle this

iOS Swift BLE: Discovering Multiple Characteristics within a Single Service

So I am following this iOS App code example from http://www.raywenderlich.com/85900/arduino-tutorial-integrating-bluetooth-le-ios-swift
The code below, taken from the link above, registers one characteristic with the service. How would you register multiple characteristics for one service so that they can be read and written to, using Swift?
/* Services & Characteristics UUIDs */
let BLEServiceUUID = CBUUID(string: "025A7775-49AA-42BD-BBDB-E2AE77782966")
let PositionCharUUID = CBUUID(string: "F38A2C23-BC54-40FC-BED0-60EDDA139F47")
let BLEServiceChangedStatusNotification = "kBLEServiceChangedStatusNotification"
.
.
.
.
.
func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
let uuidsForBTService: [CBUUID] = [PositionCharUUID]
if (peripheral != self.peripheral) {
// Wrong Peripheral
return
}
if (error != nil) {
return
}
if ((peripheral.services == nil) || (peripheral.services.count == 0)) {
// No Services
return
}
for service in peripheral.services {
if service.UUID == BLEServiceUUID {
peripheral.discoverCharacteristics(uuidsForBTService, forService: service as CBService)
}
}
}
func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
if (peripheral != self.peripheral) {
// Wrong Peripheral
return
}
if (error != nil) {
return
}
for characteristic in service.characteristics {
if characteristic.UUID == PositionCharUUID {
self.positionCharacteristic = (characteristic as CBCharacteristic)
peripheral.setNotifyValue(true, forCharacteristic: characteristic as CBCharacteristic)
// Send notification that Bluetooth is connected and all required characteristics are discovered
self.sendBTServiceNotificationWithIsBluetoothConnected(true)
}
}
Thanks in advance!
I think its as simple as just adding more if statements in your for loop. Like this:
let characteristicOneUUID= CBUUID(string: "025A7775-49AA-42BD-BBDB-E2AE77782966")
let characteristicTwoUUID= CBUUID(string: "F38A2C23-BC54-40FC-BED0-60EDDA139F47")
for characteristic in service.characteristics {
//Characteristic One
if characteristic.UUID == characteristicOneUUID{
peripheral.setNotifyValue(true, forCharacteristic: characteristic as CBCharacteristic)
}
//Characteristic Two
if characteristic.UUID == characteristicTwoUUID{
peripheral.setNotifyValue(true, forCharacteristic: characteristic as CBCharacteristic)
}
}

Resources