IoTマニ車

大晦日ハッカソン2018の成果です。

今日はこのマニ車のIoT対応をやります。マニ車を回した回数をWiFiでインターネット上のサーバーに送り、パソコンやスマホ等のブラウザに通知して表示させたいと思います。
f:id:licheng:20181231173648j:plain:w500

WiFiマイコンはおなじみのESP8266。ロータリーエンコーダ的なものは機構を作るのがめんどうなので、マニ車の回転の検出はジャイロセンサを使います。手持ちのジャイロセンサは、MPU-6050の上位互換のMPU-9250とLSM6DS3。どっちも定番のICですが、今回はMPU-9250を使用します。
f:id:licheng:20181231173705j:plain:w500

どっかでもらったデジットハッカソンの成果発表のときにいただいたダイセンの基板詰め合わせの中にちょうどいいサイズの基板があったのでこれに部品を載せて配線します。
f:id:licheng:20181231173727j:plain:w500

太い熱収縮チューブで基板ごとパックしたかったのですが、手持ちがないので代わりにマスキングテープでミイラパッケージ。
f:id:licheng:20181231173943j:plain:w500

お経のかわりに回路を納めたら回転しにくくなりました。鎖についた分銅に比べて中心部の質量が大きくなったせいと思われます。バッテリーを小さいものにかえて分銅側に固定することで改善しました。
f:id:licheng:20181231173957j:plain:w500

紙のお経を納めるスペースがなくなったので、ESP8266のROMに般若心経を納めました。volatile付けてもコードから参照しなかったら消されてしまったので、起動時にfor文で読むようにしました。
f:id:licheng:20181231174110p:plain:w500

IoTのバックエンドには以前使ったことのあるMilkcocoaを使いました。でももうちゃんと保守されてない感じですね。Milkcocoaは実体としてはMQTTブローカーサービスのようなので、次はHerokuあたりで自前でやってみたいと思います。MQTTブローカーはMosquittoとかいうのを使えばいいのかな? (しらんけど)

じっさいに動かした動画。マニ車を回した回数が、パソコン、スマホタブレットのブラウザにリアルタイムに表示されます。ちょっと回転の検出が甘いですが。


IoTマニ車

ロボットハンガー

アルミフレームを組んでホビーロボット用のハンガーを作りました。
f:id:licheng:20181216202724j:plain:w500
アルミフレームはモノタロウ等で手に入り、六角レンチ一本で組めて簡単に剛性と直角が得られるすごく便利な部材です。2020タイプ(20mm×20mm)、3030タイプ(30mm×30mm)、4040タイプ(40mm×40mm)などがありますが、ホビーロボットを吊るハンガーには2020タイプが適当でしょう。また、適当な長さにカットされたものを買うのが手軽です。バンドソーなどの電動工具があるなら自分でカットするのもよいでしょうが、金ノコで切るのはけっこう根性がいると思います。
参考までに上の写真のロボットハンガーに使った部材を以下に示します。

フレームの溝にTナットを通し、ブラケットを当ててボルトで締めて固定するだけです。プラスネジより六角穴付ボルトの方が頑丈に締められます。注意すべきは、十字に重ねて組む場合にはDブラケットSSでは組めないのでアングルブラケットSSを使うということ。またアングルブラケットSSの固定に10mmのボルトでは長すぎるので、8mmのボルトを使います。(ということに後から気づいたので、じつは写真の作例では手持ちのM5プラスネジで代用しました。)いっそ全部アングルブラケットでもよかったかも。

上に渡した梁の適当な位置にTナットを通して10mmのボルトを締めます。このボルトにロボットを吊るすヒモを引っかけます。写真のような片持ち梁では、ロボットを吊って動かしたときに倒れないようにバランスに気をつけます。

むかし市販されてたLUBICのロボットハンガー組立キットではぜんぶアングルブラケットでプラスネジ固定でしたね。ホビーロボットを吊るすくらいなら十分な強度なのでしょう。

SyncToyでフォルダの同期

Windowsで2つのフォルダの内容を同期するには昔はRealSyncを使ってました。当時はたいへん重宝しましたが、もう十年以上前に開発が止まっているようです。今は何が良いのか、なかなか決定版というのがみつかりません。まあ、今どきはDropBox等のクラウドストレージを使うことが多いので、こういうツールの出番は少なくなってるのでしょう。しかしまったく出番が無いわけでもありません。今回はSyncToyというツールを試してみました。

SyncToyとは?

SyncToyのインストール

SyncToyの設定 (フォルダ・ペアの作成)

  • インストールしたSyncToyを起動
  • [Create New Folder Pair]ボタンをクリック
  • [Left Folder]に同期元フォルダ、[Right Folder]に同期先フォルダを指定
  • "What do you want to do?" で、同期のモードを指定
    • [Synchronoize]: 左側フォルダ⇔右側フォルダ の双方向に同期
    • [Echo]: 左側フォルダ→右側フォルダ の片方向に同期
    • [Contribute]: 左側フォルダ→右側フォルダ の片方向に同期 (ただし削除は行わない)
  • "Name your folder pair" で、このフォルダ・ペアに適当な名前をつける

SyncToyの実行 (フォルダ・ペアの同期)

  • SyncToyを起動
  • 実行したいフォルダペアを左のペインから選択
  • [Run]ボダンをクリックして同期を実行
  • [Preview]ボタンをクリックすると同期を実行はせずに、同期されるべきファイルを確認できる。

Androidアプリの書き方思い出し(3)

オブジェクトのシリアライズ

オブジェクトをIntentに持たせたりSharedPreferencesに保存する場合、JSON形式でシリアライズして文字列として扱う。JSON形式のシリアライズにはGSONというライブラリを使う。
まず、Moduleのbuild.gradleに依存関係を追記する。

dependencies {
    compile 'com.google.code.gson:gson:2.8.5'
}

シリアライズは、GsonクラスのtoJsonメソッドを用いる。

Hoge hoge; // シリアライズしたいオブジェクト

Gson gson = new Gson();
String jsonStr= gson.toJson(hoge);

シリアライズは、GsonクラスのfromJsonメソッドを用いる。

String jsonStr; // デシリアライズしたい文字列

Gson gson = new Gson();
Hoge hoge = gson.fromJson(jsonStr, Hoge.class);

Androidアプリの書き方思い出し(2)

設定データの保存/読み出し

SharedPreferencesを使う。
おなじみのキーバリューストア方式である。

// 保存
SharedPreferences pref = getSharedPreferences("Hoge",MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putString("Key1", "Piyo");
editor.putInt("Key2", 123);
editor.commit(); // commit()だとファイルIOが同期待ち。apply()だと非同期。

// メソッドチェーンして1行で書いてもよい
pref.edit().putString("Key1", "Piyo").commit();
// 読み出し
SharedPreferences pref = getSharedPreferences("Hoge",MODE_PRIVATE);
// 第2引数は何も入っていないときの初期値
String piyo = pref.getString("Key1", "Foo");
int fuga = pref.getInt("Key2", 456 );

画面遷移時に引数を渡す

Intentにキーバリューストア方式で種々のデータを持たせる。

// 呼び出し元
Intent intent = new Intent(getApplication(), HogeActivity.class);
intent.putExtra(“Key1”, "Piyo");
intent.putExtra(“Key2”, 123);
startActivity(intent);
// 呼び出し先
Intent intent = getIntent();
String Piyo = intent.getStringExtra(“Key1”);
int fuga = intent.getIntExtra(“Key2”, 456);

元画面に戻るときに戻り値を返す

startActivityのかわりにstartActivityForResultを使う。
第2引数の要求コードは任意の定数を与える。

private static final int REQUEST_CODE = 1; // 任意の定数

Intent intent = new Intent(getApplication(), HogeActivity.class);
startActivityForResult(intent, REQUEST_CODE);

呼び出し先Activity で Intentに種々のデータを持たせる。
setResultで結果コード(OKかキャンセルかなど)とIntentを設定して、finishする。

Intent intent = new Intent();
intent.putExtra("Key1", "Piyo");
setResult(RESULT_OK, intent);
finish();

呼び出し元Activity で onActivityResult が呼ばれる。
第1引数の要求コードで、どの呼び出しからの戻り値か判別できる。
第2引数の結果コードで、OKかキャンセルかなどが判別できる。
第3引数のIntentから種々のデータを取り出せる。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    // 要求コードごとの処理
    switch(requestCode){ 
        case(REQUEST_CODE):
            // 結果コードごとの処理
            if(resultCode == RESULT_OK){
                String piyo = data.getStringExtra("Key1");
            }else if(resultCode == RESULT_CANCELED){
                ;
            }else{
                ;
            }
            break;
        default:
            break;
    }
}

Androidアプリの書き方思い出し

画面遷移

サブ画面に移るには、Intentを作ってstartActivityする。

  Intent intent = new Intent(getApplication(), サブ画面Activityのクラス名.class);
  startActivity(intent);

サブ画面のActivityを作るには、[Projact]ウィンドウの[app]を右クリックして[New] > [Activity] > [Empty Activity] を選んであとは指示に従って名前などを設定する。クラスのjavaソースとレイアウトのxmlソースのひな型が生成され、AndroidManifest.xmlにもActivityの宣言が追加される。
元の画面に戻るには、サブ画面のActivityをfinishする。

  finish();

画面のタイトル

AndroidManifest.xmlの<activity>要素にandroid:labelで指定する。

<activity android:name=".HogeActivity"
    android:label="@string/hoge_title">
</activity>

リスト表示

  • レイアウトにListViewを配置し、idを記述する。
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/listHoge"/>
  • ActivityのコードでListViewオブジェクトを定義してレイアウトと紐づける。
  • 表示したい内容物(ここでは文字列の配列)を含んだArrayAdapterオブジェクトを作る。
  • ArrayAdapterオブジェクトをListViewオブジェクトにセットする。
public class HogeActivity extends Activity {

    private ListView listHoge;

    private static final String[] items = {
        "Itme1", "Itme2", "Itme3"
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activityHoge);

        listHoge = (ListView)findViewById(R.id.listHoge);
        ArrayAdapter<String> arrayAdapter =
                new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
        listHoge.setAdapter(arrayAdapter);
    }
}
  • リストのどれかをタップされたイベントの処理はsetOnItemClickListenerで記述する。
        listHoge.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // position 番目のアイテムがタップされたときの処理
            }
        });

Fireタブレット

AmazonのFireタブレットは異様に安い。そのかわりAmazon特製オレオレAndroidなので、ふつうのAndroidタブレットとしては使いづらい。とくかくAmazonを利用すること中心のユーザー体験を強いられる。まあ、Amazonの販促ツールなのだから当然といえる。

ぼくは「安いAndroidタブレット」としてFireタブレットを選ぶことはオススメしない。ふだん使いのふつうのAndroidタブレットが欲しかったら真っ当なお値段の真っ当なAndroidタブレットを買ってください。

ただしまあ、Fireタブレットはとくかく安いので、開発者なら開発用・デモ用に1台くらい予備に持っててもいいんじゃないかと思う。最新のOSに追従したいアプリ開発者にとっては微妙かもしれんけど、ぼくのような組込み系開発者が趣味の開発に使うぶんにはじゅうぶん間に合う。

開発者向けオプション

Fireタブレットも通常のAndroid機とほぼ同様に、[設定] > [デバイスオプション] > [Fireタブレットのバージョン情報] で [シリアル番号] を何度もタップすると、[デバイスオプション] のメニューに [開発者向けオプション] が出現する。続いて [開発者向けオプション] のメニューを [オン] にして、さらに [USBデバッグ] を許可する。これで、Android Studioからアプリのインストールとデバッグをおこなうことができる。

Android Studioから接続したところ。Fire OS 6.3.0.1の正体はAndroid 7.1.2ベースらしい。
f:id:licheng:20181212195349p:plain

ホームアプリを変更

FireタブレットのホームアプリはAmazonの利用を前面に押し出してくるので厄介。デモで人に見せる時に昨日買ったマンガのタイトルがバレるのは困る(笑)

このホームアプリはふつうの方法では変更できない。だってAmazonの販促ツールだから。そこで、ごにょごにょしてホームアプリを変更する。べつにroot化したりするわけじゃないけど、野良APKをインストールするので開発者じゃない方にはまったくオススメしません。ここに書いたことは一切自己責任で行ってください。

(1) Google Playストアをインストール
  • [設定] > [セキュリティとプライバシー] で [不明ソースからのアプリ] を許可。
  • 以下の野良APKをFireタブレットのブラウザ(Silk)でダウンロードしてインストール。
  • かならずこの順番どおりにインストールすること。

【Fire OS 5】

  1. Google Account Manager, 5.1
  2. Google Services Framework, 5.1
  3. Google Play services 11.7.44
  4. Google Play Store 8.5.39

【Fire OS 6】

  1. Google Account Manager 7.1.2
  2. Google Services Framework 7.1.2
  3. Google Play services 14.3.66
  4. Google Play Store 12.0.19
(2) Nova Launcherをインストール
  • インストールしたGoogle Playストアを開き、サインインする。
  • Nova Launcherというホームアプリを検索し、インストールする。
  • Nova Launcherを起動し、適宜設定する。
(3) LauncherHijackをインストール
  • LauncherHijackのAPKをブラウザでダウンロードしてインストールする。
  • [設定] > [ユーザー補助] の [To detect home button press] を有効にする。
  • Launcher Hijackを起動するとランチャーアプリを選択する画面になるので、Nova Launcherを選択する。
  • [設定] > [セキュリティとプライバシー] で [不明ソースからのアプリ] を許可しないように戻しておく。

以上で、FireタブレットがまあまあふつうのAndroidタブレット風になる。