65.9K
CodeProject is changing. Read more.
Home

Sketch framework and Class Library - Part 3

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.29/5 (4 votes)

Aug 2, 2016

CPOL

6 min read

viewsIcon

10814

downloadIcon

109

Demo Windows Programme for the standard interface for multiple Arduino boards using ArduinoSerialClassLibrary

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()
    'create a DetectArduino object and see what we've found
    If det.PortList.Count > 0 Then
        Dim ypos As Integer = 100
        For Each item In det.PortList
           'create label for each one found
            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

'...do the detction stuff as above

    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() 'we only want a one-shot timer
        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
        'simply toggle the property value and wait for a response
            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
            'write a silly value into the number control so that we know it gets updated with the status
            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
            'for each entry in the status description dictionary we will print the description and then use the key to get the relevant value from the status values dictionary
            txtSketchStatus.Text &= entry.Value & " = " & ard.Status(entry.Key) & vbcrlf
        Next

        'here we iterate through the value descriptions and for each one we create two label controls
        'the first holds the description and the second will be updated with the value every time we poll it
        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
                'we are going to make a copy of the values dictionary to work with
                cpy = New Dictionary(Of String, String)(ard.Vals)
                'actually we can still get an exception here if a value is changed whilst the copy is in progress
               ' - you can sort that one out for yourself.
               'It'll probably mean improving ArduinoSerial class with some semaphore
                For Each v In cpy
                    'each value will have a label named lblK where K is the key for the value
                    lbls = Me.Controls.Find("lbl" & v.Key, True)
                    lbls(0).Text = v.Value
                Next
            Catch ex As Exception
                ' for now ignore error and don't update any more values
            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