この記事では、
・Monaca
・ニフクラ mobile backend
を使用しています。
Cordovaのバージョンは7.1です。
#概要
毎週5時間x15週、
講師「適当にグループ作ってIoTな何か作ってみて」
くらいのノリでグループワークをする講義がありまして、
ぼく「IoTと言えばBLEビーコンでしょ!ビーコンは低価格省エネ省スペース!神!」
※BLEビーコンについて…Bluetooth Low Elergy(BLE)で動作するビーコンです。
端末側でBluetoothをオンにしておくだけで特にペアリングもいらず勝手に電波受信してくれます
ってビーコンの良さを語ったら二人集まってくれたので三人でビーコン使ったアプリを開発する事になりました。
#理想と現実(0週目)
5時間x15週が始まる前にどういうアプリを開発するかについてまず決めたんですが、
最初の目標としては
**「学内全員のストーカーをするアプリ」**を作りたかったんですよ。
は?
って思う方が多いと思いますが、
BLEビーコンの電波の最大通信範囲はモノによっては最大450mなんてモノがあります。
それを大学各地に置く事ができれば…
三点測位でその人の位置が分かっちゃうわけですよ。
その人がアプリを入れてくれてさえいれば!常に!学内にいる全員の居場所が分かる!!!
友達とはぐれる心配もない!次の教室の場所が分からなくても大丈夫!みんながいる場所に行けばいい!!
学食が混んでる?コンビニが並んでる?大丈夫!これ見れば空いてる場所がひと目で分かるよ!!
……そんなアプリが作りたかったんです。
が、
・予算(1人あたり1万円程度)
・開発期間
・人数(ここが一番大きい)
を考えた結果断念しました……(´・ω・`)
その結果、
じゃあ何作ろうか?ってなった時に、
「小規模なチェックインアプリなら自分たちでも作れるんじゃないか」
って話になったんですよ。
大学全体ではなく、教室全体レベルの電波強度を持つビーコンを学食、図書館にに置いて、
人数や動向を把握できれば割と良いデータが取れるんじゃないかな?と。
そこにいる人数を表示できたり、自分のチェックイン履歴が見れたり、
メイン画面で可愛い女の子が「チェックインしたよー!」って言ってくれたり?
一言で表すなら**"学内版swarm"**です。
その程度なら自分たちでも開発できそう!って事でまとまりました。
アプリ名は「meeacon」。
由来としては私(me)のbeaconくらいのノリです。
……当初の予定では「meacon」だったんですが、これ実は
〔航海用情報装置に〕誤った信号を送る
って意味らしいんです。流石にそんな意味が被っちゃうのはマズいなぁ……と思い変更しました。
ちなみにこの辺で使用機材について選定。
3人でも開発が無事にできるようにとなるべく簡単なものを選んだ結果、
開発環境は先述の通りmonaca、データベースはニフクラ、使用したビーコンはMM-BLEBC1です。
monacaのゴールドプランと合わせて約3万円です。
#利点
「swarmでいいじゃん」って意見が聞こえてきそうなので一応利点を上げておくと、
・GPSと違って屋内での位置を正確に測定できる
・GPSと違って詐称できないので正確なデータが取れる
・GPSより遥かに省エネ←これ重要
#開発スタート(1週目)
まず開発するにあたって役割分担を決めました。
ぼく : リーダー兼開発担当(言い出しっぺなので)
Mくん : プレゼン作成担当
Tくん : デザイン担当
で決定。
実はこの時「技術面なんて余裕でしょ」くらいに考えてました。
理由としては、monaca×beaconって割と参考にできるものやチュートリアルが結構あったんですよ。
ビーコンを検知して通知を送るMonacaアプリを作る
とか、
iBeacon | Monaca Docs
とか。
会員登録面に関しても、
Monacaアプリから会員管理機能を使う
みたいなのがあって、「この2つ掛け合わせればいいだけじゃん!3週くらいで開発終わるだろうな」って考えてました。この時は。
#問題だらけ(2週目)
一番最初に手こずったのが、
どうしてAndroid 6.0でBLEを使うのに位置情報のパーミッションが必要なワケ?
この問題です。
「BLEビーコンは省エネ!Bluetoothオンにするだけ!」って謳ってたのに、
GPSまでオンにしないと検知してくれないようじゃどうしようもない!!!
ここに辿り着くまでに相当な日数を費やしました……
まさかそんな改悪がされていると思わず、
「プラグイン側で何か弄るべきなのかな」
「コードの書き方が悪いのかな」
って見当違いの部分をひたすら考えてました。つらい。
この問題に関しては
[[Monaca] AndroidのminSdkVersionについて]
(https://qiita.com/keeey/items/1b3c5ff6d987f8c550c0)
を参考にAndroid4.4が対象となるようにバージョンを下げました。
あとはアプリのビルドが上手く行ったり行かなかったりで躓いてました。
原因はmonaca側でCordovaのバージョンアップをしていなかったせい。
ちゃんと7.1まで上げましょう!
#企画書作成(3,4週目)
本来なら先にやるべきだとは思いますがここで企画書を作成しました。
といってもほとんどプレゼン担当Mくんに投げて自分は開発に注力してました。
その結果無事にアプリの大枠は完成しました。ありがとうMくん!
あとこの辺で、
[初心者3人でwebサービス(webアプリ)を作ったので、立ち上げからリリースまでを時系列に書いていく]
(https://qiita.com/yuno_miyako/items/d6595719ae7065927499)
この記事を見つけて、メンバーで共有してみました。
自分たちと同じような状況下でかなり参考になりました。ありがとうございます。
#jQueryが邪魔(5週目)
いよいよ開発も半分くらいは終わって、あとは履歴機能と人数表示機能付ければ終わり!ってところで
デザイン担当Tくん**「css書いても色が変わらない」**
そんな事あるか~~?と思って確認してもやっぱりダメ。
で、色々原因を探ってもらった結果、jQueryのせいらしい事が判明。
「jQueryって便利!こんな簡単にアプリのボタンや色や位置まで指定できちゃうんだね!」
なんて思ってたんですが、規定のデザイン以外に変えるのは相当難しいという事で……
結局ほぼ諦めでデフォルトのデザインを使う事になりました(´・ω・`)
#データベース(6週目)
この頃にはチェックイン情報は無事にデータベースに保存できるようになってました。
が、データベース知識0のぼくは各場所のクラスを作り、チェックインした際にその値をtrue、他をfalseにしていました。
これが何を意味するか……
もし場所が100個になったら?
何も格納されていないものが99個もできちゃうんですよ。無駄すぎる。
いろいろ調べてみた結果、"checkIn""checkOut"の2つのクラスを作ってそれぞれにチェックインした場所の名前を格納する形になりました。
#履歴と共有と人数表示(7~9週目)
履歴機能…意外と簡単でした。というのもサンプルがいくつかあったため、それを参考に書きました。
function myHistory() {
var people = ncmb.DataStore("people");
people.equalTo("userName", userId())
.order("createDate", true)
.limit(50)
.fetchAll()
.then(function (results) {
var table = document.getElementById("table1");
table.innerHTML = '<tr><th>場所</th><th>時間</th><th>入退室</th></tr>';
for (var i = 0; i < results.length; i++) {
var object = results[i];
var msec = Date.parse(object.get("createDate"));
var date = new Date(msec);
var updateTime = date.toLocaleString("ja-JP", { hour12: true });
if (object.checkIn != undefined) {
table.innerHTML += '<tr><td>' + object.checkIn + '</td><td>' + updateTime + '</td><td>' + "入室" + '</td></tr>';
}
else if (object.checkOut != undefined) {
table.innerHTML += '<tr><td>' + object.checkOut + '</td><td>' + updateTime + '</td><td>' + "退室" + '</td></tr>';
}
else {
console.log("error!");
}
}
})
.catch(function (err) {
console.log(err);
});
}
どうして時刻をperseしているかに関してですが、
上の方で載せたニフクラのcreateTimeとupdateTime、コンマ以下3ケタまで取得されるようになってるんですよ。
一方、標準のData関数ではコンマ以下2ケタ。
なので、一旦1970年1月1日からの経過時間にperseした後に形式を変更する形になってます。
ここでも結構苦戦しましたが、
[JavaScript の Date は罠が多すぎる]
(https://qiita.com/labocho/items/5fbaa0491b67221419b4)
を参考に何とかしました。ありがとうございます。
共有機能…cordovaプラグインにて対応(x-socialsharing)。あっさりいけました。
<a data-rel="popup" data-role="button" input type="button" data-inline="true" onclick="window.plugins.socialsharing.share(returnLastCheckIn()+' #meeacon')"
data-theme="b" data-mini="false">共有</a>
人数表示機能…元々はチェックインした人数-チェックアウトした人数を実現したかったのですが、
非同期処理でそれを行うのが自分には難しかったため断念。
今日の朝6時からチェックインした人とチェックアウトした人を全員分全て取得し、
それをこちらで仕分ける形になりました……
どこぞで見かけた**username==username**並に最悪なソースコードです。
function AllCTrue() {
var people = ncmb.DataStore("people");
var now = new Date();
now.setHours(6);
var msec = Date.parse(now);
var date = new Date(msec);
var checkIn = people.inArray("checkIn", "C食堂");
var checkOut = people.inArray("checkOut", "C食堂");
people.or([checkIn, checkOut])
.greaterThanOrEqualTo("createDate", { "__type": "Date", "iso": date.toISOString(date) })
.limit(1000)
.fetchAll()
.then(function (trueResults) {
var allCheckIn = 0;
var allCheckOut = 0;
for (var i = 0; i < trueResults.length; i++) {
if (trueResults[i].checkIn == "C食堂") {
allCheckIn++;
}
else if (trueResults[i].checkOut == "C食堂") {
allCheckOut++;
} else {
console.log("error");
}
}
allUser = allCheckIn - allCheckOut;
document.getElementById('CUser').innerHTML = "現在C食堂にいる人数は" + allUser + "人です。"
})
.catch(function (err) {
console.log(err);
});
}
#進捗発表会とその反省(10,11週目)
進捗発表して実演しました。それだけです。
反省としては、あまりUIにこだわれなかった事と本稿の最初で述べた理想と現実について触れられなかった事ですね……
あまり面白みのない発表となってしまったので最終発表ではもう少しユーモアのある発表を心がけたいと思いました。
ちなみにこの段階でのデザインがこちら。
#実地試験に向けて(12週目)
ここでなんと大学の先輩が絵を描いてくださる事に。
青葉ちゃん
名前はBluetoothなので青葉ちゃんです。描いてくれて感謝……!
#実地試験(13週目)
研究室とグループワークの部屋でそれぞれ実地試験を行いました。
先に研究室で行ったんですが、「アプリの初回起動時にチェックインされたままになってしまう」という致命的なエラーを発見。
どうやらdelegateの作成時に、
cordova.plugins.locationManager.startRangingBeaconsInRegion(beaconRegion)
(範囲内にいる際に呼ばれる関数)
が呼ばれてしまっていた事が原因みたいです。
また、この関数が原因でチェックインとチェックアウトを数秒毎に繰り返してしまう事も判明。
調べてみたところ、「別にこれ無くても動作するし問題ないのでは」という結論に至ったため削除。
#UIの変更、バグ修正(14週目)
元々のUIはこれです。
こんな表示だったのが、
こうなりました。(黒塗りしてある部分は場所名です)
画面遷移図通りにはいきませんでしたが、ボタンが絵に被ってしまう事を考えてこのUIに落ち着きました。
バグ…というか仕様上どうしようもなかった事なんですが、Android8.0以降はバックグラウンドでの実行が著しく制限されるみたいです。
Beacon Detection With Android 8によると、
…Android8.0以降は長時間のスキャンサービスは許可されません。
絶望的ですね。
#最終発表(15週目)
形だけはできてたので難なく発表して終了。
ただ、開発方法が**「同一プロジェクトをクラウドで一緒に編集する」という最悪な方法だったのでそこは突っ込まれました。そりゃそうだ。
デザイン担当にhtmlをいじってもらってる際に片方の編集が消えちゃった事もありました……
ちゃんとgitにプルもプッシュもマージもできるようになってます。git使いましょうね。
#おわりに
一応は無事に完成できて良かったです。ある程度の達成感もありましたし、いい経験になりました。
Monacaを業務で1年使っての感想
という記事にも書いてありましたが、サードパーティ製のプラグインを使うのは本当に諸刃の剣です。ビルド成功しても思わぬバグがあったり、別プラグインと競合してしまったり、iOS用だからAndroidだとどうにもならない、みたいな場合があります。
要は「同時に両方簡単に開発できる」なんてそんな美味い話はない**って事ですね。
それと、iOS向けに何か作りたい場合はMacが必須だという事を学びました。
Mac欲しいですね。いつかMacを手に入れられるよう頑張ります!!
あと、固定Beaconを使って現在位置を測位するアプリをDelphi/C++BuilderとBeaconFenceで作る
こちらの記事を読んで三点測位に興味を持ったので、次は三点測位にチャレンジしてみたいです。