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


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/


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


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.


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.




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



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("", 0, 1);
if(result == 0)
    Console.WriteLine("Connected to");

// Disconnect the client

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.


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:


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));


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.


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);


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.


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

Share Button


  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#?

        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 🙂

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

Leave a Reply