Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード

2015-12-12

Googleリポジトリのコントリビュータになりたくて19日間粘った話

「Google リポジトリのコントリビュータです」って言えたらかっこいいなぁというミーハー精神からプルリクを送り、却下されそうになりつつも粘ってマージしてもらって CONTRIBUTORS に名前も入れてもらえた(まだ世界で9人しかいない!)、という経緯について、嬉しいので書いておこうと思います。


google/eddystone リポジトリ

Google が提唱しているビーコン規格、「Eddystone」というものがありまして、これは Apple の iBeacon の対抗規格となるわけですが、その売りのひとつとして、「オープン」である、という点があります。


そんなわけで、Google社のリポジトリ google/eddystone にて、その規格やサンプルコード、関連ツール等が公開されています。


最初のプルリク

先日の海外案件にて iBeacon をいじっていたときに、とある「クローズドの壁」にぶつかり、Eddybeacon も試してみるかー、という話になったのが発端です。


で、下記記事にも書いたのですが、Google の Eddystone リポジトリに置いてある iOS 向けのサンプルが不完全で、肝心の Eddystone-URL フレームタイプがサポートされていない、という状態でした。


Eddystone の仕様について勉強しつつ Google のコードを読んでいるうちに、「自分が実装するならこのへんにこう書くな」というのが思い浮かんできて、それと冒頭に書いたようなミーハー心も手伝って、コードを修正してプルリクを送ってみました。


Travis CI のテストが通らない・・・

こんな感じで failed になってしまいました。

さっくりあきらめようかとも思いましたが、ログをよく見ると自分がやった修正と全然関係ないところで引っかかっていて、他のプルリクの自動テスト結果を見ると同じログが出ていたところもあったので、コミットログからコミッタらしき人に連絡をとってみました。

Hi, my pull request could not pass the check by Travis CI, however this error seem not to be caused by my changes. Please confirm my changes don't cause errors and merge. Thanks.

(このエラーって僕の修正のせいじゃないと思うんだけど見てもらえますか?)


で、このコミッタの mashbridge さんがいろいろな人に連絡をとってくれて、g-ortuno さんという人が Travis を直してくれたのでした。


フィードバック→修正の繰り返し

プルリクの修正の中身については、marcwan さんという人がフィードバックをくれました。


「インデントは4スペースじゃなくて2スペースを使うように」とか「1行あたり100文字を超えないように」とか以外の大きいフィードバックとしては、

This pull request basically adds support for an Eddystone beacon sending out both URL and UID frames. It would be nice if we could also support just scanning and reporting for URL frames as well, even if the underlying hardware isn't broadcasting UID frames.

For this, I had thought to add a new didFindURL:(NSUUID *) method to the delegate class. IF a beacon has URL + UID, then they'll get the didFindBeacon: as they currently do, but whenever we see a URL, we could also fire a didFindURL: delegate as well (provided people are listening to it).

Should we fold in duplicates of a given URL within a certain timeframe? Probably. Maybe don't report a URL more than once a minute?

I can do this new change, or if you're feeling super energetic, you're welcome to contribute that too ^_^

というものがあり、修正イメージがすぐに湧いたしそんなに大変じゃなさそうだったので対応してpushしました。


(Eddystoneの仕様や実装の細かい話については、下記記事もご参照ください)


CONTRIBUTORS に!

そんなこんなで

Awesome, Played around with it and it seems great, thanks so much!

とマージしてくれそうな雰囲気になり、


Do you want to add yourself to the CONTRIBUTORS file too?

と、あちらから CONTRIBUTORS ファイルへ僕の名前を追加することを申し出てくれたのでした。

中身を見るとまだ9名しかいない(2015年12月現在)のでこれはかなり嬉しかったです。



おわりに

そんなこんなで、最初のプルリクから19日後に、google/eddystone リポジトリに自分のソースコードをマージしてもらうことができました。といってもほとんどは待ち時間で、そんなにすごいやりとりがあったわけではないのですが、Travis CI のよくわからないエラーで fail になったときに面倒ながらも問い合わせコメントを書いてみたところが分岐点だったなと。確か、いったんはあきらめたのですが、思い直してダメ元でコメント書いてみてよかったです。せっかくなので LinkedIn にも書いておこうと思います!


余談:その他の著名リポジトリへのContributions

あまり書く機会もないので、過去にマージしてもらえた著名リポジトリへのプルリクもここで掘り起こしてみたいと思います。


GPUImage

iOSにおける画像処理ライブラリのデファクトスタンダード

バイラテラルフィルタのシェーダのパラメータの修正。修正量は少ないですが、GPUImage のシェーダのソースをいろいろと読み、バイラテラルフィルタの特性をちゃんと理解しての修正なので、ある程度の貢献ではあると思います。


appledoc

Apple風ドキュメントを生成してくれるツール。

僕の環境で落ちて使えなかったので、判定を入れただけ。あまり本質的な修正でもないので、それほど誇れる話ではないです。


LGBluetooth

Core Bluetooth のラッパーライブラリ。

LGBluetooth は元のリポジトリが消えて、別のところに移されたので、残念ながらプルリクのログは残っていないようです。が、コミットログには残っています。当時は案件でも使っていたので自分の必要な機能を追加してはプルリクを送っていました。


facebook/pop

Facebook の Paper が話題になった頃に公開され、同アプリで使われているということでこちらも話題になったアニメーションエンジン。


ソースの規模が他のアニメーション系ライブラリと比べて大きく、何をやっているんだろう、とソースを読んでみようとして、

どのクラスがどういう役割なのかを把握しようと思ったところ無駄に依存し合ってる部分が多くあって設計指針が把握しづらいと感じたので、依存関係を整理してプルリクを送ったのでした。


f:id:shu223:20151212160336p:image:w500



他にも大小あります。不具合修正や、他の人も必要になるであろう機能追加であればなるべくpull requestを送るようにしています。



2015-12-03

RSSI と TxPower からビーコンとの距離および近接度(Proximity)を推定する

先日、Eddystone の使いどころについて書いた記事で、iBeaconと比較した場合の利点のうちのひとつは「カスタマイズ性」(=自前実装が可能)であると書きました。 *1


この場合、Core Bluetooth を使って、

  1. Eddystone ビーコンをスキャン
  2. 発見時に得られるアドバタイズメントデータを解析

という処理を実装することになるわけですが、

その際に Core Location の iBeacon 実装と同等のことをしたければ、つまり「近接度(Proximity)」を知りたければ、 didDiscoverPeripheral〜 メソッドの引数に渡されてくる RSSI と、ビーコンの TxPower から推定距離を算出する 必要があります。 *2


TxPower と RSSI について

TxPower とは?

ビーコンが発する信号の強さを示していて、単位は dBm (デシベルメートル)です。iBeacon の場合は 1m 離れた地点での受信信号強度を利用しますが、Eddystone の場合は 0m 地点での強度を利用する、という違いがあります。

Note that this is different from other beacon protocol specifications that require the Tx power to be measured at 1 m.


1m 地点の受信信号強度に 41dBm を足すと 0m 地点での受信信号強度に なります。

Note to developers: the best way to determine the precise value to put into this field is to measure the actual output of your beacon from 1 meter away and then add 41dBm to that. 41dBm is the signal loss that occurs over 1 meter.



iBeacon の場合はこの値は Proximity UUID や Major, Minor が入っている Manufacturer Data の領域の最後に入っています。



Eddystone の場合は、この値は Eddystone-UID フレーム や、Eddystone-URL フレーム より取得できます。



ちなみに iBeacon の場合は Core Location 内部で Proximity を計算するので、この値を Core Location の API から直接取得することはできません。が、iOS をペリフェラル(ビーコン側)としてふるまわせたい場合に利用する `peripheralDataWithMeasuredPower:` メソッドの引数に `nil` を指定するとデフォルト値が設定されるのですが、その値は -59dBm となります。


RSSI とは?

Received Signal Strength Indication の略で、ここでは iOS デバイスが受信した電波の強度を示します。単位は同じく dBm です。


RSSI と TxPower から推定距離を計算する

自由空間では受信信号強度は距離の二乗に反比例して減衰していく(フリスの伝達公式)ので、RSSI と TxPower と距離(d)の関係は次のようになります。

RSSI = TxPower - 20 * lg(d)

(lg は底を 10 とする常用対数)


というわけで、距離 d の計算式は以下のようになります。

d = 10 ^ ((TxPower - RSSI) / 20)

Swift のコードだとこんな感じです。

let d = pow(10.0, (TxPower - RSSI) / 20.0)

ちなみに上で「自由空間」と書きましたが、自由空間というのは「障害物のない理想空間」を意味していて、実際には障害物の有無などで電波の受信強度というのは変わってきます


これを考慮したい場合には次のように距離に係る部分を変数にします。

RSSI = TxPower - 10 * n * lg(d)

この係数は

  • n = 2.0 : 障害物のない理想空間
  • n < 2.0 : 電波が反射しながら伝搬する空間
  • n > 2.0 : 障害物に吸収され減衰しながら伝搬する空間

という意味になります。



(参考書籍、ページ)

  • iBeacon ハンドブック 2.3 「ビーコンまでの距離推定」

iBeacon ハンドブック
iBeacon ハンドブック
posted with amazlet at 14.12.08
(2014-03-25)
売り上げランキング: 11,647


近接度(Proximity)への変換

Core Location の実装ではビーコンとの距離を、CLBeacon の `proximity` プロパティから、以下の3段階で得られるようになっています。

  • CLProximityImmediate (非常に近い)
  • CLProximityNear (近い)
  • CLProximityFar (遠い)

上に載せた『ビーコンハンドブック』によると、それぞれ 〜20cm、1〜2m、それ以上、とされています。 *3


単純な閾値による判定が行われているのか、その閾値はいくつなのか、というのはわかりませんが、上記を目安に近づけることは可能です。


またレンジングの間隔は1秒であること、CLBeacon の `accuracy` プロパティは "one sigma horizontal accuracy in meters" を示していることから、何回分かのRSSIから計算した推定距離を平均化して近接度の判定に用いていると推測されます。


accuracy(精度)の計算

CLBeacon の accuracy について散見される誤解

ちょっと話はずれるのですが、CLBeacon の `accuracy` プロパティについて、「ビーコンとの推定距離をメートル単位で示したもの」という記述 を結構みかけます。(日本語記事だけじゃなくて、StackOverflowとかでも見かける)


ただAppleのリファレンス によるとこの accuracy は

one sigma horizontal accuracy in meters

とあり、この "one sigma" は

標準偏差によって定義される範囲は測定の68.3%(1σ)の信頼区間である。標準偏差を正確に求めるために十分な回数の測定が行われ、測定の誤差に偏りがなければ、測定のうち68.3%は 1σ の範囲にあり、95.4%は 2σの範囲にあり、99.7%は 3σ の範囲にあることになる。(正確度と精度 - Wikipedia

f:id:shu223:20151203074506p:image:226]

の意味での "1σ"(シグマ)です。


なので、`accuracy` は 推定距離ということではなく、推定距離のばらつきを示す値です。


というわけでリファレンスでも、

Use this property to differentiate between beacons with the same proximity value. Do not use it to identify a precise location for the beacon.

と、「同じ proximity であるビーコン同士の比較に使いましょう」としているわけですね。


accuracy の計算

というわけで CLBeacon の accuracy 相当の値が欲しい場合には、推定距離の標準偏差を計算します。


まとめ

RSSI と TxPower から距離を推定する方法について書きました。個人的には Eddystone を iBeacon の代替として用いる際にこの実装が必要になりましたが、iBeacon を使う場合でも、(ラップされているとはいえ、)このあたりを理解しておくとトラブルシューティングの際などにも見通しがよくなるのではないかと思います。


*1:iOSでは、iBeacon仕様のビーコンを検出し Proximity UUID / Major / Minor を取得するということを Core Bluetooth で自前で実装することができないようになっている。詳しくは こちらの記事 を参照

*2:推定距離を算出せず、RSSIの値をそのまま平均して閾値処理にかけて近接度を判定するという実装もみかけます。RSSIは対数ベースの値だし、あまり適切ではないと思うのですが、どうなんでしょうね。。

*3:Appleのリファレンス、プログラミングガイドではこの記述を発見できず。WWDCの資料とか、他のドキュメントに書いてある?

2015-12-02

Eddystone と iOS - その2: 実装編

その1 では iOS アプリ開発者から見た、Eddystone を採用するメリット・デメリットについて書きました。本記事は実装編として、Core Bluetooth を用いた Eddystone 検出機能の実装方法 をポイントをかいつまんで紹介したいと思います。


f:id:shu223:20151201110208p:image



なお、本記事では Core Bluetooth や Bluetooth Low Energy の基礎的なことは省略します。よろしければ下記書籍を参考にしてください。


iOS×BLE Core Bluetoothプログラミング
堤 修一 松村 礼央
ソシム
売り上げランキング: 898

電子書籍版 もあります)


Eddystone を発見する

普通に CBCentralManager でスキャンを開始するだけです。

let services = [CBUUID(string: "FEAA")]
centralManager.scanForPeripheralsWithServices(services, options: nil)

上のコードでは Eddystone ビーコンだけを発見対象とするよう、Eddystone Service UUID `FEAA` を指定しています。


アドバタイズメントデータから Eddystone フレームを取り出す

スキャンしてペリフェラル(ここではEddystoneビーコン)を発見すると、didDiscover〜が呼ばれるわけですが、この引数に入ってくるアドバタイズメントデータから Eddystone frame を取り出します。

func centralManager(
    central: CBCentralManager,
    didDiscoverPeripheral peripheral: CBPeripheral,
    advertisementData: [String : AnyObject],
    RSSI: NSNumber)
{
    let serviceData = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID : NSData]
    
    if let serviceData = serviceData {
        let eddystoneServiceData = serviceData[CBUUID(string: "FEAA")]
    }
}

こんな感じで、アドバタイズメントデータから `CBAdvertisementDataServiceDataKey` をキーとしてサービスデータを取り出し、その中からさらに Eddystone Service UUID をキーとして Eddystone フレームデータが取り出せます。


フレームタイプを判別する

Eddystone フレームの先頭バイトが Eddystone のフレームタイプを示します。

class func frameTypeForEddystoneServiceData(data: NSData) -> EddystoneFrameType {
    
    var bytes = [UInt8](count: data.length, repeatedValue: 0)
    data.getBytes(&bytes, length: data.length)
    let firstByte = bytes[0]
    
    if firstByte == 0x00 {
        return .UID
    }
    else if firstByte == 0x10 {
        return .URL
    }
    else if firstByte == 0x20 {
        return .TLM
    }
    return .Unknown
}

(Enum定義は省略)

こんな感じで、UID / URL / TLM を判別します。


各フレームデータのパース

ここは細かい話になるので、詳細は割愛しますが、実装にあたっては下記のソースコードを参考にしました。

  • Google のサンプルコード
    • 本家のコードなので仕様の解釈に困ったらこちらが指針になる
    • あくまでサンプル。あまり綺麗に、使い回しやすくは書かれていない。。
    • Eddystone-URLフレームタイプは非サポートPull Request 送信済み

距離推定(近接度推定/レンジング)

iBeacon と同等に使おうと思うと、アドバタイズメントデータ受信時に呼ばれる `centralManager:didDiscoverPeripheral:advertisementData:RSSI:` の第4引数に入ってくる RSSI 値と、UID あるいは URL フレームに入っている TxPower より距離を推定するなり、RSSI 値を閾値処理して近い/遠い(iBeacon でいう Proximity)を判別するなりする必要があります。


これについては色々とあるのでまた別記事に書きたいと思います。


バックグラウンドサポート

Eddystone向けの特別な手順は必要ありません。普通のセントラル側のバックグラウンド対応と同様です。詳しくは冒頭で紹介した参考書籍等をご参照ください。


TLM / URL フレームの取り扱い

TLM / URL フレームは UID フレームと違って UUID/Major/Minor に相当するような「ビーコンを特定するデータ」を持っていません。が、たとえばTLMフレームからメンテナンス用の情報を取得できたとして、「このTLMデータはどのビーコンのものなの?」というのを解決しないと意味がありません。


ではどうするのかというと、ペリフェラルの UUID を用いて UID フレームと紐付けます。


TLM フレームは Frame Specification に書かれている通り、UID や URL フレームの合間にアドバタイズされるものなので、


  • UIDフレームデータを保持するモデルクラスに、TLMデータを保持できるようプロパティを用意しておく
@interface EddystoneUIDBeacon : EddystoneBeacon

@property (nonatomic) NSNumber *txPower;
@property (nonatomic) NSString *namespaceId;
@property (nonatomic) NSString *instanceId;

@property (nonatomic) EddystoneTLMBeacon *tlmBeacon;

@end
  • UIDフレームが検出されたら、ペリフェラルUUIDをキーとして、UIDフレームデータをDictionaryに保持する
beaconDic[beacon.identifier] = foundBeacon
  • TLMフレームが検出されたら、ペリフェラルUUIDをキーとして上記Dictionaryの値を取り出し、値(=UIDフレームデータ)があればそのプロパティにTLMデータをセットする
foundBeacon = beaconDic[beacon.identifier]
foundBeacon.tlmBeacon = beacon

といった感じで実装できます。


じゃあURLの場合はどうするのかというと、Googleのサンプルには実装されていなかったので、正式なところはよくわかりません。URLにID的なものを持たせられる、という考え方なのかもしれません。


が、自分が今回試していた kontakt.io のビーコンでは、UID/URL/TLMの3パケットを順番にアドバタイズする(設定切り替えによってではなく)という挙動だったので、TLM同様ペリフェラルUUIDを用いてUIDフレームと紐付ける、という実装を行いました。(Google に送った Pull Request もそういう実装)


まとめ

今回は実装編として、Eddystone ビーコンを iOS アプリから検出する際の実装方法をポイントをかいつまんで紹介しました。Eddystone は Bluetooth SIG に承認されないと使用できない16ビットUUID を持っているとか、TLMはUID/URLフレームの合間にアドバタイズされるものであるとか、TLMフレームはUIDフレームと紐付けて用いるとか、(今回は書きませんでしたが)URLを20バイト以内(実質18バイト以内)にどう収めているか等々、個人的には実装してみるまでは見過ごしていた仕様が多々ありました。今回割愛した、「RSSIからの距離あるいは近接度推定」についてはiBeaconと共通の話でもあるので、また別記事で書きたいと思います。


(追記)書きました: RSSI と TxPower からビーコンとの距離および近接度(Proximity)を推定する - Over&Out その後


2015-12-01

Eddystone と iOS - その1: iBeacon と比較したメリット・デメリット

2015年7月にGoogleから発表されたビーコン規格、Eddystone。「Eddystoneとは?」という概要については既に多く出ている他の解説記事にお任せして、iOS アプリ開発者から見た、Eddystone を採用するメリット・デメリット や、仕様をパッと見ただけではわからなかった実装のポイント 等について書きたいと思います。


f:id:shu223:20151201110208p:image


本記事ではまず「iOS アプリにおいて iBeacon ではなく Eddystone を採用するメリット・デメリット」について。


(Eddystoneの概要については次の記事がおすすめです)


「クロスプラットフォーム」「オープン」はどう嬉しいのか?

iBeacon との違いとしてまず挙げられるのが、

  • 「クロスプラットフォーム」(Android・iOS両サポート)
  • 「オープン」(Apache v2.0ライセンス、規格やサンプルコードも公開)

という点です。


ただ、これらについて、下記のような疑問を持った開発者の方は多いのではないのでしょうか。。

  • iBeacon 形式のビーコンモジュールも Android アプリから検出できたんじゃ・・・?」(結局BLEなので)
  • 「iBeaconの規格は一般公開はされてないものの、アドバタイズメントパケットのフォーマット以前から知られていたんじゃ・・・」

というあたりです。


で、個人的には「クロスプラットフォーム」という点については、iBeaconと比較して特筆すべきアドバンテージは感じられていません *1


「オープン」という点については、実際にさわってみて、メリット・デメリットの両面があると感じました。以下でそれについて説明したいと思います。


メリット: カスタマイズ性

とあるiOSアプリで「iBeaconのレンジングを使い、それに紐付けられたコンテンツのリストを近い順に並び替える」ということをやっていました。


これで困ったのが、Core Location の `locationManager:didRangeBeacons:inRegion:` が呼ばれる間隔が、1秒固定であること。


つまり、1秒より小さい間隔でコンテンツを並び替えることができません。屋内でiBeaconを使う場合、ビーコン間は 2、3メートルぐらいしか離れてないことも少なくないので、更新が1秒間隔だと、ビーコンAの近くからビーコンBの近くに移動した場合にちょっと間が空いてからビーコンBがトップに来る、みたいな感じになります。ユーザーからすると「もっさり」に感じられるわけです。


ただインターバルを変えるプロパティ等も用意されていないのでどうしようもありません。ビーコンモジュール側のアドバタイズインターバルは100msecぐらいまで細かくできるというのに・・・


で、じゃあ iBeacon モジュールのレンジングを Core Bluetooth で実装するか、と考えるわけですが、iBeaconのアドバタイズメントパケットを発見はできるものの、 `centralManager:didDiscoverPeripheral peripheral:advertisementData:RSSI:` で 肝心の Manufacturer Data を返してくれない、という壁にぶち当たります。

func centralManagerDidUpdateState(central: CBCentralManager) {
    NSLog("manufacturer data:%@", advertisementData[CBAdvertisementDataManufacturerDataKey])
}

(実行結果)

manufacturer data:(null)

同じビーコンモジュールのアドバタイズメントパケットを Bluetooth Explorer で見てみるとちゃんと Manufacturer Data が入っているので、間違いなくApple様が意図的にiOSに課した制約のようです。


この Manufacturer Data の中に Proximity UUID, Major, Minor という iBeacon の iBeacon たる情報が入っている ので、これにより Core Location の iBeacon 関連実装を Core Bluetooth 使って自前でやる、ということは実質不可能である、といえます。(というわけで提唱されたのが AltBeacon


で、ここで Eddystone の「オープン」という点が生きてきます。公開されている仕様に則って好きなように実装することができます。冒頭のケースでは、最小100msec間隔でレンジングできるように実装しました。


デメリット: 自前実装の大変さ・怪しさ

好きなように自前実装できる、というメリットとは裏腹に、自前実装しないといけない、というのはデメリットでもあります。


Eddystone を検出する、ということ自体は Core Bluetooth に慣れていれば一瞬でできますし、そのアドバタイズメントパケットをパースするコードは Googleのサンプル や、こちらの実装 が参考になるのですが、困ったのは、RSSI / TxPower からの距離推定の実装です。計算式はググれば見つかるものの、細かい最適化のところで(専門家じゃないので)あまり自信がありません。


そしてやっているうちに、

  • RSSIはガンガンいろんな要因でぶれるので細かい差は信用できない、そう考えるとAppleのProximityの3段階ぐらいが妥当
  • 推定距離のブレを減らすために移動平均しよう
  • その際のどれぐらいブレがあるのかも評価しよう(Core Location の `accuracy`)

といった感じで、結局 Core Location の実装に近づいていく というジレンマもありました。


この辺り、iOS向けEddystoneサポート実装の決定版みたいなOSSが出てくることを期待します。


ちなみに Google による iOS 実装はあくまでサンプルという感じで、あんまり綺麗な実装とは思えなかったし、Eddystone-URLをサポートしてない、という状況です。(対応して Pull Request 送った ものの、それに対するレスポンスも遅い。。)


フレームタイプ Eddystone-URL の利点

3つあるフレームタイプのひとつ、Eddystone-URL では URL を直接アドバタイズメントパケットに含めることができるため、

これによってユーザーはGoogle Chromeが備わってさえいれば、iBeaconのようにわざわざ店舗毎に専用アプリをインストールせずともEddystoneからの情報を取得できるようになります。(no title

という点も Eddystone のわかりやすい iBeacon との違いとしてよく挙げられます。


が、逆に言えば、専用アプリのダウンロードを前提としているサービス・プロダクトにとってはアドバンテージにならない、ともいえます。


とくに、(個人的な話で恐縮ですが、)iOS専業エンジニアである自分はそもそも専用アプリの開発プロジェクトにしか呼ばれないので、自分にはずっと関係ないかなと。。


それと、iBeacon では、監視領域内に入りつつもアプリがインストールされていない場合にはストアへのリンクがロック画面に表示される、という機能もあります。URLの代わりにはなっていないですが。


フレームタイプ Eddystone-TLM の利点

このフレームタイプのパケットからは以下のようなメンテナンス用情報を取得することができます。

  • バッテリー電圧(mV/bit)
  • ビーコンの温度
  • アドバタイズ回数(前回起動時からの総数)
  • 起動時間(前回起動時から)

公式仕様


これらはiBeaconでは取得できないデータなので、何か非常におもしろそうではあります。


・・・が、具体的な活かし方がまだあまり思いついていません。


バッテリー電圧については、これにより TxPower & RSSI から算出した推定距離を補正できるかなと思ったのですが(バッテリー電圧が下がると同じ TxPower を設定していても観測される RSSI は下がる?と考えた)、ビーコンモジュールのメーカーに問い合わせたところ、バッテリー電圧が電波の強さに(大きくは)影響しないようなバッテリーを使用しており、かつ回路も工夫してある、とのこと。じゃあ他のビーコンモジュールはどうなのか、というのはわからないのですが、影響するとして、どんな計算式で補正可能なのかがわかりません。


ビーコンの温度は、バッテリー消費量に影響する らしいので活かせそうな気もしますが、その点についてはバッテリーレベルを直接見たほうがいい気がします。


アドバタイズ回数や起動時間も、電池交換のタイミングを知りたいならやはりバッテリーレベルを直接見たほうがいいし、アドバタイズ間隔なら設定値として知っているし、というところでやはり具体的な使いどころが思いつかない。。


バックグラウンドでも監視可能?

iBeaconはバックグラウンドでも領域監視可能ですが、Eddystoneもバックグラウンドで監視可能です。Core Bluetooth では、「バックグラウンドでのスキャン時には対象とする Service UUID を明示的に指定しないといけない」という制約がありますが、Eddystone としての Service UUID が定義されているので、スキャン開始時に明示的に指定することができます。

let services = [CBUUID(string: "FEAA")]
centralManager.scanForPeripheralsWithServices(services, options: nil)

というわけでこの点については iBeacon と比較してもデメリットにはならないかと。ただ、ロックスクリーンへのアイコン表示は iBeacon を用いないとできない機能です。


まとめ

かなりざっくばらんに書きましたが、結論として、"iOSアプリ開発者からみた" Eddystone を採用するメリットというのは、今のところ「カスタマイズ性」というところに尽きるかなと。他にもTLMパケットのように「Eddystone にあって iBeacon にないもの」は色々とあるものの、様々な代替手段を考えると、具体的な使いどころが個人的にはまだピンと来ていません。


とはいえ、レンジングの件含め、自分で好きなように実装できる、というのは十分に Eddystone 採用の理由に足るメリットかと思います。実際のサービスやプロダクトでiBeaconを使おうとするとそのブラックボックスに踏み込みたい場面は結構あるのではないでしょうか。


「その2」では、実装してみて色々と気づいた点を書いてみようと思います。


*1:ただ当方Androidアプリを書かないので、そちらの視点に立つと何かあるのかもしれません

2015-11-29

「外国企業から仕事を受けてたまに海外に行ったりしたい」という憧れが叶った話

10/29〜11/29のまるっと1ヶ月間、ドイツはバンベルク(Bamberg)という都市でフリーランスとして出張仕事をしてきました。早いものでついに今日で最終日なので、諸々ふりかえってみたいと思います。(※ちなみに当方iOS専業フリーランスエンジニアです)


きっかけ:ハッカソンでの営業活動

標題に書いた通りですが、基本的に日本に住みつつ、たまーに海外に行けたら楽しいだろうな、という目標というか願望がありまして。


で、9月にベルリンへ 行った際、参加した現地のハッカソンで、オーディエンス投票1位をとったことが引き金になりました。



ここで終了後に会場内で、同じく参加していた Artirigo 社の CTO、アンドレ(André)が僕に話しかけてくれました。


で、僕はいつでも自分アピールができるように、コンパクトな Moff と、日本語とはいえ物量で圧倒できる自著毎日持ち歩いていたので、すかさずデモしたり、WHILLの動画も見せたり、GitHub 活動 もアピールしたりしまして、「ワオ、実はエンジニアを探してるところなんだ、CEOと相談してみるよ」ということで連絡先を交換したのでした。


で、日本に帰ってからすぐに連絡来てスカイプでちょこっと話して、お金やスケジュールをメールベースで調整して、一週間後には正式にオファーをいただいたのでした。





条件面の調整

最初のスカイプで自分の日本での単価を伝えはしましたが、ドイツの相場はわからないし、僕としては値段が理由でスパッと断られると残念でしかない *1 ので、スカイプ中にも、スカイプが終わった後のテキストチャットでも、


I can change my fee for German price. Please feel free to tell me when it's too expensive. I really work with you.

(フィーはドイツの値段に合わせて変更できるので、高いと思ったら言ってくださいね、ほんと一緒に仕事したいんです!)


と弱冠しつこいぐらいにお金の問題で話が立ち消えないようにはしました。


で、その後あちらでの検討を経て提示された条件は、ほぼ希望通りの単価、その代わり今回は航空券代、宿泊費等は自分で持つ、というものでした。


通常は仕事で必要になった出張の交通費や宿泊費はいただきますが、本件に関しては「ドイツに行って仕事したい」というのは僕側の要望であって、「じゃあリモートでもいいよ」と言われてしまうと僕としては嬉しくないので、この条件で二つ返事で承諾させていただきました。


バンベルク(Bamberg)

今回の話が出るまで僕も知らなかったし、ベルリンに住んでいる知り合いの人たちも知らなかったようなので結構マイナーな街なのかもしれませんが、


見応えのある旧市街は、ドイツでも最もすばらしく無傷に保存された歴史的な市街地であり、1993年にユネスコの世界遺産に登録された。(by ウィキペディア



ということで街ごと世界遺産で、中世の街並みがそのまま残っているような大変素晴らしいところでした。



f:id:shu223:20151129125003j:image:w600

最初に借りたアパートのある路地。真向かいに古くて大きい教会があって、鐘がゴーンゴーン聞こえて非常に趣があった



f:id:shu223:20151129125049j:image:w600

2つ目のアパートは川沿い。築500年ぐらいのこれまた素晴らしいところでした。



f:id:shu223:20151129125131j:image:w600

ちょっと遠く(といっても歩ける距離)にある小さい古城、Altenburg。ここは他の教会とは違って塔の中に入って登れたのがよかった



f:id:shu223:20151129125206j:image:w600

バンベルク大聖堂。文字通り神がかってました。。



すごくコンパクトな街で、徒歩でだいたいどこでも行けてしまうので、ペーパードライバーでアメリカはちょっと辛かった自分としてはそこもお気に入りポイントです。


ちなみに日本人は全然いませんでした *2 。日本料理屋で一見日本人ぽい人がいたので聞いてみたら、うちには一人も日本人はいない、とのこと。(メニューも、味噌汁が Misohiru だったり、唐揚げが Karrage だったり微妙に間違っていた)


仕事

詳しい事業内容はまだ秘密なので書けないのですが、僕がやることはもちろん iOS アプリの開発。ビーコンとか位置情報とかが関わってくるので、個人的には技術領域的にも楽しかったです。


業務時間

9時半〜18時ぐらい。他の人は9時〜17時ぐらいには帰っている印象。僕は仕事は全然苦じゃないというかむしろ楽しいのでだいたい最後の人がオフィスを出るタイミングで一緒に出る(最初は鍵渡されてなかったので)という感じでやってました。


オフィス

オフィスは新市街の閑静な住宅街にあります。旧市街の僕が泊まっていたアパートからは徒歩10分ぐらい。


僕のアパートは非常にネットが遅かったのですが、オフィスは専用回線を引いているのか、素晴らしく速かったです。あとデスクが高さを電動で変えられるようになってるのがすごい便利で、昼食後の眠い時間にはいつも高さを上げてスタンディングデスクにしてました。



で、これまた素晴らしいのが、キッチンがあって、ランチをつくってくれるコックさんがいるところ。


f:id:shu223:20151129125334j:image:w600


毎日こうやってみんなで手作りランチを食べてました。


英語

僕にとっての最大の不安はコレでした。Hacker Paradise に参加した際の記事 にも書いたのですが、


みんなが英語で普通に(自分向けにゆっくり話してくれるモードじゃなく)話し始めると、まったくついていけない。文脈自体を見失って、情報を95%ぐらいロストするので、とてもじゃないけどチームで仕事できる気がしない(大事なミーティングで何も理解してない、ってことになる)。


というレベルなので、果たして仕事になるのかと。正直なところ、途中で「こいつダメだ」ってクビを切られる覚悟すら持っていました。


が、基本的にCTOのアンドレと二人で仕事をするのが中心だったのと、彼がとても親切で、僕の拙い話を腰を据えて聞いてくれるし、うまく言えずにいると文脈を汲んで「〜っていうこと?」と提示してくれるし、僕が聞き取れないときも別の言い方をしたり図を書いてくれたり *3 、、、ということでコミュニケーションは何とか大丈夫でした。


やるべきことや優先度についてちゃんと理解できさえすれば、あとは「手を動かす」という自分が確実に貢献できるところなので、結果的にはだいぶお役に立てて、クビになることもなく、「今後も引き続きお願いしたい」ということをファウンダーの人達にも言っていただけました。来年あたりにまた呼んでもらえそうです。


ちなみにドイツの若い人はめっちゃ流暢にペラペラしゃべります。少なくともArtirigoのメンバーはシリコンバレーで働いている非ネイティブ、ぐらいにはみんな話せる印象。で、ドイツではみんな英語話せるのか、と思いきや、年配の人は話せない人が多い。(たとえばランチつくってくれてるコックさんは話せない)


まとめ

というわけで1ヶ月間のバンベルク滞在、最高でした。


ただ、今回の件はたまたま色々な幸運が重なった感があり、まだまだ「外国企業から仕事を受けてたまに海外に行ったりしたい」ということを定常化するには実力不足だとは思っています。語学的な話だけではなくて、わざわざ海外から呼び寄せるほどのレアスキルを持っているわけでもない、という点でも。引き続き精進してまいります。


お知らせ

帰国してすぐに、2つのイベントに登壇します。


ひとつ目は PARTY の清水幹太さんにお声がけいただいた「Demoday.tokyo」というピッチイベント。


f:id:shu223:20151129133211p:image:w300


デモデイ、ということで、仕事以外で、何かつくったものを発表する場、とのこと。ただ僕は基本的にお客さん仕事が大好きで、ありがたいことに楽しいお仕事ばかりなのであまり個人制作とかはやっていません。で、どうしようかなーと悩んでたのですが、オープンソースで公開している iOS Sampler シリーズ のおかげでこうやって一介のフリーランスエンジニアに海外から仕事が来たりしてるので、そういうキャリア戦略込みで話をしようかなぁと思っております。



ふたつ目は「キャリアごはん」というエンジニアtypeさん主催のイベント。


f:id:shu223:20151127145614j:image:w300

  • 楽天株式会社 技術理事 吉岡弘隆さん
  • 株式会社トレタ CTO 最高技術責任者 増井 雄一郎さん
  • 株式会社ソラコム Cofounder&CTO 安川健太さん
  • フリーランスiOSエンジニア 堤 修一さん

というすごい方々に混じって登壇させていただきます。先着80名とのこと。どうぞよろしくお願いします!


*1:実際に値段をどれぐらいまで下げられるかは他の仕事や予定との兼ね合いで判断が変わってくるところですが、他にこういう海外にいける話もないので、とにかく断られる前に相談・検討させて欲しい、という意図

*2:滞在中「住んでいる日本人」というのは一人も会わなかったし、そういう人がいるという話も聞かなかった

*3:あと一度もそういう状況について一度も嫌な顔をしない、どころか常にサワヤカに接してくれる、という彼の人間性に随分救われた

2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2018 | 02 |