diff --git a/README.md b/README.md
index 2eb30ed3d16f0f4c8fc55bb645c6a3066606604d..7e08802df3f1a5d7f98dd998c3913889b682bdf7 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,138 @@
-# abb?egm?instructions
+# ABB EGM instructions
+
+## Requirements
+
+- A robot with a license to run EGM and with EGM installed
+- Python
+- Protobuf
+
+## Setup
+
+You can skip this and "Unpack and work" from the included [rspag file](./robotstudio/EGM_Example_IRB14000_RW608.rspag) if you just need a station to test things with.
+
+### Setup on controller
+
+#### 1. Install RobotStudio
+
+A zip file containing the installer can be [downloaded from ABB's website](https://new.abb.com/products/robotics/robotstudio/downloads).
+
+Extract the zip and run `RobotStudio 20XX.X\RobotStudio\setup.exe`.
+
+Either skip providing a license, or get one from `vm05.cs.lth.se` if you are
+connected to LU's network.
+
+#### 2. Install RobotWare
+
+Install RobotWare by going to the Add-in tab on the ribbon in Robotstudio and selecting:
+
+1. RobotApps
+1. Gallery
+1. RobotWare for IRC5
+1. Select version
+1. Add
+
+![Install RobWare](./imgs/02_install_robware.png)
+
+#### 3. Create or connect to controller
+
+##### 3A. Create a (virtual) station
+
+Create new station using desired robot, start from the File button in the ribbon.
+
+If you are using a real controller, skip to next step.
+
+1. New
+1. Solution with Station and Virtual Controller
+1. Give the solution a name and location
+1. Select controller version (6.13.01)
+1. Select desired robot, tick Customize options
+1. Press create.
+1. Add the EGM option found under Change Options > Engineering Tools.
+
+![Create station](./imgs/03A1_create_station.png)
+![Add EGM option](./imgs/03A2_options.png)
+
+##### 3B. Connect to a real controller
+
+Skip this step if you are using a virtual station.
+
+Start from the Controller ribbon.
+
+1. Add Controller
+2. Add Controller...
+3. Fill in IP and press refresh.
+
+If you are connected to the service port you should be able to use "One-click Connect"
+
+![Connect to real controller](./imgs/03B1_connect.png)
+
+#### 4. Set up address and ports to listen on
+
+Add UCDevice (ip and port of your computer) by:
+
+1. Open the Controller tab in the ribbon.
+1. Select Configuration
+1. Select Communication
+1. Select Transmission protocol
+1. Right-click in the main window
+1. Press New transmission protocol
+1. Define your UCDevice
+   1. Give it a name
+   1. Set type to UDPUC
+   1. Set remote address to 127.0.0.1 (localhost)
+   1. Change remote port to something suitable (e.g. 6510)
+
+If you are using the Yumi you need to add one UCDevice for each arm.
+
+![Open configuration, communication](./imgs/04_1_open_syspar.png)
+![Edit protocol](./imgs/04_2_edit_protocols.png)
+![Edit protocol form](./imgs/04_3_edit_protocols_form.png)
+
+#### 5. Restart controller
+
+Restart controller to load new system parameters.
+
+![Restart controller](./imgs/05_restart_controller.png)
+
+#### 6. Set up RAPID code
+
+1. Remove `Module1.mod` to T_ROB_L and/or T_ROB_R.
+   ![Remove module](./imgs/06_1_delete_module.png)
+1. Add `rapid/egm_example_yumi.mod` to T_ROB_L and/or T_ROB_R if you are using Yumi.
+   Otherwise use `rapid/egm_example.mod`.
+   ![Load module](./imgs/06_2_load_module.png)
+1. Modify the `egm_setup.mod` to refer to the correct UCDevice.
+   ![Edit values in module](./imgs/06_3_edit_values.png)
+
+### Python environment setup
+
+#### Virtual environment using Anaconda
+
+1. Install [Anaconda](https://www.anaconda.com/products/individual) or [Miniconda](https://docs.conda.io/en/latest/miniconda.html).
+1. Create environment and activate it
+
+```bash
+conda create --name abb_egm python==3.9.10 protobuf # or another python version
+conda activate abb_egm
+```
+
+#### Virtual environment using `virtualenv`
+
+1. Install [Python](https://www.python.org/downloads/).
+1. Create environment, activate it and install dependencies
+
+```bash
+virtualenv venv
+venv/bin/activate  # or source venv/bin/activate on linux
+pip install protobuf
+```
+
+### Install `abb_egm_pyclient`
+
+```bash
+pip install git+https://gitlab.control.lth.se/tetov/abb_egm_pyclient.git
+```
+
+## Usage
+
+See [abb-egm-pyclient usage](https://gitlab.control.lth.se/tetov/abb_egm_pyclient#usage).
diff --git a/imgs/02_install_robware.png b/imgs/02_install_robware.png
new file mode 100644
index 0000000000000000000000000000000000000000..4bde31645e139a84ed63d809ffc9278b9dc953f4
Binary files /dev/null and b/imgs/02_install_robware.png differ
diff --git a/imgs/03A1_create_station.png b/imgs/03A1_create_station.png
new file mode 100644
index 0000000000000000000000000000000000000000..1515cb37aca95c2222ad950f500a1a1453b21ad8
Binary files /dev/null and b/imgs/03A1_create_station.png differ
diff --git a/imgs/03A2_options.png b/imgs/03A2_options.png
new file mode 100644
index 0000000000000000000000000000000000000000..b821c37bd3befed96461e2a35cc4c54a6227c394
Binary files /dev/null and b/imgs/03A2_options.png differ
diff --git a/imgs/03B1_connect.png b/imgs/03B1_connect.png
new file mode 100644
index 0000000000000000000000000000000000000000..e6617bf57e56c215770335890a544cb1ae2f70af
Binary files /dev/null and b/imgs/03B1_connect.png differ
diff --git a/imgs/04_1_open_syspar.png b/imgs/04_1_open_syspar.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2c25554f1399ca9725c1ee9b51d3e3970492818
Binary files /dev/null and b/imgs/04_1_open_syspar.png differ
diff --git a/imgs/04_2_edit_protocols.png b/imgs/04_2_edit_protocols.png
new file mode 100644
index 0000000000000000000000000000000000000000..3da31d8d21f4aa36c0b6482ed86a1a8d77e5058c
Binary files /dev/null and b/imgs/04_2_edit_protocols.png differ
diff --git a/imgs/04_3_edit_protocols_form.png b/imgs/04_3_edit_protocols_form.png
new file mode 100644
index 0000000000000000000000000000000000000000..eea5015d488542f16ddf192042f75abfa5e810e5
Binary files /dev/null and b/imgs/04_3_edit_protocols_form.png differ
diff --git a/imgs/05_restart_controller.png b/imgs/05_restart_controller.png
new file mode 100644
index 0000000000000000000000000000000000000000..c502192446a728a29c400991f8caa2869d9e161c
Binary files /dev/null and b/imgs/05_restart_controller.png differ
diff --git a/imgs/06_1_delete_module.png b/imgs/06_1_delete_module.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0b757ddd57bbfd5620afb9d7919ff65ea49cca9
Binary files /dev/null and b/imgs/06_1_delete_module.png differ
diff --git a/imgs/06_2_load_module.png b/imgs/06_2_load_module.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c0d26520e3fa9db520b9ed9fcabc659070fa981
Binary files /dev/null and b/imgs/06_2_load_module.png differ
diff --git a/imgs/06_3_edit_values.png b/imgs/06_3_edit_values.png
new file mode 100644
index 0000000000000000000000000000000000000000..f70de3afb8f5669ac2e82d15be94247884e4bb5d
Binary files /dev/null and b/imgs/06_3_edit_values.png differ
diff --git a/rapid/egm_example.mod b/rapid/egm_example.mod
new file mode 100644
index 0000000000000000000000000000000000000000..77c7b728383711162b049f67fed83912e58cde41
--- /dev/null
+++ b/rapid/egm_example.mod
@@ -0,0 +1,95 @@
+MODULE EGM_test
+
+    ! egm setup
+    CONST string s_external_motion_interface_name:="default";
+    CONST string s_udpuc_device:="EGM"; 
+
+    ! Change to flip between act joint and act pose
+    CONST bool bool_pose := TRUE;
+
+    VAR egmident egmID1;
+    VAR egmstate egmSt1;
+
+    ! limits for cartesian convergence: +-1 mm
+    CONST egm_minmax egm_minmax_lin1:=[-1,1];
+    ! limits for orientation convergence: +-2 degrees
+    CONST egm_minmax egm_minmax_rot1:=[-2,2];
+    ! limits for joint position convergence: +-0.5 degrees
+    CONST egm_minmax egm_minmax_joint:=[-0.5,0.5];
+
+    ! Start position
+    CONST jointtarget jpos10:=[[0,0,0,0,40,0],[9E+09,9E+09,9E+09,9E+09,9E+09,9E+09]];
+    ! Used tool
+    LOCAL PERS tooldata t_EGM_example1:=[TRUE,[[12.3313,-0.108707,416.142],[0.903899,-0.00320735,0.427666,0.00765917]],[2.6,[-111.1,24.6,386.6],[1,0,0,0],0,0,0.072]];
+
+    ! corr-frame: wobj, sens-frame: wobj
+    LOCAL PERS wobjdata wobj_EGM_example1:=[FALSE,TRUE,"",[[150,1320,1140],[1,0,0,0]],[[0,0,0],[1,0,0,0]]];
+    ! Correction frame offset: none
+    VAR pose corr_frame_offs:=[[0,0,0],[1,0,0,0]];
+
+
+
+    PROC main()
+        ! Move to start position. Fine point is required.
+        MoveAbsJ jpos10\NoEOffs,v50,fine,tool0;
+
+        EGMReset egmID1;
+        EGMGetId egmID1;
+        egmSt1:=EGMGetState(egmID1);
+        TPWrite "EGM state: "\Num:=egmSt1;
+
+        IF bool_pose THEN
+            p_egm_pose_world;
+        ELSE 
+            p_egm_joint;
+        ENDIF
+            
+        egmSt1:=EGMGetState(egmID1);
+        IF egmSt1=EGM_STATE_CONNECTED THEN
+            TPWrite "Reset EGM instance egmID1";
+            EGMReset egmID1;
+        ENDIF
+    ENDPROC
+
+    PROC p_egm_pose_world()
+        IF egmSt1<=EGM_STATE_CONNECTED THEN
+            ! Set up the EGM data source
+            EGMSetupUC ROB_ID, egmID1, s_external_motion_interface_name, s_udpuc_device
+              \pose\CommTimeout:=60;
+        ENDIF
+        
+        ! Correction frame is the World coordinate system and the sensor measurements are relative to the WCS as well
+        EGMActPose egmID1 EGM_FRAME_WORLD, tool0.tframe, EGM_FRAME_WORLD
+            \x:=egm_minmax_lin1 \y:=egm_minmax_lin1 \z:=egm_minmax_lin1
+            \rx:=egm_minmax_rot1 \ry:=egm_minmax_rot1 \rz:=egm_minmax_rot1
+            \Tool:=tool0,corr_frame_offs,
+            \LpFilter:=20;
+        ! Run: the convergence condition has to be fulfilled during 10 seconds before RAPID execution continues to the next instruction
+        EGMRunPose egmID1,EGM_STOP_HOLD
+          \x\y\z
+          \Rx\Ry\Rz
+          \CondTime:=10\RampInTime:=0.05;
+
+    ENDPROC
+
+    PROC p_egm_joint()
+        IF egmSt1<=EGM_STATE_CONNECTED THEN
+            EGMSetupUC ROB_ID, egmID1, s_external_motion_interface_name, s_udpuc_device
+              \joint \CommTimeout:=60;
+        ENDIF
+
+        EGMActJoint egmID1
+                \J1:=egm_minmax_joint
+                \J2:=egm_minmax_joint
+                \J3:=egm_minmax_joint
+                \J4:=egm_minmax_joint
+                \J5:=egm_minmax_joint
+                \J6:=egm_minmax_joint
+                \MaxSpeedDeviation:=100;
+
+        EGMRunJoint egmID1, EGM_STOP_HOLD
+                \J1 \J2 \J3 \J4 \J5 \J6
+                \CondTime:=60
+                \RampInTime:=0.05;
+    ENDPROC
+ENDMODULE
diff --git a/rapid/egm_example_yumi.mod b/rapid/egm_example_yumi.mod
new file mode 100644
index 0000000000000000000000000000000000000000..797fc6a53a4b4743defc4e97613a5315c9fadb7e
--- /dev/null
+++ b/rapid/egm_example_yumi.mod
@@ -0,0 +1,96 @@
+MODULE EGM_test
+
+    ! egm setup
+    CONST string s_external_motion_interface_name:="default";
+    CONST string s_udpuc_device:="EGM_ROB_L"; 
+
+    ! Change to flip between act joint and act pose
+    CONST bool bool_pose := TRUE;
+
+    VAR egmident egmID1;
+    VAR egmstate egmSt1;
+
+    ! limits for cartesian convergence: +-1 mm
+    CONST egm_minmax egm_minmax_lin1:=[-1,1];
+    ! limits for orientation convergence: +-2 degrees
+    CONST egm_minmax egm_minmax_rot1:=[-2,2];
+    ! limits for joint position convergence: +-0.5 degrees
+    CONST egm_minmax egm_minmax_joint:=[-0.5,0.5];
+
+    ! Start position
+    CONST jointtarget jpos10:=[[0,0,0,0,40,0],[30,9E+09,9E+09,9E+09,9E+09,9E+09]];
+    ! Used tool
+    LOCAL PERS tooldata t_EGM_example1:=[TRUE,[[12.3313,-0.108707,416.142],[0.903899,-0.00320735,0.427666,0.00765917]],[2.6,[-111.1,24.6,386.6],[1,0,0,0],0,0,0.072]];
+
+    ! corr-frame: wobj, sens-frame: wobj
+    LOCAL PERS wobjdata wobj_EGM_example1:=[FALSE,TRUE,"",[[150,1320,1140],[1,0,0,0]],[[0,0,0],[1,0,0,0]]];
+    ! Correction frame offset: none
+    VAR pose corr_frame_offs:=[[0,0,0],[1,0,0,0]];
+
+
+
+    PROC main()
+        ! Move to start position. Fine point is required.
+        MoveAbsJ jpos10\NoEOffs,v50,fine,tool0;
+
+        EGMReset egmID1;
+        EGMGetId egmID1;
+        egmSt1:=EGMGetState(egmID1);
+        TPWrite "EGM state: "\Num:=egmSt1;
+
+        IF bool_pose THEN
+            p_egm_pose_world;
+        ELSE 
+            p_egm_joint;
+        ENDIF
+            
+        egmSt1:=EGMGetState(egmID1);
+        IF egmSt1=EGM_STATE_CONNECTED THEN
+            TPWrite "Reset EGM instance egmID1";
+            EGMReset egmID1;
+        ENDIF
+    ENDPROC
+
+    PROC p_egm_pose_world()
+        IF egmSt1<=EGM_STATE_CONNECTED THEN
+            ! Set up the EGM data source
+            EGMSetupUC ROB_ID, egmID1, s_external_motion_interface_name, s_udpuc_device
+              \pose \CommTimeout:=60;
+        ENDIF
+        
+        ! Correction frame is the World coordinate system and the sensor measurements are relative to the WCS as well
+        EGMActPose egmID1
+            EGM_FRAME_WORLD, tool0.tframe, EGM_FRAME_WORLD
+            \x:=egm_minmax_lin1 \y:=egm_minmax_lin1 \z:=egm_minmax_lin1
+            \rx:=egm_minmax_rot1 \ry:=egm_minmax_rot1 \rz:=egm_minmax_rot1
+            \Tool:=tool0,corr_frame_offs
+            \LpFilter:=20;
+        ! Run: the convergence condition has to be fulfilled during 10 seconds before RAPID execution continues to the next instruction
+        EGMRunPose egmID1,EGM_STOP_HOLD 
+          \x \y \z \Rx \Ry \Rz
+          \CondTime:=10 \RampInTime:=0.05;
+
+    ENDPROC
+
+    PROC p_egm_joint()
+        IF egmSt1<=EGM_STATE_CONNECTED THEN
+            EGMSetupUC ROB_ID, egmID1, s_external_motion_interface_name, s_udpuc_device
+              \joint \CommTimeout:=60;
+        ENDIF
+
+        EGMActJoint egmID1
+                \J1:=egm_minmax_joint
+                \J2:=egm_minmax_joint
+                \J3:=egm_minmax_joint
+                \J4:=egm_minmax_joint
+                \J5:=egm_minmax_joint
+                \J6:=egm_minmax_joint
+                \J7:=egm_minmax_joint
+                \MaxSpeedDeviation:=100;
+
+        EGMRunJoint egmID1, EGM_STOP_HOLD
+                \J1 \J2 \J3 \J4 \J5 \J6 \J7
+                \CondTime:=60
+                \RampInTime:=0.05;
+    ENDPROC
+ENDMODULE
diff --git a/robotstudio/EGM_Example_IRB14000_RW608.rspag b/robotstudio/EGM_Example_IRB14000_RW608.rspag
new file mode 100644
index 0000000000000000000000000000000000000000..ac71062e56fa2d910c56d61bcec0e2e13b1e0939
Binary files /dev/null and b/robotstudio/EGM_Example_IRB14000_RW608.rspag differ