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

47 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.

      • 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.

  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 ?

  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

  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.

  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?

Leave a Reply