Does anybody know how to receive messages through uBudu Mesh Network?
I'm working on an app using uBudu beacons and the general idea is to allow users to send messages to each other through these beacons. I've successfully hooked up iOS-Mesh-SDK as described here: https://github.com/Ubudu/IOS-Mesh-SDK.
There is an example of how to send mesh message to another beacon and it works perfectly, but as for retrieving these messages from beacon to user app I have no idea.
There are methods in MeshBeacon class:
- (void)abortMeshMessage;
- (void)clearMeshMessageQueue;
- (void)setMeshNotification:(BOOL)enable withCompletionBlock:(UMeshBeaconSuccessBlock)completionBlock;
but nothing about retrieving the message.
Any suggestions are highly appreciated!
As you correctly pointed out, the SDK doesn't yet have the method for receiving messages but there is an update of the library coming with support of this feature.
I found maybe brute, but working solution for this problem.
First we need to set ourselves as delegate of BeaconManager
UBUMeshBeaconManager.sharedInstance().delegate = self
In implementation of delegate method, connect to closest beacon and reset its peripheral delegate to self.
func meshManager(meshManager: UBUMeshBeaconManager!, didUpdateVisibleAndConnectableNodes meshNodes: [AnyObject]!) {
UBUMeshBeaconManager.sharedInstance().connectToClosestBeacon({ (meshManager, meshBeacon, userInfo) -> Void in
meshBeacon.peripheral.delegate = self
}, progressBlock: { (meshManager, userInfo) -> Void in
}, failedBlock: { (meshManager, meshBeacon, userInfo, error) -> Void in
})
}
And waiting for characteristics updates in Peripheral Delegate method. All services and characteristics will be set behind the scene by UbuduSDK.
func peripheral(peripheral: CBPeripheral!,
didUpdateValueForCharacteristic characteristic: CBCharacteristic!,
error: NSError!) {
var string = NSString(data: characteristic.value, encoding: NSASCIIStringEncoding)
println(string)
}
Related
I am developing an iOS app using BLE, I have the BLE comms working but I do not seem to receive any data. The plan is to use multiple BLE UUID characteristics to send data from our ECU to the iOS mobile app. I know the BLE ECU is working as I have checked it with a BLE scanner.
I have followed this example, but still cannot get the BLE read to work correctly. Oddly if read the bytes back one by one I get the required data, but I cannot seem to find a solution to read the complete string in one go? I could use a loop to read all the data back, but surely there must be a way to read back the BLE receive buffer in one go?
https://www.freecodecamp.org/news/ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes/
Any help would be highly appreciated.
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
var characteristicASCIIValue = NSString()
guard characteristic == rxCharacteristic,
let characteristicValue = characteristic.value,
let ASCIIstring = NSString(data: characteristicValue, encoding: String.Encoding.utf8.rawValue) else { return }
print("Value Recieved: \((characteristicASCIIValue as String))")
if(characteristic.uuid.isEqual(CBUUIDs.ECU_UUID_A)
{
print ("Found ECU_UUID_A")
Extract_ECU_UUID_A(ASCIIstring)
}
else if(characteristic.uuid.isEqual(CBUUIDs.ECU_UUID_B)
{
print ("Found ECU_UUID_B")
Extract_ECU_UUID_B(ASCIIstring)
}
}
I'm trying to make iOS app to communicate with watch, but i get inconsistent behaviour all the time - either the communication is too slow, or none of the data gets transferred at all.
Besides, i don't see any "Phone disabled" screen when the watchKit runs (which causes a crash, because i need to get data from the phone first).
This is what i have in regards to establishing the WCSession in the iPhone app
App Delegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if NSClassFromString("WCSession") != nil {
if #available(iOS 9.0, *) {
if(WCSession.isSupported()){
self.session = WCSession.defaultSession()
self.session.delegate = self
self.session.activateSession()
if session.paired {
print("Watch connected")
} else {
print("No watch")
}
}
} else {
}}
if NSClassFromString("WCSession") != nil {
if(WCSession.isSupported()){
session.sendMessage(["b":"delegateSaysHi"], replyHandler: nil, errorHandler: nil)
}}
}
MainViewController
(viewDidLoad)
if NSClassFromString("WCSession") != nil {
if(WCSession.isSupported()){
self.session = WCSession.defaultSession()
self.session.delegate = self
self.session.activateSession()
if session.paired {
print("Watch connected")
} else {
print("No watch")
}
}}
MainViewController (Method for transferring bunch of data from iOS app to watchKit app)
func transferData(){
do {
let dataArray = ["somedata": array2d1]
try WCSession.defaultSession().updateApplicationContext(dataArray)
let dataArray1 = ["somedata1": array2d2]
try WCSession.defaultSession().updateApplicationContext(dataArray1)
let dataArray2 = ["somedata2": array2d3]
try WCSession.defaultSession().updateApplicationContext(dataArray2)
let dataArray3 = ["somedata3": array2d4]
try WCSession.defaultSession().updateApplicationContext(dataArray3)
// and up to 12
}
catch {
print("Something wrong happened")
}
}
And this is for watchKit app
App Delegate
func applicationDidFinishLaunching() {
if(WCSession.isSupported()){
self.session = WCSession.defaultSession()
self.session.delegate = self
self.session.activateSession()
}
}
func applicationDidBecomeActive() {
if(WCSession.isSupported()){
self.session.sendMessage(["b":"peek"], replyHandler: nil, errorHandler: nil)
}
InterfaceController (awakeWithContext)
if(WCSession.defaultSession().reachable){
self.session.sendMessage(["b":"peek"], replyHandler: nil, errorHandler: nil)
}
Method for receiving ApplicationContext data
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
if let retrievedArray1 = applicationContext["somedata"] as? [[String]] {
self.watchAppArray = retrievedArray1
}
if let retrievedArray2 = applicationContext["somedata2"] as? [[String]] {
self.watchAppArray = retrievedArray1
// and so on for 12 arrays sent from phone
}
}
}}
Any advices on clearing out the situation are very welcome!
Thank you.
Multiple delegates/activations:
You're repeatedly setting up, delegating, and activating sessions in different parts of your app. You keep changing your delegate, so code in one part of the app will no longer be used after you delegated handling to a different part of your app.
You should use a single session/delegate throughout your app. One solution is to setup a WCSession singleton which would be available app-wide. Here's a guide which walks you through that process.
Only the most recent application context would get sent:
By trying to queue up multiple application context requests, the earlier ones would no longer be in the queue when the system gets around to transmitting it, as the system would have already replaced the preceding context with the later one. So only the last (dataArray3) would ever get transmitted.
Use the updateApplicationContext:error: method to communicate recent state information to the counterpart. When the counterpart wakes, it can use this information to update its own state. ... This method overwrites the previous data dictionary, so use this method when your app needs only the most recent data values.
If all of the arrays represent the recent state of your application, you want to transmit them together in a single dictionary.
var dataArray = [String: AnyObject]()
dataArray["somedata"] = array2d1
dataArray["somedata1"] = array2d2
dataArray["somedata2"] = array2d3
dataArray["somedata3"] = array2d4
do {
try session.updateApplicationContext(dataArray)
}
catch {
print(error)
}
It may also help to add some error handling to your sendMessage code, as the paired device may not always be reachable.
Slow communication:
As for the communication being too slow, there are two issues at hand.
Transfers may not happen immediately.
When only one session is active, the active session may still send updates and transfer files, but those transfers happen opportunistically in the background.
Remember that background transfers are not be delivered immediately. The system sends data as quickly as possible but transfers are not instantaneous, and the system may delay transfers slightly to improve power usage. Also, sending a large data file requires a commensurate amount of time to transmit the data to the other device and process it on the receiving side.
The more data you send, the longer it takes to transmit/receive it all.
When sending messages, send only the data that your app needs. All transfers involve sending data wireless to the counterpart app, which consumes power. Rather than sending all of your data every time, send only the items that have changed.
You can control how much data you send, as well as whether the data is sent interactively or in the background. If the watch is reachable, you could use sendMessage for immediate communication. If it's not reachable, you could fall back on a background method.
I am trying to send some data from an iOS device to the HM-10 Bluetooth LE Module connected to an arduino. Problem is after connecting to the module discoverServices doesn't return a characteristic for the service.
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
if(error != nil) {
print(error?.description)
}
for service in peripheral.services! {
let thisService = service as CBService
print("Service: \(thisService.description)")
print("Characteristic: \(thisService.characteristics)")
}
}
This outputs:
Service: <CBService: 0x137e84ea0, isPrimary = YES, UUID = FFE0>
Characteristic: nil
I am a beginner with this arduino stuff as well as iOS. So any suggestions would be welcome. Maybe there is a way to write to the bluetooth module without knowing the characteristic... I have no idea.
I finally managed to get the answer. Because of my rudimentary understanding of the CoreBluetooth Framework I forgot to call discoverCharacteristics in didDiscoverServices. Well, I am really learning by doing. (I somehow thought discoverServices would call discoverCharacteristics itself.)
I've been writing an app using Swift that connects to a bluetooth BLE device. For some reason, the app doesn't always connect to the device. In this case it will connect but gets disconnected straight away. This only happens maybe 1 in 10 times it connects, but definitely interferes with the reliability of the app.
I'm using CoreBluetooth to connect to the BLE device. Attempting connection again usually always gets it to reconnect, and other apps that communicate with this device works correctly every time, so I'm confident that it is not a problem with the peripheral.
I'd just like to know if there is anyone out there who has had a similar issue or if there is a particular reason why this may be happening?
EDIT: Here's the code for the willSelectRow of my table. This is where I get the peripheral to connect.
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
centralManager.stopScan()
connectingPeripheral.append(discoveredDeviceArrayInfo[indexPath.row])
connectPeripheralNow(connectingPeripheral[0])
return indexPath
}
This is where I get it to connect, at this point I select the row which sets the CBPeripheral details of the device to connect to.
connectPeripheralNow looks like this:
func connectPeripheralNow(peripheral: CBPeripheral!){
self.centralManager.connectPeripheral(peripheral, options: nil)
}
didConnectPeripheral and didDiscoverServices looks like this
func centralManager(central: CBCentralManager,didConnectPeripheral peripheral: CBPeripheral)
{
peripheral.delegate = self
peripheral.discoverServices([CBUUID(string: "FFE5")])
print("SUCCESS: Connected to " + peripheral.name!)
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?)
{
if let servicePeripherals = peripheral.services as [CBService]!
{
for servicePeripheral in servicePeripherals
{
print("INFORMATION: Service discovered " + String(stringInterpolationSegment: servicePeripheral.UUID))
peripheral.discoverCharacteristics(nil, forService: servicePeripheral)
}
}
}
Just for the info, I do get a 'SUCCESS: Connected to xxx' message appear which shows that it is connecting. If you need more code, let me know!
What you are mentioning is more of an expected behaviour. BTLE is designed to consume very little amount of energy so it drops the connection as soon as possible. If you do not need a permanent connection then follow Discover --> Connect --> Read/write --> Setup timer --> When timer fires Connect.
If you need a permanent connection, you should subscribe for characteristic which transmits real time data like heart rate application. You would need to implement setNotifyValue:forCharacteristic: method. Read Apple Documentation for more details.
I am doing a beacon ranging using Core Location with UUID, Major & Minor in my app. After ranging I need to connect that beacon using Core Bluetooth api. I am using estimote beacon in my application.
The ESTBeaconConnection class is what you're looking for. You can use the initWithBeacon initializer and pass it the CLBeacon object you got from ranging:
let bc = ESTBeaconConnection(beacon: myBeacon,
delegate: self,
startImmediately: true)
This'll initiate the connection process.
The delegate should implement the ESTBeaconConnectionDelegate protocol. When the connection is successfully established, you'll get a call to beaconConnectionDidSucceed, and you can start writing new settings to the beacon:
func beaconConnectionDidSucceed(connection: ESTBeaconConnection!) {
connection.writeMajor(123) { (newMajor, error) in
if (error != nil) {
println("successfully wrote the new major: \(newMajor)")
} else {
println("didn't write the new major, error was \(error)")
}
connection.disconnect()
}
}
It's also a good idea to implement the beaconConnectionDidFailWithError delegate—should something go south, you'll know about it.
Finally, since you can only connect to and change settings of your own beacons, you need to authenticate yourself before attempting to connect:
ESTCloudManager.setupAppID("appid", andAppToken: "app token")
You can generate the app token on https://cloud.estimote.com.