Official iOS SDK for Nova Bluetooth iPhone flash.
This is version 2 of the SDK which has undergone significant changes from version 1. If you are migrating, see V2 migration guide.
- Uses BluetoothLE to discover nearby Nova flashes and communicate with them
- Allows control of multiple flashes (each can be uniquely identified)
- Monitor signal strength of each flash (e.g. which is closest to the phone)
- Supports both automatic and manual connect modes
- Manual connect: clients can explore Nova flashes in range, signal strength and explicitly connect and disconnect as needed
- Automatic connect: SDK will automatically connect to closest flash (single or multiple flashes) or a previously remembered flash
- Provides seamless connection user experience from app - the user does not need to explictly pair a flash in Bluetooth settings
- Allow connected flashes to be lit and unlit with user defined brightness and color temperature
Just some ideas...
- Integrate with a custom camera app to provide
- Create a flashlight app
- Morse code beacon
- Use Nova devices as beacons in a location aware app
- Some sweet art
A hello world app is provided that simply connects to the closest flash and toggles the light on and off every second.
Use Cocoapods and add to your Podfile:
pod 'NovaSDK', '~> 2.0.0'
The library is small and its only dependency is the system CoreBluetooth framework.
-
NVFlashService: The core service used to discover nearby flashes. Clients can iterate through available nearby flashes, set rules for auto-connection and pause/resume scanning when the app moves to the background. -
NVFlashServiceDelegate: Optional delegate protocol that can be implemented by client to receive callbacks when new flashes are discovered, connected or disconnected. -
NVFlash: Each discovered flash. Clients can explicitly connect/disconnect, monitor signal strength and connectivity status, and control the light emitted from each flash. -
NVFlashSettings: A configuration used when telling the flash to light up. Includes warm LED brightness, cool LED brightness and a timeout to automatically shutdown the flash. -
NVFlashStatus: Enum type describing status of a flash.Available: in range and available to be connected toUnavailable: no longer in rangeConnecting: connection in progressReady: connected and ready for useBusy: connected but currently processing an existing command
Initialize the Nova flash service. A good time to do this is in applicationDidFinishLaunching:.
// Objective-C
// Setup. Do this when app starts.
// A good time is applicationDidFinishLaunching:
NVFlashService *flashService = [NVFlashService new];
// Optionally, set a delegate implementing NVFlashServiceDelegate to be
// notified of flash discovery/connection events.
flashService.delegate = self;// Swift
// Setup. Do this when app starts.
// A good time is applicationDidFinishLaunching:
let flashService = NVFlashService()
// Optionally, set a delegate implementing NVFlashServiceDelegate to be
// notified of flash discovery/connection events.
flashService.delegate = selfIn applicationDidBecomeActive: you should enable the service, which
activates the Bluetooth radio and begins scanning:
// Objective-C
[flashService enable];// Swift
flashService.enable()In applicationWillResignActive: you should disable the service, which
shuts down the BluetoothLE scanning, conserving the battery life of
both the iPhone and Nova:
// Objective-C
[flashService disable];// Swift
flashService.disable()While the flash service is enabled it will automatically perform Bluetooth scans to discover nearby flashes.
There are two options for knowing which flashes are in range:
- Periodically at the contents of
flashService.flashesorflashService.connectedFlashesarray - Receive a callback by implementing the
NVFlashServiceDelegateprotocol
The NVFlashService.flashes array contains NVFlash objects and can be queried at any time and returns all flashes that have been discovered.
The NVFlashService.connectedFlashes is similar except it contains the subset of flashes that are currently connected.
// Objective-C
for (id<NVFlash> flash in flashService.flashes) { // alternatively flashService.connectedFlashes
NSLog(@"flash: identifier=%@, status=%@, signalStrength=%f",
flash.identifier, // unique ID assigned to each flash
NVFlashStatusString(flash.status), // connectivity status
flash.signalStrength); // value from 0.0 (weakest) to 1.0 (strongest)
}// Swift
for flash in flashService.flashes as [NVFlash] { // alternatively flashService.connectedFlashes
NSLog("flash: identifier=%@, status=%@, signalStrength=%f",
flash.identifier, // unique ID assigned to each flash
NVFlashStatusString(flash.status), // connectivity status
flash.signalStrength) // value from 0.0 (weakest) to 1.0 (strongest)
}Optionally, a client may implement the NVFlashServiceDelegate protocol and set it as the flashService.delegate to receive callbacks as discovery and connection events occur.
// Objective-C
// Implement NVFlashServiceDelegate protocol. All methods are optional.
- (void) flashServiceAddedFlash:(id<NVFlash>)flash {
NSLog(@"added flash %@", flash.identifier);
}
- (void) flashServiceRemovedFlash:(id<NVFlash>)flash {
NSLog(@"removed flash %@", flash.identifier);
}
- (void) flashServiceConnectedFlash:(id<NVFlash>)flash {
NSLog(@"connected flash %@", flash.identifier);
}
- (void) flashServiceDisconnectedFlash:(id<NVFlash>)flash {
NSLog(@"disconnected flash %@", flash.identifier);
}
// Swift
// Implement NVFlashServiceDelegate protocol. All methods are optional.
func flashServiceAddedFlash(flash: NVFlash) {
NSLog("added flash %@", flash.identifier)
}
func flashServiceRemovedFlash(flash: NVFlash) {
NSLog("removed flash %@", flash.identifier)
}
func flashServiceConnectedFlash(flash: NVFlash) {
NSLog("connected flash %@", flash.identifier)
}
func flashServiceDisconnectedFlash(flash: NVFlash) {
NSLog("disconnected flash %@", flash.identifier)
}There are two ways to connect to flashes:
- Manually call
connect/disconnectonNVFlashinstances of interest. This gives the most control. - Enable
autoConnectand allow the flash service to automatically connect to the closest Nova for you. This is the simplest.
This is the recommend approach as it lets the flash service do the hard work for you.
When autoConnect is enabled,the flash service will periodically attempt to connect to a suitable Nova (or multiple Novas). Clients will know when a Nova is connected by looking at the flashService.connectedFlashes array or handling flashServiceConnectedFlash:/flashServiceDisconnectedFlash: callbacks.
// Objective-C
// call this after initializing NVFlashService
flashService.autoConnect = YES;// Swift
// call this after initializing NVFlashService
flashService.autoConnect = trueThe default auto connect rules will attempt to connect to:
- just 1 Nova at a time
- the Nova with the strongest signal strength
- from the set all discovered Novas with signal strength >0.1 (10%)
The above rules can be changed. For example:
// Objective-C
flashService.autoConnectMaxFlashes = 4; // connect to the 4 closest Novas
flashService.autoConnectMinSignalStrength = 0.5; // require signal strength >=50%// Swift
flashService.autoConnectMaxFlashes = 4 // connect to the 4 closest Novas
flashService.autoConnectMinSignalStrength = 0.5 // require signal strength >=50%In some cases clients may wish to remember a flash and only reconnect to that same flash (or flashes) in the future.
To do this, clients should store a list of NVFlash.identifier values. These are strings that could be persisted locally. Each string is unique for a given flash.
Upon reconnecting:
// Objective-C
// Only auto connect to specific flashes.
// Identifiers are strings previously obtained from NVFlash.identifier
flashService.autoConnectWhiteList = @[identifier1, identifier2];// Swift
// Only auto connect to specific flashes.
// Identifiers are strings previously obtained from NVFlash.identifier
flashService.autoConnectWhiteList = [identifier1, identifier2]For clients that want more control over which Novas to connect to, the individual NVFlash instances can be iterated over (see above) and explicitly connected/disconnected at appropriate times.
Important: Do not attempt to manually connect to flashes if autoConnect is enabled. If in doubt use autoConnect instead (see above) - it's much simpler.
// Objective-C
id<NVFlash> flash = /* whichever flash instance */;
// to connect
[flash connect];
// to disconnect
[flash disconnect];// Objective-C
let flash: NVFlash = /* whichever flash instance */
// to connect
flash.connect()
// to disconnect
flash.disconnect()Nova contains banks of LEDs, half of which have a warm white tint and the other half have a cool white tint. The brightness of both banks can be controlled individually for different variations of brightness and color temperature.
The NVFlashSettings object holds the desired brightness/temperature settings. The fields are:
warm: Brightness of warm LEDs from 0 (off) to 255 (full brightness)cool: Brightness of cool LEDs from 0 (off) to 255 (full brightness)
For convenience, there are presets used for commonly used settings:
// Objective-C
NSFlashSettings *settings = [NFFlashSettings bright];
// or...
NSFlashSettings *settings = [NFFlashSettings gentle];
NSFlashSettings *settings = [NFFlashSettings neutral];
NSFlashSettings *settings = [NFFlashSettings warm];
NSFlashSettings *settings = [NFFlashSettings customWarm:123, cool:201];// Swift
let settings = NFFlashSettings.bright()
// or...
let settings = NFFlashSettings.gentle()
let settings = NFFlashSettings.neutral()
let settings = NFFlashSettings.warm()
let settings = NFFlashSettings.custom()
let settings = NFFlashSettings.customWarm(123, cool: 201)Once the settings (see above) have been defined, use beginFlash to send a message to the flash to activate the LEDs.
Important: Due to the nature of BluetoothLE, there is a slight delay between making this call and the LEDs actually lighting up. If timing is important, you can pass a callback that will be called when the flash has acknowledged that the LEDs are on.
// Objective-C
id<NVFlash> flash = /* whichever flash you want to control */;
// Fire and forget. Flash typically lights up within 100-200 milliseconds.
[flash beginFlash:settings];
// Alternatively, fire and pass a callback to know when flash has responded
[flash beginFlash:settings withCallback:^(BOOL success) {
if (success) {
NSLog(@"Flash is now lit up");
} else {
NSLog(@"Flash failed to light. Try charging it.");
}
}];// Swift
let flash: NVFlash = // whichever flash you want to control
// Fire and forget. Flash typically lights up within 100-200 milliseconds.
flash.beginFlash(settings)
// Alternatively, fire and pass a callback to know when flash has responded
flash.beginFlash(settings) {success in
if (success) {
NSLog("Flash is now lit up")
} else {
NSLog("Flash failed to light. Try charging it.")
}
}To turn off the lights, call endFlash.
// Objective-C
[flash.endFlash];// Swift
flash.endFlash()Like beginFlash, you may also pass a callback to endFlash to get notified when the flash has acknowledged that it's now off.
The Nova hardware includes a timeout feature to automatically turn the flash off after a certain amount of time. This is a safety mechanism to ensure that if an app crashes or other unexpected events occur on the phone, the flash will not remain on and drain all the battery.
The default timeout is 10 seconds. This is approximate because Nova does not contain a high resolution timer. In reality it will be accurate with about 20% tolerance.
To change the timeout, you can modify the NVFlashSettings.
// Objective-C
NVFlashSettings *settings = [[NVFlashSettings warm] flashSettingsWithTimeout:20000)]; // in milliseconds// Swift
let settings = NVFlashSettings.warm().flashSettingsWithTimeout(20000) // in millisecondsHere's that hello world app again. It demonstrates initializing the service, auto connecting to the closest Nova, listening to connect/disconnect events and controlling the light.
If you are integrating Nova into a camera app, here is the basic flow of events for taking a photo.
Triggering a flash should only be attempted when flashService.status == NVFlashServiceReady.
// Objective-C
// Step 1: Tell flash to activate.
[flash beginFlash:settings withCallback:^ (BOOL success) {
// Step 2: This callback is called when the flash has acknowledged that
// it is lit.
// Check whether flash activated succesfully.
if (success) {
// Step 3: Tell camera to take photo.
[myCameraAbstraction takePhotoWithCallback:(^ {
// Step 4: When photo has been captured, turn the flash off.
[flashService:endFlashWithCallback:(^ {
// Step 5: Done. Ready to take another photo.
})];
})];
} else {
// Error: flash could not be triggered.
}
}];// Swift
// Step 1: Tell flash to activate.
flash.beginFlash(settings) { success in
// Step 2: This callback is called when the flash has acknowledged that
// it is lit.
// Check whether flash activated succesfully.
if (success) {
// Step 3: Tell camera to take photo.
myCameraAbstraction.takePhotoWithCallback() {
// Step 4: When photo has been captured, turn the flash off.
flashService.endFlash() { success in
// Step 5: Done. Ready to take another photo.
}
}
} else {
// Error: flash could not be triggered.
}
}Standard key-value observing can be used on the NVFlash instance fields (e.g. status, signalStrength, lit) to monitor when something about the flash changes. It is recommend to add/remove observers in either flashServiceFlashAdded:/flashServiceFlashRemoved: or flashServiceConnectedFlash:/flashServiceDisconnectedFlash: delegate callbacks.
This library should only ever be called from the main thread. All calls are fast and non-blocking.
For short bursts of light, it's often simpler to use a timeout on NVFlashSettings instead of explicitly calling endFlash.
The Nova hardware and software was created by Joe Walnes. For help email hello@novaphotos.com or raise a GitHub issue.
- www.novaphotos.com
- Nova Camera App on AppStore (uses this SDK)
- Where to buy (including Apple Store, Amazon, Photojojo...)
- @joewalnes on GitHub, [Twitter](https://twitter.com/joewalnes], Medium
- More Nova open source repositories (including apps, Android SDK and protocol documentation)
Photo by Photojojo
