A Programmable Logic Controller EMUlator for linux



PLC-EMU stands for Programmable Logic Controller EMUlator. This means PLC-EMU is a tool for emulating PLCs on a Linux box, using I/O cards. This way you can build a cheap alternative to PLC's, for use with automation applications. It consists of a text-based Ladder Diagram parser, a minimal C API and an optional ncurses-based interface for online control.


First of all, you need to install one or more PCI/ISA digital I/O card (analog I/O not supported- yet.) PLC-EMU works in two modes: through comedi, and in user space. In Comedi mode, all you need to do is install and set up the apropriate Comedi driver for your card, and copy the setup values to PLC-EMU's config file. Consult www.comedi.org for a list of compatible cards, and instructions on comedi.
In user-space mode, you need to know the base I/O ports memory region of your cards, and which area of it the card uses for reading (read offset) and writing (write offset).
For pci cards, run

lspci -vvv 
to find out the base address. The rest should be found in the cards' manual.
PLC-EMU has been successfully tested in both modes on a system running linux kernel 2.6.15 Preemptive, with the CPU timer frequency set at 1000Hz, and an Adlink PCI-1756 digital IO card.


As you are expected to know if you are still reading this, PLC's are real time controllers whose function is to periodically read inputs, run several real time tasks, and control outputs, in a steady time period, which in commercial PLC's varies usually from 2 to 10 milliseconds. PLC's are the standard platform for automation applications, and can they can be programmed in one or more of the 4 programming languages as defined by IEC61131-3: Instruction List, Ladder Diagram, Function Block Diagram, or Structured Text.
PLC-EMU emulates this function: In a configurable time cycle, it will read the inputs from your card, run a task as programmed by the user, and send the appropriate outputs back to the card. In the platform which has been tested, PLC-EMU would run correctly with a time cycle of 2 milliseconds, losing up to 1 millisecond in the worst case, which is compensatable in a preemptive kernel.
Apart from inputs and outputs, PLC-EMU also holds an internal "address space" of a user-defined number of memory variables which you may use in your programs as up/down counters, or boolean variables.
It also supports Timer and Blinking Timer registers, whose number and function can as well be configured by the user.
Last, PLC-EMU supports serial communication. In the time cycle it will read a byte from a named pipe, and might write up to one byte to another pipe. This way you can control it externally, and link it with other applications.


Download the tarball from www.sourceforge.net/projects/plcemu

Unzip the source files in a directory and run in superuser mode

make install 

This should create an executable named "plcemu", a text file named "plc.config", and two named pipes, namely "plcpipe" and "plcresponse".
If you dont have the comedi libraries, edit your plc.config file to not use comedi,run configure with the option:

>/.configure -enable-uspace 
Note that you dont need this if you have the comedi libraries, but you havent set up comedi for some reason.
If you don't have actual hardware, run configure with the option
>/.configure -enable-sim 
for text simulation mode.


Edit a plc.config file, which holds tab-separated configuration variables and their values. All variables must be set, otherwise it will not run. An example plc.config as included with the distribution:

USE_COMEDI 1 			;1 to use comedi, 0 to work in user space
STEP 10 			;time cycle in milliseconds
PIPE plcpipe 			;UNIX path of named pipe polled for commands
RESPONSE plcresponse 		;named pipe for response
SIGENABLE 36 			;POSIX enable signal to lock/unlock the interface
PAGELEN 64 			;max screen length in characters.
PAGEWIDTH 160 			;>> width
NT 16 				;number of timers
NS 16 				;number of blinking timers
NM 32 				;number of internal variables
HW Adlink_PCI-1756 		;just a text tag that appears in a footer
DI 4 				;number of bytes of digital inputs
DQ 4 				;number of bytes of digital outputs
;user space interface:
BASE 50176 			;hardware address base
WR_OFFS 4 			;write offset
RD_OFFS 0 			;read offset
;COMEDI interface:
COMEDI_FILE 0 			;device and subdevice nodes of comedi driver
Read below if you want to use an alternate configuration file.


At the moment, two ways of programming are supported. You can edit a text file in your favorite text editor and then load it into PLC-EMU (see below) or use the online editor. That file should contain initialization variables and a task in LD. Alternatively, you can still edit your project.c and project.h files in C and recompile, using

make project;make install

Program Structure

The program file consists of two sections: init section, and PLC task section, separated by the keyword "LD", if we ae programming in Ladder, or "IL", if we are programming in Instruction List.


In the init section, you can write the initialization state of the plc, in the format separated by tabs.

Supported variables are:

Integer variables
Non negative numbers.

Unique value variables
Boolean variables which, if they are not set, are presumed to work with their default values. So it only makes sense to initialize them in their non-default states.

Ladder Diagram

Everything after the keyword "LD", is supposed to be in Ladder diagram format. The version of LD PLC-EMU supports, consists of the following operators and operands, in a diagram of maximum 1024 characters wide, and random lines long.

These are the accepted symbols that can exist along with the operands.

Input operands
These can appear anywhere in a line before a '(', followed by a valid non negative index. Valid values of indexes are dependant on the operand and the configuration. This means, that if you have 16 timers and 64 inputs, you can write t14 and i62, but not t45 or i567. In every cycle, their values are polled and propagated to the diagram. Accepted symbols (case sensitive) are:

Output operands
These symbols must follow operator '(' and be followed by a valid index. Each output operand should appear only once.

Instruction List

Alternatively, you can use the keyword "IL" (or F5 in the editor window) to define a IEC61131-3 compatible Instruction List program. Currently, subroutines are not implemented. Just like standard Instruction List, all instructions store their result to an internal Accumulator register, while "ST" stores the Accumulator's value to its operand. A line of an IL program, shall follow the format:

Supported Operators are: Modifier symbols recognized are: Operands are the same as in LD, with the difference that it is assumed that they are Words (unsigned Integers), unless noted otherwise with the symbol '/'. As defined in the IEC standard, each instruction supports its own set of data types and modifiers, according to the following scheme:
Instruction 	Modifiers 	Data Types
	) 	N/A		N/A


Example of a valid program file, using Ladder Diagram:

I	1	My button
I	3	My other button
Q	1	My led
Q	2	My other led
M	1	My bool variable
M	4	My up-counter
T	1 	My on-timer
S	3	My blinker
MEMORY	4	654	
TIME	1	500	
PRESET	1	20	
BLINK	3	40	

 r1---------!m1----------(Q0    ;rising input 1 and not pulse of counter 1 contacts 
                                ;output 1
 b3---+---i2-----+             	;timer 1 or blinker 3 or falling edge of input
      |		 |		;3 and input 2
      |          |              ;sets pulse of counter 4. 
 f3---+          +--------[M4
 c255------------+--------(T1   ;command 255 starts timer 1 and sets output 2
 b1-----------+-----------]T1   ;blinker 1 stops timer 1, 
	      |	                ;resets pulse of counter 4 and output 1
	      |                 ;and writes byte 99 to serial output
You can write a text file with the initialization and LD program externally and then load it into PLC-EMU, or use the curses interface to edit and save it.


The alternative to LD programming, is doing it in C. PLC-EMU comes with a file project.c, which includes a C function, defined as

PLC_task(struct plc_regs * p){}
This function is called once every PLC cycle, and you can edit it as you wish.
Similarly, there is PLC_init(), which is executed once, in the beginning.
You may edit the bodies of these functions, and then recompile PLC-EMU.

The argument *p, is the main struct that represents the PLC.

As you can see in plclib.h, PLC-EMU provides several functions that can operate on this struct, which can be used in your code.


int re(struct PLC_regs * p,int type,int idx)
//return rising edge of operand 
int fe(struct PLC_regs * p,int type,int idx)
//return falling edge of operand
int set(struct PLC_regs * p, int type, int idx)
//set operand
int reset(struct PLC_regs * p, int type, int idx)
//reset operand
int contact(struct PLC_regs * p, int type, int idx,BYTE val)
//contacts an output with a value
int resolve(struct PLC_regs * p, int type, int idx)
//return an operand value
int down_timer(struct PLC_regs * p, int idx)
//reset timer
The argument "type" is the type of operand we are operating on, and can be one of the following values:
#define DI 0 	//digital input
#define DQ 1	//digital output
#define COUNTER 2	//pulse of counter
#define TIMER 	3 	//output of timer
#define BLINKER 4	//output of blinker
While the argument "idx" is the index of the given operand.
For example,
contact(&p, TIMER, 4, TRUE);
will start timer 4.

You can also use pretty much anything defined in plcemu.h and plclib.h, as long as you understand how the thing works... read carefully and have fun.
You may define your own structs, functions, etc. in project.h.


Executing plcemu from the command line

Usage: plcemu [-i program file] [-c config file] [-d]

Curses interface

PLC-EMU's interface consists of 5 monitoring windows, and a Ladder editor.

Inputs/Outputs window

Here you can see the current state of I/O.
When an I/O is green, it is set to 1.(although you can also read the value "1")
When it is red, it means it has been forced.

Memory window

Here you can see the value of internal memory registers.
Green means that the counter is receiving a pulse.
Red means that a counter has been disabled, and only its pulse is used as a boolean variable.

Timers window

Timers state is displayed here, in the form:
T[index] X [scale] [value]/[preset].
Green means the timer is counting.
Red means the timer has reached its preset value, and its output is positive.

Blinkers window

Here the current state of blinkers is displayed, in the form
S[index] X[scale].
Green means that blinkers'output is 1.

PLC task editor

When the PLC is in STOP mode, you can edit your LD task in a plain text editor.
Tabs, Copy/paste, select etc. are not supported-sorry.
Ctrl-X saves and returns to monitoring windows.
If you have an error in your ladder diagram, it will not be executed and an error message will be displayed.
In RUN mode, F5 toggles the language interpreter from Ladder to Instruction List and vice versa.


PLC-EMU does not support analog i/o yet, nor USB devices.
The C API needs to be expanded with more functions
Structured Text language should be supprorted sometime in the future, as well as an IEC61131-3 compatible XML editor for these
Multitasking rungs will be also implemented.

Please send feedback,suggestions, questions, requests, help(?) to:
Antonis Kalamaras(kalamara AT users.sourceforge.net)

Thanks to Sotiris Kontogiannis for his ncurses text editor and libraries
SourceForge.net Logo www.comedi.org Logo