This chapter describes a possible use of DigiAPI.
The DigiAPI package is a set of functions to control a 'digital' modelrailroad by programs written in C-language or BASIC for the Personal Computers family.
It comes in 2 flavours:
In this document a number of terms are used which should be clear to the reader, independent of the system being used.
The driver must provide the following functionality:
To fulfill the functional requirements, a 'driver' has been built. It contains a number of service-routines (and for DOS also hardware interrupt service routines), and provides the following facilities
The shareware components of the DigiAPI package are:
The registration package consists of:
The 'demo' version has some limitations for not-registered users:
The package has been built with the following assumptions
The following restrictions apply:
The drivers have been tested by the author with combinations of the following hardware and software
NOTE
A typical model railroad control program with this driver may look like
#include "DigiAPI.h"
HFILE CHandle; /* I/O handle */
char key; /* keyboard input */
CHandle = DigiOpen("COM1", PROTOCOL_MAERKLIN, 0); /* start */
DigiEnableFeedback(CHandle, 64); /* monitoring feedback encoders */
while (key != ESC) {
if (kbhit()) { /* keyboard input */
if (key = getch()) { /* normal key (incl alt-numpad) */
switch(key) {
case '?': - - - /* help function */
break;
case ESC: break; /* nothing (termination pending) */
default: printf("%c", BEL); /* unsupported key */
break;
}
else { /* 'special' key */
switch(key = getch()) { /* extended character */
case PgDn: - - - /* your 'extended' key */
break;
default: printf("%c", BEL); /* unsupported key */
break;
}
}
for (i=0; i<MAXTRAIN; i++) { /* scan train matrix */
- - - - - /* train control function */
}
for (i=1; i<10, ++i) { /* obtain feedback info */
DigiQueryFeedback(CHandle, i); /* read feedback contact bits */
- - - - - - /* process changes */
}
if (!(DigiTime()%5000)) { /* every 5 seconds */
- - - - - /* anything else */
}
DigiSleep(25); /* give up remainder of timeslice */
}
DigiClose(CHandle); /* terminate */
NOTE:
An equivalent framework in QuickBASIC might look like
' $INCLUDE 'DigiAPI.INC'
DIM Port as string * 6
Port = "COM1"
CHandle = DigiOpen(varptr(Port), VarSeg(Port), PROTOCOLMAERKLIN, 0);
DigiEnableFeedback(CHandle, 64);
key$ = INKEY$
while key$ <> ESC$
select case key$
case '?'
- - - - -
case ESC
- - - - -
case else
print "unsupported key"
end select
for i=0 to MAXTRAIN
- - - - -
next i
for i=1 to 10
DigiQueryFeedback(CHandle, i)
- - - - - -
next i
if MOD(DigiTime,5000) < 50 then
- - - - -
end if
DigiSleep(25)
key$ = INKEY$
wend
DigiClose(CHandle);
NOTES:This code may not be true and workable QuickBASIC!
The file DigiAPI.INC contains the DECLARE lines for all functions contained in LDigiAPI.LIB. It should be included in a QuickBASIC program to obtain the required calling conventions and activates syntax checking by the . Subsequently the file LDigiAPI.LIB should be specified as library to the LINK utility to include the DigiAPI functions.
Two sample programs are included in the DIGIAPI package. A C-program for loc speed calibration and a QuickBASIC program for some funtional tests.
The OS/2-drivers differ from the DOS drivers in several ways:
The sample program under DOS chokes the processor (takes all the cycles that are available). This is OK in a typical single-task environment, but not very suitable for a multitasking system. Under OS/2 a very simple solution for this problem is to insert for example a DigiSleep(1) at the end of the infinite (keyboard-)loop. There may be better solutions.
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.
The following groups of 'management' functions are provided
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.
The manufacturers have no consistent numbering and addressing scheme, and frequently the number encoding in commands differs from the numbering used in the documentation. In many cases numbers and addresses start with '1' while the encoding in the actual command starts with '0. For accessory decoders frequently a completely different numbering is used because of the grouping of several decoder functions in a single decoder (4 switches can be controlled by 1 decoder).
Since these differences may lead to confusion, DigiAPI uses a consistent the numbering schem by itself and translates these numbers to the manufacturer's schemes. Some examples:
The manufacturer independent numbering (or addressing) scheme of DigiAPI deviates also in some aspects from the manufacturers numbering. For example:
See Manufacturer Specific Information for the translation between a DigiAPI address and the address of the specific manufacturer's component.
| 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() may cause unpredicatable results! | |
| DigiClose | Stop 'background' tasks and shutdown the communication with the Interface | |
| DigiFlush | Wait until all data transmitted | |
| DigiPurge | Discard remaining data in output buffer | |
| DigiQuerySystemState | Obtain system state information | |
| Emergency Management | ||
| DigiGo | Set Interface in running ('go') state | |
| DigiStop | Set Interface in 'stop'-state | |
| Train Management | ||
| DigiSetTrainSpeed | Control speed of a train | |
| DigiQueryTrainSpeed | Obtain current speed of a train | |
| DigiSetTrainFunction | Control setting of a function of train | |
| DigiQueryTrainFunction | Obtain current setting of a train function | |
| Accessory Management | ||
| DigiQueryAccessoryState | Obtain current setting of a switch or signal | |
| DigiSetContactOn | Set accessory contact ON | |
| DigiSetContactOff | Set accessory contact OFF | |
| DigiSwitchAccessory | Switch an accessory | |
| Feedback Management | ||
| DigiEnableFeedback | Start monitoring of a feedback contact | |
| DigiQueryFeedbackCount | Obtain the count of 'on' switches since last time the counter was reset | |
| DigiQueryFeedbackState | Obtain the current status of a feedback contact | |
| DigiQueryFeedbackTime | Obtain the time since a feedback contact first became 'on' | |
| DigiSetFeedbackAction | Register a task for feedback ection | |
| Program / Task Management | ||
| DigiTime | Give time since last DigiOpen() with milliseconds accuracy | |
| DigiSleep | Pause for a certain time (and give up the remainder of a time slice when running in a multitasking environment) | |
| DigiSound | Sound generator like BASIC sound or OS/2 DosBeep() | |
| Problem Management | ||
| DigiTrace | Start, resume or stop recording data that is being sent to (only in OS/2-drivers). | |
| Miscellaneous | ||
| DigiQueryStatistics | Provide current value of a statistics counter | |
This chapter gives some recommendations about the use of the functions provided by MKLAPI.
At program start with DigiOpen(), the system time is taken as base for the function DigiTime().
This function will return a value in milliseconds since the last
DigiOpen().
The function DigiTime() 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:
The timer can be used for example for
The functionality of the -driver for QuickBASIC is the same as for the drivers for C-language. However the following should be noted:
This DigiAPI is a program development tool for application programs in any language, but samples are given for C-language and QuickBASIC. You need DigiAPI.H (DigiAPI.INC for BASIC) at compile-time and the DigiAPI.DLL or LDigiAPI.LIB files at LINK-time. These are additions to the compiler and other toolkits you may need with your program development.
The following compiler properties apply.
Other compiler options are (probably) not of influence on the use of DigiAPI.
The QuickBASIC compiler is needed to create an object deck of your BASIC program. This OBJect file is combined with LDigiAPI.LIB file as provided by this DigiAPI package to build an EXE-file with the LINK utility.
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 DOS sample application ("CALIBRA1") I use MakeFile.
For a simple test-program in QuickBASIC I use batch-file Test.Bat.
NOTE: With the LDigiAPI.LIB need to be specified with the link. models.
For the 32-bits OS/2-sample application ("CALIBRAT") I use this MakeFile.
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.
It is difficult to say how many storage is occupied by the OS/2-version of the DigiAPI-routines.
The DOS-version requires about 6K bytes of storage.
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!
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 in the introduction 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!
This appendix describes a number of design arguments for this driver and explains decisions where a choice had to be made.
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.
Some operating environments have standard facilities for this (OS/2, Windows), but DOS doesn't. Under DOS you have to do it yourself. By intercepting hardware interrupts, or the timer-tick interrupt it is possible to implement a kind of task-switching.
The method of reading feedback encoders in DigiAPI is a form of multi-tasking. For OS/2 it uses the standard OS/2 facilities (called multi-threading), for DOS it uses the system timer to execute periodical actions independently from the main-program, and it uses also the hardware interrupts of the asynchronous communications port to perform some communications work. So 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.
The driver generates read-feedback-encoder commands if needed (such as for Märklin) in the following way:
Reading feedback concact encoders is programmed as follows:

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.
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!
Although all versions of this driver offer a consistent application programming interface for communication with the Interface, it is most likely that there will be differences between the programs using either of them. Some of these differences might be:
My own programs are primarily for OS/2 and I prefer to use the OS/2
facilities and those of the Program Development Toolkit as good as possible.
For instance for the relatively simple programs, like 'HANDIG', I use
VIO-routines for color and cursor manipulation.
To be able to build a DOS-version of this program with the same source,
I have written a library of routines to mimic the effect of these OS/2 VIO
calls with the help of standard BIOS calls.
I do not include this library in the DigiAPI package, since I assume you use
your own techniques, develop for your own environment and have no need to
make programs for OS/2 that have to run under DOS.
When there is no possibility to use the same code for OS/2 and DOS, you might consider using a compiler variable (with the -D compiler option) to distinguish which pieces of codes are OS/2 or DOS specific. See for an example the MAKE-files in the DigiAPI package.
The trace facility produces the following records:
unsigned long timestamp; /* value of DigiTime() */
unsigned char protocol, /* protocol identifier */
direction, /* IN or OUT going packet */
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.
The output of the trace formatting program DIGITRC will look like
DigiAPI trace formatter version 2.0 - Rob Hamerling
---------------------------------------------------------------------
98/02/01 21:17:30
Start trace of Maerklin traffic at Sun Feb 1 21:17:30 1998
0.279 O C0
Reset S88's after reading
0.283 O 84
Read S88 1...4 with reset
0.301 I 8001
1 16
.....
.....
1.236 O 4024
Function decoder 36 ON =
1.301 O 4024
Function decoder 36 ON =
1.362 O 003C
Loc=60 Speed=0 Function=OFF
1.423 O 003C
Loc=60 Speed=0 Function=OFF
.....
.....
17.502 I 8001
1 16
Totals: OUT 150 trace records with 213 bytes,
IN 625 trace records with 1250 bytes.
This is not a complete trace, just some parts to show some essentials.
DIGITRC 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 (output filename of DigiAPI).
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 >TRACE.FMTso that you can look afterwards with a text editor or file browse utility in TRACE.FMT for the formatted trace.
NOTE: The trace facility cannot be used by multiple simulataneously running application program based on DigiAPI at a time when run with the same default directory.
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 not at all) documented by the manufacturer (esp. Märklin!). With experiments during development of DigiAPI and it predecessors with my Märklin Digital modelrailroad I have found for example:
Some Interfaces, Controller and decoders may react differently than others!