Moving Robots via Modbus

From CPR Wiki

The Modbus interface provides several ways to move a robot to desired positions.

This article explains what registers and coils need to be set and how position values need to be converted.

Direct positioning (Move-To)

This is a simple approach for moving the robot to a target position. Queueing positions is not supported; if you send another position while the robot is still moving it will stop, then start the next motion.

The following motion types are supported:

  • Linear motion to cartesian position
  • Linear motion relative to the base or tool coordinate system
  • Joint motion
  • Relative joint motion


The approach for sending a position and starting a motion is the same for all types:

  1. Write the position values to the holding registers
  2. Write the motion velocity to holding register 180
  3. Write a rising edge (0, then 1) to the coil to start the motion

The register and coil addresses depend on the motion type, you will find them in the user manual.


In this example we want to move a gantry robot to the following position: X=100mm, Y=200mm, Z=-40mm, A=0°, B=0°, C=180°

The cartesian position needs to be written to holding registers 130-135, the cartesian orientation to holding registers 136-141. To provide enough accuracy for far positions each position value is first multiplied with a unit factor of 100, then split across two 16-bit registers:

   32-bit value for X: 100mm x 100 = 10000
   Lower register for X (130) = 10000
   Upper register for X (131) = 0

The upper register is always 0 for positions between 0 and 655mm or 655° (maximum register value 65535). Bigger or negative values need to be split binarily. In this example the Z position is negative:

   Value for Z: -40mm x 100 = -4000
   Convert to an unsigned 32 bit number then split it across both registers:
   Lower register for Z (134): 65535 - 4000 = 61535
   Upper register for Z (135): 65535 - 0 = 65535
   Positions smaller than -655mm or -655° will overflow into the upper register and need to be handled accordingly.

The requested velocity must written to holding register 180. For cartesian motions the value is set as a multiple of 0.1mm/s, for joint motions it is a multiple of 0.1% (relative to the maximum velocity), so use factor 10. The actual motion speed also depends on the global override value (holding register 187).

   In our case we want to move with 120mm/s:
   Velocity register (180): 120 x 10 = 1200

Cartesian motions are started by creating a rising edge on coil 100.

   Write a 0 to coil 100, then write a 1.
   If you prefer you can reset the coil to 0 after writing the 1, this way you will need one write less before triggering the motion.

Using a robot program via variables

This approach is useful for more complex motions and for sending motion parameters to a robot program.


  1. Create and load a robot program that moves to a variable position (Linear-by-Variable or Joint-by-Variable). Set it to run in an infinite loop if you want to move to several positions. An example program is shown here.
  2. Write the target position to the Modbus variable that you use in your program, e.g. mb_pos_w1
  3. Start the program (if it is not running already)

This also works for sending positions to more complex programs.


Creating and uploading the program must be done using CPRog/iRC.

Write the position to the Modbus variable of your choice:

   Variable mb_pos_w1 is defined be holding registers 456-472. The first 6 registers define the robot joint values, the next 3 the external joints, then 3 registers for the cartesian position, then 3 for the cartesian orientation. The last register defines the conversion type, this is useful for converting between joint and cartesian positions. Read the user manual for more information.

Unlike the Move-To registers only one register per value is used here with a conversion factor of 10. Set the registers accordingly.

   Example: Linear motion to X=100mm, Y=200mm, Z=-40mm, A=0°, B=0°, C=180°
   Registers 456 - 465 = 0
   Register 466 (X) = 100 x 10 = 1000
   Register 467 (Y) = 200 x 10 = 2000
   Register 468 (Z) = -40 x 10 => 65535 - 400 = 65135
   Register 469 (A) = 0 x 10 = 10
   Register 470 (B) = 0 x 10 = 10
   Register 471 (C) = 180 x 10 = 1800
   Register 472 (conversion type) = 0

Start the program (if it is not running yet).

   Write a rising edge (0, then 1) to coil 122 ("start program").
   Or write 1 to holding register 260 ("program run state").