こんにちは、ENGかぴです。
VB.NET(Visual Basic 2022)のChartを使用するとArduino等の外部機器から通信で取得したデータをグラフで表示することができます。UDP通信を使って取得した温湿度データをトレンドグラフで表示する例をまとめました。
Chartの実装の方法とUDP通信の方法は下記記事にまとめています。ソースコードを流用してトレンドグラフを表示するようにします。
VB.NET(VB2022)のChartでグラフを表示する方法
VB.NET(VB2022)のUDP通信を非同期処理で行う方法
Arduinoは下記記事のESP-WROOM-32E(以下ESP32とする)を使用します。
ESP32-WROOM-32Eで温湿度データをUDP通信する
Windowsフォームアプリケーション(.NETFramework)を対象としています。以下はVisual Basic 2022をVB2022とします。
VB.NET(VB2022)のデスクトップアプリで動作確認したことを下記リンクにまとめています。
VB.NET(VB2022)のデスクトップアプリ開発でできること
Chartでトレンドグラフを表示する

①のESP32はUDPサーバーとして動作し、DHT20モジュールから定期的(10秒毎)に温湿度データを取得しながらクライアントの要求を待機します。
②のクライアントは本記事で作成するアプリになります。アプリからタイマのインターバル(10秒)毎に「CHARTAPP」の文字列をUDP通信でESP32に送信します。
ESP32はアプリからの要求に対して温湿度データを含めた電文で応答します。ESP32からの応答パケットから温湿度のデータを取得してChartのデータに組み込んでグラフ表示します。
クライアントで使用しているパソコンはWiFiを搭載していないためLANケーブルをアクセスポイントに接続して通信します。
コントロールの追加

本記事のコントロールは流用元のVB.NET(VB2022)のUDP通信を非同期処理で行う方法で使用しているUDP通信に関わるコントロールにChartのコントロールを追加して実装します。
黒色のコントロールは流用元のUDP通信に関するコントロールです。赤色が追加するコントロールです。Label1~4についてはデフォルトの表示を使用しているため黒色ですが追加する項目です。
Chartを初期配置すると棒グラフの表示になるためChartのSeriesからSeries1メンバーのChartTypeをSpLineに変更(ソースコードで変更できますが見た目の問題でプロパティを直接変更しています)すると丸みを帯びた線グラフの表示になります。
温湿度の変化の傾向を表示するためスケーリング値(範囲:0~10000)を使用してグラフを表示します。カーソル位置のタイムスタンプと温湿度が分かるようにスケーリング値から換算したデータをカーソル欄に表示します。
トレンドグラフに複数の項目がある場合、軸の範囲がデータの大きい方を基準になるため値の範囲が狭い項目の傾向が掴みづらくなります。そのため項目に寄らず一律の基準として0~10000の範囲にスケーリングした値でトレンドグラフを表示します。
2軸にする方法がありますが、それぞれの軸のグリッド線を引くと線が煩雑になり見にくくなるためトレンドグラフのようにデータの傾向を確認する目的であればスケーリング値を使用する方法が良い場合もあります。
PR:RUNTEQ(ランテック )- マイベスト3年連続1位を獲得した実績を持つWebエンジニア養成プログラミングスクール
応答電文からデータを取得する

応答電文はヘッダ8バイトに続けて湿度のデータ、温度のデータを続けて合計16バイトのデータになります。Timer1のTickタスクで電文を判定を行います。
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If sz >= 8 Then
rp = RxRing.rp
For i = 0 To 8 - 1
str &= Chr(RxRing.dat(rp))
rp += 1
Next
If str = "CHARTAPP" Then
If sz >= 16 Then
For i = 0 To 16 - 1
logbuf(i) = RxRing.dat(RxRing.rp)
RingRpAdd()
Next
ChartDataSet()
End If
End If
End If
End Sub
3行目はヘッダーのサイズである8バイト以上であるかの確認を行っています。8バイト以上であれば受信したデータから文字列を生成します。(5行~8行目)Chr()メソッドでアスキー文字に変換して文字列を&で連結しています。
生成した文字列が「CHARTAPP」と一致する場合、すべてのデータを受け入れて取得します。(12行~15行目)16行目のCharDataSet()内でChartにデータをセットします。
Chartにデータをセットする
Private Sub ChartDataSet()
Dim tempbuf(GLF_ROW) As Integer
temp = (CInt(logbuf(12)) << 8) + logbuf(13)
temp_log = (CInt(logbuf(14)) << 8) + logbuf(15)
log.temp.CopyTo(tempbuf, 1)
For i = 1 To GLF_ROW - 1
log.temp(i) = tempbuf(i)
Next
log.temp(0) = temp_log
'以下省略
End Sub
4行目、5行目は温度データ(temp:100倍値)と温度データ(temp_log:スケーリング値)のデータを2バイトのデータに換算しています。スケーリング値をグラフにセットして表示します。
Chartのグラフは軸の左端が最古のデータ、右端が最新のデータになるようにプロットします。グラフ用のデータは最新のデータから降順に古いデータとなり配列の末端が最古のデータになるようにセットします。
6行目のCopyTo()メソッドは配列をコピーします。第1引数にコピー先の配列を指定します。第2引数に先頭からオフセット(ずらして格納)するバイト数を指定します。例ではlog.temp[]配列を1バイトずらしてtempbuf[]配列に格納しています。
log.temp[]のサイズを360、tempbuf[]のサイズを361にすることで1バイトずらして格納できるようにしています。
tempbuf[]配列はlog.temp[]配列を1バイトずらした配列になっているため8行~10行目でlog.temp[]配列に格納し直してlog.temp[]配列のデータを1つずらした配列を生成しています。最後に最新のデータを配列0番目にセットすると降順に古いデータとなり配列の末端が最古のデータになります。
Chartにセットするデータを生成します。DataTabelクラスの変数を初期化(New)して準備し、行(Row)と列(Columns)のデータに分けて格納します。
Dim dt As New DataTable
Dim row As DataRow
dt.Columns.Add("X")
dt.Columns.Add("Temperature")
For i = 0 To GLF_ROW - 1
row = dt.NewRow() '行項目を新規追加
row("X") = log.timstp(GLF_ROW - 1 - i)
row("Temperature") = log.temp(GLF_ROW - 1 - i)
dt.Rows.Add(row)
Next
Chart1.Series.Clear() 'グラフの項目をクリア
Chart1.DataSource = dt 'DataTableと関連付け
Chart1.DataBind() 'コントロールををデータソースにバインド
dtオブジェクトのColumnsのAdd()メソッドで列の項目を指定します。例では文字列の”X”を指定しています。続けてAdd()メソッドで文字列の”Temperature”を指定して列の項目を追加します。
列の項目に対して行のデータを追加します。DataRowクラスのrowオブジェクトのNewRow()メソッドで行項目を生成し、列で追加した項目に対応する行のデータをセットします。
row(“X”)はデータを取得したときのタイプスタンプをセットしています。軸のラベル表示には使用しませんがカーソル欄で時刻を確認する時の参照データになります。row(“Temperature”)はlog.temp[]配列を指定します。配列の参照は最古のデータが先頭(8行~9行目のGLF_ROW -1 – i の部分です)になるようにしています。
rowオブジェクトに行のデータをセットした後は、dtオブジェクトのRowsコレクションにAdd()メソッドで行のデータを追加します。7行~12行目を繰り返してグラフのデータをセットします。
ChartのSeriesのコレクションをClear()メソッドで初期化します。Chart1のDataSourceにdtオブジェクトを指定するとグラフのデータとして使用できるようになります。
広告
マイベスト3年連続1位を獲得した実績を持つ実践型のプログラミングスクール
グラフの軸を設定する
'グリッドの設定(タイトル、スクロール)
Chart1.ChartAreas(0).AxisX.ScrollBar.IsPositionedInside = False
Chart1.ChartAreas(0).AxisY.ScrollBar.IsPositionedInside = False
Chart1.ChartAreas(0).AxisX.LabelStyle.Enabled = False
Chart1.ChartAreas(0).AxisY.LabelStyle.Enabled = False
Chart1.ChartAreas(0).AxisY2.LabelStyle.Enabled = False
'グリッドの設定(X軸)
Chart1.ChartAreas(0).AxisX.MajorTickMark.Enabled = False
Chart1.ChartAreas(0).AxisX.MajorGrid.LineDashStyle = ChartDashStyle.Dash
Chart1.ChartAreas(0).AxisX.MinorGrid.Enabled = True
Chart1.ChartAreas(0).AxisX.MinorTickMark.Enabled = True
Chart1.ChartAreas(0).AxisX.MinorGrid.LineDashStyle = ChartDashStyle.Dash
'グリッドの設定(Y軸)
Chart1.ChartAreas(0).AxisY.MajorGrid.Enabled = True
Chart1.ChartAreas(0).AxisY.MajorTickMark.Enabled = False
Chart1.ChartAreas(0).AxisY.Maximum = 10000
Chart1.ChartAreas(0).AxisY.Minimum = 0
Chart1のグラフの軸を管理するChartAreasオブジェクトの設定を行います。(0)の番号は軸のメンバーの番号です。今回は軸は1つなので0を指定しています。
AxisXはX軸のオブジェクト、AxisYはY軸のオブジェクトです。各軸の設定の追加はAxisX、AxisYオブジェクトのメンバーを指定して行います。(以下ではAxisX、AxisYオブジェクトの表記は省略)
ScrollBarはデフォルトで使用になっていますが、スクロールバーの表示のデフォルトはグラフエリアの内側になっています。外側に配置する場合はScrollBarのIsPositionedInsideをTrueに変更します。
LabelStyle.Enabledプロパティは軸のラベルの表示を選択します。X軸、Y軸ともに軸のラベルを使用しないのでFalseにしています。AxisY2をFalseにすると第2軸(右側の軸)のラベル表示がなくなるためグラフの表示範囲が広がります。
MajorGridを使用する場合はEnableをTrueにします。MajorTickMarkは軸の外側にグリッドの線を表示する設定です。MajorGridのLineDashStyleはグリッド線の種別を指定する設定です。例ではDash(破線)を指定しています。
MinorGridについてもMajorGridと同様の設定ですが、MinorGridはMajorGridを分割するグリッド線です。デフォルトではFalseになっているため必要に応じて設定します。
Y軸についても同様にMajorGrid及びMinorGridの設定ができますが、Y軸のグリッド線をすべて使用すると線が多くなり見にくいためデフォルト設定のままにしています。
Y軸はスケーリングした値を使用してトレンドグラフを表示するためMaximumに10000、Minimumに0を指定して範囲を固定しています。
カーソル位置の設定と表示
Private Sub Chart1_PostPaint(sender As Object, e As ChartPaintEventArgs) Handles Chart1.PostPaint
'省略
Chart1.ChartAreas(0).CursorX.SetCursorPosition(cnt)
ind = Chart1.Series(cursor_no).Points(cnt).YValues(0)
Chart1.ChartAreas(0).CursorY.SetCursorPosition(ind)
Label1.Text = Chart1.Series(0).Points(cnt).AxisLabel
Label2.Text = HumidCng(Chart1.Series(0).Points(cnt).YValues(0)) & "%"
Label3.Text = Chart1.Series(1).Points(cnt).AxisLabel
Label4.Text = TempCng(Chart1.Series(1).Points(cnt).YValues(0)) & "℃"
End Sub
カーソルの位置や表示についてはVB.NET(VB2022)のChartでグラフを表示する方法で説明しています。ここではカーソル欄に表示するタイムスタンプ、温度、湿度の表示について説明します。
X軸のカーソル位置の指定はCursorXのSetCursorPosition()メソッドを使用します。引数にデータの番号を指定するとX軸のカーソルが表示できます。
Y軸のカーソル位置の指定はCursorYのSetCursorPosition()メソッドを使用します。X軸のカーソルで算出したcntを使用してSeries().Points(cnt).YValues(0)プロパティでY軸のデータを取得して引数に指定します。
カーソル欄に表示するタイムスタンプは7行目のAxisLabelプロパティで取得してLabel1のテキストプロパティに指定します。9行目もタイムスタンプを取得して表示しています。
湿度のデータは8行目のYValues(0)プロパティで取得したデータを換算して表示します。温度のデータは10行目のYValues(0)プロパティで取得したデータを換算して表示します。
'湿度データを換算
Private Function HumidChg(str As String) As String
Dim rtn As String
rtn = (str / 100).ToString("0.00")
Return rtn
End Function
'温度データを換算
Private Function TempChg(str As String) As String
Dim rtn As String
rtn = ((1.2 * str - 4000) / 100).ToString("0.00")
Return rtn
End Function
トレンドグラフのデータはスケーリング値を使用しているため実際の温度・湿度のデータに換算します。HumidChg()関数は湿度のデータを返す自作の関数です。湿度の範囲は0~100%なので100で割った値になります。小数点2桁で表示するためToString(“0.00”)を文字列のフォーマットを指定しています。
TempChg()関数は温度のデータを返す自作の関数です。-40℃~80℃の範囲は120になりますが、100倍値にしているため12000になります。これを10000にスケーリングしているため1.2倍した後-40℃(-4000)を引いて温度データ(100倍値)を生成しています。これを100で割ったとき小数点2桁で表示するためToString(“0.00”)で文字列のフォーマットを指定しています。
動作確認(デバッグ)
デバッグは実際のアプリケーションの動作を模擬して実行するものです。デバッグの開始はエディタ上部の「▶開始」をクリックすると開始します。

ESP32とアクセスポイントに電源を入れてUDPサーバを待機させます。アプリの接続先にIPアドレス「192.168.11.10」、ポートを「10002」を入力して、「接続」をクリックするとUDP通信の初期化を行い送受信できるようになります。
ロガー操作欄の「開始」をクリックするとアプリから10秒毎に「CHARTAPP」を送信します。ESP32は温湿度データを含む電文で応答します。10秒毎に温湿度データのトレンドグラフが更新されていく様子が分かります。「停止」をクリックすると定期的な送信を停止します。
「データ取得(瞬時)」をクリックするとボタンを押したタイミングの温湿度データの電文を取得できます。
アプリは受信した電文から温湿度データを取得してChartにデータを表示します。アプリは360件のログを表示するようにしているため3600秒(1時間)のトレンドグラフになります。部屋の加湿器をONして動作確認しましたが、湿度が徐々に上がっている様子がトレンドグラフから分かりました。
マウスのカーソルの移動に合わせてカーソル位置が更新されることが確認でき、カーソル位置の波形のデータ値がグループボックスのカーソルに表示されることが確認できました。
グラフは左クリックで範囲を指定して部分拡大することができます。拡大するとスクロールバーが表示され指定した範囲を拡大して表示できます。
カーソル対象を切り替えるとグループボックスの色を変更して対象の項目が分かるようにしています。ソースコードのMouseClickイベントの処理を参照してください。
コードのデバッグを行う場合は、コードエディタでブレークポイントを置くことで一時的にプログラムを停止させることができます。

ブレークポイントはコードエディターの最左部分をクリックして●マークが表示されれば設置できています。例ではButton1がクリックされた時に発生したイベントの先頭部分にブレークポイントを置いています。この状態でButton1をクリックするとブレークポイントでプログラムが一時停止します。
F11を押すとステップ実行になるため1行ずつ内容を確認しながらデバッグを行うことができます。上部の「▶続行(C)」をクリックするとブレークポイント状態から通常の動作に復帰します。
PR:(即戦力のスキルを身に着ける:DMM WEBCAMP 学習コース(はじめてのプログラミングコース))
ソースコード全体
ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。
リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。
コントロールの番号やイベントハンドラーなどのプロパティ名などを変更している場合はソースコードのイベントハンドラーをお使いのイベントハンドラーに置き換えてください。
Timer2のインターバルを指定するようにListBoxを追加したりログの表示件数を拡張したりすることでトレンドグラフとして保存する範囲を拡張することができます。
関連リンク
VB.NET(VB2022)のデスクトップアプリで動作確認したことを下記リンクにまとめています。
VB.NET(VB2022)のデスクトップアプリ開発でできること
マイクロソフトはVisual Studio以外にもVSCodeという便利なエディターを提供しています。下記リンクではVSCodeのインストールの仕方からC言語開発環境の作り方までをまとめています。
PR:無料トライアル実施中【PC専用】AIスライド資料作成ツールの利用:イルシル
最後まで、読んでいただきありがとうございました。