📞Arduinoのシリアル通信 1

ArduinoSerial Arduino

ArduinoとWindows間で、USB経由でシリアル通信をする機会が多くあったので、昔を思い出しつつまとめていきたいと思っています。今回は、Windows側から送信したコマンドをArduinoで受信し、Arduino上のLEDをON/OFFするという、シリアル通信版「Lチカ」です。

1.Arduino側のプログラム

Arduino IDEで書き込むプログラムです。PCから文字を受け取り、それに応じて内蔵LED(13番ピン)をON/OFFします。

C++
// Arduino側:受信プログラム
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600); // 通信速度を9600bpsに設定
}

void loop() {
  if (Serial.available() > 0) {
    char data = Serial.read(); // データを1文字読み込む

    if (data == '1') {
      digitalWrite(LED_BUILTIN, HIGH); // LED点灯
      Serial.println("LED ON");
    } 
    else if (data == '0') {
      digitalWrite(LED_BUILTIN, LOW);  // LED消灯
      Serial.println("LED OFF");
    }
  }
}

2.Windows側のコード [C#](コンソール版)

C#のSystem.IO.Ports.SerialPort1クラスを使用して、Arduinoが接続されているCOMポート2へデータを送信します。

  1. System.IO.Portsは、元々標準で使用可能でしたが、現在は外付けのパッケージになっています。NugetからSystem.IO.Portsをインストールしてください。 ↩︎
  2. コードの中でCOM3としていますが、お手元の環境によって番号変わって来ますので、Arduinoを接続したうえで、デバイスマネージャで確認して必要に応じて変更してください。 ↩︎
C#
using System;
using System.IO.Ports;

class Program
{
    static void Main()
    {
        // ポート名(COM3などは環境に合わせて変更)と通信速度を指定
        SerialPort serialPort = new SerialPort("COM3", 9600);

        try
        {
            serialPort.Open(); // ポートを開く
            Console.WriteLine("通信開始:1で点灯、0で消灯、Qで終了");

            while (true)
            {
                string input = Console.ReadLine();
                if (input.ToLower() == "q") break;

                serialPort.Write(input); // Arduinoへデータを送信
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            if (serialPort.IsOpen) serialPort.Close(); // ポートを閉じる
        }
    }
}

3.Windows側のプログラム [C#] Form版

Form版のコードです。Form版もSystem.IO.Ports.SerialPortをNuGetでインストールしておく必要があります。COMポートCOMポートの確認も忘れずに。

3.1 画面構成

Form画面の構成を以下のように設定してください。

3.2 ソースコード

Arduino側のコードは前回(コンソール版)と同じもので動作します。

C#
using System;
using System.IO.Ports;
using System.Windows.Forms;

namespace ArduinoGuiApp
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort;

        public Form1()
        {
            InitializeComponent();
            InitializeSerialPort();
        }

        private void InitializeSerialPort()
        {
            serialPort = new SerialPort();
            serialPort.BaudRate = 9600;

            // 利用可能なCOMポートを列挙してコンボボックスに追加
            string[] ports = SerialPort.GetPortNames();
            cmbPortName.Items.AddRange(ports);
            if (ports.Length > 0) cmbPortName.SelectedIndex = 0;
        }

        // 接続・切断ボタンの処理
        private void btnConnect_Click(object sender, EventArgs e)
        {
            if (!serialPort.IsOpen)
            {
                try
                {
                    serialPort.PortName = cmbPortName.Text;
                    serialPort.Open();
                    btnConnect.Text = "切断";
                    ToggleControls(true);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("接続に失敗しました: " + ex.Message);
                }
            }
            else
            {
                serialPort.Close();
                btnConnect.Text = "接続";
                ToggleControls(false);
            }
        }

        // 送信処理:LED ON
        private void btnLedOn_Click(object sender, EventArgs e)
        {
            SendData("1");
        }

        // 送信処理:LED OFF
        private void btnLedOff_Click(object sender, EventArgs e)
        {
            SendData("0");
        }

        private void SendData(string data)
        {
            if (serialPort.IsOpen)
            {
                serialPort.Write(data);
            }
        }

        private void ToggleControls(bool connected)
        {
            btnLedOn.Enabled = connected;
            btnLedOff.Enabled = connected;
            cmbPortName.Enabled = !connected;
        }

        // フォームを閉じる際にポートを確実に閉じる
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            if (serialPort.IsOpen) serialPort.Close();
            base.OnFormClosing(e);
        }
    }
}

3.2 ソースの解説

  • ポートの自動取得: SerialPort.GetPortNames() を使うことで、ポート番号を選択するようにしました。
  • 状態管理: ポートが閉じているときに送信ボタンを押すとエラーになるため、接続状態に応じて Enabled プロパティを切り替える(ボタンをグレーアウトさせる)処理をしています。
  • リソース管理: フォームを閉じる際(OnFormClosing)にポートを閉じる処理をしえています。

4.まとめ

今回は、Arduino側が受信専用、Windows側が送信専用になっている一方通行の通信の例でしたが、無事LEDのON/OFFはできたでしょうか。

Arduinoからセンサーのデータを取得したり、時間のかかる処理をしてその完了を取得する場合は、双方向の通信が必要になり、より複雑な処理が必要になりますので、次の機会に説明したいと思っています。