My starting point was my Servo Control via .NET application described a couple posts ago. It already had the framework for sending and receiving over the serial port, so we just need to make the adjustments for binary.
First to change is the UI. The text input field and send button has been replaced by 3 buttons, 9 track bars, and 18 numeric up/down controls.
The buttons are named as follows:
btnAllDown, btnAllMiddle, btnAllUp
The track bars are named as follows (range 0 to 100):
trackBar11, trackBar12, trackBar13, trackBar21, trackBar22, trackBar23, trackBar31, trackBar32, trackBar33
The numeric up/downs are named as follows (range 0 to 4096):
nudServoLowerLimit11, nudServoLowerLimit12, nudServoLowerLimit13, nudServoLowerLimit21, nudServoLowerLimit22, nudServoLowerLimit23, nudServoLowerLimit31, nudServoLowerLimit32, nudServoLowerLimit33
nudServoUpperLimit11, nudServoUpperLimit12, nudServoUpperLimit13, nudServoUpperLimit21, nudServoUpperLimit22, nudServoUpperLimit23, nudServoUpperLimit31, nudServoUpperLimit32, nudServoUpperLimit33
Next I defined the structure that will be sent over the serial line. This structure must match the one on the receiving end. Since this structure will be reused for several commands, I have the first byte be the command and a built-in getBytes() function for convenience.
Warning! The Marshal commands in .NET will create a byte array for a given structure. However, by default the byte array created will reserve 2 bytes for any variables listed as a Byte (the structure below would be 6 bytes in length). For it to be compatible with the Arduino side of the communication, we must tag the structure "LayoutKind.Sequential, Pack :=1". Now a Byte variable will be treated as 1 byte and the structure returned will be 5 bytes in length.
' Must set Sequential with Pack = 1 so .NET creates a byte array
' with the Byte as 1 byte (and not as 2).
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure CommandSetServoValueStructure
Public command As Byte
Public servoNumber As UInt16
Public newValue As UInt16
Public Function getBytes() As Byte()
Dim binaryBytes(5) As Byte
Dim pointerCommand As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Me))
Marshal.StructureToPtr(Me, pointerCommand, False)
Marshal.Copy(pointerCommand, binaryBytes, 0, Marshal.SizeOf(Me))
Marshal.FreeHGlobal(pointerCommand)
Return binaryBytes
End Function
End Structure
Each command will fill out the values of this structure. Sending it over the network is then a simple:
Dim thisCommand As CommandSetServoValueStructure
' Fill in thisCommand as appropriate.
Dim binaryBytes() As Byte = thisCommand.getBytes
connectedSerialPort.Write(binaryBytes, 0, 5)
Write takes in the bytes to send, the starting index, and the number of bytes to send - which in our cause is the size of our structure.
Each track bar sets the servo's position by percentage (from 0 to 100).
Try
Dim commandSetServoPercent As CommandSetServoValueStructure
commandSetServoPercent.command = 1
commandSetServoPercent.servoNumber = servoNumber
commandSetServoPercent.newValue = value_percent
Dim binaryBytes() As Byte = commandSetServoPercent.getBytes
connectedSerialPort.Write(binaryBytes, 0, 5)
Catch ex As Exception
End Try
End Sub
Private Sub trackBar11_ValueChanged(sender As Object, e As EventArgs) Handles trackBar11.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(0, trackBar11.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar21_ValueChanged(sender As Object, e As EventArgs) Handles trackBar21.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(1, trackBar21.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar31_ValueChanged(sender As Object, e As EventArgs) Handles trackBar31.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(2, trackBar31.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar12_ValueChanged(sender As Object, e As EventArgs) Handles trackBar12.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(3, trackBar12.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar22_ValueChanged(sender As Object, e As EventArgs) Handles trackBar22.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(4, trackBar22.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar32_ValueChanged(sender As Object, e As EventArgs) Handles trackBar32.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(5, trackBar32.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar13_ValueChanged(sender As Object, e As EventArgs) Handles trackBar13.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(6, trackBar13.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar23_ValueChanged(sender As Object, e As EventArgs) Handles trackBar23.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(7, trackBar23.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub trackBar33_ValueChanged(sender As Object, e As EventArgs) Handles trackBar33.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoPercentToSerial(8, trackBar33.Value)
End If
Catch ex As Exception
End Try
End Sub
Setting Servo Range
Command 2: Setting the Servo Upper Limit
The top numeric up/down sets the upper range for the servo.
Private Sub sendCommandSetServoUpperLimitToSerial(ByVal servoNumber As Integer, ByVal newUpper_pulseLength As Integer)
Try
Dim commandSetServoUpperLimit_pulseLength As CommandSetServoValueStructure
commandSetServoUpperLimit_pulseLength.command = 2
commandSetServoUpperLimit_pulseLength.servoNumber = servoNumber
commandSetServoUpperLimit_pulseLength.newValue = newUpper_pulseLength
Dim binaryBytes() As Byte = commandSetServoUpperLimit_pulseLength.getBytes
connectedSerialPort.Write(binaryBytes, 0, 5)
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit11_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit11.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(0, nudServoUpperLimit11.Value)
sendCommandSetServoPercentToSerial(0, trackBar11.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit21_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit21.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(1, nudServoUpperLimit21.Value)
sendCommandSetServoPercentToSerial(1, trackBar21.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit31_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit31.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(2, nudServoUpperLimit31.Value)
sendCommandSetServoPercentToSerial(2, trackBar31.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit12_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit12.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(3, nudServoUpperLimit12.Value)
sendCommandSetServoPercentToSerial(3, trackBar12.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit22_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit22.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(4, nudServoUpperLimit22.Value)
sendCommandSetServoPercentToSerial(4, trackBar22.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit32_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit32.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(5, nudServoUpperLimit32.Value)
sendCommandSetServoPercentToSerial(5, trackBar32.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit13_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit13.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(6, nudServoUpperLimit13.Value)
sendCommandSetServoPercentToSerial(6, trackBar13.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit23_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit23.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(7, nudServoUpperLimit23.Value)
sendCommandSetServoPercentToSerial(7, trackBar23.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoUpperLimit33_ValueChanged(sender As Object, e As EventArgs) Handles nudServoUpperLimit33.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoUpperLimitToSerial(8, nudServoUpperLimit33.Value)
sendCommandSetServoPercentToSerial(8, trackBar33.Value)
End If
Catch ex As Exception
End Try
End Sub
Command 3: Setting the Servo Lower Limit
The bottom numeric up/down sets the lower range for the servo.
Private Sub sendCommandSetServoLowerLimitToSerial(ByVal servoNumber As Integer, ByVal newLower_pulseLength As Integer)
Try
Dim commandSetServoLowerLimit_pulseLength As CommandSetServoValueStructure
commandSetServoLowerLimit_pulseLength.command = 3
commandSetServoLowerLimit_pulseLength.servoNumber = servoNumber
commandSetServoLowerLimit_pulseLength.newValue = newLower_pulseLength
Dim binaryBytes() As Byte = commandSetServoLowerLimit_pulseLength.getBytes
connectedSerialPort.Write(binaryBytes, 0, 5)
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit11_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit11.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(0, nudServoLowerLimit11.Value)
sendCommandSetServoPercentToSerial(0, trackBar11.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit21_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit21.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(1, nudServoLowerLimit21.Value)
sendCommandSetServoPercentToSerial(1, trackBar21.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit31_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit31.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(2, nudServoLowerLimit31.Value)
sendCommandSetServoPercentToSerial(2, trackBar31.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit12_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit12.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(3, nudServoLowerLimit12.Value)
sendCommandSetServoPercentToSerial(3, trackBar12.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit22_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit22.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(4, nudServoLowerLimit22.Value)
sendCommandSetServoPercentToSerial(4, trackBar22.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit32_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit32.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(5, nudServoLowerLimit32.Value)
sendCommandSetServoPercentToSerial(5, trackBar32.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit13_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit13.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(6, nudServoLowerLimit13.Value)
sendCommandSetServoPercentToSerial(6, trackBar13.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit23_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit23.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(7, nudServoLowerLimit23.Value)
sendCommandSetServoPercentToSerial(7, trackBar23.Value)
End If
Catch ex As Exception
End Try
End Sub
Private Sub nudServoLowerLimit33_ValueChanged(sender As Object, e As EventArgs) Handles nudServoLowerLimit33.ValueChanged
Try
If Not isPopulatingSettings Then
sendCommandSetServoLowerLimitToSerial(8, nudServoLowerLimit33.Value)
sendCommandSetServoPercentToSerial(8, trackBar33.Value)
End If
Catch ex As Exception
End Try
End Sub
Set All
The set all buttons are just shortcuts for changing every track bar to 0%, 50%, or 100%.
Private Sub setAll(ByVal newValue_percent As Integer)
Try
trackBar11.Value = newValue_percent
trackBar21.Value = newValue_percent
trackBar31.Value = newValue_percent
trackBar12.Value = newValue_percent
trackBar22.Value = newValue_percent
trackBar32.Value = newValue_percent
trackBar13.Value = newValue_percent
trackBar23.Value = newValue_percent
trackBar33.Value = newValue_percent
Catch ex As Exception
End Try
End Sub
Private Sub btnAllDown_Click(sender As Object, e As EventArgs) Handles btnAllDown.Click
Try
setAll(0)
Catch ex As Exception
End Try
End Sub
Private Sub btnAllMiddle_Click(sender As Object, e As EventArgs) Handles btnAllMiddle.Click
Try
setAll(50)
Catch ex As Exception
End Try
End Sub
Private Sub btnAllUp_Click(sender As Object, e As EventArgs) Handles btnAllUp.Click
Try
setAll(100)
Catch ex As Exception
End Try
End Sub
Additional Support Code
Imports System
Imports System.IO.Ports
Imports System.Runtime.InteropServices
Public Class FormMain
Private _isInitializingForm As Boolean = True
Private _isLoadingConfiguration As Boolean = False
Public ReadOnly Property isPopulatingSettings As Boolean
Get
If _isInitializingForm Then
Return True
End If
If _isLoadingConfiguration Then
Return True
End If
Return False
End Get
End Property
''' <summary>
''' Definite a delegate to transfer incoming data from the background
''' (serial) thread to the foreground (UI) thread.
''' </summary>
''' <param name="incomingBuffer"></param>
''' <remarks></remarks>
Private Delegate Sub ProcessIncomingTextDelegate(ByVal incomingBuffer As String)
''' <summary>
''' The serial port object that will communicate.
''' </summary>
''' <remarks></remarks>
Private WithEvents connectedSerialPort As New SerialPort()
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_isInitializingForm = False
End Sub
Private Sub loadConfigurationValues()
Try
_isLoadingConfiguration = True
Try
nudServoUpperLimit11.Value = 550
nudServoUpperLimit12.Value = 550
nudServoUpperLimit13.Value = 550
nudServoUpperLimit21.Value = 550
nudServoUpperLimit22.Value = 550
nudServoUpperLimit23.Value = 550
nudServoUpperLimit31.Value = 550
nudServoUpperLimit32.Value = 550
nudServoUpperLimit33.Value = 550
nudServoLowerLimit11.Value = 200
nudServoLowerLimit12.Value = 200
nudServoLowerLimit13.Value = 200
nudServoLowerLimit21.Value = 200
nudServoLowerLimit22.Value = 200
nudServoLowerLimit23.Value = 200
nudServoLowerLimit31.Value = 200
nudServoLowerLimit32.Value = 200
nudServoLowerLimit33.Value = 200
Catch ex As Exception
End Try
_isLoadingConfiguration = False
Catch ex As Exception
End Try
End Sub
Private Sub FormMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
loadConfigurationValues()
refreshPortsList()
If ddlCOMPort.SelectedIndex >= 0 Then
' Port is found, attempt to connect to it.
connectSerialPort()
Else
' Update which controls are enabled (so disconnect button is disabled at startup).
updateConnectedEnableFields()
End If
Catch ex As Exception
End Try
End Sub
Private Sub btnConnect_Click(sender As Object, e As EventArgs) Handles btnConnect.Click
Try
connectSerialPort()
Catch ex As Exception
End Try
End Sub
Private Sub btnDisconnect_Click(sender As Object, e As EventArgs) Handles btnDisconnect.Click
Try
disconnectSerialPort()
Catch ex As Exception
End Try
End Sub
Private Sub connectedSerialPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles connectedSerialPort.DataReceived
Try
' Received data from the serial port, send it for processing.
processIncomingData(connectedSerialPort.ReadExisting())
Catch ex As Exception
End Try
End Sub
Private Sub connectedSerialPort_ErrorReceived(sender As Object, e As SerialErrorReceivedEventArgs) Handles connectedSerialPort.ErrorReceived
Try
Catch ex As Exception
End Try
End Sub
Private Sub btnRefreshPorts_Click(sender As Object, e As EventArgs) Handles btnRefreshPorts.Click
Try
refreshPortsList()
Catch ex As Exception
End Try
End Sub
''' <summary>
''' Returns if the serial port is currently connected.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Private ReadOnly Property isSerialPortConnected
Get
Return connectedSerialPort.IsOpen()
End Get
End Property
''' <summary>
''' Update whether or not each UI control should be enabled based on
''' the connection state.
''' </summary>
''' <remarks></remarks>
Private Sub updateConnectedEnableFields()
Try
Dim isConnected As Boolean = isSerialPortConnected
btnConnect.Enabled = Not isConnected
btnDisconnect.Enabled = isConnected
ddlCOMPort.Enabled = Not isConnected
btnRefreshPorts.Enabled = Not isConnected
Catch ex As Exception
End Try
End Sub
''' <summary>
''' Connect to the selected serial port.
''' </summary>
''' <remarks></remarks>
Private Sub connectSerialPort()
Try
If ddlCOMPort.SelectedIndex >= 0 Then
connectedSerialPort.PortName = ddlCOMPort.SelectedItem
connectedSerialPort.BaudRate = 9600
connectedSerialPort.WriteTimeout = -1
connectedSerialPort.ReadTimeout = -1
connectedSerialPort.ReadBufferSize = 4096
connectedSerialPort.WriteBufferSize = 2048
connectedSerialPort.Parity = IO.Ports.Parity.None
connectedSerialPort.StopBits = IO.Ports.StopBits.One
connectedSerialPort.DataBits = 8
connectedSerialPort.Open()
'Threading.Thread.Sleep(1000)
' Send Extended ASCII Code 128 for binary mode.
Dim binaryBytes() As Byte = {128}
connectedSerialPort.Write(binaryBytes, 0, 1)
End If
updateConnectedEnableFields()
Catch ex As Exception
End Try
End Sub
''' <summary>
''' Disconnect the serial port.
''' </summary>
''' <remarks></remarks>
Private Sub disconnectSerialPort()
Try
connectedSerialPort.Close()
updateConnectedEnableFields()
Catch ex As Exception
End Try
End Sub
''' <summary>
''' Detect available serial ports and update the COM port list.
''' </summary>
''' <remarks></remarks>
Private Sub refreshPortsList()
Try
' Keep track of the selected port so we can reselect it after fresh.
Dim currentlySelectedPort As String = ddlCOMPort.SelectedItem
' Get the available ports and update the dropdown list.
Dim availablePorts As Array = IO.Ports.SerialPort.GetPortNames()
ddlCOMPort.Items.Clear()
ddlCOMPort.Items.AddRange(availablePorts)
' Reselect the previously selected port as a default.
If currentlySelectedPort IsNot Nothing Then
If ddlCOMPort.Items.Contains(currentlySelectedPort) Then
ddlCOMPort.SelectedItem = currentlySelectedPort
End If
End If
If ddlCOMPort.SelectedIndex = -1 AndAlso ddlCOMPort.Items.Count > 0 Then
' No port selected but one is available, default to it.
ddlCOMPort.SelectedIndex = 0
End If
Catch ex As Exception
End Try
End Sub
''' <summary>
''' Function to process incoming data from the serial port.
''' </summary>
''' <param name="incomingBuffer"></param>
''' <remarks></remarks>
Private Sub processIncomingData(ByVal incomingBuffer As String)
Try
If txtReceive.InvokeRequired Then
' On background (serial port) thread. Invoke a delegate to call this
' function again on the foreground (UI) thread.
Dim incomingTextDelegate As New ProcessIncomingTextDelegate(AddressOf processIncomingData)
Me.Invoke(incomingTextDelegate, New Object() {incomingBuffer})
Else
' On foreground (UI) thread. Process the message.
txtReceive.Text += incomingBuffer
End If
Catch ex As Exception
End Try
End Sub
' ---------- CODE FROM ABOVE ----------
End Class
In Operation
At startup...
The Board Manager automatically connects to the first COM port it detects and sends the binary input command.
Setting some of the servo values...
And the servos in action...
Copyright (c) 2015 Clinton Kam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
No comments:
Post a Comment