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.
Photo

Serial Port Data Lost


  • Please log in to reply
4 replies to this topic

#1 technochris1

technochris1

    New Member

  • Members
  • Pip
  • 3 posts

Posted 03 October 2013 - 04:24 PM

I came from Arduino with a project that needs more power and memory. This project receives data over the Uart/Rs232 line by line (each line ends in rn), then parses the data out. I have successfully ported over most of my program by the Serial port is where i am having problems. I have looked for hours for a reliable example of serial that works alongside of my program.   Ive tried "hari" Serial Port Helper class, and get missing data on occasion. Ive tried to make my own, without success.  

public class Program    {        static SerialPort _port;        static string Message = "";        static long RxTime;        static bool DataRx;        static void Main(string[] args)        {            _port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);            _port.DataReceived += new SerialDataReceivedEventHandler(serial_DataReceived);            _port.Open();            while (true)            {                while (Message.Length > 0)                {                    if ((Message.IndexOf('r') > 0) && (Message.IndexOf('n') > 0))                    {                        ParseData(Message.Substring(0, Message.IndexOf('r')));                        Message = Message.Substring(Message.IndexOf('n') + 1);                        DataRx = false;                    }                    if ((DateTime.Now.Ticks - RxTime >= (TimeSpan.TicksPerMillisecond * 500)) && DataRx)                    {                        Debug.Print("Incomplete/Currupt Data: " + Message);                        Message = "";                        DataRx = false;                    }                }                            }        }        private static void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)        {            RxTime = DateTime.Now.Ticks;            DataRx = true;            //System.Threading.Thread.Sleep(10);            string msg = null;            byte[] bytes = new byte[_port.BytesToRead];            _port.Read(bytes, 0, bytes.Length);            msg = new string(System.Text.Encoding.UTF8.GetChars(bytes));            if (msg != null)            {                Message += msg;            }        }        static public void ParseData(string s)        {            Debug.Print("Parse: " + s);            byte[] msg = System.Text.Encoding.UTF8.GetBytes(s);            _port.Write(msg, 0, msg.Length);            _port.WriteByte(13);            _port.WriteByte(10);        }    }

This Code work independently without a problem. But Once i add the rest of the program i start to lose data. I'm assuming that my program is what is causing the data loss.      The rest of my program is relatively simple. Data received is parsed (Looking for a room number, text explaining command). Then sent to add an instance of a class to an ArrayList. Every object in the ArrayList is looked at and removed after a certain time.



#2 SpitfireMike

SpitfireMike

    Member

  • Members
  • PipPip
  • 17 posts
  • LocationColorado Springs, CO, USA

Posted 04 October 2013 - 04:03 PM

I'd guess that the Main loop is spinning, preventing the event from firing.  Add a Thread.Sleep(10) in your Main while (true) loop to give the event queue a little breathing room.

 

The .NET event is similar to the Arduino interrupt, the big difference is the event is queued and processed in the same thread as the event source and interrupts are much more immediate.  Since the _port is running in the main thread, the light loop in Main isn't yielding to let the event fire.



#3 technochris1

technochris1

    New Member

  • Members
  • Pip
  • 3 posts

Posted 05 October 2013 - 02:56 PM

 

I'd guess that the Main loop is spinning, preventing the event from firing.  Add a Thread.Sleep(10) in your Main while (true) loop to give the event queue a little breathing room.

 

The .NET event is similar to the Arduino interrupt, the big difference is the event is queued and processed in the same thread as the event source and interrupts are much more immediate.  Since the _port is running in the main thread, the light loop in Main isn't yielding to let the event fire.

 

 

Should i Open the SerialPort in a separate Thread?

 

 

I get the events, Its just that when im parsing data in the ParseData method, i seem to loose data. I sent a 100 Line txt document to the serial port as quick as 9600 will let me. During the send into the netduino, i  loose data. it seems that when i read from the string "Message", line get cut out. I'm guessing the reason is because of threads and when one is writing the string , another is removing data, and parts get lost???? During my parseData method, i need to check  the SD card if certain data exists, Maybe this slows down the netduino to the point where my data is messed up?

 

Is there any Serialport that exists that can read line by line without data loss not matter how long the parse method takes?

 

 

I found this "Extension" to the SerialPort... But During the Parse, i still lose lines....

public class SimpleSerial : System.IO.Ports.SerialPort{    // CONSTRUCTORS -- Pass the Buck    public SimpleSerial(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits)        : base(portName, baudRate, parity, dataBits, stopBits) { }    public SimpleSerial(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits)        : base(portName, baudRate, parity, dataBits) { }    public SimpleSerial(string portName, int baudRate, System.IO.Ports.Parity parity)        : base(portName, baudRate, parity) { }    public SimpleSerial(string portName, int baudRate)        : base(portName, baudRate) { }    public SimpleSerial(string portName)        : base(portName) { }    /// <summary>    /// Writes the specified string to the serial port.    /// </summary>    /// <param name="txt"></param>    public void Write(string txt)    {        base.Write(Encoding.UTF8.GetBytes(txt), 0, txt.Length);    }    /// <summary>    /// Writes the specified string and the NewLine value to the output buffer.    /// </summary>    /// <param name="txt"></param>    public void WriteLine(string txt)    {        this.Write(txt + "rn");    }    /// <summary>    /// Reads all immediately available bytes, as binary data, in both the stream and the input buffer of the SerialPort object.    /// </summary>    /// <returns>byte[]</returns>    public byte[] ReadExistingBinary()    {        int arraySize = this.BytesToRead;        byte[] received = new byte[arraySize];        this.Read(received, 0, arraySize);        return received;    }    /// <summary>    /// Reads all immediately available bytes, based on the encoding, in both the stream and the input buffer of the SerialPort object.    /// </summary>    /// <returns>String</returns>    public string ReadExisting()    {        try        {            return new string(Encoding.UTF8.GetChars(this.ReadExistingBinary()));        }        catch (Exception)        {            return string.Empty;        }    }    /// <summary>    /// Opens a new serial port connection.    /// </summary>    public new void Open()    {        this._Remainder = string.Empty;  // clear the remainder so it doesn't get mixed with data from the new session        base.Open();    }    /// <summary>    /// Stores any incomplete message that hasn't yet been terminated with a delimiter.    /// This will be concatenated with new data from the next DataReceived event to (hopefully) form a complete message.     /// This property is only populated after the Deserialize() method has been called.    /// </summary>    private string _Remainder = string.Empty;    public string Remainder    {        get { return this._Remainder; }    }    /// <summary>    /// Splits data from a serial buffer into separate messages, provided that each message is delimited by one or more end-of-line character(s).    /// </summary>    /// <param name="delimiter">Character sequence that terminates a message line. Default is "rn".</param>    /// <returns>    /// An array of strings whose items correspond to individual messages, without the delimiters.    /// Only complete, properly terminated messages are included. Incomplete message fragments are saved to be appended to    /// the next received data.    ///     /// If no complete messages are found in the serial buffer, the output array will be empty with Length = 0.    /// </returns>    public string[] Deserialize(string delimiter = "rn")    {        string receivedData = string.Concat(this._Remainder, this.ReadExisting());   // attach the previous remainder to the new data        return SplitString(receivedData, out this._Remainder, delimiter);  // return itemized messages and store remainder for next pass    }    /// <summary>    /// Splits a stream into separate lines, given a delimiter.    /// </summary>    /// <param name="input">    /// The string that will be deserialized.    ///     /// Example:    /// Assume a device transmits serial messages, and each message is separated by rn (carriage return + line feed).    ///     /// For illustration, picture the following output from such a device:    /// First message.rn    /// Second message.rn    /// Third message.rn    /// Fourth message.rn    ///     /// Once a SerialPort object receives the first bytes, the DataReceived event will be fired,    /// and the interrupt handler may read a string from the serial buffer like so:    /// "First message.rnSecond message.rnThird message.rnFourth me"    ///     /// The message above has been cut off to simulate the DataReceived event being fired before the sender has finished     /// transmitting all messages (the "ssage.rn" characters have not yet traveled down the wire, so to speak).    /// At the moment the DataReceived event is fired, the interrupt handler only has access to the (truncated)     /// input message above.    ///     /// In this example, the string from the serial buffer will be the input to this method.    /// </param>    /// <param name="remainder">    /// Any incomplete messages that have not yet been properly terminated will be returned via this output parameter.    /// In the above example, this parameter will return "Fourth me". Ideally, this output parameter will be appended to the next    /// transmission to reconstruct the next complete message.    /// </param>    /// <param name="delimiter">    /// A string specifying the delimiter between messages.     /// If omitted, this defaults to "rn" (carriage return + line feed).    /// </param>    /// <param name="includeDelimiterInOutput">    /// Determines whether each item in the output array will include the specified delimiter.    /// If True, the delimiter will be included at the end of each string in the output array.    /// If False (default), the delimiter will be excluded from the output strings.    /// </param>    /// <returns>    /// string[]    /// Every item in this string array will be an individual, complete message. The first element    /// in the array corresponds to the first message, and so forth. The length of the array will be equal to the number of    /// complete messages extracted from the input string.    ///     /// From the above example, if includeDelimiterInOutput == True, this output will be:    /// output[0] = "First message.rn"    /// output[1] = "Second message.rn"    /// output[2] = "Third message.rn"    ///     /// If no complete messages have been received, the output array will be empty with Length = 0.    /// </returns>    private static string[] SplitString(string input, out string remainder, string delimiter = "rn", bool includeDelimiterInOutput = false)    {        string[] prelimOutput = input.Split(delimiter.ToCharArray());        // Check last element of prelimOutput to determine if it was a delimiter.        // We know that the last element was a delimiter if the string.Split() method makes it empty.        if (prelimOutput[prelimOutput.Length - 1] == string.Empty)            remainder = string.Empty;   // input string terminated in a delimiter, so there is no remainder        else        {            remainder = prelimOutput[prelimOutput.Length - 1];  // store the remainder            prelimOutput[prelimOutput.Length - 1] = string.Empty;   // remove the remainder string from prelimOutput to avoid redundancy        }        if (includeDelimiterInOutput == true)            return ScrubStringArray(prelimOutput, removeString: string.Empty, delimiter: delimiter);        else            return ScrubStringArray(prelimOutput, removeString: string.Empty, delimiter: string.Empty);    }    /// <summary>    /// Removes items in an input array that are equal to a specified string.    /// </summary>    /// <param name="input">String array to scrub.</param>    /// <param name="removeString">String whose occurrences will be removed if an item consists of it. Default: string.Empty.</param>    /// <param name="delimiter">    /// Delimiter that will be appended to the end of each element in the output array. Default: rn (carriage return + line feed).    /// To omit delimiters from the end of each message, set this parameter to string.Empty.    /// </param>    /// <returns>    /// String array containing only desired strings. The length of this output will likely be shorter than the input array.    /// </returns>    private static string[] ScrubStringArray(string[] input, string removeString = "", string delimiter = "rn")    {        // Note: I originally wanted to use a System.Collections.ArrayList object here and then run the ToArray() method,        // but the compiler throws runtime exceptions for some reason, so I've resorted to this manual array-copying approach instead.        int numOutputElements = 0;        // Determine the bounds of the output array by looking for input elements that meet inclusion criterion        for (int k = 0; k < input.Length; k++)        {            if (input[k] != removeString)                numOutputElements++;        }        // Declare and populate output array        string[] output = new string[numOutputElements];        int m = 0;  // output index        for (int k = 0; k < input.Length; k++)        {            if (input[k] != removeString)            {                output[m] = input[k] + delimiter;                m++;            }        }        return output;    }


#4 YuvaRaja

YuvaRaja

    New Member

  • Members
  • Pip
  • 8 posts
  • LocationINDIA

Posted 08 October 2013 - 12:08 PM

Create a thread and continuously check for data is available or not..

ex:

public static void SomeThread()        {            while (true)            {                int t = Serialport.BytesToRead;                if (t > 0)                {                    receivedData = new byte[t];                    Serialport.Read(receivedData, 0, receivedData.Length);                }            }        }


#5 MarkW

MarkW

    New Member

  • Members
  • Pip
  • 1 posts

Posted 31 October 2013 - 12:30 AM

Out of curiosity, what version of Netduino are you using?

 

I had a similar issue using Hari's code with the basic Netduino.  In my case I modified the code to use the proper references for the basic Netduino as Hari's code called later versions.  I had a 3 button form for FWD, REV and STOP to control a steeping motor.  The motor would basically run indefinitely until the STOP button was pressed.

 

The problem I was having was that the event would get out of sync with the button.  I would never lose the data but it would get into a situation where the response would not trigger until another button on_click occurred.  For example, I could press FWD and the motor would start.  Then it would stop when I pressed STOP.  When I press FWD again, it might not go.  If I pressed STOP again, the motor would go forward and from then on would be out of sync with the button.  After a while, it would drop another step and I would be two steps out of sync.  Debug.print statements indicated that the contents of the buffer were not corrupt or lost; the trigger would just wait for the next on_click event before it acted.

 

I switched to a Netduino Plus 2 and after updating the references, I no longer had the problem.  Everything works as it should.  I'll point out that I am a beginner with both Netduino and C# so there could easily be some issue that I am not aware of or don't completely understand.






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

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.