Amazon Dash Buttonをハックして自分専用に作り変えるネタが流行ってますが、もう少し簡単にできないものかと、思いまして作ってみました。
シナリオ
- 会議室のホワイトボードマーカーが書けなくなったことに気がつく
- ホワイトボードの近くの小さなBLEタグのボタンを押す
- Kintoneへレコードが登録される
- 総務部の人がそのレコードを見てホワイトボードマーカーを補充
- 総務部の人はKintoneのレコードを削除する
材料
- ボタン付きBLEタグ:iTag 833円(https://www.amazon.co.jp/dp/B017SC9U76?th=1)
- お手持ちの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のおまけ付き)