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.