Capacitor をつかって NanoVNA-Web-Client を Android アプリ化してみた。

あんまりハマりどころはなかったのでそれほど書くことはないけど、いくつかメモ。

node_modules

アプリサイズを減らすためにできるだけリソースを減らす必要がある。node_modules 以下を雑に入れると数十MBになってしまうので、気をつける。

Web 版のリソースを完全にそのまま入れるより、必要なリソースだけをコピーするようなビルドスクリプトを書いたほうが良さそう。今回は Makefile でなんとかした。

splash 画像の生成自動化

Ionic のブログに Photoshop template があるので、これを利用させてもらう。

が、ブログの通り cordova-res を使うとうまくいかなかったので、Makefile と ImageMagick でなんとかした。

res:
	convert images/splash.png -gravity center -resize 480x320^ -extent 480x320 ./android/app/src/main/res/drawable/splash.png
	convert images/splash.png -gravity center -resize 800x480^ -extent 800x480 ./android/app/src/main/res/drawable-land-hdpi/splash.png
	convert images/splash.png -gravity center -resize 480x320^ -extent 480x320 ./android/app/src/main/res/drawable-land-mdpi/splash.png
	convert images/splash.png -gravity center -resize 1280x720^ -extent 1280x720 ./android/app/src/main/res/drawable-land-xhdpi/splash.png
	convert images/splash.png -gravity center -resize 1600x960^ -extent 1600x960 ./android/app/src/main/res/drawable-land-xxhdpi/splash.png
	convert images/splash.png -gravity center -resize 1920x1280^ -extent 1920x1280 ./android/app/src/main/res/drawable-land-xxxhdpi/splash.png
	convert images/splash.png -gravity center -resize 480x800^ -extent 480x800 ./android/app/src/main/res/drawable-port-hdpi/splash.png
	convert images/splash.png -gravity center -resize 320x480^ -extent 320x480 ./android/app/src/main/res/drawable-port-mdpi/splash.png
	convert images/splash.png -gravity center -resize 720x1280^ -extent 720x1280 ./android/app/src/main/res/drawable-port-xhdpi/splash.png
	convert images/splash.png -gravity center -resize 960x1600^ -extent 960x1600 ./android/app/src/main/res/drawable-port-xxhdpi/splash.png
	convert images/splash.png -gravity center -resize 1280x1920^ -extent 1280x1920 ./android/app/src/main/res/drawable-port-xxxhdpi/splash.png
	optipng $(wildcard ./android/app/src/main/res/*/splash.png)

Play Store のメモ

リリースしたことある気がしていたが、よく考えたら前職で数回やっただけなので個人のアカウントではやったことがなかった。いろいろストアの使い勝手が変わっていた。

  • 初回の審査は結構時間がかかる。一週間ぐらい? 更新の審査は割とすぐ。
  • 要求される画像がいっぱいあってだるい。全部埋めないとベータ版すらリリースできない
  • adb という仕組みができていた。(端末にあわせて必要なリソースだけを含む apk を自動生成する)

無料アプリの開発者視点からすると、別に Play Store に出すメリットはぜんぜんない。他の端末にさくっと入れたいときに便利かな、と思う。けど、それも普通にウェブサイト経由で .apk ダウンロードして入れればいいだけなので、それほど致命的に面倒くさいわけではない。まぁ Play Store のショバ代は初回に $25 なので Apple (毎年$99もとる) よりは格段に優しいといえる。

  1. トップ
  2. tech
  3. NanoVNA WebApp の Android アプリ化
  1. トップ
  2. nanovna
  3. NanoVNA WebApp の Android アプリ化

NiZ のアプリケーションは Windows 向けしかない。プロトコルが気になったので、とりあえず打鍵回数を読み出すところをまでをやってみた。環境は macOS で hid_api gem を使い、Ruby で書いた。

できたもの

実行するとこういう出力をする。最後の数字がキーに対応するカウント。

key=  1 hwcode=   1             ESC: 35
key=  2 hwcode=  15               1: 66
key=  3 hwcode=  16               2: 92
key=  4 hwcode=  17               3: 114
key=  5 hwcode=  18               4: 119
key=  6 hwcode=  19               5: 53
key=  7 hwcode=  20               6: 99
key=  8 hwcode=  21               7: 18
key=  9 hwcode=  22               8: 115
key= 10 hwcode=  23               9: 76
key= 11 hwcode=  24               0: 171
key= 12 hwcode=  25               -: 307
key= 13 hwcode=  26               =: 78
key= 14 hwcode=  41               \: 38
key= 15 hwcode=  14               `: 33
key= 16 hwcode=  28             TAB: 575
key= 17 hwcode=  29               Q: 253
key= 18 hwcode=  30               W: 627
key= 19 hwcode=  31               E: 1461
key= 20 hwcode=  32               R: 911
key= 21 hwcode=  33               T: 1137
key= 22 hwcode=  34               Y: 616
key= 23 hwcode=  35               U: 1317
key= 24 hwcode=  36               I: 1662
key= 25 hwcode=  37               O: 1536
key= 26 hwcode=  38               P: 549
key= 27 hwcode=  39               [: 584
key= 28 hwcode=  40               ]: 41
key= 29 hwcode=  27              BS: 1662
key= 30 hwcode=  67          L-CTRL: 18
key= 31 hwcode=  43               A: 1794
key= 32 hwcode=  44               S: 914
key= 33 hwcode=  45               D: 853
key= 34 hwcode=  46               F: 249
key= 35 hwcode=  47               G: 520
key= 36 hwcode=  48               H: 828
key= 37 hwcode=  49               J: 671
key= 38 hwcode=  50               K: 1424
key= 39 hwcode=  51               L: 696
key= 40 hwcode=  52               ;: 452
key= 41 hwcode=  53               ': 155
key= 42 hwcode=  54             RET: 1281
key= 43 hwcode=  55         L-Shift: 1610
key= 44 hwcode=  56               Z: 177
key= 45 hwcode=  57               X: 128
key= 46 hwcode=  58               C: 459
key= 47 hwcode=  59               V: 253
key= 48 hwcode=  60               B: 384
key= 49 hwcode=  61               N: 1352
key= 50 hwcode=  62               M: 482
key= 51 hwcode=  63               ,: 383
key= 52 hwcode=  64               .: 255
key= 53 hwcode=  65               /: 161
key= 54 hwcode=  66         R-Shift: 850
key= 55 hwcode= 156            R-Fn: 135
key= 56 hwcode=  67          L-CTRL: 1653
key= 57 hwcode= 166            L-Fn: 6
key= 58 hwcode=  69           L-Alt: 35
key= 59 hwcode=  68           Super: 703
key= 60 hwcode=  70           Space: 2037
key= 61 hwcode=  68           Super: 7
key= 62 hwcode=  71           R-Alt: 5
key= 63 hwcode=  74          R-Ctrl: 5
key= 64 hwcode=  88      Left Arrow: 24
key= 65 hwcode=  89      Down Arrow: 19
key= 66 hwcode=  90     Right Arrow: 34
key= 67 hwcode=   0                : 0
key= 68 hwcode=   0                : 0
key= 69 hwcode=   0                : 0
key= 70 hwcode=   0                : 0
key= 71 hwcode=   0                : 0
key= 72 hwcode=   0                : 0
key= 73 hwcode=   0                : 0
key= 74 hwcode=   0                : 0
key= 75 hwcode=   0                : 0

プロトコルの概要

すべて HID 上で行われる。64bytes 固定で read/write する。

日本語で説明するよりソースコード読んだほうが早いので説明するのをやめる。

カウントを得るためにすること

キーマップを得る

NiZ キーボード側から、現在のキーマップを取得する。66キーのモデルなので、66キー × 3レイヤー (ノーマル・左Fn・右Fn) で 198 個の論理キー定義がある。

これによって、キーID (1〜66) と、割当られているキーコード (.pro ファイルの HWCODE) のマッピングを得られる。

カウント一覧を得る

カウントは66個の数値の配列のようなものが得られる。インデックスがそれぞれキーIDに対応していると思われる。

インデックスからキーマップを得てカウントと共に表示すればおわり。

備考

macOS だからか、実装が悪いのか、どこが悪いのかわからないが、挙動が不安定

  • HidApi.open がしばしば失敗する
  • read に失敗する (タイムアウト)

何回か繰替えすと成功する。

hid_api

hid_api は timeout のメソッドがあるにも関わらず timeout の処理が抜けているので、モンキーパッチを書いてる。PR 作ろうかと思ったけど結構放置されてそうでめんどい。

macOS でもキーマップを書きかえたいんだけど???

read した形式で書き戻せばいいだけだと思う。

どうやって解析したか?

Windows に USBPcap を入れて、公式のアプリケーションを操作しながらログをとった。

あとは気合

ソースコード

https://github.com/cho45/niz-tools-ruby github にうつしました

>

  1. トップ
  2. tech
  3. NiZ Keyboard PLUM からキーマップや打鍵回数を読み出す

(この写真のキー配列はデフォルトのものではなく、既にカスタマイズ済みです。mac 用のキーキャップが付属しており、HHKB を基準にできるだけキーキャップもあわせています)

3行で

  • 信頼性のあるキーボードがほしいなら買わないほうが良い
  • 打鍵感は十分に良い
  • これにしかない魅力はある

NiZ というキーボード

https://www.nizkeyboard.com/ が公式っぽい。中華製の静電容量無接点キーボード (HHKBやRealforceなどと同じ分類)。

HHKB と比べると以下のような特徴がある

  • 35g / 45g を選べる。35g モデルでも 10g 増やすバネで、特定のキー(ホームポジションとか)だけ荷重を増やせる
    • HHKB は 45g キーストローク 4mm
  • キー数のバリエーションが多い
  • 完全にキーマップをカスタマイズ可能 (ファームウェアレベルで行う)
  • Type-C コネクタ (mini-B よりも機械的に丈夫なコネクタ)
  • Bluetooth 接続対応・バッテリー内蔵
  • 価格は若干安い

価格が安いだけなら買うつもりはなかったけど、キーが HHKB よりも軽くて Bluetooth 接続が可能で、Type-C 接続な点で気になった。66キー(HHKBとほぼ同じ大きさ) 35g のモデルを買ってみた。スペック通りなら HHKB 信者を止めることになる。

初期設定とハマったところ

デフォルトだと「Office モード」というモードらしく、キーのリマップが効かないモードになっている。 Fn+M キーを同時に2秒押して Program モードというモードにしなくてはいけない。

専用アプリを使ってのキーカスタマイズはこのモードでしか適用されない。

キーが効きにくい……

試しうちしてすぐ気付いたが、左 Ctrl (というか位置的には Caps-Lock)、スペース、RET、Shift が叩くように入力しないと反応しない、または普通に使っていて入力をとりこぼすことがわかった。

https://www.nizkeyboard.com/2019/07/07/some-keys-doesnt-work-look-at-this/ 公式にこういうエントリがあるぐらいなので頻出問題みたい。手順では F2 F10 を押しながらキャリブレーションすると書いてあるけど、単に基準となるキーが変わるだけで、どのキーを押しながらでもキャリブレーションできる。自分の場合は左 Shift + スペースを軽く押しながらキャリブレーションすることで、気になる範囲の取りこぼしはなくなった気がする。

キーボードの信頼性

市販のキーボードは「普通に動く」けれど、それはもうそれだけですごいのだ、ということに気付く。キーボードの信頼性というのはそこで、普段意識するようなことはない。最低限ちゃんと思った通りに入力できなければならない。

NiZ は(自分の場合は)キャリブレーションなしには全く使いものにならないものだった。普通に考えたら初期不良だし、キーボードをキャリブレーションしたらなおるとか思いもしないだろう。そういう初期調整も含めての安価な値段だと思う。

NiZの良い点

良い点は冒頭でもほとんど記したけど

  • 打鍵が軽い
  • スコスコしたスムーズな打鍵感
  • 打鍵音が静か (HHKB と比べると雲泥の差)
  • カスタマイズ性
  • Bluetooth

とかいろいろある。このキーボードにしかない特徴があるので、うまくキャリブレーションするか、あたり個体を掴めれば良い選択かもしれない。

とりあえず普段遣いできる感じのキャリブレーションはすることができたので、しばらく使ってみるつもり。

細かいメモ

USB デバイスとしては `ID 0483:512a STMicroelectronics 66EC-S ` で認識されている。STM32 っぽい……と言いたいところだけど、中華デバイスは STM32 互換の MCU に STM32 のツールで作ったファームウェアを書いていたりするので、これだけでは判断できない。

  1. トップ
  2. tech
  3. NiZ Keyboard PLUM という静電容量無接点キーボード

デバッグ機能の分類

  • デバッグ制御インターフェイス : CPU制御を行う。ブレークポイントやプログラムカウンタの増加(実行)など
  • トレース機能: 実行履歴を出力する機能。デバッガではいちいち実行を止めるため、実際の速度で動かしたときのタイミング問題などを検出するのは難しい。そこでトレース機能が必要になる。

デバッグ制御インターフェイス

  • ICE: in-circuit emulator CPU の代わりに回路中のCPUをエミュレーションするもの。元々は専用の IC だったが、最近のMCUはオンチップエミュレータとなっている。
  • JTAG デバッグ制御インターフェイス。元々はデバッグ用ではなく検査用。5線で通信する。各ピンの状態がわかる (バウンダリスキャン)、というのが本来の用途。
  • JTAG エミュレータ: JTAG をデバッグ制御として、オンチップエミュレータを操作する形のICE
  • SWD デバッグ制御インターフェイス 2線ですむ。ARM MCU はこれが主流。デバッグ専用で、検査ポートとしては使えない (バウンダリスキャンはできない)。プロトコルは32bit値のR/Wに特化している。

JTAG も SWD も CPU 内部のレジスタを読み書きすることで、CPU 側にある任意の機能を呼び出している。なので、デバッグプロトコルは JTAGとSWDの上に乗っており、CPU によって異る。

トレース機能

  • ETM: Embedded Trace Macrocell 分岐のたびにPC情報を得ることでトレースする。
  • SWV: Serial Wire Viewer PCをサンプリングすることでトレースする。(サンプリングなので抜けが発生しうる)

(高級な print デバッグを想像するとよい。が、自分は使ったことがないのでわからない)

ARM 関連

  • CMSIS-DAP: Cortex Microcontroller Software Interface. Standard - CoreSight Debug Access Port . ARM が定義した USB と SWD/JTAG インターフェイスのプロトコル
  • ARM CoreSight: ARM Core 内のデバッグ用コンポーネント

USB から CMSIS-DAP を使って JTAG/SWD を操作し、CPU 内部の CoreSight に接続する、という感じ。

USB デバッガ

  • SEGGER J-Link: JTAG/SWD を USB 経由で扱うインターフェイス
  • ST-Link: STM8/STM32 専用の JTAG/SWD を USB 経由で扱うインターフェイス
  • LPC-Link2: CMSIS-DAPデバッガにしたり SEGGER J-Link にしたり Redlink にしたりできるボード。

このへんややこしいが、「SERGGER J-Link」はデバッガハードウェアの名称でもあり、USB 接続時のプロトコルでもある。

ST-Link も同様で、ST-Link2 ST-Link3 というデバッガハードウェアもあるし、USB 接続時のプロトコルでもある。

LPC-Link2 は USB 接続時のプロトコルとして SEGGER J-Link / CMSIS-DAP / Redlink (nxp の独自プロトコル) を切り替えられるボード。

結局どのプロトコルでも、同じCPUならできることは一緒。だが、ホスト側のドライバの関係でパフォーマンスに違いがでることがある。

  1. トップ
  2. tech
  3. JTAG/SWD 関係の用語

https://github.com/GoogleChromeLabs/comlink

「Comlink makes WebWorkers enjoyable.」と書いてあるが、キモは postMessage の抽象化なので、iframe 間通信にも使用することができる。ただ、origin の取り扱いをする必要があるので、ラッパーが必要。といってもラッパーも Comlink.windowEndpoint() という形で Comlink 自身に用意されている。

iframe の親でも小でも、どちらでも Comlink.expose することができる (役割は反転できる)。ただ、その際に該当する window インスタンスが必要なので、iframe の親側では小フレームのロードを待たなければならない。

comlink のレポジトリに iframe の例もある

  1. トップ
  2. tech
  3. Comlink は iframe 間通信にも使える

実行コンテキストの違い

Cordova は file: で実行される。Capacitor は http: (Android) で実行される。

この違いは案外大きい。ESM (type="module" などでロードされる ECMAScript Modules) は application/javascript でなければ実行されないという厳格なルールがあるが、file:// ではすべてのファイルに mime-type が設定されないため、実行できない。回避する方法は結局のところ localhost にサーバを立てるぐらいしかない。この点だけでももう Cordova は厳しい。

アプリケーションの本体はどこに…

Cordova はビルド時に必要なソースコードを生成する。このため実際のビルドは必ず cordova コマンドを介す。

Capacitor はソースコードのスキャフォルドを最初に生成する。レポジトリには各プラットフォームの完全なプロジェクトが生成される。このため、実際のビルドは Android Studio や Xcode など、ネイティブな環境で行われる。

  1. トップ
  2. tech
  3. 3秒でわかる Capacitor と Cordova の違い

Android Debug Bridge (adb)  |  Android Developers に従うだけ。

一旦 USB で接続して、デバイス側の adb を tcpip 経由に変更する

adb tcpip 5555

USB 接続を外して、adb connect [device ip address] を実行する

 adb connect device_ip_address

細かい手順は公式の説明にちゃんと書いてある。これで手元の端末 (Huawei P20) ではうまくいった。

これで adb は WiFi 経由でデバイスを認識しているので、adb install なども WiFi 経由で可能になる。

Android Chrome の Wi-Fi 経由デバッグ

上記手順を踏んでいれば chrome://inspect を開くと該当デバイスの Chrome インスタンスも見えているはず。そのままデバッグできる。

なぜ WiFi 経由で adb したいか

Android をホストとして USB デバイスを接続していると、USB ポートは必ず埋まってしまう。もし USB で adb 接続しようと思うと大変煩雑になる

なんか接続が切れる

デバイス側のセキュリティ要件なのかわからないが、しばらく経過すると、勝手に USB デバッグのチェックが外れたりする。よくわかってない。

とりあえず wakelock をとったほうがいいのかも

  1. トップ
  2. tech
  3. root化しなくても Wi-Fi 経由での adb はできる

GitHub Pages を GitHub Actions で PDF 化して releases にアップロードするというのをやってみた。

やりかた

  • deploy された GitHub Pages を wkhtmltopdf で PDF 化
  • ghr で PDF を releases にアップロード

が基本的なところ。GitHub Pages のデプロイは Actions よりも遥かに早く終わるので、厳密に同期をしていない (Actions で Jekyll を起動して HTML を生成するのが正攻法だけど、この方法はものすごく時間がかかってしまう)。

wkhtmltopdf は openlabs/docker-wkhtmltopdf という Docker イメージを利用した。ただし、このイメージには日本語フォントが入っていないので、ちょっと工夫する必要がある。具体的にはレンダリングしたい HTML 側で、明示的に Google Fonts などから Web Fonts としてロードしてやる必要がある。

ghr は go を入れるところからやってしまったが、たぶん ghr の releases からバイナリ落としてきて展開するほうが早いと思う。そのうち変更するかも。

実際の設定

name: document release

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
        
    - name: Set up Go 1.12
      uses: actions/setup-go@v1
      with:
        version: 1.12
      id: go
      
    - name: Convert HTML to PDF
      run: |
        docker run \
        -v ${{ github.workspace }}:/srv/jekyll  -v ${{ github.workspace }}/_site:/srv/jekyll/_site \
        openlabs/docker-wkhtmltopdf  --print-media-type https://cho45.github.io/NanoVNA-manual/ /srv/jekyll/_site/NanoVNA-manual.pdf

    - name: Upload to releases
      env:
        GO111MODULE: on
        GOPATH: /home/runner/work/
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        go get -u github.com/tcnksm/ghr
        $GOPATH/bin/ghr -u cho45 -r NanoVNA-manual -recreate -replace -n latest -b "latest" latest ${{ github.workspace }}/_site/NanoVNA-manual.pdf
  1. トップ
  2. tech
  3. GitHub Pages を GitHub Actions で PDF 化して releases にアップロード

以下のようなテーブルのとき

CREATE TABLE foo
(
   aset set('aaa', 'bbb', 'ccc')
);
  • 空文字列を SET のメンバーとして許していない
  • null 可

値を追加する

UPDATE foo SET `aset` = CONCAT_WS(',', NULLIF(aset, ''), #{member});

aset が空文字列(emptyset)のとき、これをそのままカンマで CONCAT_WS すると、",ccc" とかになり、空文字列のメンバーは許容していないため data truncated でエラーになってしまう。

CONCAT_WS は引数が null のときはそれを飛ばして連結するので、それを利用する。

ref. https://stackoverflow.com/questions/14642658/the-best-way-to-remove-value-from-set-field

値を削除する

UPDATE foo SET `aset` = TRIM(BOTH ',' FROM REPLACE(CONCAT(',', aset, ','), CONCAT(',', #{member}, ','), ','))

もっとややこしい。というのも ",aaa" も "aaa,,ccc" も "aaa," も不正なので、こういう値にならないようにしないといけない。

備考

SET は実際は 64bit の数値なので、SET のメンバー名(文字列)を2の乗数の数値に変換する方法があれば、単にビット演算ですむ。けど、MySQL 上にはこの方法 (メンバー名を SET の数値に直接変換する) がない(と思う)。アプリケーション側で数値と文字列のマッピングを持てば SQL は簡単になるが DDL と常に整合性をとる必要があってややこしく、それなら SET じゃなく BIGINT UNSIGNED で持てばいいことになる。

なんかいい方法あったら教えてください (SET 使うな以外で)

  1. トップ
  2. tech
  3. MySQL で SET 型の UPDATE

ある程度きちんと網羅されたのがなかったので自分で書いていた。コード読みながら書いてるので、誤解がなければ正しいことが書いてあるはず。とはいえ完全に網羅できてない (electrical delay まわりとかの記述がまるっと抜けている)。

  1. トップ
  2. tech
  3. NanoVNA のユーザーガイド(マニュアル)を書いた
  1. トップ
  2. nanovna
  3. NanoVNA のユーザーガイド(マニュアル)を書いた

ソニー 広角単焦点レンズ フルサイズ FE 28mm F2 デジタル一眼カメラα[Eマウント]用 純正レンズ SEL28F20 - ソニー(SONY)

ソニー(SONY)

5.0 / 5.0

ソニー FE マウントは 55mm しか持っていなかったが、28mm もついに買ってしまった。FE マウントでは安い部類なのでさっさと買えばよかった。