Adding event handler in main() for SerialPort - c#

I try to subscribe a event handler to the data received event. Seems like I cant specify the event handler function name. I dont understand why
myComPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived); is giving me error message.
Here is the problem, hope anyone can answer it.
a busy cat http://img827.imageshack.us/img827/5904/20120125102247.png
a busy cat http://img444.imageshack.us/img444/3855/20120125102202.png
namespace serialport
{
public class Program
{
internal List<Byte> portBuffer = new List<Byte>(1024);
static void Main()
{
//1. find available COM port
string[] nameArray = null;
string myComPortName = null;
nameArray = SerialPort.GetPortNames();
if (nameArray.GetUpperBound(0) >= 0)
{
myComPortName = nameArray[0];
}
else
{
Console.WriteLine("Error");
return;
}
//2. create a serialport object
// the port object is closed automatically by use using()
SerialPort myComPort = new SerialPort();
myComPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
myComPort.PortName = myComPortName;
//the default paramit are 9600,no parity,one stop bit, and no flow control
//3.open the port
try
{
myComPort.Open();
}
catch (UnauthorizedAccessException ex)
{
MessageBox.Show(ex.Message);
}
//Add timeout, p161
//reading Bytes
byte[] byteBuffer = new byte[10];
Int32 count;
Int32 numberOfReceivedBytes;
myComPort.Read(byteBuffer, 0, 9);
for (count = 0; count <= 3; count++)
{
Console.WriteLine(byteBuffer[count].ToString());
}
}
//The event handler should be static??
void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int numberOfBytesToRead;
numberOfBytesToRead = myComPort.BytesToRead;
byte[] newReceivedData = new byte[numberOfBytesToRead];
myComPort.Read(newReceivedData, 0, numberOfBytesToRead);
portBuffer.AddRange(newReceivedData);
ProcessData();
}
private void ProcessData()
{
//when 8 bytes have arrived, display then and remove them from the buffer
int count;
int numberOfBytesToRead = 8;
if (portBuffer.Count >= numberOfBytesToRead)
{
for (count = 0; count < numberOfBytesToRead; count++)
{
Console.WriteLine((char)(portBuffer[count]));
}
portBuffer.RemoveRange(0, numberOfBytesToRead);
}
}
}
}

First, since method Main is static, you can only call other static methods in the same class. As it is, comPort_DataReceived is declared as an instance method, the following code should fix the assignment of the event handler:
static void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// ...
}
Second, since myComPort is defined in Main, it will not be visible in comPort_DataReceived. You have two choices: either declare myComPort as a static member of your class, or use the sender argument of the event handler:
static void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = (SerialPort)sender;
// ...
}

In your event handler, myComPort isn't in scope - it's declared locally in your main() method. I would suggest that you extract the com port handling into a class and make myComPort a member variable of that class.
Also, your comments note that the SerialPort class has a managed resource that it needs to dispose of using the IDisposable / Using pattern, but you don't have a using block wrapping the access to the comm port.
Last, the method you are adding as the event handler exists as an instance member rather than as a static member; to access it from the main() method's static scope, you need to either grab it from an instance of the class or make the method static.

Tetsujin no Oni's answer is the ideal way to handle your issue with scope. Another approach that also works is to declare myComPort as a static member of your Program, e.g.:
internal List<Byte> portBuffer = new List<Byte>(1024);
private SerialPort myComPort = new SerialPort();
Then simply remove the myComPort declaration from your main method.

Related

How to pass a Variable to Lambda Expression

I was just starting to use C# last week and tried to create a simple serial monitor program. I want my program to read data from serialport continuously while displaying those data in the Form Application at the same time.
I use the code from here as reference https://github.com/ahelsayd/Serial-Lab
I use the same BeginInvoke() function. However I can not pass the variable that I want to write.
This is the original code
private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (mySerial.IsOpen)
{
try
{
//=====Only this part is different=======================
int dataLength = mySerial.BytesToRead;
byte[] dataReceived = new byte[dataLength];
int nbytes = mySerial.Read(dataReceived, 0, dataLength);
if (nbytes == 0) return;
//=====Only this part is different=======================
this.BeginInvoke((Action)(() =>
{
data = System.Text.Encoding.Default.GetString(dataReceived);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}));
}
catch { alert("Can't read form " + mySerial.PortName + " port it might be opennd in another program"); }
}
}
//And then update the UI
private void update_rxtextarea_event(object sender, DoWorkEventArgs e)
{
this.BeginInvoke((Action)(() =>
{
if (rx_textarea.Lines.Count() > 5000)
rx_textarea.ResetText();
rx_textarea.AppendText("[RX]> " + data);
}));
}
This code can read the Serialport and Write into the Form simultaneously. However, it does not receive all data from the serialport. So I modified the code to write the data into a buffer first until all data is received.
The modified code
private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (mySerial.IsOpen)
{
try
{
//=====Only this part is different=======================
string Data = mySerial.ReadExisting();
serialBuffer.Append(Data);
string bufferString = serialBuffer.ToString();
int index = -1;
do
{
index = bufferString.IndexOf(terminationSequence);
if (index > -1)
{
string message = bufferString.Substring(0, index);
bufferString = bufferString.Remove(0, index + terminationSequence.Length);
}
}
while (index > -1);
serialBuffer = new StringBuilder(bufferString);
byte[] bytes = new byte[30];
if (serialBuffer.Length == 15) {
Console.WriteLine("data:" + serialBuffer);
bytes = Encoding.ASCII.GetBytes(serialBuffer.ToString());
}
//=====Only this part is different=======================
this.BeginInvoke((Action)(() =>
{
data = Encoding.ASCII.GetString(bytes);
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
}
}));
}
catch { alert("Can't read form " + mySerial.PortName + " port it might be opennd in another program"); }
}
}
The problem is in the Form application the value of bytes is always null although when I checked by writing the output value to console window I can see the value of bytes updated.
I am very confused why the variable dataReceived value can be accessed by BeginInvoke while the variable bytes keep having null value. Is there something that I've missed that causing the value not get updated?
So I wasn't able to do it yesterday, but I promised the OP I would write something in an answer related to a comment of mine. From what I understand he/she is trying to update something in a Form based on received data from a serial port. Here's how I do it:
First off you need to declare a delegate and an event (which is basically a list of delegates):
public delegate void Data_Received_EventHandler(object sender, Data_Received_EventArgs e);
public event Data_Received_EventHandler Data_Received;
These are used to substitute the original "data received" event and event args from the serial port.
I usually define the Data_Received_EventArgs type as something based of an array of bytes, for simplicity:
public class Data_Received_EventArgs : EventArgs
{
public byte[] data;
internal Data_Received_EventArgs(int length)
{
data = new byte[length];
}
}
Then, in the original data reception event (let's say your serial port is called serialPort1):
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// check how many bytes you need to read:
int bytesToRead = serialPort1.BytesToRead;
// declare your arguments for the event based on that number of bytes (check the Data_Received_EventArgs constructor):
Data_Received_EventArgs args = new Data_Received_EventArgs(bytesToRead);
// copy the bytes from your serial port into the arguments:
for (int i = 0; i < bytesToRead; i++)
args.data[i] = (byte)serialPort1.ReadByte();
// verify if there are subscribers to the event (list not empty) and fire your event using BeginInvoke:
if (Data_Received != null)
BeginInvoke((MethodInvoker)delegate () { Data_Received(this, args); });
}
What we now have is an event which is guaranteed to execute its handlers on the UI thread.
To use it, you can subscribe to the event like so:
Data_Received += My_Data_Received_Handler;
Your handler should have the same signature as the delegate we declared in the first line of code (should be void and have specified parameters):
private void My_Data_Received_Handler(object sender, Data_Received_EventArgs e)
{
// read bytes from the arguments as you normally would from an array and do whatever e.g.:
some_Label.Text = e.data[0].ToString(); // without worrying about crossthreading
}
I know this is not the answer the OP wanted, but I hope it helps in simplifying what he/she was trying to do in the first place.

code not reading from serial port

Good day,
I have tried everything to read a few strings from my xbee module on csharp.
but my code keeps telling me the serial port is not open when it reaches the event handler. any help would be appreciated greatly. thanks string display = myserial.ReadLine();
using System;
using System.Management;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
namespace ConsoleApplication2
{
class Program
{
public static SerialPort myserial = new SerialPort();
public string display;
static void Main(string[] args)
{
string[] ports = SerialPort.GetPortNames();
foreach (string p in ports)
{
Console.WriteLine(p);
}
SerialPort myserial = new SerialPort();
myserial.BaudRate = 9600;
myserial.Parity = Parity.None;
myserial.StopBits = StopBits.One;
myserial.DataBits = 8;
myserial.Handshake = Handshake.None;
myserial.RtsEnable = true;
myserial.DtrEnable = true;
myserial.ReadTimeout = 100000;
myserial.PortName = "COM3";
myserial.ReadTimeout = 10000;
myserial.DataReceived += new SerialDataReceivedEventHandler(DataRecievedHandler);
myserial.Open();
if (myserial != null)
{
if (myserial.IsOpen)
{
Console.WriteLine("connected");
}
}
Console.ReadLine();
}
static void DataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
{
string display = myserial.ReadLine();
}
}
}
Your problem is that you have an ambiguity in your code. 2 Variables with the same name.
The class variable that you declare outside the main:
class Program
{
public static SerialPort myserial = new SerialPort();
and the variable inside the main method:
static void Main(string[] args)
{
SerialPort myserial = new SerialPort();
Inside the method the compiler will take the local variable myserial. You open it and register the event:
myserial.DataReceived += new SerialDataReceivedEventHandler(DataRecievedHandler);
So far everything is fine. But outside the Main method this SerialPort myserial does not exist. That means when you try to access myserial inside the DataRecievedHandler method the compiler "thinks" that you mean the first variable on class level! But this SerialPort has never been opened! Therefore it gives you the error.
You can solve it by using the sender object inside the event. Since the open SerialPort fires this event:
static void DataRecievedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
if(port != null)
{
string display = port.ReadLine();
}
}
Note: This variable display exist only inside the DataRecievedHandler method. You cannot use it in the main. Because you declare it again. This is a local variable which is not the same as you have declared on class level! remove the string and the class level variable will be used:
make it:
display = port.ReadLine();
2.
You can also solve it by simply removing the declaration of the SerialPort myserial variable inside the Main method. Probably would be simpler ;)
Just remove this line inside the Main method:
SerialPort myserial = new SerialPort();

How to read continuously from the SerialPort

I need to read continuously the bytestream from my Arduino board.
For this, I've created a method that is reading the Serialport in a loop and expose the ByteStream to the other classes:
public async void ReadSerialPort
{
dataReader = new DataReader(SerialPort.InputStream)
while (true) {
uint ReadBufferLength = 16;
Task < UInt32 > loadAsyncTask;
loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask();
UInt32 rxbytes = await loadAsyncTask;
RxBytes = new byte[rxbytes];
if (rxbytes > 0) {
datareader.ReadBytes(ByteStream);
}
}
}
But I think this is not the most efficient method for reading the serialport continuously as this method needs a couple of milliseconds to get the bytestream.
Is there an alternative for reading the bytestream?
Use event to read data from SerialPort:
static void Main(string[] args)
{
SerialPort sp= new SerialPort();
sp.DataReceived += Sp_DataReceived;
}
private static void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
throw new NotImplementedException();
}

Arduino to C# Data Receive

I've asked this question earlier today, but have refined my code so am putting a new question up here.
This is the code I have at the moment:
Arduino Code:
void setup()
{
pinMode(13,OUTPUT);
digitalWrite(13,LOW);
Serial.begin(9600);
}
void loop()
{
if(Serial.available() > 0)
{
char letter = Serial.read();
if (letter == 'A')
{
digitalWrite(13,HIGH);
Serial.println("THE LED IS ON");
}
else if (letter == 'B')
{
digitalWriter(13,LOW);
Serial.println("THE LED IS OFF");
}
}
}
I have a C# program with an onButton, offButton, and textboxInterface. This is the code I have in C#.
C# Code:
using System.IO.Ports;
public partial class Form1: Form
{
public static System.IO.Ports.SerialPort serialPort1;
private delegate void LineReceivedEvent(string line);
public Form1()
{
InitizlizeComponent();
System.ComponentModel.IContainer components = new System.ComponentModel.Container();
serialPort1 = new System.IO.Ports.SerialPort(components);
serialPort1.PortName = "COM7";
serialPort1.BaudRate = 9600;
serialPort1.DtrEnable = true;
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
}
private static void serialPort1_DataReceived(object sender, SerialDataEventReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
textboxInterface.Text = indata;
}
I think that is mostly right (?), the only error I am getting is as the last textboxInterface with an error coming up saying: *An object reference is required for the non-static field, method, or property 'Arduino_Interface.Form1.textboxInterface'*
Can someone please show me what stupid thing I'm doing...
First, remove static from the declaration of serialPort1_DataReceived. You need access to the form's instance fields so it cannot be static.
Second, this event will be raised on a background thread and you cannot update the UI from that thread. You will need to marshal the call to UI thread to update the textbox. Something like this:
private void serialPort1_DataReceived(object sender, SerialDataEventReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
this.BeginInvoke(new Action(() => textboxInterface.Text = indata));
}

Serial port event handler creates an object which is NUll in main code

I am trying to write a simple serial port data logging app which monitors the serial port and writes the data to a file.
If there is a long gap between the data the the file is closed and a new one started.
I use a timer to determine when to close the file. and the serial event handler to open the file.
The problem is that when the file is closed the file object seems to be null; The file itself is created by another object as it does other stuff deleted from my example code.
For some reason the object created by the serial event handler is valid but the file within the object is null.
I am a very experienced C and linux kernal programmer but new to C# so the syntax and object nature still catches me out
but I cannot see any reason why this should not work unless I am losing an object context somewhere.
That might be a problem as there are four serial port objects open at the same time writing to four different files.
I already solved my timer problem which wouldn't work when called from the serial handler by chaning from a FOrms.timer to Timers.timer.
so I am wondering if its a threading issue.
Any light you can shed would be welcome.
class CommGroup
{
private SerialPort _comPort;
private System.Timers.Timer myTimer;
private ResultsLog logFile = null;
public CommGroup()
{
_comPort = new SerialPort(string name);
_comPort.PortName = name;
_comPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
_comPort.Open();
myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(TimerEventProcessor);
}
private void TimerEventProcessor(Object myObject,
EventArgs myEventArgs)
{
System.Timers.Timer timer = (System.Timers.Timer) myObject ;
timer.Stop();
if (logFile != null)
{
logFile.Close(); /* this fails due to null object */
}
}
private void DataReceivedHandler(object sender,SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
if (myTimer.Enabled == false)
{
myTimer.Interval = 5000;
myTimer.Start();
}
else
{
myTimer.Stop();
myTimer.Interval = 5000;
myTimer.Start();
}
if (logFile == null)
{
logFile = new ResultsLog("Filename");
}
logFile.LogResult(sp.ReadExisting());
}
}
public class ResultsLog
{
private StreamWriter resultFile;
public ResultsLog(string filename)
{
StreamWriter resultFile = new StreamWriter(filename, true);
}
public void Close(string errorname)
{
if (this.resultFile != null)
{
resultFile.Flush();
resultFile.Close();
}
else
{
MessageBox.Show("File NULL Error " + errorname, "File Close");
}
}
public void LogResult(string result)
{
if (resultFile != null)
{
resultFile.Write(result);
}
}
}
Is your resultFile the troublesome null?
You are re-declaring locally in your ResultsLog constructor. Should be:
public ResultsLog(string filename)
{
resultFile = new StreamWriter(filename, true);
}
Definately could be threading because you have multiple threads accessing the log object without syncronization.
Use lock around your accesses to the varibale and see if it helps.

Categories

Resources