Posted at

BLEタグとiPhoneでつくるオレオレAmazon Dash Button

More than 1 year has passed since last update.

Amazon Dash Buttonをハックして自分専用に作り変えるネタが流行ってますが、もう少し簡単にできないものかと、思いまして作ってみました。


シナリオ


  1. 会議室のホワイトボードマーカーが書けなくなったことに気がつく

  2. ホワイトボードの近くの小さなBLEタグのボタンを押す

  3. Kintoneへレコードが登録される

  4. 総務部の人がそのレコードを見てホワイトボードマーカーを補充

  5. 総務部の人はKintoneのレコードを削除する


材料


  1. ボタン付きBLEタグ:iTag 833円(https://www.amazon.co.jp/dp/B017SC9U76?th=1)

  2. お手持ちのiPhone


原理

BLE通信でボタン付きBLEタグのボタンクリックイベントを拾うだけ


BLEのServiceやCharacteristicはどうやって調べたの?

下記のアプリを使うとあっという間に分かります。

BLExplr

https://itunes.apple.com/jp/app/blexplr/id524018027?mt=8


コード

コンセプト通り機能するかを確認するレベルの内容ですが、こんな感じです。


ViewController.swift

import UIKit

import CoreBluetooth
import Alamofire

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {

@IBOutlet weak var label: UILabel!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var centralManager: CBCentralManager!
var tag: CBPeripheral!
let kintoneBaseURL = "https://<Kintoneのドメイン>/k/v1"
let kintoneAppID = /* <KintoneアプリのID> */
let kintoneToken = /* <KIntoneアプリのアクセストークン> */
let blueTagUUID = /* <iTagのUUID> */
let blueTagServiceID = /* <iTagのService ID> */
let blueTagCharID = /* <iTagのCharacteristic ID> */

override func viewDidLoad() {
super.viewDidLoad()
self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
label.isHidden = true
activityIndicator.startAnimating()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

// CentralManager状態の受信
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch (central.state) {
case .poweredOff:
NSLog("BLE PoweredOff")
case .poweredOn:
NSLog("BLE PoweredOn")
central.scanForPeripherals(withServices: nil, options: nil)
case .resetting:
NSLog("BLE Resetting")
case .unauthorized:
NSLog("BLE Unauthorized")
case .unknown:
NSLog("BLE Unknown")
case .unsupported:
NSLog("BLE Unsupported")
}
}

// Peripheral探索結果の受信と接続
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
NSLog("name: \(peripheral.name), UUID: \(peripheral.identifier.uuidString), adData: \(advertisementData), RSSI: \(RSSI)")

if(peripheral.identifier.uuidString == blueTagUUID){
self.centralManager.stopScan()
self.tag = peripheral
self.centralManager.connect(self.tag, options: nil)
}
}

// Peripheralへの接続結果の受信(成功時)
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
NSLog("connection success: \(peripheral.identifier.uuidString)")
peripheral.delegate = self

// サービスを探索
let UUID = CBUUID(string: blueTagServiceID)
peripheral.discoverServices([UUID])
}

// Peripheralへの接続結果の受信(失敗時)
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
NSLog("connection failed: \(peripheral.identifier.uuidString)")
}

// Service探索結果の受信
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if (error != nil) {
NSLog("error: \(error)")
return
}

for service in peripheral.services!
{
NSLog("found service: \(service)")

//Characterristicを探索
let UUID = CBUUID(string: blueTagCharID)
peripheral.discoverCharacteristics([UUID], for:service as CBService)
}
}

// Characterristic探索結果の受信
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
if (error != nil) {
NSLog("error: \(error)")
return
}

for characteristic in service.characteristics! {
NSLog("found characteristic: \(characteristic.uuid)")
if(characteristic.uuid.uuidString == blueTagCharID){
peripheral.setNotifyValue(true, for: characteristic)
}
}
}

// データ更新通知受け取り開始/停止結果を取得する
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
NSLog("error: \(error)")
} else {
NSLog("isNotifying: \(characteristic.isNotifying)")
activityIndicator.hidesWhenStopped = true
activityIndicator.stopAnimating()
label.isHidden = false
}
}

// データ更新通知を受け取る = ボタンが押された際に発生するイベント
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
NSLog("error: \(error)")
return
}

NSLog("value updated. UUID: \(characteristic.uuid.uuidString), value: \(characteristic.value)")

if(label.text == "Push me!"){
label.text = "Hello!"
}else{
label.text = "Push me!"
}
sendKintone()
}

func sendKintone(){
let headers: HTTPHeaders = [
"X-Cybozu-API-Token":kintoneToken
]

let params: Parameters = [
"app": kintoneAppID,
"record": [
"アイテム" : ["value" : "会議室A:ホワイトボードマーカー(黒)"],
"数量" : ["value" : "2"],
]
]

_ = Alamofire.request("\(kintoneBaseURL)/record.json", method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON{ resp in

switch resp.result {
case .success:
NSLog("Sent")
case .failure(let error):
NSLog(error as! String)
}
}
}
}



参考にした記事

[Swift][初心者]BLE接続するための情報まとめ

Core Bluetooth with Swift (ObjCのおまけ付き)