Dobot Magician UART 2020/03/12
2020/11/13 Bluetooth / UART 版を作成しました。
こちらのほうがコードが整理されており、分かりやすいです。
また、アラーム内容の取得、可動範囲判断が追加されています。
■Dobot Magician UART 接続のDelphi サンプル(残骸)です。
Dobot 本体のインターフェイスコネクタ (MIL 10P)に UART 信号があります。
Windows の場合、これを使用しなくても、USB 経由の仮想 COM ポートを接続先として使用できまます。
※仮想 COM ポートは、Dobot Studio をインストールすると作成されます。
バイト列で送信、受信を行います。DobotDLL が無くても動きます。
Arduino でも同じ感じで通信が行えます。Wi-Fi アダプタを使った UDP 通信も同じ内容になります。
シリアル通信は、APDComport コンポーネントを使っています。受信がイベント取得になるので、分かりにくいコードになっています。
実装では、シンプルな Uni232C コンポーネントを使用しています。Windows 64bit 環境、Android 環境でも使用できます。
以下、残骸ですが、とっかかりの参考にはなると思います。
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, OoMisc, AdPort, Vcl.StdCtrls;
type
TIOFunction = (
IOFunctionDummy, //Do not config function
IOFunctionPWM, //PWM Output
IOFunctionDO, //IO Output
IOFunctionDI, //IO Input
IOFunctionADC //AD Input
);
type
TForm4 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
Button3: TButton;
ApdComPort1: TApdComPort;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Button8: TButton;
Button9: TButton;
Button10: TButton;
Button11: TButton;
Button12: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure ApdComPort1TriggerAvail(CP: TObject; Count: Word);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure Button9Click(Sender: TObject);
procedure Button10Click(Sender: TObject);
procedure Button11Click(Sender: TObject);
procedure Button12Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
resBuf : TBytes;
resEnd : boolean;
resBegin : boolean;
resIndex : integer;
resCount : integer;
procedure initResParams;
function WaitResEnd(timeOut: Cardinal): boolean;
function GetQueuedCmdCurrentIndex(var queuedCmdCurrentIndex: Uint64):boolean;
function GetPose(var x, y, z, r: single; var a0, a1, a2, a3: single): boolean;
function SetEndEffectorSuctionCup(ctrl, suck: Byte):boolean;
function GetEndEffectorSuctionCup(var isCtrlEnable: Byte; var isSucked: Byte): boolean;
function ClearAllAlarmsState: boolean;
function SetIOMultiplexing(ioAddress: Byte; ioFunction: TIOFunction): boolean;
function SetIODO(ioAddress: Byte; ioLevel: Byte): boolean;
function GetIODO(ioAddress: Byte; var ioLevel: Byte): boolean;
function GetIODI(ioAddress: Byte; var ioValue: Byte):boolean;
function SetQueuedCmdForceStopExec: boolean;
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
// 強制出力停止
// 通信は出来なくなる。
// Dobot本体のリセット処理が必要。その後、再接続処理が必要。
// 使わないほうが良い
function TForm4.SetQueuedCmdForceStopExec: boolean;
var
B : TBytes;
begin
result := false;
if ApdComport1.Open then begin
SetLength(B, 6);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 0; // len= 2 + Payload Len;
B[3] := 242; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0D;
InitResParams;
ApdComPort1.PutBlock(B[0], 6);
waitResEnd(500);
result := (Length(resBuf) = 6) and (resBuf[3] = 242);
end;
end;
function TForm4.GetIODI(ioAddress: Byte; var ioValue: Byte):boolean;
// GetIODI 入力状態を取得
var
checkSum : Byte;
i : integer;
B : TBytes;
begin
result := False;
if ApdComport1.Open then begin
SetLength(B, 8);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 2; // len= 2 + Payload Len;
B[3] := 133; // id;
B[4] := 0; // ctrl r/w = r, isQueued = False;
B[5] := ioAddress;
B[6] := 0;
// チェックサム
checkSum := 0;
for i := 3 to 6 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[7] := checkSum;
initResParams;
ApdComPort1.PutBlock(B[0], 8);
waitResEnd(500);
result := (Length(resBuf) = 8) and (resBuf[3] = 133) and (resBuf[5] = ioAddress);
if result then
ioValue := resBuf[6];
end;
end;
function TForm4.GetIODO(ioAddress: Byte; var ioLevel: Byte): boolean;
// 出力状態を取得
var
checkSum : Byte;
i : integer;
B : TBytes;
begin
result := False;
if ApdComport1.Open then begin
SetLength(B, 8);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 2; // len= 2 + Payload Len;
B[3] := 131; // id;
B[4] := 0; // ctrl r/w = r, isQueued = False;
B[5] := ioAddress;
B[6] := 0;
// チェックサム
checkSum := 0;
for i := 3 to 6 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[7] := checkSum;
initResParams;
ApdComPort1.PutBlock(B[0], 8);
waitResEnd(500);
result := (Length(resBuf) = 8) and (resBuf[3] = 131) and (resBuf[5] = ioAddress);
if result then
ioLevel := resBuf[6];
end;
end;
function TForm4.SetIODO(ioAddress: Byte; ioLevel: Byte): boolean;
// 出力
var
checkSum : Byte;
i : integer;
B : TBytes;
s : string;
begin
result := False;
if ApdComport1.Open then begin
SetLength(B, 8);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 2; // len= 2 + Payload Len;
B[3] := 131; // id;
B[4] := 1; // ctrl r/w = W, isQueued = False;
B[5] := ioAddress; // EIO Num(1..20)
B[6] := ioLevel; // Level output 0-Low level 1-High level
// チェックサム
checkSum := 0;
for i := 3 to 6 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[7] := checkSum;
initResParams;
ApdComPort1.PutBlock(B[0], 8);
waitResEnd(500);
s := '';
for i := 0 to Length(resBuf) - 1 do
s := s + IntToHex(resBuf[i], 2) + '';
Memo1.Lines.Add(s);
result := (Length(resBuf) = 8) and (resBuf[3] = 131);
end;
end;
function TForm4.SetIOMultiplexing(ioAddress: Byte; ioFunction: TIOFunction): boolean;
// 多重入出力を設定
var
checkSum : Byte;
i : integer;
B : TBytes;
begin
result := False;
if ApdComport1.Open then begin
SetLength(B, 8);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 2; // len= 2 + Payload Len;
B[3] := 130; // id;
B[4] := 1; // ctrl r/w = W, isQueued = False;
B[5] := ioAddress; // EIO Num(1..20)
B[6] := Ord(ioFunction); // TIOFunction,
// チェックサム
checkSum := 0;
for i := 3 to 6 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[7] := checkSum;
initResParams;
ApdComPort1.PutBlock(B[0], 8);
waitResEnd(500);
result := (Length(resBuf) = 8) and (resBuf[3] = 130);
end;
end;
function TForm4.ClearAllAlarmsState: boolean;
// アラームクリア
var
B : TBytes;
begin
result := false;
if ApdComport1.Open then begin
SetLength(B, 6);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 0; // len= 2 + Payload Len;
B[3] := 20; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $EB;
InitResParams;
ApdComPort1.PutBlock(B[0], 6);
waitResEnd(500);
result := (Length(resBuf) = 6) and (resBuf[3] = 20);
end;
end;
function TForm4.GetEndEffectorSuctionCup(var isCtrlEnable: Byte; var isSucked: Byte): boolean;
// 吸引カップの状態を取得
var
B : TBytes;
begin
result := False;
if ApdComport1.Open then begin
SetLength(B, 6);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 0; // len= 2 + Payload Len;
B[3] := 62; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
B[5] := $C2; // checkSum;
InitResParams;
ApdComPort1.PutBlock(B[0], 6);
waitResEnd(500);
result := (Length(resBuf) = 8) and (resBuf[3] = 62);
if result then begin
isCtrlEnable := resBuf[5];
isSucked := resBuf[6];
end;
end;
end;
function TForm4.SetEndEffectorSuctionCup(ctrl, suck: Byte):boolean;
// 吸引カップ ON/OFF
var
B : TBytes;
i : integer;
checkSum : Byte;
begin
result := False;
if ApdComport1.Open then begin
SetLength(B, 8);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 2; // len= 2 + Payload Len;
B[3] := 62; // id;
B[4] := 3; // ctrl r/w($01) = W, isQueued($02) = True;
B[5] := ctrl; //isCtrlEnabled
B[6] := suck; //issucked
// チェックサム
checkSum := 0;
for i := 3 to 6 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[7] := checkSum;
InitResParams;
ApdComPort1.PutBlock(B[0], 8);
waitResEnd(500);
result := (Length(resBuf) = 14) and (resBuf[3] = 62);
end;
end;
function TForm4.GetPose(var x, y, z, r: single; var a0, a1, a2, a3: single): boolean;
// GetPose 現在値を取得
var
i : integer;
B : TBytes;
begin
result := false;
if ApdComport1.Open then begin
SetLength(B, 6);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 0; // len= 2 + Payload Len;
B[3] := 10; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $F6;
InitResParams;
ApdComPort1.PutBlock(B[0], 6);
waitResEnd(2000); // Homing 時は2秒程度必要
if (Length(resBuf) = 38) and (resBuf[3] = 10) then begin
for i := 0 to 3 do begin
x.Bytes[i] := resBuf[5 + i];
y.Bytes[i] := resBuf[9 + i];
z.Bytes[i] := resBuf[13 + i];
r.Bytes[i] := resBuf[17 + i];
a0.Bytes[i] := resBuf[21 + i];
a1.Bytes[i] := resBuf[25 + i];
a2.Bytes[i] := resBuf[29 + i];
a3.Bytes[i] := resBuf[33 + i];
end;
result := true;
end;
end;
end;
function TForm4.GetQueuedCmdCurrentIndex(var queuedCmdCurrentIndex: Uint64): boolean;
// コマンドキューの現在の位置
var
B : TBytes;
begin
result := False;
if ApdComport1.Open then begin
SetLength(B, 6);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 0; // len= 2 + Payload Len;
B[3] := 246; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $A;
InitResParams;
ApdComPort1.PutBlock(B[0], 6);
waitResEnd(2000); // Homing 時は2秒程度必要
if (Length(resBuf) = 14) and (resBuf[3] = 246) then begin
queuedCmdCurrentIndex :=
resBuf[5+0] or
(resBuf[5+1] shl 8) or
(resBuf[5+2] shl 16) or
(resBuf[5+3] shl 24) or
(UInt64(resBuf[5+4]) shl 32) or
(UInt64(resBuf[5+5]) shl 40) or
(UInt64(resBuf[5+6]) shl 48) or
(UInt64(resBuf[5+7]) shl 56);
result := true;
end;
end;
end;
function TForm4.WaitResEnd(timeOut: Cardinal): boolean;
// Dobot 返信が終わるまで待つ
var
Ticks : Cardinal;
begin
result := false;
Ticks := GetTickCount;
while not resEnd do begin
Sleep(10);
Application.ProcessMessages;
if resEnd then begin
Sleep(10);
result := True;
break;
end
else if (GetTickCount - Ticks) > timeOut then break;
end;
end;
procedure TForm4.InitResParams;
// 受信待ちの変数を初期化
begin
SetLength(resBuf, 0);
resEnd := False;
resBegin := False;
resIndex := 0;
resCount := 0;
end;
procedure TForm4.ApdComPort1TriggerAvail(CP: TObject; Count: Word);
// Dobot からの返信
var
B : TBytes;
i : integer;
begin
if Count > 0 then begin
SetLength(B, Count);
ZeroMemory(B, Count);
ApdComPort1.GetBlock(B[0], Count);
for i := 0 to Count -1 do begin
if not resBegin and (B[i] = $AA) then begin
resBegin := True;
resIndex := 0;
SetLength(resBuf, 3);
end
else if resBegin and (resIndex = 2) then begin
SetLength(resBuf, B[i] + 4);
resCount := B[i] + 4;
end;
if Length(resBuf) > resIndex then
resBuf[resIndex] := B[i];
Inc(resIndex);
if resIndex = resCount then begin
resEnd := True;
break;
end;
end;
end;
end;
procedure TForm4.Button10Click(Sender: TObject);
// SetIOMultiplexing
var
ioLevel : Byte;
begin
if ApdComport1.Open then begin
SetIOMultiplexing(18, TIOFunction.IOFunctionDO);
GetIODO(18, ioLevel);
// 出力値を反転
ioLevel := (ioLevel + 1) mod 2;
SetIODO(18, ioLevel);
end;
end;
procedure TForm4.Button11Click(Sender: TObject);
// GetIODI
var
value: Byte;
begin
SetIOMultiplexing(20, TIOFunction.IOFunctionDI);
GetIODI(20, value);
Memo1.Lines.Add('Value=' + value.ToString);
end;
procedure TForm4.Button12Click(Sender: TObject);
// ArcCmd (未検証)
var
checkSum : Byte;
i : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
arcx, arcy, arcz, arcr, tox, toy, toz, tor : single;
begin
arcx := 200;
arcy := 0;
arcz := 70;
arcr := 0;
tox := 230;
toy := -100;
toz := 70;
tor := 0;
if ApdComport1.Open then begin
SetLength(B, 38);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 32; // len= 2 + Payload Len;
B[3] := 101; // id;
B[4] := 3; // ctrl r/w = W, isQueued = True;
// X
for i := 0 to 3 do B[5 + i] := arcx.Bytes[i];
// Y
for i := 0 to 3 do B[9 + i] := arcy.Bytes[i];
// Z
for i := 0 to 3 do B[13 + i] := arcz.Bytes[i];
// R
for i := 0 to 3 do B[17 + i] := arcr.Bytes[i];
// X
for i := 0 to 3 do B[21 + i] := tox.Bytes[i];
// Y
for i := 0 to 3 do B[25 + i] := toy.Bytes[i];
// Z
for i := 0 to 3 do B[29 + i] := toz.Bytes[i];
// R
for i := 0 to 3 do B[33 + i] := tor.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to 36 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[37] := checkSum;
InitResParams;
ApdComPort1.PutBlock(B[0], 38);
waitResEnd(500);
if (Length(resBuf) >= 14) and (resBuf[3] = 101) then begin
queuedCmdIndex :=
resBuf[5+0] or
(resBuf[5+1] shl 8) or
(resBuf[5+2] shl 16) or
(resBuf[5+3] shl 24) or
(UInt64(resBuf[5+4]) shl 32) or
(UInt64(resBuf[5+5]) shl 40) or
(UInt64(resBuf[5+6]) shl 48) or
(UInt64(resBuf[5+7]) shl 56);
executedCmdIndex := 0;
while (executedCmdIndex < queuedCmdIndex) do begin
if getPose(x, y, z, r, a0, a1, a2, a3) then begin
Memo1.Lines.Add(Format('X= %.2f, Y= %.2f, Z= %.2f, R= %.2f', [x, y, z, r]));
GetQueuedCmdCurrentIndex(executedCmdIndex);
end;
end;
if getPose(x, y, z, r, a0, a1, a2, a3) then
Memo1.Lines.Add(Format('X= %.2f, Y= %.2f, Z= %.2f, R= %.2f', [x, y, z, r]));
Memo1.Lines.Add('>>>> ArcCmd Finished <<<<');
end;
end;
end;
procedure TForm4.Button1Click(Sender: TObject);
// Dobot Open
begin
ApdComport1.ComNumber := 29;
ApdComport1.Baud := 115200;
ApdComport1.Open := True;
end;
procedure TForm4.Button2Click(Sender: TObject);
// Dobot Close
begin
ApdComport1.Open := False;
end;
procedure TForm4.Button3Click(Sender: TObject);
// HomeCmd ホーミング
var
checkSum : Byte;
i : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
if ApdComport1.Open then begin
SetLength(B, 10);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 4; // len= 2 + Payload Len;
B[3] := 31; // id;
B[4] := 3; // ctrl r/w = W, isQueued = True;
// ダミー(4バイト)
for i := 0 to 3 do B[5 + i] := 0;
// チェックサム
checkSum := 0;
for i := 3 to 8 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[9] := checkSum;
initResParams;
ApdComPort1.PutBlock(B[0], 10);
waitResEnd(500);
if (Length(resBuf) >= 14) and (resBuf[3] = 31) then begin
queuedCmdIndex :=
resBuf[5+0] or
(resBuf[5+1] shl 8) or
(resBuf[5+2] shl 16) or
(resBuf[5+3] shl 24) or
(UInt64(resBuf[5+4]) shl 32) or
(UInt64(resBuf[5+5]) shl 40) or
(UInt64(resBuf[5+6]) shl 48) or
(UInt64(resBuf[5+7]) shl 56);
executedCmdIndex := 0;
while (executedCmdIndex < queuedCmdIndex) do begin
getPose(x, y, z, r, a0, a1, a2, a3);
Memo1.Lines.Add(Format('X= %.2f, Y= %.2f, Z= %.2f, R= %.2f', [x, y, z, r]));
GetQueuedCmdCurrentIndex(executedCmdIndex);
end;
getPose(x, y, z, r, a0, a1, a2, a3);
Memo1.Lines.Add(Format('X= %.2f, Y= %.2f, Z= %.2f, R= %.2f', [x, y, z, r]));
Memo1.Lines.Add('>>>> Homing Finished <<<<');
end;
end;
end;
procedure TForm4.Button4Click(Sender: TObject);
// PTPCmd 2点間直線移動
var
checkSum : Byte;
i : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
x := 230;
y := 0;
z := 70;
r := 0;
if ApdComport1.Open then begin
SetLength(B, 23);
B[0] := $AA;
B[1] := $AA;
B[2] := 2 + 17; // len= 2 + Payload Len;
B[3] := 84; // id;
B[4] := 3; // ctrl r/w = W, isQueued = True;
B[5] := 2; // PTPMode 2= MOVL_XYZ
// X
for i := 0 to 3 do B[6 + i] := x.Bytes[i];
// Y
for i := 0 to 3 do B[10 + i] := y.Bytes[i];
// Z
for i := 0 to 3 do B[14 + i] := z.Bytes[i];
// R
for i := 0 to 3 do B[18 + i] := r.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to 21 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[22] := checkSum;
InitResParams;
ApdComPort1.PutBlock(B[0], 23);
waitResEnd(500);
if (Length(resBuf) >= 14) and (resBuf[3] = 84) then begin
queuedCmdIndex :=
resBuf[5+0] or
(resBuf[5+1] shl 8) or
(resBuf[5+2] shl 16) or
(resBuf[5+3] shl 24) or
(UInt64(resBuf[5+4]) shl 32) or
(UInt64(resBuf[5+5]) shl 40) or
(UInt64(resBuf[5+6]) shl 48) or
(UInt64(resBuf[5+7]) shl 56);
executedCmdIndex := 0;
while (executedCmdIndex < queuedCmdIndex) do begin
if getPose(x, y, z, r, a0, a1, a2, a3) then begin
Memo1.Lines.Add(Format('X= %.2f, Y= %.2f, Z= %.2f, R= %.2f', [x, y, z, r]));
GetQueuedCmdCurrentIndex(executedCmdIndex);
end;
end;
if getPose(x, y, z, r, a0, a1, a2, a3) then
Memo1.Lines.Add(Format('X= %.2f, Y= %.2f, Z= %.2f, R= %.2f', [x, y, z, r]));
Memo1.Lines.Add('>>>> PTPcmd Finished <<<<');
end;
end;
end;
procedure TForm4.Button5Click(Sender: TObject);
// GetPose
var
x, y, z, r : single;
a0, a1, a2, a3 : single;
begin
if GetPose(x, y, z, r, a0, a1, a2, a3) then begin
Memo1.Lines.Add(Format('X= %.2f, Y= %.2f, Z= %.2f, R= %.2f', [x, y, z, r]));
Memo1.Lines.Add(Format('A0= %.2f, A1= %.2f, A2= %.2f, A3= %.2f', [a0, a1, a2, a3]));
end;
end;
procedure TForm4.Button6Click(Sender: TObject);
// GetQueuedCmdCurrentIndex
var
queuedCmdCurrentIndex : Uint64;
begin
queuedCmdCurrentIndex := 0;
GetQueuedCmdCurrentIndex(queuedCmdCurrentIndex);
Memo1.Lines.Add('CurrentIndex=' + queuedCmdCurrentIndex.ToString);
end;
procedure TForm4.Button7Click(Sender: TObject);
// Clear All Alarms State
begin
ClearAllAlarmsState;
end;
procedure TForm4.Button8Click(Sender: TObject);
// SetEndEffectorSuctionCup
var
ctrl, suck : Byte;
begin
GetEndEffectorSuctionCup(ctrl, suck);
suck := (suck + 1) mod 2;
SetEndEffectorSuctionCup(1, suck);
end;
procedure TForm4.Button9Click(Sender: TObject);
// SetQueuedCmdForceStopExec,
begin
if SetQueuedCmdForceStopExec then begin
ApdComport1.Open := False;
ShowMessage('Dobot をリセットしてください.' + #13 + 'リセット完了後、再接続してください.');
end;
end;
end.