Generic serial controller
Last updated
Last updated
This guide explains how to configure and interface a custom motion controller using serial communication. It is intended for users integrating third-party hardware or building DIY motion systems.
⚠️ This system cannot drive arbitrary hardware. You must know and understand the serial protocol expected by your controller (message structure, command format, required timing, etc.).
Start by creating a new Generic serial output
Assign the serial port and enter the Serial commands configuration
Configure the serial port settings to match your controller:
Baudrate
Common values include 9600, 38400, 115200, etc.
Stop bits
Usually set to 1
or 2
, depending on the controller.
Data bits
Typically set to 8
.
Parity
Choose None
, Even
, or Odd
as required.
Idle delay after serial port opening (ms)
Optional delay (in milliseconds) after opening the serial port before sending any commands. Useful if the device needs time to initialize.
RTS / DTR
Enable these only if your device requires them to boot or begin communication.
Many modern USB-to-serial chips require RTS to be enabled, and sometimes DTR as well, to properly trigger communication. However, on older usb to serial-based Arduino boards, enabling DTR will often cause the board to reset on connection, making it temporarily unavailable. In such cases, you can use the Idle delay after serial port opening setting to allow the device enough time to fully reboot before any data is sent.
Set how many axis values you want to send to the controller (e.g., 6 for a 6DOF platform).
Choose how axis values are formatted in the serial output:
Binary – Sent as raw bytes (e.g., 0xFF
)
Decimal (string) – Sent as text (e.g., "127"
)
Hex (string) – Sent as hex-encoded string (e.g., "7F"
)
Defines the numeric resolution of each axis. It affects the value range produced by placeholders like <Axis1>
. Choosing a higher resolution than the hardware is capable of improves flexibility and avoids future limitations.
8
0–255
127
1 byte
Only if your hardware explicitly requires 8-bit values
10
0–1023
511
2 bytes
Arduino analog resolution; reasonable for smooth control
12
0–4095
2047
2 bytes
Balanced precision and compatibility
16
0–65535
32767
2 bytes
Recommended for most new custom protocols
Recommendation: Use 16-bit resolution if you're designing your own firmware/protocol. The slight increase in data size is worth the precision and ease of scaling.
You can define different messages for three phases of controller activity:
Startup Commands
Sent once when the controller is activated (e.g., SimHub motion connects, axis test starts). Often used for initialization.
Motion Update Commands
Sent repeatedly during active motion updates (typically once per frame).
Shutdown Commands
Sent once when the controller is deactivated (e.g., SimHub disconnects, test stops). Used for safe shutdown or reset.
When are commands Sent?Commands are only transmitted when the controller is actively in use:
When axis assignation testing is running
When manual control is enabled
When game effects are running
If none of these are active, no commands are sent, even if the serial port remains open.
SimHub may keep the port open while idle to:
Prevent other applications from taking control of the port
Allow for faster resume when motion is reactivated
No serial data is sent during this idle period.
Each command allows:
A customizable command string
An optional delay (in ms)
An optional "wait for response" before continuing
Use placeholders to insert dynamic data into your command strings.
Axis values represent the computed actuator positions based on platform geometry and motion logic. The controller configuration in SimHub is purely focused on communication — it does not define or interpret motion roles (like surge, heave, or roll).
Each controller simply receives the values it is told to expect. The actual computation of those values is handled earlier in the SimHub pipeline using the platform's geometry and motion profile.
Use the following placeholders to represent axis values:
<Axis1>
, <Axis1a>
, <Axis1b>
, etc.
Repeat for each axis (e.g., <Axis2>
, <Axis3>
) depending on your axis count.
These placeholders reflect the current values of each axis in the selected output format (binary, decimal, or hex). They are case sensitive.
Two additional shortcuts for compatibility with other software exits
<Left>
shorthand for <Axis1>
<Right>
shorthand for <Axis2>
You can insert literal byte values using the following syntax:
Decimal byte: <13>
(carriage return)
Hex byte: <0xFF>
(sync byte, for example)
These are inserted as raw bytes in binary output.
If you define settings via the Protocol Control Panel (see below), you can insert them using:
For example:
<0xFF>Update<Axis1><Axis2><13>
Sends a sync byte, followed by axis 1 and 2 values, then a carriage return
Start<0xAA><0xBB>
Typical startup command using two header bytes
Stop<10><13>
Ends with a line feed and carriage return
You can define custom parameters that can be reused in your messages.
Available UI elements:
Textbox – Free-form text
Checkbox – Boolean flag
Slider – Range selector
Combobox – Dropdown menu
Group – Logical grouping
Computed setting – Dynamically calculated values
Use these with the <Setting,...>
placeholder.
Use higher resolution (12 or 16 bits) for most new projects
When debugging:
Use a serial monitor (e.g., RealTerm, Arduino Serial Monitor) for early testing
Include start/sync bytes if your controller expects a specific header
Use delays if your controller cannot handle rapid updates
Avoid optimizing for byte count too early; prioritize readability and compatibility
* We’ve used Free Serial Analyzer for years (in its paid version); the free version (with some limitations) is often sufficient for basic troubleshooting.
This feature does not automatically adapt to unknown hardware
You must:
Know your controller’s protocol
Understand its timing and formatting
Test it manually during development
Failure to configure the protocol correctly may result in no movement or unexpected behavior.
Once the protocol configured, you can assign and test the axis with "Axis assignment"
For deeper diagnostics, consider using a serial sniffer to observe actual communication without interfering (e.g., * )