Edited at

スマートLED電球をiOSから制御してクリスマスな雰囲気を演出する

More than 3 years have passed since last update.

こんにちは、LEDが大好きなkentya6です。

今回は、NexturnというLED電球をiOSから制御してみようと思います。

Nexturnは、下の写真のやつです。秋葉原のドスパラで買ってきました。

Nexturn

Nexturnは、見た感じ普通の電球ですが、BLE通信で様々な色に変化させることができます。

値段も3000円ちょっとなので、某スマートLED電球よりも安くスマートLED電球を手に入れられました。

ではNexturnを使って、イルミネーションっぽいクリスマスな雰囲気を感じることができるiOSアプリを作ってみます。


iOSでBLE通信をする流れ

今回は、大雑把ですが以下のようにBLE通信を行います。


  1. アプリ起動

  2. Nexturnを検索

  3. 接続

  4. Serviceを取得

  5. Characteristicを取得

  6. ユーザが押したボタンに対応するCharacteristicを指定してデータ通信し、LEDを点灯

Nexturnと接続するために、まずNexturnのデバイス情報を確認します。


Nexturnのデバイス情報を確認する

今回は、LightBlueというアプリを使用して、Nexturnのデバイス情報を確認してみます。

Nexturnの電源を入れ、LightBlueを起動します。すると、なにやらNexturnらしきデバイスが検出されました。

Nexturn検出

このNexturnの部分をタップすると、デバイス情報が次の画像のように出てきます。

Nexturnデバイス情報

画像に表示されているUUIDを使用して、iOSデバイスとNexturnを接続します。

デバイス情報をどんどん眺めていくと、何やらLEDの色を変化させることができそうな箇所が見つかりました。

NexturnLED情報

これらの情報を使ってBLE通信をすれば点灯させられそうです!

というわけで、実際にこれらの情報を使ってアプリケーションを開発します。


アプリ開発

Nexturnとの接続に必要な情報と、LEDを点灯させるために必要な情報が分かったところで、これから実際にNexturnを制御するiOSアプリケーションを開発してみます。

今回開発するアプリケーションの機能は以下のようなものとしました。


  • 赤、青、緑、白それぞれの色ごとに独立して点灯

  • 全色同時に点灯

  • 消灯

それぞれの色ごと、機能ごとにボタンを割り当てるので、必要なボタンは6個になります。

また、毎回同じ明るさだとちょっと単純過ぎるので、ボタンを押す度に255段階の明るさがランダムで決定されるようにします。

それでは、先ほど述べたBLE通信の流れに沿って、実際にNexturnを制御するコードを書いてみます。


プログラム構成

今回は、主に3つのクラスでアプリケーションを製作しました。

それでは、実際に書いたコードの説明を簡単に行いたいと思います。


1. ViewController

ViewControllerでは、以下の処理を行っています。


  1. ボタンを押したときの処理を記述

  2. CentralManagerインスタンスを作成し、Nexturnと通信する準備を開始


ViewController.swift

import UIKit

class ViewController: UIViewController {
@IBOutlet private weak var redButton: UIButton!
@IBOutlet private weak var greenButton: UIButton!
@IBOutlet private weak var blueButton: UIButton!
@IBOutlet private weak var whiteButton: UIButton!
@IBOutlet private weak var randomButton: UIButton!
@IBOutlet private weak var offButton: UIButton!
var centralManager = CentralManager.alloc()

override func viewDidLoad() {
super.viewDidLoad()
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
centralManager = CentralManager(delegate: self.centralManager, queue: queue, options: nil)
}

// 押されたボタンに対応したデータを渡す
@IBAction func ledButtonTapped(sender: AnyObject) {
centralManager.ledButtonTapped(sender.tag)
}
}



2. CentralManager

CentralManagerでは、以下の処理を行っています。


  1. BLEデバイス(ペリフェラル側)を検索

  2. 検索したデバイスがNexturnだった場合、接続を開始

  3. Nexturnに接続後、Serviceを検索

  4. 押されたボタン情報をNexturnに通知


CentralManager.swift

import Foundation

import CoreBluetooth

class CentralManager: CBCentralManager, CBCentralManagerDelegate {
private var nexturnObject = NexturnObject()

override init(delegate: CBCentralManagerDelegate!, queue: dispatch_queue_t!, options: [NSObject : AnyObject]!) {
super.init(delegate: delegate, queue: queue, options: options)
self.delegate = self
}

// MARK: - CBCentralManager Delegate Method
// CBCentralManagerのステータス更新後に呼ばれる
func centralManagerDidUpdateState(central: CBCentralManager!) {
switch central.state {
case .PoweredOn:
let options = ["CBCentralManagerScanOptionAllowDuplicatesKey" : true]
scanForPeripheralsWithServices(nil, options: options)
default:
break
}
}

// ペリフェラル発見時に呼ばれる
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
switch peripheral.name {
case nexturnObject.kNexturnName:
peripheral.delegate = nexturnObject
nexturnObject.peripheral = peripheral
connectPeripheral(nexturnObject.peripheral, options: nil)
default:
break
}
}

// ペリフェラルと接続後に呼ばれる
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
switch peripheral.name {
case nexturnObject.kNexturnName:
let UUID = CBUUID(string: nexturnObject.kNexturnLEDServiceUUID)
nexturnObject.peripheral?.discoverServices([UUID])
stopScan()
default:
break
}
}

// MARK: - Call from IBAction
// 押されたボタンに対応したデータを渡す
func ledButtonTapped(tag: NSInteger) {
nexturnObject.ledButtonTapped(tag)
}
}



3. NexturnObject

NexturnObjectでは、以下の処理を行っています。


  1. NexturnのServiceを取得

  2. NexturnのCharacteristicを取得

  3. ボタンを押されたとき、各ボタンに対応するデータをNexturnに送信

各Characteristicに対してデータを送信することで、対応する色が光るという仕組みになっています。

指定するCharacteristicは、前述したデバイス情報で確認した「Red」や「Blue」といったプロパティです。


NexturnObject.swift

import Foundation

import CoreBluetooth

class NexturnObject: NSObject, CBPeripheralDelegate {
// MARK: - Please Replace This UUID to Your's Nexturn UUID
let kNexturnUUID = "F71FCA69-78A5-B654-9667-F27BF8E5CAC9"
let kNexturnName = "Nexturn"
let kNexturnLEDServiceUUID = "FFE0"
var peripheral: CBPeripheral?
private var characteristicArray = [CBCharacteristic]()

private enum ledButtonTag: Int {
case Red, Green, Blue, White, Random, Off

private var type: NSData {
get {
switch self {
case .Red, .Green, .Blue, .White:
return createLedData(UInt8(arc4random_uniform(255)))
case .Random:
return createLedData(arc4random_uniform(UInt32.max))
case .Off:
return createLedData(UInt32(0))
}
}
}

// LEDデータを作成
func createLedData(hexData: UInt32) -> NSData {
let red = Byte((hexData & 0xFF000000) >> 24)
let green = Byte((hexData & 0x00FF0000) >> 16)
let blue = Byte((hexData & 0x0000FF00) >> 8)
let white = Byte(hexData & 0x000000FF)
var data = [red, green, blue, white]

return NSData(bytes: &data, length: 4)
}

// LEDデータを作成
func createLedData(hexData: UInt8) -> NSData {
var data = Byte(hexData)

return NSData(bytes: &data, length: 1)
}
}

// 押されたボタンに対応したデータをNexturnに送信
func ledButtonTapped(tag: NSInteger) {
let buttonTag = ledButtonTag(rawValue: tag)!

switch buttonTag {
case .Red, .Green, .Blue, .White:
self.peripheral?.writeValue(buttonTag.type, forCharacteristic: characteristicArray[tag], type: .WithResponse)
case .Random, .Off:
self.peripheral?.writeValue(buttonTag.type, forCharacteristic: characteristicArray[4], type: .WithResponse)
}
}

// MARK: - CBPeripheralDelegate
// Service発見時に呼ばれる
func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
for service in peripheral.services {
self.peripheral?.discoverCharacteristics(nil, forService: service as CBService)
}
}

// Characteristic発見時に呼ばれる
func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
for characteristic in service.characteristics {
self.characteristicArray.append(characteristic as CBCharacteristic)
}
}
}



デモ

今回開発したアプリケーションは、このような画面にしました。

アプリケーション画面

それぞれのボタンを押すと、ボタンに対応した色にNexturnが光ってくれます。

デモはこんな感じです。

demo2.gif

押したボタンに応じてNexturnの色が変わっていますね!

今日はクリスマス・イブということで、クリスマスツリーを隣に置いただけですがクリスマス感を演出してみました。

今回作成したアプリケーションはGitHubで公開していますので、みなさん自由にNexturnを制御して遊んでみてください。