I'm developing an app that takes bytes from COM port and manage them.
Unfortunately I don't have the opportunity to work on pc where this app will run, so I need a way to send "fake data" on my COM port so my app can read it.
Whatever I do, no bytes are available on selected COM port.
This is a basic sample:
'First I create and open desired COM port
Dim port = New SerialPort(cboPorts.SelectedItem, 9600, Parity.None, 8, StopBits.One)
port.Open()
If Not port.IsOpen Then Exit Sub
Then, with a timer, I try to write on it and then read:
Private Sub tmrTest_Tick(sender As Object, e As EventArgs) Handles tmrTest.Tick
Dim tot = rnd.Next(0, 3)
If tot > 0 Then
Dim buffer(tot) As Byte
rnd.NextBytes(buffer)
port.Write(buffer, 0, tot)
Dim t = port.BytesToRead
Console.WriteLine("Got {0} bytes on COM port", t)
End If
End Sub
In real app, bytes are not read right after they are written (I have a timer that reads COM port every second) but result is the same: port.BytesToRead is always zero.
Serial ports don't work this way. You can't read back the data you just wrote out. When you read, you you read the input buffer and when you write you write to the output buffer (you can't read the output buffer)
The only way to simulate what you want is to use another machine and connect a serial port between them, or read and write to two different serial ports on the same machine AppA writes to serial port 1 and reads from port 2 and AppB does the opposite.
Related
I have a barcodescanner hooked up to a serialport. I read the Serialport via DataReceived-Event
Now there is some weird timing issue occurring on repetitive scans that I try to explain in the following. So can you explain to me what is causing this difference?
I use this barcode: 01513511220001.
I scan it twice with both examples
Example1:
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SP1.DataReceived
If SP1.BytesToRead > 0 Then
Threading.Thread.Sleep(1) '!!!
Dim str As String = SP1.ReadExisting
Debug.WriteLine(str)
End If
End Sub
'Output Scan 1:
'01513511220001
'Output Scan 2!!!:
'01513511220001
Example2:
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SP1.DataReceived
If SP1.BytesToRead > 0 Then
Dim str As String = SP1.ReadExisting
Debug.WriteLine(str)
End If
End Sub
'Output Scan 1:
'01513511220001
'Output Scan 2!!!:
'015135112
'20001
Note sometimes it cuts after the 9th digit sometimes it cuts after the 8th digit.
Understanding ReadExisting
You are using the ReadExisting function, which as per the documentation
Reads all immediately available bytes, based on the encoding, in both the stream and the input buffer of the SerialPort object.
As it says it immediately reads and gives you the data, even though it's not fully complete. A barcode scanner does not know how long the code is so it continuously reads it, it is up-to the software to make sense of it.
Understanding DataReceived
Similarly DataReceived method is called anytime there is data received via the port as per documentation (regardless of partial or full data, which depends on barcode scanner, internal buffers, etc.)
Indicates that data has been received through a port represented by the SerialPort object.
Again it is upto the software to make sense of the data being received.
Understanding your examples
The reason why your example 1 always works is because you add a delay before reading the actual data, giving a chance for the internal buffers to be full and hence you captured the full data upon calling ReadExisting
Note: If you have to add a delay to the code, it's always the wrong way to do it (extreme exceptions exist, but this isn't it)
Possible Solutions
I would suggest using ReadLine method but it has its challenges
Simply put a logic to wait till all 14 characters are read before moving to next step of processing (given you have a fixed size code, even not this can still be done but logic becomes more complex).
Crude Example for Solution 2
Private Sub SerialPort1_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs) Handles SP1.DataReceived
If SP1.BytesToRead >= 14 Then 'Basically wait for the bytes to fill upto the length of the code
Dim str As String = SP1.ReadExisting
Debug.WriteLine(str)
End If
End Sub
Note: I have not ran the above example, it's just indicative to what needs to be done
Specifically for Honeywell 1911i
As per its manual, you need to ensure the Programming Interface for the device is set to RS232 Serial Port to do this refer to page 2-1 (page 27) of the manual under the heading Programing the Interface > RS232 Serial Port.
In this interface it automatically adds the suffix of Carriage Return and Line Feed, basically the NewLine character. Excerpt from the manual
The RS232 Interface bar code is used when connecting to the serial port of a PC or terminal. The following RS232 Interface bar code also programs a carriage return (CR) and line feed (LF) suffix, baud rate, and data format as indicated below. It also changes the trigger mode to manual.
This should allow you to use the ReadLine method to fetch the barcode without complexity.
You can set this interface by simply scanning the RS232 Interface Bar Code from the manual.
I have done some research online on how to send audio from one computer to the other in VB.net / C# using NAudio. With all the information that I've found on the internet I made two test programs: One that sends the audio (Wasapi Loopback) and one that receives and plays the audio. They look like this
Sender:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Using Capture As New WasapiLoopbackCapture()
MsgBox("Format information" + vbNewLine + vbNewLine + " Sample Rate: " + Capture.WaveFormat.SampleRate.ToString() + vbNewLine + " Bits per Sample: " + Capture.WaveFormat.BitsPerSample.ToString() + vbNewLine + " Channels: " + Capture.WaveFormat.Channels.ToString(), 64, "")
Dim client As New UdpClient
Dim ep As New IPEndPoint(IPAddress.Parse("192.168.1.101"), 12345)
client.Connect(ep)
Capture.StartRecording()
Using w As New WaveFileWriter("dump.wav", Capture.WaveFormat)
AddHandler Capture.DataAvailable, Sub(s As Object, f As WaveInEventArgs)
client.Send(f.Buffer, f.BytesRecorded)
w.Write(f.Buffer, 0, f.BytesRecorded)
End Sub
Threading.Thread.Sleep(10000)
Capture.StopRecording()
End Using
End Using
End Sub
Receiving End:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim client As New UdpClient(12345)
Dim endPoint As New IPEndPoint(IPAddress.Any, 12345)
Dim ms As MemoryStream
While True
Dim data() As Byte
data = client.Receive(endPoint)
ms = New MemoryStream(data)
ms.Position = 0
Dim rs As New RawSourceWaveStream(ms, New WaveFormat(44100, 32, 2))
Dim wo As New WaveOut()
wo.Init(rs)
wo.Play()
End While
End Sub
This does work and all, it creates the dump.wav file (which is not distorted) and sends the audio over to the receiving end and it plays the audio, however this audio is very loud and heavily distorted.
I am not sure what is causing this problem, but it may be caused by the UDPClient. But I don't think the sender is causing the problem, as the dump.wav is generated with no distortions or loud audio.
I was unable to find any solution online and have no idea what the source of the problem actually is.
I suspect its a WaveFormat mismatch causing the distortion. It's not 32 bit PCM, but floating point so use WaveFormat.CreateIeeeFloatWaveFormat.
But you may want to reconsider sending this volume of audio data over the network. Usually for chat applications you'd want to reduce the bandwidth by compressing the audio, and maybe going for mono and a lower sample rate.
The NAudioDemo project has a network chat sample showing how to do this.
On its own, the UDP protocol has no way of knowing if a data packet is lost along the way, thus it does not guarantee arrival of the packets you send.
If a data packet is lost then your file will (of course) become corrupted. The solution is either to switch to TCP (which guarantees data arrival and order as long as the connection is active) or find yourself a reliable UDP implementation.
Two bottlenecks with TCP are:
It is a streamed protocol, meaning there is no notion of packets on the application layer. It just delivers a (constant) stream of bytes.
It is somewhat slower than UDP (but not necessarily too slow!). Whether its speed is sufficient you'll have to determine yourself.
The first bottleneck can be resolved by implementing your own message framing protocol, meaning code that keeps track of the length of each piece of data you send.
Message framing description: https://stackoverflow.com/a/37352525/3740093
VB.NET implementation: https://stackoverflow.com/a/35240061/3740093
All I'm looking for is a simple TCPClient/Listner example on Windows Form VB.Net. I'm a newbie and Microsoft TCPClient/Listner class examples are not what I am looking for. All I am looking is for the TCPClient to send a message and for a TCPListener to get the message and to send a message back "I got your message" ?
A little help would be great. I have some codes, but is only to send message to server and not back from server to client..
Any help will be very appreciated..
TCP communication is stream-based, which means it doesn't handle any packets. Due to this, messages that you receive might be either partial or lumped together.
You could for example send:
Hello!
How are you?
But you might receive:
Hello!How are you?
or:
Hello!How ar
e you?
(or something similar)
To fix this you must apply something called "length-prefixing". Length-prefixing (or length prefixing) means that before you send a message, you put its length (amount of characters/bytes) in the beginning of it. By doing so, the endpoint will know exactly how many bytes to read for each message. Thus there will be no problems with messages being partial or lumped together.
This is not the most straightforward thing to do as a beginner, as to get it to work properly on both sides you have to structure your code just right. So I've created two classes that will take care of this for you. See the examples below on how to use them for simple text message-based communication.
Link to source: http://www.mydoomsite.com/sourcecodes/ExtendedTcpClient.zip
Link to C# source : http://www.mydoomsite.com/sourcecodes/ExtendedTcpClient%20CSharp.zip
EDIT (2019-11-08)
Some time ago I made an upgraded version of this with a bit better code structure and error handling. For those of you interested, the new code can be downloaded here (VB.NET only):
https://www.mydoomsite.com/sourcecodes/ExtendedTcpClient%202.0.zip
Example usage
Note that in those examples Client does not refer to the client side, but to the TcpClient.
Server side
First declare a new variable for ExtendedTcpClient, and be sure to
include WithEvents in the declaration.
Dim WithEvents Client As ExtendedTcpClient
Then you will need a TcpListener and a Timer to check for incoming connections.
Dim Listener As New TcpListener("0.0.0.0", 5555) 'Listen for any connection on port 5555.
Dim WithEvents Tmr As New System.Windows.Forms.Timer
Then you need to subscribe to the timer's Tick event.
Private Sub Tmr_Tick(sender As System.Object, e As System.EventArgs) Handles Tmr.Tick
End Sub
In there you check for incoming connections via the Listener.Pending() method. When you are to accept a connection you first declare a new
instance of the ExtendedTcpClient. The class requires to have a
form as its owner, in this application Me is the current form.
Then you use the ExtendedTcpClient.SetNewClient() method with
Listener.AcceptTcpClient() as its argument to apply the
TcpClient from the listener. Put this code in the Tmr_Tick sub:
If Listener.Pending() = True Then
Client = New ExtendedTcpClient(Me)
Client.SetNewClient(Listener.AcceptTcpClient())
End If
Now the client and server are connected to each other.
Now you need to subscribe to the PacketReceived event of the
client. Create a sub like so:
Private Sub Client_PacketReceived(sender As Object, e As ExtendedTcpClient.PacketReceivedEventArgs) Handles Client.PacketReceived
End Sub
All received data are presented in an array of bytes.
In the PacketReceived sub you can output the received packet as text into a TextBox. Just check if the packet header is PlainText and then
you can convert the received packets contents (which is an array of
bytes, accessed via e.Packet.Contents) to a string and put it in
the TextBox.
If e.Packet.Header = TcpMessagePacket.PacketHeader.PlainText Then
TextBox1.AppendText("Message recieved: " & System.Text.Encoding.Default.GetString(e.Packet.Contents) & Environment.NewLine)
End If
System.Text.Encoding.Default.GetString() will convert a byte array to normal text.
In the PacketReceived sub you can also make it send "Message received" to the client.
Dim ResponsePacket As New TcpMessagePacket(System.Text.Encoding.Default.GetBytes("Message received."), TcpMessagePacket.PacketHeader.PlainText)
ResponsePacket.Send(Client.Client) 'Get the ExtendedTcpClient's underlying TcpClient.
Lastly, when closing the form you just need to disconnect the client.
Private Sub ServerWindow_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If Client IsNot Nothing Then Client.Disconnect()
End Sub
And that's it for the server side.
Client side
For the client side you will do pretty much the same as the server side, though you won't be needing a TcpListener nor a Timer.
Dim WithEvents Client As New ExtendedTcpClient(Me) 'The current form as its owner.
Connect to the server via the IP and port you've given the listener.
Client.Connect("127.0.0.1", 5555) 'Connects to localhost (your computer) at port 5555.
Now if you want to send text to the server you'd do something like this (in for example a button):
Dim MessagePacket As New TcpMessagePacket(System.Text.Encoding.Default.GetBytes(TextBox2.Text), TcpMessagePacket.PacketHeader.PlainText)
MessagePacket.Send(Client.Client)
TextBox2 includes the text you want to send.
Lastly, you will need to subscribe to the PacketReceived event here too to check for responses from the server. In there you receive text just like the server does.
Private Sub Client_PacketReceived(sender As Object, e As ExtendedTcpClient.PacketReceivedEventArgs) Handles Client.PacketReceived
If e.Packet.Header = TcpMessagePacket.PacketHeader.PlainText Then
TextBox1.AppendText(System.Text.Encoding.Default.GetString(e.Packet.Contents) & Environment.NewLine) 'Prints for example "Message received." from the server.
End If
End Sub
And now everything should be working!
Link to a complete example project (only client-to-server): http://www.mydoomsite.com/sourcecodes/TCP%20Messaging%20System.zip
Link to C# example: http://www.mydoomsite.com/sourcecodes/CSharp%20TCP%20Messaging%20System.zip
If you want to add more headers to the class (the headers indicate to you what kind of data each packet contains), open TcpMessagePacket.vb and add more values in the PacketHeader enum (located in the region called Constants).
Hope this helps!
Screenshot from the example project
(Click the image for larger resolution)
I take a part on a project and after many tests, me and my colleagues feel little disappointed by our coding skills and methodology. The issue is the following:
The application must read data from some type of RS-485 devices connected on a single bus via one or multiple serial ports. However, these devices come from different manufactures so as a result, they have different connection settings, different command sets and return different data. Moreover, each serial port can handle none, one or multiple devices simultaneously.
Each of our custom COMPort objects contains a device list and each device contains the type and the device settings that are recommended. So, after loading all settings and building all objects, the application starts polling each device in each COMPort object list. Every time, before writing each command, the serial port buffers are emptied and its settings change (Baud-rate, Parity, Handshake) without closing and reopening the port. After every command, a Threading.Thread.Sleep(delay) is executed with variable delay, depending on the response time of each device (delay varies from 150ms to 800ms). After Sleep() ends, application Reads the serial port and gets expected data. After that, either if BytestoRead > 0 or BytestoRead = 0 or an Exception occurs(wrong data returned, error in checksums, etc.), this function returns and application continues to read the next device in the list.
So, application executes in an endless loop, reading all devices from start to end, until it finally ends when user stops it from UI (when the project is a Windows Forms) or when the service Stops (when the project is a Windows service). The application does not use DataReceived event because we faced a lots of trouble, so the Read procedure is done manually.
Finally, application has a global log file which is accessible from all objects and holds all usual and unusual events and each COMPort object has its own log file in which all valid read data are written. Moreover, each COMPort has a timer object which checks for daytime changes per second and everyday at midnight all log files close and new files are opened.
The main problem is that all this seems to work great but unfortunately the application stops writing to logs after 1-2 hours (most of the times after 1:58 minutes exactly on one client machine), then after some minutes it crashes, sometimes with Send or Don't send window and sometimes silently. The CPU load never exceeds 1% and private & virtual memory does not seem to have a leak because it doesn't grow.
I have to mention that this application is tested on 3 machines (Windows XP, Windows 2003), on the following situations: mixed types of devices, one type of device, no devices connected (using null modem cable on one development machine.) Even on the last situation, the application crashes silently. This leads me to the thought that the issue has nothing to do with the Read procedure because in the “no devices attached” scenario, there is no data to be read. Also, the project is built with VB.Net 2008 using .Net Framework 2.0.
EDIT : Below is a small summary of the Read, Write and Polling procedures. I wrote it on Notepad++ as an example because I don't have access to the real code right now, so excuse any syntax errors. The aim of this is to give a better explanation of the situation I describe above and nothing more.
Private deviceIndex as Integer = -1
Private deviceList as List(Of Devices)
Private serialPort as SerialPort()
Private Sub PollSensor()
Try
If (deviceIndex <> -1) And deviceIndex > deviceList.Count then
deviceIndex = 0
End If
With serialPort
If .IsOpen then
.DiscardInBuffer()
.DiscardOutBuffer()
' SerialPort attributes must change during runtime to suit device requirements.
' Some devices require for example 4800 BaudRate and some other 9600.
.BaudRate = deviceList(deviceIndex).BaudRate
.Parity = deviceList(deviceIndex).Parity
.Handshake = deviceList(deviceIndex).HandShake
' Write command to device, passing device specific response time.
' Some devices have for example 150ms response time and some other 800ms.
WritetoDevice(new Byte[] {01, 01}, deviceList(deviceIndex).ResponseTime)
End If
End With
Catch ex As Exception
' Log
End Try
End Sub
Private Sub WritetoDevice(ByVal command As Byte[], ByVal responseTime As Integer)
Try
serialPort.Write(command, 0, command.Length)
' Cause application to pause for time equal with the device response time.
Threading.Thread.Sleep(responseTime)
' If device response is regular, data may have been available in serial port.
If ReadDevice() = 0 then
' Poll next device
deviceIndex += 1
PollSensor()
End If
Catch ex As Exception
' Log
End Try
End Sub
Private Function ReadDevice() As Integer
Dim data as String = ""
Try
With serialPort
If .IsOpen then
If .BytesToRead() > 0 Then
Do
data += .ReadExisting()
Until .BytesToRead() = 0
' Do something with data
End If
End If
End With
Catch ex As Exception
' Log
Finally
' Continue with the next device in deviceList, ignoring the current device Read() result
' This happens because devices return faulty data too often and the polling must continue
Return 0
End Try
End Function
We have tried to use the Debug Diagnostics Tool 1.2 x86 but the results returned, do not help us to understand the source of the issue. Some of the exported reports contain the following, although all exceptions are handled in Try..Catch blocks:
- The following threads in ... are waiting in a WaitOne
- 6,25% of threads blocked
- System.ExecutionEngineException
- System.StackOverflowException
- System.OutOfMemoryException
- System.FormatException
- System.Threading.ThreadAbortException
Does anyone have a better idea on how the above scenario could work?
Feel free to criticize any of the above methods, because that will help us to become better.
I got an Arduino hooked up to the PC and would like to read the serial port of it.
I read the serial port in Visual Basic with com.Readline, but it won't read everything.
It looses around 2-3 lines that were sent over the serial port to the Visual Basic code.
They get "lost".
If I go even with higher Baud rates (57600) it loses even more lines...
I use this to read:
Private Sub com9_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles com.DataReceived
Dim returnStr As String
returnStr = com.ReadLine
ReceiveSerialData(returnStr)
End Sub
Is the program too slow to get all data or what is the problem?
SerialPort.DataReceived Event has an interesting comment which may point you in the right direction:
The DataReceived event is not guaranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer.
Note that the page(s) also mention the fact that the buffering behavior complicates things...