Application Programming Interface
and Driver
for Digital Modelrailroads

Version 3.3


Guide to Application Programming with DigiAPI
Rob Hamerling
Vianen, The Netherlands

E-mail: r.hamerling@hccnet.nl
homepage: http://www.robh.nl/

Copyright © R. Hamerling, 1991, 2007
All Rights Reserved.



Table of Contents

Appendix

Related publications


Functional description

This chapter describes a possible use of DigiAPI.

Overview

The DigiAPI package is a set of functions to control a 'digital' modelrailroad by programs written in C-language.

Terminology

In this document a number of terms are used which should be clear to the reader, independent of the system or protocol being used.

Interface
Märklin: 6050 or 6051
Lenz: Interface LI100
Trix: Interface 60824 or 60842
P50X: Intellibox integrated Interface
Loconet: DigiTrax MS-100
Loconet: LocoBuffer (John Jabour)
Feedback encoder
Märklin: S88 (6088)
Lenz: LR100 or LS110
Trix: Function decoder 66821
Turnout, Switchpoint or Signal decoder
Märklin: K83 (6083), K84 (6084)
Lenz: LS100 or LS110
Trix: Feedback encoder 66820

Functional Requirements

The driver must provide the following functionality:

Provided Facilities

To fulfill the functional requirements, a 'driver' has been built. It contains a number of service-routines, which provide the following facilities:

Deliverables

The shareware components of the DigiAPI package are:

The registration package consists of a key-file DigiAPI.Key which unlocks the limitations of the demo version. The key-file may me installed in the current directory or any library in LIBPATH, DPATH or PATH. The most obvious place is the library of DigiAPI.DLL and its companion DLLs.

Demo

The 'demo' version has some limitations for not-registered users:

Of course these limitations disappear after installing the key file which you receive after registration.

Prerequisites

The package has been built with the following assumptions

Restrictions and Limitations

The following restrictions apply:

Tests

The drivers have been tested by the author with combinations of the following hardware and software

NOTE

  • Not all combinations of hardware and software have been tested, but it is not likely that any viable combination will not work.
  • Application framework

    A typical model railroad control program with this driver may look like the C-program below:

    
         #include "DigiAPI.h"
    
         HFILE  hChannel;                   /* I/O handle */
         int    i;                          /* counter */
         char   ucKey;                      /* keyboard input */
    
         hChannel = DigiOpen("COM1", PROTOCOL_P50X, 0);  /* start */
         for (i=1; i<MAXGROUP; ++i)         /* all sensorgroups */
           DigiEnableSensorGroup(hChannel,i);  /* activate sensor monitoring */
         while (ucKey != ESC) {
           if (kbhit()) {                   /* ucKeyboard input */
             if (ucKey = getch()) {         /* normal key (incl alt-numpad) */
               switch(ucKey) {
                 case '?': - - -            /* help function */
                           break;
                 case ESC: break;           /* nothing (termination pending) */
                 default:  printf("%c", BEL);  /* unsupported */
                           break;
               }
             else {                         /* 'special' ucKey */
               switch(ucKey = getch()) {    /* extended character */
                 case PgDn: - - -           /* your 'extended' ucKey */
                           break;
                 default:  printf("%c", BEL);  /* unsupported ucKey */
                           break;
               }
             }
           for (i=0; i<MAXTRAIN; i++) {  /* scan train matrix */
             - - - - -                      /* train control function */
             }
           for (i=1; i<MAXGROUP; ++i) {  /* obtain feedback info */
             DigiQuerySensorGroup(hChannel, i);  /* read feedback contact bits */
             - - - - - -                    /* process changes */
             }
           if (!(DigiQueryTime()%5000)) {   /* every 5 seconds */
             - - - - -                      /* anything else */
             }
           DosSleep(1);                     /* give up remainder of timeslice */
           }
         DigiClose(hChannel);               /* terminate */
         }
    
    

    NOTE:

    Sample programs

    A sample program is included in the DIGIAPI package:

  • A program in C-language for loc speed calibration.
  • Some REXX procedures with the same functionality, and some more REXX procedures for test- and demo-purposes.
  • Some Java applications for test- and demo-purposes.

    Design

    To make the Application Programming Interface independent of the model railroad system, a certain abstraction level has to be defined. And to support different 'digital' model railroad systems with all their specific requirements, a number of concepts have been implemented. These aspects are covered in this chapter to help you to understand its facilities and make optimal use of them.

    Functional Abstractions

    The following groups of 'management' functions are provided

    Concepts

    A number of terms are used in this document, of which it is important to understand these concepts within the scope of controlling a digital model railroad based on DigiAPI.

    Numbering and Addressing

    The manufacturers of digital modelrailroad systems have no consistent numbering and addressing scheme, and frequently the number encoding in commands differs from the numbering used in the documentation. For example:

    To handle these differences, DigiAPI uses a numbering scheme which is a sort of common denominator and translates these numbers to the manufacturer's schemes. Some examples:

    DigiAPI has also the loc decoder speed indications generalised, for example:

    See Manufacturer Specific Information for the translation between a DigiAPI address and the address of the specific manufacturer's component.


    Facilities

    Overview of available Functions

    Table of user callable functions
    General Modelrailroad Management
    DigiOpen Start communication with the and the background tasks. This function should be the first to be called, since it includes initialisation code for some other routines. Calling another DigiAPI-function before a successful DigiOpen() is usually detected, but may occasionally cause unpredicatable results!
    DigiClose Stop 'background' tasks and shutdown the communication with the Interface
    DigiFlush Wait until all data transmitted
    DigiReset Re-initiate communications with interface
    Emergency Management
    DigiGo Set Interface in running ('go') state
    DigiStop Set Interface in 'stop'-state
    DigiHalt Emergency stop for all locs, or a single specified loc
    Loc / Function decoder Management
    DigiControlLocSpeed Control speed and direction of loc
    DigiQueryLocSpeed Obtain current speed and direction of a loc
    DigiControlLocFunction Control setting of a function of loc
    DigiQueryLocFunction Obtain current setting of all functions of aloc or function decoder
    Turnout Management
    DigiControlTurnout Throw turnout
    DigiQueryTurnout Obtain current state of turnout
    Feedback Management
    DigiEnableSensorGroup Start monitoring of a group of sensor contacts
    DigiQuerySensorGroup Obtain the current status of a group of sensor contacts
    Event Management
    DigiOpenEventQueue Start of event signalling to application
    DigiCloseEventQueue Terminate event signalling to application
    DigiQueryEventQueue Query number of event signals waiting for application
    DigiReadEventQueue Read events signal
    DigiWriteEventQueue Queue event signal for application
    Problem Management
    DigiTrace Start, resume or stop recording data that is being sent to (only in OS/2-drivers).
    Timer Services
    DigiQueryTime Give time since last DigiOpen() with milliseconds accuracy
    DigiSleep Pause for a certain time and give up the remainder of a time slice
    Miscellaneous
    DigiCommand Send command to Interface, and return reply if any
    DigiQueryState Obtain system state information
    DigiQueryStatistics Provide current value of a statistics counter
    DigiQueryVersion Obtain system firmware version

    This set of functions is available for both programs in C-language and for REXX procedues.


    Hints and Tips

    This chapter gives some recommendations about the use of the functions provided by DigiAPI.

  • Timers
  • Errors and Signals

    Use of timer functions

    At program start with DigiOpen(), the system time is taken as base for the function DigiQueryTime(). This function will return a value in milliseconds since the last DigiOpen(). The function DigiQueryTime() has an accuracy of 1 millisecond. For most model railroad purposes this gives more accuracy than normally needed.
    The use of this timer facility provided by the DigiAPI-driver has the following advantages:

    Errors and Signals

    See Error codes in the programmers reference manual of DigiAPI.


    Installation

    Application development considerations

    This DigiAPI is a program development tool for application programs in any language. The source samples are for C-language. You need DigiAPI.h at compile-time and DigiAPI.Lib at LINK-time. These are additions to the compiler and other toolkits you may need with your program development.

    C-Compiler considerations

    The following compiler properties apply.

    Building an EXE-file

    One of DigiAPI libraries should be available for the link-step with your own OBJect file(s). I prefer to Compile and LINK with the help of the NMAKE utility.

    For the sample application ("CALIBRAT") I use this MakeFile.

    REXX development

    The REXX interface of DigiAPI provides a set of functions practically idential to the C-language interface. These functions must be loaded before you can use them. See the examples or the reference manual.

    Java development

    The Java interface of DigiAPI provides a class with methods, practically idential to the C-language interface.

    You need to add DigiAPI.class tot your LibPath environment. The quick-and-dirty way is to have '.;' in your LibPath and put DigiAPI.class in the directory of your application(s). A better way is to put DigiAPI.class in a .jar file: one of the existing jar-files in your LibPath, or a separate .jar file, which you add to the LibPath specification in Config.Sys.

    Storage and performance

    There is not much to say about storage and performance of the driver. It is highly optimised code, and probably only a minor part of your 'real' modelrailroad control program.

    Memory Utilisation

    It is difficult to say how many storage is occupied by the DigiAPI-routines.

    Processor utilisation

    You might be curious about the cycles that will be consumed by the task that repeatedly reads feedback encoders. I've done some measurements, but found a maximum difference of about 10% between feedback monitoring enabled and disabled. This number is practically independent of the number of feedback encoders which are specified for monitoring!

    Comments, Remarks, Bug-reports

    If there are bugs, requirements or questions about this -driver, the documentation or the sample program (that you get when you register), please send me a message by E-mail. You can find my name and node-(point)-number on the title page of this document.

    Enjoy and good luck with your program development! May this driver lead to a very attractive and successful model railroad control program!

    Do not 'forget' to register!


    Internals

    This appendix describes a number of design arguments for this driver and explains decisions where a choice had to be made.

    Multitasking

    There are many situations in real-time programming, where you want the system to do something for you, while in the meantime you would like to do something else in the mainline (before the first piece of program completes).

    An example of this is reading feedback encoders. While you have to wait for the response of the Interface, you might want to update your screen, read data from disk, or process Keyboard input. In many sample programs that I have seen, the wait is frequently done in the form of a short loop (a for- or while-statement). This means that the processor is working, in fact consuming cycles, while you cannot do anything (unless you make a more 'complicated' loop). And in many cases you have to experiment with this loop to get the needed wait-interval (not too long and not too short). Running the same program on a machine with a different processor-speed, requires a change in the loop.

    A solution for this is to have multiple programs to run simultaneously. Executing multiple programs (tasks) in parallel is called multi-tasking. Since we are generally dealing with a single micro processor, the tasks are simultaneaously present in memory, but only 1 of them is actually processing. There must be some mechanism to switch the processor between the tasks.

    The method of reading feedback encoders in DigiAPI is a form of multi-tasking. DigiAPI uses the standard OS/2 facilities (called multi-threading). In fact there are a few extra tasks to:

    Although you don't have to bother about the reading of feedback encoders, it does have some influence on the design of your railroad control program. These practical aspects are explained below.

    Reading feedback encoders

    The driver generates read-feedback-encoder commands if needed (such as for Märklin) in the following way:

    1. After monitoring of feedback is enabled periodically a read-feedback command is transmitted to read all feedback encoders on systems which need this type of feedback processign (such as Märklin and Trix).
    2. The receive-part of the communications task is signalled that a response from the Interface is expected. The receive-task waits as long as not all expected input is received, or an unexpected long time had to be waited.
    3. After processing the response, the system waits some time before issuing a new command to read feedback information (typically 100 msec).
    4. Even if for some reason the previous receive did not finish within reasonable time (typically 1000 msec), a new read-command is issued.
    5. Commands for monitoring are automatically merged into the stream of control commands (to locs and switches). It is important that both types of commands will not monopolize the datastream to the Interface! DigiAPI manages the regulation of both streams.
    From 'life'-tests I have concluded:

    Reading feedback concact encoders is programmed as follows:

    1. When enabling feedback monitoring, and if needed (e.g. Märklin, Trix), a command is issued for 'destructive' reads (read with reset).
    2. Periodically read commands are transmitted.
    3. Spontaneous feedback information (Lenz, Loconet) is processed as well.

      Feedback contact data processing
    Organisation of processing feedback signals

    In this sample a group-read of 6 Märklin S88-boxes is used.
    1. Collection of 'raw' data from the Interface. Array-1 always represents the current switch positions of all monitored feedback contacts, apart from a slight delay between consecutive reads.
    2. Cumulative ON-bits since most recent DigiQueryFeedback() for each specific feedback contact in Array-2.

    The incoming data of a contact is stored in array 1, and all 1-bits are copied immediately to array 2 with an 'or'-operation. So array 2 is a cumulation of ON status since the last time the contact (group) was queried form the application program.

    When DigiQueryFeedback(x) is executed, the contents of group x in array 2 are returned to the caller. At the same time group x in array 2 is refreshed with the then current contents of array 1. So a second DigiQueryFeedback() immediately after the first gives the actual status.
    Function DigiQueryFeedback might be changed later to be able to specify that you need data from the first or from the second array. A second call is required if you want to know for sure if the contact is currently in OFF after it has been ON since the most recent DigiQueryFeedback().

    After feedback monitoring is started, the first data to be received is the current status of the Interface. This may not be the actual status. To be sure to get the actual status, wait some time after poll-start (more than 100 msecs) before expecting to get the actual status!

    This mechanism is suitable for all types of switches that are applied in model-railroads, and ensures that you won't miss any 'switch-on' condition, even if it is 'on' during a very short period. This organisation is illustrated in the figure above.

    You have to be careful with specifying a higher value in DigiEnableFeedback() than you really have contacts (and encoders) on your model railroad.

    So specifying more feedback contacts than area really active will unnecessary downgrade the responsiveness of your program. You might decide to suspend monitoring feedback during processor-intensive work, or not to use reading feedback at all.

    DigiAPI reads always a range of feedback encoders. The range is determined from the lower an upper bound of the enable contacts. When intermediate contacts are not enabled, the encoders, even without any 'activated' contacts, are read by DigiAPI.

    Communications Interface Specification

    The contains a device driver level communication facility with between computer and , but with a rather high level interface for the application programmer. It contains many basic functions, that otherwise have to be programmed yourself. However, it enforces a specific organisation of your program and has some limitations.

    Some of the internals of the communications task are briefly explained to show why it works as it works now, and may prevent questions. On the other hand, it may trigger suggestions for changes! Feel free to go in discussion with me!

    Wiring
    For the connection between computer and Interface the wiring should be as indicated below. Failing wires will in most cases result in an open error.
    • Read Data (RxD), write data (TxD) and Ground are always required.
    • Clear-to-Send is used by most systems for flow control (from computer to Interface) except by Trix.
    • DTR has to be wired with the Intellibox, Trix and DigiTrax Loconet MS-100 (not with LocoBuffer).
    • RTS has to be wired with the Intellibox, DigiTrax Loconet MS-100 and LocoBuffer.
    • DSR hase to be wired with the Intellibox, DigiTrax MS-100 and LocoBuffer (modification by Rob Hamerling: 10 KOhm resistor between DTR and DSR).
    • DCD has to be wired with the Intellibox.
    The signals are used by the drivers as appropriate for the specified protocol (system). When your Interface has a standard RS-232 connector, then a normal modem cable (all wired straight through) is all you need for any system. A so called nul-modem cable is always wrong!
    Speed
    DigiAPI has a table of properties for each system, among which the speed of the Interface. When you specify 0 (zero) it will take the default, but if you specify a speed, it will override this default. DigiAPI allows any speed between 10 and 115200 bps.
    FIFO
    So called FIFO hardware buffering, provided by the newer UART-chips (such as 16550AF) on asynchronous communication ports is usually NOT used, due to the need for CTS flow control on byte-level. DigiAPI will use hardware buffering of the COM-port's UART when available and when the protocol allows it.
    Output flow control
    The CTS signal is used for 'hardware output flow control' from the computer by most systems (not Trix Selectrix). DigiAPI will take care of CTS flow control when appropriate. In some cases (like with the Intellibox) it ignores CTS, but takes care not to overload the Interface buffers!
    Since the Lenz LI100 is know for its 'slow' setting of CTS, the Lenz driver of DigiAPI has a special facility to correct this behaviour. A new command is not sent when the hardware of the COM-port is still transmitting data of a previous command.
    Input flow control
    None of the systems have a provision for input flow control. This will not be a problem, since there is enough buffering and CPU-power to process the low volume input.
    XON/XOFF flow control
    Not used. This type of flow-control is not supported by any Interface, since commands may contain these characters!
    Null stripping
    The standard OS/2 device driver for the COM-port allows null stripping. This is not used, since commands and replies may contain zeroes!

    Trace facility

    Trace records

    The trace facility produces the following records:

     unsigned long timestamp;  /* value of DigiQueryTime() */
     unsigned char protocol,   /* protocol identifier */
                   direction,  /* In or Out going frame */
                   length,     /* length of the trace data */
                   data[255];  /* trace data (variable, max 255 bytes */
    

    The first record of a new trace is an identification record, containing the date and time of the trace start.

    Trace Sample

    The output of the trace formatting program DIGITRC for an Intellibox session (P50Xb protocol) will look like:

     DigiAPI trace formatter version 3.2  -  Rob Hamerling
    -------------------------------------------------------------
           2.999 O C4
                   XNOP
           3.545 I 00
                   OK
           3.545 O A2
                   XStatus: Query system status
           3.557 I 18
                   Power on, H0-scale voltage, Halt
           3.557 O A7
                   XPwrOn: Resume operations
           3.944 I 00
                   OK
           3.945 O A40600
                   XSOGet: Query SO # 6
           3.968 I 00FE
                   SO #   6: 254 = $FE = %11111110
           3.968 O A40D00
                   XSOGet: Query SO # 13
           3.991 I 0002
                   SO #  13:   2 = $02 = %00000010
           3.991 O 5254203235350D
                   RT 255.
           4.060 I 52532D32333220435453206C696E65206175746F2D6F6E203D20496D6D6564696174650D5D
                   RS-232 CTS line auto-on = Immediate.]
           4.061 O A40E00
                   XSOGet: Query SO # 14
           4.089 I 0064
                   SO #  14: 100 = $64 = %01100100
           4.089 O 99
                   XSensOff: Generate events for all currently 'on' sensors
           4.102 I 00
                   OK
           4.102 O C8
                   XEvent: Query pending events
           4.136 I 8140
                   Pending events mask: '8140' ( Lok Sts )
           4.137 O A2
                   XStatus: Query system status
           4.153 I 08
                   Power on, H0-scale voltage
           4.153 O C9
                   XEvtLok: Query loc events
           4.191 I 00010A800080
                   Loc 10  Speed +0  On=F.1.......
           4.191 O 850A00
                   XLokCfg: Query Configuration Loc 10
           4.227 I 00000EFFFF
                   Loc 10:  MOT (14 non-Stop speed steps)
    
           ...  (etc) ...
    
    Totals: OUT     436 records with    1017 bytes of trace data,
             IN     436 records with     833 bytes of trace data.
    

    This is not a complete trace, just some parts to show some essentials.

    DifiTrc does not need parameters, but you may specify the input filename [including path] if your trace is in another directory or named other than DigiAPI.TRC, which is the default.
    DigiTrc writes the output to 'stdout', which is normally the computer display. But the output may be redirected to your printer or to a file. So you could use for example the command:

      DIGITRC  >DigiAPI.FMT
    
    so that you can look afterwards with a text editor or file browse utility in DigiAPI.FMT for the formatted trace.

    NOTE: The trace facility cannot be used by multiple simultaneously running application programs based on DigiAPI at a time when run with the same default directory. However DigiAPI can trace multiple channels simultaneously, and names the output file after there protocol and channel, for example P50X.TR1 when Intellibox protocol is used on channel 1.


    Interface peculiarities

    When writing a model railroad control program (with or without this driver), you should be aware of some peculiarities, which are not very well (or sometimes not at all) documented by the manufacturer

    Märklin 6050/6051

    With experiments during development of DigiAPI and it predecessors with a Märklin Digital modelrailroad I have found:

    Some Interfaces, Controllers and decoders may react differently than others!

    Lenz LI100
    The LI100 is half duplex. CTS is dropped at the end of a command and rises
    Lenz LI100 drops CTS before sending unsolicited data. This prevents a command being sent.

    Marklin 6050/51

    Strictly half duplex. Buffer for only 1 single command.

    Trix Selectrix 88624/88642

    Full duplex.

    Uhlenbrock Intellibox

    Device with multiple buffers: 16 turnout commands and 4 loc commands per loc protocol can be buffered. May cause synchronisation problems in the application.

    Digitrax Loconet MS100
    Unbuffered full duplex device.

    John Jabour LocoBuffer's LocoBuffer
    This is an intelligent replacement for the DigiTrax MS-100. It has input and output buffers and takes care of collision detection, retransmission, timing, etc. and supports 57600 bps COM-port speed.

    Summary of changes

    3.2
    August 2003
    • Support of LocoBuffer version 1.5.0.0 and up.
    3.1
    April 2002
    • Added Java interface with native method under OS/2. This allows writing Java applications.
    • Added support for DigiTrax Loconet protocol with LocoBuffer of John Jabour.
    • Extended registration key-file search. When keyfile is not in the current (application) directory it will be searched in LIBPATH, DPATH and PATH (in that order).
    • DigiHalt() now 'global' only (loc address specification obsolete).
    3.0
    April 2001
    • Complete restructuring an almost complete rewrite of the code.
    • Protocols now 'pluggable' with separate DLLs.
    • Dropped support for DOS, DigiAPI is now for OS/2 only.
    • Added support for Uhlenbrock Intellibox (P50X protocol) and for DigiTrax Loconet protocol with MS-100.
    • Added interface for REXX programming.
    • Many functions changed, some removed, others added.
    2.3
    February 1999
    • Systems with command/reply sequences, 'conversational mode' like Lenz, are now handled as such to prevent observed flow control problems with 'full duplex' mode (next command sent without waiting for a reponse).
    • Some minor bug fixes, internal performance improvements, and documentation extensions.
    2.2
    December 1998
    • Corrections in feedback for Lenz (reversed contact numbering).
    • Documentation extensions.
    2.1
    September 1998
    • Added support for Trix Selectrix, Märklin 6023 TRAIN-ING (6023)
    • The function DigiEnableFeedback() now activates only the specified feedback contact. So the application program should activate every individual participating contact, in stead of only the highest contact as with DigiAPI version 2.0.
      Note: when deactivating monitoring ALL monitoring is stopped, and when reactivating it, all participcating contacts have to be re-enabled again!
    • Improvements in handling of timeouts and queueing to prevent data loss in extreme situations with high command traffic.
    • The DigiGo() function now issues only a single 'GO' command for Mäerklin.
    2.0
    First release as successor of MKLAPI 1.7.
    It is a major overhaul involving many changes, and it would be largely a duplication of this documentation to make a complete list of changes.
    If you have based your model railroad application on MKLAPI, it needs a major overhaul to migrate it to DigiAPI. Some functionality that you needed to put in your application is now provided by DigiAPI. Read this Programming Guide and the Reference Manual carefully to make yourself familiar with the conceptual changes.
    Below only some 'highlights' of changes:
    • Was MKLAPI restricted to Märklin Digital only and was tightly coupled to the facilities of this system, DigiAPI is on a higher abstraction level and has hardly any dependencies on a specific system, but is is a sort of 'least common multiple'. This allows multiple systems to be supported, currently only Märlin and Lenz Digital Plus, more to come later. For your application it means that it supports automatically more than only Märklin Digital. There is nothing else you have to put in your program than a facility to indicate which protocol DigiAPI has to use.
    • The 'channel' concept has been introduced. It means that DigiAPI supports multiple COM-ports and a selectable protocol per port. Therefore most functions have a parameter to indicate to which channel a command applies.
    • Reading of feedback information is now generalised and incorporated in a 'monitoring' task. Depending on the type of system under control more than only feedback information is read. For example with Lenz also system status information and loc information (of 'active' locs only) is requested on a regular basis.
    • DigiAPI is shareware and distributed with some functional limitations. You need a key-file to unlock the limitations.


    End of DigiAPI Programming Guide