Dobot Bluetooth UART 通信 Delphi 2020/11/13 ~ 2020/11/17
・2020/11/16 Android のスクリーンショットを追加
・2020/11/17 Android のコードを最後に追加
Dobot 純正の Bluetooth アダプタを使わずに、市販の Bluetooth モジュールを使っています。
Windows、Android 端末からワイヤレスで Dobot をコントロールできます。
※ iPhone (iOS) は Classic Bluetooth には対応していないため、使用できません。
送受信しているバイト配列のデータは、UART、Wi-Fi(UDP) でも同じ内容です。
Bluetooth モジュール RBT-001、レベル変換基板 80FG990 はマイクロテクニカで購入しました。
端末から見て受信側が安定しないので、バッファ IC 74HC4050 を追加しています。
接続図はこちらです。
ペアリング前にユーティリティソフトウェア(Simply Blue Commander)で、下記を設定します。
- RBT-001 Hardware Commands
・Set Event Filter: No events reported, no UART break (full cable replacement)
・Set Local Name:RBT-001 ※デフォルトは、"EasyBT"
・Change UART speed: 19200

下記のサンプルコードのターゲットは、Windows(VCL)です。
Android の場合は、少し遅い感じ。
画面の更新が Windows とは異なるようで、そのままのコードではHoming 中のリアルタイムの座標は表示されませんでした。
Android 用のコードを、Windows の次(最後)に追加しました。 2020/11/17

{
Classic Bluetooth で Dobot Magician を使う (Windows)
Dobot 背面のインターフェースコネクタ 10P のシリアル(TX, RX, GND)に、
Bluetooth モジュール RBT-001 を接続する
使用コネクタは「ケーブル圧接型ソケット10P FC-10P」、
コネクタ付きケーブルの場合は「フラットリボンケーブル FC/FCコネクター 10ピン」で検索する
レベル変換基板と Dobot との接続
TX - TX, RX - RX, GND - GND
TX, RX とも間にバッファ IC (74HC4050 等) を入れると、受信側が安定する
Windows、Android 端末 (iOS(iPhone)では使用不可)でペアリング前に、
ユーティリティソフトウェア(Simply Blue Commander)で、下記を設定
- RBT-001 Hardware Commands
・Set Event Filter: No events reported, no UART break (full cable replacement)
・Set Local Name:RBT-001
・Change UART speed: 19200
2020/11/12 f.izawa
URL: http://www.izawa-web.com/
e-mail : f.izawa@dream.com
参照元:Dobot Magician Communication Protocol
}
unit Unit8;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Bluetooth, Vcl.StdCtrls,
System.Bluetooth.Components, Vcl.Buttons, Vcl.ExtCtrls, math;
type
TPTPMode = (
JUMP_XYZ,
MOVJ_XYZ,
MOVL_XYZ,
JUMP_ANGLE,
MOVJ_ANGLE,
MOVL_ANGLE,
MOVJ_INC,
MOVL_INC
);
TJOGMode = (
IDEL, // Invalid status
AP_DOWN, // X+ / Joint1+
AN_DOWN, // X- / Joint1-
BP_DOWN, // Y+ / Joint2+
BN_DOWN, // Y- / Joint2-
CP_DOWN, // Z+ / Joint3+
CN_DOWN, // Z- / Joint3-
DP_DOWN, // R+ / Joint4+
DN_DOWN, // R- / Joint4-
LP_DOWN, // L+. Only when the parameter isJoint=1, the LP_DOWN is available
LN_DOWN // L-. Only when the parameter isJoint=1, the LN_DOWN is available
);
TJOGModel = (
COORDINATE_MODEL,
JOINT_MODEL
);
TIOFunction = (
IOFunctionDummy, // Do not config function
IOFunctionPWM, // PWM Output
IOFunctionDO, // IO Output
IOFunctionDI, // IO Intput
IOFunctionADC // AD Input
);
type
TForm8 = class(TForm)
Bluetooth1: TBluetooth;
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
SpeedButton3: TSpeedButton;
SpeedButton4: TSpeedButton;
SpeedButton5: TSpeedButton;
SpeedButton6: TSpeedButton;
SpeedButton7: TSpeedButton;
SpeedButton8: TSpeedButton;
Button8: TButton;
Button9: TButton;
Button10: TButton;
Edit1: TEdit;
Edit2: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Edit3: TEdit;
Label4: TLabel;
Edit4: TEdit;
Button11: TButton;
Label5: TLabel;
Label6: TLabel;
Edit5: TEdit;
Edit6: TEdit;
Button12: TButton;
Button13: TButton;
Timer1: TTimer;
Edit7: TEdit;
Edit8: TEdit;
Edit9: TEdit;
Edit10: TEdit;
Edit11: TEdit;
Edit12: TEdit;
Edit13: TEdit;
Edit14: TEdit;
Edit15: TEdit;
Edit16: TEdit;
Edit17: TEdit;
Edit18: TEdit;
Edit19: TEdit;
Edit20: TEdit;
Edit21: TEdit;
Button14: TButton;
Edit22: TEdit;
Button15: TButton;
Button16: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure SpeedButton1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure SpeedButton1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Button7Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure Button9Click(Sender: TObject);
procedure Button10Click(Sender: TObject);
procedure Button11Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button12Click(Sender: TObject);
procedure Button13Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Button14Click(Sender: TObject);
procedure Button15Click(Sender: TObject);
procedure Button16Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form8: TForm8;
ADevice : TBluetoothDevice;
ASocket : TBluetoothSocket;
AlarmCode : array of string = [
'$00', 'ERR_COMMON_RESET',
'$10', 'ERR_PLAN_INV_SINGULARITY',
'$11', 'ERR_PLAN_INV_CALC',
'$12', 'ERR_PLAN_INV_LIMIT',
'$13', 'ERR_PLAN_PUSH_DATA_REPEAT',
'$14', 'ERR_PLAN_ARC_INPUT_PARAM',
'$15', 'ERR_PLAN_JUMP_PARAM',
'$20', 'ERR_MOVE_INV_SINGULARITY',
'$21', 'ERR_MOVE_INV_CALC',
'$22', 'ERR_MOVE_INV_LIMIT',
'$30', 'ERR_OVERSPEED_AXIS1',
'$31', 'ERR_OVERSPEED_AXIS2',
'$32', 'ERR_OVERSPEED_AXIS3',
'$33', 'ERR_OVERSPEED_AXIS4',
'$40', 'ERR_LIMIT_AXIS1_POS',
'$41', 'ERR_LIMIT_AXIS1_NEG',
'$42', 'ERR_LIMIT_AXIS2_POS',
'$43', 'ERR_LIMIT_AXIS2_NEG',
'$44', 'ERR_LIMIT_AXIS3_POS',
'$45', 'ERR_LIMIT_AXIS3_NEG',
'$46', 'ERR_LIMIT_AXIS4_POS',
'$47', 'ERR_LIMIT_AXIS4_NEG',
'$48', 'ERR_LIMIT_AXIS23_POS',
'$49', 'ERR_LIMIT_AXIS23_NEG',
'$50', 'ERR_LOSE_STEP_AXIS1',
'$51', 'ERR_LOSE_STEP_AXIS2',
'$52', 'ERR_LOSE_STEP_AXIS3',
'$53', 'ERR_LOSE_STEP_AXIS4' ];
runFlag : boolean; // 非常停止通知、検出用
tmBusy : boolean; // インターバルタイマーで処理中
const
// SPP(Serial Port Profile) による通信のUUID
ServiceUUID = '{00001101-0000-1000-8000-00805F9B34FB}';
// 検索する BT デバイス名
// デフォルト 'EasyBT' を 'RBT-001' に変更している
BTDeviceHead = 'RBT-001';
// プロトタイプ
function RBTReceiveData(ASocket: TBluetoothSocket; var readData: TBytes; ATimeout: Cardinal): integer;
function GetPose(ASocket: TBluetoothSocket; var x, y, z, r: single; var a0, a1, a2, a3: single): boolean;
implementation
{$R *.dfm}
function SetIOMultiplexing(ASocket: TBluetoothSocket; ioAddress: Byte; ioFunction: Byte): boolean;
// 多重入出力を設定
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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] := ioFunction; // TIOFunction,
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 130);
end;
end;
function SetPTPCommonParams(ASocket: TBluetoothSocket;
velocityRatio, accelerationRatio: single): boolean;
// PTP 移動 速度係数、加速度係数設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 8; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 83; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[5 + i] := velocityRatio.Bytes[i];
for i := 0 to 3 do B[9 + i] := accelerationRatio.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 83);
end;
end;
function GetIODI(ASocket: TBluetoothSocket; ioAddress: Byte; var ioValue: Byte):boolean;
// GetIODI 入力状態を取得
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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 len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 133) and (B[5] = ioAddress);
if result then
ioValue := B[6];
end;
end;
function GetIODO(ASocket: TBluetoothSocket; ioAddress: Byte; var ioLevel: Byte): boolean;
// 出力状態を取得
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, 8);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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 len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 131) and (B[5] = ioAddress);
if result then
ioLevel := B[6];
end;
end;
function SetIODO(ASocket: TBluetoothSocket; ioAddress: Byte; ioLevel: Byte): boolean;
// 出力
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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 len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 131);
end;
end;
function GetPTPCommonParams(ASocket: TBluetoothSocket;
var velocityRatio, accelerationRatio: single): boolean;
// PTP 移動 速度係数、加速度係数取得
var
B : TBytes;
len, i : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 83; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $AD;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 83);
if result then begin
for i := 0 to 3 do begin
velocityRatio.Bytes[i] := B[5 + i];
accelerationRatio.Bytes[i] := B[9 + i];
end;
end;
end;
end;
function SetJOGCommonParams(ASocket: TBluetoothSocket;
velocityRatio, accelerationRatio: single): boolean;
// JOG 移動 速度係数、加速度係数設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 8; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 72; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[ 5 + i] := velocityRatio.Bytes[i];
for i := 0 to 3 do B[ 9 + i] := accelerationRatio.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 72);
end;
end;
function GetJOGCommonParams(ASocket: TBluetoothSocket;
var velocityRatio, accelerationRatio: single): boolean;
// PTP 移動 速度係数、加速度係数取得
var
B : TBytes;
len, i : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 72; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $B8;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 72);
if result then begin
for i := 0 to 3 do begin
velocityRatio.Bytes[i] := B[5 + i];
accelerationRatio.Bytes[i] := B[9 + i];
end;
end;
end;
end;
function SetSetPTPCoordinateParams(ASocket: TBluetoothSocket;
xyzVelocity, rVelocity, xyzAcceleration, rAcceleration: single): boolean;
// PTP LINE 移動 速度、加速度設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 16; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 81; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[ 5 + i] := xyzVelocity.Bytes[i];
for i := 0 to 3 do B[ 9 + i] := rVelocity.Bytes[i];
for i := 0 to 3 do B[13 + i] := xyzAcceleration.Bytes[i];
for i := 0 to 3 do B[17 + i] := rAcceleration.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
// チェックサム
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 81);
end;
end;
function GetPTPCoordinateParams(ASocket: TBluetoothSocket;
var xyzVelocity, rVelocity, xyzAcceleration, rAcceleration: single): boolean;
var
i, len : integer;
B : TBytes;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 81; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $AF;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 81);
if result then begin
for i := 0 to 3 do begin
xyzVelocity.Bytes[i] := B[ 5 + i];
rVelocity.Bytes[i] := B[ 9 + i];
xyzAcceleration.Bytes[i] := B[13 + i];
rAcceleration.Bytes[i] := B[17 + i];
end;
end;
end;
end;
function SetJOGCoordinateParams(ASocket: TBluetoothSocket;
velocity, acceleration: array of single): boolean;
// Jog移動 X, Y, Z R 軸の速度と加速度
var
B : TBytes;
len, i, j : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 32; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 71; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for j := 0 to 3 do begin
for i := 0 to 3 do begin
B[ 5 + j * 4 + i] := velocity[j].Bytes[i]; // X,Y, Z, R axis
B[21 + j * 4 + i] := acceleration[j].Bytes[i];
end;
end;
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
// チェックサム
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 71);
end;
end;
function GetJOGCoordinateParams(ASocket: TBluetoothSocket;
var velocity: array of single; var acceleration: array of single): boolean;
var
i, j, len : integer;
B : TBytes;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 71; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $B9;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 71);
if result then begin
for j := 0 to 3 do begin
for i := 0 to 3 do begin
velocity[j].Bytes[i] := B[ 5 + j * 4 + i];
acceleration[j].Bytes[i] := B[21 + j * 4 + i];
end;
end;
end;
end;
end;
function SetQueuedCmdClear(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 245; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0A;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 245);
end;
end;
function SetQueuedCmdStartExec(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len +4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 240; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0F;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 240);
end;
end;
function SetDeviceName(ASocket: TBluetoothSocket; const deviceName: string): boolean;
// デバイス名をセット
var
B, temp : TBytes;
len, i, n : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
temp := TEncoding.ANSI.GetBytes(deviceName);
n := Length(temp);
len := 2 + n; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 1; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to n - 1 do B[5 + i] := temp[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
// チェックサム
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 1);
end;
end;
function GetDeviceName(ASocket: TBluetoothSocket; var deviceName: string): boolean;
var
B : TBytes;
len : integer;
i : Integer;
begin
result := false;
deviceName := '';
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 1; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $FF;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 1);
if result then begin
for i := 5 to len - 2 do
deviceName := deviceName + Char(B[i]);
end;
end;
end;
function GetDeviceSN(ASocket: TBluetoothSocket; var deviceSN: string): boolean;
var
B : TBytes;
len : integer;
i : Integer;
begin
result := false;
deviceSN := '';
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 0; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $00;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 0);
if result then begin
for i := 5 to len - 2 do
deviceSN := deviceSN + Char(B[i]);
end;
end;
end;
function GetAlarmsState(ASocket: TBluetoothSocket;
var isAlarm: boolean; var res: string): boolean;
// アラーム取得
var
alarmsState: array [0..15] of Byte;
B : TBytes;
len : integer;
i, j, idx, k : Integer;
begin
result := false;
isAlarm := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 20; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $EC;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 20);
if result then begin
for i := 0 to 15 do
alarmsState[i] := B[5 + i];
for i := 1 to 15 do begin // alarmsState[0] は無視
if alarmsState[i] > 0 then begin
isAlarm := true;
break;
end;
end;
end;
end;
// アラーム内容(文字列)を作成
if isAlarm then begin
for i := 1 to 15 do begin // alarmsState[0] は無視
for j := 0 to 7 do begin
if (alarmsState[i] and (1 shl j)) > 0 then begin
idx := i * 8 + j;
res := '';
for k := 0 to Length(AlarmCode) div 2 - 1 do begin
if idx = StrToInt(AlarmCode[k * 2]) then
res := res + AlarmCode[k * 2 + 1] + #09; // TAB を追加
end;
res := Trim(res); // 最後の #09 を削除
end;
end;
end;
end;
end;
function ClearAllAlarmsState(ASocket: TBluetoothSocket): boolean;
// アラームクリア
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 20; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $EB;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 20);
end;
end;
function SetQueuedCmdStopExec(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 241; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0E;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 241);
end;
end;
function SetQueuedCmdForceStopExec(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 242; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0D;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 242);
end;
end;
function GetQueuedCmdCurrentIndex(ASocket: TBluetoothSocket;
var queuedCmdCurrentIndex: Uint64): boolean;
// コマンドキューの現在の位置
var
B : TBytes;
len, i : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 246; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $0A;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 2000);
result := (len = B[2] + 4) and (B[3] = 246);
if result then begin
queuedCmdCurrentIndex := 0;
for i := 0 to 7 do
queuedCmdCurrentIndex :=
queuedCmdCurrentIndex or (Uint64(B[5 + i]) shl (i * 8));
end;
end;
end;
function SetHomeParams(ASocket: TBluetoothSocket; x, y, z, r: single): boolean;
// HOME 位置設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 16; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 30; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[ 5 + i] := x.Bytes[i];
for i := 0 to 3 do B[ 9 + i] := y.Bytes[i];
for i := 0 to 3 do B[13 + i] := z.Bytes[i];
for i := 0 to 3 do B[17 + i] := r.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 30);
end;
end;
function GetHomeParams(ASocket: TBluetoothSocket; var x, y, z, r: single): boolean;
// ホーム座標を取得
var
B : TBytes;
len, i : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 30; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $E2;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 30);
if result then begin
for i := 0 to 3 do begin
x.Bytes[i] := B[ 5 + i];
y.Bytes[i] := B[ 9 + i];
z.Bytes[i] := B[13 + i];
r.Bytes[i] := B[17 + i];
end;
end;
end;
end;
function SetHomeCmd(ASocket: TBluetoothSocket): boolean;
// HomeCmd ホーミング
var
i, len : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
result := false;
if runFlag and (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 4; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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;
// チェックサム
B[9] := $DE;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 31);
if result then begin
queuedCmdIndex := 0;
for i := 0 to 7 do
queuedCmdIndex := queuedCmdIndex or (Uint64(B[5 + i]) shl (i * 8));
executedCmdIndex := 0;
while result and (executedCmdIndex < queuedCmdIndex) do begin
Application.ProcessMessages;
if runFlag then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
Form8.Memo1.Lines.Add(Format('X = %.3f, Y = %.3f, Z = %.3f', [x, y, z]));
if result then
result := GetQueuedCmdCurrentIndex(ASocket, executedCmdIndex);
end
else begin
result := false;
break;
end;
end;
if result then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
Form8.Memo1.Lines.Add(Format('x = %.3f, y = %.3f, z = %.3f', [x, y, z]));
end;
end;
end;
end;
function SetPTPCmd(ASocket: TBluetoothSocket; ptpMode: Byte; toX, toY, toZ, toR: single): boolean;
// PTPCmd
var
checkSum : Byte;
i, len : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 17; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 84; // id;
B[4] := 3; // ctrl r/w = W, isQueued = True;
B[5] := ptpMode;// PTPMode 2= MOVL_XYZ
for i := 0 to 3 do B[ 6 + i] := toX.Bytes[i]; // X
for i := 0 to 3 do B[10 + i] := toY.Bytes[i]; // Y
for i := 0 to 3 do B[14 + i] := toZ.Bytes[i]; // Z
for i := 0 to 3 do B[18 + i] := toR.Bytes[i]; // R
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 84);
if result then begin
queuedCmdIndex := 0;
for i := 0 to 7 do
queuedCmdIndex := queuedCmdIndex or (Uint64(B[5 + i]) shl (i * 8));
executedCmdIndex := 0;
while result and (executedCmdIndex < queuedCmdIndex) do begin
Application.ProcessMessages;
if runFlag then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
Form8.Memo1.Lines.Add(Format('x = %.3f, Y = %.3f, Z = %.3f', [x, y, z]));
if result then
result := GetQueuedCmdCurrentIndex(ASocket, executedCmdIndex);
end
else begin
result := false;
break;
end;
end;
if result then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
Form8.Memo1.Lines.Add(Format('x = %.3f, Y = %.3f, Z = %.3f', [x, y, z]));
end;
end;
end;
end;
function SetJOGCmd(ASocket: TBluetoothSocket; jogMode: Byte; jogCommand: Byte): boolean;
// JogCmd
// jogMode 0 で X, Y, Z 方向に動く。方向は、 jogCommand で指定
// jog 開始は、方向を指定。停止するまで動き続ける
// Jog 停止は、jogCommand に 0 を指定。
// スピードが速すぎると、使いにくい
var
checkSum : Byte;
i, len : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 73; // id;
B[4] := 3; // ctrl r/w = W, isQueued = True;
B[5] := jogMode; // Jog mode 0-coordinate jog 1-Joint jog
B[6] := jogCommand; // Jog command(Value range0..8)
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 73);
if result then begin
queuedCmdIndex := 0;
for i := 0 to 7 do
queuedCmdIndex := queuedCmdIndex or (Uint64(B[5 + i]) shl (i * 8));
executedCmdIndex := 0;
while result and (executedCmdIndex < queuedCmdIndex) do begin
Application.ProcessMessages;
if runFlag then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
Form8.Memo1.Lines.Add(Format('X = %.3f, Y = %.3f, Z = %.3f', [x, y, z]));
if result then
result := GetQueuedCmdCurrentIndex(ASocket, executedCmdIndex);
end
else begin
result := false;
break;
end;
end;
if result then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
Form8.Memo1.Lines.Add(Format('x = %.3f, y = %.3f, z = %.3f', [x, y, z]));
end;
end;
end;
end;
function GetEndEffectorSuctionCup(ASocket: TBluetoothSocket;
var isCtrlEnable: Byte; var isSucked: Byte): boolean;
// 吸引カップの状態を取得
var
B : TBytes;
len : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 62; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
B[5] := $C2; // checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 62);
if result then begin
isCtrlEnable := B[5];
isSucked := B[6];
end;
end;
end;
function SetEndEffectorSuctionCup(ASocket: TBluetoothSocket; ctrl, suck: Byte):boolean;
// 吸引カップ ON/OFF
var
B : TBytes;
len, i : integer;
checkSum : Byte;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 62; // id;
B[4] := 3; // ctrl r/w($01) = W, isQueued($02) = True;
B[5] := ctrl; // CtrlEnable
B[6] := suck; // Suck
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 62);
end;
end;
function GetEndEffectorGripper(ASocket: TBluetoothSocket;
var isCtrlEnable: Byte; var isGripped: Byte): boolean;
// グリッパーの状態を取得
var
B : TBytes;
len : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 63; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
B[5] := $C1; // checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 63);
if result then begin
isCtrlEnable := B[5];
isGripped := B[6];
end;
end;
end;
function SetEndEffectorGripper(ASocket: TBluetoothSocket; ctrl, grip: Byte):boolean;
// 吸引カップ ON/OFF
var
B : TBytes;
len, i : integer;
checkSum : Byte;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 63; // id;
B[4] := 3; // ctrl r/w($01) = W, isQueued($02) = True;
B[5] := ctrl; // CtrlEnabled
B[6] := grip; // Grip
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 63);
end;
end;
function GetPose(ASocket: TBluetoothSocket;
var x, y, z, r: single; var a0, a1, a2, a3: single): boolean;
// GetPose 現在値を取得
var
i, len : integer;
B : TBytes;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 10; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $F6;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 1000);
result := (len = B[2] + 4) and (B[3] = 10);
if result then begin
for i := 0 to 3 do begin
x.Bytes[i] := B[ 5 + i];
y.Bytes[i] := B[ 9 + i];
z.Bytes[i] := B[13 + i];
r.Bytes[i] := B[17 + i];
a0.Bytes[i] := B[21 + i];
a1.Bytes[i] := B[25 + i];
a2.Bytes[i] := B[29 + i];
a3.Bytes[i] := B[33 + i];
end;
end;
end;
end;
// Dobot Magician アーム角度を取得
// x, y, z : エンドエフェクタの位置
// A1, A2, A3 : 主軸の回転角度、アーム 1 の傾斜角度, アーム 2 の角度(アーム 1 からの傾斜角度)
// J1, J2, J3 : 主軸の回転角度、アーム 1 の角度(垂直で 0)アーム 2 の傾斜角度(水平で 0)
function dobotArmAngle(x, y, z: double; var A1, A2, A3, J1, J2, J3 : double): boolean;
// uses ,,,, math;
var
th1, th2, th3 : double;
phi, l, ld : double;
L1, L2, L3, L4 : double;
begin
L1 := 0.0; // アーム基点の高さ(Dobot Magician の場合: 0)
L2 := 135.0; // アーム1の辺の長さ
L3 := 146.74; // アーム2の辺の長さ(147.0mm)
L4 := 60.0; // エンドエフェクタの突き出し部分(必ず水平である)
result := false;
// X-Y平面上のアーム部分の長さ
l := sqrt(x * x + y * y) - L4 ;
if (l > L4) then begin
// X-Y 平面上のアームの角度
// Dobot の場合、-125~ +125 度であること
th1 := arctan2(y, x);
// アーム1、2の底辺の長さ
ld := sqrt(l * l + (z - L1) * (z - L1));
result := (ld > 0) and (ld < (L2 + L3));
if result then begin
// アーム基点から先端への仰角
// アーム1、アーム2底辺の三角形の基点側の内角
phi := arctan2((z - L1), l);
// アーム1の角度
th2 := phi + arccos((ld * ld + L2 * L2 - L3 * L3) / (2.0 * ld * L2));
// アーム2のアーム1からの角度
th3 := arcsin((ld * ld - L2 * L2 - L3 * L3) / (2.0 * L2 * L3)) + PI / 2.0;
// ラジアン->デグリ換算
A1 := th1 * 180.0 / PI;
A2 := th2 * 180.0 / PI ;
A3 := th3 * 180.0 / PI ;
// Dobot Arm の角度表示に変換
J1 := A1;
// アーム1の角度(垂直で 0 度、前に傾斜で+方向)
// Dobot の場合 -5.0 ~ +84.0 まで
J2 := 90.0 - A2;
// アーム2の角度(水平で 0 度、前方に下がる方向が+)
// Dobot の場合、 -15.0 ~ +75.0 まで
J3 := 180.0 - A2 - A3;
end;
end;
end;
// 可動範囲内にあるか
function dobotCheckXYZ(x, y, z : double): boolean;
var
A1, A2, A3, J1, J2, J3 : double;
dist1, dist2 : double;
begin
result := false;
if dobotArmAngle(x, y, z, A1, A2, A3, J1, J2, J3) then begin
dist1 := sqrt(x * x + y * y);
dist2 := sqrt((dist1 - 60.0) * (dist1 - 60.0) + z * z);
// (J2 < 89.00) : 大きくすると、下側に(初期値:83.99)
// (J3 < 75.00) : 大きくすると、中心方向に(初期値:75.00)
result := (J1 > -125.0 ) and (J1 < 125.0) and
(J2 > -4.99) and (J2 < 89.5) and
(J3 > -14.9 ) and (J3 < 89.5) and
(dist2 <= 268);
// 268 はアームが最大に伸びた時の長さ(268 ~ 272mm)一直線にはならないため、制限が必要
end;
end;
function dobotSetupIO(ASocket: TBluetoothSocket): boolean;
// IO セットアップ
begin
result := false;
if (ASocket = nil) or not ASocket.Connected then exit;
SetIOMultiplexing(ASocket, 18, Ord(TioFunction.IOFunctionDO));
SetIOMultiplexing(ASocket, 19, Ord(TioFunction.IOFunctionDI));
SetIOMultiplexing(ASocket, 20, Ord(TioFunction.IOFunctionDI));
result := true;
end;
function searchRBT(const deviceHead : string): boolean;
// Bluetooth デバイスを検索
var
ABluetoothManager : TBluetoothManager;
APairedDevices : TBluetoothDeviceList;
ADevice : TBluetoothDevice;
idx, i : integer;
begin
result := false;
if (ASocket = nil) or not ASocket.Connected then begin
try
ABluetoothManager := TBluetoothManager.Current;
if ABluetoothManager.ConnectionState = TBluetoothConnectionState.Connected then begin
// 過去にペアリングされたデバイスの一覧から、ターゲット を探す
APairedDevices := ABluetoothManager.GetPairedDevices;
if APairedDevices.Count > 0 then begin
idx := -1;
for i := 0 to APairedDevices.Count -1 do begin
if (BTDeviceHead = APairedDevices[i].DeviceName) then begin
idx := i;
break; // リストアップを終了
end;
end;
if idx >= 0 then begin
ADevice := APairedDevices[idx];
if ADevice <> nil then begin
ASocket := ADevice.CreateClientSocket(StringToGUID(ServiceUUID), False);
if ASocket <> nil then begin
// 接続
ASocket.Connect;
result := ASocket.Connected;
end;
end;
end;
end;
end;
except
on E : Exception do begin
ShowMessage(E.Message);
end;
end;
end;
end;
function RBTReceiveData(ASocket: TBluetoothSocket;
var readData: TBytes; ATimeout: Cardinal): integer;
// Dobot(Bluetooth) からの応答
var
AData : TBytes;
i : integer;
Ticks : Cardinal;
idx : integer;
loop : boolean;
cnt : integer;
begin
cnt := 0; idx := 0;
Setlength(readData, 64); // 大き目に用意
Ticks := TThread.GetTickCount;
loop := True;
while loop and (cnt < 500) do begin
//Sleep(1);
AData := ASocket.ReceiveData;
if Length(AData) > 0 then begin
for i := 0 to Length(AData) - 1 do begin
readData[idx] := AData[i];
// 受信バッファの長さを再セット
if idx = 2 then SetLength(readData, readData[2] + 4);
Inc(idx);
if idx >= Length(readData) then begin
loop := False;
break;
end;
end;
end;
Inc(cnt);
if loop then
loop := TThread.GetTickCount - Ticks < ATimeout;
end;
result := idx;
end;
procedure TForm8.Button10Click(Sender: TObject);
// Jog 移動の速度、加速度をセット
var
velocity : array [0..3] of single;
acceleration : array [0..3] of single;
i : integer;
begin
for i:= 0 to 3 do begin
velocity[i] := StrToFloatDef(Edit1.Text, 100);
acceleration[i] := StrToFloatDef(Edit2.Text, 100);;
end;
if SetJOGCoordinateParams(ASocket, velocity, acceleration) then
Memo1.Lines.Add('OK');
end;
procedure TForm8.Button11Click(Sender: TObject);
// PTP LINE 移動、速度、加速度セット
var
xyzVelocity, rVelocity, xyzAcceleration, rAcceleration: single;
begin
xyzVelocity := StrToFloatDef(Edit3.Text, 100);
xyzAcceleration := StrToFloatDef(Edit4.Text, 100);
rVelocity := StrToFloatDef(Edit5.Text, 100);
rAcceleration := StrToFloatDef(Edit6.Text, 100);
if SetSetPTPCoordinateParams(
ASocket, xyzVelocity, rVelocity, xyzAcceleration, rAcceleration) then
Memo1.Lines.Add('Ok');
end;
procedure TForm8.Button12Click(Sender: TObject);
// PTP CommonParams
// 速度係数、加速度係数を取得
// 数値はパーセント
var
velocityRatio : single;
accelerationRatio : single;
begin
if GetPTPCommonParams(ASocket, velocityRatio, accelerationRatio) then
Memo1.Lines.Add(Format('velocityRatio = %.3f, accelerationRatio = %.3f',
[velocityRatio, accelerationRatio]));
end;
procedure TForm8.Button13Click(Sender: TObject);
// PTP CommonParams
// 速度係数、加速度係数をセット
// 数値はパーセント
var
velocityRatio : single;
accelerationRatio : single;
begin
velocityRatio := 120.0;
accelerationRatio := 80.0;
if SetPTPCommonParams(ASocket, velocityRatio, accelerationRatio) then
Memo1.Lines.Add('Ok');
end;
procedure TForm8.Button14Click(Sender: TObject);
// HOME 位置セット
begin
if SetHomeParams(ASocket, 200, 0, 100, 0) then
Memo1.Lines.Add('Ok');
end;
procedure TForm8.Button15Click(Sender: TObject);
// Get ... テスト
var
x, y, z, r : single;
velocityRatio, accelerationRatio: single;
velocity, acceleration: array [0..3] of single;
xyzVelocity, rVelocity, xyzAcceleration, rAcceleration: single;
i : integer;
begin
if GetHomeParams(ASocket, x, y, z, r) then
Memo1.Lines.Add(Format('X = %.3f, Y = %.3f, Z = %.3f, R = %.3f' , [ x, y, z, r]));
if GetJOGCommonParams(ASocket, velocityRatio, accelerationRatio) then
Memo1.Lines.Add(Format('velocityRatio = %.3f, accelerationRatio = %.3f',
[velocityRatio, accelerationRatio]));
if SetJOGCommonParams(ASocket, velocityRatio, accelerationRatio) then
Memo1.Lines.Add('Ok');
if GetJOGCoordinateParams(ASocket, velocity, acceleration) then begin
for i := 0 to 3 do
Memo1.Lines.Add(Format('velocity[%d] = %.3f', [i, velocity[i]]));
for i := 0 to 3 do
Memo1.Lines.Add(Format('acceleration[%d] = %.3f', [i, acceleration[i]]));
end;
if GetPTPCoordinateParams(
ASocket, xyzVelocity, rVelocity, xyzAcceleration, rAcceleration) then begin
Memo1.Lines.Add(Format('xyzVelocity = %.3f, rVelocity = %.3f' ,
[xyzVelocity, rVelocity]));
Memo1.Lines.Add(Format('xyzAcceleration = %.3f, rAcceleration = %.3f' ,
[xyzAcceleration, rAcceleration]));
end;
end;
procedure TForm8.Button16Click(Sender: TObject);
// デバイス名セット
var
deviceName : string;
begin
deviceName := 'Dobot No.2';
if SetDeviceName(ASocket, deviceName) then
Memo1.Lines.Add('Ok');
end;
procedure TForm8.Button1Click(Sender: TObject);
// Bluetooth 検索、接続
begin
if searchRBT(BTDeviceHead) then begin
SetQueuedCmdClear(ASocket);
SetQueuedCmdStartExec(ASocket);
Memo1.Lines.Add('CONNECT');
dobotSetupIO(ASocket);
end;
end;
procedure TForm8.Button2Click(Sender: TObject);
// 吸引カップ 反転
// グリッパーの場合は、ctrl = 0 で OFF になる
// ctrl = 1 にし、suck で 開閉をコントロール可能
var
ctrl, suck: Byte;
begin
if GetEndEffectorSuctionCup(ASocket, ctrl, suck) then begin
SetEndEffectorSuctionCup(ASocket, 1, not suck and 1); // 反転
end;
end;
procedure TForm8.Button3Click(Sender: TObject);
// ホーミング
begin
runFlag := True; // 非常停止に対応するため
if SetHomeCmd(ASocket) then
Memo1.Lines.Add('HOME END');
end;
procedure TForm8.Button4Click(Sender: TObject);
// PTP 移動
var
pose, adeg : array [0..3] of single;
begin
// MOVL_XYZ = 直線移動
// 直線になるようにアームの角度を細かく補正しているので、MOVJ より遅くなる
// X,Y 位置を変えずに垂直移動する場合に使用
// MOVJ_XYZ = ジャンプ移動
// 直線補正をせずに最小限のアーム移動をする
// このため、Z 値のみの移動でも円弧動作になる
// MOVL よりかなり速い
// 通常の移動はこれを使用
// JUMP_XYZ = ピック&プレース移動
// 予め高さ制限を設定しておくと、始点-終点の指定のみで、
// ピック&プレース動作になる
// 速さは 同じ動作を MOVJ, MOVL で行った場合より若干速い
if GetPose(ASocket, pose[0], pose[1], pose[2], pose[3], adeg[0], adeg[1], adeg[2], adeg[3]) then begin
runFlag := True;
// 直線移動
SetPTPCmd(ASocket, Ord(TPTPMode.MOVL_XYZ), pose[0] + 20, pose[1] + 20, pose[2] + 20, pose[3]);
// ジャンプ移動
SetPTPCmd(ASocket, Ord(TPTPMode.MOVJ_XYZ), pose[0], pose[1], pose[2] , pose[3]);
end;
end;
procedure TForm8.Button5Click(Sender: TObject);
// アラームクリア
begin
ClearAllAlarmsState(ASocket);
end;
procedure TForm8.Button6Click(Sender: TObject);
// アラーム取得
var
isAlarm : boolean;
res : string;
begin
// アラームがある場合、res にその内容が TAB 区切りで列挙される
GetAlarmsState(ASocket, isAlarm, res);
if isAlarm then
// TAB をコンマに変換
Memo1.Lines.Add(StringReplace(res, #09, ', ', [rfReplaceAll]));
end;
procedure TForm8.Button7Click(Sender: TObject);
// デバイスシリアルと名前を取得
var
dobotSN, dobotName : string;
begin
if GetDeviceSN(ASocket, dobotSN) then
Memo1.Lines.Add('SN = ' + dobotSN);
if GetDeviceName(ASocket, dobotName) then
Memo1.Lines.Add('Name = ' + dobotName);
end;
procedure TForm8.Button8Click(Sender: TObject);
// コマンド停止
// 以降継続不能となるので、切断→再接続を行う
// 使い方は不明・・・
begin
if SetQueuedCmdStopExec(ASocket) then begin
Memo1.Lines.Add('SetQueuedCmdStopExec!!');
// 切断
ASocket.Close;
ASocket := nil;
// 接続
Button1Click(self);
end;
end;
procedure TForm8.Button9Click(Sender: TObject);
// 非常停止
// 以降継続不能となるので、切断→再接続を行う
// これ以上スマートな使い方は不明
begin
// 非常停止を通知
// 現状の対象は HOMEcmd、PTPcmd、JOGcmd のみ
runFlag := false;
Application.ProcessMessages;
if SetQueuedCmdForceStopExec(ASocket) then begin
Memo1.Lines.Add('SetQueuedCmdForceStopExec!!');
ASocket.Close; // 切断
ASocket := nil;
Button1Click(self); // 再接続
end;
end;
procedure TForm8.FormCreate(Sender: TObject);
begin
Memo1.Lines.Clear;
end;
procedure TForm8.SpeedButton1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
// Jog コマンド開始
// SpeedButton1 ~ 8 で共用
// SpeedButton1 ~ 8 が、X+. X-, Y+, Y-, Z+, Z-, R+, R-に対応
var
jogMode, jogCommand : Byte;
begin
jogMode := Ord(TJOGModel.COORDINATE_MODEL); // =0
jogCommand := StrToIntDef(Copy((Sender as TSpeedButton).Name, 12), 0);
if jogCommand > 0 then begin
runFlag := True;
SetJogCmd(ASocket, jogMode, jogCommand);
end;
end;
procedure TForm8.SpeedButton1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
// Jog コマンド終了(停止)
// SpeedButton1 ~ 8 で共用
var
jogMode, jogCommand : Byte;
begin
jogMode := Ord(TJOGModel.COORDINATE_MODEL); // =0
jogCommand := Ord(TJOGMode.IDEL); // =0
SetJogCmd(ASocket, jogMode, jogCommand);
end;
procedure TForm8.Timer1Timer(Sender: TObject);
// インターバル = 250 msec
var
pos, ang : array [0..3] of single;
i : integer;
edt : TEdit;
isAlarm : boolean;
res : string;
Ticks : Cardinal;
//ioValue: Byte;
begin
if not tmBusy then begin
tmBusy := true;
if (ASocket <> nil) and ASocket.Connected then begin
Ticks := TThread.GetTickCount;
// リアルタイムポーズ
if GetPose(ASocket, pos[0], pos[1], pos[2], pos[3], ang[0], ang[1], ang[2], ang[3]) then begin
for i := 0 to 3 do begin
edt := FindComponent('Edit' + IntToStr(7 + i)) as TEdit;
if (edt <> nil) and (edt.Text <> Format('%.3f', [pos[i]])) then
edt.Text := Format('%.3f', [pos[i]]);
edt := FindComponent('Edit' + IntToStr(11 + i)) as TEdit;
if (edt <> nil) and (edt.Text <> Format('%.3f', [ang[i]])) then
edt.Text := Format('%.3f', [ang[i]]);
end;
// X, Y, Z 座標から アーム角度を逆算
{
dobotArmAngle(pos[0], pos[1], pos[2], A[0], A[1], A[2], J[0], J[1], J[2]);
for i := 0 to 2 do begin
edt := FindComponent('Edit' + IntToStr(15 + i)) as TEdit;
if (edt <> nil) and (edt.Text <> Format('%.3f', [J[i]])) then
edt.Text := Format('%.3f', [J[i]]);
// 計算誤差
edt := FindComponent('Edit' + IntToStr(18 + i)) as TEdit;
if (edt <> nil) and (edt.Text <> Format('%.3f', [ang[i]-J[i]])) then
edt.Text := Format('%.3f', [ang[i]-J[i]]);
end;
}
// 計算上の可動範囲の確認
// 可動範囲内か
if not dobotCheckXYZ(pos[0], pos[1], pos[2]) then begin
if Edit22.Text <> 'NG' then Edit22.Text := 'NG';
end
else begin
if Edit22.Text <> '' then Edit22.Text := '';
end;
end;
// アラーム取得
res := '';
if GetAlarmsState(ASocket, isAlarm, res) then begin
if isAlarm then
// TAB を変換
res := StringReplace(res, #09, ', ', [rfReplaceAll]);
end;
if Edit21.Text <> res then Edit21.Text := res;
// DIO テスト(結構時間がかかる + 100msec)
//GetIODI(ASocket, 19, ioValue);
//Memo1.Lines.Add('DI19 = ' + ioValue.ToString);
//GetIODI(ASocket, 20, ioValue);
//Memo1.Lines.Add('DI20 = ' + ioValue.ToString);
//SetIODO(ASocket, 18, not ioValue and 1); // 反転
//GetIODO(ASocket, 18, ioValue);
//Memo1.Lines.Add('DO18 = ' + ioValue.ToString);
// 処理時間を表示(30~110 msec)
Caption := (TThread.GetTickCount - Ticks).ToString + ' msec';
end;
tmBusy := false;
end;
end;
end.
// 2020/11/17 Android でテスト済
// 通信の部分は、Windows 同じ
unit Unit8;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
System.Bluetooth, System.Bluetooth.Components, FMX.StdCtrls,
FMX.Controls.Presentation, FMX.Edit, FMX.Layouts, FMX.ScrollBox, FMX.Memo,
FMX.Objects, math, FMX.Ani, FMX.ListBox, System.IniFiles, System.IOUtils;
type
TPTPMode = (
JUMP_XYZ,
MOVJ_XYZ,
MOVL_XYZ,
JUMP_ANGLE,
MOVJ_ANGLE,
MOVL_ANGLE,
MOVJ_INC,
MOVL_INC
);
TJOGMode = (
IDEL, // Invalid status
AP_DOWN, // X+ / Joint1+
AN_DOWN, // X- / Joint1-
BP_DOWN, // Y+ / Joint2+
BN_DOWN, // Y- / Joint2-
CP_DOWN, // Z+ / Joint3+
CN_DOWN, // Z- / Joint3-
DP_DOWN, // R+ / Joint4+
DN_DOWN, // R- / Joint4-
LP_DOWN, // L+. Only when the parameter isJoint=1, the LP_DOWN is available
LN_DOWN // L-. Only when the parameter isJoint=1, the LN_DOWN is available
);
TJOGModel = (
COORDINATE_MODEL,
JOINT_MODEL
);
TIOFunction = (
IOFunctionDummy, // Do not config function
IOFunctionPWM, // PWM Output
IOFunctionDO, // IO Output
IOFunctionDI, // IO Intput
IOFunctionADC // AD Input
);
type
TForm8 = class(TForm)
ScaledLayout1: TScaledLayout;
Edit1: TEdit;
Button1: TButton;
Bluetooth1: TBluetooth;
Rectangle1: TRectangle;
Label1: TLabel;
Rectangle2: TRectangle;
Label2: TLabel;
Rectangle3: TRectangle;
Label3: TLabel;
Rectangle4: TRectangle;
Label4: TLabel;
Rectangle5: TRectangle;
Label5: TLabel;
Label6: TLabel;
Rectangle6: TRectangle;
Rectangle7: TRectangle;
Rectangle8: TRectangle;
Label7: TLabel;
Label8: TLabel;
Rectangle9: TRectangle;
Rectangle10: TRectangle;
Label10: TLabel;
Rectangle11: TRectangle;
Label11: TLabel;
Rectangle12: TRectangle;
Label12: TLabel;
Rectangle13: TRectangle;
Label13: TLabel;
Button2: TButton;
Rectangle14: TRectangle;
Button3: TButton;
Rectangle15: TRectangle;
Button4: TButton;
Rectangle16: TRectangle;
Rectangle17: TRectangle;
Rectangle18: TRectangle;
Rectangle19: TRectangle;
Rectangle20: TRectangle;
Rectangle21: TRectangle;
Rectangle22: TRectangle;
Rectangle23: TRectangle;
Rectangle24: TRectangle;
Rectangle25: TRectangle;
Rectangle26: TRectangle;
Rectangle27: TRectangle;
Rectangle28: TRectangle;
Rectangle29: TRectangle;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Button8: TButton;
Button9: TButton;
Button10: TButton;
Button11: TButton;
Button12: TButton;
ComboBox1: TComboBox;
Button13: TButton;
Button14: TButton;
Button15: TButton;
Label9: TLabel;
Rectangle30: TRectangle;
Rectangle31: TRectangle;
Label14: TLabel;
Rectangle32: TRectangle;
Label15: TLabel;
Rectangle33: TRectangle;
Label16: TLabel;
Rectangle35: TRectangle;
Label18: TLabel;
Rectangle36: TRectangle;
Label19: TLabel;
Rectangle37: TRectangle;
Label20: TLabel;
Rectangle34: TRectangle;
Label17: TLabel;
Rectangle38: TRectangle;
Label21: TLabel;
Timer2: TTimer;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button5MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button13Click(Sender: TObject);
procedure Button14Click(Sender: TObject);
procedure Button15Click(Sender: TObject);
procedure ComboBox1Change(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ private 宣言 }
public
{ public 宣言 }
end;
var
Form8: TForm8;
ADevice : TBluetoothDevice;
ASocket : TBluetoothSocket;
AlarmCode : array of string = [
'$00', 'ERR_COMMON_RESET',
'$10', 'ERR_PLAN_INV_SINGULARITY',
'$11', 'ERR_PLAN_INV_CALC',
'$12', 'ERR_PLAN_INV_LIMIT',
'$13', 'ERR_PLAN_PUSH_DATA_REPEAT',
'$14', 'ERR_PLAN_ARC_INPUT_PARAM',
'$15', 'ERR_PLAN_JUMP_PARAM',
'$20', 'ERR_MOVE_INV_SINGULARITY',
'$21', 'ERR_MOVE_INV_CALC',
'$22', 'ERR_MOVE_INV_LIMIT',
'$30', 'ERR_OVERSPEED_AXIS1',
'$31', 'ERR_OVERSPEED_AXIS2',
'$32', 'ERR_OVERSPEED_AXIS3',
'$33', 'ERR_OVERSPEED_AXIS4',
'$40', 'ERR_LIMIT_AXIS1_POS',
'$41', 'ERR_LIMIT_AXIS1_NEG',
'$42', 'ERR_LIMIT_AXIS2_POS',
'$43', 'ERR_LIMIT_AXIS2_NEG',
'$44', 'ERR_LIMIT_AXIS3_POS',
'$45', 'ERR_LIMIT_AXIS3_NEG',
'$46', 'ERR_LIMIT_AXIS4_POS',
'$47', 'ERR_LIMIT_AXIS4_NEG',
'$48', 'ERR_LIMIT_AXIS23_POS',
'$49', 'ERR_LIMIT_AXIS23_NEG',
'$50', 'ERR_LOSE_STEP_AXIS1',
'$51', 'ERR_LOSE_STEP_AXIS2',
'$52', 'ERR_LOSE_STEP_AXIS3',
'$53', 'ERR_LOSE_STEP_AXIS4' ];
//runFlag : boolean; // 非常停止通知、検出用
//tmBusy : boolean; // インターバルタイマーで処理中
CMDMODE : integer;
GB_queuedCmdIndex : UInt64;
BTDeviceHead : string;
const
// SPP(Serial Port Profile) による通信のUUID
ServiceUUID = '{00001101-0000-1000-8000-00805F9B34FB}';
// 検索する BT デバイス名
// デフォルト 'EasyBT' を 'RBT-001' に変更している
//BTDeviceHead = 'RBT-001';
// プロトタイプ
function RBTReceiveData(ASocket: TBluetoothSocket; var readData: TBytes; ATimeout: Cardinal): integer;
function GetPose(ASocket: TBluetoothSocket; var x, y, z, r: single; var a0, a1, a2, a3: single): boolean;
implementation
{$R *.fmx}
function SetIOMultiplexing(ASocket: TBluetoothSocket; ioAddress: Byte; ioFunction: Byte): boolean;
// 多重入出力を設定
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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] := ioFunction; // TIOFunction,
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 130);
end;
end;
function SetPTPCommonParams(ASocket: TBluetoothSocket;
velocityRatio, accelerationRatio: single): boolean;
// PTP 移動 速度係数、加速度係数設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 8; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 83; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[5 + i] := velocityRatio.Bytes[i];
for i := 0 to 3 do B[9 + i] := accelerationRatio.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 83);
end;
end;
function GetIODI(ASocket: TBluetoothSocket; ioAddress: Byte; var ioValue: Byte):boolean;
// GetIODI 入力状態を取得
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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 len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 133) and (B[5] = ioAddress);
if result then
ioValue := B[6];
end;
end;
function GetIODO(ASocket: TBluetoothSocket; ioAddress: Byte; var ioLevel: Byte): boolean;
// 出力状態を取得
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, 8);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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 len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 131) and (B[5] = ioAddress);
if result then
ioLevel := B[6];
end;
end;
function SetIODO(ASocket: TBluetoothSocket; ioAddress: Byte; ioLevel: Byte): boolean;
// 出力
var
checkSum : Byte;
i, len : integer;
B : TBytes;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len= 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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 len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 131);
end;
end;
function GetPTPCommonParams(ASocket: TBluetoothSocket;
var velocityRatio, accelerationRatio: single): boolean;
// PTP 移動 速度係数、加速度係数取得
var
B : TBytes;
len, i : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 83; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $AD;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 83);
if result then begin
for i := 0 to 3 do begin
velocityRatio.Bytes[i] := B[5 + i];
accelerationRatio.Bytes[i] := B[9 + i];
end;
end;
end;
end;
function SetJOGCommonParams(ASocket: TBluetoothSocket;
velocityRatio, accelerationRatio: single): boolean;
// JOG 移動 速度係数、加速度係数設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 8; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 72; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[ 5 + i] := velocityRatio.Bytes[i];
for i := 0 to 3 do B[ 9 + i] := accelerationRatio.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 72);
end;
end;
function GetJOGCommonParams(ASocket: TBluetoothSocket;
var velocityRatio, accelerationRatio: single): boolean;
// PTP 移動 速度係数、加速度係数取得
var
B : TBytes;
len, i : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 72; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $B8;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 72);
if result then begin
for i := 0 to 3 do begin
velocityRatio.Bytes[i] := B[5 + i];
accelerationRatio.Bytes[i] := B[9 + i];
end;
end;
end;
end;
function SetSetPTPCoordinateParams(ASocket: TBluetoothSocket;
xyzVelocity, rVelocity, xyzAcceleration, rAcceleration: single): boolean;
// PTP LINE 移動 速度、加速度設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 16; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 81; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[ 5 + i] := xyzVelocity.Bytes[i];
for i := 0 to 3 do B[ 9 + i] := rVelocity.Bytes[i];
for i := 0 to 3 do B[13 + i] := xyzAcceleration.Bytes[i];
for i := 0 to 3 do B[17 + i] := rAcceleration.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
// チェックサム
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 81);
end;
end;
function GetPTPCoordinateParams(ASocket: TBluetoothSocket;
var xyzVelocity, rVelocity, xyzAcceleration, rAcceleration: single): boolean;
var
i, len : integer;
B : TBytes;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 81; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $AF;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 81);
if result then begin
for i := 0 to 3 do begin
xyzVelocity.Bytes[i] := B[ 5 + i];
rVelocity.Bytes[i] := B[ 9 + i];
xyzAcceleration.Bytes[i] := B[13 + i];
rAcceleration.Bytes[i] := B[17 + i];
end;
end;
end;
end;
function SetJOGCoordinateParams(ASocket: TBluetoothSocket;
velocity, acceleration: array of single): boolean;
// Jog移動 X, Y, Z R 軸の速度と加速度
var
B : TBytes;
len, i, j : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 32; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 71; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for j := 0 to 3 do begin
for i := 0 to 3 do begin
B[ 5 + j * 4 + i] := velocity[j].Bytes[i]; // X,Y, Z, R axis
B[21 + j * 4 + i] := acceleration[j].Bytes[i];
end;
end;
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
// チェックサム
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 71);
end;
end;
function GetJOGCoordinateParams(ASocket: TBluetoothSocket;
var velocity: array of single; var acceleration: array of single): boolean;
var
i, j, len : integer;
B : TBytes;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 71; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $B9;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 71);
if result then begin
for j := 0 to 3 do begin
for i := 0 to 3 do begin
velocity[j].Bytes[i] := B[ 5 + j * 4 + i];
acceleration[j].Bytes[i] := B[21 + j * 4 + i];
end;
end;
end;
end;
end;
function SetQueuedCmdClear(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 245; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0A;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 245);
end;
end;
function SetQueuedCmdStartExec(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len +4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 240; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0F;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 240);
end;
end;
function SetDeviceName(ASocket: TBluetoothSocket; const deviceName: string): boolean;
// デバイス名をセット
var
B, temp : TBytes;
len, i, n : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
temp := TEncoding.ANSI.GetBytes(deviceName);
n := Length(temp);
len := 2 + n; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 1; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to n - 1 do B[5 + i] := temp[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
// チェックサム
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 1);
end;
end;
function GetDeviceName(ASocket: TBluetoothSocket; var deviceName: string): boolean;
var
B : TBytes;
len : integer;
i : Integer;
begin
result := false;
deviceName := '';
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 1; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $FF;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 1);
if result then begin
for i := 5 to len - 2 do
deviceName := deviceName + Char(B[i]);
end;
end;
end;
function GetDeviceSN(ASocket: TBluetoothSocket; var deviceSN: string): boolean;
var
B : TBytes;
len : integer;
i : Integer;
begin
result := false;
deviceSN := '';
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 0; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $00;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 0);
if result then begin
for i := 5 to len - 2 do
deviceSN := deviceSN + Char(B[i]);
end;
end;
end;
function GetAlarmsState(ASocket: TBluetoothSocket;
var isAlarm: boolean; var res: string): boolean;
// アラーム取得
var
alarmsState: array [0..15] of Byte;
B : TBytes;
len : integer;
i, j, idx, k : Integer;
begin
result := false;
isAlarm := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 20; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
// チェックサム
B[5] := $EC;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 20);
if result then begin
for i := 0 to 15 do
alarmsState[i] := B[5 + i];
for i := 1 to 15 do begin // alarmsState[0] は無視
if alarmsState[i] > 0 then begin
isAlarm := true;
break;
end;
end;
end;
end;
// アラーム内容(文字列)を作成
if isAlarm then begin
for i := 1 to 15 do begin // alarmsState[0] は無視
for j := 0 to 7 do begin
if (alarmsState[i] and (1 shl j)) > 0 then begin
idx := i * 8 + j;
res := '';
for k := 0 to Length(AlarmCode) div 2 - 1 do begin
if idx = StrToInt(AlarmCode[k * 2]) then
res := res + AlarmCode[k * 2 + 1] + #09; // TAB を追加
end;
res := Trim(res); // 最後の #09 を削除
end;
end;
end;
end;
end;
function ClearAllAlarmsState(ASocket: TBluetoothSocket): boolean;
// アラームクリア
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 20; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $EB;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 20);
end;
end;
function SetQueuedCmdStopExec(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 241; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0E;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 241);
end;
end;
function SetQueuedCmdForceStopExec(ASocket: TBluetoothSocket): boolean;
var
B : TBytes;
len : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 242; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
// チェックサム
B[5] := $0D;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 242);
end;
end;
function GetQueuedCmdCurrentIndex(ASocket: TBluetoothSocket;
var queuedCmdCurrentIndex: Uint64): boolean;
// コマンドキューの現在の位置
var
B : TBytes;
len, i : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 246; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $0A;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 2000);
result := (len = B[2] + 4) and (B[3] = 246);
if result then begin
queuedCmdCurrentIndex := 0;
for i := 0 to 7 do
queuedCmdCurrentIndex :=
queuedCmdCurrentIndex or (Uint64(B[5 + i]) shl (i * 8));
end;
end;
end;
function SetHomeParams(ASocket: TBluetoothSocket; x, y, z, r: single): boolean;
// HOME 位置設定
var
B : TBytes;
len, i : integer;
checkSum : integer;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 16; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 30; // id;
B[4] := 1; // ctrl r/w($01) = W, isQueued($02) = false;
for i := 0 to 3 do B[ 5 + i] := x.Bytes[i];
for i := 0 to 3 do B[ 9 + i] := y.Bytes[i];
for i := 0 to 3 do B[13 + i] := z.Bytes[i];
for i := 0 to 3 do B[17 + i] := r.Bytes[i];
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 30);
end;
end;
function GetHomeParams(ASocket: TBluetoothSocket; var x, y, z, r: single): boolean;
// ホーム座標を取得
var
B : TBytes;
len, i : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 30; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $E2;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 30);
if result then begin
for i := 0 to 3 do begin
x.Bytes[i] := B[ 5 + i];
y.Bytes[i] := B[ 9 + i];
z.Bytes[i] := B[13 + i];
r.Bytes[i] := B[17 + i];
end;
end;
end;
end;
function SetHomeCmdEx(ASocket: TBluetoothSocket): boolean;
// HomeCmd ホーミング
var
i, len : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 4; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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;
// チェックサム
B[9] := $DE;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 31);
if result then begin
queuedCmdIndex := 0;
for i := 0 to 7 do
queuedCmdIndex := queuedCmdIndex or (Uint64(B[5 + i]) shl (i * 8));
executedCmdIndex := 0;
while result and (executedCmdIndex < queuedCmdIndex) do begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
if result then
result := GetQueuedCmdCurrentIndex(ASocket, executedCmdIndex)
else
break;
end;
if result then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
end;
end;
end;
end;
function SetHomeCmd(ASocket: TBluetoothSocket;var queuedCmdIndex : Uint64): boolean;
// HomeCmd ホーミング
var
i, len : integer;
B : TBytes;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 4; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // 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;
// チェックサム
B[9] := $DE;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 31);
if result then begin
queuedCmdIndex := 0;
for i := 0 to 7 do
queuedCmdIndex := queuedCmdIndex or (Uint64(B[5 + i]) shl (i * 8));
end;
end;
end;
function SetPTPCmd(ASocket: TBluetoothSocket; ptpMode: Byte; toX, toY, toZ, toR: single): boolean;
// PTPCmd
var
checkSum : Byte;
i, len : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 17; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 84; // id;
B[4] := 3; // ctrl r/w = W, isQueued = True;
B[5] := ptpMode;// PTPMode 2= MOVL_XYZ
for i := 0 to 3 do B[ 6 + i] := toX.Bytes[i]; // X
for i := 0 to 3 do B[10 + i] := toY.Bytes[i]; // Y
for i := 0 to 3 do B[14 + i] := toZ.Bytes[i]; // Z
for i := 0 to 3 do B[18 + i] := toR.Bytes[i]; // R
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 84);
if result then begin
queuedCmdIndex := 0;
for i := 0 to 7 do
queuedCmdIndex := queuedCmdIndex or (Uint64(B[5 + i]) shl (i * 8));
executedCmdIndex := 0;
while result and (executedCmdIndex < queuedCmdIndex) do begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
if result then
result := GetQueuedCmdCurrentIndex(ASocket, executedCmdIndex)
else
break;
end;
if result then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
end;
end;
end;
end;
function SetJOGCmd(ASocket: TBluetoothSocket; jogMode: Byte; jogCommand: Byte): boolean;
// JogCmd
// jogMode 0 で X, Y, Z 方向に動く。方向は、 jogCommand で指定
// jog 開始は、方向を指定。停止するまで動き続ける
// Jog 停止は、jogCommand に 0 を指定。
// スピードが速すぎると、使いにくい
var
checkSum : Byte;
i, len : integer;
B : TBytes;
queuedCmdIndex : Uint64;
executedCmdIndex : Uint64;
x, y, z, r, a0, a1, a2, a3 : single;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 73; // id;
B[4] := 3; // ctrl r/w = W, isQueued = True;
B[5] := jogMode; // Jog mode 0-coordinate jog 1-Joint jog
B[6] := jogCommand; // Jog command(Value range0..8)
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 73);
if result then begin
queuedCmdIndex := 0;
for i := 0 to 7 do
queuedCmdIndex := queuedCmdIndex or (Uint64(B[5 + i]) shl (i * 8));
executedCmdIndex := 0;
while result and (executedCmdIndex < queuedCmdIndex) do begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
if result then
result := GetQueuedCmdCurrentIndex(ASocket, executedCmdIndex)
else
break;
end;
if result then begin
result := GetPose(ASocket, x, y, z, r, a0, a1, a2, a3);
end;
end;
end;
end;
function GetEndEffectorSuctionCup(ASocket: TBluetoothSocket;
var isCtrlEnable: Byte; var isSucked: Byte): boolean;
// 吸引カップの状態を取得
var
B : TBytes;
len : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 62; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
B[5] := $C2; // checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 62);
if result then begin
isCtrlEnable := B[5];
isSucked := B[6];
end;
end;
end;
function SetEndEffectorSuctionCup(ASocket: TBluetoothSocket; ctrl, suck: Byte):boolean;
// 吸引カップ ON/OFF
var
B : TBytes;
len, i : integer;
checkSum : Byte;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 62; // id;
B[4] := 3; // ctrl r/w($01) = W, isQueued($02) = True;
B[5] := ctrl; // CtrlEnable
B[6] := suck; // Suck
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 62);
end;
end;
function GetEndEffectorGripper(ASocket: TBluetoothSocket;
var isCtrlEnable: Byte; var isGripped: Byte): boolean;
// グリッパーの状態を取得
var
B : TBytes;
len : integer;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 63; // id;
B[4] := 0; // ctrl r/w($01) = R, isQueued($02) = false;
B[5] := $C1; // checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 63);
if result then begin
isCtrlEnable := B[5];
isGripped := B[6];
end;
end;
end;
function SetEndEffectorGripper(ASocket: TBluetoothSocket; ctrl, grip: Byte):boolean;
// 吸引カップ ON/OFF
var
B : TBytes;
len, i : integer;
checkSum : Byte;
begin
result := False;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 2; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 63; // id;
B[4] := 3; // ctrl r/w($01) = W, isQueued($02) = True;
B[5] := ctrl; // CtrlEnabled
B[6] := grip; // Grip
// チェックサム
checkSum := 0;
for i := 3 to len + 2 do checkSum := checkSum + B[i];
checkSum := $100 - checkSum;
B[len + 3] := checkSum;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 500);
result := (len = B[2] + 4) and (B[3] = 63);
end;
end;
function GetPose(ASocket: TBluetoothSocket;
var x, y, z, r: single; var a0, a1, a2, a3: single): boolean;
// GetPose 現在値を取得
var
i, len : integer;
B : TBytes;
begin
result := false;
if (ASocket <> nil) and ASocket.Connected then begin
len := 2 + 0; // len = 2 + Payload Len;
SetLength(B, len + 4);
B[0] := $AA;
B[1] := $AA;
B[2] := len; // len = 2 + Payload Len;
B[3] := 10; // id;
B[4] := 0; // ctrl r/w = R, isQueued = False;
// チェックサム
B[5] := $F6;
// 送信
ASocket.SendData(B);
// 受信
len := RBTReceiveData(ASocket, B, 1000);
result := (len = B[2] + 4) and (B[3] = 10);
if result then begin
for i := 0 to 3 do begin
x.Bytes[i] := B[ 5 + i];
y.Bytes[i] := B[ 9 + i];
z.Bytes[i] := B[13 + i];
r.Bytes[i] := B[17 + i];
a0.Bytes[i] := B[21 + i];
a1.Bytes[i] := B[25 + i];
a2.Bytes[i] := B[29 + i];
a3.Bytes[i] := B[33 + i];
end;
end;
end;
end;
// Dobot Magician アーム角度を取得
// x, y, z : エンドエフェクタの位置
// A1, A2, A3 : 主軸の回転角度、アーム 1 の傾斜角度, アーム 2 の角度(アーム 1 からの傾斜角度)
// J1, J2, J3 : 主軸の回転角度、アーム 1 の角度(垂直で 0)アーム 2 の傾斜角度(水平で 0)
function dobotArmAngle(x, y, z: double; var A1, A2, A3, J1, J2, J3 : double): boolean;
// uses ,,,, math;
var
th1, th2, th3 : double;
phi, l, ld : double;
L1, L2, L3, L4 : double;
begin
L1 := 0.0; // アーム基点の高さ(Dobot Magician の場合: 0)
L2 := 135.0; // アーム1の辺の長さ
L3 := 146.74; // アーム2の辺の長さ(147.0mm)
L4 := 60.0; // エンドエフェクタの突き出し部分(必ず水平である)
result := false;
// X-Y平面上のアーム部分の長さ
l := sqrt(x * x + y * y) - L4 ;
if (l > L4) then begin
// X-Y 平面上のアームの角度
// Dobot の場合、-125~ +125 度であること
th1 := arctan2(y, x);
// アーム1、2の底辺の長さ
ld := sqrt(l * l + (z - L1) * (z - L1));
result := (ld > 0) and (ld < (L2 + L3));
if result then begin
// アーム基点から先端への仰角
// アーム1、アーム2底辺の三角形の基点側の内角
phi := arctan2((z - L1), l);
// アーム1の角度
th2 := phi + arccos((ld * ld + L2 * L2 - L3 * L3) / (2.0 * ld * L2));
// アーム2のアーム1からの角度
th3 := arcsin((ld * ld - L2 * L2 - L3 * L3) / (2.0 * L2 * L3)) + PI / 2.0;
// ラジアン->デグリ換算
A1 := th1 * 180.0 / PI;
A2 := th2 * 180.0 / PI ;
A3 := th3 * 180.0 / PI ;
// Dobot Arm の角度表示に変換
J1 := A1;
// アーム1の角度(垂直で 0 度、前に傾斜で+方向)
// Dobot の場合 -5.0 ~ +84.0 まで
J2 := 90.0 - A2;
// アーム2の角度(水平で 0 度、前方に下がる方向が+)
// Dobot の場合、 -15.0 ~ +75.0 まで
J3 := 180.0 - A2 - A3;
end;
end;
end;
// 可動範囲内にあるか
function dobotCheckXYZ(x, y, z : double): boolean;
var
A1, A2, A3, J1, J2, J3 : double;
dist1, dist2 : double;
begin
result := false;
if dobotArmAngle(x, y, z, A1, A2, A3, J1, J2, J3) then begin
dist1 := sqrt(x * x + y * y);
dist2 := sqrt((dist1 - 60.0) * (dist1 - 60.0) + z * z);
// (J2 < 89.00) : 大きくすると、下側に(初期値:83.99)
// (J3 < 75.00) : 大きくすると、中心方向に(初期値:75.00)
result := (J1 > -125.0 ) and (J1 < 125.0) and
(J2 > -4.99) and (J2 < 89.9) and
(J3 > -14.9) and (J3 < 89.9) and
(dist2 <= 268.5);
// 268 はアームが最大に伸びた時の長さ(268 ~ 272mm)一直線にはならないため、制限が必要
end;
end;
function dobotSetupIO(ASocket: TBluetoothSocket): boolean;
// IO セットアップ
begin
result := false;
if (ASocket = nil) or not ASocket.Connected then exit;
SetIOMultiplexing(ASocket, 18, Ord(TioFunction.IOFunctionDO));
SetIOMultiplexing(ASocket, 19, Ord(TioFunction.IOFunctionDI));
SetIOMultiplexing(ASocket, 20, Ord(TioFunction.IOFunctionDI));
result := true;
end;
function RBTReceiveData(ASocket: TBluetoothSocket;
var readData: TBytes; ATimeout: Cardinal): integer;
// Dobot(Bluetooth) からの応答
var
AData : TBytes;
i : integer;
Ticks : Cardinal;
idx : integer;
loop : boolean;
cnt : integer;
begin
cnt := 0; idx := 0;
Setlength(readData, 64); // 大き目に用意
Ticks := TThread.GetTickCount;
loop := True;
while loop and (cnt < 500) do begin
//Sleep(1);
AData := ASocket.ReceiveData;
if Length(AData) > 0 then begin
for i := 0 to Length(AData) - 1 do begin
readData[idx] := AData[i];
// 受信バッファの長さを再セット
if idx = 2 then SetLength(readData, readData[2] + 4);
Inc(idx);
if idx >= Length(readData) then begin
loop := False;
break;
end;
end;
end;
Inc(cnt);
if loop then
loop := TThread.GetTickCount - Ticks < ATimeout;
end;
result := idx;
end;
function searchRBT(const deviceHead : string): boolean;
// Bluetooth デバイスを検索
var
ABluetoothManager : TBluetoothManager;
APairedDevices : TBluetoothDeviceList;
ADevice : TBluetoothDevice;
idx, i : integer;
begin
result := false;
if (ASocket = nil) or not ASocket.Connected then begin
try
ABluetoothManager := TBluetoothManager.Current;
if ABluetoothManager.ConnectionState = TBluetoothConnectionState.Connected then begin
// 過去にペアリングされたデバイスの一覧から、ターゲット を探す
APairedDevices := ABluetoothManager.GetPairedDevices;
if APairedDevices.Count > 0 then begin
idx := -1;
for i := 0 to APairedDevices.Count -1 do begin
if (BTDeviceHead = APairedDevices[i].DeviceName) then begin
idx := i;
break; // リストアップを終了
end;
end;
if idx >= 0 then begin
ADevice := APairedDevices[idx];
if ADevice <> nil then begin
ASocket := ADevice.CreateClientSocket(StringToGUID(ServiceUUID), False);
if ASocket <> nil then begin
// 接続
ASocket.Connect;
result := ASocket.Connected;
end;
end;
end;
end;
end;
except
on E : Exception do begin
ShowMessage(E.Message);
end;
end;
end;
end;
procedure TForm8.Button13Click(Sender: TObject);
// 吸引カップ 吸着・破壊反転
var
ctrl, suck: Byte;
begin
if GetEndEffectorSuctionCup(ASocket, ctrl, suck) then begin
SetEndEffectorSuctionCup(ASocket, 1, not suck and 1); // 反転
end;
end;
procedure TForm8.Button14Click(Sender: TObject);
// グリッパー開・閉反転
var
ctrl, grip: Byte;
begin
if GetEndEffectorGripper(ASocket, ctrl, grip) then begin
SetEndEffectorGripper(ASocket, 1, not grip and 1); // 反転
end;
end;
procedure TForm8.Button15Click(Sender: TObject);
// グリッパー、吸引カップ停止
begin
SetEndEffectorGripper(ASocket, 0, 0); // OFF
end;
procedure TForm8.Button1Click(Sender: TObject);
// 接続
var
dobotSN, dobotName : string;
velocity : array [0..3] of single;
acceleration : array [0..3] of single;
i : integer;
IniFile: TMemIniFile; // uses ,,, System.IniFiles;
begin
if Edit1.Text = '' then begin
BTDeviceHead := 'RBT-001';
Edit1.Text:= BTDeviceHead;
end
else
BTDeviceHead := Edit1.Text;
IniFile := TMemIniFile.Create(System.IOUtils.TPath.Combine(
System.IOUtils.TPath.GetDocumentsPath, 'dobotBT.ini'), TEncoding.UTF8);
// uses ,,, System.IOUtils;
with IniFile do begin
try
WriteString('RBT-001', 'DeviceName', Edit1.Text);
UpdateFile;
finally
Free;
end;
end;
if searchRBT(BTDeviceHead) then begin
//Edit1.Text:= BTDeviceHead;
SetQueuedCmdClear(ASocket);
SetQueuedCmdStartExec(ASocket);
if GetDeviceSN(ASocket, dobotSN) then Label5.Text := dobotSN;
if GetDeviceName(ASocket, dobotName) then Label6.Text := dobotName;
for i := 0 to 3 do begin // JOG 初期速度
velocity[i] := 100;
acceleration[i] := 100;
end;
SetJOGCoordinateParams(ASocket, velocity, acceleration);
SetJOGCommonParams(ASocket, 100, 100);
CMDMODE := 202; // モニタモード
Timer2.Enabled := true;
end;
end;
procedure TForm8.Button2Click(Sender: TObject);
// アラームクリア
begin
ClearAllAlarmsState(ASocket);
end;
procedure TForm8.Button3Click(Sender: TObject);
// 非常停止
begin
CMDMODE := 0;
Timer2.Enabled := False;
if SetQueuedCmdForceStopExec(ASocket) then begin
ASocket.Close; // 切断
ASocket := nil;
Button1Click(self); // 再接続
end;
end;
procedure TForm8.Button4Click(Sender: TObject);
// Home
begin
CMDMODE := 101;
Timer2.Enabled := True;
end;
procedure TForm8.Button5MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
// JOG 開始
var
jogMode, jogCommand : Byte;
btn : TButton;
begin
jogMode := Ord(TJOGModel.COORDINATE_MODEL); // =0
btn := Sender as TButton;
if btn <> nil then begin
jogCommand := StrToIntDef(Copy(btn.Name, 7), 0) - 4;
if jogCommand > 0 then begin
SetJogCmd(ASocket, jogMode, jogCommand);
end;
end;
end;
procedure TForm8.Button5MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
// JOG 停止
var
jogMode, jogCommand : Byte;
begin
jogMode := Ord(TJOGModel.COORDINATE_MODEL); // =0
jogCommand := Ord(TJOGMode.IDEL); // =0
SetJogCmd(ASocket, jogMode, jogCommand);
end;
procedure TForm8.ComboBox1Change(Sender: TObject);
// JOG の速度をセット
var
spd : single;
velocity : array [0..3] of single;
acceleration : array [0..3] of single;
i : integer;
begin
spd := 100;
with ComboBox1 do begin
if ItemIndex >= 0 then begin
spd := StrToFloatDef(Items[ItemIndex], 100);
end;
end;
// Jog 移動の速度、加速度をセット
for i:= 0 to 3 do begin
velocity[i] := spd;
acceleration[i] := spd;
end;
SetJOGCoordinateParams(ASocket, velocity, acceleration);
SetJOGCommonParams(ASocket, 100, 100);
end;
procedure TForm8.FormCreate(Sender: TObject);
// フォーム生成
var
btn : TButton;
lbl : TLabel;
i : integer;
//IniFile: TMemIniFile; // uses ,,, System.IniFiles;
begin
for i := 1 to 12 do begin
btn := FindComponent('Button' + IntToStr(i)) as TButton;
if btn <> nil then
btn.Align := TAlignLayout.Client;
end;
for i := 13 to 15 do begin
btn := FindComponent('Button' + IntToStr(i)) as TButton;
if btn <> nil then btn.Align := TAlignLayout.Client;
end;
for i := 1 to 4 do begin
lbl := FindComponent('Label' + IntToStr(i)) as TLabel;
if lbl <> nil then lbl.Align := TAlignLayout.Client;
lbl := FindComponent('Label' + IntToStr(i+9)) as TLabel;
if lbl <> nil then lbl.Align := TAlignLayout.Client;
end;
for i := 1 to 8 do begin
lbl := FindComponent('Label' + IntToStr(i)) as TLabel;
if lbl <> nil then lbl.text := '';
lbl := FindComponent('Label' + IntToStr(i+13)) as TLabel;
if lbl <> nil then begin
if i > 4 then lbl.text := '';
lbl.Align := TAlignLayout.Client;
end;
end;
// 縦画面に固定
Application.FormFactor.Orientations :=
[TFormOrientation.Portrait, TFormOrientation.InvertedPortrait];
IniFile := TMemIniFile.Create(System.IOUtils.TPath.Combine(
System.IOUtils.TPath.GetDocumentsPath, 'dobotBT.ini'), TEncoding.UTF8);
// uses ,,, System.IOUtils;
with IniFile do begin
try
Edit1.Text := ReadString('RBT-001', 'DeviceName', '');
finally
Free;
end;
end;
end;
procedure TForm8.Timer2Timer(Sender: TObject);
var
executedCmdIndex: UInt64;
pos, ang : array [0..3] of single;
i : integer;
lbl : TLabel;
isAlarm : boolean;
res : string;
Ticks : Cardinal;
dist1, dist2 : double;
begin
case CMDMODE of
// HOME
101:begin
Timer2.Enabled := False;
if SetHomeCmd(ASocket, GB_queuedCmdIndex) then begin
CMDMODE := 102; // GetPose...
end
else
CMDMODE := 202; // モニタを続ける
Timer2.Enabled := True;
end;
// GetPose
102,202:begin
Timer2.Enabled := False;
Ticks := TThread.GetTickCount;
if GetPose(ASocket, pos[0], pos[1], pos[2], pos[3], ang[0], ang[1], ang[2], ang[3]) then begin
dist1 := sqrt(pos[0] * pos[0] + pos[1] * pos[1]);
dist2 := sqrt((dist1 - 60.0) * (dist1 - 60.0) + pos[2] * pos[2]);
for i := 0 to 3 do begin
lbl := FindComponent('Label' + IntToStr(1 + i)) as TLabel;
if (lbl <> nil) then begin
if (lbl.Text <> Format('%.2f ', [pos[i]])) then
lbl.Text := Format('%.2f ', [pos[i]]);
end;
end;
// アーム角度
for i := 0 to 3 do begin
lbl := FindComponent('Label' + IntToStr(18 + i)) as TLabel;
if (lbl <> nil) then begin
case i of
0:begin
if (lbl.Text <> Format('%.2f ', [ang[i]])) then
lbl.Text := Format('%.2f ', [ang[i]]);
if (ang[i] > -125) and (ang[i] < 125) then
lbl.TextSettings.FontColor := TAlphaColors.Black
else
lbl.TextSettings.FontColor := TAlphaColors.Red;
end;
1:begin
if (lbl.Text <> Format('%.2f ', [ang[1]])) then
lbl.Text := Format('%.2f ', [ang[i]]);
if (ang[1] > -4.99) and (ang[1] < 89.9) then
lbl.TextSettings.FontColor := TAlphaColors.Black
else
lbl.TextSettings.FontColor := TAlphaColors.Red;
end;
2:begin
if (lbl.Text <> Format('%.2f ', [ang[2]])) then
lbl.Text := Format('%.2f ', [ang[2]]);
if (ang[2] > -14.9) and (ang[2] < 89.9) then
lbl.TextSettings.FontColor := TAlphaColors.Black
else
lbl.TextSettings.FontColor := TAlphaColors.Red;
end;
3: begin
if (lbl.Text <> Format('%.2f ', [dist2])) then
lbl.Text := Format('%.2f ', [dist2]);
if (dist2 > 268.5) then
lbl.TextSettings.FontColor := TAlphaColors.Red
else
lbl.TextSettings.FontColor := TAlphaColors.Black;
end;
end;
end;
end;
// アラーム取得
res := '';
if GetAlarmsState(ASocket, isAlarm, res) then begin
if isAlarm then
// TAB を変換
res := StringReplace(res, #09, ', ', [rfReplaceAll]);
end;
if Label7.Text <> res then Label7.Text := res;
// 計算上の可動範囲の確認
// 可動範囲内か
if not dobotCheckXYZ(pos[0], pos[1], pos[2]) then begin
if Rectangle30.Fill.Color <> TAlphaColorRec.Red then
Rectangle30.Fill.Color := TAlphaColorRec.Red;
end
else begin
if Rectangle30.Fill.Color <> TAlphaColorRec.Lime then
Rectangle30.Fill.Color := TAlphaColorRec.Lime;
end;
Label8.Text := (TThread.GetTickCount - Ticks).ToString + ' msec';
if CMDMODE = 102 then
CMDMODE := 103; // GetQueuedCmdCurrentIndex ...
end
else
CMDMODE := 202; // モニタを続ける
Timer2.Enabled := True;
end;
// GetQueuedCmdCurrentIndex
103:begin
Timer2.Enabled := False;
if GetQueuedCmdCurrentIndex(ASocket, executedCmdIndex) then begin
if executedCmdIndex < GB_queuedCmdIndex then
CMDMODE := 102 // GetPose
else
CMDMODE := 202; // モニタを続ける
end
else
CMDMODE := 202; // モニタを続ける
Timer2.Enabled := True;
end;
end;
end;
end.