2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

XBeeから受信したデータをWPFでリアルタイム表示してみた

Posted at

はじめに

XBee使ってデータ飛ばして、USBでPCに受信して、WPFでリアルタイム表示したい。

やりたいこと

  • XBeeから送られてくる6桁の符号付きデータ(例:+00010)を受信して表示

  • 表示形式

    • 先頭の + / - を残す
    • 小数点を下1桁の前に挿入
      +00010+0.1m
    • 最後に "m" をつける
  • COMポート経由、9600bps、パリティなし、8bit、USB-A接続

  • TeraTermで確認 → WPFで表示へ

TeraTermで受信してみた

↓受信したいポートを設定して…

Pasted image 20250722154056.png
Pasted image 20250722154107.png

…何も出んのやが?

とりあえずデバイスマネージャーとファイアウォールを疑ったけど異常なし。

ならば再起動である(大正義)

Pasted image 20250722154326.png

いけた(文明の力)

でもなにこれ、XBeeから取得しているからか、回数が符号前に表示されているし、末尾はASCIIの都合か毎回変化するし…

シーケンサーのプログラムがおかしいんかな?

とりあえず受信できたからヨシ!

WPFでリアルタイム表示アプリをつくる

必要なやつ

System.IO.Ports

NuGetから入れる

方法1:GUIから入れる場合

  1. ソリューションエクスプローラーでプロジェクトを右クリック → 「NuGetパッケージの管理」

  2. 「参照」タブで System.IO.Ports を検索

  3. 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>

んで、実行!

Pasted image 20250722161351.png

…できた

が、コードに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()とセットで呼ぶのが安全。

動けば後はこっちのもん
じゃかじゃか弄って

Pasted image 20250722162953.png
Pasted image 20250722162930.png
OK!
一瞬有効っ数字がずれることあるけど一瞬やし人の目に知覚できるかできないかぐらいやから大丈夫大丈夫

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?