Arduino
Delphi
Pascal
IoT
objectpascal
DelphiDay 16

Delphi で Arduino につなぐ (シリアル通信)

はじめに

これは Delphi Advent Calendar 2017 16 日目の記事です。

まさか 3 日目のやましょうさんの記事とカブるとは思わなかったのでボツにしようかとも思ったのですが、折角書いたので公開する事にしました。

記事の趣旨としては Arduino とシリアル通信してみようというものです。

See Also:

Delphi 用のシリアル通信コンポーネント/ライブラリ

Delphi は標準でシリアル通信コンポーネントを持っていませんが、GetIt パッケージマネージャでインストールできるものがあります。

Turbo Pack Async Pro (VCL)

ライセンスは MPL です。VCL アプリケーションで使えます。

image.png

TMS Async (VCL)

有償のコンポーネントです。GetIt にあるのはトライアル版です。VCL アプリケーションで使えます。

image.png

Uni232C (VCL / FireMonkey)

やましょうさんトコのコンポーネント。マルチプラットフォームで動作。有償ですが、GetIt のは Windows 限定版 (WUni232C) で無償で使えます。

image.png

Communication Lab (VCL / FireMonkey)

image.png

有償のコンポーネントです。FireMonkey でも動作します。

以前は GetIt にあった気がするのですが...そして、以前は Free for non-commercial use. の表記があったと記憶しているのですが、今では確認できません。ライセンスが変更されたのでしょうか?

余談ですが、作者の Mitov さんは Visuino の作者としても知られています。

Comport Library (VCL)

ライセンスはパブリックドメインとなっています。VCL アプリケーションで使えます。新しいバージョン用のパッケージはこちらにあります、

image.png

以前これで PC-E500 用の転送ソフトを作りました。

ComPort for xxxxxx (FireMonkey)

これも以前は GetIt にあった気がするのですが...有償です。FireMonkey アプリケーションで使えます。

Ararat Synapse

今回ご紹介するのは Ararat Synapse です。自由に使ってよいそうです (BDS style license って?)。

Ararat Synapse は通信ライブラリで、その機能の一部にシリアル通信 (SynaSer) があります。

インストール

Delphi は無償の Starter Edition を使います。

ライブラリは C:\Delphi_Lib\Synapse に置く事にします。

[ファイル | バージョン管理リポジトリから開く]

image.png

Subversion を選択。[OK] ボタンを押す。

image.png

以下のように指定します。

image.png

コピー先は Syanapse ライブラリの保存先なので適当な場所に変更してもらって構いません。

設定項目 内容
リポジトリの URL svn://svn.code.sf.net/p/synalist/code/trunk
コピー先 C:\Delphi_lib\Synapse
再帰的 チェック

[OK] を押すとチェックアウトが開始されます。

image.png

チェックアウトが終了したら、[ツール | オプション] で ▶環境オプション ▶Delphi オプション ▶ライブラリと辿ります。

image.png

[ライブラリパス] の右側の […] ボタンを押します。

image.png

Syanapse のパスを入力し、[追加] ボタンを押し、[OK] ボタンを押します。

image.png

これで利用準備ができました。

シリアル通信 (受信)

uses に Synaser を追加すれば、シリアル通信ライブラリが使えるようになります。

unit Unit1;

interface

uses
  ..., Synaser; // <-- 追加


  ...

受信を試すには送信側を作る必要がありますね。これを Arduino (UNO) でやります。

image.png

スケッチはこんな感じです。

Serial_Out.ino
void setup() {
  Serial.begin(115200);
  while(!Serial);
}

void loop() {
  Serial.println("Hello, world.");
  delay(1000);
}

一秒おきに "Hello,world." を送信します。通信速度は 115200 bps です。Arduino UNO は COM6 で認識されているようです。

...という事は、Delphi では COM6 をオープンして 115200 bps で受信すればいいわけです。

こんな感じでコンポーネントを貼り付け、

image.png

Timer1 は最初止めておきます。1 秒おきにデータが来るので 250ms (1/4 秒) くらいの頻度にします。

image.png

コードはこんな感じになります。TBlockSerial クラスを操作します。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Synaser;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    btnStart: TButton;
    btnStop: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    Timer1: TTimer;
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private 宣言 }
    ser: TBlockSerial;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.btnStartClick(Sender: TObject);
// Start ボタン押下時
begin
  if Assigned(ser) then
    Exit;
  ser := TBlockSerial.Create;
  ser.Config(115200, 8, 'N', SB1, False, False); // ボーレート他の設定
  ser.Connect(Edit1.Text);                       // COM ポートの指定を伴って接続
  Timer1.Enabled := True;
end;

procedure TForm1.btnStopClick(Sender: TObject);
// Stop ボタン押下時
begin
  Timer1.Enabled := False;
  if not Assigned(ser) then
    Exit;
  ser.Free;
  ser := nil;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
// タイマーイベント
var
  buf: string;
begin
  if not Assigned(ser) then
    Exit;
  if not ser.CanRead(0) then
    Exit;
  buf := ser.Recvstring(0);
  Memo1.Lines.Append(buf);
end;

end.

Arduino UNO を起動しておき、実行して [Start] ボタンを押すと Arduino UNO からのデータを受信できます。

image.png

シリアル通信 (送信)

今度は逆です。

image.png

Arduino 側は "hello" を受信すると内蔵 LED (13) をちょっとの間光らせます。

Serial_In.ino
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(115200);
  while(!Serial);
}

void loop() {
  String str = Serial.readStringUntil('Q'); 
  if (str == "hello") {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(2000);
  }
  digitalWrite(LED_BUILTIN, LOW);
}

Delphi 側はこんな感じでコンポーネントを配置して、

image.png

コードはこんな感じになります。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Synaser;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Edit1: TEdit;
    btnStrat: TButton;
    btnStop: TButton;
    edSend: TEdit;
    btnSend: TButton;
    procedure btnStratClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
  private
    { Private 宣言 }
    ser: TBlockSerial;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnStratClick(Sender: TObject);
// Start ボタン押下時
begin
  if Assigned(ser) then
    Exit;
  ser := TBlockSerial.Create;
  ser.Config(115200, 8, 'N', SB1, False, False); // ボーレート他の設定
  ser.Connect(Edit1.Text);                       // COM ポートの指定を伴って接続
end;

procedure TForm1.btnStopClick(Sender: TObject);
// Stop ボタン押下時
begin
  if not Assigned(ser) then
    Exit;
  ser.Free;
  ser := nil;
end;

procedure TForm1.btnSendClick(Sender: TObject);
// Send ボタン押下時
begin
  if not Assigned(ser) then
    Exit;

  ser.SendString(edSend.Text);
end;

end.

送るだけなので簡単です。

Arduino を先に起動しておき、[Start] ボタンを押してから Edit に適当な文字を入れて [Send] ボタンを押してみてください。文字列が "hello" なら Arduino UNO の内蔵 LED がちょっとだけ点灯します。

image.png

FireMonkey の場合

SynaSer (Synapse) はフレームワークに依存しないので...

image.png

image.png

こんな感じで FireMonkey フォームを作って、

image.png

こんな感じのコードを書けば、

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Edit, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo, Synaser;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Label1: TLabel;
    Edit1: TEdit;
    btnStart: TButton;
    btnStop: TButton;
    Timer1: TTimer;
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { private 宣言 }
    ser: TBlockSerial;
  public
    { public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.btnStartClick(Sender: TObject);
// Start ボタン押下時
begin
  if Assigned(ser) then
    Exit;
  ser := TBlockSerial.Create;
  ser.Config(115200, 8, 'N', SB1, False, False); // ボーレート他の設定
  ser.Connect(Edit1.Text);                       // COM ポートの指定を伴って接続
  Timer1.Enabled := True;
end;

procedure TForm1.btnStopClick(Sender: TObject);
// Stop ボタン押下時
begin
  Timer1.Enabled := False;
  if not Assigned(ser) then
    Exit;
  ser.DisposeOf;
  ser := nil;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
// タイマーイベント
var
  buf: string;
begin
  if not Assigned(ser) then
    Exit;
  if not ser.CanRead(0) then
    Exit;
  buf := ser.Recvstring(0);
  Memo1.Lines.Append(buf);
end;

end.

VCL と同じように FireMonkey (Windows) でも動作します。

image.png

おわりに

シリアル通信はチョー簡単でしたね。

なお、冒頭でお断りしたように Syanapse は通信ライブラリでして、例えば blcksock を uses に加えると TBlockSocket クラスを用いて UDP 通信ができます。まぁ、Delphi には最初から Indy ライブラリがあるので出番は少ないかもしれませんが。

なお、先日 Delphi 10.2.2 がリリースされました。Starter Edition (無償版) も更新できますヨ!