. I am working on an app that uses a core bluetooth framework .App is related to start, lock, unlock doors. I am almost done with the app with the help of examples provided by the Apple. That is Heart Rate Monitor and Temperature Sensor.
I have 2 Questions.
1: Heart Rate Monitor app reads the data from a peripheral while in my case I just have to signal the peripheral that startButton is pressed. So my Question is that whether I have to use writeValueForCharacteristic in discoverCharacteristics method.
2: If so kindly help me with the button code here
- (IBAction) startButtonPressed: (id) sender {
}
- (IBAction) lockDoors: (id) sender {
}
- (IBAction) unLockDoors: (id) sender {
}
Code is given by.
- (void) peripheral:(CBPeripheral *) peripheral didDiscoverServices:(NSError *)error
{
if (error)
{
NSLog(#"Discovered services for %# with error: %#", peripheral.name, [error localizedDescription]);
return;
}
for (CBService * service in peripheral.services)
{
NSLog(#"Service found with UUID: %#", service.UUID);
if([service.UUID isEqual:[CBUUID UUIDWithString:#"1809"]])
{
/* Car Start Service - discover
CAR_START_CHARACTERISTIC_UUID,
LOCK_DOOR_CHARACTERISTIC_UUID,
UNLOCK_DOOR_CHARACTERISTIC_UUID,
TRUNK_CHARACTERISTIC_UUID
ALARM_CHARACTERISTIC_UUID
*/
[peripheralManager discoverCharacteristics:[NSArray arrayWithObjects:[CBUUID UUIDWithString:#"2A1E"], [CBUUID UUIDWithString:#"2A1C"], [CBUUID UUIDWithString:#"2A21"], [CBUUID UUIDWithString:#"2A1F"],[CBUUID UUIDWithString:#"2A1G"],nil] forService:service];
}
else if([service.UUID isEqual:[CBUUID UUIDWithString:#"180A"]])
{
/* Device Information Service - discover manufacture name characteristic */
[peripheralManager discoverCharacteristics:[NSArray arrayWithObject:[CBUUID UUIDWithString:#"2A29"]] forService:service];
}
else if ( [service.UUID isEqual:[CBUUID UUIDWithString:CBUUIDGenericAccessProfileString]] )
{
/* GAP (Generic Access Profile) - discover device name characteristic */
[peripheralManager discoverCharacteristics:[NSArray arrayWithObject:[CBUUID UUIDWithString:CBUUIDDeviceNameString]] forService:service];
}
}
}
- (void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error)
{
NSLog(#"Discovered characteristics for %# with error: %#", service.UUID, [error localizedDescription]);
return;
}
if([service.UUID isEqual:[CBUUID UUIDWithString:#"1809"]]) {
for (CBCharacteristic * characteristic in service.characteristics)
{
/* Set Car Start */
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A1C"]])
{
self.carStartCharacteristic = characteristic;
NSLog(#"Found Car Start Characteristic");
}
/* Set Lock Doors */
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A1E"]])
{
self.lockDoorsCharacteristic = characteristic;
NSLog(#"Found Lock Doors Characteristic");
}
/* Set Un Lock Doors */
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A11"]])
{
self.unLockDoorsCharacteristic = characteristic;
NSLog(#"Found UnLock Doors Characteristic");
}
/* Set Trunk Open */
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A13"]])
{
self.trunkCharacteristic = characteristic;
NSLog(#"Found Trunk Characteristic");
}
/* Set Alarm */
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A14"]])
{
self.alarmCharacteristic = characteristic;
NSLog(#"Found Alarm Characteristic");
}
}
}if([service.UUID isEqual:[CBUUID UUIDWithString:#"180A"]])
{
for (CBCharacteristic * characteristic in service.characteristics)
{
/* Read manufacturer name */
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A29"]])
{
[peripheralManager readValueForCharacteristic:characteristic];
NSLog(#"Found a Device Manufacturer Name Characteristic - Read manufacturer name");
}
}
}
if ( [service.UUID isEqual:[CBUUID UUIDWithString:CBUUIDGenericAccessProfileString]] )
{
for (CBCharacteristic *characteristic in service.characteristics)
{
/* Read device name */
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:CBUUIDDeviceNameString]])
{
[peripheralManager readValueForCharacteristic:characteristic];
NSLog(#"Found a Device Name Characteristic - Read device name");
}
}
}
}
You will use both. You will scan (if not previously connected), then discover services, then discover characteristics. Once that is done, you will call writeValueForCharacteristic from your button with whatever value you wish to write.
(IBAction) startButtonPressed: (id) sender {
[peripheral writeValue:yourNSData forCharacteristic:yourCBCharacteristic type:yourWriteType];
}
Related
I have written an app which I am trying to get and update the battery level on my cB-OLP425 module.
I have used the following code but sometimes it gives me a value of 70 and at other times it gives me a value of -104. I have looked at numerous posts and tried a lot of things but nothing seems to work.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if([service.UUID isEqual:[CBUUID UUIDWithString:#"180F"]]) {
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(#"discovered service %#", service.UUID);
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A19"]]) {
NSLog(#"Found Notify Characteristic %#", characteristic.UUID);
self.mycharacteristic = characteristic;
[self.testPeripheral readValueForCharacteristic:mycharacteristic];
[self.testPeripheral setNotifyValue:YES forCharacteristic:mycharacteristic];
char batlevel;
[mycharacteristic.value getBytes:& batlevel length:0];
int n = (float)batlevel;
int value = n ;
self.batteryLevel = value;
NSLog(#"batterylevel1;%f",batteryLevel);
/* [[NSUserDefaults standardUserDefaults]setFloat: batteryLevel forKey:#"battery_level"];*/
}
} }
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if([characteristic.UUID isEqual:[CBUUID UUIDWithString:#"2A19"]]) {
NSLog(#"Found Battery Characteristic %#", characteristic.UUID);
[mycharacteristic.value getBytes:& batteryLevel length:0];
return;
}
[self.delegate peripheralDidReadChracteristic:mycharacteristic withPeripheral:testPeripheral withError:error];
}
Could someone please help me!!
Your getBytes seems to be the issue. We read the battery level in our app using:
UInt8 batteryLevel = ((UInt8*)value.bytes)[0];
You could also do it with:
UInt8 batteryLevel;
[value getBytes:&battery length:1];
You need to make sure the type is UInt8 otherwise you would read it into the wrong location.
Also, don't read the value right away in the didDiscoverCharacteristicsForService. Wait until you get notified that the value has been updated. The docs state:
When you attempt to read the value of a characteristic, the peripheral calls the peripheral:didUpdateValueForCharacteristic:error: method of its delegate object to retrieve the value. If the value is successfully retrieved, you can access it through the characteristic’s value property, like this:
I made an iphone application which uses OpenAL to play many sounds.
These sounds are in mp3, quite heavy (more than 1mn)and I stream them (2 buffers per sound) in order to use less memory.
To manage interruptions, I use this code :
In OpenALSupport.c file :
//used to disable openAL during a call
void openALInterruptionListener ( void *inClientData, UInt32 inInterruptionState)
{
if (inInterruptionState == kAudioSessionBeginInterruption)
{
alcMakeContextCurrent (NULL);
}
}
//used to restore openAL after a call
void restoreOpenAL(void* a_context)
{
alcMakeContextCurrent(a_context);
}
In my SoundManager.m file :
- (void) restoreOpenAL
{
restoreOpenAL(mContext);
}
//OPENAL initialization
- (bool) initOpenAL
{
// Initialization
mDevice = alcOpenDevice(NULL);
if (mDevice) {
...
// use the device to make a context
mContext=alcCreateContext(mDevice,NULL);
// set my context to the currently active one
alcMakeContextCurrent(mContext);
AudioSessionInitialize (NULL, NULL, openALInterruptionListener, mContext);
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryAmbient error: &setCategoryError];
...
}
And finally in my AppDelegate :
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[CSoundManager getInstance] restoreOpenAL];
...
}
With this method the sounds are back after a call , but flows seem to be played randomly.
Is there a specific way to manage interruption with streaming sounds ? I don't find any article about that.
Thanks for your help.
Ok, I answer to my own question.
I solved the problem by managing error on my streaming method :
- (void) updateStream
{
ALint processed;
alGetSourcei(sourceID, AL_BUFFERS_PROCESSED, &processed);
while(processed--)
{
oldPosition = position;
NSUInteger buffer;
alSourceUnqueueBuffers(sourceID, 1, &buffer);
////////////////////
//code freshly added
ALint err = alGetError();
if (err != 0)
{
NSLog(#"Error Calling alSourceUnQueueBuffers: %d",err);
processed++;
//restore old position for the next buffer
position = oldPosition;
usleep(10000);
continue;
}
////////////////////
[self stream:buffer];
alSourceQueueBuffers(sourceID, 1, &buffer);
////////////////////
//code freshly added
err = alGetError();
if (err != 0)
{
NSLog(#"Error Calling alSourceQueueBuffers: %d",err);
processed++;
usleep(10000);
//restore old position for the next buffer
position = oldPosition;
}
///////////////////
}
}
I've implemented google analytics in my ios app but nothing ever gets logged to analytics. I've been running it for a month, so I know there's something that's not working.
I added a GANTrackerDelegate to trace why my dispatch calls are failing, but it never gets called. I know dispatch is getting called. Does anyone know? Here's my class.
#import <Foundation/Foundation.h>
#import "GANTracker.h"
#interface trackerDelegate : NSObject<GANTrackerDelegate> {
}
#end
#import "trackerDelegate.h"
#implementation trackerDelegate
#pragma mark GANTrackerDelegate
- (void)trackerDispatchDidComplete:(GANTracker *)tracker
eventsDispatched:(NSUInteger)eventsDispatched
eventsFailedDispatch:(NSUInteger)eventsFailedDispatch {
NSLog(#"events dispatched: %d, events failed: %d", eventsDispatched, eventsFailedDispatch);
}
#end
Here's my tracker class. Note, I can trace all of these things getting called, and I know that dispatch is called repeatedly and returns 'YES', but the delegate routine never gets called. It might be a coding thing, but I'm trying to see if the dispatch calls are failing or not. The id I'm using is valid because it works for Android.
tracker.h
#interface Tracker : NSObject {
}
+ (void) startTracking;
+ (void) endTracking;
+ (void) dispatch;
+ (void) trackPageView : (NSString *) pageId;
+ (void) trackEvent : (NSString *) categoryId
: (NSString *) actionID
: (NSString *) labelID
: (int) tvalue;
#end
tracker.m
#import "Tracker.h"
#import "trackerDelegate.h"
#implementation Tracker
static BOOL trackingOn = false;
static BOOL dirty = false;
trackerDelegate *tg = nil;
+ (void) startTracking
{
if (trackingOn){
return;
}
#try{
if (!tg)
{
tg = [[trackerDelegate alloc] init];
}
[[GANTracker sharedTracker] startTrackerWithAccountID:#"UA-VALID-GOOGLEID"
dispatchPeriod:-1
delegate:tg];
trackingOn = true;
}
#catch (NSException*)
{
trackingOn = false;
}
}
+ (void) endTracking
{
#try{
if (trackingOn)
{
[[GANTracker sharedTracker] stopTracker];
}
trackingOn = false;
}
#catch (NSException *){
trackingOn = false;
}
}
+ (void) dispatch
{
if (!dirty){
return;
}
[self startTracking];
#try
{
if (![[GANTracker sharedTracker] dispatch]) {
trackingOn = false;
NSLog(#"Google anaytics dispatch failed");
return;
}
dirty = false;
}
#catch (NSException *){
trackingOn = false;
}
}
+ (void) trackPageView : (NSString *) pageId
{
[self startTracking];
#try{
if (trackingOn){
NSError *error;
if (![[GANTracker sharedTracker] trackPageview:pageId
withError:&error]) {
trackingOn = false;
NSLog(#"Google anaytics track pageview failed");
return;
}
dirty = true;
}
}
#catch (NSException *){
trackingOn = false;
}
}
+ (void) trackEvent : (NSString *) categoryId
: (NSString *) actionID
: (NSString *) labelID
: (int) tvalue
{
[self startTracking];
#try
{
if (trackingOn){
NSError *error;
if (![[GANTracker sharedTracker] trackEvent:categoryId
action:actionID
label:labelID
value:tvalue
withError:&error])
{
trackingOn = false;
NSLog(#"Google anaytics track event failed");
return;
}
dirty = true;
}
}
#catch (NSException *){
trackingOn = false;
}
}
#end
Muchas Gracias!!
First of all you should initiate the Google Analytics account using the Web-property ID given to you at the time of creating an account in Google Analytics site.
Then you must add the following code to start the tracking and set the dispatch time. Then only you could view the analytics count in your account in Google Analytics online account. Here is the initiating code for that.
[[GANTracker sharedTracker]startTrackerWithAccountID:#"UA-xxxxxx-yy"
dispatchPeriod:10
delegate:nil];
You should also make sure, you have included the following code in the dealloc in the AppDelegate.
[GANTracker sharedTracker] stopTracker];
Hope this will work fine. Thanks.
During a test, a client noticed that video playback in the iPhone pauses when headphones are unplugged. He wanted similar functionality for audio playback, and maybe the ability to pop up a message.
Does anyone know if there's an event of some kind I could hook into to make this possible?
See Responding to Route Changes from the Audio Session Programming Guide.
This changed with iOS 7, you just need to listen to the notification named AVAudioSessionRouteChangeNotification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(audioRouteChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
Swift 3.0 #snakeoil's solution:
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.yourMethodThatShouldBeCalledOnChange), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
Here's the full implementation I eventually used for sending events when the headphones are plugged in (and unplugged).
There was a fair amount of complexity I needed to deal with to ensure things still worked after the app was returned from the background.
CVAudioSession.h file
#import <Foundation/Foundation.h>
#define kCVAudioInputChangedNotification #"kCVAudioInputChangedNotification"
#define kCVAudioInterruptionEnded #"kCVAudioInterruptionEnded"
#interface CVAudioSession : NSObject
+(void) setup;
+(void) destroy;
+(NSString*) currentAudioRoute;
+(BOOL) interrupted;
#end
CVAudioSession.m file
#import "CVAudioSession.h"
#import <AudioToolbox/AudioToolbox.h>
#implementation CVAudioSession
static BOOL _isInterrupted = NO;
+(void) setup {
NSLog(#"CVAudioSession setup");
// Set up the audio session for recording
OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListener, (__bridge void*)self);
if (error) NSLog(#"ERROR INITIALIZING AUDIO SESSION! %ld\n", error);
if (!error) {
UInt32 category = kAudioSessionCategory_RecordAudio; // NOTE CANT PLAY BACK WITH THIS
error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
if (error) NSLog(#"couldn't set audio category!");
error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void*) self);
if (error) NSLog(#"ERROR ADDING AUDIO SESSION PROP LISTENER! %ld\n", error);
UInt32 inputAvailable = 0;
UInt32 size = sizeof(inputAvailable);
// we do not want to allow recording if input is not available
error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);
if (error) NSLog(#"ERROR GETTING INPUT AVAILABILITY! %ld\n", error);
// we also need to listen to see if input availability changes
error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void*) self);
if (error) NSLog(#"ERROR ADDING AUDIO SESSION PROP LISTENER! %ld\n", error);
error = AudioSessionSetActive(true);
if (error) NSLog(#"CVAudioSession: AudioSessionSetActive (true) failed");
}
}
+ (NSString*) currentAudioRoute {
UInt32 routeSize = sizeof (CFStringRef);
CFStringRef route;
AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
&routeSize,
&route);
NSString* routeStr = (__bridge NSString*)route;
return routeStr;
}
+(void) destroy {
NSLog(#"CVAudioSession destroy");
// Very important - remove the listeners, or we'll crash when audio routes etc change when we're no longer on screen
OSStatus stat = AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void*)self);
NSLog(#".. AudioSessionRemovePropertyListener kAudioSessionProperty_AudioRouteChange returned %ld", stat);
stat = AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void*)self);
NSLog(#".. AudioSessionRemovePropertyListener kAudioSessionProperty_AudioInputAvailable returned %ld", stat);
AudioSessionSetActive(false); // disable audio session.
NSLog(#"AudioSession is now inactive");
}
+(BOOL) interrupted {
return _isInterrupted;
}
// Called when audio is interrupted for whatever reason. NOTE: doesn't always call the END one..
void interruptionListener( void * inClientData,
UInt32 inInterruptionState) {
if (inInterruptionState == kAudioSessionBeginInterruption)
{
_isInterrupted = YES;
NSLog(#"CVAudioSession: interruptionListener kAudioSessionBeginInterruption. Disable audio session..");
// Try just deactivating the audiosession..
OSStatus rc = AudioSessionSetActive(false);
if (rc) {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionBeginInterruption - AudioSessionSetActive(false) returned %.ld", rc);
} else {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionBeginInterruption - AudioSessionSetActive(false) ok.");
}
} else if (inInterruptionState == kAudioSessionEndInterruption) {
_isInterrupted = NO;
// Reactivate the audiosession
OSStatus rc = AudioSessionSetActive(true);
if (rc) {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionEndInterruption - AudioSessionSetActive(true) returned %.ld", rc);
} else {
NSLog(#"CVAudioSession: interruptionListener kAudioSessionEndInterruption - AudioSessionSetActive(true) ok.");
}
[[NSNotificationCenter defaultCenter] postNotificationName:kCVAudioInterruptionEnded object:(__bridge NSObject*)inClientData userInfo:nil];
}
}
// This is called when microphone or other audio devices are plugged in and out. Is on the main thread
void propListener( void * inClientData,
AudioSessionPropertyID inID,
UInt32 inDataSize,
const void * inData)
{
if (inID == kAudioSessionProperty_AudioRouteChange)
{
CFDictionaryRef routeDictionary = (CFDictionaryRef)inData;
CFNumberRef reason = (CFNumberRef)CFDictionaryGetValue(routeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
SInt32 reasonVal;
CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
if (reasonVal != kAudioSessionRouteChangeReason_CategoryChange)
{
NSLog(#"CVAudioSession: input changed");
[[NSNotificationCenter defaultCenter] postNotificationName:kCVAudioInputChangedNotification object:(__bridge NSObject*)inClientData userInfo:nil];
}
}
else if (inID == kAudioSessionProperty_AudioInputAvailable)
{
if (inDataSize == sizeof(UInt32)) {
UInt32 isAvailable = *(UInt32*)inData;
if (isAvailable == 0) {
NSLog(#"AUDIO RECORDING IS NOT AVAILABLE");
}
}
}
}
#end
I am trying to implement async tcp networking with runloop.
currently I manage to connect, but when I try to send something I get that -1 bytes have been written - but CFWriteStreamCopyError returns null.
code sample below, first function connects, second send a simple message.
any help will be appreciated, including random bug spotting (I am new to objective-c and to iphone development in general).
struct header
{
uint32_t length;
uint32_t type;
} header;
- (void) connect
{
NSLog(#"Attempting to (re)connect to %#:%d", m_host, m_port);
while(TRUE)
{
CFHostRef host = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)m_host);
if (!host)
{
NSLog(#"Error resolving host %#", m_host);
[NSThread sleepForTimeInterval:5.0];
continue;
}
CFStreamCreatePairWithSocketToCFHost(kCFAllocatorDefault, host , m_port, &m_in, &m_out);
CFRelease(host);
if (!m_in)
{
NSLog(#"Error");
}
CFStreamClientContext context = {0, self,nil,nil,nil};
if (CFReadStreamSetClient(m_in, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, networkReadEvent, &context))
{
CFReadStreamScheduleWithRunLoop(m_in, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
}
if (CFWriteStreamSetClient(m_out, kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, networkWriteEvent, &context))
{
CFWriteStreamScheduleWithRunLoop(m_out, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
}
BOOL success = CFReadStreamOpen(m_in);
CFErrorRef error = CFReadStreamCopyError(m_in);
if (!success || (error && CFErrorGetCode(error) != 0))
{
NSLog(#"Connect error %s : %d", CFErrorGetDomain(error), CFErrorGetCode(error));
[NSThread sleepForTimeInterval:5.0];
}
else
{
NSLog(#"Connected");
break;
}
}
[self startSession];
}
- (void) startSession
{
struct header hh;
hh.type = RTR_CREATE_SESSION;
hh.length = 0;
CFIndex res = CFWriteStreamWrite(self.m_out, (const UInt8*)&hh, sizeof(hh));
NSLog(#"Written %d", res);
CFErrorRef error = CFWriteStreamCopyError(self.m_out);
if (error)
{
NSLog(#"Read error %s : %d", CFErrorGetDomain(error), CFErrorGetCode(error));
CFRelease(error);
}
}
figured it out, I forgot to open the write stream as well:
CFWriteStreamOpen(m_out);