This page shows how to use the Fanuc SDK from Matlab, to read and control a Fanuc controller or a ROBOGUIDE virtual robot 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 controller.

## 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")`, and the `netstandard2.1` DLL. Not tested by UnderAutomation yet |
| Controllers     | R-J3iB, R-30iA, R-30iB, R-30iB Plus, R-50iA, and ROBOGUIDE robots                                                              |

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

### On Windows

1. Download [UnderAutomation.Fanuc.zip](https://github.com/underautomation/Fanuc.NET/releases/latest/download/UnderAutomation.Fanuc.zip) from the latest release of [Fanuc.NET](https://github.com/underautomation/Fanuc.NET/releases/latest).
2. 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.Fanuc.zip, copied next to this script
dll = fullfile(pwd, 'net48', 'UnderAutomation.Fanuc.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.Fanuc.*
import UnderAutomation.Fanuc.Common.*
%%
```

`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 `netstandard2.1` folder. See [dotnetenv](https://www.mathworks.com/help/matlab/ref/dotnetenv.html) in the Matlab documentation.

## First program

### Connect and read a register

**C# : ConnectQuick**
```csharp
using UnderAutomation.Fanuc;

public class ConnectQuick
{
    static void Main()
    {
        /**/
        var robot = new FanucRobot();
        robot.Connect("192.168.0.1");
        /**/
    }
}
```

**Python : ConnectQuick**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot

##
robot = FanucRobot()
robot.connect("192.168.0.1")
##
```

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

%%
% With an IP address only, the SDK connects to the web server of the controller (CGTP)
robot = FanucRobot();
robot.Connect('192.168.0.1');

% Numeric register R[1] and current position of motion group 1
r1 = robot.Cgtp.ReadNumericRegisterWithComment(1);
disp(char(r1.ToString()));

position = robot.Cgtp.ReadCartesianPosition(1);
fprintf('X=%.3f, Y=%.3f, Z=%.3f\n', position.X, position.Y, position.Z);
%%

robot.Disconnect();
```

With an IP address only, `Connect` uses the web server of the controller (CGTP), with no option and firmware V8.30 or later (V9.10 for the position).

### Enable other protocols

Telnet KCL, FTP, SNPX, RMI and Stream Motion are enabled in a `ConnectionParameters` object. Each one has its own setup on the controller: see [Connect to your robot](/fanuc/documentation/connect).

**C# : Connect**
```csharp
using UnderAutomation.Fanuc;
using UnderAutomation.Fanuc.Common;

public class Connect
{
  static void Main()
  {
    // Create a robot instance
    FanucRobot robot = new FanucRobot();

    // Configure connection parameters
    ConnectionParameters parameters = new ConnectionParameters("192.168.0.1");

    /**/
    // Enable Telnet KCL for remote commands
    parameters.Telnet.Enable = true;
    parameters.Telnet.TelnetKclPassword = "your_password";

    // Enable FTP for file and variable access
    parameters.Ftp.Enable = true;
    parameters.Ftp.FtpUser = "";
    parameters.Ftp.FtpPassword = "";

    // Enable SNPX for high-speed register and I/O access
    parameters.Snpx.Enable = true;

    // Enable CGTP Web Server (enabled by default)
    parameters.Cgtp.Enable = true;

    // Enable RMI for remote motion commands
    parameters.Rmi.Enable = true;

    // Connect to the robot
    robot.Connect(parameters);

    // Check connection status
    bool isConnected = robot.Enabled;

    // Disconnect when done
    robot.Disconnect();
    /**/
  }
}
```

**Python : Connect**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.common.languages import Languages

# Create a robot instance
robot = FanucRobot()

# Configure connection parameters
parameters = ConnectionParameters("192.168.0.1")

##
# Enable Telnet KCL for remote commands
parameters.telnet.enable = True
parameters.telnet.telnet_kcl_password = "your_password"

# Enable FTP for file and variable access
parameters.ftp.enable = True
parameters.ftp.ftp_user = ""
parameters.ftp.ftp_password = ""

# Enable SNPX for high-speed register and I/O access
parameters.snpx.enable = True

# Enable CGTP Web Server (enabled by default)
parameters.cgtp.enable = True

# Enable RMI for remote motion commands
parameters.rmi.enable = True

# Connect to the robot
robot.connect(parameters)

# Check connection status
is_connected = robot.enabled

# Disconnect when done
robot.disconnect()
##
```

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

%%
parameters = ConnectionParameters('192.168.0.1');

% Enable the protocols you use. Each one has its own setup on the controller.
parameters.Telnet.Enable = true;
parameters.Telnet.TelnetKclPassword = 'your_password';

parameters.Ftp.Enable = true;
parameters.Ftp.FtpUser = '';
parameters.Ftp.FtpPassword = '';

parameters.Snpx.Enable = true;

robot = FanucRobot();
robot.Connect(parameters);
%%

robot.Disconnect();
```

### License

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

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

public class License
{
    static void Main()
    {
        /**/
        // Register the license once, before the first connection
        FanucRobot.RegisterLicense("YourCompanyName", "YOUR_LICENSE_KEY");

        LicenseInfo info = FanucRobot.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 (!FanucRobot.LicenseInfo.IsLicensed)
        {
            Console.WriteLine(FanucRobot.LicenseInfo);
            return;
        }
        /**/
    }
}
```

**Python : License**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.license.license_state import LicenseState

##
# Register the license once, before the first connection
FanucRobot.register_license("YourCompanyName", "YOUR_LICENSE_KEY")

info = FanucRobot.license_info

# 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)
##
```

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

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

info = FanucRobot.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 FanucRobot()`                         | `FanucRobot()`, after `import UnderAutomation.Fanuc.*`             |
| `"192.168.0.1"`                            | `'192.168.0.1'`, a char vector                                     |
| `Pause("MyProgram", force: true)`          | `Pause('MyProgram', true)`: no named arguments                     |
| An optional argument, like `groupNum`      | Pass every argument. `[]` passes `null`                            |
| A `float` argument, like a register value  | `single(123.45)`                                                   |
| An `int` argument                          | `int32(999)`                                                       |
| A nullable value (`int?`, `bool?`)         | An object with `HasValue` and `Value`, or `GetValueOrDefault()`    |
| `positions[0]`                             | `positions(1)`: Matlab indexes .NET arrays from 1                  |
| A `string` result                          | `char(result)` gives a Matlab char vector                          |
| An enumeration value, like `KCLPorts.DOUT` | `KCLPorts.DOUT`, after `import UnderAutomation.Fanuc.Common.Kcl.*` |
| `try { } finally { }`                      | `try ... catch`, then the cleanup in both branches                 |

## Examples

### Read the position

**C# : CgtpPositionKinematicsRead**
```csharp
using UnderAutomation.Fanuc;
using UnderAutomation.Fanuc.Common;

public class CgtpPositionKinematicsRead
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();
        robot.Connect("192.168.0.1");

        /**/
        // Read Cartesian position
        CartesianPosition cartesian = robot.Cgtp.ReadCartesianPosition();
        Console.WriteLine($"X={cartesian.X}, Y={cartesian.Y}, Z={cartesian.Z}");
        Console.WriteLine($"W={cartesian.W}, P={cartesian.P}, R={cartesian.R}");

        // Read joint position
        JointsPosition joints = robot.Cgtp.ReadJointPosition();
        Console.WriteLine($"J1={joints.J1}, J2={joints.J2}, J3={joints.J3}");

        // Multi-group
        CartesianPosition group2 = robot.Cgtp.ReadCartesianPosition(groupNum: 2);
        JointsPosition joints2 = robot.Cgtp.ReadJointPosition(groupNum: 2);
        /**/
    }
}
```

**Python : CgtpPositionKinematicsRead**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot

robot = FanucRobot()
robot.connect("192.168.0.1")

##
# Read Cartesian position
cartesian = robot.cgtp.read_cartesian_position()
print(f"X={cartesian.x}, Y={cartesian.y}, Z={cartesian.z}")
print(f"W={cartesian.w}, P={cartesian.p}, R={cartesian.r}")

# Read joint position
joints = robot.cgtp.read_joint_position()
print(f"J1={joints.j1}, J2={joints.j2}, J3={joints.j3}")

# Multi-group
group2 = robot.cgtp.read_cartesian_position(group_num=2)
joints2 = robot.cgtp.read_joint_position(group_num=2)
##
```

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

robot = FanucRobot();
robot.Connect('192.168.0.1');

%%
% Cartesian position of motion group 1
cartesian = robot.Cgtp.ReadCartesianPosition(1);
fprintf('X=%.3f, Y=%.3f, Z=%.3f\n', cartesian.X, cartesian.Y, cartesian.Z);
fprintf('W=%.3f, P=%.3f, R=%.3f\n', cartesian.W, cartesian.P, cartesian.R);

% Joint position of motion group 1, in degrees
joints = robot.Cgtp.ReadJointPosition(1);
fprintf('J1=%.3f, J2=%.3f, J3=%.3f\n', joints.J1, joints.J2, joints.J3);
%%

robot.Disconnect();
```

### Read and write numeric registers

**C# : SnpxRegistersNumeric**
```csharp
using UnderAutomation.Fanuc;
using UnderAutomation.Fanuc.Common;

public class SnpxRegistersNumeric
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();
        var parameters = new ConnectionParameters("192.168.0.1");
        parameters.Snpx.Enable = true;
        robot.Connect(parameters);

        /**/
        // Read R[1] as float (default)
        float value = robot.Snpx.NumericRegisters.Read(1);

        // Write R[1]
        robot.Snpx.NumericRegisters.Write(1, 123.45f);

        // Read as 32-bit integer
        int intValue = robot.Snpx.NumericRegistersInt32.Read(1);
        robot.Snpx.NumericRegistersInt32.Write(1, 999);

        // Read as 16-bit integer
        short shortValue = robot.Snpx.NumericRegistersInt16.Read(1);
        /**/
    }
}
```

**Python : SnpxRegistersNumeric**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters

robot = FanucRobot()
parameters = ConnectionParameters("192.168.0.1")
parameters.snpx.enable = True
robot.connect(parameters)

##
# Read R[1] as float (default)
value = robot.snpx.numeric_registers.read(1)

# Write R[1]
robot.snpx.numeric_registers.write(1, 123.45)

# Read as 32-bit integer
int_value = robot.snpx.numeric_registers_int32.read(1)
robot.snpx.numeric_registers_int32.write(1, 999)

# Read as 16-bit integer
short_value = robot.snpx.numeric_registers_int16.read(1)
##
```

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

parameters = ConnectionParameters('192.168.0.1');
parameters.Snpx.Enable = true;
robot = FanucRobot();
robot.Connect(parameters);

%%
% Read R[1] as a real value
value = robot.Snpx.NumericRegisters.Read(1);

% Write R[1]: the .NET method takes a float, so pass a single
robot.Snpx.NumericRegisters.Write(1, single(123.45));

% Read and write R[2] as a 32 bit integer
intValue = robot.Snpx.NumericRegistersInt32.Read(2);
robot.Snpx.NumericRegistersInt32.Write(2, int32(999));
%%

robot.Disconnect();
```

### Read and write digital signals

**C# : SnpxIoDigital**
```csharp
using UnderAutomation.Fanuc;

public class SnpxIoDigital
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();
        var parameters = new ConnectionParameters("192.168.0.1");
        parameters.Snpx.Enable = true;
        robot.Connect(parameters);

        /**/
        // Read SDI[10]
        bool sdi10 = robot.Snpx.SDI.Read(10);

        // Write RDO[1] = ON
        robot.Snpx.RDO.Write(1, true);

        // Read UI[5]
        bool ui5 = robot.Snpx.UI.Read(5);

        // Read 100 SDI signals starting at index 1
        bool[] values = robot.Snpx.SDI.Read(1, 100);

        // Write 3 SDO signals starting at index 1
        robot.Snpx.SDO.Write(1, new[] { true, false, true });
        /**/
    }
}
```

**Python : SnpxIoDigital**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters

robot = FanucRobot()
parameters = ConnectionParameters("192.168.0.1")
parameters.snpx.enable = True
robot.connect(parameters)

##
# Read SDI[10]
sdi10 = robot.snpx.sdi.read(10)

# Write RDO[1] = ON
robot.snpx.rdo.write(1, True)

# Read UI[5]
ui5 = robot.snpx.ui.read(5)

# Read 100 SDI signals starting at index 1
values = robot.snpx.sdi.read(1, 100)

# Write 3 SDO signals starting at index 1
robot.snpx.sdo.write(1, [True, False, True])
##
```

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

parameters = ConnectionParameters('192.168.0.1');
parameters.Snpx.Enable = true;
robot = FanucRobot();
robot.Connect(parameters);

%%
% Read SDI[10]
sdi10 = robot.Snpx.SDI.Read(10);

% Write RDO[1] = ON
robot.Snpx.RDO.Write(1, true);

% Read UI[5]
ui5 = robot.Snpx.UI.Read(5);
%%

robot.Disconnect();
```

### Run a program

**C# : TelnetProgramControlRun**
```csharp
using UnderAutomation.Fanuc;

public class TelnetProgramControlRun
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();
        var parameters = new ConnectionParameters("192.168.0.1");
        parameters.Telnet.Enable = true;
        parameters.Telnet.TelnetKclPassword = "TELNET_PASS";
        robot.Connect(parameters);

        /**/
        // Run the default program
        robot.Telnet.Run();

        // Run a specific program
        robot.Telnet.Run("MyProgram");

        // Pause a running program (stops at the next fine point)
        robot.Telnet.Pause("MyProgram");

        // Force pause immediately
        robot.Telnet.Pause("MyProgram", force: true);

        // Hold a program (stops at the current position)
        robot.Telnet.Hold("MyProgram");

        // Resume a paused or held program
        robot.Telnet.Continue("MyProgram");
        /**/
    }
}
```

**Python : TelnetProgramControlRun**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters

robot = FanucRobot()
parameters = ConnectionParameters("192.168.0.1")
parameters.telnet.enable = True
parameters.telnet.telnet_kcl_password = "TELNET_PASS"
robot.connect(parameters)

##
# Run the default program
robot.telnet.run()

# Run a specific program
robot.telnet.run("MyProgram")

# Pause a running program (stops at the next fine point)
robot.telnet.pause("MyProgram")

# Force pause immediately
robot.telnet.pause("MyProgram", force=True)

# Hold a program (stops at the current position)
robot.telnet.hold("MyProgram")

# Resume a paused or held program
robot.telnet.continue_("MyProgram")
##
```

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

parameters = ConnectionParameters('192.168.0.1');
parameters.Telnet.Enable = true;
parameters.Telnet.TelnetKclPassword = 'your_password';
robot = FanucRobot();
robot.Connect(parameters);

%%
% Run a program
robot.Telnet.Run('MyProgram');

% Pause it (pass every argument: Matlab has no optional arguments)
robot.Telnet.Pause('MyProgram', false);

% Resume it
robot.Telnet.Continue('MyProgram');
%%

robot.Disconnect();
```

### Forward kinematics, offline

**C# : KinematicsFK**
```csharp
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Kinematics;

public class KinematicsFK
{
    static void Main()
    {
        /**/
        // Load robot geometry
        var dh = DhParameters.FromArmKinematicModel(ArmKinematicModels.CRX10iA);

        // Joint angles in degrees (Fanuc convention)
        var jointsDeg = new JointsPosition { J1 = 0, J2 = -30, J3 = 45, J4 = 0, J5 = 60, J6 = 90 };

        // Compute pose: returns XYZ + WPR
        CartesianPosition pose = KinematicsUtils.ForwardKinematics(jointsDeg, dh);
        /**/
    }
}
```

**Python : KinematicsFK**
```python
from underautomation.fanuc.kinematics.kinematics_utils import KinematicsUtils
from underautomation.fanuc.kinematics.dh_parameters import DhParameters
from underautomation.fanuc.common.joints_position import JointsPosition

##
# Load robot geometry
dh = DhParameters.from_arm_kinematic_model("CRX10iA")

# Joint angles in degrees (Fanuc convention)
joints_deg = JointsPosition(j1=0, j2=-30, j3=45, j4=0, j5=60, j6=90)

# Compute pose: returns XYZ + WPR
pose = KinematicsUtils.forward_kinematics(joints_deg, dh)
##
```

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

%%
% Offline: no robot and no connection are needed
dh = DhParameters.FromArmKinematicModel(ArmKinematicModels.CRX10iA);

% Joint angles in degrees
joints = JointsPosition(0, -30, 45, 0, 60, 90);

% Forward kinematics: X, Y, Z in mm and W, P, R in degrees
pose = KinematicsUtils.ForwardKinematics(joints, dh);
fprintf('X=%.2f, Y=%.2f, Z=%.2f, W=%.2f, P=%.2f, R=%.2f\n', ...
    pose.X, pose.Y, pose.Z, pose.W, pose.P, pose.R);
%%
```

## Known limits

- Matlab has no named arguments and no optional arguments: pass all the arguments of a .NET method, in order.
- A .NET event, like `robot.Telnet.CommandSent`, needs `addlistener`, and its handler runs on a thread of the SDK. Prefer to read the values in a loop.
- Stream Motion sends a position at every communication cycle, 2 to 8 ms. Use the target tracking or a trajectory of the motion planner, not a Matlab callback for each cycle.
- 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) and [.NET enumerations](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-net-enumerations.html).

## What to read next

- [Connect to your robot](/fanuc/documentation/connect): the protocols, their setup on the controller, the connection parameters.
- [Test with ROBOGUIDE](/fanuc/documentation/simulator): work without a real robot.
- The protocol pages: [CGTP](/fanuc/documentation/cgtp), [SNPX](/fanuc/documentation/snpx), [Telnet](/fanuc/documentation/telnet).
- [Licensing](/fanuc/documentation/license): the 30 day trial and the license key.