0

BLE NanoをArduino IDEで開発する

BlendMicro npmが自動再接続・複数接続できるようになった
で、BLE Nanoにも接続できるっぽいと書いた。今やってる

BLENano実機の開発環境が手に入ったのでセットアップしたメモを書いておく。

BLE Nano

写真の上の部分がBLE Nanoで、下はそれにUSBインタフェースを追加するMK20 USBボード。プログラムを書き込む時だけ下が必要で、デプロイする時は上の部分だけで良い。
なおBLE Nanoは12ピンでMK20は14ピン、1ピンずつ余るけど差し間違えると死ぬ可能性があるので注意。写真のようにUSBコネクタ側を余らせ、USBコネクタと反対側に白いアンテナとRedBearLabのクママークが来るようにする

BlendMicroとの違い

BLE Nanoと似たものにBlendMicroがあるが、これは普通のAVRマイコンを使ったArduinoにnRF8001というBLEチップを外付けして、その間の通信が簡単にできるArduinoライブラリとセットにしたボード。

それに対してBLE NanoはBLEの機能が内蔵されたnRF51822というARM CPUと、電源のレギュレータや無線アンテナなどを1枚の基板に載せたマイコンボード。

ワンチップに全て入っているので、外付けBLEモジュールと通信してるBlendMicroよりもBLEのより細かい機能を扱える。サンプルコードを見るとBLEのCentral(スマホやPC側の動作)として動かす例なんかもあって、だいたい何でもできそう。
またICが1つだけなので消費電力も低いらしく、ボタン電池で動く。

買う

スイッチサイエンスで売ってる

Amazonにもある
RedBearLab BLE Nanoキット
RedBearLab BLE Nanoキット
posted with amazlet at 15.12.03
スイッチサイエンス


開発方法

開発方法は2通りあって、mbedのオンラインコンパイラを使うか、Arduino IDE用のプラグインを入れてArduino風のAPIで開発もできる。

Arduino風に開発する方は、digitalWriteやanalogRead等のIO操作やdelay(msec)などのArduino互換の関数があるだけで、BLE操作の部分はふつうにmbedのC++のライブラリをincludeして行うのであまり関係ない気もする。
そして何より、ひとたびBLEを動かし始めたらmainループの中でble.waitForEvent()を呼んで、あとは全てイベント駆動で書かなければならないのでどれだけ既存のArduinoライブラリが動くかわからない。
イベント駆動についてはIntroduction to mbed BLEに書いてあるが、ようするに基本的にTickerという定期的に関数を呼び出してくれるタイマーか、BLEのデータ受信イベント等にコールバック関数を登録してアプリケーションを書く事になり、mainループは使わない。

そう考えるとArduino風に書けるメリットはあんまり無いような??気がしてきたけど、オンラインコンパイラなしで、ネットワークの無い所でも開発できるのでArduino IDE拡張の方が個人的には好きかも。(mbedオンラインコンパイラよりemacsで書いて+GitHubで管理したいし)


mbedオンラインコンパイラで開発する

web上にあるIDEで開発して、コンパイルするとhexバイナリをダウンロードできる。
BLENanoをMacにUSB接続すると /Volumes/MBED にマウントされるので、そこにhexファイルをコピーする。BLE Nanoが自動的に再起動して、hexが読み込まれ実行される。

たとえばURI BeaconのチュートリアルにあるBLE_URIBeaconというプログラムを書き込むと、PhysicalWebのURI Beaconが作れる。Androidアプリから見れるはず。
mbedオンラインコンパイラで公開されているプロジェクトは自分のアカウントにforkして読み込んで自由に編集できるので、試してみるとよい。


BLE NanoのArduino風開発環境セットアップ

2ステップ必要
  1. BLE NanoをArduino化して
  2. Arduino IDEにプラグインをインストールする
Getting Started – nRF51822 — RedBearLabの下の方にある説明よりも、
https://github.com/RedBearLab/nRF51822-Arduino
の説明の方が新しくて正しい。
とくに最近ArduinoはパッケージマネージャがついてBLENanoもそれに乗せて配布されるようになっているので、最新のインストール方法はgithubのREADMEを見たほうが良さそう。


BLE NanoにArduinoファームウェアをインストールする


https://github.com/RedBearLab/nRF51822-Arduino
をgit cloneしてきて、bootloaderをインストールする

% cp ./bootloader/bootloader.hex /Volumes/MBED/

Arduino IDEにプラグインをインストールする

https://github.com/RedBearLab/nRF51822-Arduinoに書いてあるバージョンのArduino IDEをダウンロードする。
現在は1.6.5が指定されている。最初1.6.6でやっていたんだけどExamplesが読み込めなかったので1.6.5に戻した。マイナーバージョンが違うだけで動かないなんて…

[Arduino]->[Preferences]から[Boards Manager URLs]にRedBearLabのjsonを追加してから、
[Tools]->[Board]->[Boards Manager]で「RedBearLab nRF51822 Boards」を追加する。

この手のArduino IDEをいじる系はBlendMicroやDigiSparkなど色々あるけど皆違うバージョンを指定しているので、俺は.appの名前を変えて複数保存している。


Arduino IDEでBLENanoを開発する

ToolsのBoardとPortをBLENanoに設定してから、とりあえずExamplesの[Basic]->[Blink]をそのまま書き込んだら、動いた。
基板裏面のLEDが13番ピンに接続されているらしい。


BLEの例は、SimpleChatを書き込んでからblendmicro npm付属のblendmicroコマンドで探したら見つかった。「TXRX」というのがそれ。


これでとりあえずプログラムが書き込める事が確認できた。

0

coffee-scriptからES6に移行する理由

coffeeからES6(babel)に少しずつ書き換える
の続き。

そもそも書き直す必要があるのか


正直シンタックスはcoffeeの方が好き。無駄にカッコつけなくていいし、インデントでpipeやpromiseをつないで書いていくのも見た目が良い。
coffeeは書きやすい。
まあすでに動いている物を書き直す必要は無いと思う。


移行するとしたら、ネガティブな動機が1つ、ポジティブな動機は2つある。

coffeeの開発の停滞

詳しくcoffeeの開発状況をウォッチしてないからよくわからないけど、詳しい人が「開発が停滞している」と言っているのでそうなんだと思う。
さよなら CoffeeScript – mizchi's blog

coffeeはここ1年ぐらいバージョンが上がった時にdiffだけは見ているのだが、毎回git diffで差分を見て一瞬で理解できるぐらいの更新しかされてない。まあようするに停滞しているのは間違いない。


HTML5の新しいAPIを使いたい

Web Bluetooth APIやWeb MIDI APIを使ってみると、型付き配列、Promise、IteratorなどのES6で増えた機能が前提で作られている。

この中で特にIteratorはES6のfor-of文で書くと綺麗に書けるんだけど、for-ofを使わないとしたらJavaみたいな感じでnext()を呼んだりdoneをチェックしたりとかしなくてはならなくて、すごく面倒くさい。

一方coffeeではfor-of文が対象Objectのkeyとvalueの2つを引数に取ってループを回す構文として実装されている。RubyのHash#eachみたいなやつ。
これはこれで便利なのだが、つまりcoffeeでES6のIteratorを扱うとつらい。
既にfor-ofが予約されてしまっている以上、もしcoffeeにIterator回し構文を追加するとしたら別の名前になる・・のかな?

たぶん、USB MIDIコントローラー250個とか接続できるから非同期でデバイス情報が返ってくるようになっているんだと思う。今後ハードウェアを扱うAPIは増えるだろうから、そういう場合はES6で書いたほうがいい。スマホWebアプリとかでそういう機会が増えると思う。


Map, Set, Promise, Buffer, 型付きArrayなど新しい組み込み型はブラウザならネイティブに実装されているのでcoffeeでも問題ない。というかbabelでも変換していない(ES5に翻訳しようがない)

ESLintが良い

少なくともcoffee-lintより気が利いている、というか勉強になる事をサジェストしてくれる気がする。具体的には忘れたけど。
プロジェクト毎のカスタムも容易でよくできている。

ES6で新しい組み込み型が増えているから、そういうのに合わせて細かくlintしてくれるようになるかもだし。


まとめ

別にふつうのアプリケーションを書くなら今はcoffeeでいいと思う。既に動いているのを書き直す必要もない。

いちはやく新しい文法で書いてみたいとかで無ければ、ES6(というかbabelが)変化激しいし、coffeeでいいかな・・・

ただ、いくつかの新しいHTML5 APIを使う場合はES6で書いたほうが明らかに書きやすくはなる。
ESLint良いよね。

0

hubot-rss-reader v0.8.1をリリースした

hubot-rss-readerをv0.8.1にアップデートした。

このアップデートでは、hubotが起動していない期間の記事を正しく配信できるようになった。Heroku無料枠で1日18時間制限がある場合などに便利。

結論から言うと、hubot-rss-reader v0.8.x以降はHubotに最初から入っているredis-brainをhubot-mongodb-brainなどのまともな実装に切り替えないと300日ぐらい後に死んでしまう可能性がある。



何が変わったか

RSSから取得した記事のうち、何が新着で何がチャットに配信済みなのか、という判定方法を変更した。


これまでの実装

新着記事を取得したらそのURLをオンメモリに持っておいて、RSSチェック毎に新しい記事かどうか判定するのに使っていた。つまり起動して最初の1回目のRSSチェックは配信済みURLリストとして保存されるだけになっていた。

なぜディスクに保存していなかったかというと、Hubotデフォルトのredis-brainの実装があまりにもひどかったから。詳しくは下に書いた
hubotのbrainが爆発した
またHubotのbrainが爆発したのでhubot-mongodb-brain作った

redis-brainがひどい

redis-brainは全データまとめてJSON.stringifyして1つkeyに巨大な文字列として保存している。しかも更新が無くても20秒毎にRedisに書き込んでいる。
サービスによって設定値が違うだろうけど、例えばRedisToGoでは1回に1.5MBを超えるとmaxmemory errorが起きて失敗する。もし毎日50byteのURLを100個保存すると1日で5Kbyteになり、300日で1.5MBを超えてしまい、死ぬ。

そもそもbrainはKVS風なAPIなのになぜRedisをKVSとして扱わないのかよくわからない。

俺は自作のhubot-mongodb-brainに切り替えているので問題ないが、ふつうのredis-brainではURLリストごときでもヤバイので、保存する事ができなかった。


v0.8.0での変更


Herokuが無料プランでは1日18時間しか動かせなくなったので、夜に6時間寝るように設定している
するとオンメモリに持っていたURLリストは消えるので、夜のうちに更新された記事は新着/既出判定できず、配信できなくなっていた。

これはかなり困る。
300日で破裂するbrainとどっちを取るかっていうとRSSがちゃんと配信されない事の方が嫌だったので、brainに保存するように変更した。


まとめ

brainを変更したほうがいい

0

Node.jsのfeedparserにドイツ語等を読ませる

ドイツ語などのヨーロッパの言語ではたまにåとかöみたいな文字が使われていて、UTF-8とかじゃなくISO-8859-1などの文字コードが使われている事があるらしい。

ドイツのspiegelという雑誌のRSSがちょうどISO-8859-1で、hubot-rss-readerで読むとところどころ文字化けするようになっていた。

こんな感じ


Node.jsでfeedを読む

feedの取得にはrequest、parseにfeedparserを使うのが多分普通なのだが、この2つはpipeでつないで使うようになっている。

で、最初は
Node.jsで文字コードの自動判別と自動変換 – Qiita
のようにjschardetで文字コードを判別して、iconvで変換してみた。でもrequest→feedparserがstreamなので少しずつchunkで送られてきて、日本語のマルチバイト文字の途中で区切りが来たりする事がある。そのchunkを1つずつjschardetにかけてしまうと全然違う文字コードが出てきたりする事があって良くない。

冷製になって考えたら、XMLなんだから頭に<?xml encoding=”ISO-8859-1″とか書いてあるのを読んでUTF-8じゃなかったら変換するstreamを作ればいい事に気づいた。

streamでXMLを食わせるとUTF-8に変換して吐き出すstream

というわけで、まずXMLのencoding attributeを読んで必要があればUTF-8に変換するstreamを返す関数を作る

charset-convert-stream.coffee
stream = require 'stream'
Iconv = require('iconv').Iconv

module.exports = ->

iconv = null

charsetConvertStream = stream.Transform()

charsetConvertStream._transform = (chunk, enc, next) ->
if m = chunk.toString().match /<\?xml[^>]* encoding=['"]([^'"]+)['"]/
charset = m[1]
if charset.toUpperCase() isnt 'UTF-8'
iconv = new Iconv charset, 'UTF-8//TRANSLIT//IGNORE'
if iconv?
@push iconv.convert(chunk)
else
@push chunk
next()

return charsetConvertStream


ドイツ語のfeedを読む

requestのon “response”からpipeでcharsetConvertStreamを通してfeedparserに繋ぐと、いい感じにUTF-8にエンコードされて出てくる。

FeedParser = require 'feedparser'
request = require 'request'
charsetConvertStream = require './charset-convert-stream'

feedparser = new FeedParser

req = request
uri: process.argv[2] or 'http://www.spiegel.de/schlagzeilen/tops/index.rss'
timeout: 10000
encoding: null # null指定でrequestが勝手にエンコードしなくなる
headers:
'User-Agent': 'test-feed-reader'

req.on 'error', (err) ->
console.error err

req.on 'response', (res) ->
if res.statusCode isnt 200
return console.error "statusCode: #{res.statusCode}"
this
.pipe charsetConvertStream()
.pipe feedparser

feedparser.on 'error', (err) ->
console.error err

feedparser.on 'data', (entry) -> # エントリーが1件ずつイベントで出てくる
console.log entry.title
console.log entry.summary or entry.description


文字化けなくなった

ここまでやって気づいたんだけど、 feedparser.meta[‘#xml’].encoding にエンコード情報が入っているのでこれを使ってtitleやdescriptionなど必要な所だけ取り出してからiconv.convertしても良かったかも・・

0

CircleCIでのAndroidビルドのメモリ使用量制限、SDKのアップデート

前:CircleCIでAndroidのプロジェクトをビルドする

色々やってプロジェクトが複雑化してきたら、2回ビルドできなくなった。けど解決した。

circle.ymlはこうなっている

machine:
java:
version: oraclejdk8
environment:
JAVA_OPTS: "-Xms518m -Xmx2048m"
dependencies:
pre:
- echo y | android update sdk --no-ui --all --filter "extra-android-m2repository,extra-android-support,extra-google-google_play_services,extra-google-m2repository"
test:
pre:
- cd mobile/src/main/java/org/shokai/voicetweet/ && cp TwitterConfig.java.sample TwitterConfig.java
override:
- ./gradlew assembleDebug

メモリが足りない


メモリを4GB以上使うとテストがこける。

Warning: You build has exceeded the memory limit of 4G on 1 container.


CircleCiでAndroid開発を爆速にする | Covelline Developer Blog
を参考に、machineのJAVA_OPTSでメモリ使用量を設定した。
AndroidのVMを起動してのテストせず、ビルドだけなので2GBに制限した。見ているとgradleからたまにjavaのプロセスがforkしているように見えたのでとりあえず4GBの半分使うようにしておいた。

CircleCIでの実行時は環境変数CIRCLECIが設定されるのでbuild.gradleも設定した。

android {
(略)
dexOptions {
if(System.getenv()["CIRCLECI"] as boolean) {
javaMaxHeapSize "2048M"
preDexLibraries false
}
}
}
あとCircleCIではdex(Dalvic VMの実行ファイルだが、次回コンパイルを高速にするらしい?)を生成しないようにした。


Android SDKのアップデート


play-service-wearableを6.5.87から8.3.0にアップデートしたら、CircleCIに8.3.0が無くてこういうエラーが出る

Could not find com.google.android.gms:play-services-wearable:8.3.0.
Searched in the following locations:
https://jcenter.bintray.com/com/google/android/gms/play-services-wearable/8.3.0/play-services-wearable-8.3.0.pom
https://jcenter.bintray.com/com/google/android/gms/play-services-wearable/8.3.0/play-services-wearable-8.3.0.jar
file:/usr/local/android-sdk-linux/extras/android/m2repository/com/google/android/gms/play-services-wearable/8.3.0/play-services-wearable-8.3.0.pom
file:/usr/local/android-sdk-linux/extras/android/m2repository/com/google/android/gms/play-services-wearable/8.3.0/play-services-wearable-8.3.0.jar
file:/usr/local/android-sdk-linux/extras/google/m2repository/com/google/android/gms/play-services-wearable/8.3.0/play-services-wearable-8.3.0.pom
file:/usr/local/android-sdk-linux/extras/google/m2repository/com/google/android/gms/play-services-wearable/8.3.0/play-services-wearable-8.3.0.jar


CircleCIのドキュメントによるとdependenciesのpreでandroidコマンドを実行してupdateできるとの事だったので、circle.ymlに設定した。

dependencies:
pre:
- echo y | android update sdk --no-ui --all --filter "extra-android-m2repository,extra-android-support,extra-google-google_play_services,extra-google-m2repository"
–filterオプションに渡せるパッケージ名は
% android list sdk --all --extended

で確認できる。