ArduinoからのデータをPCの画面に表示する簡易的なアプリの作り方を、紹介します。
センサーを使って計測する際などに、PCの大きな画面で確認できれば便利だと思います。
今回、C#に慣れていない状態で恐る恐る着手しましたが、高機能の開発環境のおかげで思ったより快適に作業できました。
最後に文法の勉強のために読んだ本の紹介もしているので、「C#?やったことない……。抵抗がある」という初めての方の参考になれば幸いです。
はじめに
Arduinoでセンサーを使った計測をする際、測定値を確認する方法は2つあります。
- LCDモジュールを回路に組み込んで、その画面に値を表示する
- PCに接続し、シリアル通信でデータを受け取る
上記一つ目だと、独立したシステムになるので、PCが無くても値を確認できます。ただしLCDモジュールを購入したり配線したりする必要があります。
二つ目は一番手軽な方法で、配線はUSBケーブルでPCに接続するだけです。データの送受信は、無料の開発環境であるArduino IDEの「シリアルモニタ」で実行できます。
ただし、上図のシリアルモニタだと基本的には文字の羅列なので、細かくデータの見た目を整えることができません。
個人的には細かい文字だと見るのがしんどいので、簡単な表示画面をWindowsで作ってみようと思った次第です。
この記事で作るもの
Arduinoからシリアル通信でデータを受信し、PCの画面上に表示する簡単なWindowsアプリを、C#で作ります。開発環境は、Visual studio 2022を使います。
動作デモ
▼動作している様子は、こんな感じです。(gifアニメ)
起動したら接続可能なポートをコンボボックスに表示し、接続ボタン押下で通信を開始します。
左側に大きい文字で距離と電圧を表示し、右側はログとして一行ずつデータを追記していきます。
▼ 測定の様子です。段ボールに貼った白い紙を、測距センサにかざしています。
システム構成
この記事のシステム構成は、下図の通りです。
- Arduino側の動作…… センサーを接続し、1秒ごとにAD変換された値をPCへ向けて出力する
- PC側の動作…… シリアル通信でArduinoからのデータを受信し、距離や電圧に換算して画面に表示する
- 最終的なコードは、最後に掲載しています。
開発環境と、使う部材
開発環境と、使う部材は以下の通りです。
Visual studio 2022(コミュニティ版)
マイクロソフトが開発している、ソフト開発のための総合開発環境です。
▼ ダウンロードはこちらからできます。
Windows PC
Windows 10 64bitのPCで実行しました。
Arduino
電子工作、各種測定、何にでも大活躍。手軽に使える便利なマイコンボードです。
▼ 初めて購入される方は、キットを買うと一気に部材が揃います。Arduino本体と、USBケーブル、抵抗やブレッドボードなどがそろっています。同梱されているブレッドボードやジャンプワイヤーは電子工作で日常的に使う物なので、入門後もずっと役立ちます。
▼ 単品で集めることもできます。(この記事の実験では、ブレッドボードやジャンプワイヤは使いませんが、電子工作では必需品です。)
▼ 秋月電子だとArduinoを始め、USBケーブルやブレッドボード、基板など豊富な選択肢からまとめ買いできます。
測距センサ
シャープ測距モジュール GP2Y0A21YK0Fを使いました。タミヤの工作ボードの端材に六角スペーサーを付けた台を作って、固定しています。
先端は、スズメッキ線を半田付けして、Arduinoのポートに直接挿しています。折れそうで不安定なので本来はコネクタを付けるべきなのですが、むかし半田付けしたものが手元にあったので今回使いました。
では、これから具体的な手順を説明します。
Arduino側の準備
配線する
Arduinoとセンサーを下図のように接続します。
使う測距センサには三本の線があり、それぞれ電源供給の5V、GND、信号線です。
コードを書く
測距センサのデータを、シリアル通信でPCへ出力するコードを書きました。
1秒ごとに送信するだけです。
送信データは、AD変換された値です。距離や電圧への換算は、PC側で行います。
データを出力できているか、確認
Windowsアプリを作る前に、そもそも測距センサのデータが正常にPCへ送信されているかを確認しておきます。Arduino IDEというArduino専用の開発環境を使います。
▼ Arduino IDE
PCにUSBケーブルでArduinoを接続し、Arduino IDEを立ち上げます。上部メニューのツール → シリアルモニタという機能を使えば、すぐに確認できます。
▼ 無事、データがPCへ送信されていました。
さて、ここからはWindowsアプリを作っていく作業に移ります。
Visual Studioを導入する
Visual Studioのコミュニティ版(個人使用が無料)をインストールし、立ち上げます。
▼ ダウンロードは、こちらからできます。
Windowsフォームアプリ を立ち上げる
▼ 立ち上げたらまずはこんな画面がでるので、「新しいプロジェクトの作成」を選択。
▼ C# 用の「Windows フォームアプリケーション(.NET Framework)」を選択し、次へ をクリックします。
プロジェクト名 や 保存場所 を適宜入力して、「作成」をクリックします。
Windowsアプリを作る環境が立ち上がります。
NuGet でsystem.IO.Portsをインストールしておく
Visual studioでシリアル通信を使うために、NuGet というシステムから、別途必要な機能のパッケージをインストールしておきます。
ちなみに、NuGetの発音は”New Get”(ニューゲッツ)です。.NETの動画でそう発音されていました。(調べるまでは、ずっとナゲット🍟かと思っていました)
▼ ツール → NuGet パッケージマネージャ → ソリューションのNuGetパッケージの管理 を選択し……
▼ 参照 をクリックして、検索欄に system.IO.Ports
と入力します。
候補が出てきたらsystem.IO.Portsを選択し、右に表示されている自分のプロジェクト名にチェックを入れて、インストールボタンをクリックすれば完了です。
ソフトの外観を作る
まずは、ソフトの外観を作ります。
ドラッグアンドドロップで、ぽんぽんとコントロール(ボタンなどの部品)を配置していきます。
見た目は、右下のプロパティパネルで適宜変えられます。
▼ こんな感じで配置してみました。
距離に換算する式を作っておく
次にプログラムのコードを書き進める前に、Arduinoから受信した値を距離に変換する式を作っておきます。
- 距離は、データシートのグラフから近似式を求めて、その式に電圧を代入します。
- 電圧は、ArduinoでAD変換された値から換算します。
(ArduinoのAD変換についてのドキュメントはこちら)
元にするデータシートのグラフは、以下のPDFの p5. Fig. 2 Exsample of distance measuring characteristics(output) に掲載されています。
- SHARP GP2Y0A21YK0 データシート
【PDF】https://jp.sharp/products/device/doc/opto/gp2y0a21yk_e.pdf
近似式は、エクセルの機能を使って求めました。
まず、データシートp5.下段の Inverse number of distance(1/cm) の値を読み取り、距離の逆数(x軸)と電圧(y軸)をエクセルにプロットします。
そして、xy軸を入れ替えて線形近似とみなし、エクセルの機能で式を求めます。
(グラフのデータ要素上で右クリック → 近似曲線の追加 を選択する)
y=0.0479x-0.0085 という式が出来上がりました。
▼ これを、プログラムでこんな感じで使用します。
//受信した値を電圧に換算
voltage = float.Parse(AdcVal) * 5 / 1024;
//Excelで出した近似式を使用し、電圧から距離を出す
distance = (float)(1/(0.0479 * voltage - 0.0085));
データシートのグラフからの値の読み取りは、目分量です。
▼ ちなみに、下図は今回の距離の測定結果です。5cmごとに測ってみました。
全体的にばらつきが大きいですが、特に40cmを超えるとデータが乱れてしまいました。今回はアプリを作るのが主目的なので、特に対策などはしませんでした。
コードを書いてく
さて、準備が整ったので、先ほど配置したコントロールにイベント、イベントハンドラーを追加して、実行するコードを書いていきます。
イベントを追加する
イベント、イベントハンドラーはざっくりと言うと下記のような意味です。
- イベント…… ボタンのクリックなど、アプリ上で起きた何らかの事象
- イベントハンドラー…… イベントの発生を受取り実行される処理
詳しくは、マイクロソフトのドキュメントや、最後に紹介しているC#おすすめ本をご覧ください。
追加方法
▼イベントは、ボタンなど対象のコントロールを選択後、右下のプロパティパネルの稲妻マークをクリックして、設定します。
例えば、「クリックされた」というイベントなら、アクションのClick欄にイベント名を記入してエンターキーで確定します。
すると、こんなコードが生成されます。(イベント名は、ConnectButtonClickedとした場合)
private void ConnectButtonClicked(object sender, EventArgs e)
{
//この中に処理を書く
}
今回のイベント
今回のイベントとその後の処理は下記の通りです。
- 接続ボタンをクリック時
- 接続を開始する
- 切断ボタンクリック時
- シリアル通信を終了する
- シリアルデータ受信時
- 受信した値を距離や電圧に換算する
- 換算した値を表示する(直接ではなくInvokeの引数として呼び出す)
表示値の処理には、Invokeメソッドを使った
受取ったデータをテキストやログとして表示する部分は、別途メソッドを用意し、デリゲートを使ってInvokeメソッドの引数として呼び出しています。以下のコードの7行目です。
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string AdcVal = sp.ReadLine();
//中略(受け取ったデータを電圧へ換算する処理など)
//メインスレッドで実行するための命令
Invoke(new AddLabelDelegate(AddLabel));
}
private delegate void AddLabelDelegate(); //デリゲート型を宣言
void AddLabel() //表示値の処理
{
distanceLabel.Text = distance.ToString("F2") + " cm" + "\r";
//以下略(テキストボックスやラベルの表示値を変える処理)
}
最初は、シリアルデータ受信時のイベントのイベントハンドラーに全部書いてしまっていました。
しかしドキュメントの「注釈」の項に、以下のような説明があったので修正しました。
(日本語版は読みづらい機械翻訳だったので、英語を引用します。)
The DataReceived event is raised on a secondary thread when data is received from the SerialPort object.(中略) If it is necessary to modify elements in the main Form or Control, post change requests back using Invoke, which will do the work on the proper thread.
https://learn.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.datareceived?view=dotnet-plat-ext-6.0
概要は、「シリアルデータ受信イベントの DataReceived event はセカンダリスレッドで発生するため、UI要素の変更などを行うのは適切ではない。その場合は、Invokeを使用しなさい」とのことです。
💡 ちなみに、どっちのスレッドで実行されてるのか確認してみました。
確認には、Visual studioの機能を使いました。まず確認したい行の左端をクリックして、ブレークポイントを設けます。
そして、デバッグを開始(緑色の三角形の再生ボタンを押下)して、上部メニューのデバッグ→ウインドウ→スレッド をクリックし表示パネルを出します。
そしてアプリを実行すると、ブレークポイント部分で処理が止まり、結果が表示されます。
▼ DataReceivedイベントのイベントハンドラー内を見ると、メインではなくワーカースレッドと表示されています。
▼ Invokeで呼び出すようにしたメソッドの中で確認すると、メインスレッドと表示されていました。正しく動いているようです。
書いたコード
最終的なコードは以下の通りです。
Arduino側
Windowsアプリ側
Arduinoから受け取った値を、距離や電圧に換算して、画面に表示します。
▼ コントロール(部品)に付けた名前は、下図の通りです。
参考文献
C#のドキュメント
- system.IO.Ports
https://learn.microsoft.com/ja-jp/dotnet/api/system.io.ports?view=dotnet-plat-ext-6.0 - SerialPort.GetPortNames メソッド(※ 接続可能なCOMポートを取得できる機能)
https://learn.microsoft.com/ja-jp/dotnet/api/system.io.ports.serialport.getportnames?view=dotnet-plat-ext-6.0 - SerialPort.DataReceived Event(※ シリアルデータを受信したら発生)
https://learn.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.datareceived?view=dotnet-plat-ext-6.0 - Visual Studio で C# を使用して Windows フォーム アプリを作成する
https://learn.microsoft.com/ja-jp/visualstudio/ide/create-csharp-winform-visual-studio?view=vs-2022 - イベントの処理と発生
https://learn.microsoft.com/ja-jp/dotnet/standard/events/
Arduinoのドキュメント
- analogRead() – Arduino Reference ※AD変換機能について
https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/ - Serial – Arduino Reference
https://www.arduino.cc/reference/en/language/functions/communication/serial/
C#が初めての方へ、おすすめ本
C#を始めから学べる書籍を紹介します。
▲ 第一歩のとっかかりとして最適な本です。ネットのチュートリアルだけではなかなか理解が進まなかった私は、この本に助けられました。
優しい語り口と随所のイラストがとてもかわいく、ほっこりと勉強を進められます。
最初はコンソールでプログラムの結果を確認しながら学習を進め、後半はWindowsアプリを作っていきます。イベントやイベントハンドラーも、本書の課題でボタンを設置したりしてるうちに、なんとなく馴染んできました。
作るアプリはシンプルな機能なので、プログラムの流れやコードの書き方を把握しやすく、とにかくC#に慣れることに専念できます。
デリゲートなど省かれている項目もありますが、読み終わる頃には、次の本やドキュメントを読むのが、だいぶ楽になっているはずです。
▲ 分厚い本です。先に紹介した「確かな力が身につく~」では扱われていなかった項目が網羅されていて、文法上で何かわからないことがあったら、すぐ参照しています。頼りになる本です。
プログラミング自体が初めての方は、まず先に紹介した入門書を一通り読んでおくと、理解がスムーズだと思います。
個人的には、今回Windows formを使う上で、最後の章に載っていた「11.4.1 イベントの基本」がとても勉強になりました。
イベントの発生側と受け取り側の処理の流れが、サンプルコードを用いて解説されています。
イベントはあまり馴染みのない概念だったので、コードを動かしながら読んでいると、少しずつ理解が進んできました。(まだ途上ですが)
内容は言語の仕様が中心でサンプルコードも全てコンソールアプリですが、「Windows formでGUIアプリを作りたい」という方にも基礎固めに役立つと思います。
動画もあります
この記事の内容を、6分半ほどの動画にしました。(注意:音声ありです)