This page shows how to read and write the variables and the registers of a Yaskawa Motoman controller with the SDK. A job and a PC exchange data through these variables: counters, offsets, recipe numbers, positions.

## Variable types

| Variable | Content                               | Read                   | Write                   | .NET type                 |
| -------- | ------------------------------------- | ---------------------- | ----------------------- | ------------------------- |
| `B`      | Byte, 0 to 255                        | `ReadByte`             | `WriteByte`             | `byte[]`                  |
| `I`      | Integer, 16 bits                      | `ReadInteger`          | `WriteInteger`          | `short[]`                 |
| `D`      | Double integer, 32 bits               | `ReadDoubleInteger`    | `WriteDoubleInteger`    | `int[]`                   |
| `R`      | Real, 32 bit floating point           | `ReadReal`             | `WriteReal`             | `float[]`                 |
| `S`      | String of 16 bytes                    | `Read16BytesChar`      | `Write16BytesChar`      | `string[]`                |
| `S`      | String of 32 bytes                    | `Read32BytesChar`      | `Write32BytesChar`      | `string[]`                |
| `P`      | Robot position                        | `ReadPositionVariable` | `WritePositionVariable` | `RobotPositionIntData[]`  |
| `BP`     | Base axis position                    | `ReadBasePosition`     | `WriteBasePosition`     | `RobotBasePositionData[]` |
| `EX`     | Station axis position                 | `ReadExternalPosition` | `WriteExternalPosition` | `RobotExternalAxisData[]` |
| `M`      | Register of the concurrent I/O ladder | `ReadRegister`         | `WriteRegister`         | `short[]`                 |

Every read method takes the number of the first variable and a count: `ReadInteger(10, 2)` reads `I010` and `I011`. It returns an object whose `Value` is an array, one item per variable. Every write method takes the number of the first variable and an array: one call writes several variables in a row.

The numbers start at `0` (`B000`, `I000`...). The number of variables of each type depends on the controller and on its settings.

## Numeric variables

**C# : VariableNumeric**
```csharp
using UnderAutomation.Yaskawa;
using UnderAutomation.Yaskawa.HighSpeedEServer;

public class VariableNumeric
{
    static void Main()
    {
        var robot = new YaskawaRobot();
        robot.Connect("192.168.0.1");

        /**/
        // B000 to B003: byte variables (0 to 255). Read and write an even number of values
        byte[] b = robot.HighSpeedEServer.ReadByte(0, 4).Value;
        robot.HighSpeedEServer.WriteByte(0, new byte[] { 1, 2, 3, 4 });

        // I010 and I011: integer variables (16 bits)
        short[] i = robot.HighSpeedEServer.ReadInteger(10, 2).Value;
        robot.HighSpeedEServer.WriteInteger(10, new short[] { -100, 200 });

        // D000: double integer variable (32 bits)
        int[] d = robot.HighSpeedEServer.ReadDoubleInteger(0, 1).Value;
        robot.HighSpeedEServer.WriteDoubleInteger(0, new[] { 100000 });

        // R005 to R007: real variables (32 bit floating point)
        float[] r = robot.HighSpeedEServer.ReadReal(5, 3).Value;
        robot.HighSpeedEServer.WriteReal(5, new[] { 1.5f, -2.25f, 3f });
        /**/

        robot.Disconnect();
    }
}
```

**Python : VariableNumeric**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters

robot = YaskawaRobot()
robot.connect(ConnectParameters("192.168.0.1"))

##
# B000 to B003: byte variables (0 to 255). Read and write an even number of values
b = list(robot.high_speed_e_server.read_byte(0, 4).value)
robot.high_speed_e_server.write_byte(0, [1, 2, 3, 4])

# I010 and I011: integer variables (16 bits)
i = list(robot.high_speed_e_server.read_integer(10, 2).value)
robot.high_speed_e_server.write_integer(10, [-100, 200])

# D000: double integer variable (32 bits)
d = list(robot.high_speed_e_server.read_double_integer(0, 1).value)
robot.high_speed_e_server.write_double_integer(0, [100000])

# R005 to R007: real variables (32 bit floating point)
r = list(robot.high_speed_e_server.read_real(5, 3).value)
robot.high_speed_e_server.write_real(5, [1.5, -2.25, 3.0])
##

robot.disconnect()
```

**Matlab : VariableNumeric**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.Yaskawa.dll'));
import UnderAutomation.Yaskawa.*
import UnderAutomation.Yaskawa.HighSpeedEServer.*

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

%%
% I000 to I004: integer variables
i = double(robot.HighSpeedEServer.ReadInteger(0, 5).Value);

% Write I010 and I011: a Matlab vector of the .NET type of the method
robot.HighSpeedEServer.WriteInteger(10, int16([-100, 200]));

% R005 to R007: real variables
r = double(robot.HighSpeedEServer.ReadReal(5, 3).Value);
robot.HighSpeedEServer.WriteReal(5, single([1.5, -2.25, 3]));
%%

robot.Disconnect();
```

- `B` variables are read and written by pairs: `ReadByte` rounds the count up to an even number, and `WriteByte` needs an even number of values.
- `D` variables are 32 bit integers, not floating point values. Use `R` variables for decimal values.

## String variables

**C# : VariableString**
```csharp
using UnderAutomation.Yaskawa;
using UnderAutomation.Yaskawa.HighSpeedEServer;

public class VariableString
{
    static void Main()
    {
        var robot = new YaskawaRobot();
        robot.Connect("192.168.0.1");

        /**/
        // S000 and S001: string variables of 16 bytes
        string[] texts = robot.HighSpeedEServer.Read16BytesChar(0, 2).Value;

        // Longer strings are cut to 16 characters
        robot.HighSpeedEServer.Write16BytesChar(0, new[] { "PART-A", "BATCH 12" });

        // String variables of 32 bytes, on the controllers that have them
        string[] longTexts = robot.HighSpeedEServer.Read32BytesChar(0, 1).Value;
        robot.HighSpeedEServer.Write32BytesChar(0, new[] { "Reference 2026-10-01 line 4" });
        /**/

        robot.Disconnect();
    }
}
```

**Python : VariableString**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters

robot = YaskawaRobot()
robot.connect(ConnectParameters("192.168.0.1"))

##
# S000 and S001: string variables of 16 bytes
texts = list(robot.high_speed_e_server.read16_bytes_char(0, 2).value)

# Longer strings are cut to 16 characters
robot.high_speed_e_server.write16_bytes_char(0, ["PART-A", "BATCH 12"])

# String variables of 32 bytes, on the controllers that have them
long_texts = list(robot.high_speed_e_server.read32_bytes_char(0, 1).value)
robot.high_speed_e_server.write32_bytes_char(0, ["Reference 2026-10-01 line 4"])
##

robot.disconnect()
```

A string longer than the size of the variable is cut. Write ASCII characters only.

The 32 byte strings exist on the controllers that store their `S` variables in 32 bytes. On the others, the controller refuses the request.

## Position variables

A `P` variable holds a robot position: in pulses, or in a Cartesian frame with a tool, a user frame and a posture.

**C# : VariablePosition**
```csharp
using UnderAutomation.Yaskawa;
using UnderAutomation.Yaskawa.HighSpeedEServer;

public class VariablePosition
{
    static void Main()
    {
        var robot = new YaskawaRobot();
        robot.Connect("192.168.0.1");

        /**/
        // P000: a position variable
        RobotPositionIntData p0 = robot.HighSpeedEServer.ReadPositionVariable(0, 1).Value[0];

        // PulseValue: Axes are pulses. Otherwise: X, Y, Z in micrometers, Rx, Ry, Rz in 1/10000 degree
        Console.WriteLine($"{p0.DataType}, tool {p0.ToolNumber}: {string.Join(", ", p0.Axes)}");

        // P001: a Cartesian position in the robot frame
        var p1 = new RobotPositionIntData
        {
            DataType = RobotPositionDataType.RobotCoordinateValue,
            Form = new RobotPosture(),
            ToolNumber = 0,
            UserCoordinateNumber = 0,
        };
        p1.Axis1 = 850000;   // X = 850 mm
        p1.Axis2 = 0;        // Y
        p1.Axis3 = 400000;   // Z = 400 mm
        p1.Axis4 = 1800000;  // Rx = 180 degrees
        p1.Axis5 = 0;        // Ry
        p1.Axis6 = 0;        // Rz

        robot.HighSpeedEServer.WritePositionVariable(1, new[] { p1 });
        /**/

        robot.Disconnect();
    }
}
```

**Python : VariablePosition**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters
from underautomation.yaskawa.high_speed_e_server.robot_position_int_data import RobotPositionIntData
from underautomation.yaskawa.high_speed_e_server.robot_position_data_type import RobotPositionDataType
from underautomation.yaskawa.high_speed_e_server.robot_posture import RobotPosture

robot = YaskawaRobot()
robot.connect(ConnectParameters("192.168.0.1"))

##
# P000: a position variable
p0 = robot.high_speed_e_server.read_position_variable(0, 1).value[0]

# PulseValue: axes are pulses. Otherwise: X, Y, Z in micrometers, Rx, Ry, Rz in 1/10000 degree
print(f"{p0.data_type.name}, tool {p0.tool_number}: {list(p0.axes)}")

# P001: a Cartesian position in the robot frame
p1 = RobotPositionIntData(None)
p1.data_type = RobotPositionDataType.RobotCoordinateValue
p1.form = RobotPosture.from_integer(0)
p1.tool_number = 0
p1.user_coordinate_number = 0
p1.axis1 = 850000   # X = 850 mm
p1.axis2 = 0        # Y
p1.axis3 = 400000   # Z = 400 mm
p1.axis4 = 1800000  # Rx = 180 degrees
p1.axis5 = 0        # Ry
p1.axis6 = 0        # Rz

robot.high_speed_e_server.write_position_variable(1, [p1])
##

robot.disconnect()
```

| `DataType`                                                                                  | `Axes`                                                    |
| ------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `PulseValue`                                                                                | One value per axis, in pulses                             |
| `BaseCoordinateValue`, `RobotCoordinateValue`, `UserCoordinateValue`, `ToolCoordinateValue` | X, Y, Z in micrometers, then Rx, Ry, Rz in 1/10000 degree |

The units of a `P` variable are the raw units of the controller: 850 mm is `850000`. They differ from `GetRobotCartesianPosition()`, which returns mm and degrees.

To write a variable, set `Form`: `new RobotPosture()` is the default posture. In Python, use `RobotPosture.from_integer(0)`.

## Base and station position variables

`BP` variables hold the position of base axes (travel tracks), `EX` variables the position of station axes (positioners). The values are in pulses, up to 8 axes.

**C# : VariableBaseExternal**
```csharp
using UnderAutomation.Yaskawa;
using UnderAutomation.Yaskawa.HighSpeedEServer;

public class VariableBaseExternal
{
    static void Main()
    {
        var robot = new YaskawaRobot();
        robot.Connect("192.168.0.1");

        /**/
        // BP000: base axis position variable (travel track)
        RobotBasePositionVariableData bp = robot.HighSpeedEServer.ReadBasePosition(0, 1);
        RobotBasePositionData bp0 = bp.Value[0];
        Console.WriteLine($"{bp0.DataType}: {string.Join(", ", bp0.Axes)}");

        // Change the first axis and write the variable back
        bp0.Axis1 += 1000;
        robot.HighSpeedEServer.WriteBasePosition(0, new[] { bp0 });

        // EX000: station axis position variable (positioner), in pulses
        RobotExternalAxisData ex0 = robot.HighSpeedEServer.ReadExternalPosition(0, 1).Value[0];
        ex0.Axis1 = 0;
        robot.HighSpeedEServer.WriteExternalPosition(0, new[] { ex0 });
        /**/

        robot.Disconnect();
    }
}
```

**Python : VariableBaseExternal**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters

robot = YaskawaRobot()
robot.connect(ConnectParameters("192.168.0.1"))

##
# BP000: base axis position variable (travel track)
bp0 = robot.high_speed_e_server.read_base_position(0, 1).value[0]
print(f"{bp0.data_type.name}: {list(bp0.axes)}")

# Change the first axis and write the variable back
bp0.axis1 += 1000
robot.high_speed_e_server.write_base_position(0, [bp0])

# EX000: station axis position variable (positioner), in pulses
ex0 = robot.high_speed_e_server.read_external_position(0, 1).value[0]
ex0.axis1 = 0
robot.high_speed_e_server.write_external_position(0, [ex0])
##

robot.disconnect()
```

The data type of a `BP` variable (pulses or base frame) cannot be set by the SDK: read the variable, change its axes and write it back, as above.

## Registers

`M` registers are the 16 bit registers of the concurrent I/O ladder of the controller.

**C# : VariableRegister**
```csharp
using UnderAutomation.Yaskawa;
using UnderAutomation.Yaskawa.HighSpeedEServer;

public class VariableRegister
{
    static void Main()
    {
        var robot = new YaskawaRobot();
        robot.Connect("192.168.0.1");

        /**/
        // M000 to M009: registers of the concurrent I/O ladder (16 bits)
        short[] registers = robot.HighSpeedEServer.ReadRegister(0, 10).Value;
        Console.WriteLine(string.Join(", ", registers));

        // Write M560 and M561. The controller refuses the registers reserved for the system
        robot.HighSpeedEServer.WriteRegister(560, new short[] { 12, 34 });
        /**/

        robot.Disconnect();
    }
}
```

**Python : VariableRegister**
```python
from underautomation.yaskawa.yaskawa_robot import YaskawaRobot
from underautomation.yaskawa.connect_parameters import ConnectParameters

robot = YaskawaRobot()
robot.connect(ConnectParameters("192.168.0.1"))

##
# M000 to M009: registers of the concurrent I/O ladder (16 bits)
registers = list(robot.high_speed_e_server.read_register(0, 10).value)
print(registers)

# Write M560 and M561. The controller refuses the registers reserved for the system
robot.high_speed_e_server.write_register(560, [12, 34])
##

robot.disconnect()
```

The controller reserves some registers for its own use, and refuses to write them. Check the free registers in the concurrent I/O manual of your controller.

## Errors

- A variable number out of range makes the controller refuse the request: the SDK throws an `InvalidDataAnswerException`.
- One write call takes a limited number of values, for example 120 `D` variables. Beyond, the SDK throws an exception that gives the maximum: split the array in several calls.

## Try it in the demo application

Everything on this page can be tried without writing code, in the **Variables** page of the [demo application](/yaskawa/documentation/demo-app).

![Variables page of the Yaskawa SDK demo application](/yaskawa/WinformsScreenshots/Variable.jpg)

Its C# source is [VariableControl.cs](https://github.com/underautomation/Yaskawa.NET/blob/main/UnderAutomation.Yaskawa.Showcase.Forms/Components/VariableControl.cs).

## Reference

**Methods of HighSpeedEServerClientBase**
```csharp
// Reads multiple 16-byte string variables (S variables) from the robot controller. String variables are fixed-length, null-terminated ASCII strings.
RobotStringVariableData Read16BytesChar(int firstIndex, int count);

// Reads multiple 32-byte string variables (S variables) from the robot controller (DX200 only). Extended string variables for longer text storage than 16-byte variants.
RobotStringVariableData Read32BytesChar(int firstIndex, int count);

// Reads multiple base position variables (BP variables) from the robot controller. Base position variables define reference coordinate frames for robot operations.
RobotBasePositionVariableData ReadBasePosition(int firstIndex, int count);

// Reads multiple byte variables (B variables) from the robot controller. Byte variables are 8-bit unsigned values used for compact data storage.
RobotByteVariableData ReadByte(int firstIndex, int count);

// Reads multiple double-precision variables (D variables) from the robot controller. Note: The protocol actually transmits float values which are then cast to double.
RobotDoubleIntegerVariableData ReadDoubleInteger(int firstIndex, int count);

// Reads multiple external axis variables (EX variables) from the robot controller. External axis variables store positions for additional axes beyond the main robot arm.
RobotExternalAxisVariableData ReadExternalPosition(int firstIndex, int count);

// Reads multiple integer variables (I variables) from the robot controller. Integer variables are 16-bit signed values (-32768 to 32767).
RobotIntegerVariableData ReadInteger(int firstIndex, int count);

// Reads multiple position variables (P variables) from the robot controller. Position variables store complete robot poses including position, orientation, and configuration.
RobotPositionVariableData ReadPositionVariable(int firstIndex, int count);

// Reads multiple real (single-precision float) variables (R variables) from the robot controller. Real variables are 32-bit IEEE 754 floating-point values.
RobotRealVariableData ReadReal(int firstIndex, int count);

// Reads multiple 16-bit register values (M variables) from the robot controller. Registers are used for general-purpose integer storage in robot programs.
RobotRegisterData ReadRegister(int firstIndex, int count);

// Writes 16-byte string variables (S variables) to the robot controller. Strings longer than 16 characters will be truncated.
RobotDataHeader Write16BytesChar(int firstIndex, string[] data);

// Writes 32-byte string variables (S variables) to the robot controller. Strings longer than 32 characters will be truncated.
RobotDataHeader Write32BytesChar(int firstIndex, string[] data);

// Writes base position variables (BP variables) to the robot controller.
RobotDataHeader WriteBasePosition(int firstIndex, RobotBasePositionData[] data);

// Writes byte variables (B variables) to the robot controller.
RobotDataHeader WriteByte(int firstIndex, byte[] data);

// Writes double-precision variables (D variables) to the robot controller.
RobotDataHeader WriteDoubleInteger(int firstIndex, int[] data);

// Writes external axis variables (EX variables) to the robot controller using generic type. Provided for backward compatibility with existing code.
RobotDataHeader WriteExternalPosition(int firstIndex, RobotAxisRawData<int>[] data);

// Writes external axis variables (EX variables) to the robot controller.
RobotDataHeader WriteExternalPosition(int firstIndex, RobotExternalAxisData[] data);

// Writes integer variables (I variables) to the robot controller.
RobotDataHeader WriteInteger(int firstIndex, short[] data);

// Writes position variables (P variables) to the robot controller.
RobotDataHeader WritePositionVariable(int firstIndex, RobotPositionIntData[] data);

// Writes real (single-precision float) variables (R variables) to the robot controller.
RobotDataHeader WriteReal(int firstIndex, float[] data);

// Writes multiple 16-bit register values (M variables) to the robot controller.
RobotDataHeader WriteRegister(int firstIndex, short[] data);
```

Every method also exists in an asynchronous version, with the same name followed by `Async` and an optional `CancellationToken`.

**Members of HighSpeedEServer.RobotPositionIntData**
```csharp
public class RobotPositionIntData : RobotPositionData<int> {
    // Creates a blank instance of RobotPositionIntData
    public RobotPositionIntData()

    // Creates a new instance of RobotPositionIntData with the specified header information.
    public RobotPositionIntData(RobotDataHeader header)
}
```

**Members of HighSpeedEServer.RobotPositionDataType**
```csharp
public enum RobotPositionDataType {
    // Position in base coordinate system (world frame, value 16).
    BaseCoordinateValue = 16

    // Position in encoder pulse values (joint space).
    PulseValue = 0

    // Position in robot coordinate system (robot base frame, value 17).
    RobotCoordinateValue = 17

    // Position in tool coordinate system (value 18).
    ToolCoordinateValue = 18

    // Position in user-defined coordinate system (value 19).
    UserCoordinateValue = 19
}
```

**Members of HighSpeedEServer.RobotBasePositionData**
```csharp
public class RobotBasePositionData : RobotAxisRawData<int> {
    // Creates a new instance of RobotBasePositionData with the specified header information.
    public RobotBasePositionData(RobotDataHeader header)

    // Gets the data type indicating whether values are pulse or coordinate values.
    public RobotBasePositionType DataType { get; }
}
```

**Members of HighSpeedEServer.RobotExternalAxisData**
```csharp
public class RobotExternalAxisData : RobotAxisRawData<int> {
    // Creates a new instance of RobotExternalAxisData from a generic RobotAxisRawData.
    // Used for conversion from generic types to specific types.
    public RobotExternalAxisData(RobotAxisRawData<int> source)

    // Creates a new instance of RobotExternalAxisData with the specified header information.
    public RobotExternalAxisData(RobotDataHeader header)
}
```