This article shows how to read the global variables of a Universal Robots cobot from a PC, in C# or Python, and how to change them. A global variable is a program variable or an installation variable of PolyScope. To exchange values with a running program at high speed, use [registers](/universal-robots/documentation/registers) instead.

## Which way to choose

| Way                | Reads                                           | Writes                       | When                                              |
| ------------------ | ----------------------------------------------- | ---------------------------- | ------------------------------------------------- |
| Primary Interface  | Every variable, updated when its value changes  | no                           | Follow the variables of a running program         |
| Dashboard Server   | One variable, by its name, when you ask         | no                           | Read one value from time to time (PolyScope 5)    |
| URScript           |                                                 | yes, the program stops       | Set an installation variable before a program     |
| Registers (RTDE)   | 0 to 47 numbers and 0 to 127 bits               | yes, while the program runs  | Exchange values with a running program            |

In the SDK, a variable is a `GlobalVariable`: its `Name`, its `Type` and its `Value`, with typed conversions (`ToInt`, `ToFloat`, `ToBool`, `ToPose`, `ToList`, `ToMatrix`).

## Read with the Primary Interface

The Primary Interface sends the list of the variables and their new values when they change. `GlobalVariables` keeps the last values received:

- `ListUpdated` is raised when the list of the variables changes;
- `ValuesUpdated` is raised when at least one value changes;
- `GetAll()` and `GetByName()` return the last values received.

**C# : PrimaryInterfaceVariable**
```csharp
using UnderAutomation.UniversalRobots;
using UnderAutomation.UniversalRobots.Common;
using UnderAutomation.UniversalRobots.PrimaryInterface;

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

    // The Primary Interface is enabled by default
    robot.Connect("192.168.0.1");

    /**/
    // Raised when the list of variables changes
    robot.PrimaryInterface.GlobalVariables.ListUpdated += (sender, e) =>
    {
      GlobalVariable[] variables = e.Variables;
    };

    // Raised when at least one value changes
    robot.PrimaryInterface.GlobalVariables.ValuesUpdated += (sender, e) =>
    {
      GlobalVariable[] variables = e.Variables;
    };

    // Last values received
    GlobalVariable[] all = robot.PrimaryInterface.GlobalVariables.GetAll();
    GlobalVariable myVar = robot.PrimaryInterface.GlobalVariables.GetByName("myVar");

    if (myVar != null)
    {
      // Typed value, according to myVar.Type
      switch (myVar.Type)
      {
        case GlobalVariableTypes.Bool: bool b = myVar.ToBool(); break;
        case GlobalVariableTypes.Int: int i = myVar.ToInt(); break;
        case GlobalVariableTypes.Float: float f = myVar.ToFloat(); break;
        case GlobalVariableTypes.String: string s = myVar.ToString(); break;
        case GlobalVariableTypes.Pose: Pose p = myVar.ToPose(); break;
        case GlobalVariableTypes.List: GlobalVariableValue[] l = myVar.ToList(); break;
        case GlobalVariableTypes.Matrix: Array m = myVar.ToMatrix(); break;
      }
    }
    /**/
  }
}
```

**Python : PrimaryInterfaceVariable**
```python
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.common.global_variable_types import GlobalVariableTypes

robot = UR()

# The Primary Interface is enabled by default
robot.connect("192.168.0.1")

##
# Raised when the list of variables changes
def on_list_updated(sender, e):
    variables = robot.primary_interface.global_variables.get_all()

robot.primary_interface.global_variables.list_updated(on_list_updated)

# Raised when at least one value changes
def on_values_updated(sender, e):
    variables = robot.primary_interface.global_variables.get_all()

robot.primary_interface.global_variables.values_updated(on_values_updated)

# Last values received
all_variables = robot.primary_interface.global_variables.get_all()
my_var = robot.primary_interface.global_variables.get_by_name("myVar")

if my_var is not None:
    # Typed value, according to my_var.type
    if my_var.type == GlobalVariableTypes.Bool:
        value = my_var.to_bool()
    elif my_var.type == GlobalVariableTypes.Int:
        value = my_var.to_int()
    elif my_var.type == GlobalVariableTypes.Float:
        value = my_var.to_float()
    elif my_var.type == GlobalVariableTypes.String:
        value = str(my_var)
    elif my_var.type == GlobalVariableTypes.Pose:
        value = my_var.to_pose()
    elif my_var.type == GlobalVariableTypes.List:
        value = my_var.to_list()
##
```

The robot sends the variables when they change. If your application connects while a program runs, the list stays empty until a value changes. The decoding depends on the PolyScope version (up to 3.2, up to 5.9, later versions): the SDK selects it, and `FirmwareVersion` gives it.

## Read with the Dashboard Server

`GetVariable` asks the robot for one variable of the running program. The type is deduced from the value.

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

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

    // The Dashboard Server is enabled by default
    robot.Connect("192.168.0.1");

    /**/
    // Read the variable "myVar" of the running program
    var response = robot.Dashboard.GetVariable("myVar");

    if (response.Succeed)
    {
      GlobalVariable variable = response.Value;

      Console.WriteLine($"{variable.Name} = {variable.Value} ({variable.Type})");

      // Typed conversions, according to variable.Type
      if (variable.Type == GlobalVariableTypes.Pose)
      {
        Pose pose = variable.ToPose();
      }
    }
    /**/
  }
}
```

**Python : DashboardVariable**
```python
from underautomation.universal_robots.ur import UR
from underautomation.universal_robots.common.global_variable_types import GlobalVariableTypes

robot = UR()

# The Dashboard Server is enabled by default
robot.connect("192.168.0.1")

##
# Read the variable "myVar" of the running program
response = robot.dashboard.get_variable("myVar")

if response.succeed:
    variable = response.value

    print(f"{variable.name} = {variable.value} ({variable.type})")

    # Typed conversions, according to variable.type
    if variable.type == GlobalVariableTypes.Pose:
        pose = variable.to_pose()
##
```

## Write a variable

No interface of the robot writes a variable while a program runs. Two ways remain:

### Before the program

Send URScript that sets the variable. The running program stops, so do it when no program runs, for example to initialize an installation variable. To make PolyScope save the new value of an installation variable, also set `_hidden_verificationVariable` to `0`:

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

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

    robot.Connect("192.168.0.1");

    /**/
    // Set the installation variable i_var_1 to 6.
    // The running program stops: send it when no program runs.
    // _hidden_verificationVariable = 0 makes PolyScope save the new value
    robot.PrimaryInterface.Script.Send(
      "def SetInstallationVariable():\n" +
      "  global _hidden_verificationVariable = 0\n" +
      "  global i_var_1 = 6\n" +
      "end\n");
    /**/
  }
}
```

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

robot = UR()

robot.connect("192.168.0.1")

##
# Set the installation variable i_var_1 to 6.
# The running program stops: send it when no program runs.
# _hidden_verificationVariable = 0 makes PolyScope save the new value
robot.primary_interface.script.send(
    "def SetInstallationVariable():\n"
    "  global _hidden_verificationVariable = 0\n"
    "  global i_var_1 = 6\n"
    "end\n")
##
```

A secondary program (`sec`) does not stop the program, but it cannot write a global variable.

### While the program runs

Use the registers: the program reads them with `read_input_integer_register`, `read_input_float_register` or `read_input_boolean_register`, and your application writes them with RTDE up to 500 Hz. See [Read and write registers](/universal-robots/documentation/registers).

## Try it in the demo application

Everything on this page can be tried without writing code, in the **Running program and variables** page of the [demo application](/universal-robots/documentation/demo-app).

![Running program and variables page of the Universal Robots SDK demo application](/universal-robots/WinformsScreenshots/Program.jpg)

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

## Reference

**Members of Common.GlobalVariable**
```csharp
public class GlobalVariable : GlobalVariableValue {
    public GlobalVariable()

    // Determines whether the specified object is a <xref href="UnderAutomation.UniversalRobots.Common.GlobalVariable" data-throw-if-not-resolved="false"></xref> with the same name, type, and value.
    public override bool Equals(object obj)

    // Returns a hash code based on the variable name, type, and value.
    public override int GetHashCode()

    // Variable name
    public string Name { get; }

    // Last time the variable was sampled
    public TimeSpan Time { get; }

    // Returns the variable name followed by its string representation.
    public override string ToString()
}
```

**Members of Common.GlobalVariableValue**
```csharp
public class GlobalVariableValue {
    public GlobalVariableValue()

    // Determines whether the specified object is a <xref href="UnderAutomation.UniversalRobots.Common.GlobalVariableValue" data-throw-if-not-resolved="false"></xref> with the same type and value.
    public override bool Equals(object obj)

    // Returns a hash code based on the variable type and value.
    public override int GetHashCode()

    // Estimate variable value from its string representation
    public static GlobalVariableValue Parse(string message)

    // Returns variable value if type is Bool. Il type is Float or Int, it returns True if value is not 0. Else, it returns false
    public bool ToBool()

    // Returns variable value if type is Float. Il type is int, it casts it to float. If Type is bool, it returns 1 or 0. Else it returns NaN
    public float ToFloat()

    // Returns variable value if type is Int. Il type is Float, it tries to cast it to int. If Type is bool, it returns 1 or 0. Else it returns 0
    public int ToInt()

    // Returns an array of GlobalVariableValue if Type is List. Else, null is returned
    public GlobalVariableValue[] ToList()

    // Return variable value GlobalVariable[,] if variable is a matrix. First dimension is row index and second dimension is column index.
    // Use GetLength(0) to get row number and GetLength(1) to get column count
    public Array ToMatrix()

    // Returns a Pose if Type is Pose. Else, null is returned
    public Pose ToPose()

    // Returns a string that describes the variable, regardless of its type
    public override string ToString()

    // Type of a variable
    public GlobalVariableTypes Type { get; }

    // Value of the variable
    public object Value { get; }
}
```

**Members of Common.GlobalVariableTypes**
```csharp
public enum GlobalVariableTypes {
    // Variable value is bool
    Bool = 4

    // Variable value is float
    Float = 6

    // Variable value is int
    Int = 5

    // Variable value is an array : GlobalVariableValue[]
    List = 2

    // Variable value is a matrix
    Matrix = 7

    // Variable value is null, the value has not been assigned yet
    None = 0

    // Variable value is a UnderAutomation.UniversalRobots.Pose
    Pose = 3

    // Variable value is a System.String
    String = 1
}
```

**Members of PrimaryInterface.GlobalVariables**
```csharp
public class GlobalVariables {
    // Indicates which decoder is used used to read variables according to firmware version
    public GlobalVariablesFirmwareVersion FirmwareVersion { get; }

    // Returns a list of all variables declared in the robot
    public GlobalVariable[] GetAll()

    // Get a variable by its name. Null is returned if the variable doesn't exist
    public GlobalVariable GetByName(string name)

    // Event raised whan the variable list changed. For example, after a program starts
    public event EventHandler<GlobalVariablesEventArgs> ListUpdated

    // Return a string where each line contains type, name and variable value
    public override string ToString()

    // Event raised at 10Hz when variable values are updated
    public event EventHandler<GlobalVariablesEventArgs> ValuesUpdated
}
```

## What to read next

- [Read and write registers](/universal-robots/documentation/registers): exchange values with a running program.
- [Send URScript](/universal-robots/documentation/remote-send-script): the forms of URScript and their effect on the program.