Yesterday I had to write a program that let me test a serial data path between two electronic designs. We use Visual Basic around here since it’s quick and easy. The current version of the software we have is Visual Studio 2010.
We probably should to upgrade to Visual Studio 2012, but it were not really “power users” so I don’t think we’d get much more efficient than if we stick with the 2010 version. Maybe we’ll switch in another year or two.
The serial test program was a pretty simple project. And its only for internal use, so just hammering away at a solution works, and I was able to get this done and tested in a couple of hours.
The first thing I needed to do was select two COM ports on a computer. I used a “listbox” control for displaying and selecting the COM port. The code below is modified from my program in that it only shows a single listbox that gets filled with available COM ports. In the image above it is the left hand listbox. I also have the routine display an error message if there are no ports. And I make sure that if there is a listbox item selected when this routine is called its index is stored and then re-selected at the end of the routine when ports are displayed. To add the second COM port listbox you just copy and paste the code below into the same routine and rename the variables so there is a set for each port you will be selecting.
Public Sub GetSerialPortNames() Dim SelectedCom As Integer If ListBoxCOM.Items.Count <> 0 Then SelectedCom = ListBoxCOM.SelectedIndex End If ListBoxCOM.Items.Clear() For Each sp As String In My.Computer.Ports.SerialPortNames ListBoxCOM.Items.Add(sp) Next If ListBoxCOM.Items.Count = 0 Then ListBoxCOM.Enabled = False labellistboxCOM.Text = "No Ports" labellistboxCOM.ForeColor = Color.Crimson Else ListBoxCOM.Enabled = True ListBoxCOM.SelectedIndex = SelectedCom labellistboxCOM.Text = "" End If End Sub
Serial data can go two directions in my program. It can go from port1 to port2, or from port2 to port1. These are two different electrical paths, so they both need to be tested. The routine below sends data in one direction. I have another routine not shown that sends it the other. Since I’m just checking that the each path works I can send/receive strings. These are much easier to send in VS2010 than byte arrays.
When I call the serial communication routine the first thing I do is update the baud rate (by testing some radio button states on the form). It might be nice to have this software cycle through baud rates so it tests communication across a variety range of speeds). However, there are 4 or 5 different devices that need to be tested with this software, and it probably takes more time to come up with the best way to implement an automatic system than just sticking radio buttons on the form.
After baud rate selection I open the receiving port (determined by the port selected in the ListboxCOM2 control) and then open the transmitting port (ListboxCOM control) . With both ports open I use the WrtieLine and ReadLine functions to send and receive a string of data with the two ports. The string I send is in a textbox on the form. That way I can easily change the data if necessary.
Private Sub Port1_to_Port2_Serial_Communication() Dim BaudRate As Integer = 9600 If RadioButton_Baud2400.Checked = True Then BaudRate = 2400 ElseIf RadioButton_Baud9600.Checked = True Then BaudRate = 9600 ElseIf RadioButton_Baud19200.Checked = True Then BaudRate = 19200 ElseIf RadioButton_Baud38400.Checked = True Then BaudRate = 38400 ElseIf RadioButton_Baud57600.Checked = True Then BaudRate = 57600 End If Dim port2 As IO.Ports.SerialPort = Nothing Try port2 = My.Computer.Ports.OpenSerialPort(ListBoxCOM2.SelectedItem) port2.BaudRate = BaudRate port2.ReadTimeout = 100 Using port1 As IO.Ports.SerialPort = _ My.Computer.Ports.OpenSerialPort(ListBoxCOM.SelectedItem) port1.BaudRate = BaudRate port1.WriteLine(TextBox_Port1_transmit.Text) port1.Close() End Using Dim Incoming As String = port2.ReadLine() If Incoming Is Nothing Then TextBox_Port2_receive.Text = "" Else TextBox_Port2_receive.Text = Incoming End If Catch ex As TimeoutException Label_CommStatus.Text = "READ TIMEOUT" Label_CommStatus.BackColor = Color.Red Finally If port2 IsNot Nothing Then port2.Close() End Try End Sub
To tie all of this together I run a timer that checks various flags to determine if a test has been activated. The flags are set with button presses that are not shown here. For example, I have a button that sets the flag Command_SerialSend_1to2. When this flag is set I call the Port1_to_Port2_Serial_Communication() function inside my timer routine. After being called the timer code tests to see if my transmit textbox is the same as my receive textbox. If they match then the serial data transfer was successful. I’ve also included a checkbox control associated with each function that prevents the command flag from being cleared. By selecting the checkbox I can make the test program send strings continuously. This is helpful when you need to probe around on a circuit with your oscilloscope to find out where a serial data path is broken.
I even added a couple of visual indicator that tell me if I had good or bad data transfer. When the data is sent and received correctly I turn the background of the receive textbox green and append the number of good data packets sent/received. I have good and bad packet counters that help determine if there are intermittent failures. We might run a system for a day or two to see if communication is error free, and these counters can help do that.
A second command I have is called “fast ping pong”. It’s not very fast, but is named after the fact that I use it to send 16 strings alternating from port 1->2 then port 2->1. It’s a fast test for me to see if the modules I’m testing are working. When I press the Fast Ping Pong button I get repeated communication attempts in both directions for a couple of seconds. So a single button press does all of my testing, and that makes life easier.
Private Sub TimerRead_Tick_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerRead.Tick TimerRead.Enabled = False Label_CommStatus.Text = "COM OK" Label_CommStatus.BackColor = Color.LightSteelBlue 'Do fast Ping If Command_SerialSend_Ping = True Then Command_SerialSend_1to2 = True Command_SerialSend_2to1 = True PingCount = PingCount - 1 If PingCount = 0 Then Command_SerialSend_Ping = False End If End If 'Update COM port If Command_ChangePort = True Then Command_ChangePort = False GetSerialPortNames() End If If Command_SerialSend_1to2 = True Then If CheckBox_Repeat_1to2.Checked = False Then Command_SerialSend_1to2 = False End If Port1_to_Port2_Serial_Communication() If TextBox_Port1_transmit.Text = TextBox_Port2_receive.Text Then SerialTest_GoodCount = SerialTest_GoodCount + 1 TextBox_Port2_receive.Text = TextBox_Port2_receive.Text & " " & SerialTest_GoodCount TextBox_Port2_receive.BackColor = Color.LightGreen Else SerialTest_BadCount = SerialTest_BadCount + 1 TextBox_Port2_receive.Text = TextBox_Port2_receive.Text & " " & SerialTest_GoodCount TextBox_Port2_receive.BackColor = Color.LightPink End If End If If Command_SerialSend_2to1 = True Then If CheckBox_Repeat_2to1.Checked = False Then Command_SerialSend_2to1 = False End If Port2_to_Port1_Serial_Communication() If TextBox_Port2_transmit.Text = TextBox_Port1_receive.Text Then SerialTest_GoodCount = SerialTest_GoodCount + 1 TextBox_Port1_receive.Text = TextBox_Port1_receive.Text & " " & SerialTest_GoodCount TextBox_Port1_receive.BackColor = Color.LightGreen Else SerialTest_BadCount = SerialTest_BadCount + 1 TextBox_Port1_receive.Text = TextBox_Port1_receive.Text & " " & SerialTest_GoodCount TextBox_Port1_receive.BackColor = Color.LightPink End If End If Label_BadCount.Text = SerialTest_BadCount Label_GoodCount.Text = SerialTest_GoodCount TimerRead.Enabled = True End Sub