Write to BLE Receiver in iOS Swift - ios

I'm new to iOS and BLE. I've been following a tutorial to connect and send data to a BLE receiver board on an Arduino. The tutorial was designed for a different board, and I'm using the (the BLE board is the Adafruit nrf8001 if that might help).
The following code sets up the UUIDs...
let BLEServiceUUID = CBUUID(string: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
let PositionCharUUID = CBUUID(string: "6E400002-B5A3-F393-E0A9-E50E24DCCA9E")
let BLEServiceChangedStatusNotification = "kBLEServiceChangedStatusNotification"
And this code connects my iOS device to the BLE board.
central.connectPeripheral(peripheral, options: nil)
I then have a slider set up in my ViewController, and I want to send the value of that slider to the BLE board. Here's my writePosition function:
func writePosition(position: UInt8)->NSData {
let currentValue = NSUserDefaults.standardUserDefaults().floatForKey("sliderValue")
var currentValueString=NSString(format: "%.5f", currentValue)
var writeType:CBCharacteristicWriteType
let newString:NSString = currentValueString
let data = NSData(bytes: newString.UTF8String, length: newString.length)
peripheral?.writeValue(data, forCharacteristic: self.positionCharacteristic, type: writeType)
return data
}
Note that both my arduino AND my app confirm that they are indeed connected. However, if I change the slider value in the app, it does not send its value to the BLE board. What am I doing wrong??
Thanks in advance!

Please note that writing data to a BLE can not be done (typically), faster than 35 to 50 ms. It's a good idea to use a timer (which in iOS has a minimum tick between 50 to 100 ms) to write to the charactestistic, instead of writing directly from your slider. So the timer polls the slider's value and writes it. More advanced apps should use a tx queue.
Also, you may want to take a look to this answer: SWIFT - BLE communications

Related

canSendWriteWithoutResponse always returns false after some time

I am creating an app which connects with BLE device and continuously writes on one characteristic(withoutResponse write), in one session there can be around 150 write on an interval of 2-4 seconds, and after each session we are manually disconnecting the BLE device and before start of each session BLE device is reconnected, now when writing characteristics in second session and sessions after that, 'canSendWriteWithoutResponse' always returns false, also when i try to write to characteristic anyways it is not writing(ATT Send packet is not send) nor am I getting any error message. [I am using Packet Logger to keep an eye on Bluetooth packets].
If we disconnect to BLE device and reconnect it multiple times then it resolves the issue and it starts sending the packets again.
Software - iOS 15.2 |
Hardware - iPad 8th Gen |
BLE Board - Cypress CYBT-343026 EZ-BT WICED Bluetooth Module |
Xcode - 13.1 |
CODE -
I am only writing to the characteristic again after pervious write is successful, BLE device send an ack when it receives the write. So no race condition and stuff. Also I cannot write again unless previous write is successful, it is a business requirement. Also none of the packets are being lost[I am using Packet logger as well and logging the BLE device logs using serial connection.]
func sendBytesToDevice(_ bytes: [UInt8], characteristic: CBCharacteristic) {
guard isReady else { return }
if connectedPeripheral!.canSendWriteWithoutResponse{
waiting = false
print("Ready-",characteristic.uuid)
let data = Data(bytes: UnsafePointer<UInt8>(bytes), count: bytes.count)
connectedPeripheral!.writeValue(data, for: characteristic, type: .withoutResponse)
}else{
print("Not Ready-",characteristic.uuid)
waiting = true
sendBytes = bytes
sendCharacteristic = characteristic
}
}
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
print("Peripheral is ready now.")
if waiting{
waiting = false
let data = Data(bytes: UnsafePointer<UInt8>(sendBytes!), count: sendBytes!.count)
connectedPeripheral!.writeValue(data, for: sendCharacteristic!, type: .withoutResponse)
}
}

CoreBluetooth Peripheral takes a long time to write value to Characteristic

I am writing an app that needs to write a byte value to a CBPeripheral using iOS CoreBluetooth. I am able to read values, and the write is successful and triggers the didWriteValueFor delegate like it should. Basically, it does work, BUT, it takes a long time (about 3 seconds and reports of over 10). When I test the write process in another app (nRF Connect), the write is almost instantaneous. Here's the code I am using to write to the Characteristic:
func setConfigurationValue(_ i:UInt8){
guard peripheral != nil, configurationStateCharacteristic != nil else { return }
var vint:UInt8 = i
let intData = Data(bytes: &vint, count: MemoryLayout<UInt8>.size)
isWriting = true
peripheral?.writeValue(intData, for: configurationStateCharacteristic!, type: CBCharacteristicWriteType.withResponse)
// Notify any views that they should show a progress overlay
NotificationName.showProgressOverlay.post(userInfo: ["message":"Device configuring".localized], object: self)
}
Note, that peripheral is a stored reference to the CBPeripheral object, and configurationStateCharacteristic is a reference to the CBCharacteristic I am writing to. This code does work, but what is causing the peripheral BLE device to take so long in writing the data and sending a response?
Any ideas?

Why doesn't my iOS (Swift) app properly recognize some external display devices?

So I have an odd issue and my google-fu utterly fails to even provide me the basis of where to start investigating, so even useful keywords to search on may be of use.
I have an iOS application written in swift. I have a model hooked up to receive notifications about external displays. On some adaptors, I'm able to properly detect and respond to the presence of an external display and programatically switch it out to be something other than a mirror (see code block below). But with another adaptor, instead of just 'magically' becoming a second screen, I'm asked to 'trust' the external device, and it simply mirrors the device screen. Not the intended design at all.
func addSecondScreen(screen: UIScreen){
self.externalWindow = UIWindow.init(frame: screen.bounds)
self.externalWindow!.screen = screen
self.externalWindow!.rootViewController = self.externalVC
self.externalWindow!.isHidden = false;
}
#objc func handleScreenDidConnectNotification( _ notification: NSNotification){
let newScreen = notification.object as! UIScreen
if(self.externalWindow == nil){
addSecondScreen(screen: newScreen)
}
}
#objc func handleScreenDidDisconnectNotification( _ notification: NSNotification){
if let externalWindow = self.externalWindow{
externalWindow.isHidden = true
self.externalWindow = nil
}
}
The worst issue here is that because I'm connecting to an external display to do this, I can't even run this code through the debugger to find out what is going on. I don't know where to even begin.
Any ideas?
Edit:
Thanks to someone pointing out wifi debugging, I can tell you my notifications are firing off, but they're both firing at the same time, one after the other, when the external adaptor is disconnected.

Read and handle input data when received

I am working on arduino project which involves it sending/receiving data from the iPhone to to an arduino through a BLE board. I am struggling with receiving data from the arduino back to the iPhone.
I have this function:
func readPosition() -> NSString? {
if self.positionCharacteristic == nil {
return nil
}
self.peripheral?.readValue(for: self.positionCharacteristic!)
if ((self.positionCharacteristic?.value) != nil) {
return NSString(data: self.positionCharacteristic!.value!, encoding:
String.Encoding.utf8.rawValue) }
return nil
}
My problem with it is that I don't understand how to use it in a way that it will immediately read and use what was sent from the arduino. How would I code my project to accomplish this? I need to receive constant data from a sensor that is attached to the arduino
The receiving UUID if needed:
let PositionCharUUID = CBUUID(string: "A9CD2F86-8661-4EB1-B132-367A3434BC90")
It is likely (but you need to check) that your characteristic supports Notify operations. In this case you can use
self.peripheral.setNotifyValue(true, for: self.positionCharacteristic)
You will then get a call to your peripheral delegate's didUpdateValue:for: method

How to check if iPhone is in Airplane Mode with Swift

I'd like to check whether flight mode is activated. If so, I need to show a warning message.
How can I check whether flight (airplane) mode is active using Swift?
The easiest way is to let iOS done it for you.
to go Info.plist -> add this **Application uses Wi-Fi (Boolean) YES*
To test: Kill your app -> turn on airplane mode -> open ur app: you should be able to see alert within the app. Tested on iPhone iOS9
As far as I know you can't -- or you can't using public API. My suggestion would be to call required devices and set corresponding notifications. If you really need to know if its flight mode, maybe you can do that with calling 3G, GPS, Wi-Fi etc. and check if every one is off.
I saw another suggestion with taking an in-app screenshot and checking for orange airplane ( does top contains orange part ).
Drawbacks are present for both, My suggestion is bad since your devices could be off for other, not related reasons.
Good luck.
If you are using Location Services, they should return with an error code of 1009 by presenting the following in log:
Geocode error: Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline."
If you check for a return code of -1009, you can recognize the user as offline and follow through accordingly.
You cant check the airplane mode is active or not , while you can check the cellular signal strength,
func getSignalStrength() -> Int {
let application = UIApplication.shared
let statusBarView = application.value(forKey: "statusBar") as! UIView
let foregroundView = statusBarView.value(forKey: "foregroundView") as! UIView
let foregroundViewSubviews = foregroundView.subviews
var dataNetworkItemView:UIView? = nil
for subview in foregroundViewSubviews {
if subview.isKind(of: NSClassFromString("UIStatusBarSignalStrengthItemView")!) {
dataNetworkItemView = subview
break
}
}
if dataNetworkItemView == nil
{
return 0
}
return dataNetworkItemView?.value(forKey: "signalStrengthBars") as! Int
}
Using this method you can get the signal strength and determine the device is in airplane mode or not .
In airplane mode it will return 0 while in network it will return strength.
Hope this will help you

Resources