DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
canopen.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 canopen.h
6 * @brief CANopen (CiA 301) application-layer message codec (DETWS_ENABLE_CANOPEN).
7 *
8 * A pure, zero-heap builder + parser for the CANopen messaging set carried over classic
9 * CAN frames (see shared_primitives/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 are expedited only (<= 4 octets); segmented / block SDO is not yet covered.
17 *
18 * Bridging: pair with the ESP32's TWAI peripheral (or an MCP2515 over SPI) to bridge a
19 * CANopen field bus onto Wi-Fi - expose node state / PDOs over HTTP, MQTT, or a WebSocket.
20 *
21 * @author Douglas Quigg (dstroy0)
22 * @date 2026
23 */
24
25#ifndef DETERMINISTICESPASYNCWEBSERVER_CANOPEN_H
26#define DETERMINISTICESPASYNCWEBSERVER_CANOPEN_H
27
28#include "ServerConfig.h"
29
30#if DETWS_ENABLE_CANOPEN
31
33#include <stddef.h>
34#include <stdint.h>
35
36// Function-code COB-ID bases. The 11-bit id is (function-code | node-id); the node id is
37// 1..127 (0 = broadcast for NMT / SYNC / TIME). EMCY shares 0x080 with SYNC: SYNC is the
38// node-id == 0 case, EMCY is 0x081..0x0FF.
39#define CANOPEN_COB_NMT 0x000u ///< NMT node control (broadcast), 2 data octets
40#define CANOPEN_COB_SYNC 0x080u ///< SYNC (broadcast), 0 data octets
41#define CANOPEN_COB_EMCY 0x080u ///< EMCY base (+ node id)
42#define CANOPEN_COB_TIME 0x100u ///< TIME stamp (broadcast)
43#define CANOPEN_COB_TPDO1 0x180u ///< transmit PDO 1 base (+ node id)
44#define CANOPEN_COB_RPDO1 0x200u ///< receive PDO 1 base (+ node id)
45#define CANOPEN_COB_TPDO2 0x280u ///< transmit PDO 2 base
46#define CANOPEN_COB_RPDO2 0x300u ///< receive PDO 2 base
47#define CANOPEN_COB_TPDO3 0x380u ///< transmit PDO 3 base
48#define CANOPEN_COB_RPDO3 0x400u ///< receive PDO 3 base
49#define CANOPEN_COB_TPDO4 0x480u ///< transmit PDO 4 base
50#define CANOPEN_COB_RPDO4 0x500u ///< receive PDO 4 base
51#define CANOPEN_COB_SDO_TX 0x580u ///< SDO server -> client (response), + node id
52#define CANOPEN_COB_SDO_RX 0x600u ///< SDO client -> server (request), + node id
53#define CANOPEN_COB_HEARTBEAT 0x700u ///< NMT error control (heartbeat / boot-up), + node id
54#define CANOPEN_FUNC_MASK 0x780u ///< top 4 bits select the function code
55#define CANOPEN_NODE_MASK 0x07Fu ///< low 7 bits select the node id
56
57// NMT node-control commands (CANOPEN_COB_NMT data[0]).
58#define CANOPEN_NMT_START 0x01u ///< enter Operational
59#define CANOPEN_NMT_STOP 0x02u ///< enter Stopped
60#define CANOPEN_NMT_PRE_OP 0x80u ///< enter Pre-operational
61#define CANOPEN_NMT_RESET_NODE 0x81u ///< reset application
62#define CANOPEN_NMT_RESET_COMM 0x82u ///< reset communication
63
64// NMT states reported in a heartbeat (data[0], low 7 bits).
65#define CANOPEN_STATE_BOOTUP 0x00u
66#define CANOPEN_STATE_STOPPED 0x04u
67#define CANOPEN_STATE_OPERATIONAL 0x05u
68#define CANOPEN_STATE_PRE_OP 0x7Fu
69
70// SDO command specifier (high 3 bits of data[0]).
71#define CANOPEN_SDO_CCS_DOWNLOAD 1u ///< client download initiate (write)
72#define CANOPEN_SDO_CCS_UPLOAD 2u ///< client upload initiate (read)
73#define CANOPEN_SDO_SCS_UPLOAD 2u ///< server upload initiate response
74#define CANOPEN_SDO_SCS_DOWNLOAD 3u ///< server download initiate response (ack)
75#define CANOPEN_SDO_ABORT 4u ///< abort transfer (either direction)
76
77// A few common SDO abort codes (the field is any 32-bit value).
78#define CANOPEN_ABORT_TOGGLE 0x05030000u ///< toggle bit not alternated
79#define CANOPEN_ABORT_TIMEOUT 0x05040000u ///< SDO protocol timed out
80#define CANOPEN_ABORT_NO_OBJECT 0x06020000u ///< object does not exist
81#define CANOPEN_ABORT_NO_SUBINDEX 0x06090011u ///< sub-index does not exist
82#define CANOPEN_ABORT_GENERAL 0x08000000u ///< general error
83
84/** @brief CANopen message classes (the function decoded from the COB-ID). */
85enum class CanopenType : uint8_t
86{
87 CANOPEN_T_UNKNOWN = 0,
88 CANOPEN_T_NMT,
89 CANOPEN_T_SYNC,
90 CANOPEN_T_EMCY,
91 CANOPEN_T_TIME,
92 CANOPEN_T_TPDO,
93 CANOPEN_T_RPDO,
94 CANOPEN_T_SDO_TX,
95 CANOPEN_T_SDO_RX,
96 CANOPEN_T_HEARTBEAT,
97};
98
99/** @brief A classified CANopen frame (the function code + node, from canopen_parse). */
100struct CanopenMsg
101{
102 CanopenType type;
103 uint8_t node_id; ///< 1..127, or 0 for a broadcast (NMT / SYNC / TIME)
104 uint8_t pdo_num; ///< 1..4 for TPDO / RPDO, else 0
105};
106
107/** @brief A decoded SDO initiate response (from canopen_parse_sdo_response). */
108struct CanopenSdoResponse
109{
110 uint16_t index; ///< object index echoed by the server
111 uint8_t sub; ///< sub-index echoed by the server
112 bool is_abort; ///< true => the server aborted the transfer
113 uint32_t abort_code; ///< valid when is_abort
114 bool is_upload; ///< true => upload (read) response; false => download (write) ack
115 bool expedited; ///< true => the payload is inline in data[0..len-1]
116 uint8_t data[4]; ///< expedited upload payload
117 uint8_t len; ///< expedited payload length 0..4
118};
119
120// --- builders: fill *out and return true; false on a bad argument ---
121
122/** @brief NMT node-control frame. @p node_id 0 addresses all nodes. */
123bool canopen_build_nmt(CanFrame *out, uint8_t command, uint8_t node_id);
124
125/** @brief SYNC frame (zero-length, broadcast). */
126bool canopen_build_sync(CanFrame *out);
127
128/** @brief Heartbeat / boot-up frame for @p node_id reporting @p state. */
129bool canopen_build_heartbeat(CanFrame *out, uint8_t node_id, uint8_t state);
130
131/** @brief Emergency (EMCY) frame: 16-bit error code (LE), error register, 5 manufacturer octets. */
132bool canopen_build_emcy(CanFrame *out, uint8_t node_id, uint16_t error_code, uint8_t error_reg, const uint8_t msef[5]);
133
134/** @brief Transmit-PDO frame (@p pdo_num 1..4): up to 8 raw mapped octets. */
135bool canopen_build_tpdo(CanFrame *out, uint8_t pdo_num, uint8_t node_id, const uint8_t *data, uint8_t len);
136
137/** @brief Receive-PDO frame (@p pdo_num 1..4): up to 8 raw mapped octets. */
138bool canopen_build_rpdo(CanFrame *out, uint8_t pdo_num, uint8_t node_id, const uint8_t *data, uint8_t len);
139
140/** @brief SDO expedited upload (read) request for object @p index / @p sub on @p node_id. */
141bool canopen_build_sdo_read(CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub);
142
143/** @brief SDO expedited download (write) of @p len (1..4) octets to @p index / @p sub. */
144bool canopen_build_sdo_write(CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub, const uint8_t *data,
145 uint8_t len);
146
147/** @brief SDO abort frame. @p to_server true => client->server (0x600), false => server->client (0x580). */
148bool canopen_build_sdo_abort(CanFrame *out, uint8_t node_id, uint16_t index, uint8_t sub, uint32_t abort_code,
149 bool to_server);
150
151// --- parsers ---
152
153/** @brief Classify any frame by COB-ID into its CANopen function + node. */
154bool canopen_parse(const CanFrame *f, CanopenMsg *out);
155
156/** @brief Decode an EMCY frame (must be a 0x080+node, 8-octet frame). */
157bool canopen_parse_emcy(const CanFrame *f, uint8_t *node_id, uint16_t *error_code, uint8_t *error_reg, uint8_t msef[5]);
158
159/** @brief Decode a heartbeat frame (0x700+node, 1 octet). */
160bool canopen_parse_heartbeat(const CanFrame *f, uint8_t *node_id, uint8_t *state);
161
162/** @brief Decode an SDO server response (0x580+node): upload data, download ack, or abort. */
163bool canopen_parse_sdo_response(const CanFrame *f, CanopenSdoResponse *out);
164
165#endif // DETWS_ENABLE_CANOPEN
166#endif // DETERMINISTICESPASYNCWEBSERVER_CANOPEN_H
User-facing configuration for DeterministicESPAsyncWebServer.
Shared CAN 2.0 frame type for the CAN-based industrial codecs (one source of truth).
Definition can.h:44