こんにちは、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 学習コース(はじめてのプログラミングコース))
ソースコード全体
ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、コメントに誤記が含まれていることもありますが、参考にお使いいただければと思います。
Imports System.Net
Imports System.Net.NetworkInformation
Imports System.Net.Sockets
Imports System.Text
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Structure NET_INTERFACE
Dim name As String
Dim localip As IPAddress
End Structure
Const RXRGSZ As Integer = 256
Const GLF_WAVE_X As String = "X"
Const GLF_WAVE_1 As String = "Humidity"
Const GLF_WAVE_2 As String = "Temperature"
Const GLF_ROW As Integer = 360
Const TIME_UP As Integer = 0
Const TIME_OFF As Integer = -1
Const TIM_RCV_MAX As Integer = 2
Structure TYP_COMRING
Dim rp As Integer
Dim wp As Integer
Dim dat() As Byte
End Structure
Structure HIST_DATA
Dim timstp() As Date
Dim humi() As Integer
Dim temp() As Integer
Sub Init()
ReDim timstp(GLF_ROW - 1)
ReDim humi(GLF_ROW - 1)
ReDim temp(GLF_ROW - 1)
End Sub
End Structure
Dim UdpCloseFlg As Boolean = False
Dim ClientUdp As UdpClient
Dim LocalNet() As NET_INTERFACE
Dim localIp As IPAddress
Dim localPort As Integer
Dim remotePort As Integer
Dim logbuf(16) As Byte
Dim log As HIST_DATA
Dim lifcnt As Integer
Dim RxRing As TYP_COMRING '受信したデータを管理
Dim Mpointer As Point 'マウス位置
Dim cursor_no As Integer
Dim CURSOR_COLOR As Color = Color.Gold
Dim SERIES_COLOR_1 As Color = Color.Red
Dim SERIES_COLOR_2 As Color = Color.Blue
Dim GR_COLOR As Color = Color.White
Dim GR_COLOR_2 As Color = Color.Black
Dim timRcv As Integer = TIME_OFF
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim remoteIp As String = ""
If Button1.BackColor = Color.LightGreen Then
remoteIp = TextBox1.Text & "." &
TextBox2.Text & "." &
TextBox3.Text & "." &
TextBox4.Text
localPort = TextBox8.Text
remotePort = TextBox5.Text
Try
ClientUdp = New UdpClient(localPort)
ClientUdp.Connect(remoteIp, remotePort) 'UDP接続
ClientUdp.BeginReceive(AddressOf ReceiveCallback, ClientUdp) '受信待ちの発行
Timer1.Enabled = True
Button1.BackColor = Color.LightBlue
Button2.BackColor = Color.LightGreen
Catch ex As Exception
End Try
End If
End Sub
'UDPの受信と受信API発行
Private Sub ReceiveCallback(AR As IAsyncResult)
Dim i As Integer
Try
' ソケット受信
Dim loc_ip As New IPEndPoint(localIp, localPort)
Dim getByte() As Byte = ClientUdp.EndReceive(AR, loc_ip)
For i = 0 To getByte.Length - 1
RxRingSet(getByte(i))
Next
' ソケット非同期受信(System.AsyncCallback)
ClientUdp.BeginReceive(AddressOf ReceiveCallback, ClientUdp)
Catch ex As Exception
End Try
End Sub
'LANポート検索
Private Sub GetNetworkList()
Dim netinterface = NetworkInterface.GetAllNetworkInterfaces
Dim wk(netinterface.Length - 1) As NET_INTERFACE
Dim i As Integer
Dim j As Integer = 0
Dim cnt As Integer = 0
For Each netin In netinterface
If netin.OperationalStatus = OperationalStatus.Up AndAlso
netin.NetworkInterfaceType = NetworkInterfaceType.Ethernet Then
wk(i).name = netin.Name
cnt += 1
Dim ipprperties As IPInterfaceProperties = netin.GetIPProperties
If ipprperties IsNot Nothing Then
For Each ipadrs In ipprperties.UnicastAddresses
wk(i).localip = ipadrs.Address
Next
End If
End If
i += 1
Next
ReDim LocalNet(cnt - 1)
For i = 0 To wk.Length - 1
If wk(i).name <> "" Then
LocalNet(j) = wk(i)
j += 1
End If
Next
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim i As Integer
LabelInit()
ComboBoxInit()
RxRingInit()
log.Init()
For i = 0 To GLF_ROW - 1
log.timstp(i) = Nothing
log.humi(i) = 0
log.temp(i) = 0
Next
End Sub
Private Sub ComboBox1_MouseClick(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseClick
ComboBoxInit()
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
localIp = LocalNet(ComboBox1.SelectedIndex).localip
End Sub
'ComboBoxの初期化
Private Sub ComboBoxInit()
Dim i As Integer
Dim str As String
GetNetworkList()
ComboBox1.Items.Clear()
For i = 0 To LocalNet.Length - 1
str = LocalNet(i).name & " " & "IP:" & LocalNet(i).localip.ToString
ComboBox1.Items.Add(str)
Next
End Sub
'Labelの初期化
Private Sub LabelInit()
Label1.Text = "----"
Label2.Text = "----"
Label3.Text = "----"
Label4.Text = "----"
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
SendData()
End Sub
'UDPのパケットを送信
Private Sub SendData()
Dim data() As Byte = Encoding.GetEncoding("utf-8").GetBytes("CHARTAPP")
If ClientUdp IsNot Nothing Then
Try
ClientUdp.Send(data, data.Length)
Catch ex As Exception
End Try
End If
End Sub
'電文のチェック
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim sz As Integer
Dim i As Integer
Dim str As String = ""
Dim rp As Integer
If timRcv > TIME_UP Then
timRcv -= 1
ElseIf timRcv = TIME_UP Then
timRcv = TIME_OFF
RingRpAdd()
End If
sz = RxRing.wp - RxRing.rp
If sz < 0 Then
sz += RxRing.dat.Length
End If
If sz = 0 Then
timRcv = TIME_OFF
Else
If timRcv = TIME_OFF Then
timRcv = TIM_RCV_MAX
End If
If sz >= 8 Then
rp = RxRing.rp
For i = 0 To 8 - 1
str &= Chr(RxRing.dat(rp))
rp += 1
If rp >= RxRing.dat.Length Then
rp = 0
End If
Next
If str = "CHARTAPP" Then
If sz >= 16 Then
timRcv = TIME_OFF
For i = 0 To 16 - 1
logbuf(i) = RxRing.dat(RxRing.rp)
RingRpAdd()
Next
ChartDataSet()
End If
Else
RingRpAdd()
End If
End If
End If
End Sub
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
SendData()
End Sub
'Chartのデータをセットする
Private Sub ChartDataSet()
Dim dt As New DataTable
Dim row As DataRow
Dim humi As Integer
Dim humi_log As Integer
Dim temp As Integer
Dim temp_log As Integer
Dim humibuf(GLF_ROW) As Integer
Dim tempbuf(GLF_ROW) As Integer
Dim datebuf(GLF_ROW) As Date
'ログのデータを生成
humi = (CInt(logbuf(8)) << 8) + logbuf(9)
humi_log = (CInt(logbuf(10)) << 8) + logbuf(11)
temp = (CInt(logbuf(12)) << 8) + logbuf(13)
temp_log = (CInt(logbuf(14)) << 8) + logbuf(15)
'データをずらして格納
log.humi.CopyTo(humibuf, 1)
log.temp.CopyTo(tempbuf, 1)
log.timstp.CopyTo(datebuf, 1)
For i = 1 To GLF_ROW - 1
log.timstp(i) = datebuf(i)
log.humi(i) = humibuf(i)
log.temp(i) = tempbuf(i)
Next
'最新のデータを格納
log.timstp(0) = Now
log.humi(0) = humi_log
log.temp(0) = temp_log
'グラフデータセット
dt.Columns.Add(GLF_WAVE_X)
dt.Columns.Add(GLF_WAVE_1)
dt.Columns.Add(GLF_WAVE_2)
For i = 0 To GLF_ROW - 1
row = dt.NewRow() '行項目を新規追加
row(GLF_WAVE_X) = log.timstp(GLF_ROW - 1 - i)
row(GLF_WAVE_1) = log.humi(GLF_ROW - 1 - i)
row(GLF_WAVE_2) = log.temp(GLF_ROW - 1 - i)
dt.Rows.Add(row)
Next
Chart1.Series.Clear() 'グラフの項目をクリア
Chart1.DataSource = dt 'DataTableと関連付け
Chart1.DataBind() 'コントロールををデータソースにバインド
Chart1SetSeries(GLF_WAVE_1, SERIES_COLOR_2)
Chart1SetSeries(GLF_WAVE_2, SERIES_COLOR_1)
Chart1SetAreas()
End Sub
'RxRingの初期化を行う
Private Sub RxRingInit()
RxRing.wp = 0
RxRing.rp = 0
ReDim RxRing.dat(RXRGSZ - 1)
End Sub
'RxRingのwpを更新する
Public Sub RxRingSet(dat As Byte)
RxRing.dat(RxRing.wp) = dat
RxRing.wp += 1
If RxRing.wp = RxRing.dat.Length Then
RxRing.wp = 0
End If
End Sub
'RxRingのrpを更新する
Private Sub RingRpAdd()
RxRing.rp += 1
If RxRing.rp >= RxRing.dat.Length Then
RxRing.rp = 0
End If
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If Button2.BackColor = Color.LightGreen Then
'//クライアントのインスタンスが有ったら
If ClientUdp IsNot Nothing Then
ClientUdp.Close()
Button2.BackColor = Color.LightBlue
Button1.BackColor = Color.LightGreen
End If
End If
End Sub
'Chartに項目を追加
Private Sub Chart1SetSeries(str As String, color As Color)
Chart1.Series.Add(str)
Chart1.Series(str).ChartType = SeriesChartType.Spline
Chart1.Series(str).Color = color
Chart1.Series(str).XValueMember = GLF_WAVE_X
Chart1.Series(str).YValueMembers = str
End Sub
'Chartの軸をセット
Private Sub Chart1SetAreas()
'グリッドの設定(タイトル、スクロール)
'Chart1.ChartAreas(0).AxisX.IsMarginVisible = True
'Chart1.ChartAreas(0).AxisY.IsMarginVisible = True
Chart1.ChartAreas(0).AxisX.ScrollBar.IsPositionedInside = False
Chart1.ChartAreas(0).AxisY.ScrollBar.IsPositionedInside = False
'Chart1.ChartAreas(0).AxisX.Title = "タイムスタンプ"
'Chart1.ChartAreas(0).AxisY.Title = "スケール"
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.MajorGrid.Enabled = True
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.MajorGrid.LineDashStyle = ChartDashStyle.Dash
'Chart1.ChartAreas(0).AxisY.MinorGrid.Enabled = True
'Chart1.ChartAreas(0).AxisY.MinorTickMark.Enabled = True
'Chart1.ChartAreas(0).AxisY.MinorGrid.LineDashStyle = ChartDashStyle.Dash
Chart1.ChartAreas(0).AxisY.Maximum = 10000
Chart1.ChartAreas(0).AxisY.Minimum = 0
'カーソルの設定
Chart1.ChartAreas(0).CursorX.LineColor = CURSOR_COLOR
Chart1.ChartAreas(0).CursorY.LineColor = CURSOR_COLOR
Chart1.ChartAreas(0).CursorX.IsUserEnabled = True
Chart1.ChartAreas(0).CursorY.IsUserEnabled = True
Chart1.ChartAreas(0).CursorX.IsUserSelectionEnabled = True
Chart1.ChartAreas(0).CursorY.IsUserSelectionEnabled = True
Select Case cursor_no
Case 0
GroupColor(CURSOR_COLOR, GR_COLOR)
Case 1
GroupColor(GR_COLOR, CURSOR_COLOR)
End Select
Label1.Text = "----"
Label2.Text = "----"
Label3.Text = "----"
Label4.Text = "----"
End Sub
Private Sub Chart1_PostPaint(sender As Object, e As ChartPaintEventArgs) Handles Chart1.PostPaint
Dim axis_x = Chart1.ChartAreas(0).AxisX
Dim axis_y = Chart1.ChartAreas(0).AxisY
Dim x1 = axis_x.ValueToPixelPosition(axis_x.Minimum) 'グラフ領域左端のX座標
Dim x2 = axis_x.ValueToPixelPosition(axis_x.Maximum) 'グラフ領域右端のX座標
Dim y1 = axis_y.ValueToPixelPosition(axis_y.Maximum) 'グラフ領域上部のY座標
Dim y2 = axis_y.ValueToPixelPosition(axis_y.Minimum) 'グラフ領域下部のY座標
Dim xv1 = axis_x.ValueToPixelPosition(axis_x.ScaleView.ViewMinimum) 'グラフ領域左端のX座標
Dim xv2 = axis_x.ValueToPixelPosition(axis_x.ScaleView.ViewMaximum) 'グラフ領域右端のX座標
Dim yv1 = axis_y.ValueToPixelPosition(axis_y.ScaleView.ViewMaximum) 'グラフ領域上部のY座標
Dim yv2 = axis_y.ValueToPixelPosition(axis_y.ScaleView.ViewMinimum) 'グラフ領域下部のY座標
Dim w As Double
Dim cnt As Integer
Dim ind As Integer
If Double.IsNaN(x1) = False And Double.IsNaN(x2) = False And
Double.IsNaN(y1) = False And Double.IsNaN(y2) = False Then
'グラフの軸が描写されているか
If xv1 <= Mpointer.X And Mpointer.X <= xv2 And
yv1 <= Mpointer.Y And Mpointer.Y <= yv2 Then
'マウスカーソルがグラフの軸以内であるか
w = (x2 - x1) / GLF_ROW
cnt = (CDbl(Mpointer.X) - x1) / w
If cnt < 0 Then
cnt = 0
ElseIf cnt >= GLF_ROW Then
cnt = GLF_ROW - 1
End If
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 = HumidChg(Chart1.Series(0).Points(cnt).YValues(0)) & "%"
Label3.Text = Chart1.Series(1).Points(cnt).AxisLabel
Label4.Text = TempChg(Chart1.Series(1).Points(cnt).YValues(0)) & "℃"
End If
End If
End Sub
'湿度のデータを換算
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
Private Sub Chart1_MouseMove(sender As Object, e As MouseEventArgs) Handles Chart1.MouseMove
Mpointer = e.Location 'マウス位置獲得
Chart1.Invalidate() 'グラフ更新
End Sub
Private Sub Chart1_MouseClick(sender As Object, e As MouseEventArgs) Handles Chart1.MouseClick
Dim no = e.Button
If no = MouseButtons.Right Then
If Chart1.Series IsNot Nothing Then
If cursor_no >= Chart1.Series.Count - 1 Then
cursor_no = 0
Else
cursor_no += 1
End If
End If
Select Case cursor_no
Case 0
GroupColor(CURSOR_COLOR, GR_COLOR)
Case 1
GroupColor(GR_COLOR, CURSOR_COLOR)
End Select
End If
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
If Button4.BackColor = Color.LightGreen Then
Button4.BackColor = Color.LightBlue
Button5.BackColor = Color.LightGreen
Timer2.Interval = 10000
Timer2.Enabled = True
End If
End Sub
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
If Button5.BackColor = Color.LightGreen Then
Button5.BackColor = Color.LightBlue
Button4.BackColor = Color.LightGreen
Timer2.Enabled = False
End If
End Sub
'カーソル欄の色変更
Private Sub GroupColor(c1 As Color, c2 As Color)
GroupBox7.BackColor = c1
GroupBox7.ForeColor = Chart1.Series(0).Color
GroupBox8.BackColor = c2
If Chart1.Series.Count >= 2 Then
GroupBox8.ForeColor = Chart1.Series(1).Color
Else
GroupBox8.ForeColor = GR_COLOR_2
End If
End Sub
End Class
コントロールの番号やイベントハンドラーなどのプロパティ名などを変更している場合はソースコードのイベントハンドラーをお使いのイベントハンドラーに置き換えてください。
Timer2のインターバルを指定するようにListBoxを追加したりログの表示件数を拡張したりすることでトレンドグラフとして保存する範囲を拡張することができます。
関連リンク
VB.NET(VB2022)のデスクトップアプリで動作確認したことを下記リンクにまとめています。
VB.NET(VB2022)のデスクトップアプリ開発でできること
マイクロソフトはVisual Studio以外にもVSCodeという便利なエディターを提供しています。下記リンクではVSCodeのインストールの仕方からC言語開発環境の作り方までをまとめています。
PR:無料トライアル実施中【PC専用】AIスライド資料作成ツールの利用:イルシル
最後まで、読んでいただきありがとうございました。