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(:= 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 := "Unit \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;