serialPort_DataReceived
executes on a ThreadPool thread, not on the main (aka GUI) thread. Which means it cannot directly touch your GUI controls.
So
tbRecivedMess.Text = message;
will fail, and so will any GUI operations that may be attempted inside your Start() and Stop() methods.
There are basically two solutions, depending on circumstances:
(1)
avoiding
DataReceived
You can read your SerialPort without event. Example: if your peripheral takes a command (a SerialPort.Write) to execute some quick operation and report back, then you could simply execute on the main thread like so:
SerialPort.Write(<activate command>);
Thread.Sleep(<short delay say 3 sec>);
string response=SerialPort.ReadExisting();
(2)
using Control.Invoke
when another thread (as inside the DataReceived handler) wants to touch GUI Controls, you need Controle.Invoke; details can be found everywhere, including
here.
BTW: DataReceived may not behave exactly as you would hope, it means "I've got something for you", and not "I have the exact and complete message you are expecting". Yes, when you hope to get 5 characters, it might trigger after receiving 3 of them, so ReadExisting would not give the whole message. (It often helps to insert a short delay, eg 100msec, at the start of the handler).