Skip to content

API guide

Send NMT commands, write objects and walk a drive through the CiA 402 state machine, with code that compiles and is checked byte for byte.

The shape of the API

Everything is in namespace hf::vortex; include vortex/VortexHostSession.hpp (it includes the others).

Type What it is
VortexHostSession The client: sendNmt(), sdoDownloadExpedited(), transport()
VortexCanTransport The interface you implement: bool send(const VortexCanFrame&)
VortexCanFrame id, dlc, extended, rtr, data[8], pad()
NmtCommand NMT command bytes (VortexProtocol.hpp)
kIdx*, kSub* Object indices (table)

Every call returns bool: true when your transport accepted the frame. Nothing waits for the node.

Create a session

hf::vortex::VortexHostSession session(transport);   // transport: your VortexCanTransport

The session stores a reference: keep the transport alive as long as the session. It is cheap to create one per use.

NMT

sendNmt(command, node) sends a two-byte NMT frame. Node 0 addresses every node on the bus.

bool reset_and_start_all(VortexHostSession& session) {
    constexpr std::uint8_t kAllNodes = 0;                          // NMT broadcast
    return session.sendNmt(NmtCommand::ResetCommunication, kAllNodes) &&
           session.sendNmt(NmtCommand::Start, kAllNodes);
}
Command Node goes to
NmtCommand::Start Operational: PDOs run
NmtCommand::Stop Stopped: only NMT and heartbeat
NmtCommand::PreOperational Pre-operational: SDO works, PDOs do not
NmtCommand::ResetNode Full application reset, then boot-up
NmtCommand::ResetCommunication Communication objects reset, then boot-up

Write an object (SDO download)

sdoDownloadExpedited(node, index, sub, value, size) writes size bytes (1, 2 or 4) of value, low byte first. Any other size returns false without sending.

bool write_objects(VortexHostSession& session, std::uint8_t node) {
    // 1 byte: modes of operation 0x6060:00 = 1 (profile position)
    if (!session.sdoDownloadExpedited(node, kIdxModesOfOperation, 0, 1, 1)) return false;
    // 2 bytes: controlword 0x6040:00 = 0x0006 (CiA 402 Shutdown)
    if (!session.sdoDownloadExpedited(node, kIdxControlword, 0, 0x0006, 2)) return false;
    // 4 bytes: vendor device id 0x2000:01 (placeholder index, see the protocol table)
    return session.sdoDownloadExpedited(node, kIdxVendorDeviceId, kSubVendorDeviceId, 0xCAFE0001u, 4);
}

Use the size of the object's data type in the EDS: UNSIGNED8 / INTEGER8 1, UNSIGNED16 / INTEGER16 2, UNSIGNED32 / INTEGER32 4. Pass negative values as their two's-complement bit pattern.

Enable a CiA 402 drive

A CiA 402 drive moves through its power state machine when you write commands to the controlword (0x6040) and reports its state in the statusword (0x6041):

stateDiagram-v2
    [*] --> SwitchOnDisabled: power on, initialisation
    SwitchOnDisabled --> ReadyToSwitchOn: Shutdown 0x0006
    ReadyToSwitchOn --> SwitchedOn: Switch on 0x0007
    SwitchedOn --> OperationEnabled: Enable operation 0x000F
    OperationEnabled --> SwitchedOn: Disable operation 0x0007
    OperationEnabled --> QuickStopActive: Quick stop 0x0002
    QuickStopActive --> SwitchOnDisabled: Disable voltage 0x0000
    OperationEnabled --> Fault: fault
    Fault --> SwitchOnDisabled: Fault reset 0x0080

Disable voltage (0x0000) also returns Ready to switch on and Switched on to Switch on disabled.

// CiA 402 controlword commands (CiA 402 part 2, controlword 0x6040).
constexpr std::uint16_t kCwShutdown = 0x0006;         // -> Ready to switch on
constexpr std::uint16_t kCwSwitchOn = 0x0007;         // -> Switched on
constexpr std::uint16_t kCwEnableOperation = 0x000F;  // -> Operation enabled
constexpr std::uint16_t kCwFaultReset = 0x0080;       // Fault -> Switch on disabled (rising edge of bit 7)

bool enable_drive(VortexHostSession& session, std::uint8_t node) {
    for (std::uint16_t cw : {kCwFaultReset, kCwShutdown, kCwSwitchOn, kCwEnableOperation}) {
        if (!session.sdoDownloadExpedited(node, kIdxControlword, 0, cw, 2)) {
            return false;
        }
        // The session sends only: confirm each state in the statusword (0x6041) with your
        // own SDO upload or a TPDO before sending the next command.
    }
    return true;
}

Select the mode of operation (0x6060) before enabling: 1 profile position, 3 profile velocity, 4 profile torque, 6 homing, 8 / 9 / 10 cyclic synchronous position / velocity / torque, as far as the device supports them.

What the session does not do

Need Use
SDO response or abort, expedited SDO upload (read an object) Your CAN receive path with CanOpen::ParseSdoResponse() from CanOpenExtras.h in hf-utils-canopen
Drive state from the statusword CanOpen::DecodeStatusWord() and CanOpen::NextControlWord() in the same header
Heartbeat, EMCY CanOpen::ParseHeartbeat(), CanOpen::ParseEmergency() in the same header
Segmented or block SDO (more than 4 bytes), PDO, SYNC A CANopen stack (CANopenNode); configure the node's PDO mapping with SDO writes
Retries and timeouts Your application: the session sends once

Next: Porting.