How to use =========== .. toctree:: :maxdepth: 3 :caption: Contents: Platform support ---------------- Framework: - Arduino This library is made to be used with: - ESP32 family Necessary inclusions -------------------- Only the header has to be included to you project .. code-block:: c++ #include "libDM_can.hpp" Operating mode -------------- The can library is can be used with 2 modes: 1. Loopback mode ~~~~~~~~~~~~~~~~ The driver won't need ack form network nodes to send messages. This is useful for testing. The problem is that the driver will ack the frames itself and every sent frames will be also received. This is not a problem if you only need to send or to receive signals (and not both send and receive) on can bus. For example you can configure `libDM_msg_center <../libdm_msg_center/index.html>`_ easily to only do one of the two. Also if you need to visualize frames without adding your own node, this is useful. This mode is activated by default. 2. Normal mode ~~~~~~~~~~~~~~~ The driver will need ack from network nodes to send messages and will not be able to operate without it. This mode should be used if you need to send and receive signals on can bus. This mode is activated at object construction Message emission ---------------- 1. Create a CAN_UTILS object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. important:: API ref: :cpp:class:`CAN_DRIVER` .. code-block:: c++ :linenos: streamLogger streamObj(&myTimer, sdioConf); timerTool myTimer; CAN_DRIVER can(CAN_ENUM::BAUDRATE::_500kbps, pinCanRx, pinCanTx, &streamObj, &myTimer); 2. Initialize the CAN_DRIVER object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: c++ :linenos: can.begin(); 3. add signals ~~~~~~~~~~~~~~ The library is made to facilitate the message handling. The :cpp:func:`CAN_DRIVER::addVariable` is made to concatenate signals in the indicated frame in the same order they are added, with a limit of 8 bytes by frame (standard frame limit). .. important:: API ref: :cpp:func:`CAN_DRIVER::addVariable`, :cpp:func:`CAN_DRIVER::printMappingInfo` .. admonition:: Example .. code-block:: c++ :linenos: float mFloat = 0.1234567890F; uint8_t mUint8 = 1U; uint16_t mUint16 = 2; int32_t mInt32 = -6; float mSinWave = 0.F; uint8_t mSquareWave = 0.F; can.addVariable(&mFloat, "mFloat", 0x1, "0x1"); can.addVariable(&mUint8, "mUint8", 0x1, "0x1"); can.addVariable(&mInt32, "mInt32", 0x1, "0x1"); can.addVariable(&mUint16, "mUint16", 0x2, "0x2"); can.addVariable(&mInt32, "mInt32", 0x3, "0x3"); can.printMappingInfo(true, true); // Optional print of the mapping table .. attention:: Ensure added variables are global as their references will be stored in the CAN_DRIVER object. 4. Send signals ~~~~~~~~~~~~~~~ Library allows signals sending in two ways: - Periodic send: all added signals will be sent at a given frequency. - Asynchronous send: added signals will be sent when the user wants, in a more controlled manner. 4.1. Synchronous send """"""""""""""""""""" This is the easiest way to send signals. Just call periodically the :cpp:func:`CAN_DRIVER::sendAllMessages` function and all the signals will be sent. .. important:: API ref: :cpp:func:`CAN_DRIVER::sendAllMessages` .. code-block:: c++ :linenos: float mFloat = 0.1234567890F; uint8_t mUint8 = 1U; can.addVariable(&mFloat, "mFloat", 0x1, "0x1"); can.addVariable(&mUint8, "mUint8", 0x1, "0x1"); // Send all messages at approximatly 200Hz while (true) { delay(5); can.sendAllMessages(); } 4.2. Asynchronous send """"""""""""""""""""""" This is the best method as each frame is controlled individually. This is the method used in `libDM_msg_center <../libdm_msg_center/index.html>`_ for example. Call the function :cpp:func:`CAN_DRIVER::serializeAndSendFrame` with the ID of the frame you want to send. .. important:: API ref: :cpp:func:`CAN_DRIVER::sendAllMessages` .. code-block:: c++ :linenos: float mFloat = 0.1234567890F; uint8_t mUint8 = 1U; can.addVariable(&mFloat, "mFloat", 0x1, "0x1"); can.addVariable(&mUint8, "mUint8", 0x1, "0x1"); can.addVariable(&mUint16, "mUint16", 0x2, "0x2"); // Send all messages at approximatly 200Hz uint32_t counter1 = 0U; uint32_t counter2 = 0U; while (true) { // Send frame 0x1 every 100ms if (counter1 >= 100U) { counter1 = 0U; can.serializeAndSendFrame(0x1); } // Send frame 0x2 every 10msinde if (counter2 >= 10U) { counter2 = 0U; can.serializeAndSendFrame(0x2); } delay(1); } Message reception ----------------- Message reception is less controlled than message emission. The received messages are stored in a buffer the is made available with the use of the function :cpp:func:`CAN_DRIVER::getRawRxBuffer()`. The deserialization process is the user responsability as it depends on the add messages format. 1. Create a CAN_UTILS object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. important:: API ref: :cpp:class:`CAN_DRIVER` .. code-block:: c++ :linenos: streamLogger streamObj(&myTimer, sdioConf); timerTool myTimer; CAN_DRIVER can(CAN_ENUM::BAUDRATE::_500kbps, pinCanRx, pinCanTx, &streamObj, &myTimer); 2. Initialize the CAN_DRIVER object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: c++ :linenos: can.begin(); 3. Receive signals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The library will fetch CAN incoming buffer with the use of :cpp:func:`CAN_DRIVER::receiveFrames`, and store received message until a limit of 50 messages. The user can then ask a for this reception buffer with :cpp:func:`CAN_DRIVER::getRawRxBuffer` and loop through it to decode the messages. The frame numbers in the buffer can be obtained with :cpp:func:`CAN_DRIVER::getFrameNumber`. Both methods are presented .. attention:: The reception buffer if overriden at each call of :cpp:func:`CAN_DRIVER::receiveFrames`. Ensure to copy it before calling the function again. 3.1. With partial decoding """"""""""""""""""""""""""" This method is less efficient but easier to use. This is the method used in `libDM_msg_center <../libdm_msg_center/index.html>`_ for example. .. important:: API ref: :cpp:func:`CAN_DRIVER::decodeRawRxBuffer`, :cpp:func:`CAN_DRIVER::receiveFrames`, :cpp:func:`CAN_DRIVER::getRawBufferSize` .. admonition:: Example We expect to find 2 float in the frame with ID = 0x1, and one uint8_t and one uint16_t in the frame with ID = 0x2. .. code-block:: c++ :linenos: float mReceivedFloat1 = 0.F; float mReceivedFloat2 = 0.F; uint8_t mReceivedUint8 = 0U; uint16_t mReceivedUint16 = 0U; using float_2_binary = union { float floatData = 0.F; uint8_t byteTable[4]; }; using uint16_2_binary = union { uint16_t uint16Data = 0U; uint8_t byteTable[2]; }; float_2_binary sharedMemFloat; uint16_2_binary sharedMemUint16; can.receiveFrames(); CAN_DATATYPES::frameStruct frame = CAN_DATATYPES::frameStruct(); if (can.decodeRawRxBuffer(0x1, frame)) { std::copy(frame.receivedFrameData, frame.receivedFrameData + 4, sharedMemFloat.byteTable); mReceivedFloat1 = sharedMemFloat.floatData; std::copy(frame.receivedFrameData + 4, frame.receivedFrameData + 8, sharedMemFloat.byteTable); mReceivedFloat2 = sharedMemFloat.floatData; } if (can.decodeRawRxBuffer(0x2, frame)) { mReceivedUint8 = frame.receivedFrameData[0]; std::copy(frame.receivedFrameData + 1, frame.receivedFrameData + 3, sharedMemUint16.byteTable); mReceivedUint16 = sharedMemUint16.uint16Data; } 3.2. With manual decoding """"""""""""""""""""""""""" This method is more efficient as there is only one loop through the buffer but less generic as it uses twai_message_t which is esp32 specific. .. important:: API ref: :cpp:func:`CAN_DRIVER::getRawRxBuffer`, :cpp:func:`CAN_DRIVER::receiveFrames`, :cpp:func:`CAN_DRIVER::getRawBufferSize` .. admonition:: Example We expect to find 2 float in the frame with ID = 0x1, and one uint8_t and one uint16_t in the frame with ID = 0x2. .. code-block:: c++ :linenos: float mReceivedFloat1 = 0.F; float mReceivedFloat2 = 0.F; uint8_t mReceivedUint8 = 0U; uint16_t mReceivedUint16 = 0U; using float_2_binary = union { float floatData = 0.F; uint8_t byteTable[4]; }; using uint16_2_binary = union { uint16_t uint16Data = 0U; uint8_t byteTable[2]; }; float_2_binary sharedMemFloat; uint16_2_binary sharedMemUint16; can.receiveFrames(); uint32_t frameNumber = can.getRawBufferSize(); const std::array* canRxBuffer = can.getRawRxBuffer(); for (size_t i = 0; i < frameNumber; i++) { const twai_message_t* frame = &canRxBuffer->at(i); if (frame.identifier == 0x1) { std::copy(frame->data, frame->data + 4, sharedMemFloat.byteTable); mReceivedFloat1 = sharedMemFloat.floatData; std::copy(frame->data + 4, frame->data + 8, sharedMemFloat.byteTable); mReceivedFloat2 = sharedMemFloat.floatData; } if (frame.identifier == 0x2) { mReceivedUint8 = frame->data[0]; std::copy(frame->data + 1, frame->data + 3, sharedMemUint16.byteTable); mReceivedUint16 = sharedMemUint16.uint16Data; } }