Raspberry Piを使ってビーコン発信してみた話

Raspberry Piを使ってビーコン発信してみた話

しばらく前に、Raspberry Piについて色々調べてビーコン発信機として使ってみたので(受信側はswiftで実装)、その時の話を今更ながら残しておきます。

Raspberry Piとは?

簡単に言うと、安価で入手できるマイコンボードです。その他詳細は以下。

特徴

  • Arduinoなどのマイコンボードと比べると処理性能が桁違い。
  • TCP/IPの通信、簡単な画像処理できる

機種

  • RaspberryPi3(2016年3月) 5000円くらい
    →メモリ1G、4コア1.2GHz、LAN(有線/無線)、GPIO40ピン
  • RaspberryPi zero(2015年12月) 500円くらい
    →メモリ1G、4コア900MHz、LANなし、GPIO40ピン

注意点

  • microSDがOSとか入れるストレージになっているのでSDカードが無いと動かない。
  • 電源は2.0A以上のmicroUSBケーブルが必要

OS

  • Linuxの一つのRasbianが基本。Windows10 IoT coreなどもある。
  • デフォルトで使える言語→C、C++、Java、Python、Ruby、Perl、アセンブラ etc..
    ※RasbianにはデフォルトでpythonからGPIOを扱えるライブラリがある。
    pythonの既存のライブラリ使って、HTTP通信とかもできる。

内臓のBeaconを使ってみる

Beaconとは?

Bluetoothの発信機のことです。Beaconとは、Bluetoothの中でもBLEという電力を極力使わない規格を使用したものが該当するようです。
今回は、iBeaconという、Appleが商標の規格を使ってみます。なお、対応OSはiOS7以降、Android4.3以降となっています。

作るもの

発信側であるラズパイはiBeaconで識別IDを送信。
受信側(スマホ)はBeaconとの距離を3段階(※)で表示。
※immediate(近接)、near(1m以内)、far(1m以上)

実装(発信側)

bluezインストール

まずは、Bluetoothのプロトコルスタックをインストールします。プロトコルスタックというのは一連の通信プロトコル群を実装しているモジュールのことです。
具体的なコマンドは以下。

wget https://www.kernel.org/pub/linux/bluetooth/bluez-5.9.tar.xz
tar xvJf bluez-5.9.tar.xz
cd bluez-5.9
./configure --disable-systemd --enable-library
make
make install

bluez-ibeacon(アプリ)インストール

続いて、ibeaconを発信するためのアプリケーションをインストールします。

git clone https://github.com/carsonmcdonald/bluez-ibeacon.git
bluez-ibeacon/bluez-beacon/
make

発信

以上で、設定が完了です。では、実際にBluetoothを発信してみます。

./ibeacon 200 ba6c600463454c8bb242ad651dc95821 0 0 -53

ちなみに、上記のパラメータは左から順に、送信周期、UUID、Major、Minor、信号強度です。
※UUIDはuuidgenコマンドで生成できます。なお、このUUIDはビーコン固有のUUIDではなく、ビーコン領域(リージョン)のUUIDとして使用するのが一般的です。ビーコン自体の識別はMajor値・Minor値の組み合わせで行います。

実装(受信側)

実際に記述したコードは以下の通りです。
UUID Listの箇所は上記で生成したUUIDを入力します。

import UIKit
import CoreLocation
class ViewController: UIViewController , CLLocationManagerDelegate {
var myLocationManager:CLLocationManager!
var myBeaconRegion:CLBeaconRegion!
var beaconUuids: NSMutableArray!
var beaconDetails: NSMutableArray!
let UUIDList = [
"12345678-1234-1234-1234-123456789012"
]
@IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
myLocationManager = CLLocationManager()
myLocationManager.delegate = self
myLocationManager.desiredAccuracy = kCLLocationAccuracyBest
myLocationManager.distanceFilter = 1
let status = CLLocationManager.authorizationStatus()
print("CLAuthorizedStatus: \(status.rawValue)");
if(status == .notDetermined) {
myLocationManager.requestAlwaysAuthorization()
}
beaconUuids = NSMutableArray()
beaconDetails = NSMutableArray()
}
private func startMyMonitoring() {
for i in 0 ..< UUIDList.count { let uuid: NSUUID! = NSUUID(uuidString: "\(UUIDList[i].lowercased())") let identifierStr: String = "abcde\(i)" myBeaconRegion = CLBeaconRegion(proximityUUID: uuid as UUID, identifier: identifierStr) myBeaconRegion.notifyEntryStateOnDisplay = false myBeaconRegion.notifyOnEntry = true myBeaconRegion.notifyOnExit = true myLocationManager.startMonitoring(for: myBeaconRegion) } } func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { print("didChangeAuthorizationStatus"); switch (status) { case .notDetermined: print("not determined") break case .restricted: print("restricted") break case .denied: print("denied") break case .authorizedAlways: print("authorizedAlways") startMyMonitoring() break case .authorizedWhenInUse: print("authorizedWhenInUse") startMyMonitoring() break } } func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) { manager.requestState(for: region); } func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) { switch (state) { case .inside: print("iBeacon inside"); manager.startRangingBeacons(in: region as! CLBeaconRegion) break; case .outside: print("iBeacon outside") break; case .unknown: print("iBeacon unknown") break; } } func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) { beaconUuids = NSMutableArray() beaconDetails = NSMutableArray() if(beacons.count > 0){
for i in 0 ..< beacons.count {
let beacon = beacons[i]
let beaconUUID = beacon.proximityUUID;
let minorID = beacon.minor;
let majorID = beacon.major;
let rssi = beacon.rssi;
var proximity = ""
switch (beacon.proximity) {
case CLProximity.unknown :
print("Proximity: Unknown");
proximity = "Unknown"
break
case CLProximity.far:
print("Proximity: Far");
proximity = "Far"
break
case CLProximity.near:
print("Proximity: Near");
proximity = "Near"
break
case CLProximity.immediate:
print("Proximity: Immediate");
proximity = "Immediate"
break
}
beaconUuids.add(beaconUUID.uuidString)
var myBeaconDetails = "Major: \(majorID) "
myBeaconDetails += "Minor: \(minorID) "
myBeaconDetails += "Proximity:\(proximity) "
myBeaconDetails += "RSSI:\(rssi)"
print(myBeaconDetails)
beaconDetails.add(myBeaconDetails)
myLabel.text = proximity
}
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("didEnterRegion: iBeacon found");
manager.startRangingBeacons(in: region as! CLBeaconRegion)
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("didExitRegion: iBeacon lost");
manager.stopRangingBeacons(in: region as! CLBeaconRegion)
}
}

まとめ

今回は、ラズパイの機能のほんの一部ではありますがBeaconの発信機として使ってみました。Beaconを使いこなせるようになれば、出退勤自動管理や店舗クーポンのプッシュ通知など、応用先がたくさんありそうなので引き続き遊んでみたいと思います。