Problem In Communicating with PLC from Pc

P

Thread Starter

Pankaj Suri

Hi,
I have written a code in C by which i should be able to Communicate with PLC(Modicon)
But the Reply(message) from the PLC is not correct. Kindly Help me to slove this Problem.
Protocol: MODBUS
Development Env. : TC++
Comm. Mode : ASCII



Pankaj.
[email protected]
===============================================

Code

#include <dos.h>
#include <stdio.h>
#include <conio.h>


#define PORT1 0x3F8
#define DATA_BUFFER 25
typedef unsigned int int16;
typedef unsigned short u16;
typedef unsigned char byte_t;

void main(void)
{
int loop=0;
int c;
int index=0;
int ch;
byte_t outbuf[20];
outportb(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */

/* PORT 1 - Communication Settings */

outportb(PORT1 + 3 , 0x80); /* SET DLAB ON */
outportb(PORT1 + 0 , 0x0c); /* 9600 Divisor Latch Low Byte */
outportb(PORT1 + 1 , 0x00); /* Set Baud rate - Divisor Latch High Byte */
outportb(PORT1 + 3 , 0x1b); /* 0x1b 8 Bits, No Parity, 1 Stop Bit */
outportb(PORT1 + 2 , 0xC7); /* FIFO Control Register */
outportb(PORT1 + 4 , 0x08); /* Turn on DTR, RTS, and OUT2 */

clrscr();
printf("\nSample PLC Comm's Program. Press ESC to quit \n");
/* Read A Word From PLC Port */
index=0;
outbuf[index++]=0x3a;
outbuf[index++]=0x30;
outbuf[index++]=0x34;
outbuf[index++]=0x30;
outbuf[index++]=0x38;
outbuf[index++]=0x30;
outbuf[index++]=0x30;
outbuf[index++]=0x30;
outbuf[index++]=0x36;
outbuf[index++]=0x31;
outbuf[index++]=0x36;
outbuf[index++]=0x32;
outbuf[index++]=0x33;
outbuf[index++]=0x34;
outbuf[index++]=0x0d;
outbuf[index++]=0x0a;


for(loop=0;loop<index;loop++) {
delay(50);
printf("%x ",outbuf[loop]);
outportb(PORT1,outbuf[loop]);}
printf(" \n");


do { c = inportb(PORT1 + 5); /* Check to see if char has been */
/* received. */
if (c & 1) {ch = inportb(PORT1); /* If so, then get Char */
printf(" %x",ch);} /* Print Char to Screen */
delay(20);
if (kbhit()){ch = getch(); /* If key pressed, get Char */
outportb(PORT1, ch);} /* Send Char to Serial Port */

} while (ch !=27); /* Quit when ESC (ASC 27) is pressed */
}


===============================================
OutPut::::
10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5
10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5
10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5
10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5
it goes on .......
 
Pankaj:

> Hi,
> I have written a code in C by which i should be able to Communicate with PLC(Modicon)
> But the Reply(message) from the PLC is not correct. Kindly Help me to slove this Problem.

I see a few things in your code that would concern me...

> outportb(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */

Running in polled mode is probably not the best for processor efficiency, but it IS easier to code.

> outbuf[index++]=0x3a;
> outbuf[index++]=0x30;
> outbuf[index++]=0x34;
> outbuf[index++]=0x30;

Time to learn about such fun commands as strncpy and/or sprintf! Then again, since it's a onetime thing, why not just do the following:

unsigned char outbuf[64];
sprintf (&outbuf,":0408000616234\n");

Now, if only I had my modbus reference book here to make sure you are sending the right command (and formatting).

> for(loop=0;loop<index;loop++) {

for (loop=0;loop<strlen(outbuf);loop++) {

> delay(50);
> printf("%x ",outbuf[loop]);
> outportb(PORT1,outbuf[loop]);}
> printf(" \n");

Would probably be better to make sure that the UART was in a ready mode before you send more data down it's throat. RS-232 programming is never fun or easy.

> do { c = inportb(PORT1 + 5); /* Check to see if char has been */
> /* received. */
> if (c & 1) {ch = inportb(PORT1); /* If so, then get Char */
> printf(" %x",ch);} /* Print Char to screen */
> delay(20);
> if (kbhit()){ch = getch(); /* If key pressed, get Char */
> outportb(PORT1, ch);} /* Send Char to Serial Port */

Why are you sending anything from the keyboard to the serial port?

Also, shouldn't you be doing a transition check as opposed to a level check for your "data ready" check? Consider this. What happens if the (c & 1) check at the beginning of the loop returns a 1 for, say, 5 or 6 program scans (and the data
hasn't changed or is not stable).

> } while (ch !=27); /* Quit when ESC (ASC 27) is pressed */
> }

> ===============================================
> OutPut::::
> 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10
5 3 10 5 4 10 5 5
> 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10
5 3 10 5 4 10 5 5
> 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10
5 3 10 5 4 10 5 5
> 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5 10 5 1 10 5 2 10 5 3 10 5 4 10 5 5
> it goes on .......

This tells me that you have the wrong bit somewhere.

Trust me, you really don't want to be doing low level RS-232 manipulations. It's ugly, it's hard to debug, and it's hard to get right. It's even harder when you aren't using interrupts. Interrupts are no fun either. If you can, let someone else deal with the low level RS232 stuff. Either get yourself a decent RS232 driver library or look into using an OS with decent RS232 support builtin (Windows with Visual Basic, Linux, etc...)

Ron Gage - Saginaw MI
 
D
If we translate your command
outbuf[index++]=0x3a;
outbuf[index++]=0x30;
outbuf[index++]=0x34;
outbuf[index++]=0x30;
outbuf[index++]=0x38;
outbuf[index++]=0x30;
outbuf[index++]=0x30;
outbuf[index++]=0x30;
outbuf[index++]=0x36;
outbuf[index++]=0x31;
outbuf[index++]=0x36;
outbuf[index++]=0x32;
outbuf[index++]=0x33;
outbuf[index++]=0x34;
outbuf[index++]=0x0d;
outbuf[index++]=0x0a;

it looks like
:/04/08/00/06/16/23/4/CRLF
Your PLC address is 04
Your Function 08 is the Diagnostic Command
Your SubFunction is 0006 which correct me if I am wrong doesn't exist
Data is 1623
The LRC is 4 it should be 04 as a number cannot exist on its own.

To read an Input register or word it should look something like
:/11/04/00/08/00/01/LRC/CRLF
If you have done already go to www.modbus.org and download the modbus manual.

What also disturbs me about this program is your printed output is nothing like what your code is supposed to be outputting.
ie 10 5 1 10etc is nothing like 3a 30 34 30 etc
I tried a part of your code on TC++ and it doesn't output 10 5 1 10 etc on my system so I am baffled by this.
 
> Pankaj:
> > outbuf[index++]=0x3a;
> > outbuf[index++]=0x30;
> > outbuf[index++]=0x34;
> > outbuf[index++]=0x30;

Ron Gage:
> Time to learn about such fun commands as strncpy and/or sprintf! Then
> again, since it's a onetime thing, why not just do the following:

Careful about that, though - if it's a binary protocol, sooner or later there'll be a NUL and all the str...(3) calls will fail.

> unsigned char outbuf[64];
> sprintf (&outbuf,":0408000616234\n");

Careful about this, too - sooner or later there'll be a % and it'll fail.
Safer to do this:
sprintf (&outbuf,"%s",":0408000616234\n");
or this:
snprintf (&outbuf, 64, "%s",":0408000616234\n");

> If you can, let someone else deal with the low level RS232 stuff. Either
> get yourself a decent RS232 driver library or look into using an OS with
> decent RS232 support builtin (Windows with Visual Basic, Linux, etc...)

Seconded.


Jiri
--
Jiri Baum <[email protected]> http://www.csse.monash.edu.au/~jirib
MAT LinuxPLC project --- http://mat.sf.net --- Machine Automation Tools
 
Top