ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
canopen.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file canopen.h
6 * @brief CANopen (CiA 301) application-layer message codec (PROTOCORE_ENABLE_CANOPEN).
7 *
8 * A pure, zero-heap builder + parser for the CANopen messaging set carried over classic
9 * CAN frames (see shared/can/can.h): NMT node control, SYNC, TIME, the
10 * heartbeat / boot-up (NMT error control), EMCY, PDO (process data), and expedited SDO
11 * (service data object) read / write / abort. The 11-bit CAN identifier is a 4-bit
12 * function code plus a 7-bit node id; each builder computes the right COB-ID and each
13 * parser classifies a received frame back to its function + node.
14 *
15 * Scope: the CANopen object dictionary itself is the application's; this is the wire codec.
16 * SDO transfers cover expedited (<= 4 octets) and segmented (CiA 301 §7.2.4.3, larger objects via a
17 * toggling run of 7-octet segments); block SDO is not covered.
18 *
19 * Bridging: pair with the ESP32's TWAI peripheral (or an MCP2515 over SPI) to bridge a
20 * CANopen field bus onto Wi-Fi - expose node state / PDOs over HTTP, MQTT, or a WebSocket.
21 *
22 * @author Douglas Quigg (dstroy0)
23 * @date 2026
24 */
25
26#ifndef PROTOCORE_CANOPEN_H
27#define PROTOCORE_CANOPEN_H
28
29#include "protocore_config.h" // the entry point: protocore_types.h for the widths
30
31#if PROTOCORE_ENABLE_CANOPEN
32
34
35// This module holds nothing between calls, so it carves no borrow and states none. An entry
36// takes one all the same, and never reads it, so every namespace in the tree is invoked the
37// same way.
38
39// Function-code COB-ID bases. The 11-bit id is (function-code | node-id); the node id is
40// 1..127 (0 = broadcast for NMT / SYNC / TIME). EMCY shares 0x080 with SYNC: SYNC is the
41// node-id == 0 case, EMCY is 0x081..0x0FF.
42#define CANOPEN_COB_NMT 0x000u ///< NMT node control (broadcast), 2 data octets
43#define CANOPEN_COB_SYNC 0x080u ///< SYNC (broadcast), 0 data octets
44#define CANOPEN_COB_EMCY 0x080u ///< EMCY base (+ node id)
45#define CANOPEN_COB_TIME 0x100u ///< TIME stamp (broadcast)
46#define CANOPEN_COB_TPDO1 0x180u ///< transmit PDO 1 base (+ node id)
47#define CANOPEN_COB_RPDO1 0x200u ///< receive PDO 1 base (+ node id)
48#define CANOPEN_COB_TPDO2 0x280u ///< transmit PDO 2 base
49#define CANOPEN_COB_RPDO2 0x300u ///< receive PDO 2 base
50#define CANOPEN_COB_TPDO3 0x380u ///< transmit PDO 3 base
51#define CANOPEN_COB_RPDO3 0x400u ///< receive PDO 3 base
52#define CANOPEN_COB_TPDO4 0x480u ///< transmit PDO 4 base
53#define CANOPEN_COB_RPDO4 0x500u ///< receive PDO 4 base
54#define CANOPEN_COB_SDO_TX 0x580u ///< SDO server -> client (response), + node id
55#define CANOPEN_COB_SDO_RX 0x600u ///< SDO client -> server (request), + node id
56#define CANOPEN_COB_HEARTBEAT 0x700u ///< NMT error control (heartbeat / boot-up), + node id
57#define CANOPEN_FUNC_MASK 0x780u ///< top 4 bits select the function code
58#define CANOPEN_NODE_MASK 0x07Fu ///< low 7 bits select the node id
59
60// NMT node-control commands (CANOPEN_COB_NMT data[0]).
61#define CANOPEN_NMT_START 0x01u ///< enter Operational
62#define CANOPEN_NMT_STOP 0x02u ///< enter Stopped
63#define CANOPEN_NMT_PRE_OP 0x80u ///< enter Pre-operational
64#define CANOPEN_NMT_RESET_NODE 0x81u ///< reset application
65#define CANOPEN_NMT_RESET_COMM 0x82u ///< reset communication
66
67// NMT states reported in a heartbeat (data[0], low 7 bits).
68#define CANOPEN_STATE_BOOTUP 0x00u
69#define CANOPEN_STATE_STOPPED 0x04u
70#define CANOPEN_STATE_OPERATIONAL 0x05u
71#define CANOPEN_STATE_PRE_OP 0x7Fu
72
73// SDO command specifier (high 3 bits of data[0]).
74#define CANOPEN_SDO_CCS_DOWNLOAD 1u ///< client download initiate (write)
75#define CANOPEN_SDO_CCS_UPLOAD 2u ///< client upload initiate (read)
76#define CANOPEN_SDO_SCS_UPLOAD 2u ///< server upload initiate response
77#define CANOPEN_SDO_SCS_DOWNLOAD 3u ///< server download initiate response (ack)
78#define CANOPEN_SDO_ABORT 4u ///< abort transfer (either direction)
79
80// A few common SDO abort codes (the field is any 32-bit value).
81#define CANOPEN_ABORT_TOGGLE 0x05030000u ///< toggle bit not alternated
82#define CANOPEN_ABORT_TIMEOUT 0x05040000u ///< SDO protocol timed out
83#define CANOPEN_ABORT_NO_OBJECT 0x06020000u ///< object does not exist
84#define CANOPEN_ABORT_NO_SUBINDEX 0x06090011u ///< sub-index does not exist
85#define CANOPEN_ABORT_GENERAL 0x08000000u ///< general error
86
87/** @brief Octets in a TIME message (CiA 301 TIME_OF_DAY: 4-octet ms + 2-octet days). */
88#define CANOPEN_TIME_LEN 6
89
90/** @brief The ms-after-midnight field is 28 bits; the top 4 bits of the U32 are reserved. */
91#define CANOPEN_TIME_MS_MASK 0x0FFFFFFFu
92
93/** @brief Octets of object data a single SDO segment carries. */
94#define CANOPEN_SDO_SEG_DATA 7
95
96/** @brief CANopen message classes (the function decoded from the COB-ID). */
97typedef enum PROTO_ENUM_PACKED
98{
99 CANOPEN_T_UNKNOWN = 0,
100 CANOPEN_T_NMT,
101 CANOPEN_T_SYNC,
102 CANOPEN_T_EMCY,
103 CANOPEN_T_TIME,
104 CANOPEN_T_TPDO,
105 CANOPEN_T_RPDO,
106 CANOPEN_T_SDO_TX,
107 CANOPEN_T_SDO_RX,
108 CANOPEN_T_HEARTBEAT,
109} CanopenType;
110
111/** @brief A classified CANopen frame (the function code + node, from protocore_canopen_parse). */
112typedef struct
113{
114 CanopenType type;
115 uint8_t node_id; ///< 1..127, or 0 for a broadcast (NMT / SYNC / TIME)
116 uint8_t pdo_num; ///< 1..4 for TPDO / RPDO, else 0
117} CanopenMsg;
118
119/** @brief A decoded SDO initiate response (from protocore_canopen_parse_sdo_response). */
120typedef struct
121{
122 uint16_t index; ///< object index echoed by the server
123 uint8_t sub; ///< sub-index echoed by the server
124 proto_bool is_abort; ///< true => the server aborted the transfer
125 uint32_t abort_code; ///< valid when is_abort
126 proto_bool is_upload; ///< true => upload (read) response; false => download (write) ack
127 proto_bool expedited; ///< true => the payload is inline in data[0..len-1]
128 uint8_t data[4]; ///< expedited upload payload
129 uint8_t len; ///< expedited payload length 0..4
130} CanopenSdoResponse;
131
132/** @brief Decoded CANopen TIME_OF_DAY (the TIME message payload, CiA 301 §7.2.6). The CANopen epoch is
133 * January 1, 1984, so @c days_since_1984 plus @c ms_since_midnight locate an absolute instant. */
134typedef struct
135{
136 uint32_t ms_since_midnight; ///< milliseconds after midnight (28-bit; 0..86'399'999)
137 uint16_t days_since_1984; ///< days since January 1, 1984
138} CanopenTime;
139
140/** @brief Segmented-upload reassembly state (accumulates segment data into a caller buffer). */
141typedef struct
142{
143 uint8_t *buf; ///< caller-provided accumulation buffer
144 size_t cap; ///< its capacity
145 size_t len; ///< octets accumulated so far
146 proto_bool expect_toggle; ///< the toggle the next segment must carry (starts false)
147 proto_bool done; ///< set once the last segment is accepted
148} CanopenSdoReasm;
149
150#include "shared/can/can.h" // CanFrame: the type a parameter points at
151
152/** @brief What build_nmt takes: out, command, node_id. */
153typedef struct
154{
155 CanFrame *out;
156 uint8_t command;
157 uint8_t node_id;
158} CanopenBuildNmtArgs;
159
160/** @brief What build_sync takes: out. */
161typedef struct
162{
163 CanFrame *out;
164} CanopenBuildSyncArgs;
165
166/** @brief What build_time takes: out, ms_since_midnight, ... */
167typedef struct
168{
169 CanFrame *out;
170 uint32_t ms_since_midnight;
171 uint16_t days_since_1984;
172} CanopenBuildTimeArgs;
173
174/** @brief What build_heartbeat takes: out, node_id, state. */
175typedef struct
176{
177 CanFrame *out;
178 uint8_t node_id;
179 uint8_t state;
180} CanopenBuildHeartbeatArgs;
181
182/** @brief What build_emcy takes: out, node_id, error_code, error_reg, ... */
183typedef struct
184{
185 CanFrame *out;
186 uint8_t node_id;
187 uint16_t error_code;
188 uint8_t error_reg;
189 const uint8_t *msef; ///< 5 bytes.
190} CanopenBuildEmcyArgs;
191
192/** @brief What build_tpdo takes: out, pdo_num, node_id, data, len. */
193typedef struct
194{
195 CanFrame *out;
196 uint8_t pdo_num;
197 uint8_t node_id;
198 const uint8_t *data;
199 uint8_t len;
200} CanopenBuildTpdoArgs;
201
202/** @brief What build_rpdo takes: out, pdo_num, node_id, data, len. */
203typedef struct
204{
205 CanFrame *out;
206 uint8_t pdo_num;
207 uint8_t node_id;
208 const uint8_t *data;
209 uint8_t len;
210} CanopenBuildRpdoArgs;
211
212/** @brief What build_sdo_read takes: out, node_id, index, sub. */
213typedef struct
214{
215 CanFrame *out;
216 uint8_t node_id;
217 uint16_t index;
218 uint8_t sub;
219} CanopenBuildSdoReadArgs;
220
221/** @brief What build_sdo_write takes: out, node_id, index, sub, data, ... */
222typedef struct
223{
224 CanFrame *out;
225 uint8_t node_id;
226 uint16_t index;
227 uint8_t sub;
228 const uint8_t *data;
229 uint8_t len;
230} CanopenBuildSdoWriteArgs;
231
232/** @brief What build_sdo_abort takes: out, node_id, index, sub, ... */
233typedef struct
234{
235 CanFrame *out;
236 uint8_t node_id;
237 uint16_t index;
238 uint8_t sub;
239 uint32_t abort_code;
240 proto_bool to_server;
241} CanopenBuildSdoAbortArgs;
242
243/** @brief What parse takes: f, out. */
244typedef struct
245{
246 const CanFrame *f;
247 CanopenMsg *out;
248} CanopenParseArgs;
249
250/** @brief What parse_emcy takes: f, node_id, error_code, error_reg, ... */
251typedef struct
252{
253 const CanFrame *f;
254 uint8_t *node_id;
255 uint16_t *error_code;
256 uint8_t *error_reg;
257 uint8_t *msef; ///< 5 bytes.
258} CanopenParseEmcyArgs;
259
260/** @brief What parse_heartbeat takes: f, node_id, state. */
261typedef struct
262{
263 const CanFrame *f;
264 uint8_t *node_id;
265 uint8_t *state;
266} CanopenParseHeartbeatArgs;
267
268/** @brief What parse_time takes: f, out. */
269typedef struct
270{
271 const CanFrame *f;
272 CanopenTime *out;
273} CanopenParseTimeArgs;
274
275/** @brief What parse_sdo_response takes: f, out. */
276typedef struct
277{
278 const CanFrame *f;
279 CanopenSdoResponse *out;
280} CanopenParseSdoResponseArgs;
281
282/** @brief What build_sdo_download_init takes: out, node_id, index, ... */
283typedef struct
284{
285 CanFrame *out;
286 uint8_t node_id;
287 uint16_t index;
288 uint8_t sub;
289 uint32_t total_size;
290} CanopenBuildSdoDownloadInitArgs;
291
292/** @brief What build_sdo_download_segment takes: out, node_id, ... */
293typedef struct
294{
295 CanFrame *out;
296 uint8_t node_id;
297 proto_bool toggle; ///< the toggle bit for this segment (0 for the first, then alternating)
298 const uint8_t *data;
299 uint8_t len;
300 proto_bool last; ///< true on the final segment
301} CanopenBuildSdoDownloadSegmentArgs;
302
303/** @brief What build_sdo_upload_segment_req takes: out, node_id, ... */
304typedef struct
305{
306 CanFrame *out;
307 uint8_t node_id;
308 proto_bool toggle;
309} CanopenBuildSdoUploadSegmentReqArgs;
310
311/** @brief What parse_sdo_segment takes: f, toggle, data, len, last. */
312typedef struct
313{
314 const CanFrame *f;
315 proto_bool *toggle;
316 uint8_t *data;
317 uint8_t *len;
318 proto_bool *last;
319} CanopenParseSdoSegmentArgs;
320
321/** @brief What sdo_reasm_init takes: r, buf, cap. */
322typedef struct
323{
324 CanopenSdoReasm *r;
325 uint8_t *buf;
326 size_t cap;
327} CanopenSdoReasmInitArgs;
328
329/** @brief What sdo_reasm_feed takes: r, data, len, toggle, last. */
330typedef struct
331{
332 CanopenSdoReasm *r;
333 const uint8_t *data;
334 uint8_t len;
335 proto_bool toggle;
336 proto_bool last;
337} CanopenSdoReasmFeedArgs;
338
339/**
340 * @brief CANopen (CiA 301) application-layer message codec (PROTOCORE_ENABLE_CANOPEN).
341 *
342 * A caller sets the members a call takes, invokes it through ::Canopen with the bytes it runs
343 * out of, and reads the outcome off the same handle.
344 *
345 * Canopen.build_nmt_args.out = ...;
346 * Canopen.build_nmt_args.command = ...;
347 * Canopen.build_nmt_args.node_id = ...;
348 * Canopen.build_nmt(work);
349 * // Canopen.ok is what the call reports
350 *
351 * @var CanopenNs::build_nmt_args what build_nmt takes: out, command, node_id
352 * @var CanopenNs::build_sync_args what build_sync takes: out
353 * @var CanopenNs::build_time_args what build_time takes: out, ms_since_midnight,
354 * @var CanopenNs::build_heartbeat_args what build_heartbeat takes: out, node_id, state
355 * @var CanopenNs::build_emcy_args what build_emcy takes: out, node_id, error_code, error_reg,
356 * @var CanopenNs::build_tpdo_args what build_tpdo takes: out, pdo_num, node_id, data, len
357 * @var CanopenNs::build_rpdo_args what build_rpdo takes: out, pdo_num, node_id, data, len
358 * @var CanopenNs::build_sdo_read_args what build_sdo_read takes: out, node_id, index, sub
359 * @var CanopenNs::build_sdo_write_args what build_sdo_write takes: out, node_id, index, sub, data,
360 * @var CanopenNs::build_sdo_abort_args what build_sdo_abort takes: out, node_id, index, sub,
361 * @var CanopenNs::parse_args what parse takes: f, out
362 * @var CanopenNs::parse_emcy_args what parse_emcy takes: f, node_id, error_code, error_reg,
363 * @var CanopenNs::parse_heartbeat_args what parse_heartbeat takes: f, node_id, state
364 * @var CanopenNs::parse_time_args what parse_time takes: f, out
365 * @var CanopenNs::parse_sdo_response_args what parse_sdo_response takes: f, out
366 * @var CanopenNs::build_sdo_download_init_args what build_sdo_download_init takes: out, node_id, index,
367 * @var CanopenNs::build_sdo_download_segment_args what build_sdo_download_segment takes: out, node_id,
368 * @var CanopenNs::build_sdo_upload_segment_req_args what build_sdo_upload_segment_req takes: out, node_id,
369 * @var CanopenNs::parse_sdo_segment_args what parse_sdo_segment takes: f, toggle, data, len, last
370 * @var CanopenNs::sdo_reasm_init_args what sdo_reasm_init takes: r, buf, cap
371 * @var CanopenNs::sdo_reasm_feed_args what sdo_reasm_feed takes: r, data, len, toggle, last
372 * @var CanopenNs::ok true iff f is an 8-octet frame whose command specifier is the ...
373 * @var CanopenNs::build_nmt NMT node-control frame. node_id 0 addresses all nodes
374 * @var CanopenNs::build_sync SYNC frame (zero-length, broadcast)
375 * @var CanopenNs::build_time TIME frame (broadcast): the TIME_OF_DAY (ms_since_midnight masked ...
376 * @var CanopenNs::build_heartbeat heartbeat / boot-up frame for node_id reporting state
377 * @var CanopenNs::build_emcy emergency (EMCY) frame: 16-bit error code (LE), error register, 5 ...
378 * @var CanopenNs::build_tpdo Transmit-PDO frame (pdo_num 1..4): up to 8 raw mapped octets
379 * @var CanopenNs::build_rpdo Receive-PDO frame (pdo_num 1..4): up to 8 raw mapped octets
380 * @var CanopenNs::build_sdo_read SDO expedited upload (read) request for object index / sub on ...
381 * @var CanopenNs::build_sdo_write SDO expedited download (write) of len (1..4) octets to index / sub
382 * @var CanopenNs::build_sdo_abort SDO abort frame. to_server true => client->server (0x600), false => ...
383 * @var CanopenNs::parse classify any frame by COB-ID into its CANopen function + node
384 * @var CanopenNs::parse_emcy decode an EMCY frame (must be a 0x080+node, 8-octet frame)
385 * @var CanopenNs::parse_heartbeat decode a heartbeat frame (0x700+node, 1 octet)
386 * @var CanopenNs::parse_time decode a TIME frame (0x100, 6 octets) into out. true iff f is the ...
387 * @var CanopenNs::parse_sdo_response decode an SDO server response (0x580+node): upload data, download ...
388 * @var CanopenNs::build_sdo_download_init build a segmented SDO download (write) initiate: names the object ...
389 * @var CanopenNs::build_sdo_download_segment build a segmented SDO download segment carrying len (1..7) octets ...
390 * @var CanopenNs::build_sdo_upload_segment_req build a segmented SDO upload segment request (client asks the ...
391 * @var CanopenNs::parse_sdo_segment decode an SDO segment frame (either direction) into its toggle, ...
392 * @var CanopenNs::sdo_reasm_init begin a segmented-upload reassembly with a caller-owned buffer
393 * @var CanopenNs::sdo_reasm_feed feed one decoded upload segment into the reassembler (append len ...
394 *
395 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
396 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
397 * a caller drives every namespace the same way.
398 */
399typedef struct
400{
401 CanopenBuildNmtArgs build_nmt_args;
402 CanopenBuildSyncArgs build_sync_args;
403 CanopenBuildTimeArgs build_time_args;
404 CanopenBuildHeartbeatArgs build_heartbeat_args;
405 CanopenBuildEmcyArgs build_emcy_args;
406 CanopenBuildTpdoArgs build_tpdo_args;
407 CanopenBuildRpdoArgs build_rpdo_args;
408 CanopenBuildSdoReadArgs build_sdo_read_args;
409 CanopenBuildSdoWriteArgs build_sdo_write_args;
410 CanopenBuildSdoAbortArgs build_sdo_abort_args;
411 CanopenParseArgs parse_args;
412 CanopenParseEmcyArgs parse_emcy_args;
413 CanopenParseHeartbeatArgs parse_heartbeat_args;
414 CanopenParseTimeArgs parse_time_args;
415 CanopenParseSdoResponseArgs parse_sdo_response_args;
416 CanopenBuildSdoDownloadInitArgs build_sdo_download_init_args;
417 CanopenBuildSdoDownloadSegmentArgs build_sdo_download_segment_args;
418 CanopenBuildSdoUploadSegmentReqArgs build_sdo_upload_segment_req_args;
419 CanopenParseSdoSegmentArgs parse_sdo_segment_args;
420 CanopenSdoReasmInitArgs sdo_reasm_init_args;
421 CanopenSdoReasmFeedArgs sdo_reasm_feed_args;
422 proto_bool ok;
423} CanopenVars;
424
425/** @brief The operands and the outcome. */
426extern CanopenVars CanopenV;
427
428/** @brief The entries. */
429typedef struct
430{
431 void (*const build_nmt)(uint8_t *work);
432 void (*const build_sync)(uint8_t *work);
433 void (*const build_time)(uint8_t *work);
434 void (*const build_heartbeat)(uint8_t *work);
435 void (*const build_emcy)(uint8_t *work);
436 void (*const build_tpdo)(uint8_t *work);
437 void (*const build_rpdo)(uint8_t *work);
438 void (*const build_sdo_read)(uint8_t *work);
439 void (*const build_sdo_write)(uint8_t *work);
440 void (*const build_sdo_abort)(uint8_t *work);
441 void (*const parse)(uint8_t *work);
442 void (*const parse_emcy)(uint8_t *work);
443 void (*const parse_heartbeat)(uint8_t *work);
444 void (*const parse_time)(uint8_t *work);
445 void (*const parse_sdo_response)(uint8_t *work);
446 void (*const build_sdo_download_init)(uint8_t *work);
447 void (*const build_sdo_download_segment)(uint8_t *work);
448 void (*const build_sdo_upload_segment_req)(uint8_t *work);
449 void (*const parse_sdo_segment)(uint8_t *work);
450 void (*const sdo_reasm_init)(uint8_t *work);
451 void (*const sdo_reasm_feed)(uint8_t *work);
452} CanopenNs;
453
454// What the table binds, defined once in the .c and taking one parameter each: everything
455// else an entry needs is an operand in CanopenV or a region of the borrow at a fixed offset.
456void protocore_canopen_build_nmt(uint8_t *work);
457void protocore_canopen_build_sync(uint8_t *work);
458void protocore_canopen_build_time(uint8_t *work);
459void protocore_canopen_build_heartbeat(uint8_t *work);
460void protocore_canopen_build_emcy(uint8_t *work);
461void protocore_canopen_build_tpdo(uint8_t *work);
462void protocore_canopen_build_rpdo(uint8_t *work);
463void protocore_canopen_build_sdo_read(uint8_t *work);
464void protocore_canopen_build_sdo_write(uint8_t *work);
465void protocore_canopen_build_sdo_abort(uint8_t *work);
466void protocore_canopen_parse(uint8_t *work);
467void protocore_canopen_parse_emcy(uint8_t *work);
468void protocore_canopen_parse_heartbeat(uint8_t *work);
469void protocore_canopen_parse_time(uint8_t *work);
470void protocore_canopen_parse_sdo_response(uint8_t *work);
471void protocore_canopen_build_sdo_download_init(uint8_t *work);
472void protocore_canopen_build_sdo_download_segment(uint8_t *work);
473void protocore_canopen_build_sdo_upload_segment_req(uint8_t *work);
474void protocore_canopen_parse_sdo_segment(uint8_t *work);
475void protocore_canopen_sdo_reasm_init(uint8_t *work);
476void protocore_canopen_sdo_reasm_feed(uint8_t *work);
477
478// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
479// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
480// `Canopen.build_nmt(work)` resolves to a named function and becomes a DIRECT call. An extern table
481// leaves the call indirect and the symbol live at every level, -O2 -flto included.
482static const CanopenNs Canopen __attribute__((unused)) = {
483 .build_nmt = protocore_canopen_build_nmt,
484 .build_sync = protocore_canopen_build_sync,
485 .build_time = protocore_canopen_build_time,
486 .build_heartbeat = protocore_canopen_build_heartbeat,
487 .build_emcy = protocore_canopen_build_emcy,
488 .build_tpdo = protocore_canopen_build_tpdo,
489 .build_rpdo = protocore_canopen_build_rpdo,
490 .build_sdo_read = protocore_canopen_build_sdo_read,
491 .build_sdo_write = protocore_canopen_build_sdo_write,
492 .build_sdo_abort = protocore_canopen_build_sdo_abort,
493 .parse = protocore_canopen_parse,
494 .parse_emcy = protocore_canopen_parse_emcy,
495 .parse_heartbeat = protocore_canopen_parse_heartbeat,
496 .parse_time = protocore_canopen_parse_time,
497 .parse_sdo_response = protocore_canopen_parse_sdo_response,
498 .build_sdo_download_init = protocore_canopen_build_sdo_download_init,
499 .build_sdo_download_segment = protocore_canopen_build_sdo_download_segment,
500 .build_sdo_upload_segment_req = protocore_canopen_build_sdo_upload_segment_req,
501 .parse_sdo_segment = protocore_canopen_parse_sdo_segment,
502 .sdo_reasm_init = protocore_canopen_sdo_reasm_init,
503 .sdo_reasm_feed = protocore_canopen_sdo_reasm_feed,
504};
505
507
508#endif // PROTOCORE_ENABLE_CANOPEN
509
510#endif // PROTOCORE_CANOPEN_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
Shared CAN 2.0 frame type for the CAN-based industrial codecs (one source of truth).
Definition can.h:44
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97