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.
In this document a number of terms are used which should be clear to the reader, independent of the system or protocol 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, which provide the following facilities:
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.
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 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:
A sample program is included in the DIGIAPI package:
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 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.
| 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.
This chapter gives some recommendations about the use of the functions provided by DigiAPI.
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:
The timer can be used for example for
See Error codes in the programmers reference manual of DigiAPI.
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.
The following compiler properties apply.
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.
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.
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.
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 DigiAPI-routines.
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 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!
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.
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.
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!
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.
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.FMTso 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.
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
Some Interfaces, Controllers and decoders may react differently than others!
Strictly half duplex. Buffer for only 1 single command.
Full duplex.
Device with multiple buffers: 16 turnout commands and 4 loc commands per loc protocol can be buffered. May cause synchronisation problems in the application.