Postfix:
The very short story here is, that the code listed, no matter what variation, actually works (despite the small coding mistake at the beginning of setup()). However, after spending significant time (`30h) 'debugging' it, it turned out that the cause of trouble was a faulty Arduino UNO, which, when replaced, flawlessly produced the desired results.
Sorry, I didn't come up with a better title; this post is probably for someone who understands CANbus and MCP2515 and or its library.
I have a Daly BMS (Battery Mgmt System) where I'd like to read the data from by using an Arduino UNO R3 and a MCP2515 CAN module.
It took me a while to get a handle of it, and eventually figured I have to send an empty frame (as in frame w/o data bytes) in order to get a response.
This works well so far where the frame contains the 'complete' data, as in, not more related data. To explain:
These return frames have an ID|DLC|DATA(8 bytes) and that's it.
However, there are a couple responses that have more than one frame to build the whole picture.
More precise: the 8 bytes can carry 3 16-bit values, which can relate to 3 battery cells. Here I have 16 cells, hence, 6 frames need to be read to get all 16 values.
My simplest of code returns these replies when querying a specific CAN ID:
DLC | | Frame number
CAN ID | |
| | | |----------- Data--------------|
98954001 8 1 | D | 1C | D | 17 | D | 1B | 0 |
98954001 8 1 | D | 21 | D | 15 | D | 1E | 0 |
98954001 8 1 | D | 1E | D | 16 | D | 1C | 0 |
98954001 8 1 | D | 1C | D | 19 | D | 1A | 0 |
98954001 8 1 | D | 1B | D | 18 | D | 1A | 0 |
98954001 8 1 | D | 21 | D | 16 | D | 1E | 0 |
98954001 8 1 | D | 1E | D | 17 | D | 1A | 0 |
98954001 8 1 | D | 1B | D | 1A | D | 1A | 0 |
98954001 8 1 | D | 1C | D | 18 | D | 19 | 0 |
This is correct.
So D blah, D blah, D blah stands for 6 bytes = three 16 bit values. The last byte is ignored (0).
The 1
represents the frame number.
Now... let's look at the result when I loop 5 times in order to catch 5 frames.
(Please note: I issue the query once, but read it twice, just see if a) reading multiple times works, and b) if I get the same result in each read):
i: 4 98954001 8 2 | D | 5E | D | 22 | D | 20 | 0 |
i: 5 98954001 8 4 | D | 4E | D | 32 | D | 55 | 0 |
i: 0 98954001 8 5 | D | 4E | D | 57 | D | 53 | 0 |
i: 1 ... reading reply failed ...
i: 2 ... reading reply failed ...
i: 3 ... reading reply failed ...
i: 4 ... reading reply failed ...
i: 5 ... reading reply failed ...
Message sent ...
i: 0 ... reading reply failed ...
i: 1 ... reading reply failed ...
i: 2 ... reading reply failed ...
i: 3 98954001 8 1 | D | 18 | D | 16 | D | 17 | 0 |
i: 4 98954001 8 2 | D | 5C | D | 22 | D | 1F | 0 |
i: 5 98954001 8 4 | D | 4E | D | 32 | D | 55 | 0 |
i: 0 98954001 8 5 | D | 4C | D | 56 | D | 53 | 0 |
i: 1 ... reading reply failed ...
i: 2 ... reading reply failed ...
i: 3 ... reading reply failed ...
i: 4 ... reading reply failed ...
i: 5 ... reading reply failed ...
Message sent ...
i: 0 ... reading reply failed ...
i: 1 ... reading reply failed ...
i: 2 ... reading reply failed ...
i: 3 98954001 8 1 | D | 1D | D | 14 | D | 1C | 0 |
i: 4 98954001 8 3 | D | 61 | D | 5D | D | 59 | 0 |
i: 5 98954001 8 2 | D | 5A | D | 24 | D | 1E | 0 |
i: 0 98954001 8 4 | D | 47 | D | 39 | D | 51 | 0 |
i: 1 98954001 8 5 | D | 53 | D | 53 | D | 58 | 0 |
i: 2 ... reading reply failed ...
i: 3 ... reading reply failed ...
i: 4 ... reading reply failed ...
i: 5 ... reading reply failed ...
Message sent ...
i: 0 ... reading reply failed ...
i: 1 ... reading reply failed ...
i: 2 ... reading reply failed ...
i: 3 98954001 8 1 | D | 1A | D | 18 | D | 18 | 0 |
i: 4 98954001 8 2 | D | 5E | D | 22 | D | 21 | 0 |
i: 5 98954001 8 3 | D | 5B | D | 67 | D | 53 | 0 |
i: 0 98954001 8 4 | D | 51 | D | 32 | D | 59 | 0 |
i: 1 98954001 8 5 | D | 4D | D | 59 | D | 52 | 0 |
i: 2 ... reading reply failed ...
i: 3 ... reading reply failed ...
i: 4 ... reading reply failed ...
i: 5 ... reading reply failed ...
Message sent ...
i: 0 ... reading reply failed ...
i: 1 ... reading reply failed ...
i: 2 ... reading reply failed ...
i: 3 ... reading reply failed ...
i: 4 98954001 8 1 | D | 1B | D | 15 | D | 1A | 0 |
i: 5 98954001 8 2 | D | 5A | D | 24 | D | 1E | 0 |
i: 0 98954001 8 4 | D | 4A | D | 35 | D | 53 | 0 |
i: 1 98954001 8 5 | D | 51 | D | 55 | D | 55 | 0 |
i: 2 ... reading reply failed ...
i: 3 ... reading reply failed ...
i: 4 ... reading reply failed ...
i: 5 ... reading reply failed ...
This is what the documentation says:
this is what my program looks like:
#include <Arduino.h>
#include <SPI.h>
#include <mcp2515.h>
struct can_frame can_query;
struct can_frame can_reply;
MCP2515 mcp2515(10);
void send_can_query();
void read_can_reply();
void send_can_query()
{
can_query.can_id = 0x18950140 | CAN_EFF_FLAG;
can_query.can_dlc = 8;
for (uint8_t i = 0; i < 8; i++)
{
can_query.data[i] = 0x00;
}
mcp2515.sendMessage(&can_query);
Serial.println("Message sent ...");
}
void read_can_reply()
{
const uint8_t NUMBER_OF_LOOPS = 6;
for (uint8_t i = 0; i < NUMBER_OF_LOOPS; i++)
{
Serial.print("i: ");
Serial.print(i);
Serial.print(" ");
if (mcp2515.readMessage(&can_reply) == MCP2515::ERROR_OK)
{
Serial.print(can_reply.can_id, HEX);
Serial.print(" ");
Serial.print(can_reply.can_dlc, HEX);
Serial.print(" ");
for (int i = 0; i < can_reply.can_dlc; i++)
{
Serial.print(can_reply.data[i], HEX);
Serial.print(" | ");
}
Serial.println();
}
else
{
Serial.println("... reading reply failed ...");
}
}
}
void setup()
{
while (!Serial);
Serial.begin(115200);
delay(500);
mcp2515.reset();
mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
mcp2515.setNormalMode();
Serial.println("Query and interpet CAN msg...");
Serial.println("ID DLC DATA");
}
void loop()
{
send_can_query();
read_can_reply();
read_can_reply();
delay(3000);
}
So, this result has puzzled me, and here are my questions (not having much of an idea about how this protocol really works):
- Why am I getting the read errors? Is this normal? It would be a bad protocol to use in automotive, where it is heavily used.
So, I assume the problem sits elsewhere. My wires are short, and there is no AC source near the cables and Arduino/MCP2515 to potentially interfere with the signal. - How do I know or test if there are more than the six frames?
- Assuming I have only 8 battery cells (= 3 frames to query); in fact no matter how many cells there are, how do I know how many (follow-up ) frames there are?
I reckon I have to somehow find out with test routines. - I also wondered where are frames 2..6? Are these in memory, or does
readMessage
send out another request?
I only send one query to the CAN ID. Eachmcp2515.readMessage(&can_reply)
seemingly progresses to the next frame when issued... but some have read errors. The frame IDs are not always in order. - And last, what is the best way to work around the errors? Not asking to write the code for me, but comment on my suggested idea...
Idea: it looks like I could use an array or a byte to hold a successful "frame read" and either store its number in the array, or when using a byte in its bit position, or sum up the frame numbers to 136 or however many frames I want to catch.
Any hints/answers appreciated.
I have checked the hardware:
- I have an 8Mhz crystal on the MCP2515
- I have 5.0 V on the MCP2515; as the TJA1050 CAN chip requires >4.75 V to operate properly
- the library examples work
- the bus is terminated (tried un-terminated which made no difference)