In spite of the fact that the Snap7Server is the easier object to use, initially it is probably the most complicated to digest.
Let's start to saying that Snap7Server neither is a kind of OPC Server nor is a program that gathers data from PLC and presents the results.
Snap7Server, just like a communication processor (CP), accepts S7 connections by external clients, and replies to their requests.
Just like the CPU that shares its resources
with its CP, Your application
must should share its resources (memory blocks) with
Starting from 1.4.0 Snap7Server can be work in resourceless mode (see).
The mechanism is very simple:
∑ Your program allocates a memory block and says to the server ďthis is your DB35Ē. Every time a client requests to read/write some byte from/to DB35, the server uses that block.
∑ If a client requests the access to an inexistent block (i.e. a block that you didnít shared) the server replies with an error of resource not found, just like a real PLC would do.
The client does not see any difference from a real PLC.
The simulation level is quite depth: S7 Manager (or TIA Portal) itself, sees your application as a CPU 315-2PN/DP.
Not being able to test Snap7Server with hundreds systems on the market, the key concept is:
ō If Siemens Simatic manager sees the server as a real PLC, more so every client (Scada, hmi panel, PLC driver) will see the server as a real PLC.
You can obtain the preceding result with the simple following program :
unsigned char DB1; // Our DB1
unsigned char DB2; // Our DB2
unsigned char DB3; // Our DB3
unsigned char MB; // 2048 = CPU 315 Merkers amount
int main(int argc, char* argv)
Server = new TS7Server;
Server->RegisterArea(srvAreaDB, // We are registering a DB
1, // Its number is 1 (DB1)
&DB1, // Its address
sizeof(DB1)); // Its size
// Do the same for DB2 and DB3
Server->RegisterArea(srvAreaDB, 2, &DB2, sizeof(DB2));
Server->RegisterArea(srvAreaDB, 3, &DB3, sizeof(DB3));
// Letís share all Merkers from M0.0 to M2047.7
Server->RegisterArea(srvAreaMK, 0, &MB, sizeof(MB));
// Start the server onto the default adapter ď0.0.0.0Ē
// Now the server is running ... wait a key to terminate
Server->Stop(); // not strictly needed
Please refer to API reference for functions syntax.
You can find the C# and Pascal version into examples folders.
Notice that we shared also the merkers area.
The Snap7Server is a multi-client multi-threaded server.
Once a connection is accepted, a new S7 worker
thread is created which, from this moment will serve that client.
When the clients disconnects, the S7 worker is destroyed.
Up to 1024 (*) connection can be accepted, this value however can be changed via Srv_SetParam().
At the moment there is no Blacklist/Whitelist mechanism for filtering the connections, in a future release however it could be implemented (depending on the project audience).
The compatibility with Simatic Manager, of course, is not complete.
The server, as said, is a CP simulator not a SoftPLC, i.e. there isnít a MC7 program, compatible with Simatic Manager, to be edited, uploaded or downloaded: the business logic, if any, is your application program.
S7 functions implemented (in the current release)
∑ Data I/O (also via multivariable read/write)
Read/Write DB, Mk, IPI, IPQ, Timers, and Counters.
List Blocks, List Blocks of Type, Block info.
∑ Control (1)
Run/Stop, Compress and Copy Ram to Rom.
∑ Date and Time (2)
Get/Set PLC Date and Time.
∑ System Info
Get/Set session password (3).
Some functions exist only to simulate a PLC presence, particularly :
(1)Run command is accepted and
subsequent get status command will show the CPU as running, Stop command
is accepted and subsequent get status command will show the CPU as
stopped. But they have no practical effect on the server.
Compress and Copy Ram to Rom are accepted but (obviously) they do nothing.
(2)Get Date and time returns the Host (PC in which the server is running) date and time. Set date and time is accepted but the host date and time is not modified.
(3)Whatever password is accepted.
maximum number of open TCP connections depends also on the Host OS.
S7 functions not implemented (in the current release)
∑ Block Upload/Download
Is accepted but the server replies that the operation cannot be accomplished because the security level is not met : we cannot download a block, a block must be created by the host application then shared with the server.
∑ Programming functions
The server does not replies at all.
∑ Cyclic data I/O
The server does not replies at all.
Due to limit the memory footprint (the server must work fine into a Raspberry PI too), SDBs are not present; there is no scada, AFIK, that needs to access them.
If you look at the previous small program, you see how to share resources between your application and the server.
We understood that the server replies automatically to the client requests, but :
There is a way to know what a client is requesting? Can we synchronize with it?
Is implemented a kind of log/debug mechanism?
Every time something happens in the server : when it is started, when it is stopped, when a client connects/disconnects or makes a request, an ďeventĒ is created.
The event is simply a struct defined as follow:
time_t EvtTime; // Timestamp
int EvtSender; // Sender
longword EvtCode; // Event code
word EvtRetCode; // Event result
word EvtParam1; // Param 1 (if available)
word EvtParam2; // Param 2 (if available)
word EvtParam3; // Param 3 (if available)
word EvtParam4; // Param 4 (if available)
EvtTime is the timestamp of the event, i.e. the date and time of its creation.
EvtSender is the IP of the Client involved in
this event. The format is 32 bit integer to save memory, and can be converted
into string, such as ď192.168.0.34Ē, using the socket function inet_ntoa() (Every OS socket layer has it).
If the event sender is the server itself (event generated on its startup for example), this value is 0.
EvtCode is the Event code, i.e. its identifier (see the list below).
EvtRetCode is the Event Result, it coincides with the result of the underlying S7 function if any, otherwise is 0.
EvtParam1..EvtParam4 are parameters whose meaning depends on the context.
In snap_tcpsrv.h and s7_types.h you will find all constants used.
const longword evcServerStarted = 0x00000001;
const longword evcServerStopped = 0x00000002;
const longword evcListenerCannotStart = 0x00000004;
const longword evcClientAdded = 0x00000008;
const longword evcClientRejected = 0x00000010;
const longword evcClientNoRoom = 0x00000020;
const longword evcClientException = 0x00000040;
const longword evcClientDisconnected = 0x00000080;
const longword evcClientTerminated = 0x00000100;
const longword evcClientsDropped = 0x00000200;
const longword evcReserved_00000400 = 0x00000400;
const longword evcReserved_00000800 = 0x00000800;
const longword evcReserved_00001000 = 0x00001000;
const longword evcReserved_00002000 = 0x00002000;
const longword evcReserved_00004000 = 0x00004000;
const longword evcReserved_00008000 = 0x00008000;
const longword evcPDUincoming = 0x00010000;
const longword evcDataRead = 0x00020000;
const longword evcDataWrite = 0x00040000;
const longword evcNegotiatePDU = 0x00080000;
const longword evcReadSZL = 0x00100000;
const longword evcClock = 0x00200000;
const longword evcUpload = 0x00400000;
const longword evcDownload = 0x00800000;
const longword evcDirectory = 0x01000000;
const longword evcSecurity = 0x02000000;
const longword evcControl = 0x04000000;
const longword evcReserved_08000000 = 0x08000000;
const longword evcReserved_10000000 = 0x10000000;
const longword evcReserved_20000000 = 0x20000000;
const longword evcReserved_40000000 = 0x40000000;
const longword evcReserved_80000000 = 0x80000000;
The event generated follows 2 ways : the events queue and the callbacks
The Events queue is a FIFO list protected with a critical section to ensure events consistency and itís thread-safe.
Each S7 Worker inserts its events into the queue, your application extracts them using Srv_PickEvent().
If the queue is full, i.e. you donít call Srv_PickEvent or call it too slowly, the event is not inserted and itís simply discarded.
On calling Srv_ClearEvents() the queue is flushed.
The Event queue is designed for log purpose.
The next code snippet is extracted from ServerDemo (Pascal rich-demo).
- LogTimer is a cyclic timer procedure.
- Log is a Text Memo object.
procedure TFrmServer.LogTimer(Sender: TObject);
Event : TSrvEvent;
// Updates Log memo
if Server.PickEvent(Event) then
if Log.Lines.Count>1024 then // to limit the size
// Updates other Server Infos
Finally, SrvEventText() returns the textual string of the event.
This is a sample output of this function:
2013-06-25 15:39:11 Server started
2013-06-25 15:39:24 [192.168.0.70] Client added
2013-06-25 15:39:24 [192.168.0.70] The client requires a PDU size of 480 bytes
2013-06-25 15:39:24 [192.168.0.70] Read SZL request, ID:0x0132 INDEX:0x0004 --> OK
2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0000 INDEX:0x0000 --> OK
2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0111 INDEX:0x0001 --> OK
2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0f74 INDEX:0x0000 --> OK
2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0074 INDEX:0x0000 --> OK
Ok, but I do not want to be bored with messages about SZL or Date/Time, can I filter them ?
Yes, you can filter them checking the EvtCode parameter but, even better, the server can make this for you.
If you look at the EvtCode List, you will notice that each event occupies one bit into a 32 bit word.
Itís possible to pass to the server a BitMask word, named LogMask, whose bits will act as ďand gateĒ for the events.
For example, this mask 0xFFFFFFFE has the first bit set to 0.
If you pass it to the server, every event except for ďServer StartedĒ will be stored.
While, as said, the Event queue is designed for log purpose, the callback mechanism is designed for control purpose.
Also the callback mechanism is filtered with its own bitmask and is protected with a critical section.
There are two differents callbacks (from Snap7 1.1.0) the Read Callback and the Common Callback.
Both callbacks are executed in the same thread
of the S7 Worker. The first is invoked on read request before performing the
data transfer from Snap7Server to the Client, the second after the handshake
with the client to avoid Client timeout since the code inside the callback
locks the worker.
See Srv_SetReadEventsCallback() and Srv_SetEventsCallback() for further information about the callback prototype.
Log Mask and Callback mask are different, by default are set both to 0xFFFFFFFF (all enabled) on the server creation.
The purpose of the ReadEventCallback is for writing protocol converters or gateways.
Finally letís see the complete sequence.
The Client Requests to read some data from DB4.
Invokes the Read Callback (if
assigned) passing it the read coordinates.
Into the Read Callback we are able to modify DB4 if we want.
Gets the Data from DB4.
Sends the Data and Job result to the client.
Checks the Log Filter and inserts the Event into the queue.
Checks the Callback filter and, if the callback is assigned, calls the user function passing the event as parameter.
Becomes ready for further Client requests.
Since the main application shares its resources with the server, a synchronization method is needed to ensure the data consistency.
When a memory block is shared via Srv_RegisterArea(), the server creates a block descriptor.
This descriptor contains
∑ Block number (itís used only if the block is a DB).
∑ Block memory address.
∑ Block size.
∑ A CriticalSection Object reference.
Just that object ensures the data consistency.
The S7 worker ďlocksĒ the memory block every time it needs to access it, and unlocks it at the end.
To improve the performances, a double-buffer
method is used: the S7 worker first receives the data into an internal buffer
and then copies the content into the shared block.
Or, it copies the needed data from the shared block into the internal buffer before sending them.
Only the copy operation locks the block.
If you need data consistency, you must accomplish this rule.
For this purpose there are two functions : Srv_LockArea() and Srv_UnlockArea().
You should use the first one to lock a memory block and the second one to unlock it.
On long operations I suggest you to adopt the
same double-buffer strategy : use an internal buffer then transfer the data
into the shared block.
Moreover, an exception raised when the block is locked will lead to a S7 worker freeze.
The granularity of the consistency is the PDU size.
In preparation to receive
connections a socket must be bound to a 2-tuple value :
(IP Address, Listening Port).
These coordinates are unique.
You can have a Telnet Server, a HTTP Server and a NTP Server running in the same machine because, though they have the same IP, they are listening on different ports.
You can have two HTTP Servers in the same machine that has two network adapters (i.e. two different IP addresses).
Established this (Berkley Sockets) rule, you can create multiple Snap7Servers but each of them must be ďstartedĒ onto a different network adapter, because the listening port (ISO TCP - 102) cannot be changed.
It could be useful run two servers to share data between two different network segments.
If you plan to use a physical server, 16
adapters is the maximum suggested.
If you need more, consider to use a virtual infrastructure.
There are mainly three reasons for which a start error is generated:
The first, trivial, is that the bind address is wrong.
In the PC in which you are trying to start the server is installed the Step 7 / TIA Portal environment.
Step 7 has a server listening onto the port 102 : s7oiehsx.
To overcome this problem you can temporary stop it, by running one of the batch files:
stop_s7oiehsx_x86.bat (32 bit) or
stop_s7oiehsx_x64.bat (64 bit)
To re-run the Step 7 service use their counterpart start_s7oiehsx_xxx.bat.
You can find them in the examples folder.
Tanks to www.runmode.com
ISO TCP port is a well-known port, so in Unix you need root rights, or your application must have the SUID flag set, to bind a socket to this port.
There is not a workaround for this.
If a client does not connect with the server, check your Firewall settings (especially if the host OS is Windows 7 / Windows 8).
This is a sample PG Project of the Snap7Server, with this project you can:
∑ Connect the Step 7 Manager (or Tia Portal if you convert it) to the Snap7Server and see it online.
∑ Insert it in a multi-cpu project.
∑ Integrate a WinCC flexible project into it.
You can find it in the folder examples/Step7/Server
To use it you only need to setup the network IP address (as you would do, in a real project).
There are few steps to follow:
1) Load the project into Step 7 Simatic Manager.
2) Open the Hardware Configuration
3) Open the PN-IO Interface editor
4) Open the Network properties editor
5) Set the IP Address of the PC in which the server will run (and confirm pressing the OK Button).
6) Close all previous windows, Save & Compile the Project, donít download it.
7) You can close the Hardware Configuration Editor, thatís all.
In the host PC, run a Server program, any of the ones you find in the example folder or the rich-demo ServerDemo if you want.
Now you can go ONLINE with Simatic Manager, remember to set TCP/IP Auto as PG interface.
You can modify the offline project
adding DB, variables and so on, and link them to WinCC
Remember, obviously, to create the same DBs into your application and to share them.
Snap7Server is not visible via the ďDisplay accessible nodesĒ function of Simatic manager, because to find the Ethernet nodes a Profinet packet (ServiceID=5, Discover, All) is used.
However, even WinAC, is visible with this method if itís equipped only with IE (Industrial Ethernet) standard adapter.
The final 1.000.000 Ä (Eur. because Iím Italian) question is:
Why do I need this Object ?
Letís see two real scenarios:
You have PC-based automation but:
o You donít want to create graphic screens.
o You don't want to ďexposeĒ the system with a standard keyboard.
With Snap7Server you can connect an HMI-Panel (Siemens/ESA/Pro-Face/Open source Scada) to your application and use a standard HMI-Builder.
Tomorrow you can migrate your application to another OS without be bored by graphic libraries porting.
Variant of the above:
You have a Raspberry-based (or other small Linux card) system and:
o You donít want to use an HDMI monitor.
o And you donít like a sad SSH interface.
o You like a small ISO-rail mounted box, wired using only two cables (network and power).
Snap7Server allows your embedded hardware to be connected by hundred standard systems Siemens-Aware.
You build an analogic test bench to be inserted in an existing PLC-based production line and sadly realize that:
o Your bench is PC-based.
o The line has a commercial scada as supervisor.
o You donít want to buy a PLC as line interface layer.
The commercial scada can be smoothly interfaced with your application via a Snap7Server (moreover this solution is faster).