5.5. Language bindings

5.5.1. Python bindings

Comedilib is a C library but comes also with bindings for Python, Perl, and Ruby. This enables usage of a DAQ device directly from these higher level languages.

5.5.1. Python bindings

Python bindings are automatically generated by SWIG. So always keep in mind that for precise information the SWIG documentation can come in handy in addition to the Comedi documentation.

The name of the module is called comedi. All the C functions, structs, and constants are directly available as is. So you can refer directly to the C documentation for most of the usage. Note that, as in C, the functions either return the successful result or an error code. Unlike typical Python functions, no exceptions are generated on errors (excepted type-checking performed by SWIG). For example, to open a Comedi device you can write in Python:

import comedi
device = comedi.comedi_open("/dev/comedi0")
if device is None:
    errno = comedi.comedi_errno()
    print "Error (%d) %s" % (errno, comedi.comedi_strerror(errno))

There are a few things to be aware of. The SWIG bindings automatically take care of converting functions with output parameters to function returning multiple values, if the type of the output parameter is simple. For example comedi_data_read takes as argument a lsampl_t *data which will contain the data read after the function returns. So in C it is used like this:

lsampl_t data;
rc = comedi_data_read(device, subdevice, channel, range, aref, &data);

As lsampl_t is a 32-bit unsigned int, in Python, the function is used like this:

rc, data = comedi.comedi_data_read(device, subdevice, channel, range, aref)

SWIG takes care of converting simple types between Python and C, but does not convert more complex types such as arrays or structs. Special Python classes are created for these. Moreover, Comedi also provides a few macros. These macros are available in Python as functions with similar names, but lowercase: comedi.cr_pack, comedi.cr_range, etc. So to create and modify a command, one can do in Python:

cmd = comedi.comedi_cmd_struct()
comedi.comedi_get_cmd_generic_timed(device, subdevice, cmd, nchans, period_ns)
cmd.stop_src = comedi.TRIG_COUNT
cmd.stop_arg = nscans
# create and add the channel list
clist = comedi.chanlist(nchans)
for i, channel in range(nchans):
    clist[i] = comedi.cr_pack(channel, range, comedi.AREF_GROUND)
cmd.chanlist = clist

One unfortunate consequence is that the objects to represent arrays are as low-level as C arrays (i.e., a pointer with an addition). They have no range checking and no iterator. In addition, they have to be cast from the Python object to the C object. So to create an instruction to call the internal trigger, you would write in Python:

insn = comedi.comedi_insn_struct()
insn.subdev = subdevice
insn.insn = comedi.INSN_INTTRIG
insn.n = 1
data = comedi.lsampl_array(insn.n)
data[0] = num
insn.data = data.cast()
rc = comedi.comedi_do_insn(device, insn)

When you need to convert from a raw SWIG object to a proxy object, the .frompointer of the Python object can be used. Also note that by default when the proxy object is deleted, SWIG frees the memory associated to it. To still be able to use the memory, you need to set .thisown to False.

Reading (or writing) a large set of data from a Comedi device is done via a file. In C, this is done by using a file number, but in Python you need a File object. You can get a File object via os.fdopen. For example, to read an array of input data from a Comedi device into a numpy array, one could do:

fileno = comedi.comedi_fileno(device)
file = os.fdopen(fileno, 'r+')
buf = numpy.fromfile(file, dtype=numpy.uint32, count=(nscans * nchans))