Kyle Hayes edited this page Dec 2, 2018 · 16 revisions

C API Documentation (2.0 API)

This page describes the C API. Please see the examples directory in the source for code.

A tag is a named data element in a PLC. Use of the term named might be a little strong. Some protocols use numbered data files or just numbered register addresses. Older PLCs such as AB PLC/5, SLC and MicroLogix use a form of logical addressing.

The library provides access to individual tags (or fields within them) with PLCs. The tag is the fundamental unit that the library uses. The PLCs themselves are not represented directly in the library API. This is to easier support applications that need to monitor and manipulate data across a network of PLCs.

Tags are not transparent to the network. You must read a tag explicitly in order to get the data from the PLC. Similarly any changes to a tag's data must be explicitly written.

Tags are treated like arrays. A tag that contains a single data element is treated like an array of size one.

Most functions return a status code. It will be one of the following:

  • PLCTAG_STATUS_PENDING (1) - Operation in progress. Not an error.
  • PLCTAG_STATUS_OK (0) - No error.
  • PLCTAG_ERR_ABORT (-1) - The operation was aborted.
  • PLCTAG_ERR_BAD_CONFIG (-2) - the operation failed due to incorrect configuration. Usually returned from a remote system.
  • PLCTAG_ERR_BAD_CONNECTION (-3) - the connection failed for some reason. This can mean that the remote PLC was power cycled, for instance.
  • PLCTAG_ERR_BAD_DATA (-4) - the data received from the remote PLC was undecipherable or otherwise not able to be processed. Can also be returned from a remote system that cannot process the data sent to it.
  • PLCTAG_ERR_BAD_DEVICE (-5) - usually returned from a remote system when something addressed does not exist.
  • PLCTAG_ERR_BAD_GATEWAY (-6) - usually returned when the library is unable to connect to a remote system.
  • PLCTAG_ERR_BAD_PARAM (-7) - a common error return when something is not correct with the tag creation attribute string.
  • PLCTAG_ERR_BAD_REPLY (-8) - usually returned when the remote system returned an unexpected response.
  • PLCTAG_ERR_BAD_STATUS (-9) - usually returned by a remote system when something is not in a good state.
  • PLCTAG_ERR_CLOSE (-10) - an error occurred trying to close some resource.
  • PLCTAG_ERR_CREATE (-11) - an error occurred trying to create some internal resource.
  • PLCTAG_ERR_DUPLICATE (-12) - an error returned by a remote system when something is incorrectly duplicated (i.e. a duplicate connection ID).
  • PLCTAG_ERR_ENCODE (-13) - an error was returned when trying to encode some data such as a tag name.
  • PLCTAG_ERR_MUTEX_DESTROY (-14) - an internal library error. It would be very unusual to see this.
  • PLCTAG_ERR_MUTEX_INIT (-15) - as above.
  • PLCTAG_ERR_MUTEX_LOCK (-16) - as above.
  • PLCTAG_ERR_MUTEX_UNLOCK (-17) - as above.
  • PLCTAG_ERR_NOT_ALLOWED (-18) - often returned from the remote system when an operation is not permitted.
  • PLCTAG_ERR_NOT_FOUND (-19) - often returned from the remote system when something is not found.
  • PLCTAG_ERR_NOT_IMPLEMENTED (-20) - returned when a valid operation is not implemented.
  • PLCTAG_ERR_NO_DATA (-21) - returned when expected data is not present.
  • PLCTAG_ERR_NO_MATCH (-22) - similar to NOT_FOUND.
  • PLCTAG_ERR_NO_MEM (-23) - returned by the library when memory allocation fails.
  • PLCTAG_ERR_NO_RESOURCES (-24) - returned by the remote system when some resource allocation fails.
  • PLCTAG_ERR_NULL_PTR (-25) - usually an internal error, but can be returned when an invalid handle is used with an API call.
  • PLCTAG_ERR_OPEN (-26) - returned when an error occurs opening a resource such as a socket.
  • PLCTAG_ERR_OUT_OF_BOUNDS (-27) - usually returned when trying to write a value into a tag outside of the tag data bounds.
  • PLCTAG_ERR_READ (-28) - returned when an error occurs during a read operation. Usually related to socket problems.
  • PLCTAG_ERR_REMOTE_ERR (-29) - an unspecified or untranslatable remote error causes this.
  • PLCTAG_ERR_THREAD_CREATE (-30) - an internal library error. If you see this, it is likely that everything is about to crash.
  • PLCTAG_ERR_THREAD_JOIN (-31) - another internal library error. It is very unlikely that you will see this.
  • PLCTAG_ERR_TIMEOUT (-32) - an operation took too long and timed out.
  • PLCTAG_ERR_TOO_LARGE (-33) - more data was returned than was expected.
  • PLCTAG_ERR_TOO_SMALL (-34) - insufficient data was returned from the remote system.
  • PLCTAG_ERR_UNSUPPORTED (-35) - the operation is not supported on the remote system.
  • PLCTAG_ERR_WINSOCK (-36) - a Winsock-specific error occurred (only on Windows).
  • PLCTAG_ERR_WRITE (-37) - an error occurred trying to write, usually to a socket.

Creating a Tag

In order to create a tag, you must know what protocol you are going to use and any arguments that that protocol requires. The entire set of information for accessing a tag is contained in a string that is passed to the plc_tag_create function. This string is formatted in a manner similar to a URL. It is composed of key-value pairs delimited by ampersands.

int32_t plc_tag_create(const char *attrib_str, int timeout);

Returns an integer handle to a tag in most cases. If there was an error that prevented any creation of the tag at all (i.e. no memory), a negative value will be returned. The value will be one of the above errors.

The attrib_str argument depends on the protocol type. It is not copied or kept internally, the caller is responsible for managing it. For AB EIP, the following values apply:

Allen-Bradley EIP-specific Options

  • protocol=ab_eip - Required. Currently only ab_eip supported.
  • gateway= - Required. IP address of the gateway for this protocol. Could be the IP address of the PLC you want to access.
  • path=1,0 - Required for LGX, Optional for PLC/SLC/MLGX IOI path to access the PLC from the gateway. This example translates as BACKPLANE->SLOT 0.
  • cpu=LGX - Required. Case-insensitive. Can be any one of compactlogix, clgx, lgx, controllogix, contrologix (I kant spel), flexlogix, or flgx.
  • elem_size=4 - Required. The size of an element in bytes. The tag is assumed to be composed of elements of the same size. For structure tags, use the total size of the structure.
  • elem_count=10 - Optional. The number of elements in the array. This defaults to one (1).
  • name=myDINTArray - Required The textual name of the tag to access. The name is anything allowed by the protocol. E.g. myDataStruct.rotationTimer.ACC, myDINTArray[42] etc.
  • debug=1-5 - Optional allows the selection of varying levels of debugging output. 1 shows only the more urgent problems. 5 shows almost every action within the library and will generate a very large amount of output. Generally 3 or 4 is most useful when debugging.


int32_t tag = plc_tag_create("protocol=ab_eip&gateway=,0&cpu=LGX&elem_size=4&elem_count=10&name=myDINTArray", 1000);

Destroying a Tag

Tags use internal resources and must be freed. DO NOT use free(). Tags are more than a single block of memory internally (though we'd like that level of simplicity). To free a tag, the plt_tag_destroy function must be called:

int plc_tag_destroy(int32_t tag);

Returns an error code from above.

Controlling Multithreaded Access to a Tag

The library is designed for multithreaded use. But only within single API calls. So, you need to wrap access to API functions with a mutex or something similar if you want to guard access to sequences of API calls. The C API provides plc_tag_lock and plc_tag_unlock for use with C/C++. However, if you are using a wrapper like Java, you should use the native synchronization facilities of that language!

In the example code, there is a small program, multithread.c, that illustrates the use of the built-in locking.

int plc_tag_lock(int32_t tag);
int plc_tag_unlock(int32_t tag);

Returns an error code.

Use it like this (example taken from multithread_plc5.c):

	do {
		rc = plc_tag_lock(tag);
		if(rc != PLCTAG_STATUS_OK) {
			value = 1000;
			break; /* punt, no lock */

		rc = plc_tag_read(tag, DATA_TIMEOUT);
		if(rc != PLCTAG_STATUS_OK) {
			value = 1001;
		} else {
			value =  plc_tag_get_float32(tag,0);
			/* increment the value */
			value = (value > 500.0 ? 0.0 : value + 1.5);
			/* yes, we should be checking this return value too... */
			plc_tag_set_float32(tag, 0, value);
			/* write the value */
			rc = plc_tag_write(tag, DATA_TIMEOUT);
		/* yes, we should look at the return value */
	} while(0);

Aborting an Operation

In some cases, the application using the library may decide to termination a pending operation on the tag. For instance, if the tag operation was done without a timeout value, the application will do the timeout itself and will call the plc_tag_abort function to stop any operation in progress. This frees up resources internally.

Though this operation immediately returns, it may take some amount of time for the operation to clear any internal queues if it was in flight at the time of the call. This is generally a very short period of time (milliseconds) and will not usually impact subsequent API calls.

int plc_tag_abort(int32_t tag);

Returns an error code from above.

Reading a Tag

Reading a tag brings the data at the time of read into the local memory of the PC running the library. The data is not automatically kept up to date. If you need to find out the data periodically, you need to read the tag periodically.

int plc_tag_read(int32_t tag, int timeout);


  • tag - the handle to the tag created by calling plc_tag_create.
  • timeout - A timeout value in milliseconds. If the value is zero, the function will set up the read request and immediately return. If the timeout value is greater than zero, then the function will wait up to that number of milliseconds for the read operation to complete. If the read does not complete in the timeout period, the operation will be aborted and the function will return PLCTAG_ERR_TIMEOUT.

Returns the status code (one of the ones above). PLCTAG_STATUS_OK will be returned if the read operation completed successfully. If the timeout was zero, the function will usually return PLCTAG_STATUS_PENDING. If there was a failure or error, the negative appropriate status code will be returned.

Retrieving Tag Status

The status of a tag can be retrieved with the plc_tag_status function. If there is an operation pending, this will return PLCTAG_STATUS_PENDING. All API operations set the tag status.

int plc_tag_status(int32_t tag);

Returns the status code representing the current tag status.

Writing a Tag

A tag can be written to the target PLC with the plc_tag_write function. The plc_tag_set_XXX functions should be called first in order to set the data.

Note: If you are accessing a tag in a Logix-class PLC, the type encoding rules for AB are quite arcane. In order to work around this, if you write before reading, a read will be transparently done first to get the type encoding data for the tag.

As for plc_tag_read, a timeout value is passed. If it is zero, then the write operation queues the operation for later completion and returns immediately.

int plc_tag_write(int32_t tag, int timeout);

Returns the status of the operation. Return values as for plc_tag_read.

Retrieving Tag Size

The plc_tag_size function retrieves the size of the tag in bytes. If there is an error (they are negative) that will be returned instead.

int plc_tag_get_size(int32_t tag);

Returns The size of the tag data in bytes or the error code (negative values).

Data Accessor Functions

The following functions support getting and setting integer and floating point values from or in a tag.

The getter functions return the value of the size in the function name from the tag at the byte offset in the tag's data.

The setter functions do the opposite and put the passed value (using the appropriate number of bytes) at the passed byte offset in the tag's data.

Unsigned getters return the appropriate UINT_MAX value for the type size on error. I.e. plc_tag_get_uint16 returns 65535 if there is an error. You can check for this value and then call plc_tag_status to determine what went wrong. Signed getters return the appropriate INT_MIN value on error.

Setters return one of the status codes above. If there is no error then PLCTAG_STATUS_OK will be returned.

NOTE the implementation of the floating point getter and setter may not be totally portable. Please test before use on big-endian machines.

All getters and setters convert their data into the correct endian type. A setter will convert the host endian data into the target endian data. A getter will convert the target endian data it retrieves into the host endian format.

uint64_t plc_tag_get_uint64(int32_t tag, int offset);
int plc_tag_set_uint64(int32_t tag, int offset, uint64_t val);

int64_t plc_tag_get_int64(int32_t tag, int offset);
int plc_tag_set_int64(int32_t, int offset, int64_t val);

uint32_t plc_tag_get_uint32(int32_t tag, int offset);
int plc_tag_set_uint32(int32_t tag, int offset, uint32_t val);

int32_t plc_tag_get_int32(int32_t tag, int offset);
int plc_tag_set_int32(int32_t, int offset, int32_t val);

uint16_t plc_tag_get_uint16(int32_t tag, int offset);
int plc_tag_set_uint16(int32_t tag, int offset, uint16_t val);

int16_t plc_tag_get_int16(int32_t tag, int offset);
int plc_tag_set_int16(int32_t, int offset, int16_t val);

uint8_t plc_tag_get_uint8(int32_t tag, int offset);
int plc_tag_set_uint8(int32_t tag, int offset, uint8_t val);

int8_t plc_tag_get_int8(int32_t tag, int offset);
int plc_tag_set_int8(int32_t, int offset, int8_t val);

double plc_tag_get_float64(int32_t tag, int offset);
int plc_tag_set_float64(int32_t tag, int offset, double val);

float plc_tag_get_float32(int32_t tag, int offset);
int plc_tag_set_float32(int32_t tag, int offset, float val);
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.