はじめに
これは Delphi Advent Calendar 2017 16 日目の記事です。
まさか 3 日目のやましょうさんの記事とカブるとは思わなかったのでボツにしようかとも思ったのですが、折角書いたので公開する事にしました。
記事の趣旨としては Arduino とシリアル通信してみようというものです。
See Also:
Delphi 用のシリアル通信コンポーネント/ライブラリ
Delphi は標準でシリアル通信コンポーネントを持っていませんが、GetIt パッケージマネージャでインストールできるものがあります。
Turbo Pack Async Pro (VCL)
ライセンスは MPL です。VCL アプリケーションで使えます。
TMS Async (VCL)
有償のコンポーネントです。GetIt にあるのはトライアル版です。VCL アプリケーションで使えます。
Uni232C (VCL / FireMonkey)
やましょうさんトコのコンポーネント。マルチプラットフォームで動作。有償ですが、GetIt のは Windows 限定版 (WUni232C) で無償で使えます。
Communication Lab (VCL / FireMonkey)
有償のコンポーネントです。FireMonkey でも動作します。
以前は GetIt にあった気がするのですが...そして、以前は Free for non-commercial use. の表記があったと記憶しているのですが、今では確認できません。ライセンスが変更されたのでしょうか?
余談ですが、作者の Mitov さんは Visuino の作者としても知られています。
Comport Library (VCL)
ライセンスはパブリックドメインとなっています。VCL アプリケーションで使えます。新しいバージョン用のパッケージはこちらにあります、
以前これで PC-E500 用の転送ソフトを作りました。
ComPort for xxxxxx (FireMonkey)
- ComPort for FireMonkey (Winsoft) - FireMonkey
- ComPort for macOS (Winsoft) - FireMonkey
- ComPort for Android (Winsoft) - FireMonkey
これも以前は GetIt にあった気がするのですが...有償です。FireMonkey アプリケーションで使えます。
Ararat Synapse
今回ご紹介するのは Ararat Synapse です。自由に使ってよいそうです (BDS style license って?)。
Ararat Synapse は通信ライブラリで、その機能の一部にシリアル通信 (SynaSer) があります。
インストール
Delphi は無償の Starter Edition を使います。
ライブラリは C:\Delphi_Lib\Synapse に置く事にします。
[ファイル | バージョン管理リポジトリから開く]
Subversion を選択。[OK] ボタンを押す。
以下のように指定します。
コピー先は Syanapse ライブラリの保存先なので適当な場所に変更してもらって構いません。
設定項目 | 内容 |
---|---|
リポジトリの URL | svn://svn.code.sf.net/p/synalist/code/trunk |
コピー先 | C:\Delphi_lib\Synapse |
再帰的 | チェック |
[OK] を押すとチェックアウトが開始されます。
チェックアウトが終了したら、[ツール | オプション] で ▶環境オプション ▶Delphi オプション ▶ライブラリと辿ります。
[ライブラリパス] の右側の […] ボタンを押します。
Syanapse のパスを入力し、[追加] ボタンを押し、[OK] ボタンを押します。
これで利用準備ができました。
シリアル通信 (受信)
uses に Synaser を追加すれば、シリアル通信ライブラリが使えるようになります。
unit Unit1;
interface
uses
..., Synaser; // <-- 追加
...
受信を試すには送信側を作る必要がありますね。これを Arduino (UNO) でやります。
スケッチはこんな感じです。
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 で受信すればいいわけです。
こんな感じでコンポーネントを貼り付け、
Timer1 は最初止めておきます。1 秒おきにデータが来るので 250ms (1/4 秒) くらいの頻度にします。
コードはこんな感じになります。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 からのデータを受信できます。
シリアル通信 (送信)
今度は逆です。
Arduino 側は "hello" を受信すると内蔵 LED (13) をちょっとの間光らせます。
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 側はこんな感じでコンポーネントを配置して、
コードはこんな感じになります。
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 がちょっとだけ点灯します。
FireMonkey の場合
SynaSer (Synapse) はフレームワークに依存しないので...
こんな感じで FireMonkey フォームを作って、
こんな感じのコードを書けば、
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) でも動作します。
おわりに
シリアル通信はチョー簡単でしたね。
なお、冒頭でお断りしたように Syanapse は通信ライブラリでして、例えば blcksock を uses に加えると TBlockSocket クラスを用いて UDP 通信ができます。まぁ、Delphi には最初から Indy ライブラリがあるので出番は少ないかもしれませんが。
なお、先日 Delphi 10.2.2 がリリースされました。Starter Edition (無償版) も更新できますヨ!