C#: DataReceived Event handler for serialPort not firing - c#

I'm writing a fairly basic application, where one of the tasks is communicating with an Arduino Uno card.
I would like to write the serial communication as a separate module, so the forms only calls for the input from Arduino, and the module will handle the creating and opening of the serialPort and reading data from it.
For testing purposes I wrote a program for the Arduino which prints the elapsed milliseconds every half second to the serialPort.
I would like to populate the textBox of my Form with the output from Arduino after I press a button.
I create the serialPort in the SerialComm class, and although I attach a DataReceived event handler to it, it never seems to fire.
Here is the code for the SerialComm class:
class SerialComm
{
private List<String> availablePorts;
private SerialPort arduino;
private string receivedText;
public String[] portList
{ get
{
EnumPorts();
return availablePorts.ToArray();
}
}
public string receivedData
{
get
{
return receivedText;
}
}
public void InitialiseSerial()
{
arduino = new SerialPort();
arduino.BaudRate = 9600;
arduino.DtrEnable = true;
// Add event handler
arduino.DataReceived += new SerialDataReceivedEventHandler(arduino_DataReceived);
}
public void EnumPorts()
{
availablePorts = new List<string>();
foreach (string s in SerialPort.GetPortNames())
{
availablePorts.Add(s);
}
}
public void StartMC(SerialPort serialPort, String portName)
{
arduino = serialPort;
if (arduino.IsOpen)
{
arduino.Close();
}
else
{
//Initialise Serial Port
arduino.PortName = portName;
arduino.Open();
}
}
//This never fires==================
private void arduino_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
receivedText = arduino.ReadExisting();
}
public void CloseMC()
{
if (arduino.IsOpen)
{
arduino.Close();
arduino.Dispose();
}
}
}
In the Form I call the text from Arduino like this: (I have a button (Button1), a combobox for selecting the COM port (comboBox1) and a textBox on the Form)
public partial class Form1 : Form
{
SerialComm arduino = new SerialComm();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Arduino
arduino.InitialiseSerial();
String[] portList = arduino.portList;
foreach (String COM in portList)
{
comboBox1.Items.Add(COM);
}
if (portList.Length > 0)
{
comboBox1.SelectedItem = comboBox1.Items[0];
}
else
{
comboBox1.Text = "No microcontroller found!";
}
}
private void button1_Click(object sender, EventArgs e)
{
arduino.StartMC(serialPort1, comboBox1.SelectedItem.ToString());
//as the DataReceived never fires arduino.receivedData stays null
if (arduino.receivedData != null)
{
for (int i = 0; i < 101; i++)
{
textBox1.AppendText(arduino.receivedData);
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
arduino.CloseMC();
}
}
Do you see any reason why the event handler doesn't trigger?
Thank you very much for your help in advance.
Best Regards,
Peter

Related

Receiving data from serial port in C#, while updating ui

My question is how do you update your ui, with data from serialport, while still updating other ui components? I have tried using a background worker, but it seems to block the ui, while the data is streaming in.
Image of form:
My code is:
public partial class Form1 : Form
{
static bool _continue;
static SerialPort _serialPort;
BackgroundWorker dataWorker;
string message;
public delegate void UpdateListboxData();
private void buttonPortConnect_Click(object sender, EventArgs e)
{
// Set the read/write timeouts
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
_continue = true;
dataWorker = new BackgroundWorker();
dataWorker.RunWorkerAsync();
dataWorker.DoWork += StartDataWork;
}
private void StartDataWork(object sender, DoWorkEventArgs e)
{
Delegate del = new UpdateListboxData(DataRead);
this.Invoke(del);
}
private void DataRead()
{
while (_continue)
{
try
{
message = _serialPort.ReadLine();
}
catch (TimeoutException) { }
}
}
}
you have to use Serial Port DataReceived Event and Delegate
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (TextBox.InvokeRequired)
TextBox.Invoke(new myDelegate(updateTextBox));
}
public delegate void myDelegate();
public void updateTextBox()
{
int iBytesToRead = serialPort1.BytesToRead;
//Your Codes For reding From Serial Port Such as this:
char[] ch = new char[?];
serialPort1.Read(ch, 0, ?? ).ToString();
//........
}
The way to update a winforms UI is to use if (control.InvokeRequired) {...} and Invoke as shown in the example here How do I update the GUI from another thread?
You should not read data inside the invoke'd function. This is blocking your UI.
Either read data directly in "DoWork" and invoke the delegate only with data.
Or you might use the DataReceived event of SerialPort.

Automatically auto-check every new item in a checkedboxlist in c#

I am trying to auto check every new item in my checkedboxlist I have a button that does that it will auto select all, but we don't want it that way to be controlled by a button. we want it so for every new item we get automatically will get checked.
This is my button that auto select all if there are new 20 items this will auto select all and then submit those new items
Here is the code it works but is not I want I want to auto select for every new items coming in, because I have another process that will keep adding new items to the checkedboxlist, also the lst_BarcodeScanEvents is the checkedboxlist name
private void btn_SelectALLScans_Click(object sender, EventArgs e)
{
for (var i = 0; i < lst_BarcodeScanEvents.Items.Count; i++)
{
lst_BarcodeScanEvents.SetItemChecked(i, true);
}
}
I checked other sources like google and stackoverflow but I could not find anything to my request here.
Here is the main class where I have the logic of buttons, checkedlistbox and more. and the button method btn_ConnectT_Click calls the methods from the second class
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Web;
using BarcodeReceivingApp.Core.Domain;
using BarcodeReceivingApp.Functionality;
namespace BarcodeReceivingApp
{
public partial class BarcodeReceivingForm : Form
{
//GLOBAL VARIABLES
private const string Hostname = "myip";
private const int Port = 23;
private TelnetConnection _connection;
private ParseReceivingBarcode _parseReceivingBarcode;
private ButtonsDisplay _buttonsDisplay;
public BarcodeReceivingForm()
{
InitializeComponent();
//FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
private void btn_ConnectT_Click(object sender, EventArgs e)
{
_connection = new TelnetConnection(Hostname, Port);
_connection.ServerSocket(Hostname, Port, this);
}
private void btn_StopConnection_Click(object sender, EventArgs e)
{
_connection.Exit();
}
private void btn_RemoveItemFromListAt_Click(object sender, EventArgs e)
{
if (lst_BarcodeScanEvents.CheckedItems.Count != 0)
for (var i = lst_BarcodeScanEvents.CheckedItems.Count; i > 0; i--)
lst_BarcodeScanEvents.Items.RemoveAt(lst_BarcodeScanEvents.CheckedIndices[i - 1]);
else
MessageBox.Show(#"Element(s) Not Selected...");
}
private void BarcodeReceivingForm_Load(object sender, EventArgs e)
{
_buttonsDisplay = new ButtonsDisplay(this);
_buttonsDisplay.ButtonDisplay();
}
private void btn_ApplicationSettings_Click(object sender, EventArgs e)
{
var bcSettingsForm = new BarcodeReceivingSettingsForm();
bcSettingsForm.Show();
}
private void btn_ClearBarcodeList_Click(object sender, EventArgs e)
{
lst_BarcodeScanEvents.Items.Clear();
}
private void lst_BarcodeScanEvents_ItemAdded(object sender, ListBoxItemEventArgs e)
{
MessageBox.Show(#"Item was added at index " + e.Index + #" and the value is " + lst_BarcodeScanEvents.Items[e.Index].ToString());
}
private void btn_SubmitData_Click(object sender, EventArgs e)
{
var receivingFullBarcode = new List<string>();
_connection.GetBarcodeList();
}
private void btn_SelectALLScans_Click(object sender, EventArgs e)
{
for (var i = 0; i < lst_BarcodeScanEvents.Items.Count; i++)
{
lst_BarcodeScanEvents.SetItemChecked(i, true);
}
}
}
}
Here is the second class where I use a telnet port 23 that scan barcodes where and puts it to the checkedlistbox, now the main emethods here that inserts the data to the checkedlistbox is the method serversocket and the readwrite method
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace BarcodeReceivingApp.Functionality
{
public class TelnetConnection
{
private Thread _readWriteThread;
private TcpClient _client;
private NetworkStream _networkStream;
private string _hostname;
private int _port;
private BarcodeReceivingForm _form;
private bool _isExiting = false;
public TelnetConnection(string hostname, int port)
{
this._hostname = hostname;
this._port = port;
}
public void ServerSocket(string ip, int port, BarcodeReceivingForm f)
{
this._form = f;
try
{
_client = new TcpClient(ip, port);
}
catch (SocketException)
{
MessageBox.Show(#"Failed to connect to server");
return;
}
_networkStream = _client.GetStream();
_readWriteThread = new Thread(ReadWrite);
//_readWriteThread = new Thread(() => ReadWrite(f));
_readWriteThread.Start();
}
public void Exit()
{
_isExiting = true;
}
public void ReadWrite()
{
do
{
var received = Read();
if (received == null)
break;
if (_form.lst_BarcodeScanEvents.InvokeRequired)
{
var received1 = received;
_form.lst_BarcodeScanEvents.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScanEvents.AddItem(received1 + Environment.NewLine);
}));
}
} while (!_isExiting);
//var material = received.Substring(10, 5);
//_form.label5.Text += string.Join(Environment.NewLine, material);
CloseConnection();
}
public List<string> GetBarcodeList()
{
var readData = new List<string>();
foreach (string list in _form.lst_BarcodeScanEvents.Items)
{
readData.Add(list);
MessageBox.Show(list);
}
return readData;
}
public string Read()
{
var data = new byte[1024];
var received = "";
var size = _networkStream.Read(data, 0, data.Length);
if (size == 0)
return null;
received = Encoding.ASCII.GetString(data, 0, size);
return received;
}
public void CloseConnection()
{
MessageBox.Show(#"Closed Connection",#"Important Message");
_networkStream.Close();
_client.Close();
}
}
}
so now like I said before for every new items it gets inserted to the checkedlistbox I want it to be auto selected everytime it adds a new data to the checkedlistbox.
The problem, usually simple, of setting the Checked state of a newly added Item of a CheckedListBox, was somewhat complicated because of the nature of the Listbox in question.
The CheckedListBox is a Custom Control with custom events and properties.
It's actually a relatively common customization (one implementation can be see in this MSDN Forum post:
Have ListBox an event when addid or removing Items?) but it might complicate the interpretation of the events.
The Custom Control, when adding a new Item to the List (from a class other than the Form that hosts the Control), raises the custom ItemAdded event
private void [CheckedListBox]_ItemAdded(object sender, ListBoxItemEventArgs e)
The Index of the Item added is referenced by the e.Index property of the custom ListBoxItemEventArgs object.
With the code provided, it can be determined that this event handler is the natural place where the Checked state of the new Item can be set:
private void lst_BarcodeScanEvents_ItemAdded(object sender, ListBoxItemEventArgs e)
{
lst_BarcodeScanEvents.SetItemChecked(e.Index, true);
}
For that I suggest to create an event that you subscribe after you InitializeComponent() of your form like so :
public Form1()
{
InitializeComponent();
lst_BarcodeScanEvents.ControlAdded += new ControlEventHandler(AddedNewSelect);
}
private void AddedNewSelect(object sender, ControlEventArgs e)
{
lst_BarcodeScanEvents.SetItemChecked(e.Control.TabIndex, true);
}

Trigger function in main form after serial data is received

I am making a program that reads values on NFC cards.
I have a class that reads values from Serial comms sent by an EMBED hooked up to an NFC IC. Once data has been received, the program (in the main form) must change 'page' and process the data.
I have used User Controls as different 'pages'.
I understand that there are multiple threads and i cannot change the GUI from a different thread without using some sort of Invoke/Deleagate?. However, i am unsure on how to implement this on my code.
Here is the code i have (with omissions of irrelevant code):
SerialData Class
public class SerialData
{
public SerialPort ComPort;
public String savedText;
public SerialData()
{
ComPort = new SerialPort();
// set port data
ComPort.PortName = "COM5";
ComPort.BaudRate = 9600;
ComPort.DataBits = 8;
ComPort.RtsEnable = true;
ComPort.DtrEnable = true;
}
public void readCard()
{
try
{
savedText = "";
ComPort.Open();
ComPort.DataReceived += ComPort_DataReceived;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message + ex.StackTrace);
}
}
private void ComPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var serialPort = (SerialPort)sender;
var data = serialPort.ReadExisting();
savedText += data;
}
}
Form1 Code
public partial class Form1 : Form
{
List<UserControl> Pages = new List<UserControl>();
public int CurrentPage = 0;
SerialData SerialDataDriver = new SerialData();
public Form1()
{
InitializeComponent();
Pages.Add(page1);
Pages.Add(page2);
Pages.Add(page3);
// change to first page
ChangeToPage(0);
}
private void Page1ButtonClick(object sender, EventArgs e)
{
ChangeToPage(1);
SerialDataDriver.readCard();
}
void ComPort_DataReceived(string data)
{
SerialDataDriver.ComPort.Close(); /// close ComPort
ChangeToPage(2);
}
}
Here's an example of a custom event:
public class SerialData
{
public SerialPort ComPort;
public String savedText;
public delegate void Data(string message);
public event Data NewData;
private void ComPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var serialPort = (SerialPort)sender;
var data = serialPort.ReadExisting();
savedText += data;
if (NewData != null)
{
NewData(savedText);
}
}
}
...and how to wire up and receive it in the form:
public Form1()
{
InitializeComponent();
Pages.Add(page1);
Pages.Add(page2);
Pages.Add(page3);
// change to first page
ChangeToPage(0);
SerialDataDriver.NewData += SerialDataDriver_NewData;
}
private void SerialDataDriver_NewData(string message)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate() {
SerialDataDriver_NewData(message);
});
}
else
{
// do something with "message"
this.label1.Text = message;
}
}
You can make a wrapper that implements SerialPort events using custom add and remove accessors:
using System;
using System.IO.Ports;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private readonly SerialData _data;
public Form1()
{
InitializeComponent();
Closing += (sender, args) => { _data.Dispose(); };
_data = new SerialData(new SerialPort("COM5", 9600));
_data.DataReceived += Serial_DataReceived;
}
private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// do something
}
}
public class SerialData : IDisposable
{
private readonly object _locker = new object();
private readonly SerialPort _port;
public SerialData(SerialPort port)
{
_port = port ?? throw new ArgumentNullException(nameof(port));
}
#region IDisposable Members
public void Dispose()
{
_port.Dispose();
}
#endregion
public event SerialDataReceivedEventHandler DataReceived
{
add
{
lock (_locker)
{
_port.DataReceived += value;
}
}
remove
{
lock (_locker)
{
_port.DataReceived -= value;
}
}
}
}
}

Motorola BarCode scan not working

I am trying to write a small application to read BarCode using Motorola MC5040 Symbol device. Clicking on a button on form should read BarCode.
I am having hard time finding any sample projects. I reference Symbol and Symbol.Barcode DLLs
Here is the code that is not working. Not sure how to control the side buttons on device either.
public partial class Form1 : Form
{
public static Symbol.Barcode.Reader SymbolReader = new Reader();
public static Symbol.Barcode.ReaderData SymbolReaderData = new ReaderData(ReaderDataTypes.Text, 100);
public static System.EventHandler SymbolEventHandler = null;
public Form1()
{
InitializeComponent();
InitScanner();
}
public void InitScanner()
{
SymbolEventHandler = new EventHandler(this.SymbolReader_ReadNotify);
SymbolReader.Actions.Enable();
}
public void SymbolReader_ReadNotify(object sender, EventArgs e)
{
SymbolReader.Actions.Enable();
Symbol.Barcode.ReaderData TheReaderData = SymbolReader.GetNextReaderData();
if (TheReaderData.Result == Symbol.Results.SUCCESS )
{
txtBarcode.Text = TheReaderData.Text.ToString();
SymbolReader_CycleScannerReader();
return;
}
SymbolReader_CycleScannerReader();
}
public void SymbolReader_CycleScannerReader()
{
SymbolReader.Actions.Read(SymbolReaderData);
}
private void button1_Click(object sender, EventArgs e)
{
SymbolReader_ReadNotify(sender, e);
}
}
}
Any pointers or correction will be great.
Here is a sample application using the Symbol.Barcode2 library
https://github.com/bigfont/2013-128CG-Vendord/blob/master/HelpfulStuff/CS_Barcode2Sample1/API.cs
if you initialize a Barcode2 object you can then use that object to capture scan data
var myBarcode2Obj = new Barcode2();
myBarcode2Obj.OnScan += //Your scan even here;

Handle serial thread event in WPF GUI class

I have a serial port class, and I would like to control send/receive via my GUI, and have the GUI update based on receipt of data from the serial port (or other events). So the two relevant classes are the serial class and the main window class.
I have the code below which compiles, but I get an exception when I try to run.
public class MySerThread
{
public SerialPort serport;
public event SerialDataReceivedEventHandler newSerData;
public MySerThread()
{
serport = new SerialPort("COM1", 115200);
serport.Open();
serport.DataReceived += DataReceivedHandler;
}
public void DataReceivedHandler(object s, SerialDataReceivedEventArgs e)
{
byte[] data = new byte[serport.BytesToRead];
serport.Read(data, 0, data.Length);
// here's where I think I'm going wrong?
if(newSerData != null)
newSerData(s,e);
}
}
And then in my GUI class...
public partial class MainWindow : Window
{
MySerThread myPort;
public MainWindow()
{
// Exception triggers here
myPort.newSerData += DisplaySerDataHandler;
}
private void DisplaySerDataHandler(object sender, SerialDataReceivedEventArgs e)
{
this.ReceivedCallback(e);
}
private void ReceivedCallback(SerialDataReceivedEventArgs e)
{
if(this.someTextBlock.Dispatcher.CheckAccess())
{
this.UpdateTextBlock(e);
}
else
{
this.someTextBlock.Dispatcher.BeginInvoke(new Action<SerialDataReceivedEventArgs>(this.UpdateTextBlock), e);
}
}
private void UpdateTextBlock(SerialDataReceivedEventArgs e)
{
someTextBlock.Text = "got new data";
}
}
So, what am I doing wrong here? What is the best way to do this?
You can't access myPort without creating an instance.
MySerThread myPort = new MySerThread();

Categories

Resources