Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.

Fahrice's Content

There have been 2 items by Fahrice (Search limited from 20-April 23)


By content type

See this member's

Sort by                Order  

#57131 Multiple Serials Ports in Threads - Best Practices

Posted by Fahrice on 26 March 2014 - 04:09 AM in Netduino Plus 2 (and Netduino Plus 1)

Hi Homey,

 

I've been looking over the code you've posted in this thread for a bit now trying to understand what you're trying to do.  I'd like to offer up some help with regard to the serial port issue.  Basically, I've had weird/interesting/inconsistent results when trying to build up a system to communicate via a serial port as well, but I've finally squashed the last bug and got it working very nicely.  I collected various ideas and bits and pieces of code from several different examples I've seen and put it all together into something that is clean, concise, and effective.

 

Here are some key points about the setup I've ended up with:

 

1) I'm not using multiple threads, so it's simple.

 

2) It's FAST.  I'm using this for serial communications using SPP over bluetooth, and the data transfer is nearly instantaneous.

 

3) My buffer and bufferLength variables are class variables, not local variables - as you'll see by the "this" prefix in the code below.  The bufferLength variable stores a value indicating the number of bytes in the buffer at any given time.  I keep them as class variables so their content persists through multiple DataReceived events fired by SerialPort.

 

4) I use the DataReceived event on SerialPort for one thing and one thing only:  receiving the data, storing it in the buffer, and keeping bufferLength updated.  After those things happen, I fire an event to notify anyone who cares that there is new data in the buffer.

 

Here is the code that handles SerialPort's DataReceived event:

private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
    this.VerifyOpenPort();
            
    lock (this.locker)
    {
        int bytesReceived = this.serialPort.Read(this.buffer, this.bufferLength, this.serialPort.BytesToRead);

        if (bytesReceived > 0)
        {
            this.bufferLength += bytesReceived;

            if (this.bufferLength >= BufferMax)
                throw new Exception("Buffer overflow.");

            this.RaiseDataReceived();
        }
    }
}

Now here is a bit of code I picked up from another user on these forums, although it is modified.  This allows an outside caller to try to read a line of text from the buffer.  If there isn't a full line (meaning a CR/LF character isn't in the byte array), an empty string is returned.  This method is in the same class as the OnDataReceived method above.:

public string ReadLine()
{
    string line = string.Empty;

    lock (this.locker)
    {
        for (int i = 0; i < this.bufferLength; i++)
        {
            if (this.buffer[i] == '\r' || this.buffer[i] == '\n')
            {
                this.buffer[i] = 0;

                line += new string(Encoding.UTF8.GetChars(this.buffer, 0, i));

                if (line != null && line != string.Empty)
                    Debug.Print("Line read: {" + line + "}");
                
                this.bufferLength = this.bufferLength - i - 1;

                Array.Copy(this.buffer, i + 1, this.buffer, 0, this.bufferLength);

                break;
            }
        }
    }

    return line;
}

Now for your purposes, you could modify this to return byte[] and grab the first 72 bytes from the buffer and return them - OR, if there aren't 72 bytes in the buffer yet, return null.  Here's what that might look like:

public byte[] ReadBytes(int quantity)
{
    byte[] bytes = new byte[quantity];

    lock (this.locker)
    {
        if (this.bufferLength >= quantity)
        {
            // Copy X number of bytes from class buffer into local buffer
            Array.Copy(this.buffer, 0, bytes, 0, quantity);

            // Update bufferLength
            this.bufferLength -= quantity;

            // Move bytes remaining in buffer to the beginning of the buffer
            Array.Copy(this.buffer, quantity, this.buffer, 0, this.bufferLength);
        }
        else
        {
            bytes = null;
        }
    }

    return bytes;
}

The example above will allow you to request any number of bytes from the buffer - 72 included. :-)

 

Now, up above in the first code block, you see:  this.RaiseDataReceived();

 

This method simply fires the DataReceived event on the containing class (*NOT* SerialPort) if there are any listeners.  Here's the code for it:

// This raises the event
private void RaiseDataReceived()
{
    var handler = this.DataReceived;
    if (handler != null)
    {
        handler();
    }
}

// This is the event
public event DataReceivedEventHandler DataReceived;

// And this is the definition of that DataReceivedEventHandler delegate
// This does not have to be in the same class as everything else I've
// shared - in fact, it shouldn't be in ANY class - just the same namespace.
public delegate void DataReceivedEventHandler();

Now, this is how the "ReadLine" method is used in conjunction with the DataReceived event to process the info in the buffer:

// Wire up a handler for the DataReceived event on an instance of the class 
// that contains all of the code above
this.myInstance.DataReceived += this.OnDataReceived;

// Here's the code for the handler
private void OnDataReceived()
{
    string message = this.myInstance.ReadLine();

    while (message != null && message != string.Empty)
    {
        Console.WriteLine("Received message: {0}", message);

        message = this.myInstance.ReadLine();
    }
}

And here is a sample implementation you could use to read X number of bytes rather than strings:

private void OnDataReceived()
{
    byte[] bytes = this.myInstance.ReadBytes(72);

    while (bytes != null)
    {
        /////////////////////////////////////
        // Do something here with your bytes!
        /////////////////////////////////////

        // Then try to read in another set of bytes
        bytes = this.myInstance.ReadBytes(72);
    }
}

Hopefully this all makes sense.  The idea is that you have a buffer that stores data that is loaded into it by the DataReceived event on the SerialPort instance.  Every time it fires and the data is buffered, it fires another event that tells your message processing code to use the ReadLine/ReadBytes method to pull the data out of the buffer.  The two sides of the buffer (the side that writes & the side that reads) operate completely independently of each other.

 

The bug I had was in my consumer "OnDataReceived" method where I call ReadLine.  I was only calling ReadLine one time each time the DataReceived event fired.  Well, the processing of that line wouldn't be completed and more data was already coming into the buffer faster than it was being processed, and eventually a buffer overflow would occur.  Now, every time new data arrives, ReadLine will be called repeatedly until the buffer is emptied.

 

Finally, I have one more thing that I wanted to suggest you try.  I did some playing around, and I added the following code to my DataRecieved handler for the SerialPort - and it worked.  You mentioned before "how do I know how long to Thread.Sleep for to let the buffer fill up with 72 bytes?"  Well, this will solve that and might be the simplest thing for you:

private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
    this.VerifyOpenPort();

    while (this.serialPort.BytesToRead < 72)
    {
        Thread.Sleep(1); // Just enough to not consume *every* cycle of the processor
    }
            
    lock (this.locker)
    {
        int bytesReceived = this.serialPort.Read(this.buffer, this.bufferLength, this.serialPort.BytesToRead);

        if (bytesReceived > 0)
        {
            this.bufferLength += bytesReceived;

            if (this.bufferLength >= BufferMax)
                throw new Exception("Buffer overflow.");

            this.RaiseDataReceived();
        }
    }
}

Let me know if you have any questions or if you found this useful.

 

Thanks!

 

Fahrice




#57029 First steps: SerialPort problem

Posted by Fahrice on 23 March 2014 - 06:54 PM in Visual Studio

This post seems to be about a month old so maybe you've already figured out the solution, but...

 

It seems to me that what is likely happening is HyperTerminal is not configured to display the keys you're typing - aka "local echo".  If this is true, then what is happening is:

 

1. you type a character (it is NOT displayed in HyperTerminal)

2. your Netduino receives the character

3. your Netduino blinks the LED

4. your Netduino sends the character back to HyperTerminal

5. HyperTerminal displays what it received

 

The question is - what are you expecting to see?  If you press "A", are you expecting to see "AA"?

 

Try looking for a setting in HyperTerminal that allows you to turn ON local echo.

 

I ran into this same exact issue with Putty this weekend.

 

Hope this helps someone!





home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.