Overview | Quick Start | Samples | Architecture | CoSlave | CoMaster | Listeners | Object Dictionary | Configuration | Eds2Od
Everything build-time-configurable lives in Source/Settings/; the platform binding lives in Source/System/. The samples keep per-project copies of the two settings files, so each device tunes the stack independently.
Source/Settings/CoConfiguration.hpp compiles services in or out. Every switch defaults to enabled; override on the compiler command line (preferred, e.g. -DCO_ENABLE_LSS=0) or in a project-local copy. Disabling a service removes its code from the link and removes its callbacks from ICoListener. Dependencies resolve automatically (no master → no SDO client, no PDO → no MPDO, …).
| Switch | Controls |
|---|---|
CO_ENABLE_MASTER / CO_ENABLE_SLAVE |
The CoMaster / CoSlave role — disable whichever this build never instantiates. |
CO_ENABLE_HEARTBEAT |
Heartbeat producer/consumer (1016h/1017h), OnHeartbeatTimeout. |
CO_ENABLE_SYNC |
SYNC producer/consumer (1005h/1006h/1019h), OnSync. |
CO_ENABLE_TIME |
TIME producer/consumer (1012h), OnTime. |
CO_ENABLE_EMCY |
EMCY producer/consumer (1001h/1003h/1014h/1015h), OnEmergency. |
CO_ENABLE_PDO |
PDO transmit and receive. |
CO_ENABLE_MPDO |
Multiplexed PDOs (SAM/DAM). |
CO_ENABLE_SDO_SERVER |
SDO server — without it the device cannot be configured or read by a master. |
CO_ENABLE_SDO_CLIENT |
SDO client (master only). |
CO_ENABLE_SDO_BLOCK_TRANSFER |
SDO block transfer — a pure throughput optimization, first candidate on a tight flash budget. |
CO_ENABLE_LSS |
LSS (CiA 305) — skippable when node-id and bit rate are fixed (e.g. DIP switches). |
CO_ENABLE_INDICATORS |
CiA 303-3 run/error LED patterns, OnIndicatorChanged. |
NMT is always included — every CANopen node needs it.
Source/Settings/CoSettings.hpp statically sizes all tables and buffers (defaults in parentheses):
| Constant | Meaning |
|---|---|
CoCycleTime (10) |
Stack cycle time in ms — the period at which you call Proceed(). |
SdoTimeoutTime (2000) |
SDO transfer timeout in ms. |
MaxNumberOfOdEntries (100) |
Capacity of an object dictionary. |
MaxNumberOfRemoteNodes (5) |
Remote node table of a master. |
MaxNumberOfSdoChannels (4) |
Concurrently active SDO channels. |
MaxStringLength (100) |
Largest string an SDO channel can carry. |
SdoBlocTransferUseCrc (true), MaxSdoBlockSize (127), SdoProtocolSwitchThreshold (28) |
Block transfer tuning. |
MaxNumberOfRpdoMappingParameters (9) / MaxNumberOfTpdoMappingParameters (5) |
PDO mappings per dictionary. |
MaxNumberOfPdoMappingParameterEntries (8) |
Mapped objects per PDO (a PDO carries at most 8 bytes). |
MaxNumberOfEmcyErrors (8) |
Concurrently tracked active EMCY errors. |
The mapping from protocol to CAN identifier sits behind ICoCobId — the profile is a constructor argument of your device, not a build variant of the stack.
CoCobIdCia301Source/Common/CoCobIdCia301.hpp implements the pre-defined connection set of CiA 301 (SDO at 600h/580h + node-id, TPDO1 at 180h + node-id, …). This is the default for general CANopen devices:
CANoopEn::CoCobIdCia301 _cobId;
Source/Profiles/CiA417/ supports CANopen Lift devices:
CoCobIdCia417 — the CiA 417 COB-ID mapping; constructed with the node-id: CoCobIdCia417 _cobId(nodeId);CoCia417 — the profile’s constants: device profile number 417, the profile object indices (6000h virtual device types, 6003h floor number, 6010h/6011h virtual I/O mapping, 6100h call input groups, 6108h fire inputs, output objects 6200h/6210h/6218h/6219h/62B0h/62B1h), virtual device types, basic/sub function codes, door and panel designators.CoCia417VirtualMapping — encodes/decodes the 32-bit virtual I/O mapping values (basic function, sub function, lift, panel, door, function data): construct from fields or FromValue(...), compare with Matches(...), read with GetBasicFunction(), IsStatusOn(), etc.The Samples/CanOpenClient/CiA417 projects show a complete lift call panel node, and the Samples/CanOpenMaster/CiA417 projects the matching lift call controller, built from these pieces. Pressing the panel’s user button registers a hall call with the call controller; pressing the call controller’s button answers with the call acknowledgement — each shown on the blue led of the other device.
This is what the round trip of the two sample boards looks like to a standard CANopen lift analyzer — here the Toolbox from Thor Engineering (panel = node 10, call controller 1 = node 1):
16:00:22.431 NMT-Command 'Start Remote Node' for node 10
16:00:22.804 Node 10, NMT-State operational
16:00:33.644 Node 10, Lift 1, Hall call, up, floor 1, door A, on <02-01-01-01-11-01>
16:00:34.243 Node 10, Lift 1, Hall call, up, floor 1, door A, off <02-01-01-01-11-00>
16:00:38.155 Node 1, Lift 1, Hall call acknowledge, up, floor 1, door A, on <02-01-01-01-11-01>
16:00:38.515 Node 1, Lift 1, Hall call acknowledge, up, floor 1, door A, off <02-01-01-01-11-00>
Source/System/ declares two primitives the stack synchronizes with; the application supplies the implementation (.cpp) matching its platform — that is the entire OS port:
CoSemaphore(count) — counting semaphore: Pend(timeoutMs) (0 = wait forever, returns false on timeout), Post(), Set(count). Guards the object dictionary and internal state.CoEvent() — event object: Wait(timeoutMs), Pulse() (signal and resume waiters), Set(), Reset(). Used to suspend the caller of synchronous SDO transfers.Ready-made implementations ship with the samples under Source/CANoopEn/System/:
| Platform | Implementation |
|---|---|
| Bare-metal | Trivial (single context, no suspension) — CanOpenClient/CiA401/BareMetal |
| FreeRTOS | Semaphores/event groups — the FreeRTOS samples |
Copy the matching pair into your project (they are application code, not stack code). Note that CoMaster‘s synchronous SDO calls require a real OS implementation; a pure bare-metal port supports the slave role.
C++17, no RTTI/exceptions required, no dynamic allocation. The samples build with Arm GCC (Cortex-M) and MSVC (unit tests) from the same sources.
