Home / C# / How to write a Siemens S7 plc driver with C# and Sharp7

How to write a Siemens S7 plc driver with C# and Sharp7

Sharp7 is a new C# driver to communicate to Siemens S7 Plc. The driver has been written by Davide Nardella, the author of Snap7 library, and it’s retro-compatible with the C# wrapper of Snap7. So if you were using Snap7 and the C# wrapper, you can easily replace both with Sharp7 library.

One of the biggest advantages of having the full library in C# is during debugging. You can place a breakpoint in Visual Studio and debug the messages sent and received from the TCP socket without having to leave Visual Studio and without having to use network sniffers or other external software.

Also with Sharp7 it’s easier to read/write multiple variables in a single request than with Snap7. Now ReadMultiVars and WriteMultiVars don’t require pointers and memory management.

Watch on Youtube

Download

The official website of Sharp7 is http://snap7.sourceforge.net/

Sharp7 can be downloaded from Sourceforge download page: https://sourceforge.net/projects/snap7/files/Sharp7/

sharp7-folder

The downloaded archive contains 3 folders:

  • Src: contains the sources of the library. In case of Sharp7 this is the library itself, as there are no binaries to be included
  • Doc: contains the user manual in pdf
  • Examples: contains 4 examples, one for Windows Forms, two for Console application and one for UWP

Documentation

The library comes itself with a complete user manual and examples for Windows Forms, Universal Windows Platform and a simple Console application.

As for the manual, there are some sections that are really important:

  • Compatibility (page 9): this section shows the compatibility of the library functions with the plc that you are using. S7300-S7400 supports every function, while newer plcs can only read and write data.
  • Connection (page 10-11-12): this section shows how to setup and configure the CPU and DB to grant the access to external software.
  • Connection parameters (page 22-26): this one explains how to setup IP, rack and slot of the plc, in order to be able to communicate.
  • Read/Write function description (27-34): contains the definition of the functions that you will use the most, to read and write memory area.
  • Helpers and example (57- 61): contains the definition of conversion function from .Net to S7 variables and some examples.

Configuration for S7-1200

To test the library we will write a simple console program. It will show how to connect/disconnect, read and write block of DB and multiple variables.

In this example we will connect to a S7-1200 plc. This unit has to be configured properly (see page 12 of the user manual).

So let’s uncheck optimized block access for every DB (under program blocks) and configure the CPU protection.

s7-1200-config-1

Creation of the sample project

When importing Sharp7, the best approach is to create a C# Class library project and place Sharp7 sources inside that project.

sharp7-create-new-proj

sharp7-new-project

sharp7-example-solution

Then we will reference the Sharp7 project in all the project that we need.

screenshot-2016-10-20-01-03-16

add-reference

Connection to the plc

Driver creation, connection and disconnection are really easy to do.

// Create and connect the client
var client = new S7Client();
int result = client.ConnectTo("127.0.0.1", 0, 1);
if(result == 0)
{
    Console.WriteLine("Connected to 127.0.0.1");
}
else
{
    Console.WriteLine(client.ErrorText(result));
}

// Disconnect the client
client.Disconnect();

Read from DB

Reading from a single db is also simple, but you must be comfortable with using buffers, converters and the snap7 functions.

To read for example 18 bytes, we need to create an array of 18 bytes. Then pass it to the Read function to populate it with the data from the cpu, then convert all the data from S7 format to C# format.

db1-read

For example this is the code to read this DB:

Console.WriteLine("\n---- Read DB 1");

byte[] db1Buffer = new byte[18];
result = client.DBRead(1, 0, 18, db1Buffer);
if(result != 0)
{
    Console.WriteLine("Error: " + client.ErrorText(result));
}
int db1dbw2= S7.GetIntAt(db1Buffer, 2);
Console.WriteLine("DB1.DBW2: " + db1dbw2);

double db1ddd4 = S7.GetRealAt(db1Buffer, 4);
Console.WriteLine("DB1.DBD4: " + db1ddd4);

double db1dbd8 = S7.GetDIntAt(db1Buffer, 8);
Console.WriteLine("DB1.DBD8: " + db1dbd8);

double db1dbd12 = S7.GetDWordAt(db1Buffer, 12);
Console.WriteLine("DB1.DBD12: " + db1dbd12);

double db1dbw16 = S7.GetWordAt(db1Buffer, 16);
Console.WriteLine("DB1.DBD16: " + db1dbw16); 

Write to DB

The Write function works in a way which is really similar to the Read function. We have to create a buffer, populate it with the data (converted in S7 format) that we need to write, and then call the Write function.
In this case we want to write these values:

db1-write

So we need to create a buffer of 12 bytes, populate the first 4 with a real number, the 4th-7th with a Dint number, the 8th -12th with a DWord number. Also, since our buffer starts from 4.0, we must set the start index to 4.
One has to be really careful with indexing, so what I usually do when creating the buffer is to use the real offset of the variable and to subtract the start index.
So here is the code to write DB1.DBD4, DB1.DBD8 and DB1.DBD12:

db1Buffer = new byte[12];
const int START_INDEX = 4;
S7.SetRealAt(db1Buffer, 4 - START_INDEX, (float)54.36);
S7.SetDIntAt(db1Buffer, 8 - START_INDEX, 555666);
S7.SetDWordAt(db1Buffer, 12 - START_INDEX, 123456);
result = client.DBWrite(1, START_INDEX, db1Buffer.Length, db1Buffer);
if (result != 0)
{
    Console.WriteLine("Error: " + client.ErrorText(result));
}

ReadMultiVar

Reading multiple variables in a single request is one of the most used features of S7 drivers, especially if one needs to read variables that are scattered around multiple DBs.
Example: let’s suppose, we want to read with a single request the values from DB1 and DB3.

readmultivar

So we have to create 2 buffers, in this case both of the same length, and we create an S7MultiVar variable. The S7MultiVar variables have a method “Read”, which populates all the buffers with the data from the plc.
This is the code to make the read:

// Read multi vars

var s7MultiVar = new S7MultiVar(client);
byte[] db1 = new byte[18];
s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 1, 0, 18, ref db1);
byte[] db3 = new byte[18];
s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 3, 0, 18, ref db3);
result = s7MultiVar.Read();
if (result != 0)
{
    Console.WriteLine("Error on s7MultiVar.Read()");
}

Once we called the read method, we can convert the data with the converters

db1dbw2 = S7.GetIntAt(db1, 2);
Console.WriteLine("DB1.DBW2.0 = {0}", db1dbw2);

db1ddd4 = S7.GetRealAt(db1, 4);
Console.WriteLine("DB1.DBW4.0 = {0}", db1ddd4);

db1dbd8 = S7.GetDIntAt(db1, 8);
Console.WriteLine("DB1.DBW8.0 = {0}", db1dbd8);

db3dbw2 = S7.GetIntAt(db3, 2);
Console.WriteLine("DB3.DBW2.0 = {0}", db3dbw2);

db3dbd4 = S7.GetRealAt(db3, 4);
Console.WriteLine("DB3.DBW4.0 = {0}", db3dbd4);

db3dbd8 = S7.GetDIntAt(db3, 8);
Console.WriteLine("DB3.DBW8.0 = {0}", db3dbd8);

WriteMultiVars

This function is usually used less often than the ReadMultiVars, but just in case you need it, here is how it works.
We have to create a buffer for every variable we want to write, then we populate the buffer with the converted values that we want to write, and then we call the Write method.
In this case we will write the DB1 and DB3 values from offset 2 to offset 8.

writemultivar

Again, here is the code. Pay special attention to start index and offsets.

// Write multi vars
s7MultiVar = new S7MultiVar(client);
const int DB1_START_INDEX = 2;
db1 = new byte[10];
S7.SetIntAt(db1, 2 - DB1_START_INDEX, 50);
S7.SetRealAt(db1, 4 - DB1_START_INDEX, (float)36.5);
S7.SetDIntAt(db1, 8 - DB1_START_INDEX, 123456);
s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 1, DB1_START_INDEX, db1.Length, ref db1);

const int DB3_START_INDEX = 2;
db3 = new byte[10];
S7.SetIntAt(db3, 2 - DB3_START_INDEX, -50);
S7.SetRealAt(db3, 4 - DB3_START_INDEX, (float)-25.36);
S7.SetDIntAt(db3, 8 - DB3_START_INDEX, -123456);
s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, 3, DB3_START_INDEX, db3.Length, ref db3);
result = s7MultiVar.Write();
if (result != 0)
{
    Console.WriteLine("Error on s7MultiVar.Read()");
}

Sample code

You can find the sample code and the Plc program on Github: https://github.com/mesta1/Sharp7-example

88 comments

  1. I don’t thing the Siemens project (the one loaded in the portal) is in the download – at least I cant seem to locate it.

  2. is it work in simatic manager?

    • Yes of course.

      • So.whats different between wincc and C#? Wincc is easy.I can edit dB in wincc (read or write)!why use c#?

        And
        How find component C# for Hmi?

        • WinCC, like all other HMI packages (FactoryTalk, Wonderware, iFix, …), is absolutely fine for the most of the tasks. You can do almost everything that you can do with C#.

          However, I find that more and more OEM that write their own software without those HMI packages.

          There are some advantages of using C# instead of WinCC or other HMI, for example you can use a version control software like Git, you can do countinous integration, unit testing, automation tests, etc. Also software written in C# scales well if you have a lot of programmers working on it.

          Also if you need to integrate external software, dlls, strange stuff from different vendors, custom protocols, custom hardware, I think that these HMIs may have some limits.

          One thing has to be clear, C# is more expensive because the learning curve is more steep.

          For the components there is something online, but the most components for industrial automation are not free. However it’s not that difficult to write such components. And if you need charts, gauges and other controls, there is plenty of them. This is just an example http://wpftoolkit.codeplex.com/

        • Why not to use WinCC?
          Because you have to pay for each and every runtime. I’ve installed more then 20-30 C# runtimes in my factory. Just think how much I saved.

          C# is costly??
          You only care for learning curve if you are working in Western Countries. In Asia learning curve doesn’t matter. 1 WinCC license is equal to 5 months of fresh automation engineer salary.

          Finding Components?
          Everything is available on google till you don’t use HMI word. Don’t try it to look like conventional HMI. graphs bars etc are available easily. and if you really want hmi symbols. Use free trial of symbolfactory.

  3. hi! io lkearning how o use this for a Project, now I have followed every step in your tutorial, but at the momento of running my program the buffer on the readresult variable always is 262144, I download it the plc program to my plc I´m using a CPU 1212C with tia portal v14, I configured the DB and the protection of the plc and my program connects with the adress but does not read anything, since I used your program the buffer suze that I used is 38

    • 262144 is Bad Datasize passed to send/recv. It means that you are probably reading more than you can read. I suspect that your DB has less than 38 bytes.

  4. Hello! Thanks for the tutorial. First time I used the S7.Net library, but I was only able to get it working with PLCSIM, not with real PLC. So I found this Sharp7 library and your tutorial. But the problem is that I am a newbie and can’t really figure out how to manage simple tasks like writing/reading the PLC outputs (like set Q0.0 to 1) or memory bits (e.g. read what value is in M2.1). My attempt so far was something like this: client.WriteArea(0x82, 0, 0, 1, 0x01, new byte[] {1}), which was meant to set the state of first output to 1, but it didn’t work. Could you please guide me on the right way?

    • Sharp7 has some helpers that should be used instead of ReadArea: MBRead, MBWrite, EBRead, EBWrite, ABRead, ABWrite. M is the memory area, E probably are inputs, and A are outputs. German abbreviations.
      If you need to write a single bit, without modifying the other bits, I wrote an helper in a recent article, maybe you can take a look at it and get some inspiration.

      /// <summary>
      /// Writes a bit at the specified address. Es.: DB1.DBX10.2 writes the bit in db 1, word 10, 3rd bit
      /// </summary>
      /// <param name="address">Es.: DB1.DBX10.2 writes the bit in db 1, word 10, 3rd bit</param>
      /// <param name="value">true or false</param>
      /// <returns></returns>
      private int WriteBit(string address, bool value)
      {
          var strings = address.Split('.');
          int db = Convert.ToInt32(strings[0].Replace("DB", ""));
          int pos = Convert.ToInt32(strings[1].Replace("DBX", ""));
          int bit = Convert.ToInt32(strings[2]);
          return WriteBit(db, pos, bit, value);
      }
      
      private int WriteBit(int db, int pos, int bit, bool value)
      {
          lock (_locker)
          {
              var buffer = new byte[1];
              S7.SetBitAt(ref buffer, 0, bit, value);
              return _client.WriteArea(S7Consts.S7AreaDB, db, pos + bit, buffer.Length, S7Consts.S7WLBit, buffer);
          }
      }
      
      • Thanks, I tried it and was able to get it working with PLCSIM. With this code I was able to set the Q0.0 to 1: client.ABWrite(0, 1, new byte[] { 1 }); However when I download the same TIA project to my real S71200 plc and try to set the Q0.0 with the same C# code, it connects OK, but the ABWrite method returns error code – ISO : Invalid PDU received (the same error happens when using other methods like EBRead, MBWrite, MBRead, DBWrite,…). I have double checked that in the PLC properties the PUT/GET check box is checked and Full Access is granted. Have you tried the Sharp7 library with a real PLC not only PLCSIM? The same thing happens with S7.Net driver. I have thought that my PLC could be faulty, because when I want to Flash LEDs through TIA Portal an error dialog pops up saying: “The target online device does not support LEDs Flashing or is not reachable.” However everything else works okay, I can Go Online, monitor values, force values throught Force table, PLC program runs ok. Do you have experience with such behaviour or am I missing something when I transfer from PLCSIM to real device? Thanks for your time.

        • No I don’t have a real plc to try and personally I just use DB and map everything to DB.
          You better contact the authors at this address to ask for clarifications: https://sourceforge.net/p/snap7/discussion/

        • im working with two s71200~1215, and it works fine,Although I use snap7 instead of this sharp version,i guess its the same thing.DId you configure the CPU protection?

        • Hi! Yep, I configured everything according to the tutorial. I suspect there is problem with the firmware version of my PLC.

        • hello , i’m working on the same thing, i want to read and wite I/O from real PLC and mermory adress could you tell me please how it wordked for you , could you please help me how to code it with c#

        • Hello! I wasn’t able to get it working with real PLC, only with PLCSIM. I used the code @mesta posted few comments above, but slightly modified.

          1. Example – Writing to Memory bits byteIndex-th byte:
          plcClient.WriteArea(S7Consts.S7AreaMK, 0, byteIndex, 8, S7Consts.S7WLByte, new byte[]{0,0,1,1,0,0,1,1});

          2. Example – Reading PLC outputs
          plcClient.WriteArea(S7Consts.S7AreaPA, 0, byteIndex, buffer.Length, S7Consts.S7WLByte, buffer);

        • hi again, thanks for your reply, can you plz give me the result of the examples, and for the 2nd one, isn’t a Reading PLCOutput? so we have to put Client.ReadArea(..), isn’t it?

        • Yes, you are right, I’ve made a mistake there 🙂 The result of first operation should be that the PLC memory bits will be set according to the values of the byte array => It means that memory bits from M0.0 to M0.7 will become 0, 0, 1, 1, 0, 0, 1, 1. The second example reads the state of PLC outputs and saves them to the passed buffer (again byte array).

        • ok, i got it,
          I was able read the Input//output//and the memory adress but only with plcsim for S71500 and i havn’t the plcsim for S71200 witch i should work with!! i have a real PLC 1215 C but when i execute the same code of reading data with c# , an error is shown every time! (Invalid buffer passed to send/receive) Have you found this error before?

      • The manual shows that ” When WordLen=S7WLBit the Offset (Start) must be expressed in bits.” . So I suppose the offset should be “pos*8+bit”?

  5. Hi,
    Thanks for your tutorial, i begin in C# and open-source HMI.
    How to read any DATA from multiple PLC ??
    Create loop for change connection ??

    Thanks for you’re answer.

  6. How do you write just one bit to PLC?

    • This is the code that I used in a recent video. It is part of a bigger application, you can find the sources here: https://github.com/mesta1/HMI-with-WPF-part-2-Navigation-with-PRISM/blob/master/SimpleHmi.PlcService/S7PlcService.cs

      /// <summary>
      /// Writes a bit at the specified address. Es.: DB1.DBX10.2 writes the bit in db 1, word 10, 3rd bit
      /// </summary>
      /// <param name="address">Es.: DB1.DBX10.2 writes the bit in db 1, word 10, 3rd bit</param>
      /// <param name="value">true or false</param>
      /// <returns></returns>
      private int WriteBit(string address, bool value)
      {
          var strings = address.Split('.');
          int db = Convert.ToInt32(strings[0].Replace("DB", ""));
          int pos = Convert.ToInt32(strings[1].Replace("DBX", ""));
          int bit = Convert.ToInt32(strings[2]);
          return WriteBit(db, pos, bit, value);
      }
      
      private int WriteBit(int db, int pos, int bit, bool value)
      {
          lock (_locker)
          {
              var buffer = new byte[1];
              S7.SetBitAt(ref buffer, 0, bit, value);
              return _client.WriteArea(S7Consts.S7AreaDB, db, pos + bit, buffer.Length, S7Consts.S7WLBit, buffer);
          }
      }
      
  7. Here is something I’ve accomplished with help of this article: https://github.com/stefan-schindler/plc-programmable-3d-simulation. It’s a 3D interactive simulation of a packing machine which communicates with PLCSIM via Sharp7, basically it writes the inputs and reads the outputs. So thank you once more for great article 🙂

    • Hey this is a great project ! I will try it out in the next days, thanks for posting it!

    • I want too 🙂

    • again hello 🙂
      unity mono using and debugging 3.5 net framework and i added dll sharp 3.5 net version but i dont work dll why ?

    • Hi Stefan, nice project. I’m working on something similar, and I have an issue with “Method not found: ‘System.Net.Sockets.Socket.Dispose’.” when using client.disconnect. It seems to be from the .NET version that Unity uses, so I’m wondering if you found a fix for this, or if you just ignore the error. Thanks!!

      • Hello, thanks 🙂 Have you configured the project to use full .NET framework not just the subset?

        • I’m not sure where that set up is? From what I find, Unity only uses up to .Net 3.5. I’m using Visual Studio for my C# editor, but I think that is irrelevant to what happens within Unity. Could you point me in a direction for full .NET Framework please. Thanks.

        • Thanks for pointing me in the right direction! Unity was set to use “Stable 3.5” instead of “experimental 4.6”. Once I changed that, it fixed an issue with using System.Threading.Tasks, which led to excellent communication between Unity and PLC. For anyone else: In Unity, Edit/Project Settings/Player, then change the Scripting Runtime Version. Thanks Stefan, Mesta, and Sharp7!

  8. plz I need PLCsim V13 for S71200 v, could somebody give it to me or tell me how can I get it !! :/

  9. I am amazed by the fact that you did reply every question. Gj man.

  10. Hello! I wasn’t able to get it working with PLCSIM, All examples : Sharp7Example, CSClient, VBClient the result the same “TCP Error receiving data” during make connection.

  11. Not working any more. Error ISO – invalid buffer passed to send/receive – 262144

  12. Is it possible to monitor PLC valiables continuously, if so, which is the best way to do this?
    Thanks in advance!

  13. hi wich hardware please !!can you help me ..

    • It works with Siemens S7 plc.

      • yes it’s siemens S7 plc , i tried at first to work with a real plc 1214C DC/DC/DC and it worked, but i have to make the communication with the plc cpu1215C dc/dc/dc , could you help me please what could it be the problem ?

        • Firmware update or CPU protection settings (scroll down and check Permit PUT/GET) or DB not marked for access.

  14. Hi Michele, first of all: Great job! You have explaned everything clearly and it was working with the first try.

    Now my question: ok, PLC data I have now in my C# program, but how to talk via RESTapi to another server? I have sample code in PYTHON which is working, but I would like to have the software in one “piece”.

    What is missing is the knowledge how to make the structure from the beginning.

    • If you want to create a REST api that exposes Sharp7, just create a WebApi project from Visual Studio.

      • Would you help me to getting started? It’s commercial so I don’t have a problem to talk about an official order.

        • I will put togheter a small project during the weekend and publish it, then we see.

        • That sounds incredible! If you want I could send you by email the API documentation and the corresponding PYTHON samples, but it is from a third party and therefore confidential.

          PS: Obviously you are a night owl too. 8)

        • Ok you can send the documentation at michele.cattafesta [at] mesta-automation [dot] com, so I will have a better idea on your requirements.

          And yes, I am a night owl too 🙂

        • Good morning, email is sent. Cheers

  15. hello
    i have problem.
    i want only change db1.dbw16 (just)
    ==========
    var writebuffer = new byte[18];
    S7.SetIntAt(writebuffer, 16, Convert.ToInt16(textBox1.Text));
    ============
    when i want set it. all byte Except this will be zero
    i want just change value db1.dbw16.but when i change it. all data except this will be zero.
    please help me
    thanks

  16. Hello Mesta.
    Your articles are greate!
    After them, i connect and read / write some variables in DB.
    But i still can’t read any String variable from DB.
    Could You please explain how can i get this?
    Thank You,
    Eda

  17. Hello
    can you give me example how read from timer? for example T1?
    thanks

  18. How speed read and write DB?

  19. I use 8 thread. Each a thread, I create a connection to PLC, to write value to DB.
    Thread 1: client 1, write value to DB1
    Thread 2: client 2, write value to DB2
    …..
    Thread 8: client 8, write value to DB8.

    Can they run parallel.

    • Of course they can run in parallel. You can check if the plc is accepting the requests by checking the result returned from the DBWrite method.

  20. Hi Mesta,
    Thank you vary much!

    I have retrieved data successfully from a PLC on a machine I work with. I don’t write anything to it, only read from DBs. Now there are problems with the machine and the company I purchased it from said that it maybe because of what I am doing.

    The only changes that where done, where to uncheck optimized block access for DBs and configure the CPU protection as you explained and it does not make sense to me that this would cause a problem.

    So my question is, do the Snap7 / Sharp7 libraries make changes to the PLC DBs or code to be able to retrieve data?

    Thanks again!

    • After some experimenting it looks like the changes of uncheck optimized block and configure the CPU protection where the cause.
      If you know of anything else that could be please write back.

      Thanks

      • Unchecking optimized block changes the memory structure, because Siemens seems to do some optimizations on the memory occupation. When unchecked, the old S7-300 memory structure is used, with absolute addressing.
        It seems strange to me that checking and unchecking the optimization changes the behavior of the program.
        Can you get some info from Siemens about this ? It seems quite a dangerous “feature”…

  21. Hallo

    thanks for the great tutorial , but at the risk of appearing like a total nut ,

    i seem no to be able to get started . i am very much new to the subject , being through the Reference Manual
    but i can’t get connected to neither my physical plc nor PLCSIM

    i am using TIA Portal V14 , PLCSIM V14 , on windows 10 . and every time i tried to connect to the PLC as shown in this
    tutorial , it fails , with the error type :” errTCPDataRecv : 0x0003 : TCP error receiving the data.” being returned .

    I certain that i have missed some necessary set up ( connection configurations ) that i hope you could help me with .

    Ps: List of additionnal firmware needed will also be helpful thx

  22. Hello,

    I am using Simatic Step 7 Professional V13 (without SP1) and PLCSIM V13 (without SP1). Everything is running on the virtual machine.

    Simulation mode works fine without Sharp7 application. When I run the Sharp7 application (exactly as described in the tutorial), I get this error “TCP : Error receiving Data”. I checked again and again and I am sure that I have done everything correctly.

    I have no idea what am I missing? I need help.

  23. Hi,

    I am trying to read a Real data with GetRealAt() function. The Real value in DB is “39.505”. But when I read the data the value I get is “39.5050010681152”. Any idea why do I get this value?

Leave a Reply

Share1
Tweet
Share
+1