DIGITAL COMMAND CONTROL ON THE

TETON SHORT LINE (EasyDCC)

Wayne Roderick, 3rd Division, PNR, NMRA (life)

01/23/03 rev 04/10/08

Our do-it-yourself (DIY) DCC served the Teton Short Line well for several years until our HO modular club converted to DCC. After months of study and controversy, they selected the EasyDCC system by CVP Products It was installed about a year ago and we love it. EasyDCC has a great track record starting with it's description in several MR articles in 1997. As an electronic engineer, I can appreciate the quality of design and construction. The more we used it, the more I realized that the Teton Short Line operating experience would be enhanced if we had the four digit addressing and our crew could enjoy the familiarity of EasyDCC. Visitors could use their own power without reprogramming too. I studied the well documented EasyDCC system and found that it would be a relatively easy task to interface it to the TSL computer so that we could retain our very desirable walk-around cabs. EasyDCC has an RS232 port that will interface with your computer at 9600 baud. Twenty pages of the 180 page online manual explain the protocals. A letter about being a good boy to the North Pole resulted in Santa delivering the EZ Command Station on 12/25/02. In a very short time it was interfaced to our boosters and controlling trains. It took a few more, actually quite a few, more hours to get our cabs to control it. QBASIC has been the language of choice for the TSL embedded computer for most of its years and has evolved to more than 4000 lines of code controlling everything from cabs to dispatcher graphics. it would be a monstrous effort to change it, so we won't. The EasyDCC manual has examples using generic BASIC that might be useful for an initial acquaintance. To control trains via the EasyDCC RS232 port requires you to enter the SPEED/DIRECTION commands originating in my cab system, into the DCC queue. Consists can be built and modified via this port but controlling consists has to have a little further understanding. Paul Bender posted an explanation to the EasyDCC Yahoo group on 04/10/08 that makes it quite clear.

You have to understand there are three different types of
consisting supported.

First, you can simply assign all the locomotives in the consist the same
address (This is known as basic or primary address consisting).

Second, you can consist the locomotives in the command station. I refer
to this as Command Station Assisted Consisting(CSAC).  On an EasyDCC
system, this is a "Standard Consist".  For a CSAC to work, packets are
always sent out to each locomotive in the consist.

Third, you can consist the locomotives in the decoder, using CV19 - the
consist address.  I refer to this as Decoder Assisted Consisting (DAC),
some people call this Advanced Consisting, which is the term Digitrax
uses to describe this feature.

(For more info, refer to the background information section here:
http://jmri.sourceforge.net/help/en/html/tools/consisttool/ConsistTool.shtml )

On an EasyDCC system, you can build both DACs and Standard Consists
using the serial port.  

Because of the way in which throttle commands are sent to the EasyDCC
system through the serial port (which requires sending the NMRA DCC
packets ourself), the command station does not decode or modify the
packets we send.  The result is that we CAN control a Basic Consist or
DAC by sending commands to the consist address, but we have to do more
work to command a "Standard Consist".

With a "Standard Consist", If we just sent commands to the consist
address, the only locomotive(s) that will move would have an address
corresponding to the consist address.    

To control a "Standard Consist" through the serial port, we need to
request the details of what locomotives are contained in the consist
from the command station and send out the required commands to each
locomotive within that consist, instead of sending one command to the
consist address.

The protocol used on the cab bus is different than the one used with the
serial port.  This allows the command station to automatically send out
the required commands for a "Standard Consist" when a cab selects and
controls a consist.
=====================================================================

I much appreciated Pauls clear explanation of the consisting situations. Please note that I DO NOT load the consist info into the command station and so it will not show if you ask the command station to display consist information, however as Paul describes you might want to do just that.

It is important to understand that the commands we add to the queue via the serial port do not interfere with the normal use of wired or radio cabs that are part of the EasyDCC system- Obviously, you can't control the SAME loco from more than one cab whether it be radio, wired, RS232 or the two on the command stataion.

Here is some of QBASIC code that we use

=====================================================================

SUB EZconsists STATIC              'new 01/08/03
   'This SUB, EZconsists interfaces the computer consists with EZdcc
   'It is one way only i.e. consists created on EZ do not come here.
   'A consist change results in KILL CONSIST command to EZ and then
   'the consist is rebuilt.  The consist number is the Lead Loco.
  
   DIM OldConsist(5)
   DIM OldChkSum(5)                'look for changes and output them
   Trace = 0                 'local for program developement
  ' OverKill = 0                'EZ usually doesn't accept first kill
AlertEZ:          'the first Kill to EZ is rejected with ?
   k$ = "GKFF"
   CALL EZdccSend(k$)
   FOR CabNo = 0 TO 4
      ChkSum(CabNo) = 0
      FOR Loco = 1 TO 4
	 ChkSum(CabNo) = ChkSum(CabNo) XOR Consist(CabNo, Loco)
      NEXT Loco
	 IF Trace THEN
	    PRINT "Cab"; CabNo; "OldChkSum"; OldChkSum(CabNo);
	    PRINT "ChkSum"; ChkSum(CabNo)
	    FOR c = 1 TO 4: PRINT Consist(CabNo, c); : NEXT: PRINT
	 END IF
	 IF ChkSum(CabNo) <> OldChkSum(CabNo) THEN
	      'open the port and send the cab consist

KillConsists:                ' Use the lead loco two digit number
	 OverKill = OverKill + 1
	 k$ = HEX$(OldConsist(CabNo)) 'old consist in case it changed
	 k$ = "GK" + RIGHT$(("00" + k$), 2)
	 IF Trace THEN PRINT "Kill"; k$;
	 CALL EZdccSend(k$)
	
	 k$ = HEX$(ABS(Consist(CabNo, 1)))  'current consist
	 k$ = "GK" + RIGHT$(("00" + k$), 2)
	 IF Trace THEN PRINT "kill"; k$;
	 CALL EZdccSend(k$)
	
NewConsist:
	 'The lead loco is also the consist number and cannot be reverse.
	 IF (Consist(CabNo, 1)) = 0 THEN GOTO NullLoco
	 OldConsist(CabNo) = Consist(CabNo, 1)   'the "new" old consist
	 yy$ = HEX$(ABS(Consist(CabNo, 1)))    'this is consist number
	 yy$ = RIGHT$(("00" + yy$), 2)
	 FOR Loco = 2 TO 4                     'add the rest of them
	    j$ = "GN"
	    'IF (consist(CabNo, Loco)) = 0 THEN GOTO NullLoco
	    IF (Consist(CabNo, Loco)) < 0="" then="" j$="GR" k$="HEX$(ABS(Consist(CabNo," loco)))="" k$="j$" +="" yy$="" +="" right$(("0000"="" +="" k$),="" 4)="" if="" trace="" then="" print="" k$="" +="" "="" ";="" call="" ezdccsend(k$)="" nullloco:="" next="" loco="" 'save="" a="" new="" checksum="" chksum="Consist(CabNo," 1)="" xor="" consist(cabno,="" 2)="" chksum="ChkSum" xor="" consist(cabno,="" 3)="" xor="" consist(cabno,="" 4)="" oldchksum(cabno)="ChkSum" end="" if="" 'if="" trace="" then="" call="" waitkey="" next="" cabno="" if="" trace="" then="" lprint="" "ezconsists="" done"="" ':="" call="" waitkey="" end="" sub="" sub="" ezdccsend="" (k$)="" 'new="" 01/05/03="" 'receives="" a="" string="" from="" sub="" ezconsists="" or="" sub="" ezwriteq="" and="" sends="" it.="" locate="" 30,="" 1:="" print="" space$(10);="" call="" ezopenport="" clearinputbuffer:="" a$="" locate="" 30,="" 1:="" print="" k$;="" transmit="" k$="" +="" chr$(13)="" startime!="TIMER" 'look="" for="" a="" dead="" port="" sendvalid:="" timenow!="TIMER" if="" (timenow!="" -="" startime!)=""> .25 THEN BadPort = -1: GOTO BadEZport
   IF DataWaiting THEN
       a$ = CHR$(ReadChar)
       IF a$ <> CHR$(13) THEN LOCATE 30, 9: PRINT a$;
       IF a$ = CHR$(13) THEN GOTO EZdccSendEnd
   END IF
   c$ = INKEY$
   IF c$ = CHR$(3) THEN END
   GOTO SendValid

BadEZport:
	Delay$ = LEFT$(STR$(TimeNow! - Startime!), 5)
	Text$ = "EZdcc PORT IS BAD-" + Delay$
	CALL PrntStr(Text$, 12, 10, LtRed + 16)    'make it flash
	'PRINT CHR$(7);   'ring the bell

EZdccSendEnd:
    CloseComm

END SUB

SUB EZopenPort               'new 01/05/03
   'initialize the comm port with added library
   'OpenComm port,IRQ,WordLen,Parity,StopBits,BaudRate,Handshake,0

   Port = 1: Length = 8: Parity = 0: Bits = 1: Rate& = 9600: HS = 1
   
   OpenComm Port, 0, Length, Parity, Bits, Rate&, HS, 0
   CarrierDetect 0      'don't monitor- disables UEVENT trip
END SUB

SUB EZwriteQ STATIC
'01/20/03
'This SUB replaces the DCCarray used with our old DIY DCC system. It is
'no longer necessary to maintain the array, but simply send changes to
'EZdcc. The SUB Cab2DCC assigns new speeds to the locos of each consist.
'In accordance with EZdcc specs, to put a command in the Queue, we only
'send the letter "Q" plus three bytes for address, speed/drctn and the
'checksum. Send the letter "D" plus the address to remove from queue so
'that EZ cabs can use the address without conflict.
'Initially, only short addresses will be used in keeping with
'the graphics and cab indications. This is called only from the Exec loop.

DIM OldLocoSpd(21)
Trace = 0      'displays the array for program developement

   'This is the Packet structure without preambles.
   '----------------------------------------------------------------------
   'pattern for baseline 8 bit address with 28 speed steps.
   '                  0AAAAAAA-01DsSSSS-EEEEEEEE
   '                    byte1    byte2    byte3     byte4   byte5
   ' lower case [s] is the LSB of the 5-bit speed-MSB follows
   ' speed  12 (01D1 0010) = 2nd speed step
   ' speed  02 (01D0 0010) = 1st speed step
   ' speed  01 or 10    = emergency stop- we don't use it
   ' speed  00 (00D0 0000) = stop and then dequeue
'----------------------------------------------------------------------
       FOR Loco = 1 TO LocoMany
       'PRINT "EZwriteQ1"; Loco; LocoSpd(Loco); OldLocoSpd(Loco)
       IF LocoSpd(Loco) <> OldLocoSpd(Loco) THEN   'format & send Qpacket to EZ
	    ShortAdrs = LocoDat(Loco, 1) AND 127    'last two digits of eng#
	    Byte1 = ShortAdrs                       '0AAAAAAA
	    Byte2 = 64 OR LocoSpd(Loco)             '01DsSSSS->0100000
	    Byte3 = Byte1 XOR Byte2
	     ' LPRINT "EZwriteQ2"; Loco
	    GOSUB AssemblePacket        'assemble and send to EZdcc
	IF Trace THEN GOSUB TestPrintPacket
	    OldLocoSpd(Loco) = LocoSpd(Loco)
	END IF
    NEXT Loco
    CALL ScreenStat(3)          'update the screen
    EXIT SUB
		       
AssemblePacket:   'assemble and send the string in HEX format
    Byte1$ = RIGHT$(("00" + HEX$(Byte1)), 2)    'short address
    Byte2$ = RIGHT$(("00" + HEX$(Byte2)), 2)    'speed & drctn
    Byte3$ = RIGHT$(("00" + HEX$(Byte3)), 2)    'checksum
    Qpacket$(Loco) = "Q" + Byte1$ + Byte2$ + Byte3$  'put in queue
    k$ = Qpacket$(Loco)
    CALL EZdccSend(k$)
   
    IF (Byte2$ = "40" OR Byte2$ = "60") THEN    'dequeue @ zero speed
	k$ = "D00" + Byte1$                     'remove from queue
	CALL EZdccSend(k$)
    END IF
    RETURN

TestPrintPacket:
    PRINT "Loco"; Loco; Spd(Loco); OldLocoSpd(Loco)
    PRINT "Packet"; "Loco"; Loco; Qpacket$(Loco);
    PRINT Int2Bin$(Byte3);
    PRINT Int2Bin$(Byte4);
    PRINT Int2Bin$(Byte5)
    'Waitkey           'pause
RETURN

END SUB

Teton Short Line Home Page