Home / PLC / Allen Bradley / How to communicate to an Allen Bradley Plc with C# and LibPlcTag Ethernet/IP library

How to communicate to an Allen Bradley Plc with C# and LibPlcTag Ethernet/IP library

LibPlcTag is a library that I used recently to communicate with Allen Bradley plc. It’s a C++ open source library and it can communicate with most of the Allen Bradley plcs, like Micrologix, CompactLogix, ControlLogix, SLC and Plc5.

LibPlcTag works on the Ethernet/Ip stack and is LGPL licensed (you are not forced to open source the entire project, free for commercial use).

This library is very easy to use and in this article we will see how to use a C# wrapper that I wrote time ago.

The C/C++ source code of Libplctag is hosted on Github, along with the documentation and issue tracker: https://github.com/kyle-github/libplctag

The C# wrapper is also on GitHub

Watch the video on Youtube

How to compile LibPlcTag

LibPlcTag can be compiled with CMake. You can read the documentation with all the steps on the Build.md file.

C# Wrapper

When you use a native C/C++ library in a C# project, you need a wrapper that contains the P/Invoke of the functions exposed by the C++ library.

There are several approaches that can be taken when writing a wrapper. I personally don’t like to expose C/C++ native methods, instead I usually keep all interop boilerplate inside the wrapper and expose only plain C# functions.

Just for reference, here are the wrapped functions of the C/C++ library. You can find the documentation about these functions on Libplctag Wiki.

[DllImport("plctag.dll", EntryPoint = "plc_tag_create", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr plc_tag_create([MarshalAs(UnmanagedType.LPStr)] string lpString);

[DllImport("plctag.dll", EntryPoint = "plc_tag_destroy", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_destroy(IntPtr tag);

[DllImport("plctag.dll", EntryPoint = "plc_tag_status", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_status(IntPtr tag);

[DllImport("plctag.dll", EntryPoint = "plc_tag_decode_error", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr plc_tag_decode_error(int error);

[DllImport("plctag.dll", EntryPoint = "plc_tag_read", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_read(IntPtr tag, int timeout);

[DllImport("plctag.dll", EntryPoint = "plc_tag_write", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_write(IntPtr tag, int timeout);

[DllImport("plctag.dll", EntryPoint = "plc_tag_get_uint16", CallingConvention = CallingConvention.Cdecl)]
static extern ushort plc_tag_get_uint16(IntPtr tag, int offset);

[DllImport("plctag.dll", EntryPoint = "plc_tag_get_int16", CallingConvention = CallingConvention.Cdecl)]
static extern short plc_tag_get_int16(IntPtr tag, int offset);

[DllImport("plctag.dll", EntryPoint = "plc_tag_set_uint16", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_set_uint16(IntPtr tag, int offset, ushort val);

[DllImport("plctag.dll", EntryPoint = "plc_tag_set_int16", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_set_int16(IntPtr tag, int offset, short val);

[DllImport("plctag.dll", EntryPoint = "plc_tag_get_uint8", CallingConvention = CallingConvention.Cdecl)]
static extern byte plc_tag_get_uint8(IntPtr tag, int offset);

[DllImport("plctag.dll", EntryPoint = "plc_tag_get_int8", CallingConvention = CallingConvention.Cdecl)]
static extern sbyte plc_tag_get_int8(IntPtr tag, int offset);

[DllImport("plctag.dll", EntryPoint = "plc_tag_set_uint8", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_set_uint8(IntPtr tag, int offset, byte val);

[DllImport("plctag.dll", EntryPoint = "plc_tag_set_int8", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_set_int8(IntPtr tag, int offset, sbyte val);

[DllImport("plctag.dll", EntryPoint = "plc_tag_get_float32", CallingConvention = CallingConvention.Cdecl)]
static extern float plc_tag_get_float32(IntPtr tag, int offset);

[DllImport("plctag.dll", EntryPoint = "plc_tag_set_float32", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_set_float32(IntPtr tag, int offset, float val);

[DllImport("plctag.dll", EntryPoint = "plc_tag_get_uint32", CallingConvention = CallingConvention.Cdecl)]
static extern uint plc_tag_get_uint32(IntPtr tag, int offset);

[DllImport("plctag.dll", EntryPoint = "plc_tag_get_int32", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_get_int32(IntPtr tag, int offset);

[DllImport("plctag.dll", EntryPoint = "plc_tag_set_uint32", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_set_uint32(IntPtr tag, int offset, uint val);

[DllImport("plctag.dll", EntryPoint = "plc_tag_set_int32", CallingConvention = CallingConvention.Cdecl)]
static extern int plc_tag_set_int32(IntPtr tag, int offset, int val);

How to use the wrapper

To use the wrapper you have to add the project manually to your sources. Just copy-paste the project in your solution folder, Right-click on Solution -> add existing project -> select libplctag.csproj file. Then add a reference.

Create a Tag

Libplctag is built around the concept of tags. A tag is a memory region on the plc that you can read/write. To create a Tag you have to specify the name (that is the initial address), element size (in bytes) and number of consecutive elements that you want to read/write.

  • Name: name of the tag in the plc, like N7:0, F8:10, I0:0.2, etc…
  • ElementSize: size of a single element of the tag. For example a float in F8 is 4 bytes, a numeric in N7 is 2 bytes, etc…
  • ElementCount: number of consecutive elements that we want to read

To create a Tag you can use two constructors.
One for PLC5/SLC/MicroLogix, where you don’t have to specify the path.
The second one is for CompactLogix and ControlLogix plc, where you have to specify the path.

/// <summary>
/// Creates a tag for PLC5 / SLC / MicroLogix processor types (you don't need 
/// to specify the path)
/// </summary>
public Tag(string ipAddress, CpuType cpuType, string name, int elementSize, 
    int elementCount, int debugLevel = 0)	

/// <summary>
/// Creates a tag. If the CPU type is LGX, the path has to be specified.
/// </summary>
public Tag(string ipAddress, string path, CpuType cpuType, string name, 
    int elementSize, int elementCount, int debugLevel = 0)

Here is an example on how to create a Tag for a MicroLogix plc. In this case we read the tag B3:0, 1 element:

// creates a tag to read B3:0, 1 item, from SLC ip address 192.168.0.100
var tag = new Tag("192.168.0.100", CpuType.SLC, "B3:0", DataType.Int16, 1);

Here is another example on how to create a tag for a ControlLogix/CompactLogix plc. In this case we read the first byte of a DINT array named TestDINTArray. As you see we have to specify the path.

// creates a tag to read B3:0, 1 item, from LGX ip address 192.168.0.100
var tag = new Tag("10.206.1.39", "1, 0", CpuType.LGX, "TestDINTArray[0]", 
    DataType.Int32, 1);

In case of CompactLogix/ControlLogix tags, you can also specify tags that are in different networks, like DH+, and on different plcs.
Let’s suppose we want to read a tag from a ControlLogix named TestDINTArray – the first 10 elements.
Then we want to read the tag F8:0 – first 4 elements – from a PLC5 connected to the ControlLogix with a DH+ network.
For the second tag we have to specify the nodes where we find the DH+ slot and the PLC5.
Here is how we create the two tags:

// creates a tag to read TestDINTArray, 10 item, from controllogix
var tag1 = new Tag("10.206.1.39", "1, 0", CpuType.LGX, "TestDINTArray[0]", 
    DataType.Int32, 10);

// creates a tag to read F8:0, the first 4 consecutive elements, from PLC5 via DH+
// see https://github.com/kyle-github/libplctag/blob/master/src/examples/simple_dual.c
var tag2 = new Tag("10.206.1.39", "1,2,A:27:1", CpuType.PLC5, "F8:0", 
    DataType.Float32, 4);

Read tags

To read and write tags you have to create an instance of the plc client.

var client = new Libplctag();

Then tags has to be added to the client. After adding a tag, you have to call GetStatus to verify that the tag exists and the status code is ok.
In some cases the library may return “pending”. In that case you have to put a retry mechanism that call GetStatus again until the result is ok.

// create the tag (We want to read TestDINTArray[0], TestDINTArray[1], 
// TestDINTArray[2],TestDINTArray[3])
var tag = new Tag("10.206.1.39", "1, 0", CpuType.LGX, "TestDINTArray[0]", 
    DataType.Int32, 4);

// add the tag
client.AddTag(tag);

// check that the tag has been added, if it returns pending we have to retry
while (client.GetStatus(tag) == Libplctag.PLCTAG_STATUS_PENDING)
{
    Thread.Sleep(100);
}

// if the status is not ok, we have to handle the error
if (client.GetStatus(tag) != Libplctag.PLCTAG_STATUS_OK)
{
    Console.WriteLine($"Error setting up tag internal state. Error
        {client.DecodeError(client.GetStatus(tag))}\n");
    return;
}

After adding the tags and verifying that the status is ok, you can use the tags to read or write.

Read is done by calling the ReadTag method. After the read, you have to check that the status code returned by the ReadTag is ok. Then you have to convert the result to the format that you are interested in.

// Execute the read
var result = client.ReadTag(tag, DataTimeout);

// Check the read operation result
if (result != Libplctag.PLCTAG_STATUS_OK)
{
    Console.WriteLine($"ERROR: Unable to read the data! Got error code {result}: {client.DecodeError(result)}\n" );
    return;
}
// Convert the data
var TestDintArray0 = client.GetInt32Value(tag, 0 * tag.ElementSize); // multiply with tag.ElementSize to keep indexes consistant with the indexes on the plc
var TestDintArray1 = client.GetInt32Value(tag, 1 * tag.ElementSize);
var TestDintArray2 = client.GetInt32Value(tag, 2 * tag.ElementSize);
var TestDintArray3 = client.GetInt32Value(tag, 3 * tag.ElementSize);

// print to console
Console.WriteLine("TestDintArray0: " + TestDintArray0);
Console.WriteLine("TestDintArray1: " + TestDintArray1);
Console.WriteLine("TestDintArray2: " + TestDintArray2);
Console.WriteLine("TestDintArray3: " + TestDintArray3);

Write tags

Same as for Read tags, a tag has to be added and checked with GetStatus before it becomes possible to write it.

To write a tag we use the method WriteTag, but first we have to assign the value of the tag, converted of course.
Here is the example on how to write the first 4 elements of TestDINTArray.

// set values on the tag buffer
client.SetInt32Value(tag, 0 * tag.ElementSize, 10); // write 10 on TestDINTArray[0]
client.SetInt32Value(tag, 1 * tag.ElementSize, 20); // write 20 on TestDINTArray[1]
client.SetInt32Value(tag, 2 * tag.ElementSize, 30); // write 30 on TestDINTArray[2]
client.SetInt32Value(tag, 3 * tag.ElementSize, 40); // write 40 on TestDINTArray[3]
	
// write the values
result = client.WriteTag(tag, DataTimeout);

// check the result
if (result != Libplctag.PLCTAG_STATUS_OK)
{
    LogError($"ERROR: Unable to read the data! Got error code {rc}: {client.DecodeError(result)}\n" );
    return;
}

Close and cleanup resources

The Libplctag implements IDisposable, so you must call Dispose() before closing your program.
Dispose will remove all the tags and clear the dictionary. Internally in the C++ library, all tags pointers are destroyed and sockets are released.

Eventually if you want to remove a tag during the runtime you can use the method RemoveTag() to dispose it.

Value conversion

Here are the methods to convert the values contained in a tag.

public ushort GetUint16Value(Tag tag, int offset);
public void SetUint16Value(Tag tag, int offset, ushort value);

public short GetInt16Value(Tag tag, int offset);
public void SetInt16Value(Tag tag, int offset, short value);

public byte GetUint8Value(Tag tag, int offset);
public void SetUint8Value(Tag tag, int offset, byte value);

public sbyte GetInt8Value(Tag tag, int offset);
public void SetInt8Value(Tag tag, int offset, sbyte value);

public float GetFloat32Value(Tag tag, int offset);
public void SetFloat32Value(Tag tag, int offset, float value);

public uint GetUint32Value(Tag tag, int offset);
public void SetUint32Value(Tag tag, int offset, uint value);

public int GetInt32Value(Tag tag, int offset);
public void SetInt32Value(Tag tag, int offset, int value);

Decoding error codes

LibPlcTag returns several error codes. Error codes can be translated to a string by using DecodeError();
For example, this code prints the error code number and the description:

var result = ...
if (result != Libplctag.PLCTAG_STATUS_OK)
{
	string error = client.DecodeError(result);
    Console.WriteLine($"ERROR: Unable to read the data! Got error code" + result +": " + error);
    return;
}

Limitations and gotcha

You might have some problems with this library especially when you try to request more than the limit of bytes allowed in a single request (80 bytes if I remember well).
In that case the library just fails to read/write and returns you an error code.
This means that you have to check and decode the error code and understand what it means.
Also you have to execute multiple reads/writes depending on the limits of the processor.

Source code and examples

The repository with the wrapper and examples is on GitHub: https://github.com/mesta1/libplctag-csharp
The C++ library can be found also on GitHub: https://github.com/kyle-github/libplctag, along with the official examples in C++.

Getting help and documentation

If you need help with LibPlcTag, there is an official Google Group where you can find the developer and other users that can help you. Here is the link: https://groups.google.com/forum/#!forum/libplctag
The official LibPlcTag Wiki is at this addres: https://github.com/kyle-github/libplctag/wiki
And be sure to read the long description on the ReadMe file: https://github.com/kyle-github/libplctag/blob/master/README.md

155 comments

  1. Hello I was wondering, I was using rslogix 5000, studio 5000 emulate, and rslinx classic gateway. I have in emulate a softlogix driver configured to run an emulated plc on a virtual back plane. I can set up an OPC topic and copy the link into excel to read and write values. I tried to use the control logix set up to possibly get comunicaiton with MS visual studio working. my goal is to use 3d models in unity3d to interact based on tag values from logixs, and write to them as the operator interacts with model. following the video I dont get past creating a tag. i am using my computers IP address with the path 1,2 as the emulated plc is in slot 2. any pointers would be very helpful

    • If it helps anyone who comes across this, Studio 5000 Emulate is prevented by linx from having EIP communication. Where as if you use Softlogix 5800 you can then use this code just as if it was a control logix to talk with MS visual studio. I have been able to read and write tags using this example in softlogix chassis with my controller in slot 2 and Ethernet module in slot 3. The pathway i used is: var tag = new Tag(“10.210.4.26”, “1, 2”, CpuType.LGX, “Program:MainProgram.Start”, DataType.Int16, 1);

      to read and write to a program scope tag called Start which is an Int inside the MainProgram routine of my project, and my softlogix Ethernet module set for ip 10.210.4.26. using a virtual back plane i found with controller in slot 2 only worked using 1,2 as the path.

      • Anything special you needed to configure in RSLinx/SoftLogix to get this working Jonathon?
        I get “Error setting up tag internal state. Error PLCTAG_ERR_CREATE”
        Same setup as yours – RSLinx in slot 0 and 1, Softlogix in slot 2, EtherNet in slot 3 ( so pathway=”1,2″),
        Program scope tag Start as INT, “Accept UDP Messages on Ethernet Port” in RSLinx Classic is unchecked.

        • In RSLink just make sure you configure a driver for a virtual back plane. you would do this just the same as you would for an Ethernet driver except look for the option for virtual back plane. Then is rsLogix set up your controller as a softlogix controller (mine was “1789-L60 SoftLogix 5800 controller”) and set the path to where your controller is in your VB for me it was “AB_VBP-1\2” .

          shouldnt matter much but im usuing a virtual back plane type “1789-A17 17-slot softlogix virtual chassis”

        • Yep, have the same virtual chassis backplane 1789-A17/A with [2]1789-L60 CPU (Slot 2, Rev 21.11) and [3]EtherNet/IP (IP Address 192.168.1.13, Slot 3. Rev 21.1). Backplane driver is configured AB_VBP-1.

        • Excuse me, how do you resolve Error “PLCTAG_ERR_CREATE”??
          A`m using a Controllogix, with RS232, and my path say “COOMATION_RS232/1” and my tag is named “Start” (is a INT var)
          My estructure is:
          var tag = new Tag(“192.168.0.100”, “COOMATION_RS232, 1”, CpuType.LGX, “Program:MainProgram.Start”, DataType.Int16, 1)

          And I running the RSLinx and the RSLogix5000 from a VMware WorkStation
          If you cloud help me I really appreciate it

      • Application was running on a remote machine. Ran it on the machine running SoftLogix and it worked.

        • are you still getting any issues? i would be happy to send you a copy of the files i have with screen shots if you like? i currently have a modified version of this code with wrapper that when ran will look at the values of 2 local program scope tags “start, stop” and either start the rung or stop and reset the rung. it waits for a user key input to run a second time up to loop 5 times

          the rung it working on is a latch in run that turns on an tag called light.
          [XIC(Start),XIC(Light)]XIO(Stop)OTE(Light);
          read as “start or light” and not stop = light”

        • No issues since I ran it locally, thanks Jonathon.

        • Excuse me, did you use a VM for simulated that program?
          Because am using a VM were I have the PLC(Softlogix5800) in the slot 1, the emulated(AB_VPB-1, 1789-A17 / A Chasis virtual), the EIP SoftLogix5800 in the slot 2 and the RSlinx(gateway) in the slot 0, and the Visual Studio is in my real PC, butI have the error code (-3).
          This is my structure:
          (etiqueta var = etiqueta nueva (“10.206.1.39”, “1, 2”, CpuType.LGX, “Programa: MainProgram.TestDINTArray [0]”, DataType.DINT, 1)

          I really appreciate if you could help me

  2. I was wondering, I was able to get your wrapper working in my visual basic project to work with softlogix, however i was wanting to use the project in Unity3d to have my game objects move based on tags read from the “plc”. but when i build the project the same way in unity3d i dont get any compiler errors, but i dont see the messages to write to the console for the test to see if im reading tags, as well as writing tags does not seem to effect the running codes. any ideas what may be the issue? i see this has been done with S7 before but id like to get it working with allen bradly plcs

    • So my issue I am finding is that this wrapper uses a plctag.dll that is not written in C# and is considered an unmanaged plugin for unity3d. I have tried to place the .dll in the specified folders for native/unmanaged plugins with no success, i think this is because the .dll isnt written in either C# or C++ possibly?

  3. Hi . Could you tell me how to read DI DO ? like I:0.1 please ……

    • I can get a little messy and complicatied with the paths rather quickly if you are trying to find special data types like I O C for ethernet IP devices. it may be simpler in your rslogix code to “map” those bits to something else like a array of dints the same size. I wonder if that is any bit easier for you, but also what are your DI DO tags scopes like controller, or local program level?

  4. PLC :SLC5/05 .with Ethernet
    I want to read I:1.0
    when I used
    var tag = new Tag(“10.108.1.119”, CpuType.SLC, “I1:0.0”, DataType.Int16, 1);
    GetStatus = Libplctag.PLCTAG_STATUS_OK
    but when I ReadTag . return = -33
    How can I read DI …….
    or how can I read bit ?

    • Hey Rhine, Were you able to establish communication with PLC :SLC5/05 using this library?
      or using other one?
      can you comment?
      Thanks.

  5. hello, ive not tried this wrapper yet with a SLC5, this link https://github.com/kyle-github/libplctag/issues/64 may be semi relevant to your issue, though im not sure if it will solve the case. your error is “PLCTAG_ERR_REMOTE_ERR = -33;” which may imply a data size not matching possibly. it seams like an issue with communications i believe. If i find anymore info ill post it here

  6. Hi Jonathan,check the link below, You should use some sort of TCP bridge to get it done.
    https://answers.unity.com/questions/631060/unity3d-as-opc-client.html

    • Thank you rgeorge, I will give the library in the link a try though it doesn’t look very user friendly at first glance for the application i wish to use it for.

  7. If I want to write string to tag , How can i do?
    tag0 = new Tag(ip_address, “1, 0”, CpuType.LGX, tagName, DataType.String, elementCount);

    string x = “xxx”;
    client.SetInt32Value(tag0, i * tag0.ElementSize, x); ????

  8. How can i read parameters and local tag of individual program in compactLogix as i am able to read only controller tags

    • To get down to the local or “program” scope tags you need to include the path before the tag name such as: “Program:MainProgram.Start” this will reach the tag named “Start” in the “MainProgram” task/routine. The Pathway for reaching a local tag is very similar to how the tag path need to be made for a factory talk view HMI application.

      I am not sure what parameters you are hoping to see, but you can get the tag value back if you already know the tag name and path/scope, I have not been able to pull the tag description or data type out as these should be known before trying to read from to write to the tag in question.

  9. Hi Im a experienced dev but fairly new to .net to PLC world, i was just following along the video but Im keep getting -7 error, it isnt very helpful because it doesnt tell the exact reason, i can ping the PLC, I Logix Desiger 5000 connected to it on the same VM but my code fails when I try to read just a normal tag.
    Any help of advise will be greatly appreciated.

    static void Main(string[] args)
    {

    try
    {
    var tag = new Tag(“1**.1**.1**.1*”, CpuType.SLC, “FC_210.hmi_data.PV.Value”, DataType.REAL, 1);
    //FC_210.hmi_data.CV.Value

    using (var client = new Libplctag())
    {
    // add the tag
    client.AddTag(tag);

    // check that the tag has been added, if it returns pending we have to retry
    while (client.GetStatus(tag) == Libplctag.PLCTAG_STATUS_PENDING)
    {
    Thread.Sleep(100);
    }

    var test = client.GetStatus(tag); //THIS RETURNS -7

    // if the status is not ok, we have to handle the error
    if (client.GetStatus(tag) != Libplctag.PLCTAG_STATUS_OK)
    {
    Console.WriteLine($”Error setting up tag internal state. Error{ client.DecodeError(client.GetStatus(tag))}\n”);
    return;
    }

    // Execute the read
    var result = client.GetFloat32Value(tag, DataTimeout);

    // Check the read operation result
    if (result != Libplctag.PLCTAG_STATUS_OK)
    {
    Console.WriteLine($”ERROR: Unable to read the data! Got error code {result}: {“”}\n”);
    //return;
    }
    // Convert the data
    var TestDintArray0 = client.GetInt32Value(tag, 0 * tag.ElementSize); // multiply with tag.ElementSize to keep indexes consistant with the indexes on the plc

    // print to console
    Console.WriteLine(“TestDintArray0: ” + TestDintArray0);
    }
    }
    finally
    {
    Console.ReadKey();
    }

    }

    • hello Shaz, I was wondering about the tag you are trying to look at. Is it a User Defined Data Type in your plc as well as is it controller scope or program scope? about the error your getting, a return -33 should mean
      “#define PLCTAG_ERR_TOO_LARGE (-33)”
      which would lead me to think the size for the data types are not lined us quite right, which may be the case for UDT tags, though I havent tested much outside of normal tags on program and controller level.

      • Thank you for the help and advise.

        The tag resides under controller tags(FC_210=> FC_210.hmi_data=>FC_210.hmi_data.CV=>FC_210.hmi_data.CV.Value) and
        its datatype: real, style:float and value:12.0.

        When I step through the code it does return -7 in “var test = client.GetStatus(tag);”

        About PLC= vendor:Allen-Bradley, Type: 1756-L71S GuardLogix 5570 Saftly Controller.

        Please let me know if any other details are needed. Any help or suggestions are welcome.

        Thank you

    • Sorry Shaz, Your error code was -7 not -33, Here is the list of error code definitions from libplctag.h :
      /* library internal status. */
      #define PLCTAG_STATUS_PENDING (1)
      #define PLCTAG_STATUS_OK (0)

      #define PLCTAG_ERR_ABORT (-1)
      #define PLCTAG_ERR_BAD_CONFIG (-2)
      #define PLCTAG_ERR_BAD_CONNECTION (-3)
      #define PLCTAG_ERR_BAD_DATA (-4)
      #define PLCTAG_ERR_BAD_DEVICE (-5)
      #define PLCTAG_ERR_BAD_GATEWAY (-6)
      #define PLCTAG_ERR_BAD_PARAM (-7)
      #define PLCTAG_ERR_BAD_REPLY (-8)
      #define PLCTAG_ERR_BAD_STATUS (-9)
      #define PLCTAG_ERR_CLOSE (-10)
      #define PLCTAG_ERR_CREATE (-11)
      #define PLCTAG_ERR_DUPLICATE (-12)
      #define PLCTAG_ERR_ENCODE (-13)
      #define PLCTAG_ERR_MUTEX_DESTROY (-14)
      #define PLCTAG_ERR_MUTEX_INIT (-15)
      #define PLCTAG_ERR_MUTEX_LOCK (-16)
      #define PLCTAG_ERR_MUTEX_UNLOCK (-17)
      #define PLCTAG_ERR_NOT_ALLOWED (-18)
      #define PLCTAG_ERR_NOT_FOUND (-19)
      #define PLCTAG_ERR_NOT_IMPLEMENTED (-20)
      #define PLCTAG_ERR_NO_DATA (-21)
      #define PLCTAG_ERR_NO_MATCH (-22)
      #define PLCTAG_ERR_NO_MEM (-23)
      #define PLCTAG_ERR_NO_RESOURCES (-24)
      #define PLCTAG_ERR_NULL_PTR (-25)
      #define PLCTAG_ERR_OPEN (-26)
      #define PLCTAG_ERR_OUT_OF_BOUNDS (-27)
      #define PLCTAG_ERR_READ (-28)
      #define PLCTAG_ERR_REMOTE_ERR (-29)
      #define PLCTAG_ERR_THREAD_CREATE (-30)
      #define PLCTAG_ERR_THREAD_JOIN (-31)
      #define PLCTAG_ERR_TIMEOUT (-32)
      #define PLCTAG_ERR_TOO_LARGE (-33)
      #define PLCTAG_ERR_TOO_SMALL (-34)
      #define PLCTAG_ERR_UNSUPPORTED (-35)
      #define PLCTAG_ERR_WINSOCK (-36)
      #define PLCTAG_ERR_WRITE (-37)
      #define PLCTAG_ERR_PARTIAL (-38)

      Looks like -7 would be bad parameter, what type of PLC are you using? is it a compact logix or slc5 as the structure for looking at the tag changes.

      • Jonathon thank you for the help and advise.

        The tag resides under controller tags(FC_210=> FC_210.hmi_data=>FC_210.hmi_data.CV=>FC_210.hmi_data.CV.Value) and
        its datatype: real, style:float and value:12.0.

        When I step through the code it does return -7 in “var test = client.GetStatus(tag);”

        About PLC= vendor:Allen-Bradley, Type: 1756-L71S GuardLogix 5570 Saftly Controller.

        Please let me know if any other details are needed. Any help or suggestions are welcome.

        Thank you

  10. Thank you for the help and advise.

    The tag resides under controller tags(FC_210=> FC_210.hmi_data=>FC_210.hmi_data.CV=>FC_210.hmi_data.CV.Value) and
    its datatype: real, style:float and value:12.0.

    When I step through the code it does return -7 in “var test = client.GetStatus(tag);”

    About PLC= vendor:Allen-Bradley, Type: 1756-L71S GuardLogix 5570 Saftly Controller.

    Please let me know if any other details are needed. Any help or suggestions are welcome.

    Thank you

    • I saw a few errors in your syntax for the code you posted:

      in your argument you typed .PV. instead of .CV.
      var tag = new Tag(“1**.1**.1**.1*”, CpuType.SLC, “FC_210.hmi_data.PV.Value”, DataType.REAL, 1);
      //FC_210.hmi_data.CV.Value

      In addition the 1756-L71S GuardLogix 5570 Saftly Controller is listed a controllogix controller not a SLC so you need to use this type of statement:
      var tag = new Tag(“1**.1**.1**.1*”, “1, 0”, CpuType.LGX, “FC_210.hmi_data.PV.Value”,
      DataType.REAL, 1);

      have you tried this instead to see if it gives you any error codes? also note the path “1,0” is the address to the plc on the back plane.

  11. everything is nice with control logix PLC … I can read and write tags ….
    But it will more use full when we can access tags value without any events …yes i want to say live data from plc
    Is That Possible to get data when we once connect our custom c# application to plc and value update as per changed occurred in plc

    • Is it works with Micro 850? Please, can you post details?

    • The library does not provide directly this feature, but you can build on top of it another application to achieve what you need.
      Eg. Simply reading periodically tag values and storing them in a dictionary.

  12. Antonio Gallucci

    Hi and thanks for your effort in this project. I would ask if it is possible to read multiple tags, that aren’t in an array, in on request. Es. TAGTemperature and TAGPression.

    Thanks in advance.

    • Try to create methods for each tag you want to read… You can call multiple methods in on event

      • Antonio Gallucci

        In this case there is still a request for each reading operation. My goal is to read multiple tags (that are not in an array) with one request.

    • Yes u can read multiple tags, but create seperate tag variable for each tag, and add them into the tag list,which makes easy to read orwrite the tags in a loop.

  13. Thank you for your nice work….
    Please let me know if there any function or method that will call tag update method… Just like if any tag value update in plc that should be updated in c# application. Without any button press or event

    • Hai
      you can go for javascript method by setting a timer , which will call your C# read or write function based on the timer and renders the updated data on screen (minimum time limet should be more then 5 seconds for better performance to read 100 tags)

  14. if u need more details plz share your requiremnt to my mail id
    hmjagadeesh4@gmail.com

  15. hai can anyone let me know how to read/write string tags from ABB compact logix

  16. hi,
    can we get list of all tags present in compact logix to the c# application OR
    how to get list of all tags please let me know about this
    thanks in advance

  17. nice work ,
    any please can tell me how to read tag with datatype – string ,.please help

  18. Hi, I would be thankful if you can tell me how to read a String data type. thanks!

    • I forgot to say… Im currently using a CompactLogix.

      • Hi, the string is 88 bytes, the first 4 contains the size, 82 the bytes for the data and the last 2 are offset bytes.

        So to read a string you should read the first 4 as int32 number, to get the length of the string. Then read as many bytes as the length.

        Here is the code that I use to get the value of a string tag (naturally after that has been created and read from the plc):

        int size = client.GetInt32Value(tag, 0);
        int offset = DataType.Int32;
        string output = string.Empty;

        for (int i = 0; i < tag.ElementCount; i++)
        {
        var sb = new StringBuilder();

        for (int j = 0; j < size; j++)
        {
        sb.Append((char)client.GetUint8Value(tag, (i * tag.ElementSize) + offset + j));
        }

        output = sb.ToString();
        }

        • Thanks, that was very useful, since I did a investigation and the only thing I found was the size of the string. That way I ended up reading the 82 bytes and saving them in a char array, to finally concatenate them. So as you’ll see my method ended up being more complex than it should have.

        • Hai javier and antonio
          i have tried as per your guidelines but i am getting value as 0 at
          int size = client.GetInt32Value(tag, 0); and my tag status is OK
          can you let me know how you have declared string tag

        • Hi Jai.
          The way I’ve declared the string tag was:
          var tagID = new Tag(PLC_IP, PLC_Path, PLC_Type, “ID.DATA”, DataType.Int8, 82);
          On my case it was a little more tricky, ’cause i ended up saving every byte on a char array just to concatenate them at the end…
          Not sure if it is the best way to do it, but it worked for me.

        • I declared the string tag as follow:

          Tag tag = new Tag(PLC_IP, PLC_PATH, PLC_TYPE, “TagName”, DataType.String, 1);

          Where DataType.String is 88.

  19. Hi,
    I’m currently developing a Windows form app with this library … ’till this point, it worked well on my PC, the problem arises when I try to copy only the debug folder on a new PC to be able to test it. A pop-up message appears indicating the following text:

    Unable to load DLL “plctag.dll”: The specified module could not be found. (Exception from HRESULT: 0x8007007E).

    Does anyone have any clue that may help me with this situation? Thanks

    • Hi, I faced this problem recently. It depends on the machine architecture. If you compiled the visual studio project for a 32 bit architecture (eg. Any CPU , Preferred 32 bit) it could not work on a 64 bit machine architecture. Eg. I had this problem with a machine running Windows Server 2012, but I had no problem with Windows 10. I Solved compiling the Libplctag library for a 64bit architecture.

      • Hi Antonio,
        I’m kind of new on this world, so I’ll be thankful if you could tell me how to compile the LibplctagWrapper for 64bit, since I’ve never done that. (And, do I have to compile my main project too? And how can I check if my main project is x64 or x86, as well as the LibplctagWrapper)

      • Ok, I managed to compile it to x64, and it “works” the problem is that now is my main project the one who doesn’t work… Any ideas?

        • Hi Javier, I didn’t mean compiling the LibplctagWrapper project for 64bit, but the library on which it is developed, that is Libplctag. To get the plctag.dll compiled you can follow this steps https://github.com/kyle-github/libplctag/blob/master/BUILD.md and adapt it for 64 bits when generating the dll. Anyway to be sure that this could be the solution, more details are needed. Which is the target operating system? Which is the error that you get now on your main project?

    • Hi Javier, I think I found an easy solution (abandon the way of compiling the dll for 64bit architecture). Install Microsoft Visual C++ Redistributable (https://support.microsoft.com/it-it/help/2977003/the-latest-supported-visual-c-downloads) on the target machine and retry to run the application. It seems that Libplctag.dll use need such module to work.

      • IT WORKS!!! Thanks… I was in the process of doing the first solution you mentioned before… In my case is kind of tricky ’cause the IT department is the one who manages the PC’s… And as you can imagine I have to go through a “boring” process…

    • Here is the technical explanation. I just had the same problem:

      Redistributing Visual C++ Files:
      https://docs.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files?view=vs-2019

      The Visual C++ Redistributable Packages install and register all Visual C++ libraries. If you use one, you must set it to run on the target system as a prerequisite to the installation of your application. We recommend that you use these packages for your deployments because they enable automatic updating of the Visual C++ libraries. For an example about how to use these packages, see Walkthrough: Deploying a Visual C++ Application By Using the Visual C++ Redistributable Package.

      To deploy redistributable Visual C++ files, you can use the Visual C++ Redistributable Packages (VCRedist_x86.exe, VCRedist_x64.exe, or VCRedist_arm.exe) that are included in Visual Studio.

      Determining Which DLLs to Redistribute
      https://docs.microsoft.com/en-us/cpp/windows/determining-which-dlls-to-redistribute?view=vs-2019
      To determine which DLLs you have to redistribute with your application, collect a list of the DLLs that your application depends on. These are normally listed as import library inputs to the linker. Certain libraries, such as vcruntime and the Universal C Runtime Library (UCRT), are included by default. If your app or one of its dependencies uses LoadLibrary to dynamically load a DLL, that DLL may not be listed in the inputs to the linker. One way to collect the list of dynamically loaded DLLs is to run Dependency Walker (depends.exe) on your app, as described in Understanding the Dependencies of a Visual C++ Application. Unfortunately, this tool is outdated and may report that it can’t find certain DLLs.

      Understanding the Dependencies of a Visual C++ Application
      https://docs.microsoft.com/en-us/cpp/windows/understanding-the-dependencies-of-a-visual-cpp-application?view=vs-2019
      To determine which Visual C++ libraries an application depends on, you can view the project properties. (In Solution Explorer, right-click on the project and choose Properties to open the Property Pages dialog box.) On Windows 8 and earlier, you can also use the Dependency Walker (depends.exe), which gives a more comprehensive picture of the dependencies. For more recent versions of Windows the lucasg/Dependencies tool provides similar functionality (this is a third-party tool not guaranteed by Microsoft.)
      https://github.com/lucasg/Dependencies

  20. Does anyone know how to connect and read tag from PLC Micro 850 ?

  21. Hello. I am trying to use this code for Allen-Bradley 2080-LC50-24QBB but I got error -7 (wrong parameter). Please, can you help me setup right parameters?

    var tag = new Tag(“192.168.3.200”, CpuType.PLC5, “IO_EM_DI_01”, DataType.Int16, 1);

    Thank you,
    Best regards
    ER

  22. Hi, someone know how to WRITE a string datatype in a tag? with this library ?

  23. Please , can anyone tell me how to read String Value ?
    i am using Micro1400 PLC , Thanks In Advance

  24. I need to go through 2 ethernet cards to get to my clx, so my path is “1,6,ip.ip.ip.ip,1,6” . I am getting a timeout error, is there a different syntax for the path on this? This is the same syntax that I use on Kepware for similar paths.

  25. Hello, thanks for your video and posting.
    Now I’m preparing the project ( HMI based on C#, Communication with Controllogix series).
    In that case, Do I need any OPC Server?
    or Could I connect directly C# Program and PLC?

    Thanks.

    • Hi,
      I used this method for a project with a CompactLogix, and it worked directly, no OPC Server needed.

      • Thx for that!! Cause RockWell told me that they recommend to use OPC server.

        • Yeah… That’s what almost everybody told me, at least until I found this method, and even with this one I had a few issues. But in the end, everything ended up working really well.

      • I just did some test with this library. and it works well. Thanks again.
        this method save our budget! 😀

        Could I ask something?
        when a communication error occurs, how can I connect again? I disconnected ethernet cable and connected it again, but it didn’t work properly. Do i have to start again from Creating Tag?

        • I never experienced this problem, probably ’cause since the very beginning I put the entire code in the same event, so every time i call this event it goes all the way from creating the tag and then reading. So even when I disconnected the ethernet cable and plugged it back in, it took a couple of seconds to reconnect… but it worked.

          Hope this help.

      • Ah, Thanks for your comment. Maybe my code is wrong. I made Creating Tag method and Reading tag method seperately.

        Anyway, Thanks Again Javier.

      • I fixed my code. when the communication error occured, dispose libtag object, and create tag again.
        it worked. Thanks.

  26. Ciao Michele!
    Do you know if there is any way to create an ethernet/IP server with C#. What I’m looking to get is to have a controller, lets’ say a compactLogix that send messages to a pc (unconnected message, CIP write) and the PC on the other side waiting for messages and do whatever required with the data.
    Thanks a lot for your work, it is very cool what you are doing here!!

    • I think you have to implement an application running directly on the pc (a windows machine) interacting with the PLC through the LibPlcTag wrapper. Eg. Reading periodically tags from the PLC and executing some functions when your conditions are satisfied.

      • Antonio,
        Thanks for your response. Yes I was thinking to do something like you described but I was hoping to get the Plc to notify somehow my application when new data were available. I’ll give it a try. Thanks again.

        Matteo

        • Hi Matteo,
          The main problem here is the fact that it is not like an implicit communication, which means that you can only read the data from the PLC by doing a request base on a C# event. With that in mind… In C# I used this library in a “timer event” in order to read a single tag from the PLC every 100ms. Then I used this last one as a trigger in the PLC to indicate a new result available. So, every time this tag was equal to “1”, I called another event where I requested the new result.
          Hope this help.

  27. gourav maheshwari

    how to write in compactlogix PLC tag (data type is string). i want to get value form tag and write value in tag

  28. i start debug error show Error

    Error 1
    Metadata file ‘C:\Users\Jeya Prakash\Documents\visual studio 2012\Projects\AB Recipe Pro\LibplctagWrapper\bin\Debug\LibplctagWrapper.dll’ could not be found C:\Users\Jeya Prakash\Documents\visual studio 2012\Projects\AB Recipe Pro\AB Recipe Pro\CSC AB Recipe Pro

    Error 2
    Error 4 Unexpected character ‘$’ C:\Users\Jeya Prakash\Documents\visual studio 2012\Projects\AB Recipe Pro\LibplctagWrapper\Tag.cs 70 27 LibplctagWrapper

    please help me

    • If you follow the link from my other post then there you can find slightly modified version of the LibplctagWrapper.
      Those modifications were needed to make it work in Visual Studio 2013 (removed $ characters, added string.Format).

      In order to download it you would have to register (registration is FREE).

  29. For anyone who might be interested in a VB Net Windows Forms app using libplctag library and this C# wrapper, you can find it here:

    https://www.advancedhmi.com/forum/index.php?topic=2563.0

    It might have bugs.

  30. how to write string in plc
    plz help me its urgent

    • There is a comment by Antonio G, posted on June 5, 2019 at 09:03, which shows how to read strings. You would use similar logic to write it (insert with SetUint8Value into the tag the length of the new string as the first 4 bytes and then the bytes of the string itself and write the tag to the PLC).

      There is also that VB Net Windows Forms app which does string reading and writing (in the post above yours).

    • Hi,

      Here is a section of the code that I use to set the value of a string tag (naturally, after that the tag object has been created):

      //First setting the size
      client.SetInt32Value(item, 0, YOUR_STRING_SIZE);

      //Setting string value
      byte[] bytes = System.Text.Encoding.ASCII.GetBytes(YOUR_STRING);

      int offset = DataType.Int32; //the first 4 bytes are used to store the string size
      int j = 0;

      for (j = 0; j < bytes.Length; j++)
      {
      int off = offset + j;
      client.SetUint8Value(item, off, bytes[j]);
      }

  31. I just made a HMI system with C# and this library.
    Just use less than 100 tags.

    But each time I execute the program, communication speed is different.
    (Using for update UI)

    Sometimes it’s good work.
    But sometimes, It’s too slow (more than 3 sec)

    I checked plc status and pending also, it’s well.

    Datatime out is 5 second.

    I don’t know why it happenes.

    Anyone had this kind of issue??

  32. Hi,
    I’m having some problems with adding a tag of name “Ball[0,0]”.
    On trying to add the tag, I get the error code -7, PLC_ERR_BAD_PARAM.
    The problem appears to be with the comma in the name.
    I know the name is correct because it works fine when tried in the Automated Solutions example.
    Any ideas?
    Thanks.
    Regards,
    Andy

    • There is a possible way done in the code but you need to know the X size of the array.
      If array is DINT[5,5] then you would go about reading the array itself, which should return the first array element value [0,0] if the element count = 1, similar to this:

      name = Ball, data type = DINT, ElementCount = 1

      You would apply the same logic for other array elements, for example [2,3], for which you need to calculate the element count as ( 2 * X ) + 3 + 1 = ( 2 * 5 ) + 3 + 1= 14. The +1 is added because indexes start at 0.

      name = Ball, data type = DINT, ElementCount = 14

      which will read 14 consecutive values starting at [0,0].
      Then just extract the 14th value with client.GetInt32Value(tag, 13 * tag.ElementSize).

      I am not sure if there are any limitations.

      • Thank you for your comments. From these notes, I have been able to sort out the issues.
        Much appreciated.

        • It’s a good thing that you figured it out.

          This website seems to pose some restrictions as to what a comment can contain. My attempts to post the accurate formulas for calculating the element count for both x,y and x,y,z arrays just fail.

          Mesta, what’s up with this?

      • One actually needs to know the Y size of the x,y array and both Y and Z sizes of the x,y,z array. Then for the element a,b the calculation would be (a * Y) +b +1 and for the element a,b,c it would be (a * (Y * Z)) + (b * Z) +c +1.

        If this comment gets posted then just ignore my other comment.

        • As it appears, no calculations are needed since the library allows polling arrays as either Ball[x][y] or Ball[x][y][z] instead of using single brackets with comma separated values.

  33. Thanks for all the work you’ve done to make this available!

    I’m new to this so bear with me: I have a simple system where I am trying to write a string to a location and subsequently read a string from a different location. In both methods I am able to set up the tag with no issues (status == Libplctag.PLCTAG_STATUS_OK) but both the WriteTag and ReadTag methods return -32 (ERR_TIMEOUT) and the subsequent status == ERR_BAD_CONNECTION).

    Tag setup:
    var tag = new Tag(ip, “1,0”, CpuType.LGX, location, DataType.String, 1);

    Populating the tag and call to push to controller:
    // set data size
    client.SetInt32Value(tag, 0, data.Length);

    // set string value
    byte[] bytes = System.Text.Encoding.ASCII.GetBytes(data);

    int offset = DataType.Int32; // the first 4 bytes are used to store the string size
    int j = 0;

    for (j = 0; j < bytes.Length; j++)
    {
    int off = offset + j;
    client.SetUint8Value(tag, off, bytes[j]);
    }

    // write the values
    var result = client.WriteTag(tag, timeout);

    if (result != Libplctag.PLCTAG_STATUS_OK)
    {
    string err = client.DecodeError(client.GetStatus(tag));
    io.errLog("Error writing tag data" + Environment.NewLine + err, "services.sendPLC");
    return "Error writing tag data";
    }

    Using a CompactLogix machine. The ethernet port is directly on the controller which I believe dictates the path as "1,0"? I am trying to write a 10 digit alphanumeric string.

    Thoughts?

    • Look at the address example by Mesta, it shows space in the path (not sure how relevant that is but it’s there):

      var tag = new Tag(“10.206.1.39”, “1, 0”, CpuType.LGX, “TestDINTArray[0]”, DataType.Int32, 1);

    • The mentioned space doesn’t appear to make a difference as for my testing it.

  34. BAD_CONNECTION suggests that your path is not correct (provided that your ip is actually correct).

    If you perform reading only then you can try different combinations in the path itself to see where that takes you.

  35. Has anybody got this working with Unity3D on an Android device ? I have got close, re-compiling the plctag.dll to 64 bit and all runs ok in the Unity Editor but not on the Android device. I’m using Visual Studio 2019 and Unity 2019.3

  36. hai can someone help me, i am not able to read tag values from AB micro 820TM (2080-LC20-20QBB), it is showing tag status eror(
    ERROR: Unable to read the data! Got error code -29: PLCTAG_ERR_REMOTE_ERR)

  37. Excuse me, a have the problem -3: #define PLCTAG_ERR_BAD_CONNECTION (-3)
    Here my code:
    namespace Projecto_1
    {
    class Program
    {
    private const int DataTimeout = 5000;

    static void Main(string[] args)
    {
    try
    {
    var tag = new Tag(“192.168.0.100”, “2, 0”, CpuType.LGX, “Program: MainProgram.Panelindex_PosFind_Pos”, DataType.DINT, 1);

    using (var client = new Libplctag())

    {

    // add the tag
    client.AddTag(tag);

    // check that the tag has been added, if it returns pending we have to retry
    while (client.GetStatus(tag) == Libplctag.PLCTAG_STATUS_PENDING)
    {
    System.Threading.Thread.Sleep(100);
    }

    // if the status is not ok, we have to handle the error
    if (client.GetStatus(tag) != Libplctag.PLCTAG_STATUS_OK)
    {
    Console.WriteLine($”Error setting up tag internal state. Error { client.DecodeError(client.GetStatus(tag))}\n”);
    return;
    }

    // Execute the read
    var result = client.ReadTag(tag, DataTimeout);

    // Check the read operation result
    if (result != Libplctag.PLCTAG_STATUS_OK)
    {
    Console.WriteLine($”ERROR: Unable to read the data! Got error code {result}: {client.DecodeError(result)}\n”);
    return;
    }
    // Convert the data
    var Pos = client.GetInt32Value(tag, 0 * tag.ElementSize); // multiply with tag.ElementSize to keep indexes consistant with the indexes on the plc

    //print to console
    Console.WriteLine(“Program: MainProgram.Panelindex_PosFind_Pos: ” + Pos);

    }
    }

    finally
    {
    Console.ReadKey();

    }
    }
    }
    }

    If someone can help me please.

    • PLCTAG_ERR_BAD_CONNECTION indicates that it cannot establish a connection with IP and Path you specified.
      If the IP is correct then you should try different paths, like “1, 0” or “2, 1” etc.

      There is a VB app available in one of my posts above that can make testing easier.

      • I change the IP of the EIP switch in my virtualBackplane(was the same of the VM) and i still have the problem

  38. But I try using the same EIP with a virtual Backplane(I use a VM), the tags and I have the same error code (-3)
    Could be my PC?

    • And the VB is in my real PC and the PLC in the VM

      • what are you running your virtual back plane with to your have it talk with your plc programming software? I used rslinx with a virtual back plane ran by softlogic and to address the virtual back plane it looked like this:

        var tag = new Tag(“10.210.4.26”, “1, 2”, CpuType.LGX, “Program:MainProgram.Start”, DataType.Int16, 1);

        another thing to think about isnt just the ip address of the plc but the Ethernet switch used to talk to the virtual backplane, you do need to add a “virtual” Ethernet switch on the virtual backplane to be able to communicate to the plc.

        • Yes, i have the RSLINX in slot 0, te sotflogix 5800 in the slot 1, and the ethernet modul in the slot 3 (10.206.1.39), and i created a tag TestDINTArray(DINT(7))
          var tag = new Tag(“10.210.4.26”, “1, 2”, CpuType.LGX, “Program:MainProgram.TestDINTArray[0]”, DataType.DINT, 1)

        • I`m using the RsLogix 5000 and de Emulated 5000, and I comunicated with RsLinx all in the VM and the Visual Studio in my real PC

        • And I have a controllogix 1754-L61 and for both form I can communicated

      • i wish i could post pictures and files in the comments to help you further, but without seeing what you are working with i can not efficiently help you debug.

        • Do you hace a mail???
          So I can set you picture

        • sorry not that i can post here. also i was able to run the everything on one pc without any virtual machine. im not sure how having a VM may effect the communications path.

        • I have 2 forms to work
          1.- VM (SoftLogix5800, RsLogx 5000, and RsLinx all in the virtual Machine)
          2.- We have a 1754-L61 ControlLogix and I comunicated the VM because I don`t have the Rslinx and Rslogix5000 for 64bits (the VM it`s 32 bits )

  39. I cannot comunicated*

    • ok using softlogixs version of rs5000 should be correct as the emulate version of rs5000 will not let you put in a communication module in the virtual back plane. as far as the ip address 10.210.4.26, that was specific to my communication at the time, you will need to see what the IP address should be for your case.

      • so yours should of been
        var tag = new Tag(“10.206.1.39”, “1, 2”, CpuType.LGX, “Program:MainProgram.TestDINTArray[0]”, DataType.DINT, 1)

        also the project path in logics is set up in rslinx, did you configure a driver up through linx as a virtual back plane

        • I try your form (var tag = new Tag(“10.206.1.39”, “1, 2”, CpuType.LGX, “Program:MainProgram.TestDINTArray[0]”, DataType.DINT, 1) Yes I have a backplane (AB_VPB-1, 1789-A17/A Virtual Chassis)

        • my path in linxs looks like AB_VPB-1\2

        • And if I try for the RS232 Port, How could I write my variable??:

        • I never looked into the rs232 port route and im not sure the structure of the wrapper is set to work that way. i think it acts much like a ethernet device would to talk to the plc through rslinx.

  40. Hi all,

    I have a project (Visual Studio 2019 C#) done and now I need read and write a few values with a MicroLogix1100. The instructions that you have, for windows, is start your project. I can import the DLL files to my project only?
    Thank you,

  41. Excuse, if someone can help me with my problem,
    Im using the RSLogix Emulate 5000 with the EmuLogix 5868 controller
    My Controller is in the Slot 1 and is a backplane,
    So my path looks:
    var tag = new Tag(“192.168.1.69”, “1,1 “, CpuType.LGX, “Program:MainProgram.Dato”, DataType.Int16, 1);
    But I have the message ERROR: Unable to read the data! Got error code -35: PLCTAG_ERR_UNSUPPORTED
    if someone knows the solution please

    • im sorry to say Alejandro but the rslogix emulate does not support ethernet devices like softlogix5000 does. I was not able to get rslogix emulate 5000 to work with this no matter how much i tried. it is much easier to go the softlogixs route.

      • I´m install the foftlogix 5800 in my computer (use VMware) and I have de error -3: #define PLCTAG_ERR_BAD_CONNECTION (-3), my RsLinx is in the Slot 0, my controller (softlogix5860) in slot 1 and my tag’s name is DATO(INT).
        Looks like: var tag = new Tag(“192.168.1.102”, “1,1”, CpuType.LGX, “Program:MainProgram.DATO”, DataType.Int16, 1);
        If someone could help me please

  42. Can anyone help me with Bool syntax please. I’m using RsLogix 5000 and Visual studio.

    • To read value:

      using (var client = new Libplctag())
      {
      var tag = new Tag(IpAddress.ToString(), Path, CpuType.LGX, pTag, DataType.SINT, 1); //Change your CPU
      client.AddTag(tag);
      var rc = client.ReadTag(tag, DataTimeout); //state of the reading.
      if (rc != Libplctag.PLCTAG_STATUS_OK)
      //manage error here
      else
      {
      var value = client.GetBitValue(tag, 0, 1); //get the value
      }
      }

      to set the value it is almost the same but after adding your tag you need to do
      client.SetBitValue(tag, 0, Convert.ToBoolean(pValue), DataTimeout);

  43. Hi, I use this library to communicate with some PLC. I run it inside a windows process to read and write tags in PLCs. I have just ran an error with a crash of the process:

    Process stopped due to unknown exception.
    Name of failed module: plctag.dll.
    The error arrived just after a timeout and error -32 while writing in a tag.

    When an error is catched, myapplication just write a line into a log file and continue. I have no new exception thrown because I don’t want to break the loop.
    The process was running for 2 months without any trouble before this. Do you have any idea why the process completely crashed and not started again, or suggestion where I should look?

  44. Do anyone have C# working code to write string TAG for AB control logix?If exists please share

    • Hi,

      This is the code I use to write and read string in an AllenBradley

      For read string:
      private string GetStringValue(Tag pTag, Libplctag client)
      {
      int size = client.GetInt32Value(pTag, 0);
      int offset = DataType.Int32;
      string output = string.Empty;

      for (int i = 0; i < pTag.ElementCount; i++)
      {
      var sb = new StringBuilder();

      for (int j = 0; j < size; j++)
      {
      sb.Append((char)client.GetUint8Value(pTag, (i * pTag.ElementSize) + offset + j));
      }

      output = sb.ToString();
      }
      return output;
      }

      for write string
      private void SetStringValue(Tag pTag, Libplctag client, string value)
      {
      //First setting the size
      client.SetInt32Value(pTag, 0, value.Length);

      //Setting string value
      byte[] bytes = System.Text.Encoding.ASCII.GetBytes(value);

      int offset = DataType.Int32; //the first 4 bytes are used to store the string size
      int j = 0;

      for (j = 0; j < bytes.Length; j++)
      {
      int off = offset + j;
      client.SetUint8Value(pTag, off, bytes[j]);
      }
      }

      Create your tag as string:
      var tag = new Tag(IpAddress.ToString(), Path, CpuType.LGX, pTag, DataType.String, 1, 1);

      THen you just have to call the function in the read or write function:
      var rc = client.ReadTag(tag, DataTimeout);
      var value = GetStringValue(tag, client); //read
      SetStringValue(tag, client, pValue.ToString()); // write

      Hope this help

  45. Hai
    I am trying to read DINT value from control logix i am getting below error
    ERROR: Unable to read the data! Got error code -7: PLCTAG_ERR_BAD_PARAM

  46. Thanks for the work!
    I have a question: when reading tag data from AB PLC, can one tag contain different datatypes? like Timer2 tag contains two dint and three bool. I’ve tried hard without success, and there is no corresponding information on the Internet to answer this question, does anyone know?

    • I guess you are tryint to read a UDT tag. In this case you can read all the bytes of the tag and then convert the bytes range that you need.

      Something like this:

      Tag t = new Tag(PLC_IP, PLC_PATH, PLC_TYPE, tagName, 136, 1); //Where 136 is the size of the entire UDT.

      …..

      float element1 = client.GetFloat32Value(tag, 56); //this float element start at byte 56
      float element 2 = client.GetFloat32Value(tag, 64); //this float element start at byte 64

      So, you can use the offset parameter to start reading where you need.

      • From the tag he stated, Timer2, it’s a timer with PRE and ACC as DINT values and EN, TT, DN as bool values.

        • Glad to see your sincere reply!
          My project is C code libplctag, I have tried the UDT tag,seems i didn’t use it right.
          My tag is defined as:

          protocol=abeip&gateway=192.168.1.11&path=1,0&cpu=contrologix&elem_size=1&elem_count=11&name=Timer2&debug=3

          When running to plc_tag_read(), it throw out the error code: PLCTAG_ERR_OUT_OF_BOUNDS

          So how to define the UDT tag? and the elem_size = ? elem_count =?

          Forgive my naive question.

        • protocol=abeip&gateway=192.168.16.205&path=1,0&cpu=contrologix
          & elem_size=1&elem_count=12&name=T2&debug=3

  47. The link I posted previously was for your benefit, so you need to start reading since there is a program that can read all these for you and a whole AdvancedHMI solution that can do the same and more (and all for FREE).

    Timers are data types and not UDT, so here is another link for you to read:

    https://literature.rockwellautomation.com/idc/groups/literature/documents/pm/1756-pm004_-en-p.pdf

    • Thanks for your help.
      I have solved my problem,for a timer tag, the elem_size=12 and the elem_count=1 works right
      Best regards!

  48. how to resolve this error in logix. 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

  49. I’m trying to find a Boolean in an array can anyone help with the syntax for nth element in a Bool array.

  50. UTD is week[0-52] (UTD Large Array) and in that array I have Day[0-77] (Real style Float)

    I have the following UDT. Is there way I can access the week[0].day[0] and so on… to read and write to it.
    I have tried using the following tag but gives me out of bounds error.
    (“IPAddress”, “1, 0”, CpuType.LGX, “Week[0].Day”, DataType.Float32, 100);
    var TstTag1 = client.GetFloat32Value(tag, (elementnumber * tag.ElementSize));

  51. Hi!

    I have the following error:

    System.DllNotFoundException: Unable to load shared library ‘plctag’ or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: libplctag: cannot open shared object file: No such file or directory\n at PlcTag.NativeLibrary.plc_tag_destroy(Int32 tag)\n at PlcTag.Tag`1.Disconnect()\n at PlcTag.PlcController.Disconnect()\n

    the application is developed in .Net Core and runs on a raspbian

    • Usually this happens when the compiled version of the library that you are using is not targeting the platform it is running on. Make sure you are using a .net core compiled version of the library.

    • Hi, I had this same problem. Did you solve it?

  52. Can I run multiple connections to different IP address or same address without removing Tag (client.RemoveTag(tag1);) So that I can use to trend multiple values at once on different/same thread?

  53. My Software return:
    ERROR: Unable to read the data! Got error code -32: PLCTAG_ERR_TIMEOUT

    public void tread_evaporadora_59()
    {
    try
    {
    var BLOCO_B312 = new Tag(“192.168.0.4”, CpuType.SLC, “B3:12″, DataType.Int16, 1);

    using (var client = new Libplctag())
    {

    client.AddTag(BLOCO_B312);

    //verifique se a tag foi adicionada, se retornar pendente, temos que tentar novamente
    while (client.GetStatus(BLOCO_B312) == Libplctag.PLCTAG_STATUS_PENDING)
    {
    Thread.Sleep(100);
    }

    //se o status não for ok, temos que lidar com o erro
    if (client.GetStatus(BLOCO_B312) != Libplctag.PLCTAG_STATUS_OK)
    {
    textBox50.Text = $”Error setting up tag internal state. Error{
    client.DecodeError(client.GetStatus(BLOCO_B312))}”;

    return;
    }

    var result7 = client.ReadTag(BLOCO_B312, DataTimeout);

    // Verifique o resultado da operação de leitura
    if (result7 != Libplctag.PLCTAG_STATUS_OK)
    {

    textBox50.Text = $”ERROR: Unable to read the data! Got error code {result7}: {client.DecodeError(result7)}”;
    client.Dispose();
    client.RemoveTag(BLOCO_B312);
    return;
    }

    AMB59_EVP1 = client.GetBitValue(BLOCO_B312, 0, DataTimeout);
    AMB59_FALHA_EVP1 = client.GetBitValue(BLOCO_B312, 1, DataTimeout);
    AMB59_EVP2 = client.GetBitValue(BLOCO_B312, 2, DataTimeout);
    AMB59_FALHA_EVP2 = client.GetBitValue(BLOCO_B312, 3, DataTimeout);
    AMB59_EVP3 = client.GetBitValue(BLOCO_B312, 4, DataTimeout);
    AMB59_FALHA_EVP3 = client.GetBitValue(BLOCO_B312, 5, DataTimeout);

    }
    }
    catch
    {

    }
    }

  54. Brooks Nelson

    I got this error when trying to test the first part of the code.

    Error setting up tag internal state. ErrorPLCTAG_ERR_BAD_GATEWAY.

    I have Labview setup and I can call the gateway with “IPaddress”, “1,0” and read the array fine.

    var tag = new Tag(“192.168.33.172”, “1, 0”, CpuType.LGX, “TestDINTArray[0]”, DataType.Int32, 1);

  55. Im trying to connect with a compactlogix but show me error -6, i set the path and ip, but there no comunication. Some solution?

    • I initially was getting the same -6 PLCTAG_ERR_BAD_GATEWAY error, but then I discovered that the “1, 0” path in var tag = new Tag(“192.168.33.172”, “1, 0”, CpuType.LGX, “TestDINTArray[0]”, DataType.Int32, 1); needs to have the space removed from between the comma and the 0, “1,0”, then it worked as expected.

  56. I’m trying to connect with RsLogix 5000 but to another rack, in my system, I have multiple racks in each rack EN Card in Solt 1
    the difference is Program should see the tree under another Card in Slot 1 in Rack 1 to go to another EN card with IP 10.0.0.3 in Slot 1 Rack 2 to connect to PLC in Slot 2 to be like that var tag = new Tag(“192.168.33.172”,”1,[0,2,10.0.0.3,1],2″, “TestDINTArray[0]”, DataType.Int32, 1);

  57. Hi everyone, I’m trying to make a scada with c # and wpf, I need to read tags values constantly but when I put the code needed to read a tag inside a while loop the UI doesn’t appears but the program reads the tag value in infinite loop. here its the code where I use the while loop

    if (tablero.IsEnabled)
    {
    Thread.Sleep(100);
    ReadTagConstantly read = new ReadTagConstantly();
    if (read.TestRead() == 1)
    {
    START.Background = Brushes.LightGreen;
    }
    else START.Background = Brushes.Green;
    }

    as you can figure it out I read the tag value in another class’s method, Here is the code:

    public class ReadTagConstantly
    {
    public ReadTagConstantly()
    {

    }

    public int TestRead()
    {
    int val = 0;

    var Client = new Libplctag();

    const string PLC_IP = “10.247.206.100”;

    const string PLC_PATH = “1,0”;

    const CpuType PLC_TYPE = CpuType.LGX;

    var CC200_LT_ST = new Tag(PLC_IP, PLC_PATH, PLC_TYPE, “CC200_LT_ST”, DataType.Int8, 1);

    Client.AddTag(CC200_LT_ST);

    while (Client.GetStatus(CC200_LT_ST) == Libplctag.PLCTAG_STATUS_PENDING)
    {
    Thread.Sleep(100);
    }

    if (Client.GetStatus(CC200_LT_ST) != Libplctag.PLCTAG_STATUS_OK)
    {
    MessageBox.Show(“Conexion fallida”);

    }

    int rc;

    rc = Client.ReadTag(CC200_LT_ST, 100);

    if (rc != Libplctag.PLCTAG_STATUS_OK)
    {
    MessageBox.Show(“No fue posible leer el estatus del tag”);

    }

    Console.WriteLine($”El valor del tag es : {Client.GetUint8Value(CC200_LT_ST, 0)}\n”);

    val = Client.GetUint8Value(CC200_LT_ST, 0);

    Client.Dispose();

    return val;
    }
    }

Leave a Reply to Anonymous Cancel reply

Your email address will not be published.