next up previous contents index
Next: Modbus using rllib Up: Bus systems Previous: Bus systems   Contents   Index


matplc

matplc is an OpenSource project at http://mat.sourceforge.net. It includes a soft PLC and drivers for IO. Internally it maintains a shared memory which can be accessed by signal names. A configuration file matplc.conf is responsible for defining your environment. Some hardware has been evaluated with matplc.

Figure 9.1: Twido PLC connected to PC
Image modbusfoto

Figure 9.2: Screenshot of Modbus demo
Image modbusdemo

I have tested matplc with pvbrowser connected to a Schneider Electric Twido PLC. The PC is Mobus master and connects to port 1 of the PLC using Modbus RTU. The PLC programming software TwidoSoft runs within CrossOverOffice but can't connect to the PLC via /dev/ttyS0 or /dev/cua0 . Here is my code.

Figure 9.3: qmake project file for Modbus demo
TEMPLATE = app
CONFIG   = warn_on release

INCLUDEPATH += ../../lib
# Input
HEADERS += pvapp.h
SOURCES += main.cpp     \
           mask1.cpp
LIBS    += ../../lib/matplc.a /usr/lib/librt.a
LIBS    += /usr/lib/libpvsmt.so /usr/lib/libpthread.so
# LIBS    += /usr/lib/libpvsid.so /usr/lib/libpthread.so
# DEFINES += USE_INETD
# TARGET = targetname

Figure 9.4: mat IO functions
#include "pvapp.h"
#include "plc.h"

int matBegin()
{
  //printf("plc_scan_beg\n");
  plc_scan_beg();
  //printf("plc_update\n");
  plc_update();
  //printf("return\n");
  return 0;
}

int matEnd()
{
  //printf("plc_update\n");
  plc_update();
  //printf("plc_scan_end\n");
  plc_scan_end();
  //printf("return\n");
  return 0;
}

int matRead(const char *name)
{
  plc_pt_t pt_handle;

  //printf("plc_pt_byname(%s)\n",name);
  pt_handle = plc_pt_by_name(name);
  if(!pt_handle.valid)
  {
    printf("invalid\n");
    return -1;
  }
  return plc_get(pt_handle);
}

int matWrite(const char *name, int value)
{
  plc_pt_t pt_handle;

  //printf("plc_pt_byname(%s) value=%d\n",name,value);
  pt_handle = plc_pt_by_name(name);
  if(!pt_handle.valid)
  {
    printf("invalid\n");
    return -1;
  }
  return plc_set(pt_handle,value);
}

Figure 9.5: main.cpp for Modbus demo
int pvMain(PARAM *p)
{
int ret;

  p->sleep = 2000;
  pvSetCaption(p,"pvsdemo");
  pvResize(p,0,1280,1024);
  ret = 1;
  while(1)
  {
    switch(ret)
    {
      case 1:
        ret = show_mask1(p);
        break;
      default:
        return 0;
    }
  }
}

#ifdef USE_INETD
int main(int ac, char **av)
{
PARAM p;

  if(plc_init("pvs",ac,av) < 0) printf("error plc_init\n");
  pvInit(ac,av,&p);
  /* here you may interpret ac,av and set p->user to your data */
  pvMain(&p);
  return 0;
}
#else  // multi threaded server
int main(int ac, char **av)
{
PARAM p;
int   s;

  if(plc_init("pvs",ac,av) < 0) printf("error plc_init\n");
  pvInit(ac,av,&p);
  /* here you may interpret ac,av and set p->user to your data */
  while(1)
  {
    s = pvAccept(&p);
    if(s != -1) pvCreateThread(&p,s);
    else        break;
  }
  return 0;
}
#endif

Figure 9.6: mask1.cpp generated_defineMask for Modbus demo
////////////////////////////////////////////////////////////////////////////
//
// show_mask1 for ProcessViewServer created: Tue Dec 9 15:15:06 2003
//
////////////////////////////////////////////////////////////////////////////
#include "pvapp.h"

typedef struct // (todo: define your data structure here)
{
  char s[6]; // switches
}
DATA;

// _begin_of_generated_area_ (do not edit -> use ui2pvc) -------------------

// our mask contains the following objects
enum {
  ID_MAIN_WIDGET = 0,
  textLabel1,
  table1,
  ID_END_OF_WIDGETS
};

static int generated_defineMask(PARAM *p)
{
  int w,h,depth;

  if(p == NULL) return 1;
  w = h = depth = 0;
  if(w==h) depth=0; // fool the compiler
  pvStartDefinition(p,ID_END_OF_WIDGETS);


  pvQLabel(p,textLabel1,0);
  pvSetGeometry(p,textLabel1,10,20,180,41);
  pvSetText(p,textLabel1,"Modbus demo:");
  pvSetFont(p,textLabel1,"Times New Roman",18,1,0,0,0);

  pvQTable(p,table1,0,6,2);
  pvSetGeometry(p,table1,0,80,240,150);

  pvEndDefinition(p);
  return 0;
}

// _end_of_generated_area_ (do not edit -> use ui2pvc) ---------------------

Figure 9.7: mask1.cpp defineMask for Modbus demo
static int defineMask(PARAM *p)
{
  if(p == NULL) return 1;
  generated_defineMask(p);

  pvTablePrintf(p,table1,-1,0,"L1");
  pvTablePrintf(p,table1,-1,1,"L2");
  pvTablePrintf(p,table1,-1,2,"L3");
  pvTablePrintf(p,table1,-1,3,"L4");
  pvTablePrintf(p,table1,-1,4,"L5");
  pvTablePrintf(p,table1,-1,5,"L6");
  pvTablePrintf(p,table1,0,-1,"Lamp");
  pvTablePrintf(p,table1,1,-1,"Switch");
  pvSetTablePixmap(p,table1,0,0,"icon_green.bmp");
  pvSetTablePixmap(p,table1,0,1,"icon_green.bmp");
  pvSetTablePixmap(p,table1,0,2,"icon_green.bmp");
  pvSetTablePixmap(p,table1,0,3,"icon_green.bmp");
  pvSetTablePixmap(p,table1,0,4,"icon_green.bmp");
  pvSetTablePixmap(p,table1,0,5,"icon_green.bmp");
  pvSetTableCheckBox(p,table1,1,0,0,"S1");
  pvSetTableCheckBox(p,table1,1,1,0,"S2");
  pvSetTableCheckBox(p,table1,1,2,0,"S3");
  pvSetTableCheckBox(p,table1,1,3,0,"S4");
  return 0;
}

Figure 9.8: mask1.cpp showData for Modbus demo
static int showData(PARAM *p, DATA *d)
{
  int i,val;
  char buf[80];

  if(p == NULL) return 1;
  if(d == NULL) return 1;
  // (todo: add your code here)
  matBegin();
  for(i=0; i<6; i++)
  {
    sprintf(buf,"S%d",i+1);
    val = matRead(buf);
    //printf("%d = matRead(%s)\n",val,buf);
    if(d->s[i] != val)
    {
      if(val == 1) pvSetTablePixmap(p,table1,0,i,"icon_green.bmp");
      else         pvSetTablePixmap(p,table1,0,i,"icon_red.bmp");
      d->s[i] = val;
    }
  }
  matEnd();
  return 0;
}

Figure 9.9: mask1.cpp event loop for Modbus demo
int show_mask1(PARAM *p)
{
  DATA d;
  char event[MAX_EVENT_LENGTH];
  char text[MAX_EVENT_LENGTH];
  char str1[MAX_EVENT_LENGTH];
  int  i,w,h,val,x,y,button;
  float xval, yval;

  defineMask(p);
  memset(&d,0,sizeof(DATA));
  d.s[0] = -1;
  d.s[1] = -1;
  d.s[2] = -1;
  d.s[3] = -1;
  d.s[4] = -1;
  d.s[5] = -1;
  readData(&d); // from shared memory, database or something else
  showData(p,&d);
  while(1)
  {
    pvPollEvent(p,event);
    switch(pvParseEvent(event, &i, text))
    {
      case NULL_EVENT:
        readData(&d); // from shared memory, database or something else
        showData(p,&d);
        break;
      ...
      case TABLE_TEXT_EVENT:
        sscanf(text,"(%d,%d,",&x,&y);
        pvGetText(text,str1);
        //printf("TABLE_TEXT_EVENT(%d,%d,\"%s\")\n",x,y,str1);
        if(i == table1 && x == 1)
        {
          int val;
          sscanf(str1,"%d",&val);
          sprintf(str1,"L%d",y+1);
          matBegin();
          matWrite(str1,val);
          matEnd();
        }
        break;
      ...
      default:
        printf("UNKNOWN_EVENT id=%d %s\n",i,text);
        break;
    }
  }
}

Figure 9.10: matplc.conf for Modbus demo
[PLC]
#
# matplc.conf - configuration file
#

[PLC]
point           L       "L"       pvs 4
point_alias     L1  "L1"      L 0
point_alias     L2  "L2"      L 1
point_alias     L3  "L3"      L 2
point_alias     L4  "L4"      L 3

point           S       "S"       modbus_m 6
point_alias     S1  "S1"      S 0
point_alias     S2  "S2"      S 1
point_alias     S3  "S3"      S 2
point_alias     S4  "S4"      S 3
point_alias     S5  "S5"      S 4
point_alias     S6  "S6"      S 5

# comments deleted

[PLC]
# synch the modules
synch pvs -> modbus_m
synch modbus_m -> pvs

synch_start pvs
synch_start modbus_m

[plcshutdown]
# configure which point will be used to shutdown the plc
quit_pt = quit
scan_period = 0.01

[modbus_m]
# comments deleted
network rtu_net         rtu   device /dev/cua0 baudrate 9600 parity none data_bits 8 stop_bits 1 ignore_echo false timeout 1.5 send_retries 2
node twido     rtu_net 1

# comments deleted
map out twido.out_bit.1  L1
map out twido.out_bit.2  L2
map out twido.out_bit.3  L3
map out twido.out_bit.4  L4
map in  twido.in_bit.5   S1
map in  twido.in_bit.6   S2
map in  twido.in_bit.7   S3
map in  twido.in_bit.8   S4
map in  twido.in_bit.9   S5
map in  twido.in_bit.10  S6


next up previous contents index
Next: Modbus using rllib Up: Bus systems Previous: Bus systems   Contents   Index
Rainer Lehrig 2004-02-17