Coding
Accessing the DMX4Linux drivers is like accessing files (or most
other drivers in the unix world). If you know how to work with files,
programming for DMX4Linux should be easy. We have written some example
programs that show how to do simple things.
- Writing to Universes
- Blocking Read
- Nonblocking Read
- Nonblocking Read with select
- DMXlib - supporting routines
The Filesystem interface
The Filesystem interface can be divided in two major
parts. The first one goes thru the special file
/dev/dmx ,/dev/dmxin , Currently all can
be accessed in the same way.
/dev/dmx is for writing to the output universes and
for readback from the output universe. /dev/dmxin
is used for reading from the input universes, where a write to
it is without any effect. Normaly they block when doing a read,
but opening them with the flag O_NONBLOCK the read will return
imediately.
Writing to Universes
/* This example shows a simple write to the dmx-outputs. */
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dmx/dmx.h>
int main(int argc, const char **argv)
{
int fd = open (DMXdev(&argc, argv), O_WRONLY);
if (fd!=-1)
{
dmx_t buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
lseek (fd, 0, SEEK_SET); /* set to the first channel */
write (fd, buffer, sizeof(buffer)); /* write the 10 channels */
/* now it points to the 11th channel */
close (fd);
}
return 0;
}
|
The write in the example above does not block. It returns just
after the data has been arrived in the driver. If you do a
second write direcly after the first one that looks exactly the
same it will write to channels 11 to 20 and the filepointer will
point to channel 21 (the file pointer will have the value 20
which is the 21th channel).
Blocking Read
Writing to the universes for setting channel values is the most
popular case :-) but sometimes you want to read from an input
universe or even from an output universe. A blocking variant of
that is shown in the example below.
/* This example shows a simple blocking read. */
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dmx/dmx.h>
int main (int argc, const char **argv)
{
const char *dmxname = DMXINdev(&argc, argv);
int fd = open (dmxname, O_RDONLY);
if (fd!=-1)
{
int i, n;
dmx_t buffer[10];
lseek (fd, 0, SEEK_SET); /* set to the first channel */
n=read (fd, buffer, sizeof(buffer)); /* read up to 10 channels */
if (n>0)
for (i=0; i<n; i++)
printf ("channel %d has value %d\n", i+1, buffer[i]);
else
printf ("Error %d while reading %s\n", n, dmxname);
close (fd);
}
return 0;
}
|
The read in the example blocks and returns after a write to one
of the channels 1 to 10 has been made. It returns the number of
channels that actualy has been read. The only case where you
will get less channels than requested currently is if you try to
read past the last universe.
Nonblocking Read
If you don't want to know when values have changed but want to
get the current state of the input or output for example to set
some sliders or to capture the current state you have to do a
non-blocking read. Therefore it is nessesary to add the flag
O_NONBLOCK to the open systemcall. The example below captures
200 channels and displays them in a tabular like form.
/* This example shows a non-blocking read from the dmx-output. */
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dmx/dmx.h>
int main (int argc, const char **argv)
{
const char *dmxname = DMXdev(&argc, argv);
int fd = open (dmxname, O_RDONLY | O_NONBLOCK);
if (fd!=-1)
{
int i=0;
lseek (fd, 0, SEEK_SET); /* set to the first channel */
while (i<200)
{
int j;
dmx_t buffer[20];
int n=read (fd, buffer, sizeof(buffer));
if (n<=0)
{
printf ("error %d while reading %s\n", n, dmxname);
close (fd);
return 1;
}
printf ("%d..%d:", i, i+n);
for (j=0; j<n; j++)
printf (" %03d", buffer[i+j]);
printf ("\n");
i += n;
}
close (fd);
}
else
printf ("Error while opening %s\n", dmxname);
return 0;
}
|
Nonblocking Read with select
There may be times you want to read more than one files. May be
you would like to read from a socket and write that data to a
dmx-output or so. Then you have to use threads or use the select
function. Below is an example how to use the select. The example
reads from an input, does a patch and writes them back to the
output. It swaps each even channel with the next odd channel.
/* this example shows a a non-blocking read trigered by a select. */
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <dmx/dmx.h>
int dmxin=-1,
dmxout=-1;
void exit_daemon (int sig)
{
exit(0);
}
void close_files(void)
{
if (dmxin)
close(dmxin);
if (dmxout)
close(dmxout);
}
int main(int argc, const char **argv)
{
dmx_t dmxbuffer[512];
dmxout = open (DMXdev(&argc, argv), O_WRONLY);
dmxin = open (DMXINdev(&argc, argv), O_RDONLY | O_NONBLOCK);
if (dmxout==-1 || dmxin==-1)
exit(0);
signal (SIGKILL, exit_daemon);
atexit (close_files);
while (1)
{
int n;
fd_set readset;
FD_ZERO(&readset);
FD_SET(dmxin, &readset);
FD_SET(0, &readset);
n = select (dmxin+1, &readset, NULL, NULL, NULL);
if (n>0)
{
if (FD_ISSET(dmxin, &readset))
{
int i;
lseek (dmxin, 0, SEEK_SET);
n=read (dmxin, dmxbuffer, sizeof(dmxbuffer));
for (i=0; i<512; i+=2)
{
int c = dmxbuffer[i];
dmxbuffer[i] = dmxbuffer[i+1];
dmxbuffer[i+1] = c;
}
lseek (dmxout, 0, SEEK_SET);
write (dmxout, dmxbuffer, sizeof(dmxbuffer));
}
/* exit after the user presses return */
if (FD_ISSET(0, &readset))
{
exit(0);
}
}
}
}
|
The DMXlib
Some common used functionality for DMX/Lighting applications is
collected in the DMXlib. You have already seen the function call
DMXdev() in the examples above that retrieves the
DMX device filename from the command line, environment or the
default places.
The documentation for DMXlib is
autogenerated with Doxygen. To use DMXlib include the header
file dmx/dmx.h and link your application with
-ldmx .
|