Main Site Documentation

Problem with ring buffer for xbee


#1

Hello, all NTMFers! Learned a lot from this forum when reading previous posts. So thanks goes to everyone first.

I wrote a ring buffer to store the messeage coming from xbee module. The code works fine when each messeage is shorter or equal to 8 bytes but start to act wiredly when messeage is longer than 8 byte.
The following is my code, first a String array and two pointers were defined.


private static String [] dataRecivedbuffer = new String[20];
        private static byte dataReceivedbufferpointer1 = 0; //pointer for current location of received data ring buffer;
        private static byte dataReceivedbufferpointer2 = 0; //pointer for current location of data to process;
        private static byte ringbufferDirection = 0; // if dataPointer1 jump to the beginning of buffer then direction = 1.

The serial data received event handler was like the following. Available data size was checked over and over again until it becomes 0. Each bout of data were then transferred into a string.

private static void xbee_DataReceived(object Sender, SerialDataReceivedEventArgs e)
         {
            int i = 0;
            int count = xbee.BytesToRead;
            byte[] buffer = new byte[90];

            while (count > 0)
             {
                 xbee.Read(buffer,i,count);
                 i+=count;
                 count = xbee.BytesToRead;
              }
            String tempStr = new String(Encoding.UTF8.GetChars(buffer));
            dataRecivedbuffer[dataReceivedbufferpointer1] = tempStr;
            dataReceivedbufferpointer1++;
            if (dataReceivedbufferpointer1 == 20)
             {
                 dataReceivedbufferpointer1 = 0;
                 ringbufferDirection = 1;
             }
          }

In the main funtion, data were extracted from the string array, displayed onto a HD44780 LCD.

while (true)
            {
               
                while ((ringbufferDirection == 0 && (dataReceivedbufferpointer2 < dataReceivedbufferpointer1))|| (ringbufferDirection == 1 && dataReceivedbufferpointer2 > dataReceivedbufferpointer1))
                {
                    //Display received messeage on LCD, to minic data processing
                    lcd.MoveCursor(0x54);
                    lcd.WriteData(dataRecivedbuffer[dataReceivedbufferpointer2]);
                                        
                    dataReceivedbufferpointer2++;
                    if (dataReceivedbufferpointer2 == 20)
                    {
                        dataReceivedbufferpointer2 = 0;
                        ringbufferDirection = 0;
                    }
                }                
                Thread.Sleep(100);
            }            

Xbee coordinator in AT mode was attached to Panda II and another xbee in API/AT( I tried both) mode was attached to cerbuino bee.

As I said in the beginning, those code breaks only when messeage is longer than 8 byte. I am not quite sure what would be the problem. Can some body help me figure out?

Thanks in advance,

Jerome


#2

Hi Jerome, welcome to the forum

First point, declaring your byte buffer in the datarecieved handler will be costly; I’d declare that outside the scope of the handler and re-use it always, it will reduce your GC dramatically.

Second, circular buffers tend to not use a “direction” pointer like you have, so I suspect that you’ve got some dependency there. You usually only need to keep the read pointer and the write pointer; most times you code to keep one free element so you simplify the full vs empty distinction. I didn’t run a thorough evaluation on your code though… So I would suggest you step through your code and debug what your pointers are doing when you get past that 8 byte limit


#3

Hi, Brett,

Thanks for your prompt reply. This is a homebrew ring buffer, I’ll look for more usual implementation later. I agree with you that declare byte buffer in handler is costy. I have newer version built on jagged byte array which is declared outside thehandler. But the problem in that newer version is the same.

I sort of stepped through the code by displaying the value of both two pointers and length of each bout of messeage on LCD. What happens is, when messeage exceeds 8 bytes in length, the message is split into two pieces.

msgSent: "123456789A"
two msg will show up on LCD:
msg1: "12345678"
msg2: “9A”

If msg length exceeds 16, then it will be split into 3 on the receiving end.


#4

@ Jerome - welcome to the community!

With serial data there’s never a guarantee that all of your data will arrive in a single DataReceived event. You always have to look for the start and end of your message and concatenate between events if necessary.

Also, you should consider using char[] instead of String[] since Strings in C# are immutable and will cause excessive GC even if declared as private static.


#5

@ ianlee74 thanks for your reply.

I actually thought this. The following is my latest code:

private static SerialPort xbee;
        private static byte[][] dataRecivedbuffer = new byte [20][];
        private static byte dataReceivedbufferpointer1 = 0; //pointer for current location of received data ring buffer;
        private static byte dataReceivedbufferpointer2 = 0; //pointer for current location of data to process;
        private static byte ringbufferDirection = 0; // if dataPointer1 jump to the beginning of buffer then direction = 1.
        private static byte count = 1; // bytes counts used in data received event handler
        private static byte[] buffer = new byte[2]; // temporal buffer for msg parsing, only buffer[0] is used.

The event handler: an “~” is inserted at the begining of each msg at the sending end. when “~” is found at receiving end, writing point dataReceivedpointer1 forward 1 slot.

 private static void xbee_DataReceived(object Sender, SerialDataReceivedEventArgs e)
         {
            count = 1;
            
            while (xbee.BytesToRead > 0)
             {
                xbee.Read(buffer,0,1);
                if (buffer[0] != 126)
                {
                    dataRecivedbuffer[dataReceivedbufferpointer1][count] = buffer[0];
                    count++;
                }
                else
                {
                    dataRecivedbuffer[dataReceivedbufferpointer1][0] = (byte)(count - 1); // pass messeage length into the first number of each array
                    dataReceivedbufferpointer1++;                    
                    if (dataReceivedbufferpointer1 == 20)
                    {
                        dataReceivedbufferpointer1 = 0;
                        ringbufferDirection = 1;
                    }
                    dataRecivedbuffer[dataReceivedbufferpointer1][1] = buffer[0]; // pass new byte directly into the beggining of new messeage
                    dataRecivedbuffer[dataReceivedbufferpointer1][0] = 1; // set the length of new messeage to 1;
                    count = 2;
                }                
              }
          }

But the problem is still the same, I am still get broken msg. I must miss something.


#6

Put a break at the point where you start writing to the display and inspect your dataRecivedBuffer. I suspect it looks like you expect and there’s something wrong with the way you’re writing to the display.

Also, if you are creating the data stream then I would consider including a separate character to mark the end of the message rather than relying on the next start to come along. Sometimes you will receive incomplete messages. Also, how would you know when you reached the end of the very last message.


#7

@ ianlee74 Thanks for reminding me. It seems msg longer than 8 byte does come in seperate events. That was the problem my first code.

The problem with my second code is that I should not reset the length of msg (count) whenever an event is triggered. When I simply delete that line of code, everyting starts to work like a charm.

Thanks,ianlee74, Brett


#8

@ ianlee74 -
To answer your question, the code is still a start point, I rely on the start byte “~” as indication of both beginning of new msg and also the end of last msg.

In the future a msg length byte will be placed after start byte, and also a checksum will be placed at the end of msg, per xbee api msg frame.

Also the ring buffer will be in a one dimention byte[] instead of jagged array.

Thanks,
Jerome


#9

Glad you got it going. I look forward to seeing your projects posted here in the future.