dcsimg
Results 1 to 3 of 3

Thread: [VB6] Interfaces and Internal Plugin Architecture

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    19,065

    [VB6] Interfaces and Internal Plugin Architecture

    This is a demo showing how VB6 interfaces can be implemented and consumed to offer an "internal plugin" approach to programming operational phases within the lifetime of a VB6 program.

    The basic idea here is to share a Form's MSComm control among several separate classes, each class implementing some phase of program operation that uses the MSComm control's connection to an external device.

    In order to be robust these communicating "phase" classes need to implement timeouts, so they are written as InvisibleAtRuntime UserControl modules which can easily host their own Timer controls. Otherwise they could be Class modules instead.

    Name:  Interfaces.png
Views: 173
Size:  19.8 KB

    The program's "operational phases" are:

    1. Reset the external device.
    2. Upload the script read from a file to the external device.
    3. Run an activity that interacts with the running script on the external device.
    4. On form unload, first reset the external device once more to leave it in a known state without a running script.



    Name:  sshot.png
Views: 163
Size:  7.4 KB


    The form's user interface has several sections:

    At the bottom we have a logging area where activity is reported. This could be omitted entirely, logged to disk, occur on a secondary modeless form, etc.

    In the center we have a display only "terminal" area displaying the serial port interaction with the external device. In most phases it works in text mode, but since the script interaction phase of the program uses binary datagrams it shifts into displaying hex with direction indicators at the right. Like the logging area this could be omitted or moved elsewhere. It is just another program feature used to help monitor and understand the process.

    At the top is the part of the form where you interact with the script running on the remote device. This is a trivial application where you can provide a 16-bit value and request it be incremented and returned, or you can request a reading from the 10-bit analog/digital converter on the remote device.


    Requirements

    1. Windows PC that can run VB6 programs and support the MSComm control, with a USB port and the driver for the USB-UART Bridge chip on your ModeMCU DevKit board.
    2. VB6 Pro or Enterprise.
    3. A NodeMCU DevKit board flashed with the NodeMCU Lua firmware and a USB-microUSB cable.


    As written the program makes some assumptions defined at the top of Form1:

    Code:
    Private Const COMM_PORT As Integer = 3
    Private Const COMM_SETTINGS As String = "115200,n,8,1"
    Private Const LUA_PROGRAM_FILE As String = "sample.lua"
    This presumes that your target NodeMCU DevKit board is on COM3: and is a recent firmware build that defaults to 115,200 bps instead of the older 9,600 bps default.


    NodeMCU DevKit

    I won't go into the details. You can search the web for a ton of information.

    This target device was chosen because it is self-contained and needs very little to serve as an external platform for demonstrating this "internal plugin" approach. Just a $6 NodeMCU DevKit board and a $2 USB cable you may already have for phone charging and you are set.

    If your DevKit has header pins soldered in you may want to press it into a prototyping breadboard to weight it down to your desk and protect the pins. But nothing needs to be connected aside from the USB cable.

    These contain an ESP8266 with its RAM and Flash and WiFi Access Point/Station radio and antenna and GPIO pins as well as a USB-UART Bridge chip, a couple of pushbuttons, LEDs, and 3.3v regulator.


    Normally you either flash these with custom compiled firmware or else store an "ini.lua" script in the flash filesystem to be executed upon device reset.

    Instead we will reset the device, have it not find any initialization script, and then "upload" our script by programmatically "typing it in" to the Lua command prompt.

    The last thing we "type" registers a callback into our script on receipt of data via the UART. Here is the file we "type into" Lua:

    sample.lua

    Code:
    -- Turn off the WiFi radio we are not using, but don't save the
    -- setting to flash:
    wifi.nullmodesleep(true)
    wifi.setmode(wifi.NULLMODE, false)
     
    -- Define serial input callback function:
    function handleInput(data)
      local reqType = string.byte(data, 1)
      local outValue = 0
      local status = 0
      if reqType == 0x01 then
        -- Add 1 to unsigned 16-bit big-endian input and return it:
        outValue = (string.byte(data, 2) * 256 + string.byte(data, 3) + 1) % 65536
        status = 0x01
      elseif reqType == 0x02 then
        -- Ignore the next 2 bytes of input, read and return ADC value
        -- from the NodeMCU DevKit's A0 pin:
        outValue = adc.read(0)
        status = 0x01
      else
        status = 0xff -- Respond with "bad request."
      end
      -- Note: math.floor() call not needed with an integer build of the NodeMCU
      -- firmware.
      uart.write(0, status, reqType, math.floor(outValue / 256), outValue % 256)
    end
     
    -- Register serial input callback.  When 3 characters are received
    -- execute it:
    uart.on("data", 3, handleInput, 0)
    (continued)
    Last edited by dilettante; Apr 24th, 2018 at 09:25 PM.

  2. #2

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    19,065

    Re: [VB6] Interfaces and Internal Plugin Architecture

    (continuing)

    RunningApp UserControl code

    Code:
    Option Explicit
    '==========
    'RunningApp
    '==========
    
    Implements ICommPlugin
    
    Private CommHost As ICommHost
    
    Private Request() As Byte
    Private Response As ByteString 'Binary StringBuilder object.
    
    Public Event Op1Response(ByVal Op1NewValue As Long)
    Public Event Op2Response(ByVal Op2Result As Long)
    Public Event ResponseError()
    
    Public Property Get Object() As Object
        Set Object = Me
    End Property
    
    Public Sub RequestOp1(ByVal Op1Value As Long)
        Request(0) = &H1
        Request(1) = Op1Value \ 256
        Request(2) = Op1Value Mod 256
        'Start interval timer:
        Timer1.Enabled = True
        With CommHost
            .SendData Request
            .EndOfDatagram SentDatagram:=True
        End With
    End Sub
    
    Public Sub RequestOp2()
        Request(0) = &H2
        Request(1) = &H0
        Request(2) = &H0
        'Start interval timer:
        Timer1.Enabled = True
        With CommHost
            .SendData Request
            .EndOfDatagram SentDatagram:=True
        End With
    End Sub
    
    Private Sub ICommPlugin_CommEvent(ByVal CommEvent As MSCommLib.OnCommConstants)
        'We ignore these here.
    End Sub
    
    Private Sub ICommPlugin_Connect(ByVal ICommHost As ICommHost, Optional ByVal Task As Variant)
        Set CommHost = ICommHost
        CommHost.Message "Starting interaction with the script"
        CommHost.Running State:=True
    End Sub
    
    Private Sub ICommPlugin_Disconnect()
        CommHost.Running State:=False
        Set CommHost = Nothing
    End Sub
    
    Private Sub ICommPlugin_ErrorEvent(ByVal CommEvent As MSCommLib.CommEventConstants)
        Timer1.Enabled = False
        With CommHost
            .Running State:=False
            .Message "Communication error " _
                   & CStr(CommEvent) _
                   & " while interacting with the script"
            .Finished Success:=False
        End With
    End Sub
    
    Private Sub ICommPlugin_ReceivedData(ByRef Data() As Byte)
        'Process received data.
        Dim I As Long
        Dim ResponseBytes() As Byte
        
        Timer1.Enabled = False
        With Response
            .Cat Data
            If .Length >= 4 Then
                CommHost.EndOfDatagram SentDatagram:=False
                ResponseBytes = .Left(4)
                .Value = .Mid(4) 'Extract the 4 processed bytes, though since we are using
                                 'a chatty protocol there shouldn't be anything else left.
                If ResponseBytes(0) = &H1 Then
                    Select Case ResponseBytes(1)
                        Case &H1
                            RaiseEvent Op1Response(ResponseBytes(2) * 256& + ResponseBytes(3))
                        Case &H2
                            RaiseEvent Op2Response(ResponseBytes(2) * 256& + ResponseBytes(3))
                        Case Else
                            RaiseEvent ResponseError
                    End Select
                Else
                    RaiseEvent ResponseError
                End If
            Else
                'Restart interval timer:
                Timer1.Enabled = True
            End If
        End With
    End Sub
    
    Private Sub Timer1_Timer()
        Timer1.Enabled = False
        With CommHost
            .Running State:=False
            .Message "Timed out while interacting with the script"
            .Finished Success:=False
        End With
    End Sub
    
    Private Sub UserControl_Initialize()
        Set Response = New ByteString
        With Response
            .Chunk = 4
            .Clear
        End With
        ReDim Request(0 To 2)
        Timer1.Interval = 64
    End Sub
    
    Private Sub UserControl_Resize()
        Size 480, 480
    End Sub
    
    Private Sub UserControl_Terminate()
        Set CommHost = Nothing
    End Sub
    This code interacts with Form1 to accept requests, format and send them on to the NodeMCU Lua script, and get results back and unformat those to be forwarded to and displayed on Form1.


    Summary

    You could rip out the interfaces and the UserControls and code everything inline within Form1. The notion here is that if your activities in each "operational phase" are complex you could end up with a really complicated and difficult to maintain giant blob of conditional logic to handle the serial communication. Add timeout handling and things just get uglier.

    Instead Form1 is reduced to its application activity and managing the MSComm control at a simple level while plugging in objects using secondary interfaces to move from phase to phase. Instead of phased operation it might even route traffic to an array of secondary interface objects more dynamically.

    You can even do the same sort of thing using a Winsock control and a TCP connection instead of the MSComm control and serial connection.

    The NodeMCU board is a common, cheap, and easy to use item for this demo. It has far more capability than we made use of here but that's more of an IoT topic than a VB6 programming topic. Just adding a photodiode or light-dependent resistor to pin A0 of the DevKit would make this demo more interesting with no software changes.
    Attached Files Attached Files
    Last edited by dilettante; Apr 24th, 2018 at 08:12 PM.

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    19,065

    Re: [VB6] Interfaces and Internal Plugin Architecture

    While this is a weird way to use an ESP8266 NodeMCU DevKit board, it does show one possibility.

    Using the either the USB-UART serial connection (or remotely using TCP or HTTP over WiFi) you can use the DevKit to add GPIO pin capabilities to a Windows VB6 program. You could also do the same with a simpler/cheaper ESP8266 module from the ESP-01 on up. These can cost as little as $1 a piece on eBay.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width