CAN BUS very slow

Hello everyone

I am attempting to send EMX firmware over CAN bus. Firmware is about 700kB. With a bitrate of 250kb/s I would expect in theory 22s. In practice it takes 170s to send the file.

Moreover using a lawicell canusb it seems that there’s some missing frames…

Here’s the code I am using.


int i = 0;
if (File.Exists(path))
            {
            FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read,FileShare.None, 8);
long size = f.Length;
long bytesRemaining;
int bytesRead;
byte[] data;
bytesRemaining = f.Length;
Debug.Print("Size of FW to send:" + bytesRemaining);
int nb_mess = (int)f.Length / 8;
CAN.Message[] mess = new CAN.Message[100];
while(bytesRemaining > 0) 
{
i = 0;
while (bytesRemaining > 0 && i < 100)
                    {
                        mess[i] = new CAN.Message();

                        data = new byte[(bytesRemaining > 8) ? 8 : bytesRemaining];
                        bytesRead = f.Read(data, 0, data.Length);
                        //Thread.Sleep(60);
                        mess[i].ArbID = 0x17FF0101;
                        mess[i].Data[0] = data[0];
                        mess[i].Data[1] = data[1];
                        mess[i].Data[2] = data[2];
                        mess[i].Data[3] = data[3];
                        mess[i].Data[4] = data[4];
                        mess[i].Data[5] = data[5];
                        mess[i].Data[6] = data[6];
                        mess[i].Data[7] = data[7];
                        mess[i].DLC = 8;
                        mess[i].IsEID = true;
                        mess[i].IsRTR = false;
                        i++;
                        
                        bytesRemaining -= bytesRead;
                    }
                    int numberOfMessagesPosted = can.PostMessages(mess, 0, i);
                }
                f.Close();
                f.Dispose();
            }

Is there’s something to do to speed up this process?

Ok I should read more the support documentation.

If I understand well, placing a message in the queue does not mean that message will be written on the bus. If you build an array of message how to know which one has been sent or not?

Most examples (if not all) on the forum use postmessage with only a single message to send… why using an array of message would be relevant???

Instead of using :

int numberOfMessagesPosted = can.PostMessages(mess, 0, i);

I modify it in order to be embedded in the inner loop.

int numberOfMessagesPosted = can.PostMessages(mess, i, 1);

Now all the messages are received but it still very slow.

I am using can monitor pro to check that in the first example some messages are not written while in the second all is ok.

In fact reading the file by chunk of 8 bytes is extremelly slow… (110s!!!)

The 22 second is 250kb/s = 31.25kB/s.

700kB to transfert at 31.25kB/s= 22.4s

the workload of bus is null (only emx send and lawicell)

Now that I know that filestream reads are responsible of 110s over 175, it sounds better but still!!! I know that given bitrate is different than usefull bitrate… but it 'smore than 100% of overhead!

Comment out the CAN post line and check the speed. This will tell Where the bottleneck is.

You can also try to read the entire firmware in a large buffer maybe.

Different tests will tell us a lot more.

@ leforban - I can’t help with CAN, but I have noticed that your DLC is always 8 even for the last message, which can be less than 8. Probably not a big deal, but I would refactor the code to something like this:

from:

 data = new byte[(bytesRemaining > 8) ? 8 : bytesRemaining];
bytesRead = f.Read(data, 0, data.Length);
//Thread.Sleep(60);
mess[i].ArbID = 0x17FF0101;
mess[i].Data[0] = data[0];
mess[i].Data[1] = data[1];
mess[i].Data[2] = data[2];
mess[i].Data[3] = data[3];
mess[i].Data[4] = data[4];
mess[i].Data[5] = data[5];
mess[i].Data[6] = data[6];
mess[i].Data[7] = data[7];
mess[i].DLC = 8;
mess[i].IsEID = true;
mess[i].IsRTR = false;

to:

bytesRead = f.Read(mess[i].Data, 0, (bytesRemaining > 8) ? 8 : bytesRemaining);
mess[i].ArbID = 0x17FF0101;
mess[i].DLC = bytesRead;
mess[i].IsEID = true;
mess[i].IsRTR = false;

@ andre.m: when you talk about analyser to you mean a DSO?

@ GUS yep I think there’s hand tuning to do (I can not let the board wasting its time to read the file by chunk of 8 bytes (by chunk of 1024 will be really better I think)

@ architect. hex fil for EMX are multiple of 8bytes I think. Does anyone can confirm? But you’re right something more generic is better. ( next task is to send xml files…)

As another note:

700Kb file to send it by chunks of 8 bytes

700KB/8 = 87500 messages

data = new byte[(bytesRemaining > 8) ? 8 : bytesRemaining];

will go through 700000 kb of memory. With my refactoring this is not an issue anymore.

But 87500 times mess[i] = new CAN.Message(); is an issue. With more than 20 bytes per CAN.Message object you are going through more than 1 750 000 more bytes. That taxes your loop with additional checks, garbage collection runs, etc.

I would pre-allocate and reuse a reasonable message array.

nop it allocates an array of 8 byte max.

yep I saw it and have already made the modification I will join my new code in few minutes

I also second Gus’s suggestion to read the file in bigger chunks. File IO is generally one of the slowest operation on any system.

 nop it allocates an array of 8 byte max.

Yes, 8 bytes at a time, but on every loop cycle, which is not needed.

What about this one?



        public void send(string path)
        {
            
            if (File.Exists(path))
            {
                FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, 8);
                int size = (int)f.Length;
                int bytesRemaining;
                int bytesRead;
                byte[] data;
                bytesRemaining = (int)f.Length;
                Debug.Print("Size of FW to send:" + bytesRemaining);
                CAN.Message[] mess = new CAN.Message[1];
                mess[0] = new CAN.Message();
                mess[0].ArbID = 0x17FF0101;
                mess[0].DLC = 8;
                mess[0].IsEID = true;
                mess[0].IsRTR = false;
                byte[] d = new byte[1024];
                while (bytesRemaining > 0)
                {
                    bytesRead = f.Read( d , 0,(int) ((bytesRemaining > 1024) ? 1024 : bytesRemaining));
                    for (int i = 0; i < bytesRead;i++ )
                    {
                        mess[0].Data[i%8] = d[i];
                        if (i % 8 == 7)
                        {
                            while (can.PostMessages(mess, 0, 1) != 1)
                            {
                            }
                        }
                    }
                    
                    bytesRemaining -= bytesRead;
                }
                f.Close();
                f.Dispose();
            }
        }

Looks a lot better? So how is the timing now?

107sec for this code I am disappointed :frowning:

Commenting Can.postmessages improves results of only 12 seconds

I don’t like ineficient code. If it’s fast '< 5sec) I don’t need a progress bar on the GUI side… In all manners… I will need one.

commenting data affectation and post messages and modulo computation results in 30seconds…

I need a progressbar…

Just for comparison… Recently we’ve implemented a firmware update over CAN. With EMX, 1Mbps, 1MB firmware file. Our best result was ~40seconds without checks, and ~120seconds with an echo every 256 messages from the receiving side.

97seconds for 700kB firmware file at 250kbps is therefore not so bad…

This is just the time to send file over can without updating.

Nop, I am just sending raw data. CAN Open specification does not seem to be so open… I did not find any free documentation. Maybe I need to search more…

@ andre.m - I’m not that into CAN, I just send and receive using GHI’s Premium CAN implementations. So I don’t really know what is a CANopen :slight_smile: