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:

NameMeaning
daveProtoMPIMPI for S7 300/400
daveProtoMPI2MPI for S7 300/400, "Andrew's version"
daveProtoMPI3MPI for S7 300/400, The version Step7 uses. Not yet implemented.
daveProtoPPIPPI for S7 200
daveProtoISOTCPISO over TCP
daveProtoISOTCP243ISO over TCP with CP243
daveProtoIBHMPI 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. ends the transmission of a part of a code block from PLC to programmer
NameMeaning
daveFuncOpenS7Connectionconnect to a PLC, negotiate PDU length
daveFuncReadmarks a read requeast
daveFuncWritemarks a write requeast
daveFuncStartUploadinitiates the transmission of a code block from PLC to programmer
daveFuncUploadcontinues the transmission of a part of a code block from PLC to programmer
daveFuncEndUpload

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.
NameMeaning
daveSysInfo 0x3System info of 200 family
daveSysFlagsSystem flags of 200 family
daveAnaInanalog inputs of 200 family
daveAnaOutanalog outputs of 200 family
daveInputsInput image memory
daveOutputsOutput image memory
daveFlagsFlags (Merker) area
daveDBData blocks in 300 and 400, V-Memory in 200
daveCounterCounters in 300 and 400
daveTimerTimers 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.
NameValueMeaning
daveResOK0all ok
daveResMultipleBitsNotSupported6CPU tells it does not support to read a bit block with a length other than 1 bit
daveResItemNotAvailable2003means 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.
daveResItemNotAvailable10 means a a piece of data is not available in the CPU, e.g. when trying to read a non existing DB
daveAddressOutOfRange5means 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:

daveDebugRawReadShow the single bytes received
daveDebugSpecialCharsShow when special chars are read
daveDebugRawWriteShow the single bytes written
daveDebugListReachablesShow the steps when determine devices in MPI net
daveDebugInitAdapterShow the steps when Initilizing the MPI adapter
daveDebugConnectShow the steps when connecting a PLC
daveDebugPacket
daveDebugByte
daveDebugCompare
daveDebugExchange
daveDebugPDUdebug PDU handling
daveDebugUploaddebug PDU loading program blocks from PLC
daveDebugMPI
daveDebugPrintErrorsPrint error messages
daveDebugPassive
daveDebugAllEnables 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.
*/