List of functions, structures and constants
Constants
/*
This is a wrapper for the serial or ethernet interface. This is here to make porting easier.
*/
typedef struct {
int rfd;
int wfd;
} _daveOSserialType;
typedef struct {
HANDLE rfd;
HANDLE wfd;
} _daveOSserialType;
some frequently used ASCII control codes:
DLE
ETX
STX
SYN
Protocol types to be used with newInterface:
Name | Meaning |
daveProtoMPI | MPI for S7 300/400 |
daveProtoMPI2 | MPI for S7 300/400, "Andrew's version" |
daveProtoMPI3 | MPI for S7 300/400, The version Step7 uses. Not yet implemented. |
daveProtoPPI | PPI for S7 200 |
daveProtoISOTCP | ISO over TCP |
daveProtoISOTCP243 | ISO over TCP with CP243 |
daveProtoIBH | MPI with IBH NetLink MPI to ethernet gateway |
ProfiBus/MPI speed constants to be used with newInterface:
daveSpeed9k |
daveSpeed19k |
daveSpeed187k |
daveSpeed500k |
daveSpeed1500k |
daveSpeed45k |
daveSpeed93k |
Some S7 Communication function codes (yet unused ones may be incorrect).
These codes are used as the first byte of parameters in a PDU.
Name | Meaning |
daveFuncOpenS7Connection | connect to a PLC, negotiate PDU length |
daveFuncRead | marks a read requeast |
daveFuncWrite | marks a write requeast |
daveFuncStartUpload | initiates the transmission of a code block from PLC to programmer |
daveFuncUpload | continues the transmission of a part of a code block from PLC to programmer |
daveFuncEndUpload | ends the transmission of a part of a code block from PLC to programmer |
S7 specific constants:
Block Type codes
S7 specific constants:
daveBlockType_OB
daveBlockType_DB
daveBlockType_SDB
daveBlockType_FC
daveBlockType_SFC
daveBlockType_FB
daveBlockType_SFB
Memory Area Codes
Use these constants for parameter "area" in daveReadBytes and daveWriteBytes.
Name | Meaning |
daveSysInfo 0x3 | System info of 200 family |
daveSysFlags | System flags of 200 family |
daveAnaIn | analog inputs of 200 family |
daveAnaOut | analog outputs of 200 family |
daveInputs | Input image memory |
daveOutputs | Output image memory |
daveFlags | Flags (Merker) area |
daveDB | Data blocks in 300 and 400, V-Memory in 200 |
daveCounter | Counters in 300 and 400 |
daveTimer | Timers in 300 and 400 |
Function Result Codes.
Genarally, 0 means ok, >0 are results (also errors) reported by the PLC, <0 means error reported by library code.
Name | Value | Meaning |
daveResOK | 0 | all ok |
daveResMultipleBitsNotSupported | 6 | CPU tells it does not support to read a bit block with a length other than 1 bit |
daveResItemNotAvailable200 | 3 | means a a piece of data is not available in the CPU, e.g. when trying to read a non existing DB or bit bloc of length<>1. This code seems to be specific to 200 family. |
daveResItemNotAvailable | 10 | means a a piece of data is not available in the CPU, e.g. when trying to read a non existing DB |
daveAddressOutOfRange | 5 | means the data address is beyond the CPUs address range |
daveResCannotEvaluatePDU | -123 | |
daveResCPUNoData | -124 | |
daveUnknownError | -125 | |
daveEmptyResultError | -126 | |
daveEmptyResultSetError | -127 | |
Error code to message string conversion:
Call this function to get an explanation for error codes returned by other functions.
char * daveStrerror(int code);
Max number of bytes in a single message.
An upper limit for MPI over serial is:
8 byte transport header
+2*240 max PDU len *2 if every character were a DLE
+3 DLE,ETX and BCC
= 491
Later I saw some programs offering up to 960 bytes in PDU size negotiation
Max number of bytes in a single message.
An upper limit for MPI over serial is:
8 transport header
+2*960 max PDU len *2 if every character were a DLE
+3 DLE,ETX and BCC
= 1931
For now, we take the rounded max of all this to determine our buffer size. This is ok
for PC systems, where one k less or more doesn't matter.
#define daveMaxRawLen 2048
Some definitions for debugging:
daveDebugRawRead | Show the single bytes received |
daveDebugSpecialChars | Show when special chars are read |
daveDebugRawWrite | Show the single bytes written |
daveDebugListReachables | Show the steps when determine devices in MPI net |
daveDebugInitAdapter | Show the steps when Initilizing the MPI adapter |
daveDebugConnect | Show the steps when connecting a PLC |
daveDebugPacket | |
daveDebugByte | |
daveDebugCompare | |
daveDebugExchange | |
daveDebugPDU | debug PDU handling |
daveDebugUpload | debug PDU loading program blocks from PLC |
daveDebugMPI | |
daveDebugPrintErrors | Print error messages |
daveDebugPassive | |
daveDebugAll | Enables all debug levels |
Global variables
Current debug level:
int daveDebug;
Set the Current debug level:
void setDebug(int nDebug);
Some useful data types:
#define uc unsigned char
#define us unsigned short
#define u32 unsigned int
typedef struct _daveConnection daveConnection;
typedef struct _daveInterface daveInterface;
Helper struct to manage PDUs. This is NOT the part of the packet called PDU, but a set of pointers that ease access to the "private parts" of a PDU.
typedef struct {
uc * header; /* pointer to start of PDU (PDU header) */
uc * param; /* pointer to start of parameters inside PDU */
uc * data; /* pointer to start of data inside PDU */
uc * udata; /* pointer to start of result data inside PDU */
int hlen; /* header length */
int plen; /* parameter length */
int dlen; /* data length */
int udlen; /* user or result data length */
} PDU;
Definitions of prototypes for the protocol specific functions. The library "switches" protocol by setting pointers to the protol specific implementations.
typedef int (*_initAdapterFunc) ();
typedef int (*_connectPLCFunc) ();
typedef int (*_disconnectPLCFunc) ();
typedef int (*_disconnectAdapterFunc) ();
typedef int (*_exchangeFunc) (daveConnection *, PDU *);
typedef int (*_receiveFunc) (daveConnection *, PDU *);
typedef int (*_listReachablePartnersFunc) (daveInterface * di, char * buf);
/+
This groups an interface together with some information about it's properties
in the library's context.
*/
struct _daveInterface {
_daveOSserialType fd; /* some handle for the serial interface */
int users; /* a counter used when multiple PLCs are accessed via */
/* the same serial interface and adapter. */
int localMPI; /* the adapter's MPI address */
char * name; /* just a name that can be used in programs dealing with multiple */
/* daveInterfaces */
int timeout; /* Timeout in microseconds used in transort. */
int protocol; /* The kind of transport protocol used on this interface. */
int speed; /* The MPI or Profibus speed */
int ackPos; /* position of some packet number that has to be repeated in ackknowledges */
_initAdapterFunc initAdapter; /* pointers to the protocol */
_connectPLCFunc connectPLC; /* specific implementations */
_disconnectPLCFunc disconnectPLC; /* of these functions */
_disconnectAdapterFunc disconnectAdapter;
_exchangeFunc exchange;
_listReachablePartnersFunc listReachablePartners;
};
daveInterface * daveNewInterface(_daveOSserialType nfd, char * nname, int localMPI, int protocol, int speed);
/*
A special header for MPI packets:
*/
typedef struct {
uc src_conn;
uc dst_conn;
uc MPI;
uc localMPI;
uc len;
uc func;
uc packetNumber;
} MPIheader;
/*
This is the packet header used by IBH ethernet NetLink.
*/
typedef struct {
uc ch1; // logical connection or channel ?
uc ch2; // logical connection or channel ?
uc len; // number of bytes counted from the ninth one.
uc packetNumber; // a counter, response packets refer to request packets
us sFlags; // my guess
us rFlags; // my interpretation
} IBHpacket;
/*
This holds data for a PLC connection;
*/
struct _daveConnection {
daveInterface * iface; /* pointer to used interface */
int MPIAdr; /* The PLC's address */
int messageNumber; /* current message number */
int needAckNumber; /* message number we need ackknowledge for */
int AnswLen; /* length of last message */
PDU rcvdPDU;
MPIheader templ; /* template of MPI Header, setup once, copied in and then modified */
uc msgIn[daveMaxRawLen];
uc msgOut[daveMaxRawLen];
uc * resultPointer; /* used to retrieve single values from the result byte array */
uc * _resultPointer;
uc packetNumber; /* packetNumber in transport layer */
int PDUstartO; /* position of PDU in outgoing messages. This is different for different transport methodes. */
int PDUstartI; /* position of PDU in incoming messages. This is different for different transport methodes. */
int rack; /* rack number for ISO over TCP */
int slot; /* slot number for ISO over TCP */
int maxPDUlength;
uc ackByte2;
};
/*
Setup a new connection structure using an initialized
daveInterface and PLC's MPI address.
*/
daveConnection * daveNewConnection(daveInterface * di, int MPI,int rack, int slot);
typedef struct {
uc type[2];
unsigned short count;
} daveBlockTypeEntry;
typedef struct {
unsigned short number;
uc type[2];
} daveBlockEntry;
typedef struct {
uc type[2];
uc x1[2]; /* 00 4A */
uc w1[2]; /* some word var? */
char pp[2]; /* allways 'pp' */
uc x2[4]; /* 00 4A */
unsigned short number; /* the block's number */
uc x3[26]; /* ? */
unsigned short length; /* the block's length */
uc x4[16];
uc name[8];
uc x5[12];
} daveBlockInfo;
PDU handling functions:
PDU is the central structure present in S7 communication.
It is composed of a 10 or 12 byte header,a parameter block and a data block.
When reading or writing values, the data field is itself composed of a data
header followed by payload data
typedef struct {
uc P; /* allways 0x32 */
uc type; /* Header type, one of 1,2,3 or 7. type 2 and 3 headers are two bytes longer. */
uc a,b; /* currently unknown. Maybe it can beused for long numbers? */
us number; /* A number. This can be used to make sure a received answer */
/* corresponds to the request with the same number. */
us plen; /* length of parameters which follow this header */
us dlen; /* length of data which follow the parameters */
uc result[2]; /* only present in type 2 and 3 headers. This contains error information. */
} PDUHeader;
set up the PDU header.
Needs valid header pointer in the struct p points to.
void _daveInitPDUheader(PDU * p, int type);
Add parameters after header
Adjust pointer to data. needs valid header.
void _daveAddParam(PDU * p,uc * param,us len);
add data after parameters
Set dlen needs valid header,and valid parameters.
void _daveAddData(PDU * p,void * data,int len);
Add values after value header in data
Adjust dlen and data count. Needs valid header,parameters,data,dlen
void _daveAddValue(PDU * p,void * data,int len);
Add data in user data.
Add a user data header, if not yet present.
void _daveAddUserData(PDU * p, uc * da, int len);
Build PDU for a read request
void _daveConstructReadRequest(PDU *p, int area, int DBnum, int start, int bytes);
build PDU for a BIT read request
void _daveConstructBitReadRequest(PDU *p, int area, int DBnum, int start, int bytes);
build the PDU for a write request
void _daveConstructWriteRequest(PDU *p, int area, int DBnum, int start, int bytes,void * values);
build the PDU for a bit write request
void _daveConstructBitWriteRequest(PDU *p, int area, int DBnum, int start, int bytes,void * values);
set up pointers to the fields of a received message
int _daveSetupReceivedPDU(daveConnection * dc,PDU * p);
send PDU to PLC and retrieves the answer
int _daveExchange(daveConnection * dc,PDU *p);
Utilities:
Hex dump PDU:
void _daveDumpPDU(PDU * p);
Compare blocks:
This is an extended memory compare routine. It can handle don't care and stop flags
in the sample data. A stop flag lets it return success, if there were no mismatches
up to this point.
int _daveMemcmp(us * a, uc *b, size_t len);
Hex dump:
Writes the name followed by len bytes written in hex and a newline.
void _daveDump(char * name,uc*b,int len);
names for Objects
char * daveBlockName(uc bn);
char * daveAreaName(uc n);
Data conversion convenience functions:
int daveGetByte(daveConnection * dc);
float daveGetFloat(daveConnection * dc);
int daveGetInteger(daveConnection * dc);
unsigned int daveGetDWORD(daveConnection * dc);
unsigned int daveGetUnsignedInteger(daveConnection * dc);
unsigned int daveGetWORD(daveConnection * dc);
int daveGetByteat(daveConnection * dc, int pos);
unsigned int daveGetWORDat(daveConnection * dc, int pos);
unsigned int daveGetDWORDat(daveConnection * dc, int pos);
float daveGetFloatat(daveConnection * dc, int pos);
float toPLCfloat(float ff);
short bswap_16(short ff);
int bswap_32(int ff);
Newer data conversion convenience functions:
Newer conversion routines. As the terms WORD, INT, INTEGER etc have different meanings
for users of different programming languages and compilers, I choose to provide a new
set of conversion routines named according to the bit length of the value used. The 'U'
or 'S' stands for unsigned or signed.
Get a value from the position b points to
B is typically a pointer to a buffer that has
been filled with daveReadBytes:
int daveGetS8from(uc *b);
int daveGetU8from(uc *b);
int daveGetS16from(uc *b);
int daveGetU16from(uc *b);
int daveGetS32from(uc *b);
unsigned int daveGetU32from(uc *b);
float daveGetFloatfrom(uc *b);
Get a value from the current position
in the last result read on the connection dc.
This will increment an internal pointer, so the next value is read from the position
following this value.
int daveGetS8(daveConnection * dc);
int daveGetU8(daveConnection * dc);
int daveGetS16(daveConnection * dc);
int daveGetU16(daveConnection * dc);
int daveGetS32(daveConnection * dc);
unsigned int daveGetU32(daveConnection * dc);
Get a value from a given position in the last result read on the connection dc.
int daveGetS8at(daveConnection * dc, int pos);
int daveGetU8at(daveConnection * dc, int pos);
int daveGetS16at(daveConnection * dc, int pos);
int daveGetU16at(daveConnection * dc, int pos);
int daveGetS32at(daveConnection * dc, int pos);
unsigned int daveGetU32at(daveConnection * dc, int pos);
put one byte into buffer b:
uc * davePut8(uc *b,int v);
uc * davePut16(uc *b,int v);
uc * davePut32(uc *b,int v);
uc * davePutFloat(uc *b,float v);
void davePut8at(uc *b, int pos, int v);
void davePut16at(uc *b, int pos, int v);
void davePut32at(uc *b, int pos, int v);
void davePutFloatat(uc *b,int pos, float v);
/**
Timer and Counter conversion functions:
**/
/*
get time in seconds from current read position:
*/
float daveGetSeconds(daveConnection * dc);
/*
get time in seconds from random position:
*/
float daveGetSecondsAt(daveConnection * dc, int pos);
/*
get counter value from current read position:
*/
int daveGetCounterValue(daveConnection * dc);
/*
get counter value from random read position:
*/
int daveGetCounterValueAt(daveConnection * dc,int pos);
/*
Functions to load blocks from PLC:
*/
void _daveConstructUpload(PDU *p,char blockType, int blockNr);
void _daveConstructDoUpload(PDU * p, int uploadID);
void _daveConstructEndUpload(PDU * p, int uploadID);
/*
Get the PLC's order code as ASCIIZ. Buf must provide space for
21 characters at least.
*/
#define daveOrderCodeSize 21
int daveGetOrderCode(daveConnection * dc,char * buf);
/*
connect to a PLC. returns 0 on success.
*/
int daveConnectPLC(daveConnection * dc);
/*
Read len bytes from the PLC. Start determines the first byte.
Area denotes whether the data comes from FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS. The reading and writing of other data
like timers and counters is not supported.
DB is the number of the data block to be used. Set it to zero
for other area types.
Buffer is a pointer to a memory block provided by the calling
program. If the pointer is not NULL, the result data will be copied thereto.
Hence it must be big enough to take up the result.
In any case, you can also retrieve the result data using the get macros
on the connection pointer.
FIXME: Existence of DB is not checked.
There is no error message for nonexistent data blocks.
There is no check for max. message len or
automatic splitting into multiple messages.
*/
int daveReadBytes(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
/*
Write len bytes from buffer to the PLC.
Start determines the first byte.
Area denotes whether the data goes to FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS. The writing of other data
like timers and counters is not supported.
DB is the number of the data block to be used. Set it to zero
for other area types.
FIXME: Existence of DB is not checked.
There is no error message for nonexistent data blocks.
There is no check for max. message len or
automatic splitting into multiple messages.
*/
int daveWriteBytes(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
/*
Bit manipulation:
*/
int daveReadBits(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
int daveWriteBits(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
/*
PLC diagnostic and inventory functions:
*/
int daveReadSZL(daveConnection * dc, int ID, int index, void * buf);
int daveListBlocksOfType(daveConnection * dc,uc type,daveBlockEntry * buf);
int daveListBlocks(daveConnection * dc,daveBlockTypeEntry * buf);
/*
PLC program read functions:
*/
int initUpload(daveConnection * dc,char blockType, int blockNr, int * uploadID);
int doUpload(daveConnection*dc, int * more, uc**buffer, int*len, int uploadID);
int endUpload(daveConnection*dc, int uploadID);
/*
Multiple variable support:
*/
typedef struct {
int error;
int length;
uc * bytes;
} daveResult;
typedef struct {
int numResults;
daveResult * results;
} daveResultSet;
/* use this to initialize a multivariable read: */
void davePrepareReadRequest(daveConnection * dc, PDU *p);
/* Adds a new variable to a prepared request: */
void daveAddVarToReadRequest(PDU *p, int area, int DBnum, int start, int bytes);
/* Executes the complete request. */
int daveExecReadRequest(daveConnection * dc, PDU *p, daveResultSet * rl);
/* Lets the functions daveGet work on the n-th result: */
int daveUseResult(daveConnection * dc, daveResultSet rl, int n);
/* Frees the memory occupied by the result structure */
void daveFreeResults(daveResultSet * rl);
int daveInitAdapter(daveInterface * di);
int daveConnectPLC(daveConnection * dc);
int daveDisconnectPLC(daveConnection * dc);
int daveDisconnectAdapter(daveInterface * di);
int daveListReachablePartners(daveInterface * di,char * buf);
int _daveInitAdapterDummy(daveInterface * di);
int _daveConnectPLCDummy(daveConnection * dc);
int _daveDisconnectPLCDummy(daveConnection * dc);
int _daveDisconnectAdapterDummy(daveInterface * di);
int _daveExchangeDummy(daveConnection * dc,PDU * p1);
int _daveListReachablePartnersDummy(daveInterface * di,char * buf);
/* MPI specific functions */
#define daveMPIReachable 0x30
#define daveMPIunused 0x10
#define davePartnerListSize 126
int _daveListReachablePartnersMPI(daveInterface * di,char * buf);
int _daveInitAdapterMPI1(daveInterface * di);
int _daveInitAdapterMPI2(daveInterface * di);
int _daveConnectPLCMPI1(daveConnection * dc);
int _daveConnectPLCMPI2(daveConnection * dc);
int _daveDisconnectPLCMPI(daveConnection * dc);
int _daveDisconnectAdapterMPI(daveInterface * di);
int _daveExchangeMPI(daveConnection * dc,PDU * p1);
/* ISO over TCP specific functions */
int _daveExchangeTCP(daveConnection * dc,PDU * p1);
int _daveConnectPLCTCP(daveConnection * dc);
/*
make internal PPI functions available for experimental use:
*/
int _daveExchangePPI(daveConnection * dc,PDU * p1);
void _daveSendYOURTURN(daveConnection * dc);
void _daveSendLength(daveInterface * di, int len);
void _daveSendIt(daveInterface * di, uc * b, int size);
int _daveReadChars(daveInterface * di, uc *b, int tmo, int max);
/*
make internal MPI functions available for experimental use:
*/
int _daveReadMPI(daveInterface * di, uc *b);
void _daveSendSingle(daveInterface * di, uc c);
int _daveSendAck(daveConnection * dc, int nr);
int _daveGetAck(daveInterface*di, int nr);
int _daveSendDialog2(daveConnection * dc, int size);
int _daveSendWithCRC(daveInterface * di, uc *b, int size);
int _daveReadSingle(daveInterface * di);
int _daveReadOne(daveInterface * di, uc *b);
typedef uc * (*userReadFunc) (int , int, int, int, int *);
typedef void (*userWriteFunc) (int , int, int, int, int *,uc *);
extern userReadFunc readCallBack;
extern userWriteFunc writeCallBack;
void _daveConstructReadResponse(PDU * p);
void _daveConstructWriteResponse(PDU * p);
void _daveConstructBadReadResponse(PDU * p);
void _daveHandleRead(PDU * p1,PDU * p2);
void _daveHandleWrite(PDU * p1,PDU * p2);
//void _daveSendMPIAck2(daveConnection *dc);
#endif /* _nodave */
/*
Changes:
07/19/04 added the definition of daveExchange().
09/09/04 applied patch for variable Profibus speed from Andrew Rostovtsew.
09/09/04 applied patch from Bryan D. Payne to make this compile under Cygwin and/or newer gcc.
12/09/04 added daveReadBits(), daveWriteBits()
12/09/04 added some more comments.
12/09/04 changed declaration of _daveMemcmp to use typed pointers.
*/