RS232 module drops characters using Raptor board

Thanks, but what difference does it make if I use the rs232PLC object or the sender object to get the .BytesToRead value? Both references should point to the same object in memory. Execution is in the PLC_DataReceived() function because in the initialization section, the .DataReceived event property was set to point to the PLC_DataReceived() function, like this:.


  rs232PLC.serialPort.DataReceived += new GTI.Serial.DataReceivedEventHandler(PLC_DataReceived);

Anyway, I will try your change and see what happens.

Hi,
Iā€™m not 100% sure as well about the facts and not sure if I use the right terminology, but:

The SerialPort DataReceived event gets called when bytes are available. Sometimes this happens after just one byte or a few bytes are received and sometimes youā€™ll happen to get your whole messageā€“but you should always get all the data in a follow-up DataReceived calls.

I think, the sender object of the eventhandler holds the received bytes and the count of received bytes from the moment when the event was called and these values do not change in the following time (the parameters are passed by value, not by reference). In the meantime between the call of the event and the reading of properties of the sender object in the eventhandler new data can arrive on the serial port. When you now read directly from the serial port instead from the sender object you get different values for the count of available bytes and this can interfere with the mechanism of creating the next call of the eventhandler.

Perhaps one of the other forum members knows for certain how things work and can give us an explanation.

Cheers
Roland

Edit: Assumption proved to be wrong

See the section:

ā€œPassing by Reference vs. Passing by Valueā€
here:

Objects are always passed by reference. sender is an object, so the compiler will create code to pass it by reference. But I will try it.

It will be possible to find out by experiments how it really is.
I would be surprised if the sender.BytesToRead property of the Eventhandler would change its value if you read it several times in the eventhandler while new Serial data are arriving and Iā€™m convinced that the same property of the Serial port itself does.

Edit: Assumption proved to be wrong

I ran the code that uses the sender object get the sender.BytesToRead() and call the sender.Read(), and the problem remains. I also disabled the TCP/IP communication, that did not help. We need to look somewhere else for the problem.

Did you know that this is a known issue?
Issues:
-TCP connections can fail after the first time when using MFDeploy.
-UART loses data during heavy loads.

https://www.ghielectronics.com/support/netmf/sdk/19/netmf-and-gadgeteer-package-2014-r2

Has someone at GHI a clue about this?

As far as it concerns possible differences between using reading the Sender object or the Serial port directly my opinion proved to be wrong.
I just made a test and I saw no difference. As you said the Parameter is passed by reference and changes its value when new data arrive or data are read.

But in my test where I sent short strings with 9600 baud from my PC to the raptor, I saw no data corruption.
Did not yet try to send and receive over the same Serial port.

Did you try with reading and writing in different threads?

Edit: I use NETMF 4.3

No, I have not tried that yet. I am using my own thread to write characters to the serial port, but I am using the Event Handler callback function that I register like this:

.serialPort.DataReceived += new GTI.Serial.DataReceivedEventHandler(PLC_DataReceived);

I assume this event handler is called from the Gadgeteer thread that dispatches events to registered event handlers. So effectively, I am using two different threads - my thread for writing and the Gadgeteer main thread for reading.

I will put in my own thread to poll the serial port for received characters. How often should I poll it, do you think?

when i include a statement that Debug.prints the Thread Id

Debug.Print("Write (or Read) Thread No. " + Thread.CurrentThread.ManagedThreadId.ToString());

I get the same value from inside the Eventhandler and from the main thread where I write to the port.
Bye til tomorrow

how quick can you handle between actioning based on your data ? That will probably tell you how quickly you need to assemble bytes.

I would possibly start a different way though, and work on tuning the timing as you go, as your app needs. Iā€™d first up simply start a new thread which has a while (1) loop and simply does a thread.sleep(20) when there is no characters to be read. Remember the scheduler will timeslice you out for 20ms if there are other threads active, so a 20ms sleep is not a bad starting pointā€¦ even a thread.sleep(1) might be sufficient to allow your other threads to run fine, but you may also have problems if those other threads are busy (like I imagine networking could get)ā€¦ to me this approach is simpler in contrast to trying to run a timer every X msec.

I had similar issues last year on a Cobra II - random characters dropping. I did lots of experiments and never really figured it out. Similar issue with each of two different rs232 devices, much worse when operating two serial ports at the same time. (although no issues EVER when connecting the two ports as a loop-back test! ? even running for days)
I had two limited issues with the built-in LineReceivedEventHandler.

  1. it is not on a unique thread, so your handling code must get the data and get out - donā€™t sleep or wait for something to happen, I would not even do any processing besides adding it to a queue. I always used SynchronousUnsafeEventInvocation=true with LineReceivedEventHandler
  2. there can sometimes be a delay between the rs232 data arriving and the handler getting called. Especially when using two portsā€¦ I think if data arrives on both ports at the same time, only one port handler gets called right away.
    I got the lowest latencies by watching each serial port on itā€™s own thread. Just waiting for data & processing it:
 while(true) {
    int read = port.Read(byteBuffer, 0, byteBuffer.Length);
    if (read > 0) { 
         // process  GetChars(byteBuffer, 0, read)
    }
  }

I have more detailed code examples in codeshare:
https://www.ghielectronics.com/community/codeshare/entry/748

I ended up solving this in two ways:

  1. switched from rs232 to tcp (equipment had both interfaces & my wrapper abstracted the rs232/tcp layer)
  2. added a data validation/resend layer to the rs232 only equipment, so a random missing character would be tolerated.

Thanks, that help made a big difference. I stopped using the event handler property of the RS232 module, instead I started a thread function shown below. Each time SerialPort.Read() returns, I put the characters into a Queue for later handling by another thread. Instead of missing a character in every 15 messages received, a missing character occurs about every 100 messages. Still not perfect, though.


        /// <summary>
        /// Received data polling thread for Intermec RFID reader serial port
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        void RS232DataReader()
        {
            try
            {
                while (true)
                {
                    try
                    {
                        //wait for RS232 data
                        if ((RS232Count = RS232RFID.serialPort.Read(RS232Data, 0, MaxRxDataQueueSize)) > 0)
                        {
                            if (LoggingLevel > 1)
                            {
                                Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " - "
                                    + KioskUtilities.BytesToHexString(RS232Data, RS232Count)
                                    + " : " + KioskUtilities.BytesToAsciiString(RS232Data, RS232Count));
                            }
                            //Put bytes received into a Queue, prevent the Queue size from expanding forever
                            lock (RxDataQueue.SyncRoot)
                            {
                                if (RxDataQueue.Count < MaxRxDataQueueSize)
                                {
                                    for (int i = 0; i < RS232Count; i++)
                                    {
                                        if (RxDataQueue.Count < MaxRxDataQueueSize)
                                        {
                                            RxDataQueue.Enqueue(RS232Data[i]);
                                        }
                                        else
                                        {
                                            Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " - RxDataQueue.Count = "
                                                    + RxDataQueue.Count.ToString() + " RxDataQueue is full");
                                            break;
                                        }
                                    }
                                }
                                else
                                {
                                    Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " - RxDataQueue.Count = "
                                            + RxDataQueue.Count.ToString() + " RxDataQueue is full");
                                }

                                if (RxDataQueue.Count > 0)
                                {
                                    //Signal other thread that we received some bytes
                                    RxDataEvent.Set();
                                }
                            }
                        }
                    }
                    catch (GTI.Serial.PortNotOpenException ex2)
                    {
                        KioskUtilities.LogException(code + " exception - serial port not open ", ex2);
                        RS232RFID.serialPort.Open();
                    }
                }
            }
            catch (Exception ex1)
            {
                KioskUtilities.LogException(code + " exception - ", ex1);
                KioskUtilities.Reboot();
            }
        }


Hi,
Great, thatā€™s some progress but of course the goal is uncorrupted transmission.
Iā€™m still involved.
I now can write from the raptor over the serial port to a PC console application which sends data back to the raptor. Until now I saw no data corruption but i did not test over longer periods.

Here is the code of the PC application, parhaps you can use it.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;

namespace Serial_Returner
{
    class Program
    {
        private static SerialPort _comPort;
        
        static void Main(string[] args)
        {
            //_comPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
            _comPort = new SerialPort("COM1", 38400, Parity.None, 8, StopBits.One);
            _comPort.Open();
            _comPort.DataReceived += new SerialDataReceivedEventHandler(_comPort_DataReceived);
            while (true)
            { }
        }

        static void _comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int dataLength = _comPort.BytesToRead;
            byte[] data = new byte[dataLength];
            int nbrDataRead = _comPort.Read(data, 0, dataLength);
            if (nbrDataRead == 0)
                return;
            WriteBytes(data);
        }
       
        private static void WriteBytes(byte[] bytes)
        {
            _comPort.Write(bytes, 0, bytes.Length);
            Console.Write(Encoding.ASCII.GetString(bytes));
        }
    }

        
}

@ dspacek -
can you please show the code segment where you create and start the RS232DataReader and the code of your Queue Class

Hi,
I think that I know the reason for the data corruption.
As I think the reason is that you use the RxDataEvent AutoResetEvent in your sendData method. This would perhaps be ok if the datareceived eventhandler would run in a separate thread. In the Gadgeteer and NETMF world this is not always true.
In NETMF there is the rs232.serialPort.SynchronousUnsafeEventInvocation property. If this is set to false (is standard) the eventhandler runs in the main program thread. In the Gadgeteer Serial Class this property can not be set, so I think itā€™s per standard set to false.
An Autoresetevent can only be used, if a codesegment from another thread sets or resets it. Your code stops at the
rv = RxDataEvent.WaitOne(ms_time + 100, false);
statement and can only continue when the ā€œescape timeā€ runs out. It can not be set or reset from a code segment in the same thread, as this thread does not run.

To the SynchronousUnsafeEventInvocation issue see this link:

http://www.netmf.com/gadgeteer/forum/default.aspx?g=posts&t=2301

ā€œAn interupt may occur on a thread other than the application thread. When SynchronousUnsafeEventInvocation is false (the default), the Interrupt event, which is raised in response to the interrupt on the interface, is marshalled to your application thread and raised asynchronously; whenSynchronousUnsafeEventInvocation is true, the Interrupt event is raised synchronously on the same thread that generated the interupt.ā€

Have a look at my Codeshare contribution
RS232 Gadgeteer <=> PC Round-n-Round (1)
https://www.ghielectronics.com/community/codeshare/entry/921

1 Like

Class Program is where my code starts. I start a thread function ProcessCommandsLoop(). Before going into an endless loop, the ProcessCommandsLoop() function calls new PrototypeDevice(), and in the constructor for PrototypeDevice(), I call the constructor for class IntermecIF2(). The IntermecIF2() constructor starts the RS232DataReader thread.


namespace SmartTruckRaptor
{

    public partial class Program
    {
        private AutoResetEvent StartEvent;
        private int StartTimer;
        private GT.Timer DelayStartTimer;
        private KioskTouchScreen TouchScreenServer;

        /// <summary>
        ///This method is run when the mainboard is powered up or reset.    
        /// </summary>
        void ProgramStarted()
        {
            try
            {
                KioskUtilities.StartWatchDogKicker(this, Mainboard);
                //Initialize the Glide touch screen user interface object
                TouchScreenServer = new KioskTouchScreen();

                StartEvent = new AutoResetEvent(false);
                StartTimer = 3;
                DelayStartTimer = new GT.Timer(1000, GT.Timer.BehaviorType.RunContinuously); // every second (1000ms)
                DelayStartTimer.Tick += new GT.Timer.TickEventHandler(DelayStartTimer_Tick);
                DelayStartTimer.Start();

                ThreadStart starter = new ThreadStart(ProcessCommandsLoop);
                Thread th = new Thread(starter);
                th.Start();

            }
            catch (Exception ex)
            {
                KioskUtilities.LogException("ProgramStarted() exception", ex);
                KioskUtilities.Reboot();
            }
        }

        /// <summary>
        /// Count down X seconds, then set the StartEvent to start the program
        /// Needed to give debugger time to attach and NETMF to initialize
        /// </summary>
        /// <param name="timer"></param>
        void DelayStartTimer_Tick(GT.Timer timer)
        {
            if (StartTimer > 0)
            {
                Debug.Print("Waiting for debugger - time = " + StartTimer.ToString());
                --StartTimer;
            }
            else
            {
                StartEvent.Set();
                DelayStartTimer.Stop();
            }
        }

        /// <summary>
        /// Endless loop to poll for commands from the DeviceHive server. This must be in a thread so that
        /// the main thread is not blocked and the GHI dispatcher runs to service Gadgeteer devices.
        /// </summary>
        void ProcessCommandsLoop()
        {
            try
            {
                bool rv = false;
                //wait for Gadgeteer system to start up and debugger to attach if needed
                StartEvent.WaitOne();
                PrototypeDevice Raptor = new PrototypeDevice(ethernet_ENC28,
                                                            rs232, rs2322,  rs2323, rs2324, rs485, 
                                                            relay_X1, keypad_KP16, display_T43, sdCard, temperatureHumidity, TouchScreenServer);
                if (Raptor.Init())
                {
                    //Cobra ethernet TCP/IP is started
                    while (true)
                    {
                        //keep trying to connect until connected
                        while (!rv)
                        {
                            rv = Raptor.Connect();
                        }
                        //keep processing commands until error
                        while (rv)
                        {
                            rv = Raptor.ProcessCommands();
                        }
                    }
                }
                else
                {
                    Debug.Print("ProcessCommandsLoop() error - Cobra Ethernet cannot be started");
                }
            }
            catch (Exception ex)
            {
                //This is the top-level exception catch
                KioskUtilities.LogException("ProcessCommandsLoop() exception", ex);
                KioskUtilities.Reboot();
            }
            finally
            {
                Debug.Print("Command processing has exited");
                KioskUtilities.Reboot();
            }
        }

    }
}



    /// <summary>
    /// Class to handle writing text to the ProLite display and traffic light control
    /// A type of "EquipmentEngine" so it can be an equipment item in the DeviceHive Server
    /// </summary>
    public class IntermecIF2 : EquipmentEngine
    {
        public event IF2EventHandler IF2Event;
        private int LoggingLevel;
        private SystemConfigData systemData;
        private GTME.RS232 RS232RFID; 
        private const string StateParameter = "state";   //for DeviceHive notification
        private const string DeviceTypeName = "Intermec IF2 RFID reader";  //for DeviceHive registration

        private const int MaxRxDataQueueSize = 2000;   //limit on qty of chars to put on RS232 received data Queue 
        private const int Baud_Rate = 115200;
        private ByteQueue RxDataQueue;
        private AutoResetEvent RxDataEvent;
        //private int RxDataQueueSizeExpected;
        private byte[] RxDataBuf;
        //private int RxDataCount;
        private byte[] RS232Data;
        private int RS232Count;
        private StringBuilder RxLine;
        private Queue RxLineQueue;
        private Queue AsyncLineQueue;
        private AutoResetEvent RxLineQueueEvent;
        private AutoResetEvent AsyncLineQueueEvent;
        private ArrayList RxLineList;
        private Object SendDataLock;
        private DeviceEngine ThisDevice;

        public bool InUse { get; set; }

        /// <summary>
        /// Constructs a IntermecIF2 RFID reader class by given parameters
        /// </summary>
        /// <param name="dev">Parent device</param>
        /// <param name="_rs232RFID">RFID reader RS232 port</param>
        /// <param name="_LoggingLevel">Logging detail level</param>
        /// <param name="Code">Equipment code</param>
        public IntermecIF2( DeviceEngine dev, GTME.RS232 _rs232RFID, SystemConfigData _systemData, SmartTruckConfigData _SmartTruckData, string Code)
            : base(dev)
        {
            Debug.Print("Initializing " + Code);
            code = Code;
            name = Code;
            type = DeviceTypeName; // v6
            ThisDevice = dev;
            systemData = _systemData;
            LoggingLevel = _systemData.LoggingLevel;
            RS232RFID = _rs232RFID;
            InUse = (_rs232RFID != null);

            //RxDataQueueSizeExpected = 0;
            RxDataQueue = new ByteQueue(MaxRxDataQueueSize);
            RxDataEvent = new AutoResetEvent(false);
            SendDataLock = new Object();
            RxDataBuf = new byte[MaxRxDataQueueSize];
            //RxDataCount = 0;
            RS232Data = new byte[MaxRxDataQueueSize];
            RS232Count = 0;
            RxLine = new StringBuilder();
            RxLineQueue = new Queue();
            AsyncLineQueue = new Queue();
            RxLineQueueEvent= new AutoResetEvent(false);
            AsyncLineQueueEvent = new AutoResetEvent(false); ;
            RxLineList = new ArrayList();

            if (InUse)
            {
                RS232RFID.Initialize(_SmartTruckData.IntermecBaudRate, GTI.Serial.SerialParity.None, GTI.Serial.SerialStopBits.One, 8, GTI.Serial.HardwareFlowControl.NotRequired);
                if (!ThisDevice.isPassThroughModeActive)
                {
                    //RS232RFID.serialPort.DataReceived += new GTI.Serial.DataReceivedEventHandler(IntermecIF2_DataReceived);

                    ThreadStart DataReaderThreadStarter = new ThreadStart(RS232DataReader);
                    Thread DataReaderThread = new Thread(DataReaderThreadStarter);
                    DataReaderThread.Start();

                    ThreadStart InputMessageThreadStarter = new ThreadStart(RxDataReceiver);
                    Thread InputMessageThread = new Thread(InputMessageThreadStarter);
                    InputMessageThread.Start();

                    ThreadStart AsynchronousEventThreadStarter = new ThreadStart(AsynchronousEventReceiver);
                    Thread AsynchronousEventThread = new Thread(AsynchronousEventThreadStarter);
                    AsynchronousEventThread.Start();
                }
            }

            Debug.Print("Done initializing " + Code);
        }

Here is my Queue class:


    public class ByteQueue
    {

        int Qsize;   // maximum number of elements           
        int Qstart;  // index of oldest element              
        int Qend;    // index at which to write new element  
        byte[] elementsQ;  // vector of elements                    

        public Object SyncRoot { get; set; }

        /// <summary>
        /// Construct Queue of bytes,given the maximum number of bytes
        /// </summary>
        /// <param name="_size"></param>
        public ByteQueue(int _size)
        {
            Qsize = _size + 1; // include space for empty element
            Qstart = 0;
            Qend = 0;
            elementsQ = new byte[Qsize];
            SyncRoot = new Object();
        }

        /// <summary>
        /// Make the Queue empty
        /// </summary>
        public void Clear()
        {
            Qstart = 0;
            Qend = 0;
        }

        /// <summary>
        /// Copy Queue to a byte array starting at an offset into the array
        /// </summary>
        /// <param name="Buffer"></param>
        /// <returns>Count of bytes copied</returns>
        public int CopyTo(byte[] Buffer, int BufindexStart)
        {
            int Bufindex = BufindexStart;
            int count = 0;
            int Qindex = Qstart;
            int i = 0;
            while ((Qindex != Qend) && (Bufindex < Buffer.Length))
            {
                Buffer[Bufindex++] = elementsQ[Qindex];
                count++;
                i = Qindex + 1;
                if (i >= Qsize) i = 0;
                Qindex = i;
            }
            return count;
        }

        /// <summary>
        /// Returns the count of bytes in the Queue
        /// </summary>
        public int Count
        {
            get
            {
                int result = 0;
                if (Qend > Qstart)
                    result = Qend - Qstart;
                else if (Qstart > Qend)
                    result = Qsize - (Qstart - Qend);
                else
                    result = 0;
                return result;
            }
        }


        /// <summary>
        /// Test if Queue is full
        /// </summary>
        /// <returns></returns>
        public bool isFull()
        {
            int i;
            i = Qend + 1;
            if (i >= Qsize) i = 0;
            return i == Qstart;
        }

        /// <summary>
        /// Test if Queue is empty
        /// </summary>
        /// <returns></returns>
        public bool isEmpty()
        {
            return Qend == Qstart;
        }

        /// <summary>
        /// Write an element to the end of the Queue, if it is not yet full.
        /// App must first call !isFull() to make sure Queue is not full.
        /// </summary>
        /// <param name="elem"></param>
        public void Enqueue(byte elem)
        {
            if (!isFull())
            {
                elementsQ[Qend] = elem;
                int i = Qend + 1;
                if (i >= Qsize) i = 0;
                Qend = i;
            }
        }

        /// <summary>
        /// Read and remove oldest element. 
        /// App must first call !isEmpty() to make sure Queue is not empty.
        /// </summary>
        /// <returns></returns>
        public byte Dequeue()
        {
            byte elem = 0;
            if (!isEmpty())
            {
                elem = elementsQ[Qstart];
                int i = Qstart + 1;
                if (i >= Qsize) i = 0;
                Qstart = i;
            }
            return elem;
        }
    }



Here is the remainder of my code that is relevant to this problem. I donā€™t think you are seeing the cause of the corruption. I am not using an event handler anymore, I am using a thread to send data to the device and another thread to poll the serial port to receive data, at your suggestion. I can see in the debug.print output right after calling RS232RFID.serialPort.Read() that one or more characters are missing.




        /// <summary>
        ///Send a variable length byte packet to the rs232 port for the RFID reader 
        ///will not accept any other commands while executing the previous command. 
        /// </summary>
        /// <param name="ms_time">time in milliseconds. Used for the AutoResetEvent object wait</param>
        /// <param name="CommandString">Command to Intermec RFID reader</param>
        /// <returns>true if packet sent without error and OK> response returned</returns>
        private bool SendData(int ms_time, string CommandString, ref ArrayList RxLineList)
        {
            bool rv = false;
            try
            {
                //RxDataCount = 0;
                if (InUse)
                {
                    if (RS232RFID.serialPort.IsOpen)
                    {
                        lock (RxLineQueue.SyncRoot)
                        {
                            //thread-safe initialize the Queue of lines of text received from the RFID reader
                            RxLineQueue.Clear();
                            RxLineQueueEvent.Reset();
                            RxLineList.Clear();
                        }
                        if (LoggingLevel > 0)
                        {
                            Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " SendData() " + CommandString);
                        }
                        //Send the command to the RFID reader
                        byte[] sendData = Encoding.UTF8.GetBytes(CommandString + "\r\n");
                        RS232RFID.serialPort.Write(sendData, 0, sendData.Length);
                        RS232RFID.serialPort.Flush();

                        //wait for a the OK> prompt to appear in the RFID reader received line Queue
                        while (rv = RxLineQueueEvent.WaitOne(ms_time, false))
                        {
                            lock (RxLineQueue.SyncRoot)
                            {
                                while (RxLineQueue.Count > 0)
                                {
                                    //thread-safe capture of the received char strings from the receive Queue
                                    string RxLine = (string)RxLineQueue.Dequeue();
                                    if (RxLine.ToUpper().IndexOf("OK>") < 0)
                                    {
                                        rv = false;
                                        // OK> terminator not found, add response line to list of response lines
                                        RxLineList.Add(RxLine);
                                        if (LoggingLevel > 0)
                                            Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " SendData() - " + RxLine);
                                    }
                                    else
                                    {
                                        //found the OK> terminator
                                        if (LoggingLevel > 0)
                                            Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " SendData() - " + RxLine);
                                        rv = true;
                                        return rv;
                                    }
                                }
                            }
                        }
                        Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " SendData() Error - Timeout waiting for response from RFID reader");
                    }
                    else
                    {
                        Debug.Print(code + " SendData() serial port is not open");
                        RS232RFID.serialPort.Open();
                        rv = false;
                    }
                }
                else
                {
                        Debug.Print(code + " SendData() - rs232RFID serial port is not in use");
                        rv = false;
                }
            }
            catch (GTI.Serial.PortNotOpenException ex)
            {
                KioskUtilities.LogException(code + " SendData() Exception ( serial port not open ) ", ex);
                RS232RFID.serialPort.Open();
                rv = false;
            }
            return rv;
        }



        /// <summary>
        /// Intermec BRI asychronous event receiver thread
        /// Handle response lines that begin with "EVT:"
        /// </summary>
        void AsynchronousEventReceiver()
        {
            try
            {
                while (true)
                {
                    AsyncLineQueueEvent.WaitOne();
                    lock (AsyncLineQueue.SyncRoot)
                    {
                        while (AsyncLineQueue.Count > 0)
                        {
                            //thread-safe capture of the received char strings from the asynchronous message receive Queue
                            string RxLine = (string)AsyncLineQueue.Dequeue();
                            if (LoggingLevel > 1)
                            {
                                Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " AsynchronousEventReceiver() - " + RxLine.ToString());
                            }
                            OnIntermecIF2Event(new IF2EventArgs(RxLine));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                KioskUtilities.LogException(code + " AsynchronousEventReceiver() exception ", ex);
                KioskUtilities.Reboot();
            }
        }



        /// <summary>
        /// Intermec BRI response handler thread
        /// Parse the incoming character stream into text lines
        /// Separate lines that start with ERR: into the Error line Queue
        /// Send lines that do not start with ERR: to the response handler Queue
        /// </summary>
        void RxDataReceiver()
        {
            try
            {
                bool rv;
                byte rb;
                //wait for bytes to appear in the RFID reader received data Queue
                while (true)
                {
                    rv = RxDataEvent.WaitOne();
                    if (rv)
                    {
                        lock (RxDataQueue.SyncRoot)
                        {
                            //thread-safe capture of the received Intermec text line from the receive Queue
                            while (RxDataQueue.Count > 0)
                            {
                                rb = RxDataQueue.Dequeue();
                                if (rb == (byte)'\r')
                                {
                                    //ignore carriage return
                                }
                                else if (rb == (byte)'\n')
                                {
                                    //line-feed marks end of line, send the line one of the two line Queues
                                    if (RxLine.Length > 0)
                                    {
                                        if (RxLine.ToString().ToUpper().IndexOf("EVT:") >= 0)
                                        {
                                            lock (AsyncLineQueue.SyncRoot)
                                            {
                                                AsyncLineQueue.Enqueue(RxLine.ToString());
                                                AsyncLineQueueEvent.Set();
                                            }
                                        }
                                        else
                                        {
                                            lock (RxLineQueue.SyncRoot)
                                            {
                                                RxLineQueue.Enqueue(RxLine.ToString());
                                                RxLineQueueEvent.Set();
                                            }
                                        }
                                        if (LoggingLevel > 1)
                                        {
                                            Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " RxDataReceiver() - " + RxLine.ToString());
                                        }
                                        RxLine.Clear();
                                    }
                                }
                                else
                                {
                                    //accumulate characters into a line of text
                                    RxLine.Append((char)rb);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                KioskUtilities.LogException(code + " InputMessageReceiver() exception ", ex);
                KioskUtilities.Reboot();
            }
        }



        /// <summary>
        /// Received data polling thread for Intermec RFID reader serial port
        /// </summary>
        void RS232DataReader()
        {
            try
            {
                while (true)
                {
                    try
                    {
                        //wait for RS232 data
                        if ((RS232Count = RS232RFID.serialPort.Read(RS232Data, 0, MaxRxDataQueueSize)) > 0)
                        {
                            if (LoggingLevel > 1)
                            {
                                Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " RS232DataReader() - "
                                    + KioskUtilities.BytesToHexString(RS232Data, RS232Count)
                                    + " : " + KioskUtilities.BytesToAsciiString(RS232Data, RS232Count));
                            }
                            //Put bytes received into a Queue, prevent the Queue size from expanding forever
                            lock (RxDataQueue.SyncRoot)
                            {
                                if (RxDataQueue.Count < MaxRxDataQueueSize)
                                {
                                    for (int i = 0; i < RS232Count; i++)
                                    {
                                        if (RxDataQueue.Count < MaxRxDataQueueSize)
                                        {
                                            RxDataQueue.Enqueue(RS232Data[i]);
                                        }
                                        else
                                        {
                                            Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " RS232DataReader() - RxDataQueue.Count = "
                                                    + RxDataQueue.Count.ToString() + " RxDataQueue is full");
                                            break;
                                        }
                                    }
                                }
                                else
                                {
                                    Debug.Print(DateTime.Now.ToString("HH:mm:ss.fff") + " " + code + " RS232DataReader() - RxDataQueue.Count = "
                                            + RxDataQueue.Count.ToString() + " RxDataQueue is full");
                                }

                                if (RxDataQueue.Count > 0)
                                {
                                    //Signal other thread that we received some bytes
                                    RxDataEvent.Set();
                                }
                            }
                        }
                    }
                    catch (GTI.Serial.PortNotOpenException ex2)
                    {
                        KioskUtilities.LogException(code + " RS232DataReader() exception - serial port not open ", ex2);
                        RS232RFID.serialPort.Open();
                    }
                }
            }
            catch (Exception ex1)
            {
                KioskUtilities.LogException(code + " RS232DataReader() exception - ", ex1);
                KioskUtilities.Reboot();
            }
        }


I cannot build your Codeshare project, I am using NETMF version 4.2 and Visual Studio 2010. Could you change the PC Round-n-Round project to build in my environment?

Hi,
Yes, I didnā€™t consider that you start the other methods from your ProcessCommandsLoop thread.
Your application is quite complex and not easy to analyze esp. if one has not access to the whole project to compile and run on the raptor mainboard.
Actually I do not want to go back with my raptor on 4.2 firmware as I have some other things, I want to do. If I have more ideas concerning your issue, I will come back to it.
My CodeShare project is rather simple (raptor, RS232 Module and Power Module). The classes can easily be copied to VS2010 and NETMF 4.2, if at all, only a few namespaces need to be changed.
But my example shows, that at least in this simple program environment there is no data corruption.
Perhaps itā€™s a good approach to start from a simple serialport example that works without corruption and add step by step the environment (ProcessCommandLoop thread, locks and AutoResetevents and so on) of your application to see at which step the corruption occurs.
If you find the reason, please give me a feedback. Good luck :wink:

I adapted your program for NETMF 4.2 and VS2010 by creating a new Gadgeteer project and inserting your code into it. I changed the baud rate to 115200, and the received data dropped a character after a few minutes. I see that it took longer to show the problem, 2061 messages, probably because there is nothing else for the cpu to do.

I send to PC: Hello from Gadgeteer to all other computers in the world. Message No. 02061

I received back from PC: ello from Gadgeteer to all other computers in the world. Message No. 02061

I changed two of your functions to save the last string transmitted in StringSent, so I can compare that with the string received in dataLine, and break when they are different.



        void mySerialReadManager_DataByteArrayReceived(SerialReadManager sender, byte[] data, int startIndex, int ByteCount)
        {
            MySerialBuffer.LoadSerial(data, 0, ByteCount);
            //Debug.Print("..");
            string dataLine = string.Empty;
            while ((dataLine = MySerialBuffer.ReadLine()) != null)
            {
                StringToSend = dataLine.Substring(0, dataLine.Length - 6);  // Take away the message number at the ent of the string
                StringMayBeSent = true;
                Debug.Print("I received back from PC: " + dataLine + "\r\n");
                if (StringSent.TrimEnd() != dataLine.TrimEnd())
                    Debug.Print("Sent string does not match received string");
                // parse the data sentence here if needed
            }
        }

        string StringSent;

        void timerSendData_Tick(GT.Timer timer)
        {
            if (StringMayBeSent)
            {
                StringSent = StringToSend + X_Stellig.Zahl(SendCounter.ToString(), 5) + "\r\n";
                SendData(StringSent);
                //SendData(StringToSend + X_Stellig.Zahl(SendCounter.ToString(), 5) + "\r\n");
                StringToSend = "";
                StringMayBeSent = false;
                SendCounter++;
            }
            else
            {
                //Debug.Print("Could not send");
            }
        }



I made the string longer, and the dropped character happened sooner.

I send to PC: Hello from Gadgeteer to all other computers in the world. Hello from Gadgeteer to all other computers in the world. Hello from Gadgeteer to all other computers in the world. Hello from Gadgeteer to all other computers in the world. Message No. 01903

I received back from PC: Hello from Gadgeteer to all other computers in the world. Hello from Gadgeteer to all other computers in the world. Hllo from Gadgeteer to all other computers in the world. Hello from Gadgeteer to all other computers in the world. Message No. 01903