Lely core libraries  1.9.2
doc/co/overview.md
1 CANopen library overview
2 ========================
3 
4 Contrary to most other CANopen stacks, this implementation is completely
5 passive; the library does not perform any I/O (besides may reading some files
6 from disk), it does not create threads nor does it access the system clock.
7 Instead, it relies on the user to send and receive CAN frames and update the
8 clock. This allows the library to be easily embedded in a wide variety of
9 applications.
10 
11 The library is also asynchronous. Issuing a request is always a non-blocking
12 operation. If the request is confirmed, the API accepts a callback function
13 which is invoked once the request completes (with success or failure). This
14 allows the stack to run in a single thread, even when processing dozens of
15 simultaneous requests (not uncommon for an NMT master).
16 
17 CAN network object
18 ------------------
19 
20 The interface between the CANopen stack and the CAN bus (and system clock) is
21 provided by the CAN network object (`can_net_t`/`CANNet`) from the Lely CAN
22 library ([liblely-can]). When the CANopen stack needs to send a CAN frame, it
23 hands it over to a CAN network object, which in turn invokes a user-defined
24 callback function to write the frame to the bus. Similarly, when a user reads a
25 CAN frame from the bus, he gives it to a CAN network object, which in turn
26 distributes it to the registered receivers in the CANopen stack. Additionally,
27 the user periodically checks the current time and tells the CAN network object,
28 which then executes the actions for timers that have elapsed.
29 
30 Sometimes a CANopen application runs on a device which does not have direct
31 access to a CAN bus. [CiA] 315 defines a protocol for tunneling CAN frames over
32 wired or Wireless Transmission Media (WTM). The interface for this protocol can
33 be found in lely/co/wtm.h (#co_wtm_t) and lely/co/wtm.hpp (#lely::COWTM).
34 Additionally, the `can2udp` tool can be run as a daemon or service to act as a
35 proxy between a CAN bus and UDP.
36 
37 The first task when writing a CANopen application is connecting a CAN network
38 object to the CAN bus. The following example shows how to do this using the Lely
39 I/O library ([liblely-io]), which provides a platform-independent interface to
40 the CAN bus.
41 
42 C example:
43 ```{.c}
44 #include <lely/util/errnum.h>
45 #include <lely/util/time.h>
46 #include <lely/can/net.h>
47 #include <lely/io/can.h>
48 #include <lely/io/poll.h>
49 
50 struct my_can {
51  io_handle_t handle;
52  int st;
53  io_poll_t *poll;
54  can_net_t *net;
55  struct timespec next;
56 };
57 
58 int my_can_init(struct my_can *can, const char *ifname);
59 void my_can_fini(struct my_can *can);
60 
61 // This function can be called to perform a single step in an event loop.
62 void my_can_step(struct my_can *can, int timeout);
63 
64 int my_can_on_next(const struct timespec *tp, void *data);
65 int my_can_on_send(const struct can_msg *msg, void *data);
66 
67 int
68 my_can_init(struct my_can *can, const char *ifname)
69 {
70  int errc = 0;
71 
72  // Initialize the I/O library.
73  if (lely_io_init() == -1) {
74  errc = get_errc();
75  goto error_init_io;
76  }
77 
78  // Open a handle to the CAN bus.
79  can->handle = io_open_can(ifname);
80  if (can->handle == IO_HANDLE_ERROR) {
81  errc = get_errc();
82  goto error_open_can;
83  }
84  can->st = io_can_get_state(can->handle);
85 
86  // Create a new I/O polling interface.
87  can->poll = io_poll_create();
88  if (!can->poll) {
89  errc = get_errc();
90  goto error_create_poll;
91  }
92 
93  // Create a CAN network object.
94  can->net = can_net_create();
95  if (!can->net) {
96  errc = get_errc();
97  goto error_create_net;
98  }
99 
100  // Do not block when reading or writing CAN frames and make sure we
101  // receive the frames we sent.
102  io_set_flags(can->handle, IO_FLAG_NONBLOCK | IO_FLAG_LOOPBACK);
103 
104  // Watch the CAN bus for incoming frames.
105  struct io_event event = {
106  .events = IO_EVENT_READ,
107  .u.handle = can->handle
108  };
109  io_poll_watch(can->poll, can->handle, &event, 1);
110 
111  struct timespec now = { 0, 0 };
112  // Obtain the current time. This is equivalent to
113  // clock_gettime(CLOCK_REALTIME, &now) from POSIX. To use a steady,
114  // monotonic clock instead, replace all occurrences of the following
115  // line with:
116  // clock_gettime(CLOCK_MONOTONIC, &now);
117  timespec_get(&now, TIME_UTC);
118  // Initialize the CAN network clock.
119  can_net_set_time(can->net, &now);
120 
121  can->next = now;
122  // Register my_can_on_next as the function to be invoked when the time
123  // at which the next CAN timer triggers is updated.
124  can_net_set_next_func(can->net, &my_can_on_next, can);
125 
126  // Register my_can_on_send() as the function to be invoked when a CAN
127  // frame needs to be sent.
128  can_net_set_send_func(can->net, &my_can_on_send, can);
129 
130  return 0;
131 
132  can_net_destroy(can->net);
133  can->net = NULL;
134 error_create_net:
135  io_poll_destroy(can->poll);
136  can->poll = NULL;
137 error_create_poll:
138  io_handle_release(can->handle);
139  can->handle = IO_HANDLE_ERROR;
140 error_open_can:
141  lely_io_fini();
142 error_init_io:
143  set_errc(errc);
144  return -1;
145 }
146 
147 void
148 my_can_fini(struct my_can *can)
149 {
150  can_net_destroy(can->net);
151  can->net = NULL;
152 
153  io_poll_destroy(can->poll);
154  can->poll = NULL;
155 
156  io_handle_release(can->handle);
157  can->handle = IO_HANDLE_ERROR;
158 
159  lely_io_fini();
160 }
161 
162 void
163 my_can_step(struct my_can *can, int timeout)
164 {
165  struct timespec now = { 0, 0 };
166  timespec_get(&now, TIME_UTC);
167  // Check if a CAN timer is set to expire before the specified timeout.
168  // Ignore if the expiration time is in the past, as that probably
169  // indicates an old timer.
170  int64_t msec = timespec_diff_msec(&can->next, &now);
171  if (msec > 0 && msec < timeout)
172  timeout = msec;
173 
174  // Wait at most `timeout` milliseconds for an I/O event to occur.
175  struct io_event event = IO_EVENT_INIT;
176  int n = io_poll_wait(can->poll, 1, &event, timeout);
177 
178  // Update the CAN network clock.
179  timespec_get(&now, TIME_UTC);
180  can_net_set_time(can->net, &now);
181 
182  if (n != 1 || event.u.handle != can->handle)
183  return;
184 
185  // If the CAN bus is ready for reading, process all waiting frames.
186  if (event.events & IO_EVENT_READ) {
187  struct can_msg msg = CAN_MSG_INIT;
188  int result = 0;
189  while ((result = io_can_read(can->handle, &msg)) == 1)
190  can_net_recv(can->net, &msg);
191  // Treat the reception of an error frame, or any error other
192  // than an empty receive buffer, as an error event.
193  if (!result || (result == -1 && get_errnum() != ERRNUM_AGAIN
194  && get_errnum() != ERRNUM_WOULDBLOCK))
195  event.events |= IO_EVENT_ERROR;
196  }
197 
198  // If an error occurred, update the state of the CAN device.
199  if (can->st == CAN_STATE_BUSOFF || (event.events & IO_EVENT_ERROR)) {
200  int st = io_can_get_state(can->handle);
201  if (st != can->st) {
202  if (can->st == CAN_STATE_BUSOFF) {
203  // Recovered from bus off. This is typically
204  // handled by invoking:
205  // co_nmt_on_err(nmt, 0x8140, 0x10, NULL);
206  } else if (st == CAN_STATE_PASSIVE) {
207  // CAN in error passive mode. This is typically
208  // handled by invoking:
209  // co_nmt_on_err(nmt, 0x8120, 0x10, NULL);
210  }
211  can->st = st;
212  }
213  }
214 }
215 
216 int
217 my_can_on_next(const struct timespec *tp, void *data)
218 {
219  struct my_can *can = data;
220 
221  can->next = *tp;
222 
223  return 0;
224 }
225 
226 int
227 my_can_on_send(const struct can_msg *msg, void *data)
228 {
229  struct my_can *can = data;
230 
231  // Send a single frame to the CAN bus.
232  return io_can_write(can->handle, msg) == 1 ? 0 : -1;
233 }
234 ```
235 
236 C++11 example:
237 ```{.cpp}
238 #include <lely/util/errnum.h>
239 #include <lely/util/time.h>
240 #include <lely/can/net.hpp>
241 #include <lely/io/can.hpp>
242 #include <lely/io/poll.hpp>
243 
244 using namespace lely;
245 
246 class MyCAN {
247 public:
248  explicit MyCAN(const char* ifname)
249  : m_handle(ifname) // Open a handle to the CAN bus.
250  , m_st(m_handle.getState()) // Get the state of the CAN device.
251  , m_poll(new IOPoll) // Create a new I/O polling interface.
252  , m_net(new CANNet) // Create a CAN network object.
253  {
254  // Do not block when reading or writing CAN frames and make sure
255  // we receive the frames we sent.
256  m_handle.setFlags(IO_FLAG_NONBLOCK | IO_FLAG_LOOPBACK);
257 
258  // Watch the CAN bus for incoming frames.
259  io_event event = { IO_EVENT_READ, { 0 } };
260  event.u.handle = m_handle;
261  m_poll->watch(m_handle, &event, true);
262 
263  timespec now = { 0, 0 };
264  // Obtain the current time. This is equivalent to
265  // clock_gettime(CLOCK_REALTIME, &now) from POSIX. To use a
266  // steady, monotonic clock instead, replace all occurrences of
267  // the following line with:
268  // clock_gettime(CLOCK_MONOTONIC, &now);
269  timespec_get(&now, TIME_UTC);
270  // Initialize the CAN network clock.
271  m_net->setTime(now);
272 
273  m_next = now;
274  // Register the onNext() member function as the function to be
275  // invoked when the time at which the next CAN timer triggers is
276  // updated.
277  m_net->setNextFunc<MyCAN, &MyCAN::onNext>(this);
278 
279  // Register the onSend() member function as the function to be
280  // invoked when a CAN frame needs to be sent.
281  m_net->setSendFunc<MyCAN, &MyCAN::onSend>(this);
282  }
283 
284  // Disable copying.
285  MyCAN(const MyCAN&) = delete;
286  MyCAN& operator=(const MyCAN&) = delete;
287 
288  CANNet* getNet() const noexcept { return m_net.get(); }
289 
290  // This function can be called to perform a single step in an event
291  // loop.
292  void
293  step(int timeout = 0) noexcept
294  {
295  timespec now = { 0, 0 };
296  timespec_get(&now, TIME_UTC);
297  // Check if a CAN timer is set to expire before the specified
298  // timeout. Ignore if the expiration time is in the past, as
299  // that probably indicates an old timer.
300  int64_t msec = timespec_diff_msec(&m_next, &now);
301  if (msec > 0 && msec < timeout)
302  timeout = msec;
303 
304  // Wait at most `timeout` milliseconds for an I/O event to
305  // occur.
306  io_event event = IO_EVENT_INIT;
307  int n = m_poll->wait(1, &event, timeout);
308 
309  // Update the CAN network clock.
310  timespec_get(&now, TIME_UTC);
311  m_net->setTime(now);
312 
313  if (n != 1 || event.u.handle != m_handle)
314  return;
315 
316  // If the CAN bus is ready for reading, process all waiting
317  // frames.
318  if (event.events & IO_EVENT_READ) {
319  can_msg msg = CAN_MSG_INIT;
320  int result = 0;
321  while ((result = m_handle.read(msg)) == 1)
322  m_net->recv(msg);
323  // Treat the reception of an error frame, or any error
324  // other than an empty receive buffer, as an error
325  // event.
326  if (!result || (result == -1
327  && get_errnum() != ERRNUM_AGAIN
328  && get_errnum() != ERRNUM_WOULDBLOCK))
329  event.events |= IO_EVENT_ERROR;
330  }
331 
332  // If an error occurred, update the state of the CAN device.
333  if (m_st == CAN_STATE_BUSOFF
334  || (event.events & IO_EVENT_ERROR)) {
335  int st = m_handle.getState();
336  if (st != m_st) {
337  if (m_st == CAN_STATE_BUSOFF) {
338  // Recovered from bus off. This is
339  // typically handled by invoking:
340  // nmt->onErr(0x8140, 0x10);
341  } else if (st == CAN_STATE_PASSIVE) {
342  // CAN in error passive mode. This is
343  // typically handled by invoking:
344  // nmt->onErr(0x8120, 0x10);
345  }
346  m_st = st;
347  }
348  }
349  }
350 
351 private:
352  int
353  onNext(const timespec* tp) noexcept
354  {
355  m_next = *tp;
356 
357  return 0;
358  }
359 
360  int
361  onSend(const can_msg* msg) noexcept
362  {
363  // Send a single frame to the CAN bus.
364  return m_handle.write(*msg) == 1 ? 0 : -1;
365  }
366 
367  IOCAN m_handle;
368  int m_st;
369  unique_c_ptr<IOPoll> m_poll;
370  unique_c_ptr<CANNet> m_net;
371  timespec m_next;
372 };
373 ```
374 
375 Object dictionary
376 -----------------
377 
378 The object dictionary is the central concept of the CANopen protocol. It
379 contains almost the entire state of a CANopen device, including process data and
380 communication parameters. Communication between nodes consists primarily of
381 reading from and writing to each others object dictionary. Even writing CANopen
382 applications is mostly a matter of configuring the object dictionary.
383 
384 Together with the Node-ID, the object dictionary is managed by a CANopen device
385 object (#co_dev_t/#lely::CODev). Although it is possible to construct the object
386 dictionary from scratch with the C and C++ API, it is more convenient to read it
387 from an Electronic Data Sheet (EDS) or Device Configuration File (DCF) (see
388 [CiA] 306). Functions to parse EDS/DCF files can be found in lely/co/dcf.h and
389 lely/co/dcf.hpp.
390 
391 Embedded devices often do not have the resources to parse an EDS/DCF file at
392 runtime. Using the `dcf2c` tool it is possible to create a C file containing a
393 static object dictionary, which can be compiled with the application. The static
394 object dictionary can then be converted to a dynamic one at runtime (see
395 lely/co/sdev.h and lely/co/sdev.hpp).
396 
397 Service objects
398 ---------------
399 
400 The CANopen stack implements the following services:
401 * Process data object (PDO)
402  * Receive-PDO (RPDO):
403  lely/co/rpdo.h (#co_rpdo_t) and lely/co/rpdo.hpp (#lely::CORPDO)
404  * Transmit-PDO (TPDO):
405  lely/co/tpdo.h (#co_tpdo_t) and lely/co/tpdo.hpp (#lely::COTPDO)
406 * Service data object (SDO)
407  * Server-SDO (CSDO):
408  lely/co/ssdo.h (#co_ssdo_t) and lely/co/ssdo.hpp (#lely::COSSDO)
409  * Client-SDO (CSDO):
410  lely/co/csdo.h (#co_csdo_t) and lely/co/csdo.hpp (#lely::COCSDO)
411 * Synchronization object (SYNC):
412  lely/co/sync.h (#co_sync_t) and lely/co/sync.hpp (#lely::COSync)
413 * Time stamp object (TIME):
414  lely/co/time.h (#co_time_t) and lely/co/time.hpp (#lely::COTime)
415 * Emergency object (EMCY):
416  lely/co/emcy.h (#co_emcy_t) and lely/co/emcy.hpp (#lely::COEmcy)
417 * Network management (NMT):
418  lely/co/nmt.h (#co_nmt_t) and lely/co/nmt.hpp (#lely::CONMT)
419 * Layer setting services (LSS):
420  lely/co/lss.h (#co_lss_t) and lely/co/lss.hpp (#lely::COLSS)
421 
422 While it is possible to create these services by hand, it is almost always more
423 convenient to let them be managed by a single Network Management (NMT) service
424 object (#co_nmt_t/#lely::CONMT). An NMT object manages the state of a CANopen
425 device and creates and destroys the other services as needed.
426 
427 Like all CANopen services, the NMT service gets its configuration from the
428 object dictionary. A typical CANopen application therefore consists of
429 * creating the CAN network object, as we have seen above;
430 * loading the object dictionary form an EDS/DCF file;
431 * creating an NMT service;
432 * and, finally, processing CAN frames in an event loop.
433 
434 Note, however, that a newly created NMT service starts out in the
435 'Initialisation' state and does not create any services or perform any
436 communication. This allows the application to register callback functions before
437 the node becomes operational. The NMT service, including the entire boot-up
438 sequence, can be started by giving it the RESET NODE command.
439 
440 The following example is a minimal CANopen application. Depending on the EDS/DCF
441 file, it can be a simple slave that does nothing besides producing a heartbeat
442 message, or a full-fledged master managing a network of slaves (including
443 automatic firmware upgrades!).
444 
445 C example:
446 ~~~{.c}
447 #include <lely/util/diag.h>
448 #include <lely/co/dcf.h>
449 #include <lely/co/nmt.h>
450 
451 #include <stdlib.h>
452 
453 int
454 main(void)
455 {
456  // Initialize the CAN network.
457  struct my_can can;
458  if (my_can_init(&can, "can0") == -1) {
459  diag(DIAG_ERROR, get_errc(), "unable to initialize CAN network");
460  goto error_init_can;
461  }
462 
463  // Load the object dictionary from a DCF file.
464  co_dev_t *dev = co_dev_create_from_dcf_file("test.dcf");
465  if (!dev) {
466  diag(DIAG_ERROR, get_errc(), "unable to load object dictionary");
467  goto error_create_dev;
468  }
469 
470  // Create the NMT service.
471  co_nmt_t *nmt = co_nmt_create(can.net, dev);
472  if (!nmt) {
473  diag(DIAG_ERROR, get_errc(), "unable to create NMT service");
474  goto error_create_nmt;
475  }
476  // Start the NMT service. We do this by pretending to receive a RESET
477  // NODE command from the master.
478  co_nmt_cs_ind(nmt, CO_NMT_CS_RESET_NODE);
479 
480  // The main event loop.
481  for (;;) {
482  // Process CAN frames in steps of 10 milliseconds.
483  my_can_step(&can, 10);
484  // TODO: do other useful stuff.
485  }
486 
487  co_nmt_destroy(nmt);
488  co_dev_destroy(dev);
489  my_can_fini(&can);
490 
491  return EXIT_SUCCESS;
492 
493 error_create_nmt:
494  co_dev_destroy(dev);
495 error_create_dev:
496  my_can_fini(&can);
497 error_init_can:
498  return EXIT_FAILURE;
499 }
500 ~~~
501 
502 C++11 example:
503 ~~~{.cpp}
504 #include <lely/co/dcf.hpp>
505 #include <lely/co/nmt.hpp>
506 
507 int
508 main()
509 {
510  // Initialize the I/O library.
511  lely_io_init();
512 
513  // Initialize the CAN network.
514  MyCAN can("can0");
515 
516  // Load the object dictionary from a DCF file.
517  auto dev = make_unique_c<CODev>("test.dcf");
518 
519  // Create the NMT service.
520  auto nmt = make_unique_c<CONMT>(can.getNet(), dev.get());
521  // Start the NMT service. We do this by pretending to receive a RESET
522  // NODE command from the master.
523  nmt->csInd(CO_NMT_CS_RESET_NODE);
524 
525  // The main event loop.
526  for (;;) {
527  // Process CAN frames in steps of 10 milliseconds.
528  can.step(10);
529  // TODO: do other useful stuff.
530  }
531 
532  lely_io_fini();
533  return 0;
534 }
535 ~~~
536 
537 CANopen requests
538 ----------------
539 
540 As mentioned before, the CANopen stack is asynchronous. Requests return
541 immediately, but take a callback function which is invoked once the request
542 succeeds (or fails). See the documentation of the Lely utilities library
543 ([liblely-util]) for a description of the general callback interface.
544 
545 As an example, define the following callback function for reading a 32-bit
546 unsigned integer from a remote object dictionary:
547 ~~~{.cpp}
548 #include <lely/util/diag.h>
549 #include <lely/co/csdo.hpp>
550 
551 void
552 onUp(COCSDO* sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
553  co_unsigned32_t ac, co_unsigned32_t val, void* data)
554 {
555  (void)sdo;
556  (void)data;
557 
558  if (ac)
559  // Print an error message if an abort code was received.
560  diag(DIAG_ERROR, 0, "received SDO abort code %08X (%s) when reading object %04X:%02X",
561  ac, co_sdo_ac2str(ac), idx, subidx);
562  else
563  // Print the received value on success.
564  diag(DIAG_INFO, 0, "received 0x%08X when reading object %04X:%02X",
565  val, idx, subidx);
566 }
567 ~~~
568 
569 Before the main event loop we obtain a Client-SDO service object from the NMT
570 service and issue the request:
571 ~~~{.cpp}
572 // Wait for the node to be operational.
573 while (nmt->getSt() != CO_NMT_ST_START)
574  can.step(10);
575 
576 // Obtain the first preconfigured Client-SDO connection.
577 auto csdo = nmt->getCSDO(1);
578 if (csdo)
579  // Read the device type (object 1000) from the remote object dictionary.
580  csdo->upReq<co_unsigned32_t, &onUp>(0x1000, 0x00, nullptr);
581 ~~~
582 
583 The `upReq()` function returns immediately; `onUp()` is called from the main
584 event loop when the request succeeds (or fails).
585 
586 This is an example for a Client-SDO requests, but all services follow this
587 pattern. Note that by the time the callback function is called, the request has
588 been completed. It is therefore possible to issue a new request directly from
589 the callback function. This pattern is used extensively by the master when
590 booting slaves.
591 
592 [CiA]: http://can-cia.org/
593 [liblely-can]: https://gitlab.com/lely_industries/can
594 [liblely-io]: https://gitlab.com/lely_industries/io
595 [liblely-util]: https://gitlab.com/lely_industries/util
596