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.

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

Share Button

Leave a Reply