[MAT logo][Prev][Up][Next]

A basic custom module

As far as a module is concerned, the MatPLC is a library. It provides various functions, of which only about five or seven are the most important. The others provide things like retrieving values from the config file - useful, but not vital.

Usual usage

There are two usual ways of structuring modules: the first is the "classical scan" familiar from stepladder, while the second is a "data interface", where MatPLC and some other system exchange data.

Classical scan

Here, the behaviour resembles a stepladder scan: at the top of the loop, data is read from the inputs (and other parts of the MatPLC), then the logic is executed, and then data is written to the outputs (and other parts of the MatPLC).

This is the template for a classical-scan module:

#include <plc.h>

int main(int argc,char *argv[])

  /* initialization goes here */

  while (1) {

    /* body of loop goes here */


Data interface

Here, the module does data interchange between MatPLC and something else (the "other"). In the first half of the loop, any data that's going from the other to the MatPLC is written into MatPLC points. In the second half of the loop, data is taken from MatPLC points and sent to the other. All the I/O modules work this way, exchanging data between MatPLC and the I/O.

One advantage is that the module doesn't need to keep track of which points are read-only and which are read-write in the MatPLC; in the first half, all points may be written, and in the second half all points may be read. The plc_update() discards values for which the module lacks write permission.

This is the template for a data-interface module:

#include <plc.h>

int main(int argc,char *argv[])

  /* initialization goes here */

  while (1) {

    /* process data going to MatPLC from the "other" */


    /* process data coming from MatPLC to the "other" */


Filling in the template


One of the main things you need to do here is to get handles to all the points your program will access.
foo = plc_pt_by_name("foo");
bar = plc_pt_by_name("bar");

foo and bar should be declared as variables of type plc_pt_t. If you want to be safe, you should also check that foo.valid and bar.valid are non-zero after the above initialization.

Throughout the rest of the program, you will then use the variables foo and bar to refer to those two points.

Body of loop

The two most important functions here:

read point
write the value to the point

These are used both for coils and for integer registers. When used for coils, 0 means OFF and 1 means ON.

When used for registers, value is treated as unsigned 32-bit integer (MAT defines the type u32 for this purpose). Narrower points can also be treated as unsigned integers - for instance, a plc_get() on an 8-bit point will return a number between 0 and 255.

As usual with PLC-style logic, points are read from outside the module at the top of the loop and written to the outside at the bottom (that's what the plc_update() does). Last value written wins.

Remember that for plc_set(), the module needs point ownership in the matplc.conf file. There is no warning if you try to write to a point you don't own; the changes are simply discarded and the point value remains unchanged.

Processing data going to/from MatPLC

Like "Body of loop", you will use the two functions plc_get(pt_handle) and plc_set(pt_handle,new_value). When processing data going from the MatPLC, you will only use the first of these (plc_get()); when processing data going to the MatPLC, you will only use the second (plc_set()).

Other than this separation, the previous section applies.

Summary of the basic functions

The five most important functions are:

Initializes the library. The module_name should be a string. If argc and argv are not available, pass 0 and NULL instead.
pt_handle = plc_pt_by_name(pt_name)
Obtains a handle to a MatPLC point, required for the plc_get() and plc_set() functions. The pt_name should be a string, while pt_handle should be of type plc_pt_t. The success or failure of this function is indicated by pt_handle.valid; non-zero means OK. This function, like plc_init(), should be called before the time-critical part begins.
Updates the local (buffered) copy of all the MatPLC points. It should be called before reading MatPLC points or after writing them. If the module runs in a repeated "scan", this function will usually be called once at the beginning of the scan, and once at the end.
value = plc_get(pt_handle)
Reads a MatPLC point (as it was at the time of the last plc_update(), or as changed by this module), returning it as an unsigned 32-bit integer. A 32-bit float version of this function is also available.
plc_set(pt_handle, new_value)
Writes an unsigned 32-bit integer to a MatPLC point. A 32-bit float version of this function is also available. The write will not be seen by the other MatPLC modules until plc_update() is called.
The header file defines two types for work with these: plc_pt_t, which is used to declare the handles, and u32 which is a simple 32-bit unsigned integer.

The two additional functions should be used if the module runs in a repeated scan, like a traditional PLC. They enable the MatPLC to enforce execution periods, sequencing of modules, RUN/STOP modes and the like. They take no arguments.

Beginning of scan. This should precede the plc_update() call, as shown in the templates.
End of scan. This should follow the plc_update() call, as shown in the templates.

These functions are explained in more detail in the general reference and GMM reference chapters.

Other functions

Probably the most interesting ones will be the functions to retrieve values from the config. Unfortunately there's a lot of them, because they need to cover all the combinations of retrieving from single settings and tables, from the module's own section or from a different one, and converting the values to various types. They all start with conffile_ and are described in detail in the conffile reference.

As noted, there are floating-point equivalents to plc_get() and plc_set(); they are called plc_get_f32() and plc_set_f32(), work with the type f32 instead of u32, but otherwise behave exactly like the integer versions.


The MatPLC library is not particularly thread-safe. If you are using multiple threads, it would be best to restrict MatPLC functions to one thread. However, it is quite OK for one thread to be using the runtime functions while another thread does a plc_pt_by_name().


$Date: 2005/05/15 09:37:00 $