はじめに
XBee使ってデータ飛ばして、USBでPCに受信して、WPFでリアルタイム表示したい。
やりたいこと
-
XBeeから送られてくる6桁の符号付きデータ(例:
+00010
)を受信して表示 -
表示形式
- 先頭の
+
/-
を残す - 小数点を下1桁の前に挿入
→+00010
→+0.1m
- 最後に
"m"
をつける
- 先頭の
-
COMポート経由、9600bps、パリティなし、8bit、USB-A接続
-
TeraTermで確認 → WPFで表示へ
TeraTermで受信してみた
↓受信したいポートを設定して…
…何も出んのやが?
とりあえずデバイスマネージャーとファイアウォールを疑ったけど異常なし。
ならば再起動である(大正義)
いけた(文明の力)
でもなにこれ、XBeeから取得しているからか、回数が符号前に表示されているし、末尾はASCIIの都合か毎回変化するし…
シーケンサーのプログラムがおかしいんかな?
とりあえず受信できたからヨシ!
WPFでリアルタイム表示アプリをつくる
必要なやつ
System.IO.Ports
NuGetから入れる
方法1:GUIから入れる場合
-
ソリューションエクスプローラーでプロジェクトを右クリック → 「NuGetパッケージの管理」
-
「参照」タブで
System.IO.Ports
を検索 -
Microsoft公式のパッケージをインストール
方法2:パッケージマネージャで入れる
Install-Package System.IO.Ports
使い方はリファレンスを見よう
うん、分からん
とりあえず、見よう見まねで組んでみる
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Ports;
using System.Windows.Threading;
namespace XBeeTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private SerialPort serialPort;
private string receivedData = "";
public MainWindow()
{
InitializeComponent();
StartSerial("COM4"); // ← 実際のポートに合わせて変更
}
// シリアルポートの初期化と接続
private void StartSerial(string portName)
{
try
{
serialPort = new SerialPort(portName, 9600);
serialPort.DataReceived += OnSerialDataReceived;
serialPort.Open();
}
catch (Exception ex)
{
MessageBox.Show("シリアルポートに接続できませんでした: " + ex.Message);
}
}
// データを受信したときに呼ばれる
private void OnSerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
// 受信データを読み取る
string data = serialPort.ReadExisting();
receivedData += data;
// データ形式: 例 "+01234"
if (receivedData.Length >= 6)
{
string latest = receivedData.Substring(receivedData.Length - 6);
// 先頭が "+" または "-" で、残りが数字なら表示
if ((latest[0] == '+' || latest[0] == '-') && int.TryParse(latest.Substring(1), out int value))
{
string displayText = FormatValue(latest[0], value);
// UIスレッドで更新
Dispatcher.BeginInvoke(() =>
{
RealTimeDisplay.Text = displayText;
RealTimeDisplay.Foreground = (latest[0] == '+') ? Brushes.LimeGreen : Brushes.Red;
});
}
// 長くなりすぎたら切り捨て
if (receivedData.Length > 100)
{
receivedData = receivedData.Substring(receivedData.Length - 50);
}
}
}
// 表示用に値を加工する(例: 01234 → 123.4)
private string FormatValue(char sign, int value)
{
string valueStr = value.ToString("D5"); // 桁が足りないときは0埋め
string integerPart = valueStr.Substring(0, 4);
string decimalPart = valueStr.Substring(4, 1);
return $"{sign}{integerPart}.{decimalPart}m";
}
// アプリ終了時の処理
protected override void OnClosed(EventArgs e)
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
serialPort.Dispose();
}
base.OnClosed(e);
}
}
}
<Window x:Class="XBeeTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="リアルタイム数値表示" Height="300" Width="500"
WindowStartupLocation="CenterScreen" Background="Black">
<Grid>
<TextBlock x:Name="RealTimeDisplay"
Text="000.0m"
FontSize="72"
FontWeight="Bold"
FontFamily="Consolas"
Foreground="LimeGreen"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderOptions.EdgeMode="Aliased"
TextOptions.TextFormattingMode="Display"/>
</Grid>
</Window>
んで、実行!
…できた
が、コードにCOMポート選択を埋め込むなんてナンセンス
後で使いやすいように修正するとして、復習用にコード解説
コード解説
System.IO.Ports
の使い方
ポートを開いて通信開始
serialPort = new SerialPort(portName, 9600);
serialPort.DataReceived += OnSerialDataReceived;
serialPort.Open();
-
new SerialPort("COM4", 9600)
→"COM4"
という名前のポートに、ボーレート9600で接続SerialPort(string portName, int baudRate)
-
DataReceived
イベントにメソッドを登録
→ データが届いたらOnSerialDataReceived
が呼ばれる -
.Open()
→ 実際にポートを開く(通信が始まる)
データの受信
string data = serialPort.ReadExisting();
- 届いたデータを文字列として全部読み取る
- 通常、バイト列が来るが、
ReadExisting()
を使えば読みやすい形式(文字列)で取得できる
ポートを閉じる(終了処理)
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
serialPort.Dispose();
}
- ポートは使い終わったら必ず閉じるのがマナー
-
Dispose()
でリソースを完全に解放
リファレンスを参考に、対応表を作るとこんな感じ
コード中の記述 | リファレンス上のメンバ | 解説 |
---|---|---|
new SerialPort(portName, 9600) |
SerialPort(String, Int32) コンストラクタ |
COMポート名とボーレート(通信速度)を指定してインスタンスを生成。 |
serialPort.DataReceived += OnSerialDataReceived; |
SerialPort.DataReceived イベント |
データを受信したときに自動的に呼ばれるイベント。バックグラウンドスレッドで発火。 |
serialPort.Open(); |
SerialPort.Open メソッド |
COMポートを開き、通信を開始。開かないと読み書きできない。 |
serialPort.ReadExisting() |
SerialPort.ReadExisting メソッド |
受信バッファにある全データを「文字列」として一括で読み出す。 |
serialPort.Close(); |
SerialPort.Close メソッド |
通信を終了してポートを閉じる。他のアプリが同じCOMポートを使えるようになる。 |
serialPort.Dispose(); |
SerialPort.Dispose メソッド |
リソースを解放。Close() とセットで呼ぶのが安全。 |
動けば後はこっちのもん
じゃかじゃか弄って