Main Site Documentation

Troubles with SerialDataReceivedEventHandler


#1

Hi!

I read Data from the Serial Port in a SerialDataReceivedEventHandler - my Problem is, that there is a little work to do on this data. So if I receive something, I process it in this Handler - works really good.

The problem about it: My GUI is frozen… If I comment the work in the handler out, there is no freezing of the GUI. Why could that be? The SerialDataReceivedEventHandler is a Thread or am I wrong? A Thread should run in Background and should have no influence on my GUI…?

Thanks.

Andi


#2

It is generally not good practice to do a lot of processing in an event thread.

Try inserting some Thread.Sleep(0) statements in the event handler. This will allow the GHI thread to get some processing time.


#3

I’ll try to write in a receive-queue or something else and process it in a seperate thread…

But could that be the problem, that no action on the GUI is active any more? Is there no time for the main thread if I do some processing in the event thread?

Regards

Andi


#4

I do not know for a fact that the event handling thread does not allow any other thread to get a time slice of the CPU, but that does not mean it is not true.

Have you tried to use the Sleep method?


#5

Threads do not block other threads but events block everything. Still, it is a good idea to have sleep


#6

I just tried some things with the SerialDataReceivedEventHandler. Why is it called, if no data is ready to be read? It is called, but the SerialPort.BytesToRead is Zero? It’s a little bit confusing to me…


#7

Are you reading the serial port from outside the event handler?


#8

No, I read it inside the Event Handler into a Queue and process it later in a other thread…


#9

Without seeing your event handler code I can only guess what is happening.

If you have something like the code below:

while (sp.BytesToRead > 0)
{
      sp.Read(...)

}

It is possible that another hardware serial port data ready interrupt occurred while you were in your event handler, and the interrupt was posted to the event queue. Since you cleaning out the serial receive buffer in your event handler, when the second interrupt is presented to the event handler you would find the receive buffer empty. You just need to check for no data and return.

The ReceiveEvent just says that there is data in the receive buffer. There is no actual data associated with the event.

If this does not explain what you are seeing, please post your event handler.


#10

internal void ReceiveDataHandler(object sender, SerialDataReceivedEventArgs e) {
  byte[] data = XBeeHelperFct.readWithEscaping(xbee_port_, xbee_port_.BytesToRead);
  lock (_receive_serial_data_lock) {
    foreach (byte b in data) {
      receivedSerialData_.Enqueue(b);
    }
  }

  if (data.Length > 0) {
    _receiveSerialDataEvent.Set();
  }
}

internal static byte[] readWithEscaping(SerialPort xbee_port, int length) {
  byte[] output = new byte[length];
  for (int i = 0; i < length; i++) {
	output[i] = readOneByteWithEscaping(xbee_port);
  }
  return output;
}

internal static byte readOneByteWithEscaping(SerialPort xbee_port) {
  xbee_port.Read(bytebuffer_, 0, 1);
  if (bytebuffer_[0] == IOConstants.ESCAPE_CHARACTER) {
    xbee_port.Read(bytebuffer_, 0, 1);
    return (byte)(bytebuffer_[0] ^ IOConstants.ESCAPE_XOR_VALUE);
  }
  return bytebuffer_[0];
}


This is my code in the Event Handler :slight_smile:


#11

Every time you read an escape character you are reading an additional byte from the serial buffer. If BytesToRead says 10 bytes, and one of them is an escape, you would take 11 bytes from buffer.

If the byte after the escape byte is not in the receive buffer yet, then the event thread would lock until the byte arrive. Not good. :wink:

You should read all the bytes that BytesToRead says in one read, for efficiency, and then process them. You will need a simple state machine to cover the case where the last byte read is an escape bytes and the first byte in the next event needs to be handled in a special way.

You may also want to allocate the output buffer once to avoid excessive garbage collection.


#12

[quote]Every time you read an escape character you are reading an additional byte from the serial buffer. If BytesToRead says 10 bytes, and one of them is an escape, you would take 11 bytes from buffer.
[/quote]
wooooops :slight_smile: - I think it is too late here… :stuck_out_tongue:

Thats not a real problem - the paket is sent in one stream, so theres not so much time between two bytes. There are only a few bytes which are escaped…

I wanted to do this, but in the thread, in which the data is processed, I need the escaped data :frowning:

[quote]
You may also want to allocate the output buffer once to avoid excessive garbage collection.[/quote]
Hmm how would you do this?

So I thought about it - I could read all data as one byte[] - but to unescape it, I need to copy it into another byte[] - is that better?


#13

ok, I changed some things…

I read all data into the queue - the escaping is done while reading from the queue. I have to read it byte for byte to analyse the paket - so it’s no problem to unescape the data there…

The Event Handler:


internal void ReceiveDataHandler(object sender, SerialDataReceivedEventArgs e) {
	byte[] data = new byte[xbee_port_.BytesToRead];
        xbee_port_.Read(data, 0, data.Length);
	lock (_receive_serial_data_lock) {
		foreach (byte b in data) {
			receivedSerialData_.Enqueue(b);
		}
	}

	if (data.Length > 0) {
		_receiveSerialDataEvent.Set();
	}
}

Two functions to work with the queue:


private byte readOneByteFromDataQueueWithEscaping() {
	lock (_receive_serial_data_lock) {
		if ((byte)receivedSerialData_.Peek() == IOConstants.ESCAPE_CHARACTER) {
			receivedSerialData_.Dequeue(); //escaping byte verwerfen
			return (byte)(((byte)receivedSerialData_.Dequeue()) ^ IOConstants.ESCAPE_XOR_VALUE); //nächstes byte zurückgeben (escaped)
		}
		return (byte)receivedSerialData_.Dequeue();
	}
}


private Boolean isDataOnQueueAvailable() {
	lock (_receive_serial_data_lock) {
		if (receivedSerialData_.Count == 0) {
			return false;
		} else {
			return true;
		}
	}
}


#14

does it work?


#15

irrespective of whether it works or not, the best strategy is to have the datarecieved handler JUST dequeue and put it in the internal buffer, and not try to build logic into it. Have something else looking at your buffer that acts on data once it’s in. That way you get less confusion :slight_smile:


#16

Yes, it works fine! Thanks!