DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
cia402.cpp
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.cpp
6 * @brief CiA 402 drive profile: state machine + Controlword/Statusword + CANopen object access.
7 */
8
10
11#if DETWS_ENABLE_CIA402
12
13#include <string.h>
14
15// CANopen object values are little-endian.
16static size_t put16le(uint8_t *p, uint16_t v)
17{
18 p[0] = (uint8_t)(v & 0xFF);
19 p[1] = (uint8_t)(v >> 8);
20 return 2;
21}
22
23static size_t put32le(uint8_t *p, uint32_t v)
24{
25 p[0] = (uint8_t)(v & 0xFF);
26 p[1] = (uint8_t)((v >> 8) & 0xFF);
27 p[2] = (uint8_t)((v >> 16) & 0xFF);
28 p[3] = (uint8_t)((v >> 24) & 0xFF);
29 return 4;
30}
31
32static uint16_t get16le(const uint8_t *p)
33{
34 return (uint16_t)(p[0] | ((uint16_t)p[1] << 8));
35}
36
37static uint32_t get32le(const uint8_t *p)
38{
39 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
40}
41
42Cia402State cia402_state(uint16_t sw)
43{
44 // Mask/value table from IEC 61800-7-201 (CiA 402). Order matters where masks differ.
45 if ((sw & 0x4F) == 0x00)
46 return Cia402State::not_ready_to_switch_on;
47 if ((sw & 0x4F) == 0x40)
48 return Cia402State::switch_on_disabled;
49 if ((sw & 0x6F) == 0x21)
50 return Cia402State::ready_to_switch_on;
51 if ((sw & 0x6F) == 0x23)
52 return Cia402State::switched_on;
53 if ((sw & 0x6F) == 0x27)
54 return Cia402State::operation_enabled;
55 if ((sw & 0x6F) == 0x07)
56 return Cia402State::quick_stop_active;
57 if ((sw & 0x4F) == 0x0F)
58 return Cia402State::fault_reaction_active;
59 if ((sw & 0x4F) == 0x08)
60 return Cia402State::fault;
61 return Cia402State::unknown;
62}
63
64uint16_t cia402_controlword(Cia402Command cmd)
65{
66 switch (cmd)
67 {
68 case Cia402Command::shutdown:
69 return 0x0006; // enable voltage + quick stop, switch-on 0
70 case Cia402Command::switch_on:
71 case Cia402Command::disable_operation:
72 return 0x0007; // switch-on + enable voltage + quick stop
73 case Cia402Command::enable_operation:
74 return 0x000F; // + enable operation
75 case Cia402Command::disable_voltage:
76 return 0x0000;
77 case Cia402Command::quick_stop:
78 return 0x0002; // enable voltage, quick-stop bit cleared (active)
79 case Cia402Command::fault_reset:
80 return 0x0080; // bit 7 rising edge
81 }
82 return 0x0000;
83}
84
85uint16_t cia402_enable_sequence(Cia402State state)
86{
87 switch (state)
88 {
89 case Cia402State::fault:
90 case Cia402State::fault_reaction_active:
91 return cia402_controlword(Cia402Command::fault_reset);
92 case Cia402State::switch_on_disabled:
93 return cia402_controlword(Cia402Command::shutdown);
94 case Cia402State::ready_to_switch_on:
95 return cia402_controlword(Cia402Command::switch_on);
96 case Cia402State::switched_on:
97 case Cia402State::quick_stop_active:
98 case Cia402State::operation_enabled:
99 return cia402_controlword(Cia402Command::enable_operation);
100 default: // not_ready_to_switch_on / unknown: wait, hold voltage off
101 return cia402_controlword(Cia402Command::disable_voltage);
102 }
103}
104
105bool cia402_sdo_set_controlword(CanFrame *out, uint8_t node, uint16_t controlword)
106{
107 uint8_t d[2];
108 put16le(d, controlword);
109 return canopen_build_sdo_write(out, node, CIA402_OD_CONTROLWORD, 0, d, 2);
110}
111
112bool cia402_sdo_set_mode(CanFrame *out, uint8_t node, Cia402Mode mode)
113{
114 uint8_t d = (uint8_t)(int8_t)mode; // wire byte
115 return canopen_build_sdo_write(out, node, CIA402_OD_MODES_OF_OPERATION, 0, &d, 1);
116}
117
118bool cia402_sdo_set_target_position(CanFrame *out, uint8_t node, int32_t position)
119{
120 uint8_t d[4];
121 put32le(d, (uint32_t)position);
122 return canopen_build_sdo_write(out, node, CIA402_OD_TARGET_POSITION, 0, d, 4);
123}
124
125bool cia402_sdo_set_target_velocity(CanFrame *out, uint8_t node, int32_t velocity)
126{
127 uint8_t d[4];
128 put32le(d, (uint32_t)velocity);
129 return canopen_build_sdo_write(out, node, CIA402_OD_TARGET_VELOCITY, 0, d, 4);
130}
131
132bool cia402_sdo_set_target_torque(CanFrame *out, uint8_t node, int16_t torque)
133{
134 uint8_t d[2];
135 put16le(d, (uint16_t)torque);
136 return canopen_build_sdo_write(out, node, CIA402_OD_TARGET_TORQUE, 0, d, 2);
137}
138
139bool cia402_sdo_read(CanFrame *out, uint8_t node, uint16_t index, uint8_t sub)
140{
141 return canopen_build_sdo_read(out, node, index, sub);
142}
143
144// Validate an expedited SDO upload response and copy its inline payload into @p out (>= need
145// octets). No shared state - the parsed response lives on this call's stack.
146static bool sdo_upload_bytes(const CanFrame *f, uint16_t want_index, uint8_t need, uint8_t *out)
147{
148 CanopenSdoResponse resp;
149 if (!canopen_parse_sdo_response(f, &resp))
150 return false;
151 if (resp.is_abort || !resp.is_upload || !resp.expedited || resp.len < need)
152 return false;
153 if (want_index != 0 && resp.index != want_index)
154 return false;
155 memcpy(out, resp.data, need);
156 return true;
157}
158
159bool cia402_sdo_get_u16(const CanFrame *f, uint16_t want_index, uint16_t *value)
160{
161 uint8_t d[2];
162 if (!value || !sdo_upload_bytes(f, want_index, 2, d))
163 return false;
164 *value = get16le(d);
165 return true;
166}
167
168bool cia402_sdo_get_i32(const CanFrame *f, uint16_t want_index, int32_t *value)
169{
170 uint8_t d[4];
171 if (!value || !sdo_upload_bytes(f, want_index, 4, d))
172 return false;
173 *value = (int32_t)get32le(d);
174 return true;
175}
176
177size_t cia402_pack_command(uint8_t *buf, size_t cap, uint16_t controlword, int32_t target)
178{
179 if (!buf || cap < 6)
180 return 0;
181 size_t p = put16le(buf, controlword);
182 p += put32le(buf + p, (uint32_t)target);
183 return p; // 6
184}
185
186bool cia402_unpack_status(const uint8_t *buf, size_t len, uint16_t *statusword, int32_t *actual)
187{
188 if (!buf || !statusword || !actual || len < 6)
189 return false;
190 *statusword = get16le(buf);
191 *actual = (int32_t)get32le(buf + 2);
192 return true;
193}
194
195#endif // DETWS_ENABLE_CIA402
CiA 402 / IEC 61800-7-201 drive + motion profile (DETWS_ENABLE_CIA402) over CANopen.
Definition can.h:44