スマートメーターから現在の電力を得るAndroidアプリを作ってみた
以前Webアプリケーションを作っているうちにスマートメータから情報を得る手順がちょっとわかってきたんですね。
そこで自分が前に作ったUSBシリアル通信するAndroidアプリ
と一緒にするとAndroidアプリになるのでは?と考えたのでAndroidアプリにしてみた。作ったAndroidアプリ
瞬時電力ボタンを押すとスマートメーターから瞬時電力を得る。
それだけのアプリ。
「スマートメーターの情報を最安ハードウェアで引っこ抜く」と同じことをAndroidアプリにしただけ。
用意するもの
- Androidスマホとか
- RL7023 Stick-D/IPSまたはUSBシリアル変換器につないだBP35A1とか
- USB変換アダプタとかUSBハブとか
使い方
AndroidスマホのUSBポートに
RL7023 Stick-D/IPSをUSB Type C ハブにつないで(変換アダプタでもいいが)Androidスマホに接続する。
FT230X Basic UARTと見えているのがRL7023 Stick-D/IPSです。
(これしか接続していないから当然なんだが)
USBポートにFTDI USBシリアル変換アダプター(5V/3.3V切り替え機能付き)を追加してもきちんと2デバイス検出されるのを確認しています。
このアダプタはFTDI社製FT232RLなのでAndroidからFT232R USB UARTと見えている
このファイルに載っているICを使っているUSBシリアル変換器なら使える
USBシリアル変換器経由でBP35A1をつないでもいい
(試していないがテキスト形式で受信設定している場合はRL7023 Stick-D/IPSと同じはず)
設定で
- BルートID
- Bルートパスワード
を入力して、使用するUSBデバイスを選択してからアクティブスキャンボタンを押すと接続対象のスマートメーターを探す(1分位)ので、そのあと登録ボタンを押してください。
スマートメーターが見つからなければ電波状態が悪いまたはID/パスワードが間違っているので、Bルート設定情報を確認して
できればスマートメーターに近いところで再度アクティブスキャンを行ってください。
設定ができていればホームでポートを開いて瞬時電力ボタンを押せばスマートメーターから瞬時電力を取得します。
いま何ワット?
616Wだ。
なんとなくソースコードの紹介でも
New Project -> Basic Views Activityで新規作成
1つのActivity(MainActivity)と
2つのFragment(FirstFragment,SecondFragment)との構成になっているのでそのまま踏襲する。
設定Fragment
スマートメーターと接続するには
- BルートID
- Bルートパスワード
- 周波数チャネル(アクティブスキャンで手に入れる)
- PAN ID(アクティブスキャンで手に入れる)
- IPV6リンクアドレス(アクティブスキャンで手に入れる)
を設定する必要があるために設定Fragmentを作る。
参考
HomeFragment
ホームは表示できたらいいので雑にこうした。
Androidアプリアーキテクチャ
アプリ アーキテクチャ ガイドというのが公式サイトにあるので見てみた。
上記ページからの引用
アプリの推奨アーキテクチャ
DomainLayer(UseCase)はOptionalなので
- UI Layer
- Data Layer
に分割する構造を公式が推しているからこのアーキテクチャを採用する。
UI レイヤ
上記ページからの引用
図 3. UI は、画面上の UI 要素と UI 状態を足し合わせたものです。
UI 要素とUI 状態を分離するやり方と
単方向データフローで状態を管理するやり方は
The Elm Architectureみたいな感じか?
そうであるなら知ってる
Halogen(PureScriptのフレームワーク)と同じだから。
余談なのでたたんでおく
前に作ったWebアプリのここがStateの定義で
このStateがコンポーネントのrender関数に渡されてページを作る。この後handleAction関数からStateを更新してまたrender関数にstateが渡される流れでページを作っているから。
単方向データフローというものか。
Fragment / ViewModel / UI Stateを分離してみた。
ViewModelを使わないと横に倒すだけで情報が揮発するので。
データレイヤ
ライフサイクル
データレイヤ内のクラスのインスタンスは、ガベージ コレクション ルートから到達可能である限り、メモリ内に保持されます(通常はアプリの他のオブジェクトから参照されます)。
クラスにメモリ内データ(キャッシュなど)が含まれている場合、そのクラスの同じインスタンスを一定期間再利用することをおすすめします。これは、クラス インスタンスのライフサイクルとも呼ばれます。
クラスの役割がアプリ全体で重要な場合は、そのクラスのインスタンスのスコープを Application クラスに設定できます。これにより、インスタンスがアプリのライフサイクルに従うようになります。一方、アプリの特定のフロー(登録フローやログインフローなど)で同じインスタンスを再利用するだけであれば、そのフローのライフサイクルを持つクラスにインスタンスのスコープを設定する必要があります。たとえば、メモリ内データを含む RegistrationRepository のスコープを、登録フローの RegistrationActivity またはナビゲーション グラフに設定します。
こう説明されているので、このデータはApplicationクラスに管理させるとする。
USBシリアル通信
Androidでusb-serial-for-androidライブラリを使ってシリアル通信をする。
AndroidからUSBデバイスを検出する
まずは公式サイトを確認して。
今回はusb-serial-for-androidライブラリで検出する。
ここ
アクセサリとの通信の権限を取得する
コールバックは苦手なのでcallbackflowでコールバックをFlowに変換してrequestPermission()すると権限取得ダイアログを出してくれる
ソースはここ
そのほかシリアル通信はusb-serial-for-androidライブラリにまかせる。
アクティブスキャンする
ここがそう。
お互いにこんな会話をしている(エコーバックは省略)
ここまでで接続情報が揃ったので登録ボタンを押して保存する。
スマートメーターと接続する(PANAプロトコル)
つまりSKJOINを発行する。
瞬時電力計測値要求を発行する
SKSENDTOにこんなECHONET Lite電文を与える
1081000005FF010288016201E700
ここがそう。
瞬時電力計測値を取得する
ERXUDPを受け取って解釈する。
ここがそう
感想
KotlinのByteは符号付きです。
Represents a 8-bit signed integer.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-byte/
エラー処理をOption/Eitherにするかnull / Resultにするか迷ったがとりあえず
Either<Throwable, T> にしてみた。
GUIはしんどい。
意図的にオブジェクト指向部分を小さくしようと頑張ったけど、それなりのコード量になった。
Discussion