Introduction
This article discusses the demo programme included with the ArduinoSerialClass library described in Part 2 which itself connects with devices running the a standard Arduino Sketch Framework described in Part 1
Background
The demo programme simply illustrates techniques for using the ArduinoSerialClass Library DLL. This allows an application to connect to a board and automatically discover what values and status information it is returning and what specific commands it will respond to.
If you happen to have a number of boards all doing different things with different sensors and output devices this enables you to have a generic application which will communicate correctly with any of the boards.
For example you many have several different units which are all capable of, amongst other things, reporting the current temperature. Using the standard framework and by making each version report the temperature in the same format you can have an application which will read the temerature from any of the boards.
Using the code
The code bundled with this article is the same as in Part 2. Here we are going to describe how the Demo application uses the ArduinoSerialClassLibrary.
To recap the library has two objects - DetectArduinoClass
and ArduinoSerialClass
DetectArduinoClass
has a constructor, one public method and one property.
Constructor New(String)
takes an optional String parameter "target" which will be the start of the full name of the port(s) to find. The target defaults to "Arduino". New()
scans the Management Objects looking for serial ports whose name matches the target.
Property PortList
is a Dictionary(Of String, String) which will contain a list of all of the ports that match the target name. The short name will be the key (eg COM23) and the full name will be the definition value (eg "Arduino Leonardo (COM23)")
Method Rescan(String, Boolean)
searches the management objects again. The string parameter is again optional as the target name to find, and the boolean parameter if true will clear the existing port list before rescanning.
ArduinoSerialClass
is slightly more complex. As well as a constructor New(String)
it has two methods GetFullStatus()
and DoCmd(String)
.
Properties Vals
, ValDesc
, Cmds
, Status
, StatusDesc
, InputPins
and OutputPins
are all Dictionary(Of String,String). The keys are the identifiers used for particular items and the definitions are the descriptions or, in the case of Vals
and Status
, the actual values for the particular item. Vals
and ValDesc
will have the same keys, as will Status
and StatusDesc
Turning to the demo program TestApp, it is found as a separate project in the code file consists of a single windows form.
On the left is a panel headed "Available Devices". This simply shows the PortList property from a DetectArduinoClass
object on form load.
Dim det As New DetectArduinoClass()
If det.PortList.Count > 0 Then
Dim ypos As Integer = 100
For Each item In det.PortList
Dim lbl As New Label
lbl.Name = item.Key
lbl.Top = ypos
lbl.AutoSize = True
lbl.Left = 20
lbl.Text = item.Key & " = " & item.Value
grpDevices.Controls.Add(lbl)
ypos += 20
Next
In a full application we would probably create a connect button against each item so that we could choose which device to connect to. For now we'll simply connect to the first one found.
Private WithEvents ard As ArduinoSerialClass
ard = New ArduinoSerialClass(det.PortList.Keys(0))
On the right of the screen we have panels to show the capabilities of the connected board and display its current status values and any dynamic values that are being reported.
When we create the new object ArduinoSerialClass(portname)
if the serial port can be opened it immediately requests all the descriptions and status information. This may take a few moments to be updated so we will fire off a timer to give the board time to respond and then interrogate the information returned in the object.
ard = New ArduinoSerialClass(det.PortList.Keys(0))
tmrChk.Start()
Private Sub tmrChk_Tick(sender As Object, e As EventArgs) Handles tmrChk.Tick
tmrChk.Stop()
If ard.PortFound Then
lblConnected.Text = ard.ComPortName
lblConnected.BackColor = Color.Green
grpDeviceInfo.Visible = True
grpSktechCmds.Visible = True
grpValues.Visible = True
ShowStatus()
Else
lblConnected.Text = "Not Connected"
lblConnected.BackColor = Color.Red
grpDeviceInfo.Visible = False
grpSktechCmds.Visible = False
grpValues.Visible = False
End If
End Sub
So if the ard object reports that it has connected to the port we can now update the status on the screen
The firmware and hardware info is all in property FStatus
txtStatus.Text = "Unit Name: " & ard.FStatus.UnitID & vbCrLf
txtStatus.Text &= "Firmware: " & ard.FStatus.Sketch & vbCrLf
txtStatus.Text &= "Ver: " & ard.FStatus.Ver & " "
txtStatus.Text &= " " & ard.FStatus.CompDate & vbCrLf
txtStatus.Text &= "Author: " & ard.FStatus.Author
The system status which is common to all boards using the framework is in properties SendSerialEnabled
and LoopTime
.
txtSystemStatus.Text = "Serial values out = " & IIf(ard.SendSerialEnabled,"ON", "OFF") & vbcrlf
If ard.SendSerialEnabled Then
btnEnableLoop.Text = "Enable Serial Out"
btnEnableLoop.BackColor = Color.LightPink
Else
btnEnableLoop.Text = "Disable Serial Out"
btnEnableLoop.BackColor = Color.LightGreen
End If
txtSystemStatus.Text &= "Arduino Loop Time = " & ard.LoopTime & " ms"
If ard.LoopTime >= numLoop.Minimum Then
numLoop.Value = ard.LoopTime
Else
numLoop.Value = numLoop.minimum
End If
We can also have controls to set those properties. In both cases after setting the property we fire off tmrChk again so that it can update any changes once they have been actioned.
Private Sub btnEnableLoop_Click(sender As Object, e As EventArgs) Handles btnEnableLoop.Click
If ard.PortFound Then
ard.SendSerialEnabled = Not ard.SendSerialEnabled
tmrChk.Start()
End If
End Sub
Private Sub btnLoop_Click(sender As Object, e As EventArgs) Handles btnLoop.Click
If ard.PortFound Then
ard.LoopTime = numLoop.Value
numLoop.Value = 999
tmrChk.Start
End If
End Sub
For the various descriptors - commands, inputs and outputs, we can just update a text box to list them
txtSketchCmds.Text = ard.Cmds.Count & " Commands reported" & vbcrlf
For Each cmd In ard.Cmds
txtSketchCmds.Text &= cmd.Key & " " & cmd.Value & vbCrLf
Next
and similarly for the others. To send a command to the unit we have simply provided a textbox to enter the required string and a button to send it. We could dynamically create a separate button and parameter box for each command.
For the unit status and values, we want to read the descriptor from one dictionary (.ValDesc
or .StatusDesc
) and then the corresponding value from the other dictionary (.Vals
or .Status
)
txtSketchStatus.Text = ard.StatusDesc.Count & " status report values" & vbCrLf
For Each entry In ard.StatusDesc
txtSketchStatus.Text &= entry.Value & " = " & ard.Status(entry.Key) & vbcrlf
Next
Dim ypos As Integer = 55
For Each entry In ard.ValDesc
Dim lblD As New Label
lblD.Name = "lblDesc" & entry.Key
lblD.Top = ypos
lblD.AutoSize = True
lblD.Left = 20
lblD.Text = entry.Value
grpValues.Controls.Add(lblD)
Dim lblV As New Label
lblV.Name = "lbl" & entry.Key
lblV.Top = ypos + 14
lblV.AutoSize = True
lblV.Left = 20
lblV.Text = "x"
lblV.Font = New Font(lblV.Font, FontStyle.Bold)
grpValues.Controls.Add(lblV)
ypos += 40
Next
For the values we have created Label controls with the name "lbl" & entry.key - for eample lblA
This will enable us to find the appropriate label when we want to update its value when we are polling the board, or in response to an event raised by the ArduinoSerialClass
object.
A poll timer, tmrPoll
, will be enabled whenever the board is sending serial values. When it fires we will get the values and update them. SInce we may have many values to collect there is a possibility that the _vals
structure in the ArduinoSerialClass might get updated whilst we are accessing it which will generate an exception, so we will start by making a local copy of it and working with that.
Dim lbls As Control()
Dim cpy As Dictionary(Of String, String)
Try
cpy = New Dictionary(Of String, String)(ard.Vals)
For Each v In cpy
lbls = Me.Controls.Find("lbl" & v.Key, True)
lbls(0).Text = v.Value
Next
Catch ex As Exception
End Try
Note that lbls()
is an array of controls returned by .Find
, we are assuming that there is only one control with a matching name and using lbls(0)
. You can improve this.
And that's really all there is to it. The full code together with the project for the ArduinoSerialClassLib is in the download. You will need Visual Studio to edit this of course.
Assuming you don't want to change anything in the class library you can use the compiled DLL in the bin/release folder. If you are making changes you'll probably be wanting to work with the bin/debug version at first and update the TestApp reference after every compile.
Points of Interest
So we have seen the possible benefits of using a standardised approach to all of our Arduino sktech programmes and having a standard library to access them. Code re-use, more rapid application development focussing on the important stuff at each end - the sensor and device controls in the Arduino sketch and the user interface and data structures at the control PC end.
Of course there is an overhead - you are loading up some of PROGMEM on the Arduino with a lot of string constants that you might not need - you can always trim them down if the real useful code gets too big.
What has been presented here is just one possible way of doing this. You may have your own requirements for a communication - command and response - protocol between the Arduino and the PC. Whatever best suits your particular way of working and applications is the best solution.
This particular format has enabled us to develop a lot of variants on our particular network of things.
I'd be very interested to hear of any suggestions for alternatives and improvements - either as comments on here, or new articles or simply email me.
Stay connected
Roger C-O, August 2016
History
First published 2nd August 2016
Engineering career - originally precision mechanical, then electronic, then computer, then telecommuncations, then marketing, then software and everything else involved in specifying, designing, developing and implementing small systems using hardware, firmware and software.
Expertise in various tools and languages over the years as fashions and requirements shifted. Moved from screwdrivers and soldering irons into 8080 and 6502 machine code and thence to many high level languages.
Currently mostly in a .NET standalone windows and web application environment with SQL server but also using LAMP environment when appropriate.
Retiring soon, but always open for interesting projects that float my old wooden sailing boat.