Link to home
Start Free TrialLog in
Avatar of IncognitoMan
IncognitoManFlag for Bulgaria

asked on

C# SerialPort Recieve Problem

Hello guys,
I have a cash register and I communicate with it on a low level. So, the idea is the following. The fiscal printer has got fiscal memory. IN this memory it records everything you've sold. But before I make a sale I want to check if this memory is full, cos I'll have problems with registring the sale. How do I do this? By sending a request that return something and with this something the state of the printer. So I write the following funcion that sends data.

FiscalPrinters.DatecsFP1000 fiscal = new FiscalPrinters.DatecsFP1000();
            if(serialPort.IsOpen == false)
                serialPort.Open();
            fiscal.izpratiKomanda(0x3E, 0x20);
            serialPort.Write(fiscal.izpratiKomanda(0x3E, 0x20), 0, fiscal.izpratiKomanda(0x3E, 0x20).Length);

I leave the port opened to recieve the data. I use a program to monitor the communication. The data sended is:
01 24 20 3E 05 30 30 38 37 03  in HEX
The recieved data is:
01 3C 20 3E 33 30 2D 30 38 2D 31 30 20 30 39 3A
35 38 3A 32 30 04 88 80 C0 86 86 92 05 30 37 35   in Hex
3B 03

and in string 30-08-10 it gives me the date. Which means everything in the sent command is OK :).
So, to read this line, which contains a control bit, that says is there free memory in the printer, I've written the following functuin attaced to a SerialPort DataRecieved event.

if (!serialPort.IsOpen)
            {
                serialPort.Open();
                MessageBox.Show(serialPort.ReadLine());
                serialPort.Close();
            }
            else
            {
                MessageBox.Show(serialPort.ReadLine());
            }

But the event eider doesn't trigger, or throws the following exception:
The I/O operation has been aborted because of either a thread exit or an application request.

What am I doing wrong?
Avatar of ricovox
ricovox
Flag of United States of America image

Hello,

First:
The ReadLine() function will wait (freezing your program) until some data is received and is terminated by CrLf (carriage return line feed). Are you sure that the printer responses are terminated with CrLf? I don't see them in the data that you said is sent back. (Hex values are 13, 10, respectively)

Second:
Make sure you do not close the serial port an ANY time after you send the data or any time before you read the response. Just to make sure you don't close it, you should use this code instead of the one you posted above:

(In DataReceived event handler)


if (!serialPort.IsOpen)
   throw new Exception("Serial Port is not Open");
MessageBox.Show(serialPort.ReadLine());

Open in new window

If the device doesn't send CrLf, then you can just use Read() instead, and specify the number of bytes to read. You will know which method to use by looking at the protocol for the device, which should tell you whether or not CrLf is used.
What this means, almost certainly, is that the SerialPort object attempted to complete the call to ReadLine after the port was been closed.  

This can happen because of the lack of synchronization between UI events which may cause the port to close, and the background thread in the SerialPort object that is performing the actual ReadFile operation (this executes as a result of ReadLine in your delegate).

The problem with ReadLine, and the reason that you way not want to use it, is that it blocks (i.e. freezes your program) until the the line terminating condition occurs (CrLf) -- this may be AFTER you have closed the port.  Thus the exception.

You could simply use ReadExisting () and buffer the data in byte[] variable and test the buffer for the CrLf terminating characters.  
Remember to clear this buffer AFTER you have processed and displayed its content.  If you do this, the exception should be resolved.
Avatar of Member_2_1846163
Member_2_1846163

1.- As stated before, binary data cannot be handled with the ReadLine command. Looking at your response data there is no CrLf to indicate end of string so I would not expect ReadLine to do anything sensible.
2.- I cannot see where you initialise the serial port parameters. These have to be set the same way as the printer. You need to do something like this for 8 bit clear comms:
            serialPort = new SerialPort("COM1");
            serialPort.BaudRate = 9600;
            serialPort.DataBits = 8;
            serialPort.Parity = Parity.Even;
            serialPort.HandShake = Handshake.None;
3.- Use the Read function and the set up a state machine to handle the data. Given that this is a syncrhonous request/response protocol it is quite easy to handle. You need to do something like this:
            serialPort.Write(command, command.Length())
            int status=1
            while(status > 0)
            {
                        byte[] buffer = new buffer[256];
                        int bytes = serialPort.Read(buffer, 256)
                        int byte;
                        for (byte=0;byte < bytes;byte++)
                        {
                                    byte b = buffer[byte];
                                    switch(status)
                                    {
                                     case 1:
                                                if (b == ????)
                                                ....
                                    }
                        }
            }

I can't help you much more with the state machine as I do not know the protocol that this particular printer uses.
Avatar of IncognitoMan

ASKER

Strange, very strange. Your method worked. It did solve the exception. So what I did was, I read the data, and then convert the string into a byte array.
string str = serialPort.ReadExisting();
                byte[] data = new byte[str.Length];
                data = System.Text.ASCIIEncoding.ASCII.GetBytes(str);

But using a Serial Port monitoring program, I saw that the device is returning the folloing:
01 3C 20 3E 33 30 2D 30 38 2D 31 30 20 31 38 3A
30 33 3A 30 39 04 88 80 C0 86 86 92 05 30 37 35   (HEX)
38 03

And the contents ot the byte array are:

01 66 32 63 51 48 45 48 56 45 49 48 32 49 56 58
48 51 58 48 57 04 63 63 63 63 63 63 05 48 55 53    (DEC)
56 03

The information that I want to read is between the 04 and 05 bytes on the second line. But in the port monitoring program they are 88 80 C0 86 86 92, which is exactly what should be returned, and the byte array in the program has got 63 63 63 63 63 63 which is totolly wrong. It cant return this :).
Maybo the problem is that I convert the string into byte array. But is there a way to read this message from the device and but it derectly into a byte array?
Hello,

That is strange indeed.
Yes, I would recommend you switch to using the Read(byte[], int) method so that you read the bytes directly without converting to a string.

You can use the "BytesToRead" property to get the length of the buffer you need.
Something like this:

int count = serialPort.BytesToRead;
byte[] buf = new byte[count];
serialPort.Read(buf, count);
//now buf contains the bytes that were read.

Here is a more abbreviated version:
byte[] buf = new byte[serialPort.BytesToRead];
serialPort.Read(buf, buf.Length);

Let me know how that goes.
ASKER CERTIFIED SOLUTION
Avatar of ricovox
ricovox
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If it turns out that you are still getting invalid data, perhaps you could write a small utility to convert the bytes you receive from DEC to HEX, and that might help clarify things.

ie.

foreach(byte b in buf)
   System.Diagnostics.Debug.Print(b.ToString("X"));

That way you can directly compare the output in C# to that in your port monitoring program
P.S. Better use "PrintLine" or Print(b.ToString("X") + " ");
so the numbers won't all run together.