How to use¶
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
#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 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: CAN_DRIVER
1streamLogger streamObj(&myTimer, sdioConf);
2timerTool myTimer;
3CAN_DRIVER can(CAN_ENUM::BAUDRATE::_500kbps, pinCanRx, pinCanTx, &streamObj, &myTimer);
2. Initialize the CAN_DRIVER object¶
1can.begin();
3. add signals¶
The library is made to facilitate the message handling. The 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: CAN_DRIVER::addVariable()
, CAN_DRIVER::printMappingInfo()
Example
1float mFloat = 0.1234567890F;
2uint8_t mUint8 = 1U;
3uint16_t mUint16 = 2;
4int32_t mInt32 = -6;
5float mSinWave = 0.F;
6uint8_t mSquareWave = 0.F;
7
8can.addVariable(&mFloat, "mFloat", 0x1, "0x1");
9can.addVariable(&mUint8, "mUint8", 0x1, "0x1");
10can.addVariable(&mInt32, "mInt32", 0x1, "0x1");
11can.addVariable(&mUint16, "mUint16", 0x2, "0x2");
12can.addVariable(&mInt32, "mInt32", 0x3, "0x3");
13
14can.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 CAN_DRIVER::sendAllMessages()
function and all the
signals will be sent.
Important
API ref: CAN_DRIVER::sendAllMessages()
1float mFloat = 0.1234567890F;
2uint8_t mUint8 = 1U;
3
4can.addVariable(&mFloat, "mFloat", 0x1, "0x1");
5can.addVariable(&mUint8, "mUint8", 0x1, "0x1");
6
7// Send all messages at approximatly 200Hz
8while (true)
9{
10 delay(5);
11 can.sendAllMessages();
12}
4.2. Asynchronous send¶
This is the best method as each frame is controlled individually. This is the method used in libDM_msg_center for example.
Call the function CAN_DRIVER::serializeAndSendFrame()
with the ID of the frame you want to send.
Important
API ref: CAN_DRIVER::sendAllMessages()
1float mFloat = 0.1234567890F;
2uint8_t mUint8 = 1U;
3
4can.addVariable(&mFloat, "mFloat", 0x1, "0x1");
5can.addVariable(&mUint8, "mUint8", 0x1, "0x1");
6
7can.addVariable(&mUint16, "mUint16", 0x2, "0x2");
8
9// Send all messages at approximatly 200Hz
10uint32_t counter1 = 0U;
11uint32_t counter2 = 0U;
12while (true)
13{
14 // Send frame 0x1 every 100ms
15 if (counter1 >= 100U)
16 {
17 counter1 = 0U;
18 can.serializeAndSendFrame(0x1);
19 }
20
21 // Send frame 0x2 every 10msinde
22 if (counter2 >= 10U)
23 {
24 counter2 = 0U;
25 can.serializeAndSendFrame(0x2);
26 }
27
28 delay(1);
29}
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 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: CAN_DRIVER
1streamLogger streamObj(&myTimer, sdioConf);
2timerTool myTimer;
3CAN_DRIVER can(CAN_ENUM::BAUDRATE::_500kbps, pinCanRx, pinCanTx, &streamObj, &myTimer);
2. Initialize the CAN_DRIVER object¶
1can.begin();
3. Receive signals¶
The library will fetch CAN incoming buffer with the use of CAN_DRIVER::receiveFrames()
, and store received message
until a limit of 50 messages. The user can then ask a for this reception buffer with CAN_DRIVER::getRawRxBuffer()
and loop through it to decode the messages. The frame numbers in the buffer can be obtained with CAN_DRIVER::getFrameNumber()
.
Both methods are presented
Attention
The reception buffer if overriden at each call of 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 for example.
Important
API ref: CAN_DRIVER::decodeRawRxBuffer()
, CAN_DRIVER::receiveFrames()
, CAN_DRIVER::getRawBufferSize()
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.
1float mReceivedFloat1 = 0.F; 2float mReceivedFloat2 = 0.F; 3 4uint8_t mReceivedUint8 = 0U; 5uint16_t mReceivedUint16 = 0U; 6 7using float_2_binary = union 8{ 9 float floatData = 0.F; 10 uint8_t byteTable[4]; 11}; 12 13using uint16_2_binary = union 14{ 15 uint16_t uint16Data = 0U; 16 uint8_t byteTable[2]; 17}; 18 19float_2_binary sharedMemFloat; 20uint16_2_binary sharedMemUint16; 21 22can.receiveFrames(); 23CAN_DATATYPES::frameStruct frame = CAN_DATATYPES::frameStruct(); 24 25if (can.decodeRawRxBuffer(0x1, frame)) 26{ 27 std::copy(frame.receivedFrameData, frame.receivedFrameData + 4, sharedMemFloat.byteTable); 28 mReceivedFloat1 = sharedMemFloat.floatData; 29 std::copy(frame.receivedFrameData + 4, frame.receivedFrameData + 8, sharedMemFloat.byteTable); 30 mReceivedFloat2 = sharedMemFloat.floatData; 31} 32 33if (can.decodeRawRxBuffer(0x2, frame)) 34{ 35 mReceivedUint8 = frame.receivedFrameData[0]; 36 std::copy(frame.receivedFrameData + 1, frame.receivedFrameData + 3, sharedMemUint16.byteTable); 37 mReceivedUint16 = sharedMemUint16.uint16Data; 38}
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: CAN_DRIVER::getRawRxBuffer()
, CAN_DRIVER::receiveFrames()
, CAN_DRIVER::getRawBufferSize()
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.
1float mReceivedFloat1 = 0.F; 2float mReceivedFloat2 = 0.F; 3 4uint8_t mReceivedUint8 = 0U; 5uint16_t mReceivedUint16 = 0U; 6 7using float_2_binary = union 8{ 9 float floatData = 0.F; 10 uint8_t byteTable[4]; 11}; 12 13using uint16_2_binary = union 14{ 15 uint16_t uint16Data = 0U; 16 uint8_t byteTable[2]; 17}; 18 19float_2_binary sharedMemFloat; 20uint16_2_binary sharedMemUint16; 21 22can.receiveFrames(); 23uint32_t frameNumber = can.getRawBufferSize(); 24const std::array<twai_message_t, CAN_BUFFER_MAX_FRAME_NUMBER>* canRxBuffer = 25 can.getRawRxBuffer(); 26 27for (size_t i = 0; i < frameNumber; i++) 28{ 29 const twai_message_t* frame = &canRxBuffer->at(i); 30 31 if (frame.identifier == 0x1) 32 { 33 std::copy(frame->data, frame->data + 4, sharedMemFloat.byteTable); 34 mReceivedFloat1 = sharedMemFloat.floatData; 35 std::copy(frame->data + 4, frame->data + 8, sharedMemFloat.byteTable); 36 mReceivedFloat2 = sharedMemFloat.floatData; 37 } 38 39 if (frame.identifier == 0x2) 40 { 41 mReceivedUint8 = frame->data[0]; 42 std::copy(frame->data + 1, frame->data + 3, sharedMemUint16.byteTable); 43 mReceivedUint16 = sharedMemUint16.uint16Data; 44 } 45}