Serial Control

 

Although both the NCE interface and the Mirrorbow IO board are USB they actually work as virtual serial ports. This has advantages and disadvantages! It makes the programming a lot simpler though.

Although I could do so (and have done in the past) I didn't really fancy writing any low-level control routines and there is nothing as standard within VBA. A hunt on the 'net found what looked to be a suitable set of routines which made calls directly to the Windows API (Application Program Interface). At first these seemed ideal, especially as they were free! Unfortunately I found that once you'd opened the virtual serial port you had to keep reading from or writing to it or it would lock up. Although there are ways round this it wasn't ideal.

Further searches came up with various commercial solutions (costing $150 upwards), the possibility of buying the full version of Visual Basic to get the Microsoft Comm Control (MSCOMM) or using the free alternative NETComm. I'm all for anything that's free so NETComm it was!

After some trial and error this has solved the control problems and I can now send commands to the DCC controller and read the inputs from the IO board without problems. The last problem to be resolved was I found the NCE USB board doesn't like two commands in very quick succession so it needs a short delay inserting.

Using NETComm

Once NETComm is installed the first step is to add the relevant ActiveX control to an Access form. I've created a separate form for this, and actually added two controls so far - one for the NCE port and one for the IO board. At this stage you need to know which serial port numbers your boards have been configured to, you can do this via the hardware manager on Control Panel - System. You can then set this on the Control properties. You don't need to worry too much about setting the other properties as they can (and will) be changed later but giving the controls sensible names might help.  Note that if you move the boards to a different USB port they may well renumber themselves. Obviously the form has to be open in Form View for the controls to work.

I've written a series of subroutines to simplify matters. Please note that these work for me, for what I'm doing, and with my set-up! I can't say whether they'll work for anyone else. How they work should be fairly self evident but I've added comments where this may help.

It's worth noting at this stage that the NCE uses hex codes whilst the IO board is all alphanumeric.

Utilities

Although not essential it's useful to be able to quickly create command strings. The following help with this. There's also a routine to pause the program execution for a specific number of seconds which I found on the 'net - useful in the testing phase.

Function H2B(H As String) As String

Rem Function to convert string of 2 digit Hex pairs to string of Bytes

S = ""
For I = 1 To Len(H) / 2
  NH = Mid(H, 2 * (I - 1) + 1, 1)
  If NH >= "A" Then
    T = 10 + Asc(NH) - Asc("A")
  Else
    T = Asc(NH) - Asc("0")
  End If
  NH = Mid(H, 2 * (I - 1) + 2, 1)
  If NH >= "A" Then
    T = 16 * T + 10 + Asc(NH) - Asc("A")
  Else
    T = 16 * T + Asc(NH) - Asc("0")
  End If
  S = S & Chr(T)
Next I

H2B = S

End Function
Function D2B(D As Long) As String

Rem Function to create String from Decimal
Rem High Byte First when done

D2B = Chr(D)

End Function
Function Pause(ByVal pSng_Secs As Single)

'Wait for the number of seconds given by pSng_Secs

Dim lSng_Start As Single
Dim lSng_End As Single
On Error GoTo Err_Pause

lSng_Start = Timer
lSng_End = Timer + pSng_Secs
Do While Timer < lSng_End
'' Correction if the timer moves over to a new day (midnight)
'' 86400-num of secs in a day
If Timer < lSng_Start Then lSng_End = lSng_End - 86400
Loop

Err_Pause:
Exit Function

End Function

Initialising the Ports

The following routines are used to initialise the controls and ports and then close them again.

For convenience I named my two controls NCE and INP. They're on a form called Comm. I've used the With statements just to reduce the amount of typing in the code.

Function NCEOpen()

Rem Function to initialise Com Port

With Forms!Comm.NCE

  .Settings = "9600,N,8,1"
  .RThreshold = 1
  .SThreshold = 1
  .InputMode = 0

  If .PortOpen = True Then
    .PortOpen = False
  End If
  If .PortOpen = False Then
    .PortOpen = True
  End If
  If Err Then MsgBox Error$, 48

End With

End Function

Function NCEClose()

With Forms!Comm.NCE

If .PortOpen = True Then
  .PortOpen = False
End If

If Err Then MsgBox Error$, 48

End With

End Function

Function INPInit()

Rem Function to initialise IO Board for Input

Dim S As String

With Forms!Comm.INP

  .Settings = "115200,N,8,1"
  .RThreshold = 1
  .SThreshold = 1
  .InputMode = 0

  If .PortOpen = True Then
    .PortOpen = False
  End If
  If .PortOpen = False Then 'check if the serial port is open
    .PortOpen = True 'check if the serial port is open
  End If
  If Err Then MsgBox Error$, 48 'Display error in message box

  S = "DIR1 FF"        'Sets Port 1 to all input
  .Output = S
  S = "DIR2 FF"
  .Output = S
  S = "DIR3 FF"
  .Output = S
  Pause 1              'Gives the board time to initialise completely
  S = .InputData

  If S <> "AAA" Then   'A is the correct response from the DIR command - I've read all 3 in one go
    MsgBox "Incorrect Response " & S, vbOKOnly, "Warning"
  End If

End With

End Function

Function INPClose()

With Forms!Comm.INP

If .PortOpen = True Then
  .PortOpen = False
End If

If Err Then MsgBox Error$, 48

End With

End Function

Point Control

On the control form Ctr each point is represented by two straight lines. One is coloured green to represent the active path and the other yellow to represent the inactive path.

Function SetP(Pt As Integer)

Rem Function to switch a point
Rem Pt = Point
Rem Uses the colour of the line on the control screen to determine current state of point

Dim S As String

L = "P" & Right("00" & Pt, 2) & "A"
If Forms("Ctr").Controls(L).BorderColor = 64000 Then
  St = False
Else
  St = True
End If

S = H2B("AD") & D2B(0) & Chr(Pt) & H2B(IIf(St, "03", "04")) & D2B(0)
Forms!Comm.NCE.Output = S
S = Forms!Comm.NCE.InputData
If S <> "!" Then
  Debug.Print "Invalid Control Response " & S, vbOKOnly, "Warning"
End If

Forms("Ctr").Controls(L).BorderColor = IIf(St, 64000, 64250)
L = "P" & Right("00" & Pt, 2) & "B"
Forms("Ctr").Controls(L).BorderColor = IIf(St, 64250, 64000)

End Function

Loco Control

There are two commands here. One thing I found is that for the momentum settings on the DCC decoder to work you have to set the speed to 0, not issue the stop command. You then need to issue a stop or you may get some creep and or the motor may continue to try and run.

Function MoveIt(Loc As Long, Dir As String, Spd As Long)

Rem Function to set the speed of a loco in Forward or Reverse

Dim S As String

S = H2B("A2") & D2B(0) & D2B(Loc) & IIf(Dir = "R", H2B("03"), H2B("04")) & D2B(Spd)
Forms!Comm.NCE.Output = S
S = Forms!Comm.NCE.InputData
If S <> "!" Then
  Debug.Print "Invalid Control Response " & S, vbOKOnly, "Warning"
End If

End Function

Function StopIt(Loc As Long, Dir As String)

Rem Function to send positive Stop command to loco

Dim S As String

S = H2B("A2") & D2B(0) & D2B(Loc) & IIf(Dir = "R", H2B("05"), H2B("06")) & D2B(0)
Forms!Comm.NCE.Output = S
S = Forms!Comm.NCE.InputData
If S <> "!" Then
  Debug.Print "Invalid Control Response " & S, vbOKOnly, "Warning"
End If

End Function

Lights Control

Simple routines to turn the lights on and off on the DMU set. Note that the direction of the lights is taken from the direction of travel. The only complication I had with this is how the functions are encoded to the USB controller - it's "LSB High", i.e. setting bit 4 is decimal 16 not decimal 8 as you would naturally expect. These routines will be made more sophisticated later as they do not remember the state set so when you turn the lights off it also turns off any other functions.

Function LightsOn(Loc As Long)

Dim S As String

S = H2B("A2") & D2B(0) & D2B(Loc) & H2B("07") & D2B(16)
Forms!Comm.NCE.Output = S
S = Forms!Comm.NCE.InputData
If S <> "!" Then
  Debug.Print "Invalid Control Response " & S, vbOKOnly, "Warning"
End If

End Function
Function LightsOff(Loc As Long)

Dim S As String

S = H2B("A2") & D2B(0) & D2B(Loc) & H2B("07") & D2B(0)
Forms!Comm.NCE.Output = S
S = Forms!Comm.NCE.InputData
If S <> "!" Then
  Debug.Print "Invalid Control Response " & S, vbOKOnly, "Warning"
End If

End Function

Test Routines

Obviously I wanted to test things! As I'd got my test layout set up in Setrack, with both a reed switch and block detector wired in, I developed some simple routines to drive a train. These were mostly based around the DMU set. This one is probably the most sophisticated. The decoder is set at short address 18. The set accelerates to a slow speed as it comes out of the bay platform, speeds up a bit as it goes over the points then accelerates to "full" speed. It slows down again to enter the passing loop before stopping. This is then reversed, however whilst all the other movements are timed the final stop is based on the input detection.

Function xx2()

For I = 1 To 100    'Loop for multiple runs

LightsOn 18
Pause 1
MoveIt 18, "F", 5    'Go forwards at speed 5
Pause 17
MoveIt 18, "F", 10
Pause 2
MoveIt 18, "F", 15
Pause 10
MoveIt 18, "F", 5
Pause 19
MoveIt 18, "F", 0    'Decelerate to a stop
Pause 1
StopIt 18, "F"       'Positive stop command
Pause 1
LightsOff 18
Pause 5
LightsOn 18
Pause 1
MoveIt 18, "R", 5
Pause 19
MoveIt 18, "R", 15
Pause 10
MoveIt 18, "R", 10
Pause 2
MoveIt 18, "R", 5

With Forms!Comm.INP
  S = .InputData
  S = ""
  While S <> "02"    'Loop until the input detector returns string 02 (detector is on bit 2 of port 1)
    S = "IN1"
    .Output = S
    S = .InputData
  Wend
End With
Pause 10
MoveIt 18, "R", 0
Pause 1
LightsOff 18
Pause 1
StopIt 18, "R"
Pause 5

Next I

End Function

Power to USB Ports

Just a note on this! There is a standard that says a USB port should provide 500mA of current, however many systems (especially laptops) don't comply with this. The IO board in particular could, if all the inputs are used at the same time, need most of this 500mA. I've got my boards plugged in to a powered Belkin hub to ensure this is not a problem.

 

Home | Up | Next