ScriptAuthor "Beispiel" ScriptName "Modbus-RTU (Slave)" ScriptVersion "V 1.0" // 9600 Baud // This script realized the Modbus-RTU-SLAVE function // Communication - Modbus - Read modbus Slave Data // Write Modbus Slave Data // Das UNIGATE ist auf der RS Seite Modbus-Slave // Es liest noch keine Feldbusdaten ein // Es sollte dann Feldbusdaten für FunktionCode 1 - 4 im BusInput Buffer zur Verfügung stellen // Modbus Funktion Code 15 + 16 beschreibt den BusOutput Buffer. // Dies sind zwei getrennte Buffer die auch als zwei getrennte Modbus-Register-Buffer zu sehen sind // Soll nur ein Modbus-Register-Buffer zur Verfügung stehen, muss noch festgelegt werden ab welcher // Register Adresse der Input Bereich und der Output Bereich ist, damit Read und Write Bus // nicht den Buffer ungewollt überschreibt // V1.0 22.07.05 St erstellt //******************* init errorhandler **************************************** SetErrorHandler ( :Address ) ; SetSystemErrorHandler ( :SysAddress ) ; // ********************** variable declaration ********************************* // fieldbus: Var BusInput : BUFFER[32] ; // 132=max.Fieldbus-Length IN Var BusInput100 : BUFFER[100] ; // Var BusInput120 : BUFFER[120] ; // Var BusOutput : BUFFER[32] ; // 6=max.Fieldbus-Length OUT Var BusOutput220 : BUFFER[220] ; var wMaxByteCount : word ; Moveconst (wMaxByteCount, 244); // Var wBusInputLen : Word ; Var wBusOutputLen : Word ; Var bBusInputLen : Byte ; Var bBusOutputLen : Byte ; // RS-side var aModSlvIn : Buffer[20] ; // 20 = user defined var aModSlvIn100 : buffer[100]; // Reserve buffer if aModSlvIn is to smal var aModSlvIn130 : buffer[130]; // Reserve buffer if aModSlvIn is to smal var aModSlvout : Buffer[20] ; // 20 = user defined var aModSlvOut100 : buffer[100]; // Reserve buffer if aModSlvOut is to smal var aModSlvOut130 : buffer[130]; // Reserve buffer if aModSlvOut is to smal // Modbus parameter Var wModbusRxLen : Word ; var bFunctionCode : Byte ; var wStartAddressHiLo : Word ; var wNoOfPointsHiLo : Word ; var wCoilAddressHiLo : Word ; var wForceDataHiLo : Word ; var bExceptionCode : Byte ; // constant Var w0 : Word ; MoveConst (w0, 0) ; Var w1 : Word ; MoveConst (w1, 1) ; Var w2 : Word ; MoveConst (w2, 2) ; Var w7 : Word ; MoveConst (w7, 7) ; Var w255 : Word ; MoveConst (w255, 255) ; var wFF00 : Word ; Moveconst (wFF00, 0xFF00); Var b0 : Byte ; MoveConst (b0, 0) ; Var b4 : Byte ; MoveConst (b4, 4) ; Var b6 : Byte ; MoveConst (b6, 6) ; Var b14 : Byte ; MoveConst (b14, 14) ; Var b16 : Byte ; MoveConst (b16, 16) ; // error handler Var ReturnCode : Byte ; Var NoError : Byte ; MoveConst (NoError, 0) ; // default Returncode 0 = No Error var bErrorcode : Byte ; MoveConst (bErrorcode, 0) ; var bSystemErrorcode : Byte ; // other var aUserData : Buffer[20] ; // user defined var wByteCount : Word ; var wtemp : Word ; var bStateFC5 : Byte ; var btemp : Byte ; //****************** Init serial port ****************************************** Set ( Baudrate , 9600 ) ; Set ( Databits , 8 ) ; Set ( Startbits , 1 ) ; Set ( Stopbits , 1 ) ; Set ( Parity , even ) ; // *********** init Modbus Slave *********************************************** :InitModSlave; MoveConst (bErrorcode, 0) ; Get ( RS_Switch , btemp ) ; SetByVar ( ModbusSlaveAddress , btemp ) ; if bErrorcode equal b0 then :ModSlaveIDOK; Set ( Errorcode , 1 ) ; :LoopEndlosInitModSlave; jump :LoopEndlosInitModSlave; //****************************************************************************** :ModSlaveIDOK; Set ( RS_State_LED , RedGreenFlashing ) ; // *********** Init Field Bus **************************************************** // TBD //************************ Bus start ******************************************* // Start Bus and wait until running BusStart ; Wait ( Bus_Active ) ; Set ( RS_State_LED , GreenFlashing ) ; // Read actual Bus-Input und -Output-Length Get ( BusInputSize , bBusInputLen ) ; Get ( BusOutputSize , bBusOutputLen ) ; Convert ( bBusInputLen , Byte , wBusInputLen , Word ) ; Convert ( bBusOutputLen , Byte , wBusOutputLen , Word ) ; //************************ main ************************************************ :WaitNewModBusData ; call :UpdateUserProgram; // Bus Buffer tauschen mit ModBusUserbuffer ReadModbusSlave ( aModSlvIn[0] , wModbusRxLen ) ;// Wait without timeout for Modbusdata // Get ( ErrorCode , bErrorcode ) ; // If bErrorcode equal b2 then :WaitNewModBusData; // or // If bErrorcode equal b0 then :NewModbusData; // or if wModbusRxLen equal w0 then :WaitNewModBusData; //:NewModbusData; Copy ( aModSlvIn[0], bFunctionCode, w1); if bFunctionCode greater b4 then :CheckForceQuery; // function 1 - 4, Master Read data from Slave Copy ( aModSlvIn[1], wStartAddressHiLo, w2); // user information Copy ( aModSlvIn[3], wNoOfPointsHiLo, w2); // " " CalculateWord ( wNoOfPointsHiLo , w2 , mul , wByteCount ) ; if wByteCount greater wMaxByteCount then :ExceptionResponse03; Convert ( wByteCount , Word , aModSlvOut[1] , Byte ) ; // 2. Byte = bByteCount // TBD user : calculate your data CopyIndexed ( wByteCount , BusInput[0] , wStartAddressHiLo , aModSlvOut[2] , w0 ) ;// 3. - x Byte = user data response // Copy ( aUserData[0], aModSlvOut[2], wByteCount); // 3. - x Byte = user data response // Copy ( BusInput[0], aModSlvOut[2], wByteCount); // 3. - x Byte = user data response jump :WriteToModbus; :CheckForceQuery; if bFunctionCode greater b6 then :CheckExceptionQuery; // function 5 + 6, not supported jump :ExceptionResponse01; // function 5 + 6, Master send data to Slave Copy ( aModSlvIn[1], wCoilAddressHiLo, w2); // user information Copy ( aModSlvIn[3], wForceDataHiLo, w2); // " " if bFunctionCode equal b6 then :CheckPresetSReg; if wForceDataHiLo equal w0 then :SetStateOFF; if wForceDataHiLo equal wFF00 then :SetStateON; jump :ExceptionResponse03; :SetStateOFF; moveconst ( bStateFC5, 0 ); jump :CheckPresetSReg; :SetStateON; moveconst ( bStateFC5, 1 ); //jump :CheckPresetSReg; :CheckPresetSReg; // TBD user : calculate your data Copy ( aModSlvIn[1], aModSlvOut[1], w2); // 2. + 3. Byte Copy ( aModSlvIn[3], aModSlvOut[3], w2); // 4. + 5. Byt jump :WriteToModbus; :CheckExceptionQuery; if bFunctionCode greater b14 then :CheckForceMCoilsQuery; // function 7 - 14, not supported jump :ExceptionResponse01; :CheckForceMCoilsQuery; if bFunctionCode greater b16 then :ExceptionResponse01; // function greater 16 not supported at the moment // function 15 + 16, Master send data to slave Copy ( aModSlvIn[1], wCoilAddressHiLo, w2); // user information Copy ( aModSlvIn[3], wForceDataHiLo, w2); // " " moveconst ( wByteCount, 0); // default // Bug Copy ( aModSlvIn[5], wByteCount, w1); // " " Convert ( aModSlvIn[5] , Byte , wByteCount , Word ) ; if wByteCount greater wMaxByteCount then :ExceptionResponse03; if wCoilAddressHiLo greater wBusOutputLen then :ExceptionResponse03; // TBD user : calculate your data // Copy ( aModSlvIn[6], aUserData[0], wByteCount); // Modbusdata for user //!!! Copy ( aModSlvIn[6], BusOutput[0], wByteCount); // Modbusdata for user CopyIndexed ( wByteCount , aModSlvIn[6] , w0 , BusOutput[0] , wCoilAddressHiLo ) ; Copy ( aModSlvIn[1], aModSlvOut[1], w2); // 2. + 3. Byte Copy ( aModSlvIn[3], aModSlvOut[3], w2); // 4. + 5. Byte jump :WriteToModbus; //********************* Exception Response No for user ************************* :ExceptionResponse01; // Illegal Function Moveconst ( bExceptionCode, 1); jump :SetMSB; :ExceptionResponse02; // Illegal Data Address Moveconst ( bExceptionCode, 2); jump :SetMSB; :ExceptionResponse03; // Illegal data Value Moveconst ( bExceptionCode, 3); jump :SetMSB; :ExceptionResponse04; // Slave Device Failure Moveconst ( bExceptionCode, 4); jump :SetMSB; :ExceptionResponse05; // Acknowledge Moveconst ( bExceptionCode, 5); jump :SetMSB; :ExceptionResponse06; // Slave Device Busy Moveconst ( bExceptionCode, 6); jump :SetMSB; :ExceptionResponse07; // Negative Acknowledge Moveconst ( bExceptionCode, 7); jump :SetMSB; :ExceptionResponse08; // Memory Parity Error Moveconst ( bExceptionCode, 8); jump :SetMSB; //******************* calculate Modbus Error *********************************** :SetMSB; Set ( errorcode , 14 ) ; // display error on Unigate SC Convert ( bFunctionCode , Byte , wtemp , Word ) ; // only dataconverstion from byte to word CalculateByte ( wtemp , w7 , SetBit , wtemp ) ; // set MSB of FunctionCode Convert ( wtemp , Word, bFunctionCode , Byte ) ; // only dataconverstion from word to byte // Copy ( bFunctionCode, aModSlvOut[0], w1); // 1. Byte = bFunctionCode Copy ( bExceptionCode, aModSlvOut[1], w1); // 2. Byte = Exception Code jump :WriteToModbus; //***************** send Modbus Response *************************************** :WriteToModbus ; Set ( RS_State_LED , StaticGreen ) ; Copy ( bFunctionCode, aModSlvOut[0], w1); // 1. Byte = bFunctionCode WriteModbusSlave ( aModSlvOut[0] ) ; // OutLen and CRC is automatic calculated by UNIGATE jump :WaitNewModBusData ; //******************* Subrotine ************************************************ :UpdateUserProgram; // TBD ReadBus ( BusInput[0] , wBusInputLen ) ; // TBD WriteBus ( BusOutput[0] , wBusOutputLen ) ; // Fill ( aUserData[0] , b_Char , w_NumberChar ) ; // example return; //******************* Error Handler subroutine ********************************* :Address; Get ( Errorcode , bErrorcode ) ; if bErrorcode equal b0 then :EndAddress; // TBD Set ( Errorcode , 14 ) ; :EndAddress; return; :SysAddress; // RS-LED static red, Yellow-LED blinking with value of "bSystemErrorCode" Get ( SystemError , bSystemErrorCode ) ; if bSystemErrorCode equal b0 then :EndSysAddress; // TBD Set ( Errorcode , 15 ) ; :EndSysAddress; return; //******************************************************************************