【IoT】ESP8266で温湿度取得してスマホでモニタリング 第2回


はじめに

【IoT】ESP8266で温湿度取得してスマホでモニタリング 第1回に引き続き、第2回はAndroidアプリ側を制作したので投稿します。
グラフはネットで調べたところ、MPAndroidChartが広く使われている(?)ようだったので使ってみました。

Xamarin.Android

開発環境は、前回に引き続きVisual Studio Community 2017を使っています。
Xamarin.Androidインストール手順
公式サンプルソース

MPAndroidChartライブラリ

Xamarinで使えるMPAndroidChartはこちらからソースをダウンロードできます。
ダウンロードはここからgithub
ダウンロード後、ソースをビルドすると各種DLLが生成させるようです。
(私の環境では普通にビルド通りましたけどどうなんでしょうか)
今回は、\packages\MPAndroidChart.3.0.0\lib\MPAndroidChart.dllを使いました。
Xamarin版のサンプルソースがJava版と比較して圧倒的に少ないのが難点ですね。

MQTTライブラリ

MQTTはC#で使えるライブラリを適当にチョイスしました。
ダウンロードはここからgithub
ダウンロード後、M2Mqtt.dllを使いました。
すぐ使えるアプリケーションもセットになっているので、使い易いかと思います。

作製したアプリの各画面

メイン画面

設定画面

開発で行き詰った事

NumberPickerはマイナスが表示できない問題

温度・湿度のスケールを設定するダイアログで、当初はNumberPickerで作っていたましたが、いざ作ってみるとマイナスが表示されないことに気づきました。仕方ないので、普通にListViewで選択する方向で作ってみましたが、なんかダサいですね。
後々調べてみると、NumberPickerをカスタムして使う投稿もあったみたいですね。
NumberPickerをカスタムして使う

ポップアップをクローズしたときに親画面に値を返せない問題

AlertDialogクラスにはクローズ時のイベントOnDismissが用意されていますが、さすがに親に値を返すイベントまではないようです。
仕方ないので、クローズしたときに呼び出されるコールバック関数を用意しました。

ListDialogFragment.cs
    public class DialogEventArgs : EventArgs
    {
        public string ReturnValue { get; set; }
    }

    /// <summary>
    /// リストダイアログ表示クラス
    /// </summary>
    public class ListDialogFragment : DialogFragment
    {
        public event EventHandler<DialogEventArgs> DialogClosed;
        public delegate void DialogEventHandloer(object sender, DialogEventArgs e);

        //処理内容省略...

        /// <summary>
        /// ダイアログクローズ時のイベント
        /// </summary>
        /// <param name="dialog"></param>
        public override void OnDismiss(IDialogInterface dialog)
        {
            base.OnDismiss(dialog);

            // 呼び出した親に値を返す
            if (DialogClosed != null)
            {
                DialogClosed(this, new DialogEventArgs { ReturnValue = _selectedValue });
            }
        }
    }

ダイアログを呼び出す親画面でDialogClosedにコールバック関数を設定してあげると、ダイアログをクローズしたときにコールバック関数が呼び出されます。尚、値はDialogEventArgs eのReturnValueプロパティにセットされています。

SettingActivity.cs
        protected override void OnCreate(Bundle savedInstanceState)
        {
            //処理内容省略...

            // 室温最小値ボタンのクリックイベント
            btnTempMin.Click += (_, __) =>
            {
                dialogKey = TempMinKey;
                // ダイアログ表示
                ListDialogFragment dlg = new ListDialogFragment(
                                                this,
                                                "室温(最小)を選択してください",
                                                Resource.Array.TempAxisItems,
                                                Resource.Array.TempAxisValues);
                dlg.DialogClosed += OnDialogClosed;     // 選択時のコールバック関数
                dlg.Show(this.FragmentManager, typeof(ListDialogFragment).Name);
            };
            //処理内容省略...
        }

        /// <summary>
        /// ダイヤログ選択時のコールバック関数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void OnDialogClosed(object sender, DialogEventArgs e)
        {
            //処理内容省略...
        }

MPAndroidChartのX軸のラベルをカスタムする方法

MPAndroidChartのX軸のラベルはデフォルトでは、0,1,2…のようにインデックスで表示されます。
カスタムラベルする方法は、Javaでは結構サンプルソースがあるのですが、Xamarin.Androidでは中々見つからないので苦労しました。
結局のところ、IAxisValueFormatterを継承したクラスを作らないといけないようです。
以下のソースでは、IAxisValueFormatterを継承したXLabelFormatterクラスを用意し、GetFormattedValueでラベルを作成します。
”if (int.TryParse(value.ToString("0.#"), out val) == true)”をしているのは、整数チェックをしています。というのは、引数のfloat valueには少数で値が入ってくることがあるようです。(なぜかは不明)
また、”if (labelList.Count > val && val >= 0)”は、グラフに表示するデータ件数が0件でもグラフの枠だけ表示し、ラベルは何も表示したくないので、このような処理を入れています。
XLabelFormatterでは、addLabelでX軸に表示したいラベルをlabelListにaddし、GetFormattedValueでX軸のインデックスに対応したラベルをlabelListから取得しています。

MainActivity.cs
    public class MainActivity : Activity
    {
        //処理内容省略...
        XLabelFormatter xformatter = new XLabelFormatter();
        private void setData(string xlabel, string temp, string humi)
        {
            // X軸の設定
            XAxis xAxis = mChart.XAxis;
            xAxis.SetLabelCount(tempDataset.EntryCount, true);
            xformatter.addLabel(xlabel);
            xAxis.ValueFormatter = xformatter;
            //処理内容省略...
        }

        //処理内容省略...
    }

    public class XLabelFormatter : Java.Lang.Object, IAxisValueFormatter
    {
        private static List<string> labelList = new List<string>();

        public int DecimalDigits => 0;

        public void addLabel(string item)
        {
            labelList.Add(item);
        }

        public string GetFormattedValue(float value, AxisBase axis)
        {
            int val = 0;
            string ret = "";
            if (int.TryParse(value.ToString("0.#"), out val) == true)
                if (labelList.Count > val && val >= 0)
                    ret = labelList[val];
            return ret;
        }
    }

おわりに

今回はArduinoもXamarinもAndroidも初めてづくしで調べなたら開発したので、かなり時間かかってしまった印象です。
Arduinoではセンサー系を試してみたので、次はモータ系を試してみたいですね。
構想としては、Arduino+ROS+ドローン+PS4コントローラとかやってみたいなぁ・・・
と考えているので、進捗が出ましたらまた投稿したいと思います。

関連記事

【IoT】ESP8266で温湿度取得してスマホでモニタリング 第1回

ソース

準備中...少々お待ちください。