Transparent Serial Passthru

Hi Everyone,
I’m having problems trying to implement a serial passthru. Being new to .NET MF and C# I’m sure there is a better way to do this, but so for this is what I have came up with.

The code below will work for a couple seconds, and then throw an error. (I don’t know if off the top of my head)

thanks in advance.


namespace UARTPassThru
{
    public class Program
    {
        static SerialPort UART_PC = new SerialPort("COM1", 38400);
        static SerialPort UART_ECU = new SerialPort("COM2", 38400);
        public static void Main()
        {

            UART_PC.DataReceived += new SerialDataReceivedEventHandler(UART1_DataReceived);
            UART_ECU.DataReceived += new SerialDataReceivedEventHandler(UART2_DataReceived);

            UART_PC.Open();
            UART_ECU.Open();

            Thread.Sleep(Timeout.Infinite);
        }

        static void UART1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] RX_Buffer = new byte[UART_PC.BytesToRead];
            UART_PC.Read(RX_Buffer, 0, UART_PC.BytesToRead);
            UART_ECU.Write(RX_Buffer, 0, RX_Buffer.Length);
        }

        static void UART2_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] RX_Buffer = new byte[UART_ECU.BytesToRead];
            UART_ECU.Read(RX_Buffer, 0, UART_ECU.BytesToRead);
            UART_ECU.Write(RX_Buffer, 0, RX_Buffer.Length);
        }
    }
}

Open ports first and then subscribe for the events:



UART_PC.Open();
UART_PC.DataReceived += new SerialDataReceivedEventHandler(UART1_DataReceived);

UART_ECU.Open();
UART_ECU.DataReceived += new SerialDataReceivedEventHandler(UART2_DataReceived);


This is bad because you are allocating/deallocating a buffer every time you see an event. But you also can’t allocate large buffer enough for any situation so the way I would do it is through threads. 2 thread actually, one for each receive.

On the ECU side the buffer is 128 bytes, as soon as it sees and buffer overflow it will ignore the command and flush the buffers. So I can limit the buffer size to 128.

Is this a better way of handling this? Thanks for the help.

namespace UARTPassThru
{
    public class Program
    {
        static SerialPort UART_PC = new SerialPort("COM1", 38400);
        static SerialPort UART_ECU = new SerialPort("COM2", 38400);
        static byte[] UART_PC_RXBuffer = new Byte[128];
        static byte[] UART_ECU_RXBuffer = new Byte[128];

        public static void Main()
        {
            UART_PC.Open();
            UART_ECU.Open();

            new Thread(PC_Thread).Start();
            new Thread(ECU_Thread).Start();
            
            Thread.Sleep(Timeout.Infinite);
        }

        static void PC_Thread()
        {
            int Data = 0;
            while (true)
            {
                Data = UART_PC.BytesToRead;
                if (Data > 0)
                {
                    UART_PC.Read(UART_PC_RXBuffer, 0, Data);
                    UART_ECU.Write(UART_PC_RXBuffer, 0, Data);
                }
            }
        }

        static void ECU_Thread()
        {
            int Data = 0;
            while (true)
            {
                Data = UART_ECU.BytesToRead;
                if (Data > 0)
                {
                    UART_ECU.Read(UART_ECU_RXBuffer, 0, Data);
                    UART_PC.Write(UART_ECU_RXBuffer, 0, Data);
                }
            }
        }
    }
}

I would even do



static void PC_Thread()
        {
            while (true)
            {
                if (Data > 0)
                {
                    int count = UART_PC.Read(UART_PC_RXBuffer, 0, UART_PC_RXBuffer.Length);
                    UART_ECU.Write(UART_PC_RXBuffer, 0, count);
                }
            }
        }

You need to set your read timeout to infinite so if no data are there UART.Read will not return and so your thread will be asleep.

Gus:

I believe the code you posted blocks until UART_PC_RXBuffer.Length bytes are received with infinite read timeout.

Will only forward chunks of UART_PC_RXBuffer.Length bytes?

Probably mike is right! Then maybe keep a smaller buffer and set timeout to 100ms?

The following code give me the best data thru-put. It’s about 15ms slower per read than a direct cable connection, which will work fine for my purpose.

Thanks for the help.

namespace UARTPassThru
{
    public class Program
    {
        static SerialPort UART_PC = new SerialPort("COM1", 38400);
        static SerialPort UART_ECU = new SerialPort("COM2", 38400);
        static byte[] UART_PC_RXBuffer = new Byte[128];
        static byte[] UART_ECU_RXBuffer = new Byte[128];

        public static void Main()
        {
            UART_PC.Open();
            UART_ECU.Open();

            new Thread(PC_Thread).Start();
            new Thread(ECU_Thread).Start();

            Thread.Sleep(Timeout.Infinite);
        }

        static void PC_Thread()
        {
            int PCData = 0;
            while (true)
            {
                PCData = UART_PC.BytesToRead;
                PCData = UART_PC.Read(UART_PC_RXBuffer, 0, PCData);
                UART_ECU.Write(UART_PC_RXBuffer, 0, PCData);
            }
        }

        static void ECU_Thread()
        {
            int ECUData = 0;
            while (true)
            {
                ECUData = UART_ECU.BytesToRead;
                ECUData = UART_ECU.Read(UART_ECU_RXBuffer, 0, ECUData);
                UART_PC.Write(UART_ECU_RXBuffer, 0, ECUData);
            }
        }
    }
}

“I believe the code you posted blocks until UART_PC_RXBuffer.Length bytes are received with infinite read timeout.”

I not sure that is correct. That is a max read parm. If bytes less, it will just return those and return the actual read count. The problem with using BytesToRead is it is only a hint. By the time your Read is executed, you could have many more bytes that could be read. So I would drop the BytesToRead and you buffer size instead. That should give OP more efficiency.

If I use UART_PC_RXBuffer.Length it will sit there until there are 128 bytes of data in the buffer, then it will move on and transmit them. Using BytesToRead it sends the data as it comes in.

The PC software does error checking for each incoming packet. If the packet does not have the proper header, and is not the expected length it will discard it, and restart the login process. Once every couple minutes there is a packet error. All the errors have been length errors, most times between 3-4 bytes short. So the BytesToRead is causing a small problem.

I’m going to try a for loop that read/writes one byte at a time, using BytesToRead value as the for compare number, and update the ByteToRead value at the end of each loop. This should keep it running as long as there is data.

See if this is any better.


            while (true)
            { 
                while ((ECUData = UART_ECU.BytesToRead) > 0)
                {
                     ECUData = UART_ECU.Read(UART_ECU_RXBuffer, 0, ECUData);
                     UART_PC.Write(UART_ECU_RXBuffer, 0, ECUData);
                 }
                 Thread.Sleep(5);
            }

I think it could be added this kind of Reading COM signals with Event Handler on the ebook, or in the wiki.
I know it is a sort of advanced task, because it is an interrupt driven thread, but with a structure like this:

UART.DataReceived += new SerialDataReceivedEventHandler(UART_DataReceived);

and

static void UART_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            Debug.Print("Data Received!");
            //Insert your Reading code here!!
            //throw new NotImplementedException();
        }

all the tasks could be more clear and usable.

Only if the FEZ receive some data, the UART_DataReceived thread is executed… so it take away all the problems regarding the UART.Read(xxx,xxx,xxx) locking execution, because if we are inside this task, we are sure we received some datas…