PLR-7をUSBでPCに接続し、C#でA/F値を取得するアプリを作った話

前回の記事では、PLR-7 AFR ANALYZERをPCとLAN接続しようと試みましたが、IPアドレスのセグメント不一致により通信ができず、一旦断念しました。

今回は気を取り直して、PLR-7とPCをUSB経由で接続し、C#でWindowsフォームアプリを作成してA/F(空燃比)値の取得に成功したところまでの流れをご紹介します。


目次

■ USB接続でPLR-7をPCに認識させる

PLR-7はUSB経由でPCに接続すると、「USB Serial Port(COMポート)」として認識されます。
ドライバが正しくインストールされていれば、Windowsのデバイスマネージャーに以下のように表示されます。

ポート(COMとLPT)  
└ USB Serial Port (COM9)

このCOMポート番号は、後ほどC#アプリで通信ポートとして指定するため、必ずメモしておきます。私の場合は「COM9」でした。


■ PLR-7との通信仕様を確認

USB経由の通信は、実際にはシリアル通信(RS-232C)と同様のプロトコルで動作します。
PLR-7のマニュアルによると、通信仕様は以下のとおりです。

項目設定値
ボーレート115200 bps
データビット8ビット
パリティなし
ストップビット1ビット

また、PLR-7は「RMD\r\n」というコマンドを送ることで、現在の測定値を返してくれる仕様です。
このレスポンスは次のような形式になります。

RMD,2,3,0,2, 120.43

この最後の数値(120.43)が、現在のA/F値となります。


■ C#でWindowsフォームアプリを作成

Visual StudioでWindowsフォームアプリケーションのプロジェクトを作成し、以下のような構成でアプリを作りました。

UI要素

  • TextBox(名前:textBox_AF_Value
     → 取得したA/F値をリアルタイムに表示する領域

実装の流れ(概要)

  1. 起動時にPLR-7をリモートモードに切り替える(SRM,01\r\n
  2. 続いて測定モードに切り替える(AOP,01\r\n
  3. 一定間隔(0.1秒)で RMD\r\n を送信
  4. PLR-7から返ってきたレスポンス(例:RMD,2,3,0,2, 120.41)の最後の値を抽出
  5. TextBoxに表示

■ 実際のC#コード

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AF計の値出力アプリ
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort;
        private Timer requestTimer;
        private StringBuilder receiveBuffer = new StringBuilder();

        public Form1()
        {
            InitializeComponent();

            serialPort = new SerialPort("COM9", 115200, Parity.None, 8, StopBits.One);
            serialPort.DataReceived += SerialPort_DataReceived;

            try
            {
                serialPort.Open();
                Debug.WriteLine("シリアルポートを開きました。");
                InitializeRemoteAndMeasurementModeAsync();
            }
            catch (Exception ex)
            {
                MessageBox.Show("シリアルポート接続エラー: " + ex.Message);
            }
        }

        private async void InitializeRemoteAndMeasurementModeAsync()
        {
            try
            {
                serialPort.Write("SRM,01\r\n"); // リモートモード
                await Task.Delay(500);

                serialPort.Write("AOP,01\r\n"); // 測定モード
                await Task.Delay(5000); // 測定モード有効まで待機

                requestTimer = new Timer();
                requestTimer.Interval = 100; // 0.1秒ごとに送信
                requestTimer.Tick += RequestTimer_Tick;
                requestTimer.Start();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("初期化エラー: " + ex.Message);
            }
        }

        private void RequestTimer_Tick(object sender, EventArgs e)
        {
            if (serialPort.IsOpen)
            {
                try
                {
                    serialPort.Write("RMD\r\n");
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("送信エラー: " + ex.Message);
                }
            }
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                string incoming = serialPort.ReadExisting();
                receiveBuffer.Append(incoming);

                while (receiveBuffer.ToString().Contains("\r\n"))
                {
                    string buffer = receiveBuffer.ToString();
                    int endIndex = buffer.IndexOf("\r\n");
                    string oneLine = buffer.Substring(0, endIndex);
                    receiveBuffer.Remove(0, endIndex + 2);

                    Match match = Regex.Match(oneLine, @"RMD,\d+,\d+,\d+,\d+,\s*(-?\d+\.\d+)");

                    if (match.Success)
                    {
                        string afValue = match.Groups[1].Value;
                        Invoke(new Action(() =>
                        {
                            textBox_AF_Value.Text = afValue;
                        }));
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("受信エラー: " + ex.Message);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                serialPort.Close();
            }
        }

        private void textBox_AF_Value_TextChanged(object sender, EventArgs e)
        {
        }
    }
}

■ 値の受信と表示に成功

PLR-7からの応答は分割されて届くことがあるため、SerialPort.DataReceived イベントで受信したデータを一時的にバッファにためて、改行(\r\n)で1行ずつ分割して処理しました。

正規表現を使って、レスポンスから数値部分のみを抽出し、TextBoxに表示することで、リアルタイムにA/F値が画面に表示されるアプリが完成しました。


■ 今後の展開

次回は、このアプリにロガー機能(CSV形式での保存)を追加し、A/F値の記録とファイル保存ができるようにしていきます。
また、取得したデータをグラフ化することも検討しています。


■ まとめ

  • USB接続では、PLR-7は「USB Serial Port(COMポート)」として扱える
  • C#でのシリアル通信は SerialPort クラスを使用すれば比較的簡単
  • 正確な応答を得るためには、リモートモード・測定モードへの切り替えが必要
  • A/F値の取得と画面表示までは成功

次回の記事では、ログ機能の追加とCSV保存の仕組みを紹介します。
同じようにPLR-7をPCで活用したい方の参考になれば幸いです。

副業から独立まで「稼げる」Webスキルを習得する(PR)

ここまで読んでいただきありがとうございます。 最後に宣伝をさせてください。

「副業を始めたいが、何から手をつければいいかわからない」「独学でスキルはついたが、収益化できていない」という悩みを持つ方には、マンツーマン指導のWebスクール**「メイカラ」**が適しています。

このスクールは、単に技術を教えるだけでなく、**「副業として具体的にどう稼ぐか」**という実務直結のノウハウ提供に特化している点が特徴です。

講師陣は、実際に「副業Webライターから1年で独立して月収100万円」を達成したプロや、現役で利益を出し続けているブロガーなど、確かな実績を持つプレイヤーのみで構成されています。そのため、机上の空論ではない、現場で通用する戦術を学ぶことができます。

副業に特化した強み

  • 最短ルートの提示: 未経験からでも実績を出せるよう、マンツーマンで指導。
  • AI活用の習得: 副業の時間対効果を最大化するための、正しいAI活用スキルも網羅。
  • 案件獲得のチャンス: 運営がWebマーケティング会社であるため、実力次第で社内案件の紹介など、仕事に直結する可能性があります。

受講者の多くは、「在宅でできる仕事を探している」「副業を頑張りたい」という20代・30代・40代が中心です。

受講前には、講師による無料説明が行われます。無理な勧誘はなく、自分に合った副業スタイルやプランを相談できるため、まずは話を聞いてみることから始めてみてはいかがでしょうか。

ブログで稼ぎたいなら「メイカラ」

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次