/*MTDEMO01: Convert DCON protocol of I-7000 series to Modbus/TCP protocol Compiler: BC++ 3.1 Compile mode: large Project: user.c v7000.c ..\LIB\7188EL.Lib ..\LIB\TCPIPL.Lib ..\LIB\VCOM_NNNN.Lib, with NNNN being the lib file's version. ..\LIB\MBT7_NNN.Lib, Modbus/TCP library for 7188E, NNN being the lib's version. Communication: Modbus Master <= Modbus/TCP => Ethernet port of 7188EX <==> [Modbus Kernel] <==> COM Ports of 7188EX <==> Modbus/RTU slave device This program has 4 main functions 1. 1 to N port Modbus/TCP to Modbus/RTU converter The Ethernet port is used to receive Modbus/Request from Modbus/TCP master. Other COM ports are used to link to Modbus/RTU slave devices. When gets Modbus/TCP request, the kernel can decide to send request to which COM port and return the response to the Modbus/TCP master. 2. X board supports Modbus/TCP protocol Modbus/TCP master can send request to specific NetID(station number) to access the X board that mount on io expansion bus. 3. Supports user-defined function The internal registers are open for X board and user-defined process. You can use the internal register to store some special information. 4. Supports Modbus/RTU master function Put the value to internal register, use function "ModbusMaster2Slave", you can let 7188E be a Modbus/RTU master controller. Hardware: 7188EX + I-7017 + I-7024 I-7017: RS-485 address : 1 RS-485 baudrate: 115200 Checksum: disable input range: any data format: 2's complement I-7024 RS-485 address : 2 RS-485 baudrate: 115200 Checksum: disable output range: any data format: engineer Memory mapping iMemory_AI[00]~[07] <====> I-7017 Ch0~Ch7 iMemory_AO[00]~[03] <====> I-7024 Ch0~Ch3 Note: to use MBT7_NNN.Lib, the project is a little different to Xserver demos. You must remove vModbus.c from this project. [19,Mar,2003] by Kevin [24,May,2004] by Kevin Correct comments of ScanUserProcess [13,Mar,2005] by Kevin Update Xserver library from 3.0.9 to 3.2.1 Needs to add two functions: PortUserStart(in user.c), Port9999Start(in V7000.c). [2006,Feb,17] by Kevin Use new I/O memory mapping method. */ #include #include "..\lib\7188e.h" #include "..\lib\vxcomm.h" #include "..\Lib\MBTCP.h" // Modbus/TCP Head file for I-7188E int iScanCOMPort=1; // of Modbus Kernel int iScanUserProcess=0; int ScanUserProcess(int iProcess); //The I/O memory mapping unsigned char far iMemory_DI[100]; unsigned char far iMemory_DO[100]; int far iMemory_AI[100]; int far iMemory_AO[100]; void UserCount(void) { // user's timer trigger function // // In this function cannot use any function that will use the hardware signal "clock", // Such as: // 1. ClockHigh(),ClockLow(), ClockHighLow(), // 2. Any EEPROM functions. // 3. Any 5DigitLed functions. // 4. Any NVRAM function. // 5. Any RTC function.(GetTime(),SetTime(),GetDate(),SetDate()) // // refer to demo9 for example code } void UserInit(void) { /* In this function, user CAN: 1. initialize user's program. 2. set time interval for calling UserCount(). 3. set initial value of I/O or variables for UserLoopFun(). 4. set initial value of I/O or variables for another functions. 5. change the default TCP PORT 10000/9999/502 to others value. [after vcom3004.lib] Syntax: Port10000=newport_10000; for calling UserCmd (user.c) Port9999=newport_9999; for calling VcomCmd7000 (v7000.c) Port502=newport_502; for calling VcomCmdModbus (vModbus.c) [after vcom3002.lib] PortUser=newport_User; for calling VcomCmdUser (user.c) [after vcom3005.lib] Default port value: Port10000=10000; Port9999=9999; Port502=502; PortUser=0; If the port value is 0, Xserver will not listen that port. That means the port will be disable. */ int iRet; //InitLib(); // InitModbus() already includes InitLib();. //======= Begin of Modbus Kernel ======= iRet=InitModbus(iMemory_DI,iMemory_DO,iMemory_AI,iMemory_AO); if(iRet==0) { // Initial Modbus configuration success. } else { // Initial Modbus configuration failure. } //======= End of Modbus Kernel======= // Configure the COM port that links to the i-7000 modules. // Save COM2 setting to EEPROM ComData[2-1].baud=115200; ComData[2-1].databit=8; ComData[2-1].parity=0; ComData[2-1].stopbit=1; VcomSaveComData(2-1); Set_COMEnableMode(2, _Programming); Port9999=0; //Disable listening TCP port 9999 to speed up 7188E. } void UserLoopFun(void) { //======= Begin of Modbus Kernel ======= if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusRTU_Slave) CheckModbusRTURequest(iScanCOMPort); // Is any request from Modbus/RTU Master ? if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusASCII_Slave) CheckModbusASCIIRequest(iScanCOMPort); // Is any request from Modbus/ASCII Master ? if(mtModbusPort[iScanCOMPort].EnableMode==_ModbusRTU_Gateway) { SendModbusRequest(iScanCOMPort); // Passes request to modbus slave device. CheckResponseTimeout(iScanCOMPort); // If response timeout, sets iModbusAction // to IDLE status. CheckModbusResponse(iScanCOMPort); // Is any response from modbus slave device? SendModbusResponse(iScanCOMPort); // Passes response to Modbus/RTU Master. } iScanCOMPort++; if(iScanCOMPort>iTotalCOMPort) iScanCOMPort=1; //======= End of Modbus Kernel ======= iScanUserProcess=ScanUserProcess(iScanUserProcess); } int UserCmd(unsigned char *Cmd,unsigned char *Response) { sprintf(Response,"%s",Cmd); return 1; // return ok } int VcomUserBinaryCmd(TCPREADDATA *p) { /* VXCOMM.EXE 2.6.12(09/04/2001) or later will support this function. TCP PORT 10000, command 23 will call this function. user can get the following message: p->ReadUartChar : the buffer store the command data(include "23") p->Length : the command data length(include the two byte "23") p->Socket : the socket number that receive the command, that is when the user function want return message to the client, just use the socket to send data. use: VcomSendSocket(p->Socket,pdata,datalength); */ VcomSendSocket(p->Socket,"User-defined command(23)",24); // return 24 bytes. return 1; /* any value will be accept */ } void PortUserStart(int skt) { /* XS8_3200.Lib Version 3.2.00 (20,Apr,2004) or later version supports this function. When a TCP/IP client connects to the 7188E/8000E via the user's defined port(PortUser), the Xserver calls the function once. You can use function VcomSendSocket to send a message to the client when a connection is established. For example: VcomSendSocket(skt,"Connection is established.",26); //return 26 bytes. skt: socket number assigned to the TCP/IP client. */ skt=skt; //do nothing } int VcomCmdUser(TCPREADDATA *p) { /* VCOM3005 (Feb,22,2002) or later will call this function for PortUser. When packets received by TCP PORT PortUser(user defined) of 7188E/8000E, Xserver will call this function. user can get the following message: p->ReadUartChar : the buffer store the command data. Maximum length of p->ReadUartChar is 32767 bytes. p->Length : the command data length. p->Socket : the socket number that receive the command, that is when the user function wants return message to the client, just use the socket to send data. usage: VcomSendSocket(p->Socket,pdata,datalength); */ /* here just send back the command to the client. */ VcomSendSocket(p->Socket,p->ReadUartChar,p->Length); return 1; /* any value will be accept */ } //===========================================================// // Functions about Modbus // //===========================================================// /***************************/ /* [ScanUserProcess] */ /* for 7188E/7188XB */ /* version 1.0.1 */ /* date: 9,Aug,2002 */ /***************************/ int ScanUserProcess(int iProcess) { // iProcess: executes which process? // return: executes which process next time? // // This function scans your special processes and stores the results // to the memory. Thus, users can use Modbus protocol to // read the memory to get the values of your special processes. // You should cut the hole scan process to many small processes. // That can reduce execution time of this function and // speed up the scan rate. // // The total internal registers spaces is limited by // constant MAX_REGISTER_COUNT in MBTCP_7E.h. You can change the number // to increase the total spaces. // // Xboard and user's process share the internal registers. // To use the internal registers, you must notice don't overlap // the registers. // // To program the COM port, you need to use the Modbus Utility to set // the COM port enable mode to programming mode. int iRet,iValue,i; int iNextProcess; float fValue; char sInputBuf[80]; switch(iProcess) { // Analog input ==> Memory // I-7017(#1) ch0~ch7 ==> iMemory_AI[0]~iMemory_AI[7] case 0: //The COM port must be set at Programming mode. //Then only the c code by you can control the COM port. if(mtModbusPort[2].EnableMode==_Programming) { // to read I-7017(#01) that connect to COM2 of 7188E SendCmdTo7000(2,"#01",0); // COM2, read I-7017(#01), checksum disable iRet=ReceiveResponseFrom7000_ms(2, sInputBuf, 500, 0); // COM2, bufer to receive response, timeout, // checksum disable // one channel uses 4 bytes, first byet is '>'. // data format of I-7017 must be set to [2's complement] // that can make the response string shorter. // // +32767(7FFF) ==> maximum value, for example +10.0V // -32768(8000) ==> minimum value, for example -10.0V // You can use 7000 Utility to change the maximum value and minimum value. if(sInputBuf[0]=='>' && iRet==0) { for(i=0;i<8;i++) { iMemory_AI[i]=(int)((ascii_to_hex(sInputBuf[i*4+1])<<12) +(ascii_to_hex(sInputBuf[i*4+1+1])<<8) +(ascii_to_hex(sInputBuf[i*4+1+2])<<4) +(ascii_to_hex(sInputBuf[i*4+1+3]))); } } } iNextProcess=1; break; // Memory ==> analog out // iMemory_AO[0] ==> I-7024(#2) ch0 case 1: //The COM port must be set at Programming mode. //Then only the c code by you can control the COM port. if(mtModbusPort[2].EnableMode==_Programming) { // to write I-7024(#02) that connect to COM2 of 7188E // pass iMemory_AO[0] to channel 0 of I-8024 // // +32767(7FFF) ==> maximum value, for example +10.0V // -32768(8000) ==> minimum value, for example -10.0V // You can use 7000 Utility to change the maximum value and minimum value. fValue=(float)iMemory_AO[0]/32768*10.0; // 10.0 means the maximum value sprintf(sInputBuf, "#020%+06.3f", fValue); // Ch0 SendCmdTo7000(2,sInputBuf,0); // COM2, write 7024, checksum disable iRet=ReceiveResponseFrom7000_ms(2, sInputBuf, 500, 0); // COM2, bufer to receive response, timeout, // checksum disable } iNextProcess=2; break; // Memory ==> analog out // iMemory_AO[1] ==> I-7024(#2) ch1 case 2: //The COM port must be set at Programming mode. //Then only the c code by you can control the COM port. if(mtModbusPort[2].EnableMode==_Programming) { // to write I-7024(#02) that connect to COM2 of 7188E // pass iMemory_AO[1] to channel 1 of I-8024 // // +32767(7FFF) ==> maximum value, for example +10.0V // -32768(8000) ==> minimum value, for example -10.0V // You can use 7000 Utility to change the maximum value and minimum value. fValue=(float)iMemory_AO[1]/32768.0*10.0; // 10.0 means the maximum value sprintf(sInputBuf, "#021%+06.3f", fValue); // Ch1 SendCmdTo7000(2, sInputBuf, 0); // COM2, write 7024, checksum disable iRet=ReceiveResponseFrom7000_ms(2, sInputBuf, 500, 0); // COM2, bufer to receive response, timeout, // checksum disable } iNextProcess=3; break; // Memory ==> analog out // iMemory_AO[2] ==> I-7024(#2) ch2 case 3: //The COM port must be set at Programming mode. //Then only the c code by you can control the COM port. if(mtModbusPort[2].EnableMode==_Programming) { // to write I-7024(#02) that connect to COM2 of 7188E // pass iMemory_AO[2] to channel 2 of I-8024 // // +32767(7FFF) ==> maximum value, for example +10.0V // -32768(8000) ==> minimum value, for example -10.0V // You can use 7000 Utility to change the maximum value and minimum value. fValue=(float)iMemory_AO[2]/32768*10.0; // 10.0 means the maximum value sprintf(sInputBuf, "#022%+06.3f", fValue); // Ch2 SendCmdTo7000(2, sInputBuf, 0); // COM2, write 7024, checksum disable iRet=ReceiveResponseFrom7000_ms(2, sInputBuf, 500, 0); // COM2, bufer to receive response, timeout, // checksum disable } iNextProcess=4; break; // Memory ==> analog out // iMemory_AO[3] ==> I-7024(#2) ch3 case 4: //The COM port must be set at Programming mode. //Then only the c code by you can control the COM port. if(mtModbusPort[2].EnableMode==_Programming) { // to write I-7024(#02) that connect to COM2 of 7188E // pass iMemory_AO[3] to channel 3 of I-8024 // // +32767(7FFF) ==> maximum value, for example +10.0V // -32768(8000) ==> minimum value, for example -10.0V // You can use 7000 Utility to change the maximum value and minimum value. fValue=(float)iMemory_AO[3]/32768*10.0; // 10.0 means the maximum value sprintf(sInputBuf, "#023%+06.3f", fValue); // Ch3 SendCmdTo7000(2, sInputBuf, 0); // COM2, write 7024, checksum disable iRet=ReceiveResponseFrom7000_ms(2, sInputBuf, 500, 0); // COM2, bufer to receive response, timeout, // checksum disable } iNextProcess=0; break; default: iNextProcess=0; break; } return iNextProcess; } void Modbus_Request_Event(char* CommandData,int* CommandLength) { /* Modbus_Request_Event is supported since version 1.6.8 [2007,03,13]. char* CommandData: For Modbus/TCP, it includes 6 leading bytes. (needful) For Modbus/RTU, it includes 6 leading bytes. (needless) int* CommandLength: For Modbus/TCP, it includes 6 leading bytes. For Modbus/RTU, it includes 6 leading bytes. */ /* Example code */ //int i; //printCom1("FC:%2d StartAddress:%3d IOCount:%4d\n\r",iModbusRequest_Fun, iModbusRequest_Addr,iModbusRequest_IOCount); //printCom1("Modbus Request\n\r In==>"); //for(i=0;i<*CommandLength;i++) // printCom1("[%02X] ",CommandData[i]&0xFF); } void Modbus_Response_Event(char* ResponseData,int* ResponseLength) { /* char* ResponseData: For Modbus/TCP, it includes 6 leading bytes. For Modbus/RTU, it doesn't include 6 leading bytes int* CommandLength: For Modbus/TCP, it includes 6 leading bytes. For Modbus/RTU, it doesn't include 6 leading bytes */ //If you want to change the content of the ResponseData, //you have to do 2 steps for Modbus/TCP or Modbus/RTU. //Step1: Change content (Note:you must know the modbus protocol well) //ResponseData[6]=0x19; //ResponseData[7]=0x75; //ResponseData[8]=0x04; //ResponseData[9]=0x01; //Step2: Update data length //*ResponseLength=10; //int i; //printCom1("\n\r Out==>"); //for(i=0;i<*ResponseLength;i++) // printCom1("[%02X] ",ResponseData[i]&0xFF); //printCom1("\n\r"); }