ロボットハンガー

アルミフレームを組んでホビーロボット用のハンガーを作りました。
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タブレット風になる。

x86インラインアセンブラ (2)

x86インラインアセンブラ - 滴了庵日録 のつづき。

メモ

  • 配列もC言語の変数名がそのまま使える。
  • ただし、アドレスのスケーリングは明示する必要がある。
  • 32ビット整数ならスケーリングは *4 (任意の数を指定できるわけではない)。
  • ブロック転送には、esiとediをセットで使う。
  • 8086のころは ds:[si] と es:[di]がセットだったが、今はセグメントレジスタ(dsやes)は使わない。
  • ストリング命令(movsbなど)はリピートプリフィックス命令(repなど)とセットで使う。

配列の総和を求めるコード

#include <stdio.h>

int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    int sum;

    __asm{
        xor eax, eax
        xor ecx, ecx
    _LOOP:
        add eax, a[ecx * 4] // 32ビット整数なら*4 
        inc ecx
        cmp ecx, 5
        jl _LOOP
        mov sum, eax
    }
    printf("%d\n", sum);
    return 0;
}

スタックを使う

#include <stdio.h>

int main(void)
{
    int a, b, c;
    __asm{
        push 1
        push 2
        push 3
        pop a // a に 3 が入る
        pop b // b に 2 が入る
        pop c // c に 1 が入る
    }
    printf("%d %d %d\n", a, b, c);

    return 0;
}

文字列のコピー

int main(void)
{
    char a[] = "hello, world!\0";
    char b[14];

    __asm{
        mov ecx, 14 // カウントレジスタ
        lea esi, a  // ソースインデックス
        lea edi, b  // デスティネーションインデックス
        cld         // アドレスはインクリメント sldならデクリメント
        rep movsb   // ecxぶんだけ1バイトずつコピー
    }
    printf("%s\n", b);
    return 0;
}