Examples - MODBUS Network - Slave (simple)

Top  Previous  Next

//-----------------------------------------------------------------------------
// SimpleSlave.vpl, created 2011-03-22 12:00
//
// This simple MODBUS slave is using the I/0 Extension to share its resources.
//
// IMPORTANT:
// As the memory, analog and digital in-/outputs are controlled by the firmware
// these must not be mapped using the Job configuration.
//
// LED's and switches can be controlled by the application as these are not
// accessible through MODBUS requests.
//
// To see which MODBUS commands that are supported by the I/O Extensions please
// see the "I/O Extension -> MODBUS commands" section of the online help
//
// Alternative communication with the master form the application:
//
// The only way to communicate with the MODBUS master from the application is
// through memory registers, as these can be accessed without mapping in the
// job, through the use of memioRead/memioReadX and memioWrite/memioWriteX
// functions.
//
// Note:
// When a memory register are accessed through MODBUS they are read as two
// 16bits addresses as they are 32bits long.
//
// The memory registers can be read as input register or read/written as
// holding registers.
//
// Start index of the memory registers are 0x1000 hex or 4096 decimal.
//
// MODBUS Configuration:
// Configuration of connection settings and node id is done through
// I/0 Extension, please see 'Using the I/O Extension' in the online help.
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
 
VAR_INPUT
  SW : ARRAY [1..3] OF BOOL;
END_VAR;
 
VAR_OUTPUT
  LED : ARRAY [1..4] OF BOOL;
END_VAR;
 
//-----------------------------------------------------------------------------
// THREAD_BLOCK: RegisterMonitor
//    Monitor IO memory for changes.
//
// INPUT
//    run   : Start/Stop monitoring
//    first : First index to monitor
//    last  : Last index to monitor
//-----------------------------------------------------------------------------
THREAD_BLOCK RegisterMonitor;
VAR_INPUT
  run   : BOOL := TRUE;
  first : INT := 1;
  last  : INT := 4096;
END_VAR;
VAR
  cur   : ARRAY [1..4096] OF DINT;
  old   : ARRAY [1..4096] OF DINT;
  msg   : STRING := "register \1 has changed from \4 to ";
  i     : INT;
  rc    : INT;
  rc_o : INT;
  err   : STRING := "RegisterMonitor - Failed to read memory ";
END_VAR;
  DebugMsg(message := "RegisterMonitor - Started");
  rc := 0;
  rc_o := 0;
  WHILE run DO
    // read current memory
    rc := memioReadX(index:=first, mem:=ADDR(cur), len:=(last - first + 1));
    IF NOT (rc = 0) THEN
        IF NOT (rc = rc_o) THEN
          CASE (rc) OF
              1 : DebugMsg(message := err + "'Invalid parameters'");
              2 : DebugMsg(message := err + "'Index range was out of bounds'");
          END_CASE;
          rc_o := rc;
        END_IF;
        Sleep(delay := 1000);
    ELSE
        FOR i := first TO last DO
          IF NOT ( cur[i] = old[i] ) THEN
              DebugFmt(message := msg + dintToStr(v := cur[i]),
                      v1 := i, v4 := old[i]);
              old[i] := cur[i];
          END_IF;
        END_FOR;
    END_IF;
  END_WHILE;
  DebugMsg(message := "RegisterMonitor - Ended");
END_THREAD_BLOCK;
 
//-----------------------------------------------------------------------------
// PROGRAM: SimpleSlave
//   Monitor switches and LED's
//-----------------------------------------------------------------------------
PROGRAM SimpleSlave;
VAR
  regMon   : RegisterMonitor;
  regMonSW : RF_TRIG;
 
  i        : INT;
  clk      : DINT;
  sn       : DINT;
END_VAR;
 
  DebugFmt(message := "Device \4 ready", v4 := boardSerialNumber());
 
  memioWrite(index:=1, value:=boardSerialNumber());
  DebugFmt(message := "Serial number stored in register 0x1000h + 0x1001h");
 
  // get initial message
  regMonSW(Trig := NOT SW[1]);
 
  LED[1] := ON;
BEGIN
  regMonSW(Trig := SW[1]);
  // Start / stop monitor thread
  IF regMonSW.rq THEN
    regMon.run := TRUE;
    DebugMsg(message := "Turn off switch 1 to stop register monitoring.");
  ELSIF regMonSW.fq THEN
    regMon.run := FALSE;
    DebugMsg(message := "Turn on switch 1 to start register monitoring.");
  END_IF;
 
  // Start thread if stopped and should be running
  IF regMon.run AND NOT regMon._running THEN
    regMon();
  END_IF;
 
  LED[2] := NOT regMon.run;
END;
END_PROGRAM;