iOS Development
iOS Development
Hi,
I'm new on this forum and also new as a computer software.
I'm trying to develop an application like ergdata for iPhone but the documentation is quite horrible and the firm doesn't answer to my questions about it.
So I would like to know if someone are developing on this platform and would be great to share some experiences and snippet.
For information I use swift to code.
Does anyone succeed to connect an app to its rower ?
I put the 3 header files in my project and try some commands that i found in several topic but all are in c++.
Thank you for your help.
Piboo
I'm new on this forum and also new as a computer software.
I'm trying to develop an application like ergdata for iPhone but the documentation is quite horrible and the firm doesn't answer to my questions about it.
So I would like to know if someone are developing on this platform and would be great to share some experiences and snippet.
For information I use swift to code.
Does anyone succeed to connect an app to its rower ?
I put the 3 header files in my project and try some commands that i found in several topic but all are in c++.
Thank you for your help.
Piboo
-
- Paddler
- Posts: 15
- Joined: April 21st, 2015, 6:28 am
Re: iOS Development
I just bought the PM5 retrofit kit for my Model B rower because I want to be able to develop an iOS App. I also have interest in the SDK.
So far, I found that the SDK provided on the website has an Xcode 3.0 Project file that won't build on OSX 10.10 (Yosemite).
I would be grateful for anyone who could help point me in the right direction. I'm primarily an iOS Developer but can do Mac apps, and I do have *some* understanding of C++, but it's not really my language.
I would be willing to collaborate so to publish an iOS SDK if anyone is interested.
Cheers
horseshoe7.wordpress.com
So far, I found that the SDK provided on the website has an Xcode 3.0 Project file that won't build on OSX 10.10 (Yosemite).
I would be grateful for anyone who could help point me in the right direction. I'm primarily an iOS Developer but can do Mac apps, and I do have *some* understanding of C++, but it's not really my language.
I would be willing to collaborate so to publish an iOS SDK if anyone is interested.
Cheers
horseshoe7.wordpress.com
Re: iOS Development
For PM5 and iOS, I'd suggest leaving the SDK to one side for now, and starting with a standard Bluetooth app.
PM5 and iOS can communicate using Bluetooth 4.0 (also called "Bluetooth LE" and "Bluetooth Smart").
The Concept2 Bluetooth specification is on their website here: http://www.concept2.com/files/pdf/us/mo ... nition.pdf
PM5 uses four Bluetooth "Services", but you can get started using just two of them:
Device Discovery Service, UUID: CE060000-43E5-11E4-916C-0800200C9A66
This is just used to discover and connect the PM5
Device Information Service UUID: CE060010-43E5-11E4-916C-0800200C9A66
This Service has 4 Characteristics, for things like serial number and version numbers.
So in Swift, you might have something like this:
// Service UUIDs
let BLEBaseServiceUUID: CBUUID = CBUUID(string: "CE060000-43E5-11E4-916C-0800200C9A66") // Device Discovery
let BLEInformationServiceUUID: CBUUID = CBUUID(string: "CE060010-43E5-11E4-916C-0800200C9A66") // Device Information
Then you could start a bluetooth scan, and pass it the array of Service UUIDs to scan for:
let BLEServiceUUIDArray: [CBUUID]? = [BLEBaseServiceUUID, BLEInformationServiceUUID]
Once basic communication is working, you can look at the more exciting "Rowing" Service:
Rowing Service, UUID: CE060030-43E5-11E4-916C-0800200C9A66
...which has 11 Characteristics which package up lots of live rower data.
And then (eventually) you might want to look at the CSAFE commands:
Control Service, UUID: CE060020-43E5-11E4-916C-0800200C9A66
(And at that point, you'll want to look at the SDK documentation!)
One hint:
Don't develop an app like ErgData, 'coz that already exists. Do something new and exciting!
- Rob.
PM5 and iOS can communicate using Bluetooth 4.0 (also called "Bluetooth LE" and "Bluetooth Smart").
The Concept2 Bluetooth specification is on their website here: http://www.concept2.com/files/pdf/us/mo ... nition.pdf
PM5 uses four Bluetooth "Services", but you can get started using just two of them:
Device Discovery Service, UUID: CE060000-43E5-11E4-916C-0800200C9A66
This is just used to discover and connect the PM5
Device Information Service UUID: CE060010-43E5-11E4-916C-0800200C9A66
This Service has 4 Characteristics, for things like serial number and version numbers.
So in Swift, you might have something like this:
// Service UUIDs
let BLEBaseServiceUUID: CBUUID = CBUUID(string: "CE060000-43E5-11E4-916C-0800200C9A66") // Device Discovery
let BLEInformationServiceUUID: CBUUID = CBUUID(string: "CE060010-43E5-11E4-916C-0800200C9A66") // Device Information
Then you could start a bluetooth scan, and pass it the array of Service UUIDs to scan for:
let BLEServiceUUIDArray: [CBUUID]? = [BLEBaseServiceUUID, BLEInformationServiceUUID]
Once basic communication is working, you can look at the more exciting "Rowing" Service:
Rowing Service, UUID: CE060030-43E5-11E4-916C-0800200C9A66
...which has 11 Characteristics which package up lots of live rower data.
And then (eventually) you might want to look at the CSAFE commands:
Control Service, UUID: CE060020-43E5-11E4-916C-0800200C9A66
(And at that point, you'll want to look at the SDK documentation!)
One hint:
Don't develop an app like ErgData, 'coz that already exists. Do something new and exciting!
- Rob.
- Carl Watts
- Marathon Poster
- Posts: 4688
- Joined: January 8th, 2010, 4:35 pm
- Location: NEW ZEALAND
Re: iOS Development
Yes like contact Digital rowing and offer to design their RowPro program as an App.robnotyou wrote:
One hint:
Don't develop an app like ErgData, 'coz that already exists. Do something new and exciting!
- Rob.
Why re-invent the wheel, the software is already out there and has already undergone ten years of development, the main problem these days is there are too many OS on different devices to get a single decent App running on all of them for the resources of a small company. There is also not a lot of money in this field due to the relatively limited number of potential users so loads of one man bands writing multiple Apps just doesn't make sense, the result is just poor software that splits the user base.
The ultimate software will enable you to go online at any time of the day or night and find 16 people already online wanting to row together.
Carl Watts.
Age:56 Weight: 108kg Height:183cm
Concept 2 Monitor Service Technician & indoor rower.
http://log.concept2.com/profile/863525/log
Age:56 Weight: 108kg Height:183cm
Concept 2 Monitor Service Technician & indoor rower.
http://log.concept2.com/profile/863525/log
-
- Paddler
- Posts: 23
- Joined: May 22nd, 2015, 10:22 pm
Re: iOS Development
Carl, Check out the LiveRowing website - Liverowing.net We have been in development since July of 2014. We are almost there! Beat coming in the next couple of weeks. A major tech media outlet is doing a piece on us next week in SanFran so be on the look out.
The LiveRowing Team
Denver
The LiveRowing Team
Denver
Re: iOS Development
Thanks Rob, very useful!
I am starting to learn Swift (and talking to my PM5 using BLE) and using your hints I have indeed been able to discover and connect to my PM5.
Would you / anyone happen to have e.g. a complete class, so that I don't have to manually create all the UUID's and functions to read/write and pack/unpack the values?
A la: https://github.com/anas-imtiaz/SwiftSen ... rTag.swift
Related, in the tutorial for that one ( http://anasimtiaz.com/?p=201 ), they write a byte to subscribe to notifications:
Is that a generic BLE way to subscribe, or specific for the sensortTag? I.e. what should I do to subscribe to live rowing service data?
I am starting to learn Swift (and talking to my PM5 using BLE) and using your hints I have indeed been able to discover and connect to my PM5.
Would you / anyone happen to have e.g. a complete class, so that I don't have to manually create all the UUID's and functions to read/write and pack/unpack the values?
A la: https://github.com/anas-imtiaz/SwiftSen ... rTag.swift
Related, in the tutorial for that one ( http://anasimtiaz.com/?p=201 ), they write a byte to subscribe to notifications:
Code: Select all
self.sensorTagPeripheral.writeValue(enablyBytes, forCharacteristic: thisCharacteristic, type: CBCharacteristicWriteType.WithResponse)
-
- Paddler
- Posts: 15
- Joined: April 21st, 2015, 6:28 am
Re: iOS Development
That's the whole point of an SDK or a framework. To add a layer of abstraction that makes it easier for others to use, and is not specific to any particular app. I don't like the current software out there. It also doesn't do what I want it to do, namely, take my data and be able to add it to my runkeeper, Strava, as an activity, without having to manually upload my activities from within Strava itself.Why re-invent the wheel, the software is already out there and has already undergone ten years of development....
For me, an iOS SDK is a set of classes that makes communication with the Rower straightforward. Which wraps the specifics of the bluetooth specification into an easy-to-use Objective-C / Swift flavoured API, and has nothing to do with a specific app.
What Concept2 makes available as an "SDK" is on iOS not an SDK at all.
I hope to set about changing this if I find the time.
Many thanks robnotyou !! Great help.
Re: iOS Development
Hi,
Thnak your very murch for these answers.
I didn't see the notifications.
We had the same approach since my first mail but I can't discover yet the rower.
var rowingTagPeripheral:CBPeripheral!
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
let nameOfDeviceFound:NSString = (advertisementData as NSDictionary).objectForKey(CBAdvertisementDataLocalNameKey) as! NSString
if !(nameOfDeviceFound.isEqualToString("")) {
//We found Concept2 Rower
// Stop scanning, set as the peripheral to use and establish connection
self.centralManager.stopScan()
self.rowingTagPeripheral = peripheral
self.rowingTagPeripheral.delegate = self
self.centralManager.connectPeripheral(peripheral, options: nil)
} else {
self.deviceConnectedLabel.text = "Rameur Concept2 non trouvé"
}
}
Where did I make the mistake ?
Thank you very much for your help
Thnak your very murch for these answers.
I didn't see the notifications.
We had the same approach since my first mail but I can't discover yet the rower.
var rowingTagPeripheral:CBPeripheral!
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
let nameOfDeviceFound:NSString = (advertisementData as NSDictionary).objectForKey(CBAdvertisementDataLocalNameKey) as! NSString
if !(nameOfDeviceFound.isEqualToString("")) {
//We found Concept2 Rower
// Stop scanning, set as the peripheral to use and establish connection
self.centralManager.stopScan()
self.rowingTagPeripheral = peripheral
self.rowingTagPeripheral.delegate = self
self.centralManager.connectPeripheral(peripheral, options: nil)
} else {
self.deviceConnectedLabel.text = "Rameur Concept2 non trouvé"
}
}
Where did I make the mistake ?
Thank you very much for your help
Re: iOS Development
You'll want to instantiate a CBCentral and save it to an ivar (you may already have this):
Then later in YourClass, you'll want to implement the centralManagerDidUpdateState(central:) function, which is called once you instantiated the CBCentral in init:
You'll want to implement the centralManager(central:didDiscoverPeripheral:advertisementData:RSSI:) function, which is called if the central finds the peripheral with the UUID shown above (that's the Concept2 PM5 Base Service, specifically used for a central to connect), where you can try connecting to the peripheral:
And then you'll want to implement the centralManager(central:didConnectPeripheral:) function to connect to it:
From there you'll have to implement the peripheral delegate functions to query for characteristics, subscribe to updates on characteristics, etc.
Code: Select all
class YourClass: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var central: CBCentral!
var peripheral: CBPeripheral!
override init() {
central = CBCentral(delegate: self, queue: nil, options: nil)
}
Code: Select all
func centralManagerDidUpdateState(central: CBCentralManager!) {
if central.state == .PoweredOn {
central.scanForPeripheralsWithServices(["CE060000-43E5-11E4-916C-0800200C9A66"], options: nil)
} else {
println("Local device Bluetooth not available")
}
}
Code: Select all
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
central.stopScan()
central.connectPeripheral(peripheral, options: nil)
}
Code: Select all
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
Re: iOS Development
Thank you for your rapid answer.
I already implemented the CBCentral variable in my viewDidLoad.
I did too for the centralManagerDidUpdateState.
Maybe my problem come from:
Is it useful here ?
I already implemented the CBCentral variable in my viewDidLoad.
I did too for the centralManagerDidUpdateState.
Maybe my problem come from:
Code: Select all
let nameOfDeviceFound:NSString = (advertisementData as NSDictionary).objectForKey(CBAdvertisementDataLocalNameKey) as! NSString
if !(nameOfDeviceFound.isEqualToString("")) {
Re: iOS Development
I don't know anything about using advertisementData to identify a peripheral. The UUID I posted (and robnotyou posted) is specifically noted in the C2 documentation as the service UUID used to identify a PM5 peripheral. I suggest using it.
Just to note, with Swift you usually want to avoid as! whenever possible. And avoid casting to ObjC objects whenever possible. as! means you're guaranteeing to the compiler that the value will be what you claim it will be. If during runtime it isn't, your application will crash. Unnecessarily casting to ObjC (dictionaries, strings, etc) just brings along a bunch of baggage for no useful reason.
For example, this:
Is better written in Swift as:
Just to note, with Swift you usually want to avoid as! whenever possible. And avoid casting to ObjC objects whenever possible. as! means you're guaranteeing to the compiler that the value will be what you claim it will be. If during runtime it isn't, your application will crash. Unnecessarily casting to ObjC (dictionaries, strings, etc) just brings along a bunch of baggage for no useful reason.
For example, this:
Code: Select all
let nameOfDeviceFound:NSString = (advertisementData as NSDictionary).objectForKey(CBAdvertisementDataLocalNameKey) as! NSString
if !(nameOfDeviceFound.isEqualToString("")) {
//We found Concept2 Rower
}
Code: Select all
if let nameOfDeviceFound = advertisementData[CBAdvertisementDataLocalNameKey] as? String where nameOfDeviceFound != "" {
// We found a non-empty string
}
-
- Paddler
- Posts: 15
- Joined: April 21st, 2015, 6:28 am
Re: iOS Development
I'm a bit further along than Piboo and doing my implementation in Objective-C.
I'm currently parsing the data coming in from the rowing service and don't know how to interpret the bytes.
The spec says (for example UUID 0x0037),
I'm currently parsing the data coming in from the rowing service and don't know how to interpret the bytes.
The spec says (for example UUID 0x0037),
Which means what? I wrote this code, but to be honest I'm not sure how to interpret these bytes.Data bytes packed as follows:
Elapsed Time Lo (0.01 sec lsb),
Elapsed Time Mid,
Elapsed Time High,
Distance Lo (0.1 m lsb),
Distance Mid,
Distance High,
... etc.
Code: Select all
const uint8_t *payload = [data bytes]; // data is the characteristic.value
NSTimeInterval secondsFraction = 0.01 * (uint8_t)payload[0]; // so the byte, 0-255, is multiplied by 0.01 and that's how many seconds?? Should it then have a value of 0-99 ?
NSTimeInterval seconds = (NSTimeInterval)1.f * (uint8_t)payload[1]; // likewise, this value would be 0-59 ?
NSTimeInterval minutes = (NSTimeInterval)1.f * (uint8_t)payload[2]; // what if I am rowing for longer than 255 minutes? See, I think I don't understand...
-
- Paddler
- Posts: 15
- Joined: April 21st, 2015, 6:28 am
Re: iOS Development
Allow me to answer my own question, may it be of help to you:
For these two properties above, you have to use the 3 bytes as one value, like this:horseshoe7 wrote: Data bytes packed as follows:
Elapsed Time Lo (0.01 sec lsb),
Elapsed Time Mid,
Elapsed Time High,
Distance Lo (0.1 m lsb),
Distance Mid,
Distance High,
... etc.
Code: Select all
uint32_t decimetres; // because the LSB is 0.1m, this means the 32-bit unsigned int value is counting decimetres.
[data getBytes:&decimetres range:NSMakeRange(3, 3)];
NSLog(@"Distance in m: %.1f", (float)decimetres/10.f);
Re: iOS Development
Sorry for the delay. Lot of work these last days.
I will test it today and tell you.
As u said I don't know why with it's not working.
I will use your method.
Concerning get back the data I already write it in another class RowerTag like this (not tested):
But I'm doing a conversion to get the value
So to build the distance I have to get the 3 first three value (Lo, Mid and Hi) of the return array where each is a UInt8 type.
Do I have to calculate like this :
distance = Hi *65536 + Mid * 255 + Lo ?
As we get bytes we need to convert it in bit no ?
Thank you for your help
I will test it today and tell you.
As u said I don't know why with
Code: Select all
CBAdvertisementDataLocalNameKey
I will use your method.
Concerning get back the data I already write it in another class RowerTag like this (not tested):
Code: Select all
class func dataToUnsignedBytes32(value:NSData) -> [UInt32]{
let count = value.length
var array = [UInt32](count: count, repeatedValue: 0)
value.getBytes(&array, length: count * sizeof(UInt32))
return array
}
// Distance 0.1m
class func getDistance(value:NSData) -> Double {
//let range:NSRange = NSMakeRange(0, 3)
let dataFromRowing = dataToUnsignedBytes32(value)
let distance:Double = Double(dataFromRowing[5]) * 65536 + Double(dataFromRowing[4]) * 256 + Double(dataFromRowing[3])
return distance
}
So to build the distance I have to get the 3 first three value (Lo, Mid and Hi) of the return array where each is a UInt8 type.
Do I have to calculate like this :
distance = Hi *65536 + Mid * 255 + Lo ?
As we get bytes we need to convert it in bit no ?
Thank you for your help
Re: iOS Development
Why did divide by 10 the distance ?
It's said that is 0.1m so it's already in meter no ?
It's said that is 0.1m so it's already in meter no ?