DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
cia402.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file cia402.h
6 * @brief CiA 402 / IEC 61800-7-201 drive + motion profile (DETWS_ENABLE_CIA402) over CANopen.
7 *
8 * The standardised servo / stepper drive profile: the power state machine (Controlword 0x6040 /
9 * Statusword 0x6041), the Modes of Operation, and the target/actual position-velocity-torque
10 * objects. This is the pure profile layer - the state decode + controlword commands are just
11 * value logic, and the setters/getters wrap the shipped `services/canopen` SDO / PDO codec, so
12 * the CAN stack (ESP32 TWAI or an MCP2515) becomes a motion master. Close the loop with a
13 * `services/control` PID.
14 *
15 * Statusword state masks, Controlword command values, and the object indices are verified against
16 * IEC 61800-7-201 (CiA 402) and multiple drive vendors' state-machine tables. Pure, host-tested.
17 *
18 * @author Douglas Quigg (dstroy0)
19 * @date 2026
20 */
21
22#ifndef DETERMINISTICESPASYNCWEBSERVER_CIA402_H
23#define DETERMINISTICESPASYNCWEBSERVER_CIA402_H
24
25#include "ServerConfig.h"
26
27#if DETWS_ENABLE_CIA402
28
30#include <stddef.h>
31#include <stdint.h>
32
33// --- object dictionary indices (sub-index 0 unless noted); the comment gives the CANopen type ---
34#define CIA402_OD_ERROR_CODE 0x603Fu ///< u16 last error code
35#define CIA402_OD_CONTROLWORD 0x6040u ///< u16 command word (drives the state machine)
36#define CIA402_OD_STATUSWORD 0x6041u ///< u16 status word (reports the state)
37#define CIA402_OD_QUICK_STOP_OPTION 0x605Au ///< i16 quick-stop option code
38#define CIA402_OD_MODES_OF_OPERATION 0x6060u ///< i8 requested mode
39#define CIA402_OD_MODES_DISPLAY 0x6061u ///< i8 active mode (read-back)
40#define CIA402_OD_POSITION_ACTUAL 0x6064u ///< i32 position actual value
41#define CIA402_OD_VELOCITY_ACTUAL 0x606Cu ///< i32 velocity actual value
42#define CIA402_OD_TARGET_TORQUE 0x6071u ///< i16 target torque (per-mille of rated)
43#define CIA402_OD_TORQUE_ACTUAL 0x6077u ///< i16 torque actual value
44#define CIA402_OD_TARGET_POSITION 0x607Au ///< i32 target position (PP / CSP)
45#define CIA402_OD_PROFILE_VELOCITY 0x6081u ///< u32 profile velocity (PP)
46#define CIA402_OD_PROFILE_ACCEL 0x6083u ///< u32 profile acceleration
47#define CIA402_OD_PROFILE_DECEL 0x6084u ///< u32 profile deceleration
48#define CIA402_OD_TARGET_VELOCITY 0x60FFu ///< i32 target velocity (PV / CSV)
49#define CIA402_OD_SUPPORTED_MODES 0x6502u ///< u32 supported drive modes bitfield
50
51/// Modes of Operation (object 0x6060 / 0x6061). Cast only at the wire byte.
52enum class Cia402Mode : int8_t
53{
54 no_mode = 0,
55 profile_position = 1, ///< PP
56 velocity = 2, ///< VL (frequency-converter velocity)
57 profile_velocity = 3, ///< PV
58 profile_torque = 4, ///< TQ
59 homing = 6, ///< HM
60 interpolated_position = 7, ///< IP
61 cyclic_sync_position = 8, ///< CSP
62 cyclic_sync_velocity = 9, ///< CSV
63 cyclic_sync_torque = 10, ///< CST
64};
65
66/// The eight power-state-machine states decoded from the Statusword.
67enum class Cia402State : uint8_t
68{
69 not_ready_to_switch_on,
70 switch_on_disabled,
71 ready_to_switch_on,
72 switched_on,
73 operation_enabled,
74 quick_stop_active,
75 fault_reaction_active,
76 fault,
77 unknown, ///< Statusword matched no defined state
78};
79
80/// State-machine transition commands issued via the Controlword.
81enum class Cia402Command : uint8_t
82{
83 shutdown, ///< -> Ready to switch on
84 switch_on, ///< -> Switched on
85 enable_operation, ///< -> Operation enabled
86 disable_voltage, ///< -> Switch on disabled
87 quick_stop, ///< -> Quick stop active
88 disable_operation, ///< -> Switched on
89 fault_reset, ///< clear a fault (rising edge of bit 7)
90};
91
92/// Controlword bit masks (object 0x6040).
93struct Cia402Cw
94{
95 static constexpr uint16_t switch_on = 0x0001;
96 static constexpr uint16_t enable_voltage = 0x0002;
97 static constexpr uint16_t quick_stop = 0x0004; ///< active-low: 0 requests quick stop
98 static constexpr uint16_t enable_operation = 0x0008;
99 static constexpr uint16_t fault_reset = 0x0080; ///< acts on the rising edge
100 static constexpr uint16_t halt = 0x0100;
101};
102
103/// Statusword bit masks (object 0x6041).
104struct Cia402Sw
105{
106 static constexpr uint16_t ready_to_switch_on = 0x0001;
107 static constexpr uint16_t switched_on = 0x0002;
108 static constexpr uint16_t operation_enabled = 0x0004;
109 static constexpr uint16_t fault = 0x0008;
110 static constexpr uint16_t voltage_enabled = 0x0010;
111 static constexpr uint16_t quick_stop = 0x0020; ///< 0 = quick stop active
112 static constexpr uint16_t switch_on_disabled = 0x0040;
113 static constexpr uint16_t warning = 0x0080;
114 static constexpr uint16_t remote = 0x0200;
115 static constexpr uint16_t target_reached = 0x0400;
116 static constexpr uint16_t internal_limit = 0x0800;
117};
118
119// --- state machine (pure value logic; no CAN needed) ---
120
121/// Decode the drive's power state from a Statusword (per the CiA 402 mask/value table).
122Cia402State cia402_state(uint16_t statusword);
123
124/// The Controlword value that requests transition @p cmd. Fault-reset is 0x0080 and must be a
125/// rising edge on bit 7 (clear it on the next cycle).
126uint16_t cia402_controlword(Cia402Command cmd);
127
128/// Given the drive's current @p state, the Controlword to command the next step toward Operation
129/// Enabled (fault -> reset, switch-on-disabled -> shutdown, ready -> switch on, switched-on ->
130/// enable). Returns 0x000F once already enabled. Drives the "bring the axis live" bring-up loop.
131uint16_t cia402_enable_sequence(Cia402State state);
132
133/// @return true if the Statusword's Target Reached flag (bit 10) is set.
134static inline bool cia402_target_reached(uint16_t sw)
135{
136 return (sw & Cia402Sw::target_reached) != 0;
137}
138/// @return true if the drive reports a fault (bit 3).
139static inline bool cia402_has_fault(uint16_t sw)
140{
141 return (sw & Cia402Sw::fault) != 0;
142}
143/// @return true if a warning is present (bit 7).
144static inline bool cia402_warning(uint16_t sw)
145{
146 return (sw & Cia402Sw::warning) != 0;
147}
148/// @return true if the drive's power stage voltage is applied (bit 4).
149static inline bool cia402_voltage_enabled(uint16_t sw)
150{
151 return (sw & Cia402Sw::voltage_enabled) != 0;
152}
153/// @return true if the drive follows the Controlword (bit 9 remote).
154static inline bool cia402_remote(uint16_t sw)
155{
156 return (sw & Cia402Sw::remote) != 0;
157}
158/// @return true if a set-point was internally limited (bit 11).
159static inline bool cia402_internal_limit(uint16_t sw)
160{
161 return (sw & Cia402Sw::internal_limit) != 0;
162}
163
164// --- CANopen SDO setters (expedited download to the object); fill *out, return false on bad arg ---
165
166/// SDO-write the Controlword (0x6040, u16) on @p node.
167bool cia402_sdo_set_controlword(CanFrame *out, uint8_t node, uint16_t controlword);
168/// SDO-write the requested Mode of Operation (0x6060, i8) on @p node.
169bool cia402_sdo_set_mode(CanFrame *out, uint8_t node, Cia402Mode mode);
170/// SDO-write Target Position (0x607A, i32) on @p node.
171bool cia402_sdo_set_target_position(CanFrame *out, uint8_t node, int32_t position);
172/// SDO-write Target Velocity (0x60FF, i32) on @p node.
173bool cia402_sdo_set_target_velocity(CanFrame *out, uint8_t node, int32_t velocity);
174/// SDO-write Target Torque (0x6071, i16) on @p node.
175bool cia402_sdo_set_target_torque(CanFrame *out, uint8_t node, int16_t torque);
176
177/// SDO-read request for any drive object (thin wrapper over canopen_build_sdo_read).
178bool cia402_sdo_read(CanFrame *out, uint8_t node, uint16_t index, uint8_t sub);
179
180/// Decode an SDO upload response into a 16-bit object value (e.g. the Statusword). @p want_index,
181/// if non-zero, must match the response's index. Returns false on an abort / wrong / short reply.
182bool cia402_sdo_get_u16(const CanFrame *f, uint16_t want_index, uint16_t *value);
183/// Decode an SDO upload response into a signed 32-bit value (position / velocity actual).
184bool cia402_sdo_get_i32(const CanFrame *f, uint16_t want_index, int32_t *value);
185
186// --- PDO packing for cyclic operation (the common default mappings) ---
187
188/// Pack an RPDO payload = Controlword (u16 LE) + Target (i32 LE) = 6 octets (a typical CSP/PP
189/// RPDO map). Returns the octet count, or 0 if cap < 6.
190size_t cia402_pack_command(uint8_t *buf, size_t cap, uint16_t controlword, int32_t target);
191
192/// Unpack a TPDO payload = Statusword (u16 LE) + Actual (i32 LE) into @p statusword / @p actual
193/// (a typical CSP/PP TPDO map). Returns false if len < 6.
194bool cia402_unpack_status(const uint8_t *buf, size_t len, uint16_t *statusword, int32_t *actual);
195
196#endif // DETWS_ENABLE_CIA402
197
198#endif // DETERMINISTICESPASYNCWEBSERVER_CIA402_H
User-facing configuration for DeterministicESPAsyncWebServer.
CANopen (CiA 301) application-layer message codec (DETWS_ENABLE_CANOPEN).
Definition can.h:44