23
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Bluetooth Low EnergyAdvent Calendar 2014

Day 24

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

Last updated at Posted at 2014-12-24

こんにちは、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を制御して遊んでみてください。

23
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?