This page shows how to use the Universal Robots SDK from Matlab, to read and control a UR cobot from a Matlab script. Matlab loads the .NET DLL of the SDK with its .NET interface: nothing else is installed, on the PC or on the robot.

## Requirements

| Item            | Supported                                                                                                        |
| --------------- | ---------------------------------------------------------------------------------------------------------------- |
| Windows         | Matlab with the .NET Framework 4.6.2 or later (the default on Windows 10 and 11). Load the `net48` DLL           |
| Linux and macOS | Matlab R2024b or later with the .NET runtime, selected by `dotnetenv("core")`. Not tested by UnderAutomation yet |
| Robots          | CB-Series, e-Series, UR8 Long, UR15, UR18, UR20, UR30, with PolyScope or PolyScope X, and URSim                  |

Matlab documents its .NET interface in [Call .NET from MATLAB](https://www.mathworks.com/help/matlab/using-net-libraries-in-matlab.html).

## Load the SDK

1. Download [UnderAutomation.UniversalRobots.zip](https://github.com/underautomation/UniversalRobots.NET/releases/latest/download/UnderAutomation.UniversalRobots.zip) from the latest release of [UniversalRobots.NET](https://github.com/underautomation/UniversalRobots.NET/releases/latest).
2. On Windows, unblock the zip before you extract it: right click, `Properties`, check `Unblock`.
3. Copy its `net48` folder next to your script.
4. Load the DLL with `NET.addAssembly`, then import the namespaces you use.

**Matlab : MatlabLoadAssembly**
```matlab
%%
% The net48 folder of UnderAutomation.UniversalRobots.zip, copied next to this script
dll = fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll');

if ~NET.isNETSupported
    error('No supported .NET Framework found on this computer');
end

% Load the SDK once per Matlab session
NET.addAssembly(dll);

% Import the namespaces you use
import UnderAutomation.UniversalRobots.*
import UnderAutomation.UniversalRobots.Common.*
import UnderAutomation.UniversalRobots.Rtde.*
%%
```

`NET.addAssembly` needs an absolute path: `fullfile(pwd, ...)` builds one that works on every operating system. Matlab cannot unload a .NET assembly: restart Matlab to load a new version of the DLL.

On Linux and macOS, Matlab R2024b and later can use the .NET runtime instead of the .NET Framework. Set the environment variable `DOTNET_ROOT` to the folder of the .NET runtime, call `dotnetenv("core")` before `NET.addAssembly`, and load the DLL of the `net8.0` folder. See [dotnetenv](https://www.mathworks.com/help/matlab/ref/dotnetenv.html) in the Matlab documentation.

## First program

**C# : RemoteControl**
```csharp
using UnderAutomation.UniversalRobots;

class RemoteControl
{
  static void Main(string[] args)
  {
    /**/
    var robot = new UR();

    // Primary Interface and Dashboard Server
    robot.Connect("192.168.0.1");

    // Power on the arm and release the brakes
    robot.Dashboard.PowerOn();
    robot.Dashboard.ReleaseBrake();

    // Load a program and start it
    robot.Dashboard.LoadProgram("pick_and_place.urp");
    robot.Dashboard.Play();

    // Name and state of the program
    var state = robot.Dashboard.GetProgramState();
    Console.WriteLine(state.Value.Name + ": " + state.Value.State);

    // Show a message on the teach pendant
    robot.Dashboard.ShowPopup("Started from the PC");
    /**/
  }
}
```

**Python : RemoteControl**
```python
from underautomation.universal_robots.ur import UR

##
robot = UR()

# Primary Interface and Dashboard Server
robot.connect("192.168.0.1")

# Power on the arm and release the brakes
robot.dashboard.power_on()
robot.dashboard.release_brake()

# Load a program and start it
robot.dashboard.load_program("pick_and_place.urp")
robot.dashboard.play()

# Name and state of the program
state = robot.dashboard.get_program_state()
print(f"{state.value.name}: {state.value.state}")

# Show a message on the teach pendant
robot.dashboard.show_popup("Started from the PC")
##
```

**Matlab : RemoteControl**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll'));
import UnderAutomation.UniversalRobots.*

%%
robot = UR();

% Primary Interface and Dashboard Server
robot.Connect('192.168.0.1');

% Power on the arm and release the brakes
robot.Dashboard.PowerOn();
robot.Dashboard.ReleaseBrake();

% Load a program and start it
robot.Dashboard.LoadProgram('pick_and_place.urp');
robot.Dashboard.Play();

% Name and state of the program
state = robot.Dashboard.GetProgramState();
disp(char(state.Value.Name));
disp(char(state.Value.State.ToString()));

% Show a message on the teach pendant
robot.Dashboard.ShowPopup('Started from Matlab');
%%

robot.Disconnect();
```

The connection parameters select the interfaces, like in C#:

**C# : Connect**
```csharp
using UnderAutomation.UniversalRobots;
using UnderAutomation.UniversalRobots.Rtde;

class Connect
{
  static void Main(string[] args)
  {
    /**/
    var robot = new UR();

    var parameters = new ConnectParameters("192.168.0.1");

    // Enabled by default
    parameters.PrimaryInterface.Enable = true;
    parameters.Dashboard.Enable = true;

    // RTDE: select the data to exchange and the frequency
    parameters.Rtde.Enable = true;
    parameters.Rtde.Frequency = 500; // Hz
    parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualTcpPose);
    parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualQ);
    parameters.Rtde.InputSetup.Add(RtdeInputData.InputIntRegisters, 24);

    // Local XML-RPC server, called by the robot program
    parameters.XmlRpc.Enable = true;
    parameters.XmlRpc.Port = 50000;

    // Local socket server, the robot program connects to it
    parameters.SocketCommunication.Enable = true;
    parameters.SocketCommunication.Port = 50001;

    // SSH and SFTP, with the Linux user of the controller
    parameters.Ssh.EnableSsh = true;
    parameters.Ssh.EnableSftp = true;
    parameters.Ssh.Username = "ur";
    parameters.Ssh.Password = "easybot";

    // Interpreter Mode and REST API (PolyScope X) are disabled by default
    parameters.InterpreterMode.Enable = false;
    parameters.Rest.Enable = false;

    robot.Connect(parameters);

    // ...

    // Close every service
    robot.Disconnect();
    /**/
  }
}
```

**Python : Connect**
```python
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
from underautomation.universal_robots.rtde.rtde_input_data import RtdeInputData
from underautomation.universal_robots.rtde.rtde_output_data import RtdeOutputData

##
robot = UR()

parameters = ConnectParameters("192.168.0.1")

# Enabled by default
parameters.primary_interface.enable = True
parameters.dashboard.enable = True

# RTDE: select the data to exchange and the frequency
parameters.rtde.enable = True
parameters.rtde.frequency = 500  # Hz
parameters.rtde.output_setup.add(RtdeOutputData.ActualTcpPose)
parameters.rtde.output_setup.add(RtdeOutputData.ActualQ)
parameters.rtde.input_setup.add(RtdeInputData.InputIntRegisters, 24)

# Local XML-RPC server, called by the robot program
parameters.xml_rpc.enable = True
parameters.xml_rpc.port = 50000

# Local socket server, the robot program connects to it
parameters.socket_communication.enable = True
parameters.socket_communication.port = 50001

# SSH and SFTP, with the Linux user of the controller
parameters.ssh.enable_ssh = True
parameters.ssh.enable_sftp = True
parameters.ssh.username = "ur"
parameters.ssh.password = "easybot"

# Interpreter Mode and REST API (PolyScope X) are disabled by default
parameters.interpreter_mode.enable = False
parameters.rest.enable = False

robot.connect(parameters)

# ...

# Close every service
robot.disconnect()
##
```

**Matlab : Connect**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll'));
import UnderAutomation.UniversalRobots.*
import UnderAutomation.UniversalRobots.Rtde.*

%%
robot = UR();

parameters = ConnectParameters('192.168.0.1');

% Enabled by default
parameters.PrimaryInterface.Enable = true;
parameters.Dashboard.Enable = true;

% RTDE: select the data to exchange and the frequency
parameters.Rtde.Enable = true;
parameters.Rtde.Frequency = 125;
parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualTcpPose, int32(0));
parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualQ, int32(0));
parameters.Rtde.InputSetup.Add(RtdeInputData.InputIntRegisters, int32(24));

% SSH and SFTP, with the Linux user of the controller
parameters.Ssh.EnableSftp = true;
parameters.Ssh.Username = 'ur';
parameters.Ssh.Password = 'easybot';

robot.Connect(parameters);
%%

robot.Disconnect();
```

The SDK runs for 30 days without a key. After that, register your license once per Matlab session:

**C# : License**
```csharp
using UnderAutomation.UniversalRobots;
using UnderAutomation.UniversalRobots.License;

class License
{
  static void Main(string[] args)
  {
    /**/
    // Register the license once, before the first connection
    UR.RegisterLicense("YourCompanyName", "YOUR_LICENSE_KEY");

    LicenseInfo info = UR.LicenseInfo;

    // Number of trial days remaining
    int? evaluationDaysLeft = info.EvaluationDaysLeft;

    bool licenseValid = info.State == LicenseState.Licensed;

    // A readable description of the current state
    Console.WriteLine(info);

    // Check the license once at startup, rather than catching
    // the exception on every connection
    if (!info.IsLicensed)
    {
      Console.WriteLine(info);
      return;
    }
    /**/

    try
    {
      var robot = new UR();
      robot.Connect("192.168.0.1");
    }
    catch (InvalidLicenseException ex)
    {
      Console.WriteLine(ex.Message);
      Console.WriteLine(ex.LicenseInfo.State);
    }
  }
}
```

**Python : License**
```python
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.license.license_state import LicenseState
from UnderAutomation.UniversalRobots.License import InvalidLicenseException

##
# Register the license once, before the first connection.
# The returned object describes the state of the license
info = UR.register_license("YourCompanyName", "YOUR_LICENSE_KEY")

# Number of trial days remaining
evaluation_days_left = info.evaluation_days_left

license_valid = info.state == LicenseState.Licensed

# A readable description of the current state
print(info)

# Check the license once at startup, rather than catching
# the exception on every connection
if not info.is_licensed:
    print(info)
    raise SystemExit(0)
##

try:
    robot = UR()
    robot.connect("192.168.0.1")
except InvalidLicenseException as ex:
    # The exception comes from the .NET runtime, so its members keep their original names
    print(ex.Message)
    print(ex.LicenseInfo.State)
```

**Matlab : License**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll'));
import UnderAutomation.UniversalRobots.*

%%
% Register the license once, before the first connection
UR.RegisterLicense('YourCompanyName', 'YOUR_LICENSE_KEY');

info = UR.LicenseInfo;

% A readable description of the current state
disp(char(info.ToString()));

% Check the license once at startup
if ~info.IsLicensed
    error(char(info.ToString()));
end
%%
```

## From .NET to Matlab

The names of the classes, methods and properties are the .NET names of the other pages. The C# samples of the documentation translate line by line, with these rules:

| .NET                                        | Matlab                                                           |
| ------------------------------------------- | ---------------------------------------------------------------- |
| `new UR()`                                  | `UR()`, after `import UnderAutomation.UniversalRobots.*`         |
| `"192.168.0.1"`                             | `'192.168.0.1'`, a char vector                                   |
| `OutputSetup.Add(RtdeOutputData.ActualQ)`   | `OutputSetup.Add(RtdeOutputData.ActualQ, int32(0))`              |
| An optional argument                        | Pass every argument, with its .NET type: `int32(24)`, `uint8(1)` |
| `double[]` returned                         | A .NET array: `double(values)` converts it to a Matlab vector    |
| `items[0]`                                  | `items(1)`: Matlab indexes .NET arrays from 1                    |
| `robot.Rtde.OutputDataReceived += Handler;` | `addlistener(robot.Rtde, 'OutputDataReceived', @handler)`        |
| A string returned                           | `char(value)` converts it to a char vector                       |

## Examples

### Data exchange with RTDE

**C# : DataExchange**
```csharp
using UnderAutomation.UniversalRobots;
using UnderAutomation.UniversalRobots.Common;
using UnderAutomation.UniversalRobots.Rtde;

class DataExchange
{
  static void Main(string[] args)
  {
    var robot = new UR();

    var parameters = new ConnectParameters("192.168.0.1");

    // Primary Interface: program and installation variables
    parameters.PrimaryInterface.Enable = true;

    // RTDE at 125 Hz (up to 500 Hz)
    parameters.Rtde.Enable = true;
    parameters.Rtde.Frequency = 125;

    // Data that the robot sends
    parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualTcpPose);
    parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualTcpForce);

    // Data that the PC writes
    parameters.Rtde.InputSetup.Add(RtdeInputData.StandardAnalogOutputMask);
    parameters.Rtde.InputSetup.Add(RtdeInputData.StandardAnalogOutput0);
    parameters.Rtde.InputSetup.Add(RtdeInputData.InputBitRegisters, 64);

    robot.Connect(parameters);

    robot.Rtde.OutputDataReceived += (sender, e) =>
    {
      /**/
      // Position of the tool: X, Y, Z in m, rotation vector in rad
      Pose pose = robot.Rtde.OutputDataValues.ActualTcpPose;

      // Force at the tool
      CartesianCoordinates force = robot.Rtde.OutputDataValues.ActualTcpForce;

      // Write analog output 0 and bit register 64
      var inputs = new RtdeInputValues();
      inputs.StandardAnalogOutputMask = 0b01;
      inputs.StandardAnalogOutput0 = 0.2;
      inputs.InputBitRegisters.X64 = true;
      robot.Rtde.WriteInputs(inputs);
      /**/
    };

    robot.PrimaryInterface.GlobalVariables.ValuesUpdated += (sender, e) =>
    {
      /**/
      // Program and installation variables
      GlobalVariable[] variables = robot.PrimaryInterface.GlobalVariables.GetAll();
      foreach (var variable in variables)
        Console.WriteLine(variable.Name + " = " + variable.Value);
      /**/
    };
  }
}
```

**Python : DataExchange**
```python
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
from underautomation.universal_robots.rtde.rtde_input_data import RtdeInputData
from underautomation.universal_robots.rtde.rtde_input_values import RtdeInputValues
from underautomation.universal_robots.rtde.rtde_output_data import RtdeOutputData

robot = UR()

parameters = ConnectParameters("192.168.0.1")

# Primary Interface: program and installation variables
parameters.primary_interface.enable = True

# RTDE at 125 Hz (up to 500 Hz)
parameters.rtde.enable = True
parameters.rtde.frequency = 125

# Data that the robot sends
parameters.rtde.output_setup.add(RtdeOutputData.ActualTcpPose)
parameters.rtde.output_setup.add(RtdeOutputData.ActualTcpForce)

# Data that the PC writes
parameters.rtde.input_setup.add(RtdeInputData.StandardAnalogOutputMask)
parameters.rtde.input_setup.add(RtdeInputData.StandardAnalogOutput0)
parameters.rtde.input_setup.add(RtdeInputData.InputBitRegisters, 64)

robot.connect(parameters)

def on_output_data_received(sender, e):
    ##
    # Position of the tool: X, Y, Z in m, rotation vector in rad
    pose = robot.rtde.output_data_values.actual_tcp_pose

    # Force at the tool
    force = robot.rtde.output_data_values.actual_tcp_force

    # Write analog output 0 and bit register 64
    inputs = RtdeInputValues()
    inputs.standard_analog_output_mask = 0b01
    inputs.standard_analog_output0 = 0.2
    inputs.input_bit_registers.x64 = True
    robot.rtde.write_inputs(inputs)
    ##

robot.rtde.output_data_received(on_output_data_received)

def on_values_updated(sender, e):
    ##
    # Program and installation variables
    for variable in robot.primary_interface.global_variables.get_all():
        print(variable.name, "=", variable.value)
    ##

robot.primary_interface.global_variables.values_updated(on_values_updated)
```

**Matlab : DataExchange**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll'));
import UnderAutomation.UniversalRobots.*
import UnderAutomation.UniversalRobots.Rtde.*

robot = UR();

parameters = ConnectParameters('192.168.0.1');

% RTDE at 125 Hz (up to 500 Hz)
parameters.Rtde.Enable = true;
parameters.Rtde.Frequency = 125;

% Data that the robot sends
parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualTcpPose, int32(0));
parameters.Rtde.OutputSetup.Add(RtdeOutputData.ActualTcpForce, int32(0));

% Data that Matlab writes
parameters.Rtde.InputSetup.Add(RtdeInputData.StandardAnalogOutputMask, int32(0));
parameters.Rtde.InputSetup.Add(RtdeInputData.StandardAnalogOutput0, int32(0));
parameters.Rtde.InputSetup.Add(RtdeInputData.InputBitRegisters, int32(64));

robot.Connect(parameters);

%%
% Read the last values received, 10 times
for i = 1:10
    pose = robot.Rtde.OutputDataValues.ActualTcpPose;
    fprintf('X=%.4f Y=%.4f Z=%.4f\n', pose.X, pose.Y, pose.Z);
    pause(0.1);
end

% Write analog output 0 and bit register 64
inputs = RtdeInputValues();
inputs.StandardAnalogOutputMask = uint8(1);
inputs.StandardAnalogOutput0 = 0.2;
inputs.InputBitRegisters.X64 = true;
robot.Rtde.WriteInputs(inputs);

% Or an event, raised at each package. The sender is the RTDE client
listener = addlistener(robot.Rtde, 'OutputDataReceived', @onData);
%%

pause(1);
delete(listener);
robot.Disconnect();

%%
function onData(sender, e)
    force = e.OutputDataValues.ActualTcpForce;
    fprintf('Fz=%.2f N\n', force.Z);
end
%%
```

### URScript and files

**C# : Scripts**
```csharp
using UnderAutomation.UniversalRobots;
using UnderAutomation.UniversalRobots.Ssh.Tools.Sftp;

class Scripts
{
  static void Main(string[] args)
  {
    var robot = new UR();

    var parameters = new ConnectParameters("192.168.0.1");

    // Primary Interface: send URScript
    parameters.PrimaryInterface.Enable = true;

    // SFTP: files of the robot
    parameters.Ssh.EnableSftp = true;

    robot.Connect(parameters);

    /**/
    // Run a URScript line
    robot.PrimaryInterface.Script.Send("movej([-1.5, -1.5, -2, -0.5, 1.8, 0], a=1.4, v=1.05)");

    // List the programs of the robot
    SftpFile[] items = robot.Sftp.ListDirectory("/programs/");

    // Download a program to the PC
    robot.Sftp.DownloadFile("/programs/prg.urp", @"C:\temp\prg.urp");

    // Send a program to the robot
    robot.Sftp.UploadFile(@"C:\temp\prg.urp", "/programs/prg.urp");

    // Rename, check, delete
    robot.Sftp.RenameFile("/programs/prg.urp", "/programs/prg2.urp");
    bool exists = robot.Sftp.Exists("/programs/prg2.urp");
    robot.Sftp.DeleteFile("/programs/prg2.urp");
    /**/
  }
}
```

**Python : Scripts**
```python
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters

robot = UR()

parameters = ConnectParameters("192.168.0.1")

# Primary Interface: send URScript
parameters.primary_interface.enable = True

# SFTP: files of the robot
parameters.ssh.enable_sftp = True

robot.connect(parameters)

##
# Run a URScript line
robot.primary_interface.script.send("movej([-1.5, -1.5, -2, -0.5, 1.8, 0], a=1.4, v=1.05)")

# List the programs of the robot
items = robot.sftp.list_directory("/programs/")

# Download a program to the PC
robot.sftp.download_file("/programs/prg.urp", "C:\\temp\\prg.urp")

# Send a program to the robot
robot.sftp.upload_file("C:\\temp\\prg.urp", "/programs/prg.urp")

# Rename, check, delete
robot.sftp.rename_file("/programs/prg.urp", "/programs/prg2.urp")
exists = robot.sftp.exists("/programs/prg2.urp")
robot.sftp.delete_file("/programs/prg2.urp")
##
```

**Matlab : Scripts**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll'));
import UnderAutomation.UniversalRobots.*

robot = UR();

parameters = ConnectParameters('192.168.0.1');

% Primary Interface: send URScript
parameters.PrimaryInterface.Enable = true;

% SFTP: files of the robot
parameters.Ssh.EnableSftp = true;

robot.Connect(parameters);

%%
% Run a URScript line
robot.PrimaryInterface.Script.Send('movej([-1.5, -1.5, -2, -0.5, 1.8, 0], a=1.4, v=1.05)');

% List the programs of the robot. .NET arrays start at 1 in Matlab
items = robot.Sftp.ListDirectory('/programs/');
for i = 1:items.Length
    disp(char(items(i).Name));
end

% Download a program, then send it back
local = fullfile(tempdir, 'prg.urp');
robot.Sftp.DownloadFile('/programs/prg.urp', local);
robot.Sftp.UploadFile(local, '/programs/prg_copy.urp');

% Rename, check, delete
robot.Sftp.RenameFile('/programs/prg_copy.urp', '/programs/prg2.urp');
exists = robot.Sftp.Exists('/programs/prg2.urp');
robot.Sftp.DeleteFile('/programs/prg2.urp');
%%

robot.Disconnect();
```

### Socket server

**C# : Sockets**
```csharp
using UnderAutomation.UniversalRobots;

class Sockets
{
  static void Main(string[] args)
  {
    var robot = new UR();

    var parameters = new ConnectParameters("192.168.0.1");

    // Socket server on port 50001
    parameters.SocketCommunication.Enable = true;
    parameters.SocketCommunication.Port = 50001;

    robot.Connect(parameters);

    /**/
    // The robot connects with the URScript function socket_open()
    robot.SocketCommunication.SocketClientConnection += (sender, e) =>
    {
      e.Client.SocketWrite("Hello robot");
    };

    // The robot sends a message with socket_send_string()
    robot.SocketCommunication.SocketRequest += (sender, e) =>
    {
      Console.WriteLine(e.Message);
    };

    // Send a message to every connected robot
    robot.SocketCommunication.SocketWrite("123456");
    /**/
  }
}
```

**Python : Sockets**
```python
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.connect_parameters import ConnectParameters
from underautomation.universal_robots.socket_communication.socket_client_connection_event_args import SocketClientConnectionEventArgs
from underautomation.universal_robots.socket_communication.socket_request_event_args import SocketRequestEventArgs

robot = UR()

parameters = ConnectParameters("192.168.0.1")

# Socket server on port 50001
parameters.socket_communication.enable = True
parameters.socket_communication.port = 50001

robot.connect(parameters)

##
# The robot connects with the URScript function socket_open()
def on_connection(sender, e):
    SocketClientConnectionEventArgs(e._instance).client.socket_write("Hello robot")

robot.socket_communication.socket_client_connection(on_connection)

# The robot sends a message with socket_send_string()
def on_request(sender, e):
    print(SocketRequestEventArgs(e._instance).message)

robot.socket_communication.socket_request(on_request)

# Send a message to every connected robot
robot.socket_communication.socket_write("123456")
##
```

**Matlab : Sockets**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll'));
import UnderAutomation.UniversalRobots.*

robot = UR();

parameters = ConnectParameters('192.168.0.1');

% Socket server on port 50001
parameters.SocketCommunication.Enable = true;
parameters.SocketCommunication.Port = 50001;

robot.Connect(parameters);

%%
% The robot connects with the URScript function socket_open()
addlistener(robot.SocketCommunication, 'SocketClientConnection', @onConnection);

% The robot sends a message with socket_send_string()
addlistener(robot.SocketCommunication, 'SocketRequest', @onRequest);

% Send a message to every connected robot
robot.SocketCommunication.SocketWrite('123456');
%%

%%
function onConnection(sender, e)
    e.Client.SocketWrite('Hello robot');
end

function onRequest(sender, e)
    disp(char(e.Message));
end
%%
```

### Kinematics and files, without a robot

**C# : Toolbox**
```csharp
using System.Xml.Linq;
using UnderAutomation.UniversalRobots.Common;
using UnderAutomation.UniversalRobots.Files;
using UnderAutomation.UniversalRobots.Kinematics;

class Toolbox
{
  static void Main(string[] args)
  {
    /**/
    // X, Y, Z in m, rotation vector RX, RY, RZ in rad
    var pose = new Pose(0.1, 0.2, -0.1, 0, 0.05, 0.1);

    // Convert the rotation to roll, pitch, yaw, and back
    Pose rpy = pose.FromRotationVectorToRPY();
    Pose rotationVector = rpy.FromRPYToRotationVector();

    // Nominal DH parameters of a UR3e
    IUrDhParameters dh = KinematicsUtils.GetDhParametersFromModel(RobotModelsExtended.UR3e);

    // Forward kinematics, joint positions in rad
    KinematicsResult fk = KinematicsUtils.ForwardKinematics(new double[] { 0, -1.57, 1.57, 0, 0, 0 }, dh);
    Pose flange = Pose.From4x4MatrixToRotationVector(fk.ToolTransform);

    // Inverse kinematics: up to 8 solutions
    double[][] solutions = KinematicsUtils.InverseKinematics(pose.FromRotationVectorTo4x4Matrix(), dh);

    // Open program and installation files, as XML
    URInstallation installation = URInstallation.Load(@"C:\temp\default.installation");
    URProgram program = URProgram.Load(@"C:\temp\prg.urp");
    XElement xml = program.XML;
    /**/
  }
}
```

**Python : Toolbox**
```python
from underautomation.universal_robots.common.pose import Pose
from underautomation.universal_robots.common.robot_models_extended import RobotModelsExtended
from underautomation.universal_robots.kinematics.kinematics_utils import KinematicsUtils
from underautomation.universal_robots.files.ur_program import URProgram
from underautomation.universal_robots.files.ur_installation import URInstallation

##
# X, Y, Z in m, rotation vector RX, RY, RZ in rad
pose = Pose(0.1, 0.2, -0.1, 0, 0.05, 0.1)

# Convert the rotation to roll, pitch, yaw, and back
rpy = pose.from_rotation_vector_to_rpy()
rotation_vector = rpy.from_rpy_to_rotation_vector()

# Nominal DH parameters of a UR3e
dh = KinematicsUtils.get_dh_parameters_from_model(RobotModelsExtended.UR3e)

# Forward kinematics, joint positions in rad
fk = KinematicsUtils.forward_kinematics([0, -1.57, 1.57, 0, 0, 0], dh)
flange = Pose.from4x4_matrix_to_rotation_vector(fk.tool_transform)

# Inverse kinematics: up to 8 solutions
solutions = KinematicsUtils.inverse_kinematics(pose.from_rotation_vector_to4x4_matrix(), dh)

# Open program and installation files, as XML
installation = URInstallation.load("C:\\temp\\default.installation")
program = URProgram.load("C:\\temp\\prg.urp")
xml = program.xml
##
```

**Matlab : Toolbox**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.UniversalRobots.dll'));
import UnderAutomation.UniversalRobots.Common.*
import UnderAutomation.UniversalRobots.Kinematics.*
import UnderAutomation.UniversalRobots.Files.*

%%
% X, Y, Z in m, rotation vector RX, RY, RZ in rad
pose = Pose(0.1, 0.2, -0.1, 0, 0.05, 0.1);

% Convert the rotation to roll, pitch, yaw, and back
rpy = pose.FromRotationVectorToRPY();
rotationVector = rpy.FromRPYToRotationVector();

% Nominal DH parameters of a UR3e
dh = KinematicsUtils.GetDhParametersFromModel(RobotModelsExtended.UR3e);

% Forward kinematics, joint positions in rad
fk = KinematicsUtils.ForwardKinematics([0, -1.57, 1.57, 0, 0, 0], dh);
flange = Pose.From4x4MatrixToRotationVector(fk.ToolTransform);

% Inverse kinematics: up to 8 solutions
solutions = KinematicsUtils.InverseKinematics(pose.FromRotationVectorTo4x4Matrix(), dh);

% Open a program file, as XML
program = URProgram.Load(fullfile(tempdir, 'prg.urp'));
xml = program.XML;
%%
```

## Known limits

- Matlab has no named arguments: pass all the arguments of a .NET method, in order.
- A function given to `addlistener` receives the sender and the event arguments. It does not see the variables of the script: use the sender, as in the RTDE example above.
- An exception of the SDK is a `NET.NetException` in Matlab. Its `ExceptionObject` property holds the .NET exception, with its `Message`.
- Matlab keeps the DLL loaded until it closes.

See also the Matlab pages on the limits of [.NET arrays](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-support-of-net-arrays.html), [.NET methods](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-support-of-net-methods.html), [.NET events](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-support-of-net-events.html) and [.NET enumerations](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-net-enumerations.html).

## What to read next

- [Connect to the robot](/universal-robots/documentation/connect): the settings of the robot for each interface.
- [RTDE](/universal-robots/documentation/rtde): exchange data up to 500 Hz.
- [Licensing](/universal-robots/documentation/license): the 30 day trial and the license key.