S1 Raw End-Tool Passthrough
The S1 raw end-tool API transports opaque 64-byte frames between an SDK application and the left or right arm TIB.
Choose the Correct Control Path
Use set_gripper_command() for a gripper supported by the standard WBCS controller. That path validates a physical command, sends a managed WBC target, tracks controller state, and can wait for completion.
Use raw passthrough when the application must encode and decode the installed device protocol itself. The raw API does not understand CAN IDs, RS485 messages, gripper status, force values, checksums, or device limits.
Do not use both paths to write the same TIB/end tool concurrently.
Deployment Prerequisites
Before starting the application:
-
Deploy a WBCS version that supports S1 raw end-tool relay.
-
Set the active S1
robot_config.tomlvalue below and restart WBCS:[robot_info.custom_params]
endtool_raw_enabled = true -
Connect and configure the CAN or RS485 device on the expected TIB.
-
Stop or otherwise exclude any production controller that writes to the same end tool.
A successful SDK publish only means that the DDS message was accepted locally. It does not confirm that WBCS relayed the frame or that the TIB or device accepted it.
Data Path
Transmit path:
Application device encoder
-> 64-byte frame
-> send_endtool_raw_frame(side, frame)
-> EndToolRawCommand
-> WBCS raw relay
-> EtherCAT -> TIB -> CAN/RS485 -> device
Receive path:
Device response/report
-> CAN/RS485 -> TIB -> EtherCAT
-> WBCS 1 kHz or 250 Hz receive buffer
-> singorix_proto::EndToolRawData
-> SDK transport callback
-> EndToolRawData
-> application callback and device decoder
GBS 1.18.1 uses two shared DDS topics:
singorix/wbcs/endtool/raw_command
singorix/wbcs/endtool/raw_data
For transmission, the SDK maps the selected side to a logical endpoint_id and uses channel_id=command. On reception it maps endpoint_id/channel_id back to the public side/kind fields.
Transport Contract
| Field | Meaning |
|---|---|
frame | Exactly 64 opaque bytes in the S1 WBCS/TIB layout |
side | The SDK maps LEFT/RIGHT to left_endtool/right_endtool |
kind | The SDK maps BUFFER_1KHZ/BUFFER_250HZ to feedback_1khz/feedback_250hz |
generation | The wire sequence, monotonic within one endpoint/channel stream and publisher lifetime; it may reset after WBCS restarts |
The SDK subscribes to all four combinations of side and receive kind. Every registered callback receives all of them, so the application must filter side and kind before decoding the device payload.
The correct receive kind is device-dependent. For example, the self-developed CAN gripper uses the 250 Hz buffer, while the Kunwei RS485 sensor uses the 1 kHz buffer.
API Workflow
The C++ and Python APIs have the same lifecycle:
robot.init() initializes the S1 raw-passthrough node, one shared receive reader, and one shared transmit writer automatically. If any resource cannot be created, S1 robot initialization returns false. The SDK dispatches the shared topics by endpoint/channel while preserving the public side/kind API.
- Get the S1 robot instance and call
init(). - Register a raw receive callback.
- Encode a device command into exactly 64 bytes.
- Send it to the selected arm/TIB.
- In the callback, filter side/kind and validate the complete device frame before updating application state.
- Unregister the callback before destroying the SDK instance.
Minimal C++ structure:
auto& robot = GalbotRobot::get_instance(MachineType::S1);
if (!robot.init()) {
return 1;
}
const auto handle = robot.register_endtool_raw_callback(
[](const EndToolRawData& data) {
if (data.side != EndToolSide::LEFT ||
data.kind != EndToolRxKind::BUFFER_250HZ) {
return;
}
// Validate and decode data.frame according to the installed device.
});
std::array<uint8_t, END_TOOL_RAW_FRAME_SIZE> frame{};
// Encode the device-specific CAN/RS485 command into frame.
const auto status = robot.send_endtool_raw_frame(EndToolSide::LEFT, frame);
robot.unregister_endtool_raw_callback(handle);
robot.destroy();
Callback and Resource Safety
- Receive callbacks run on middleware threads; applications should still protect shared state for concurrent invocation.
- Keep callbacks short and protect shared state with a mutex or atomics.
- Copy any data that must outlive the callback; do not retain a reference to
EndToolRawData. - An invocation already in progress may finish after
unregister_endtool_raw_callback()returns. Capture shared callback state with safe lifetime ownership. - SDK sends are serialized within one SDK instance, but there is no cross-process or cross-client resource lease.
- A raw writer can still race with a standard gripper controller or another process writing to the same TIB.
Device Adapter Responsibilities
A production adapter should validate more than a frame prefix. Depending on the device, check:
- frame type, CAN/device ID and payload length;
- command/function/response markers;
- checksum or terminator bytes;
- finite numeric values and physical ranges;
- monotonic
generationand stale response time; - unchanged payloads when the generation continues to advance;
- command input ranges before transmission.
Reject malformed frames without replacing the last known valid device state.
Provided Examples
The SDK includes C++ and Python examples for three devices:
| Device | Transport | Receive stream | Behavior |
|---|---|---|---|
| Self-developed two-finger gripper | CAN | 250 Hz | Init/move commands and passive status reports |
| Dahuan two-finger gripper | CAN/Modbus variant | Device-dependent | Configuration, motion and active status polling |
| Kunwei six-axis force sensor | RS485 | 1 kHz | 10 ms active query and force/torque decoding |
See C++ Examples and Python Examples for the complete runnable sources.
Troubleshooting
| Symptom | Checks |
|---|---|
| Publish succeeds but no device action occurs | Confirm [robot_info.custom_params].endtool_raw_enabled=true, restart WBCS, then check endpoint selection and device wiring/power |
| No receive callback | Confirm the expected side/kind, WBCS raw RX logs, device baud rate and response mode |
| Many rejected frames | Check CAN/device ID, response marker, terminator/checksum and byte offsets |
| Generation gaps | The receive consumer or middleware may be overloaded; reduce callback work and logging |
| Generation advances but payload never changes | Check for a frozen sensor/device receive path |
| Intermittent command behavior | Ensure that no standard controller or second client writes to the same TIB |