Reading & Writing Global Variables
On this page :
Learn how to read and manage global variables (program and installation) in the Universal Robots controller using the Dashboard Server Protocol and Primary Interface. Explore the GlobalVariable class, handle variable events, and test it with a Windows example.
🌍 What’s a Variable in This Chapter?
In this chapter, when we say "variable," we’re talking about global variables in the UR controller. These can be program variables or installation variables.
In our library, regardless of the protocol used to handle variables, they are represented by the GlobalVariable
class, which contains both the name and value of the variable.
This class extends GlobalVariableValue
, which only holds the value. Simple, right? 🚀
📡 Reading Variables with Dashboard
The Dashboard Server Protocol lets you query the robot for a variable’s value.
The function that makes this magic happen is GetVariable
. It returns a GlobalVariableValue
object containing the name and value of the variable. The type? It’s automatically estimated from the value! 🧠✨
using UnderAutomation.UniversalRobots;using UnderAutomation.UniversalRobots.Common;class DashboardVariable{static void Main(string[] args){var robot = new UR();// Dashboard is enabled by defaultrobot.Connect("192.168.0.1");/**/// Read variable called "myVar"var request = robot.Dashboard.GetVariable("myVar");if (request.Succeed){GlobalVariable variable = request.Value;// variable.Name// variable.Type// variable.Value// variable.ToMatrix();// variable.ToFloat();// variable.To...();}/**/}}
🔄 Reading Variables with Primary Interface
The Primary Interface is a bit more dynamic—it raises an event whenever a variable’s value changes or when the variable list is updated.
This means the library always keeps track of the latest received values for each variable, so you can access their name, type, and value at any time. 🚦
⚠ Heads-up! If your application connects while a program is running, you’ll only get the list of variables when a change occurs.
That means the list might initially be empty—because the Primary Interface only reacts to updates! ⏳
💡 Good to know: The library adapts to different controller firmware versions.
Since version 3.2, and later 5.9, the way variables are decoded has changed. But don’t worry—it’s completely seamless for developers! 🎩✨
Two events help you stay updated:
- 📝
ListUpdated
→ Triggered when the variable list changes. - 🔔
ValuesUpdated
→ Triggered when a variable’s value changes.
The Primary Interface has a GlobalVariables
property, which gives you access to the list of variables and the associated events.
using UnderAutomation.UniversalRobots;using UnderAutomation.UniversalRobots.Common;using UnderAutomation.UniversalRobots.PrimaryInterface;class PrimaryInterfaceVariable{static void Main(string[] args){var robot = new UR();// Primary Interface is enabled by defaultrobot.Connect("192.168.0.1");/**/// Be notified when the list of variables changesrobot.PrimaryInterface.GlobalVariables.ListUpdated += GlobalVariables_ListUpdated;// Be notified when at least one variable changes valuerobot.PrimaryInterface.GlobalVariables.ValuesUpdated += GlobalVariables_ValuesUpdated;// Get the list of all variablesGlobalVariable[] variables = robot.PrimaryInterface.GlobalVariables.GetAll();if (variables.Length == 0) return;// Get first variablevar variable = variables[0];// Get variable namestring name = variable.Name;// Get variable generic valueobject value = variable.Value;// Get its value according to its typeswitch (variable.Type){case GlobalVariableTypes.None:// Variable is empty or doesn't existbreak;case GlobalVariableTypes.String:string stringValue = variable.ToString();break;case GlobalVariableTypes.List:GlobalVariableValue[] listValue = variable.ToList();break;case GlobalVariableTypes.Pose:Pose poseValue = variable.ToPose();break;case GlobalVariableTypes.Bool:bool boolValue = variable.ToBool();break;case GlobalVariableTypes.Int:int intValue = variable.ToInt();break;case GlobalVariableTypes.Float:float floatValue = variable.ToFloat();break;case GlobalVariableTypes.Matrix:System.Array matrixValue = variable.ToMatrix();break;}/**/}private static void GlobalVariables_ValuesUpdated(object sender, GlobalVariablesEventArgs e){// Get new list of variable valuesGlobalVariable[] variables = e.Variables;}private static void GlobalVariables_ListUpdated(object sender, GlobalVariablesEventArgs e){// Get new list of variable valuesGlobalVariable[] variables = e.Variables;}}
✍️ Writing Variables
Unfortunately, writing a variable while a program is running is not possible. 😕
This is because none of the protocols exposed by UR controllers allow it.
That said, you can send a script like this:
global myVar = 12
🛠️ The Hidden Trick 🤫
However, be warned—this will stop the currently running program! ❌
So, if you don’t have an active program and just need to set an installation variable, this method works perfectly.
For example, you could initialize an environment variable before starting your program.
Here’s a little undocumented trick straight out of nowhere:
To ensure the new installation variable value is actually saved, you need to set _hidden_verificationVariable = 0
.
Why? Because this is how Polyscope gets notified of the change! 🧙♂️✨
This is an example of how to set the environment variable i_var_1 = 6
:
_robot.PrimaryInterface.Script.Send(@"def SetInstallationVariable():global _hidden_verificationVariable=0global i_var_1=6end");
🚫 No Global Variable Writing in a Secondary Program
Another limitation to keep in mind: writing to global variables is not allowed within a secondary program.
So, if you need to modify variables dynamically, you might need to rethink your approach! 🤔
🔄 Best Practices for Exchanging Values with Your Program
Variables are super handy for sending data from the robot to your software.
As mentioned earlier, when using the Primary Interface, you need to be connected right from the start of program execution to track variables.
But what if you need real-time communication during execution? 🤨
For high-frequency read/write operations, registers are the way to go! 🚀
- Registers are designed for general-purpose usage.
- They can be read and written at high speed using RTDE and/or Primary Interface.
So, if you're looking for seamless live data exchange, use registers instead of standard variables! 🏎️💨
🖥️ Try It Out with the Windows Example!
Want to see this in action? A special WinForms page lets you test variable reading live! 🎮
👉 Download it here and give it a spin!
📚 Available Classes
Here are the key classes you'll be working with:
Members of Common.GlobalVariable :public class GlobalVariable : GlobalVariableValue {// Variable namepublic string Name { get; }// Last time the variable was sampledpublic TimeSpan Time { get; }}
public class GlobalVariableValue {// Estimate variable value from its string representationpublic 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 falsepublic 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 NaNpublic 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 0public int ToInt()// Returns an array of GlobalVariableValue if Type is List. Else, null is returnedpublic 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 countpublic Array ToMatrix()// Returns a Pose if Type is Pose. Else, null is returnedpublic Pose ToPose()// Returns a string that describes the variable, regardless of its typepublic override string ToString()// Type of a variablepublic GlobalVariableTypes Type { get; }// Value of the variablepublic object Value { get; }}
public enum GlobalVariableTypes {// Variable value is boolBool = 4// Variable value is floatFloat = 6// Variable value is intInt = 5// Variable value is an array : GlobalVariableValue[]List = 2// Variable value is a matrixMatrix = 7// Variable value is null, the value has not been assigned yetNone = 0// Variable value is a UnderAutomation.UniversalRobots.PosePose = 3// Variable value is a System.StringString = 1}
public class GlobalVariables {// Indicates which decoder is used used to read variables according to firmware versionpublic GlobalVariablesFirmwareVersion FirmwareVersion { get; }// Returns a list of all variables declared in the robotpublic GlobalVariable[] GetAll()// Get a variable by its name. Null is returned if the variable doesn't existpublic GlobalVariable GetByName(string name)// Event raised whan the variable list changed. For example, after a program startspublic event EventHandler<GlobalVariablesEventArgs> ListUpdated// Return a string where each line contains type, name and variable valuepublic override string ToString()// Event raised at 10Hz when variable values are updatedpublic event EventHandler<GlobalVariablesEventArgs> ValuesUpdated}
Now you’re all set to handle variables like a pro! 🚀 Happy coding! 🎉