【2020年2月追記】※本文内で紹介している BLEAD-TSH は 現在販売停止しております。各種センサーとビーコンを組み合わせたソリューションをご希望の場合は、個別ににお問合せ下さい。
開発部のなーさんです。
前回の記事を書いて以降、暇なときにはイニシャルがTSHになる地名を探しています。たとえば千葉県だけでも、袖ヶ浦市とか匝瑳市(←読み方難しい)とかちらほらありました。
さて、BLEAD-TSHで遊んでみようと思い立ち、前回まずはTSHとは何か?について書いてみました。今回は、TSHのアドバタイズを受信するごく簡単なサンプルを作ってみたいと思います。
ターゲット OSはiOSで、開発言語はSwiftです。
下記は2019年5月現在の情報で執筆しています。
文中、画像中で使用されている商標等はそれぞれの企業、団体に帰属します。
コンセプト実証用のソースコードのため簡略化等された箇所があります。
作成するサンプルについて
さっそく作っていこうと思います。
当初、気負ってあれこれ機能をつけていたらブログ向きの分量ではなくなってしまいました。そこで初心に帰って下記のような最低限のサンプルにしました。
- TSHのスキャンを行うクラスを定義
- スキャンのOn/OffはViewControllerからSwitchオブジェクトで行う
- 受信したTSHのデータはprint文で出力
- Bluetoothは常にオンになっている想定
したがって、受信データから加速度などを取得したいときは、前回に掲載したフォーマット表などを参考にして切り出してください。また、iOSの制約などもあって、フォアグラウンド動作が前提となっています。
使用した環境は
●開発環境
iMac2017(macOS 10.14)
Xcode 10.2.1 + Swift5.0.1
●テスト環境
iPad Air2 (iOS12.0.1)
BLEAD-TSH 1台
サンプルプロジェクトの準備
とりあえず列挙します。
新規プロジェクトをSingle View AppでTSHScanSampleという名前で作成します。
General – Linked Frameworks and LibrariesでCoreBluetooth.frameworkを登録します。
TSHのスキャンを行うクラスとして、TSHScanner.swiftというファイルを追加します。
画面は下記のようにしました。最低Switchオブジェクトが一つだけあればOKです。ラベルはお好みで!
以降ソースコードを示します。
ソース:TSHScanner.swift
実用上はこのクラスはシングルトンにした方がよいかな〜と思います。
また、CBCentralManagerのインスタンス生成時とスキャン開始時に設定オプションを指定できますが、今回は使いどころがありそうな2つのキーを使いました。最初は両方ともfalseにしていますので、設定を変えて動作の違いを感じてください。キーは他にもあるので検索してみてください。
今回オプションに設定したキー
キーの名称 | 内容 |
CBCentralManagerOptionShowPowerAlertKey | Bluetoothがオフのときに通知をだすか否か |
CBCentralManagerScanOptionAllowDuplicatesKey | スキャン中、同じデータを受信するたびに通知するか否か |
import Foundation | |
import CoreBluetooth | |
final class TSHScanner: NSObject, CBCentralManagerDelegate { | |
/** 定数 */ | |
private let powerAlertKey = false // Bluetoothがオフのときに通知を出すか | |
private let allowDuplicateKey = false // 同じデバイスの電波を何度も通知するか | |
// CBCentralManager | |
private var cbCentralManager: CBCentralManager! | |
//*********************** | |
// MARK: - メソッド Internal | |
//*********************** | |
/** スキャン開始 */ | |
func startScan() { | |
// 一度停止を試みる | |
stopScan() | |
// スキャンの準備 | |
// delegate:デリゲートを記述した場所 | |
// queue:別スレッドでスキャンさせたいときに設定 | |
// options:各種設定 | |
cbCentralManager = CBCentralManager( | |
delegate: self, | |
queue: nil, | |
options: [CBCentralManagerOptionShowPowerAlertKey: powerAlertKey] | |
) | |
} | |
/** スキャン停止 */ | |
func stopScan() { | |
guard cbCentralManager != nil else { | |
return | |
} | |
// 停止 | |
if cbCentralManager.isScanning { | |
cbCentralManager.stopScan() | |
} | |
} | |
//*********************** | |
// MARK: - デリゲート | |
//*********************** | |
/** Bluetoothの状態通知 */ | |
func centralManagerDidUpdateState(_ central: CBCentralManager) { | |
switch central.state { | |
case .poweredOff: | |
print("Bluetoothがオフ") | |
case .poweredOn: | |
// スキャン開始 | |
// withServices:ここに設定したServiceUUIDのみ通知されます(TSHでは使いません) | |
// options:スキャンの設定を変えられます | |
cbCentralManager.scanForPeripherals( | |
withServices: nil, | |
options: [CBCentralManagerScanOptionAllowDuplicatesKey: allowDuplicateKey] | |
) | |
print("スキャン開始") | |
case .unknown: break | |
case .resetting: break | |
case .unsupported: break | |
case .unauthorized: break | |
@unknown default: | |
fatalError("fatal error") | |
} | |
} | |
/** ペリフェラルが見つかったとき */ | |
func centralManager(_ central: CBCentralManager, | |
didDiscover peripheral: CBPeripheral, | |
advertisementData: [String : Any], | |
rssi RSSI: NSNumber) { | |
// nilなら処理終了 | |
guard | |
// TSHデータの取得を試みる | |
let manuData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data, | |
let periName = peripheral.name | |
else { | |
return | |
} | |
// BLEADじゃなければ処理終了 | |
guard periName == "BLEAD" else { | |
return | |
} | |
// フォーマットから外れるときは処理終了 | |
guard | |
manuData.count == 26, | |
(manuData[0] == 0xCE && manuData[1] == 0x01), | |
(manuData[2] == 0x53 && manuData[3] == 0x04) | |
else { | |
return | |
} | |
//*********************** | |
// MARK: ここでTSHのデータを出力する | |
//*********************** | |
print("TSHデータ:", dataToString(data: manuData), Int(truncating: RSSI)) | |
} | |
//*********************** | |
// MARK: - メソッド Private | |
//*********************** | |
/** Dataを文字列に変換 */ | |
private func dataToString(data: Data) -> String { | |
return data.map{ String(format: "%.2hhx", $0) }.joined() | |
} | |
} | |
ソース:ViewController.swift
スイッチオンオフによるスキャナークラスの制御のみのシンプルな構成です。実はAppDelegate.swiftから制御しようと思ったら、あまりにも画面が地味になりすぎるので、スイッチ制御にしました。
import UIKit | |
class ViewController: UIViewController { | |
// BLEAD-TSHスキャナークラス | |
private let tshScanner = TSHScanner() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view. | |
} | |
/** スイッチでオンオフ */ | |
@IBAction func switchOnOff(_ sender: UISwitch) { | |
if sender.isOn { | |
tshScanner.startScan() | |
}else{ | |
tshScanner.stopScan() | |
} | |
} | |
} |
なお、「switchOnOff」は、SwitchのTouch Up Inseideの操作に紐づけて作成してください。

動作確認
ここまでできたらアプリを実行してみましょう!!
シミュレータでは動作しないので、スマホ実機にインストールしてください。
うまく動けばXcodeに下記のようなログが出力されます。受信したデータとRSSI値をただひたすら文字列で列挙します。
TSHのトリセツやフォーマット表、BLEAD-MC-Lightアプリの出力と見比べてみてください。
今回の記事は以上となります。
このソースコードは最低限の機能しかありません。みなさんのアイディアでいろいろ改造して、TSHとともに遊んでみてください!!
次回の予定
次回はBLEAD-TSHを使ったアプリの作例をご紹介します。
・・・さて、
もう突っ込みたくて仕方ない方もいらっしゃるかもしれません。
千葉県はChibaなので、TSH関係ないです〜
それでは!
以上、ご質問・ご指摘等ありましたら、弊社までお気軽にお問い合わせください。